Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (18,21 ko)

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

    
69
def slz_compute_scaled_function(functionSa, \
70
                                      lowerBoundSa, \
71
                                      upperBoundSa, \
72
                                      floatingPointPrecSa):
73
    """
74
    From a function, compute the scaled function whose domain
75
    is included in [1, 2) and whose image is also included in [1,2).
76
    Return a tuple: 
77
    [0]: the scaled function
78
    [1]: the scaled domain lower bound
79
    [2]: the scaled domain upper bound
80
    [3]: the scaled image lower bound
81
    [4]: the scaled image upper bound
82
    """
83
    x = functionSa.variables()[0]
84
    # Reassert f as a function (an not a mere expression).
85
    
86
    # Scalling the domain -> [1,2[.
87
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
88
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
89
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
90
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
91
    print "domainScalingExpression for argument :", domainScalingExpressionSa
92
    print "f: ", f
93
    ff = f.subs({x : domainScalingExpressionSa})
94
    #ff = f.subs_expr(x==domainScalingExpressionSa)
95
    domainScalingFunction(x) = invDomainScalingExpressionSa
96
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
97
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
98
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
99
    #
100
    # Scalling the image -> [1,2[.
101
    flbSa = f(lowerBoundSa).n()
102
    fubSa = f(upperBoundSa).n()
103
    if flbSa <= fubSa: # Increasing
104
        imageBinadeBottomSa = floor(flbSa.log2())
105
    else: # Decreasing
106
        imageBinadeBottomSa = floor(fubSa.log2())
107
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
108
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
109
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
110
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
111
    iis = invImageScalingExpressionSa.function(x)
112
    fff = iis.subs({x:ff})
113
    print "fff:", fff,
114
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
115
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
116
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
117

    
118
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
119
    # Create a polynomial over the rationals.
120
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
121
    return(polynomialRing(polyOfFloat))
122
# End slz_float_poly_of_float_to_rat_poly_of_rat
123

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

    
255

    
256
def slz_interval_scaling_expression(boundsInterval, expVar):
257
    """
258
    Compute the scaling expression to map an interval that span only
259
    a binade to [1, 2) and the inverse expression as well.
260
    Not very sure that the transformation makes sense for negative numbers.
261
    """
262
    # The scaling offset is only used for negative numbers.
263
    if abs(boundsInterval.endpoints()[0]) < 1:
264
        if boundsInterval.endpoints()[0] >= 0:
265
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
266
            invScalingCoeff = 1/scalingCoeff
267
            return((scalingCoeff * expVar, 
268
                    invScalingCoeff * expVar))
269
        else:
270
            scalingCoeff = \
271
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
272
            scalingOffset = -3 * scalingCoeff
273
            return((scalingCoeff * expVar + scalingOffset,
274
                    1/scalingCoeff * expVar + 3))
275
    else: 
276
        if boundsInterval.endpoints()[0] >= 0:
277
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
278
            scalingOffset = 0
279
            return((scalingCoeff * expVar, 
280
                    1/scalingCoeff * expVar))
281
        else:
282
            scalingCoeff = \
283
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
284
            scalingOffset =  -3 * scalingCoeff
285
            #scalingOffset = 0
286
            return((scalingCoeff * expVar + scalingOffset,
287
                    1/scalingCoeff * expVar + 3))
288

    
289
   
290
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
291
    """
292
    Compute the Sage version of the Taylor polynomial and it's
293
    companion data (interval, center...)
294
    The input parameter is a five elements tuple:
295
    - [0]: the polyomial (without variable change), as polynomial over a
296
           real ring;
297
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
298
           over a real ring;
299
    - [2]: the interval (as Sollya range);
300
    - [3]: the interval center;
301
    - [4]: the approximation error. 
302
    
303
    The function return a 5 elements tuple: formed with all the 
304
    input elements converted into their Sollya counterpart.
305
    """
306
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
307
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
308
    intervalSa = \
309
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
310
    centerSa = \
311
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
312
    errorSa = \
313
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
314
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
315
    # End slz_interval_and_polynomial_to_sage
316

    
317
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
318
                                                precision,
319
                                                targetHardnessToRound,
320
                                                variable1,
321
                                                variable2):
322
    """
323
    Creates a new polynomial with integer coefficients for use with the
324
    Coppersmith method.
325
    A the same time it computes :
326
    - 2^K (N);
327
    - 2^k
328
    - lcm
329
    """
330
    # Create a new integer polynomial ring.
331
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
332
    # Coefficients are issued in the increasing power order.
333
    ratPolyCoefficients = ratPolyOfInt.coefficients()
334
    # Build the list of number we compute the lcmm of.
335
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
336
    coefficientDenominators.append(2^precision)
337
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
338
    leastCommonMultiple = sro_lcmm(coefficientDenominators)
339
    # Compute the lcm
340
    leastCommonMultiple = sro_lcmm(coefficientDenominators)
341
    # Compute the expression corresponding to the new polynomial
342
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
343
    print coefficientNumerators
344
    polynomialExpression = 0
345
    power = 0
346
    # Iterate over two lists at the same time, stop when the shorter is
347
    # exhausted.
348
    for numerator, denominator in \
349
                            zip(coefficientNumerators, coefficientDenominators):
350
        multiplicator = leastCommonMultiple / denominator 
351
        newCoefficient = numerator * multiplicator
352
        polynomialExpression += newCoefficient * variable1^power
353
        power +=1
354
    polynomialExpression += - variable2
355
    return (IP(polynomialExpression),
356
            leastCommonMultiple / 2^precision, # 2^K or N.
357
            leastCommonMultiple / 2 ^(targetHardnessToRound + 1), # tBound
358
            leastCommonMultiple)
359
        
360
# End slz_ratPoly_of_int_to_poly_for_coppersmith
361

    
362
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
363
                                          precision):
364
    """
365
    Makes a variable substitution into the input polynomial so that the output
366
    polynomial can take integer arguments.
367
    All variables of the input polynomial "have precision p". That is to say
368
    that they are rationals with denominator == 2^precision: x = y/2^precision
369
    We "incorporate" these denominators into the coefficients with, 
370
    respectively, the "right" power.
371
    """
372
    polynomialField = ratPolyOfRat.parent()
373
    polynomialVariable = rationalPolynomial.variables()[0]
374
    print "The polynomial field is:", polynomialField
375
    return \
376
        polynomialField(rationalPolynomial.subs({polynomialVariable : \
377
                                   polynomialVariable/2^(precision-1)}))
378
    
379
    # Return a tuple:
380
    # - the bivariate integer polynomial in (i,j);
381
    # - 2^K
382
# End slz_rat_poly_of_rat_to_rat_poly_of_int
383

    
384
print "sageSLZ loaded..."