Statistiques
| Révision :

root / pobysoPythonSage / src / sageSLZ / sageSLZ.sage @ 90

Historique | Voir | Annoter | Télécharger (19,33 ko)

1
"""
2
module: sageSLZ.sage
3

    
4
Sage core function needed for the implementation of SLZ.
5

    
6
Created on 2013-08
7

    
8
moduleauthor:: S.T.
9
"""
10
print "sageSLZ loading..."
11
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
12
                                        upperBoundSa, approxPrecSa, 
13
                                        sollyaPrecSa=None):
14
    """
15
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
16
    a polynomial that approximates the function on a an interval starting
17
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
18
    approximates with the expected precision.
19
    The interval upper bound is lowered until the expected approximation 
20
    precision is reached.
21
    The polynomial, the bounds, the center of the interval and the error
22
    are returned.
23
    """
24
    RRR = lowerBoundSa.parent()
25
    intervalShrinkConstFactorSa = RRR('0.5')
26
    absoluteErrorTypeSo = pobyso_absolute_so_so()
27
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
28
    currentUpperBoundSa = upperBoundSa
29
    currentLowerBoundSa = lowerBoundSa
30
    # What we want here is the polynomial without the variable change, 
31
    # since our actual variable will be x-intervalCenter defined over the 
32
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
33
    (polySo, intervalCenterSo, maxErrorSo) = \
34
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
35
                                                    currentRangeSo, 
36
                                                    absoluteErrorTypeSo)
37
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
38
    while maxErrorSa > approxPrecSa:
39
        sollya_lib_clear_obj(maxErrorSo)
40
        sollya_lib_clear_obj(polySo)
41
        sollya_lib_clear_obj(intervalCenterSo)
42
        shrinkFactorSa = RRR('5.0')/(maxErrorSa/approxPrecSa).log2().abs()
43
        #shrinkFactorSa = 1.5/(maxErrorSa/approxPrecSa)
44
        #errorRatioSa = approxPrecSa/maxErrorSa
45
        #print "Error ratio: ", errorRatioSa
46
        
47
        if shrinkFactorSa > intervalShrinkConstFactorSa:
48
            actualShrinkFactorSa = intervalShrinkConstFactorSa
49
            #print "Fixed"
50
        else:
51
            actualShrinkFactorSa = shrinkFactorSa
52
            #print "Computed",shrinkFactorSa,maxErrorSa
53
            #print shrinkFactorSa, maxErrorSa
54
        currentUpperBoundSa = currentLowerBoundSa + \
55
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
56
                                   actualShrinkFactorSa
57
        #print "Current upper bound:", currentUpperBoundSa
58
        sollya_lib_clear_obj(currentRangeSo)
59
        sollya_lib_clear_obj(polySo)
60
        if currentUpperBoundSa <= currentLowerBoundSa:
61
            sollya_lib_clear_obj(absoluteErrorTypeSo)
62
            print "Can't find an interval."
63
            print "Use either or both a higher polynomial degree or a higher",
64
            print "internal precision."
65
            print "Aborting!"
66
            return (None, None, None, None)
67
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
68
                                                      currentUpperBoundSa)
69
        # print "New interval:",
70
        # pobyso_autoprint(currentRangeSo)
71
        (polySo, intervalCenterSo, maxErrorSo) = \
72
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
73
                                                        currentRangeSo, 
74
                                                        absoluteErrorTypeSo)
75
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
76
        #print "Max errorSo:",
77
        #pobyso_autoprint(maxErrorSo)
78
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
79
        #print "Max errorSa:", maxErrorSa
80
        #print "Sollya prec:",
81
        #pobyso_autoprint(sollya_lib_get_prec(None))
82
    sollya_lib_clear_obj(absoluteErrorTypeSo)
83
    return((polySo, currentRangeSo, intervalCenterSo, maxErrorSo))
84
# End slz_compute_polynomial_and_interval
85

    
86
def slz_compute_scaled_function(functionSa, \
87
                                      lowerBoundSa, \
88
                                      upperBoundSa, \
89
                                      floatingPointPrecSa):
90
    """
91
    From a function, compute the scaled function whose domain
92
    is included in [1, 2) and whose image is also included in [1,2).
93
    Return a tuple: 
94
    [0]: the scaled function
95
    [1]: the scaled domain lower bound
96
    [2]: the scaled domain upper bound
97
    [3]: the scaled image lower bound
98
    [4]: the scaled image upper bound
99
    """
100
    x = functionSa.variables()[0]
101
    # Reassert f as a function (an not a mere expression).
102
    
103
    # Scalling the domain -> [1,2[.
104
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
105
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
106
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
107
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
108
    print "domainScalingExpression for argument :", domainScalingExpressionSa
109
    print "f: ", f
110
    ff = f.subs({x : domainScalingExpressionSa})
111
    #ff = f.subs_expr(x==domainScalingExpressionSa)
112
    domainScalingFunction(x) = invDomainScalingExpressionSa
113
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
114
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
115
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
116
    #
117
    # Scalling the image -> [1,2[.
118
    flbSa = f(lowerBoundSa).n()
119
    fubSa = f(upperBoundSa).n()
120
    if flbSa <= fubSa: # Increasing
121
        imageBinadeBottomSa = floor(flbSa.log2())
122
    else: # Decreasing
123
        imageBinadeBottomSa = floor(fubSa.log2())
124
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
125
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
126
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
127
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
128
    iis = invImageScalingExpressionSa.function(x)
129
    fff = iis.subs({x:ff})
130
    print "fff:", fff,
131
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
132
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
133
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
134

    
135
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
136
    # Create a polynomial over the rationals.
137
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
138
    return(polynomialRing(polyOfFloat))
139
# End slz_float_poly_of_float_to_rat_poly_of_rat.
140

    
141
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
142
                                      lowerBoundSa, 
143
                                      upperBoundSa, floatingPointPrecSa, 
144
                                      internalSollyaPrecSa, approxPrecSa):
145
    """
146
    Under the assumption that:
147
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
148
    - lowerBound and upperBound belong to the same binade.
149
    from a:
150
    - function;
151
    - a degree
152
    - a pair of bounds;
153
    - the floating-point precision we work on;
154
    - the internal Sollya precision;
155
    - the requested approximation error
156
    The initial interval is, possibly, splitted into smaller intervals.
157
    It return a list of tuples, each made of:
158
    - a first polynomial (without the changed variable f(x) = p(x-x0));
159
    - a second polynomial (with a changed variable f(x) = q(x))
160
    - the approximation interval;
161
    - the center, x0, of the interval;
162
    - the corresponding approximation error.
163
    """
164
    currentSollyaPrecSo = pobyso_get_prec_so()
165
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
166
    if internalSollyaPrecSa > currentSollyaPrecSa:
167
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
168
    x = functionSa.variables()[0] # Actual variable name can be anything.
169
    (fff, scaledLowerBoundSa, scaledUpperBoundSa, \
170
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) = \
171
                slz_compute_scaled_function(functionSa, \
172
                                            lowerBoundSa, \
173
                                            upperBoundSa, \
174
                                            floatingPointPrecSa)
175
    #
176
    resultArray = []
177
    #
178
    print "Approximation precision: ", RR(approxPrecSa)
179
    # Prepare the arguments for the Taylor expansion computation with Sollya.
180
    functionSo = pobyso_parse_string_sa_so(fff._assume_str())
181
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
182
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
183
                                                  scaledUpperBoundSa)
184
    # Compute the first Taylor expansion.
185
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
186
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
187
                                        scaledLowerBoundSa, scaledUpperBoundSa,
188
                                        approxPrecSa, internalSollyaPrecSa)
189
    if polySo is None:
190
        print "Aborting"
191
        return None
192
    # Change variable stuff
193
    changeVarExpressionSo = sollya_lib_build_function_sub(
194
                              sollya_lib_build_function_free_variable(), 
195
                              sollya_lib_copy_obj(intervalCenterSo))
196
    polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
197
    resultArray.append((polySo, polyVarChangedSo, boundsSo, intervalCenterSo,\
198
                         maxErrorSo))
199
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
200
                                              upperBoundSa.parent().precision()))
201
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
202
    # Compute the next upper bound.
203
    # If the error of approximation is more than half of the target,
204
    # use the same interval.
205
    # If it is less, increase it a bit.
206
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
207
    currentErrorRatio = approxPrecSa / errorSa
208
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
209
    if currentErrorRatio  < 2 :
210
        currentScaledUpperBoundSa += \
211
            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0])
212
    else:
213
        currentScaledUpperBoundSa += \
214
            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
215
                         * currentErrorRatio.log2() * 2
216
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
217
        currentScaledUpperBoundSa = scaledUpperBoundSa
218
    # Compute the other expansions.
219
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
220
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
221
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
222
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
223
                                            currentScaledLowerBoundSa, 
224
                                            currentScaledUpperBoundSa, 
225
                                            approxPrecSa, 
226
                                            internalSollyaPrecSa)
227
        # Change variable stuff
228
        changeVarExpressionSo = sollya_lib_build_function_sub(
229
                                  sollya_lib_build_function_free_variable(), 
230
                                  sollya_lib_copy_obj(intervalCenterSo))
231
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
232
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
233
                            intervalCenterSo, maxErrorSo))
234
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
235
        # Compute the next upper bound.
236
        # If the error of approximation is more than half of the target,
237
        # use the same interval.
238
        # If it is less, increase it a bit.
239
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
240
        currentErrorRatio = approxPrecSa / errorSa
241
        if currentErrorRatio  < RR('1.5') :
242
            currentScaledUpperBoundSa = \
243
                            boundsSa.endpoints()[1] + \
244
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0])
245
        elif currentErrorRatio < 2:
246
            currentScaledUpperBoundSa = \
247
                            boundsSa.endpoints()[1] + \
248
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
249
                             * currentErrorRatio.log2()
250
        else:
251
            currentScaledUpperBoundSa = \
252
                            boundsSa.endpoints()[1] + \
253
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
254
                             * currentErrorRatio.log2() * 2
255
        # Test for insufficient precision.
256
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
257
            print "Can't shrink the interval anymore!"
258
            print "You should consider increasing the Sollya internal precision"
259
            print "or the polynomial degree."
260
            print "Giving up!"
261
            sollya_lib_clear_obj(functionSo)
262
            sollya_lib_clear_obj(degreeSo)
263
            sollya_lib_clear_obj(scaledBoundsSo)
264
            return None
265
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
266
            currentScaledUpperBoundSa = scaledUpperBoundSa
267
    sollya_lib_clear_obj(functionSo)
268
    sollya_lib_clear_obj(degreeSo)
269
    sollya_lib_clear_obj(scaledBoundsSo)
270
    if internalSollyaPrecSa > currentSollyaPrecSa:
271
        pobyso_set_prec_so_so(currentSollyaPrecSo)
272
    return(resultArray)
273
# End slz_get_intervals_and_polynomials
274

    
275

    
276
def slz_interval_scaling_expression(boundsInterval, expVar):
277
    """
278
    Compute the scaling expression to map an interval that span only
279
    a binade to [1, 2) and the inverse expression as well.
280
    Not very sure that the transformation makes sense for negative numbers.
281
    """
282
    # The scaling offset is only used for negative numbers.
283
    if abs(boundsInterval.endpoints()[0]) < 1:
284
        if boundsInterval.endpoints()[0] >= 0:
285
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
286
            invScalingCoeff = 1/scalingCoeff
287
            return((scalingCoeff * expVar, 
288
                    invScalingCoeff * expVar))
289
        else:
290
            scalingCoeff = \
291
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
292
            scalingOffset = -3 * scalingCoeff
293
            return((scalingCoeff * expVar + scalingOffset,
294
                    1/scalingCoeff * expVar + 3))
295
    else: 
296
        if boundsInterval.endpoints()[0] >= 0:
297
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
298
            scalingOffset = 0
299
            return((scalingCoeff * expVar, 
300
                    1/scalingCoeff * expVar))
301
        else:
302
            scalingCoeff = \
303
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
304
            scalingOffset =  -3 * scalingCoeff
305
            #scalingOffset = 0
306
            return((scalingCoeff * expVar + scalingOffset,
307
                    1/scalingCoeff * expVar + 3))
308

    
309
   
310
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
311
    """
312
    Compute the Sage version of the Taylor polynomial and it's
313
    companion data (interval, center...)
314
    The input parameter is a five elements tuple:
315
    - [0]: the polyomial (without variable change), as polynomial over a
316
           real ring;
317
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
318
           over a real ring;
319
    - [2]: the interval (as Sollya range);
320
    - [3]: the interval center;
321
    - [4]: the approximation error. 
322
    
323
    The function return a 5 elements tuple: formed with all the 
324
    input elements converted into their Sollya counterpart.
325
    """
326
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
327
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
328
    intervalSa = \
329
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
330
    centerSa = \
331
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
332
    errorSa = \
333
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
334
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
335
    # End slz_interval_and_polynomial_to_sage
336

    
337
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
338
                                                precision,
339
                                                targetHardnessToRound,
340
                                                variable1,
341
                                                variable2):
342
    """
343
    Creates a new multivariate polynomial with integer coefficients for use
344
     with the Coppersmith method.
345
    A the same time it computes :
346
    - 2^K (N);
347
    - 2^k (bound on the second variable)
348
    - lcm
349

    
350
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
351
                         variables.
352
    :param precision: the precision of the floating-point coefficients.
353
    :param targetHardnessToRound: the hardness to round we want to check.
354
    :param variable1: the first variable of the polynomial (an expression).
355
    :param variable2: the second variable of the polynomial (an expression).
356
    
357
    :returns: a 4 elements tuple:
358
                - the polynomial;
359
                - the module (N);
360
                - the lcm used to compute the integral coefficients and the 
361
                  module.
362
    """
363
    # Create a new integer polynomial ring.
364
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
365
    # Coefficients are issued in the increasing power order.
366
    ratPolyCoefficients = ratPolyOfInt.coefficients()
367
    # Build the list of number we compute the lcmm of.
368
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
369
    coefficientDenominators.append(2^precision)
370
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
371
    leastCommonMultiple = lcm(coefficientDenominators)
372
    # Compute the expression corresponding to the new polynomial
373
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
374
    print coefficientNumerators
375
    polynomialExpression = 0
376
    power = 0
377
    # Iterate over two lists at the same time, stop when the shorter is
378
    # exhausted.
379
    for numerator, denominator in \
380
                            zip(coefficientNumerators, coefficientDenominators):
381
        multiplicator = leastCommonMultiple / denominator 
382
        newCoefficient = numerator * multiplicator
383
        polynomialExpression += newCoefficient * variable1^power
384
        power +=1
385
    polynomialExpression += - variable2
386
    return (IP(polynomialExpression),
387
            leastCommonMultiple / 2^precision, # 2^K or N.
388
            leastCommonMultiple / 2 ^(targetHardnessToRound + 1), # tBound
389
            leastCommonMultiple)
390
        
391
# End slz_ratPoly_of_int_to_poly_for_coppersmith
392

    
393
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
394
                                          precision):
395
    """
396
    Makes a variable substitution into the input polynomial so that the output
397
    polynomial can take integer arguments.
398
    All variables of the input polynomial "have precision p". That is to say
399
    that they are rationals with denominator == 2^precision: x = y/2^precision
400
    We "incorporate" these denominators into the coefficients with, 
401
    respectively, the "right" power.
402
    """
403
    polynomialField = ratPolyOfRat.parent()
404
    polynomialVariable = rationalPolynomial.variables()[0]
405
    print "The polynomial field is:", polynomialField
406
    return \
407
        polynomialField(rationalPolynomial.subs({polynomialVariable : \
408
                                   polynomialVariable/2^(precision-1)}))
409
    
410
    # Return a tuple:
411
    # - the bivariate integer polynomial in (i,j);
412
    # - 2^K
413
# End slz_rat_poly_of_rat_to_rat_poly_of_int
414

    
415
print "\t...sageSLZ loaded"