Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (15,26 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
    #goldenRatioSa = RRR(5.sqrt() / 2 - 1/2)
16
    #intervalShrinkConstFactorSa = goldenRatioSa
17
    intervalShrinkConstFactorSa = RRR('0.5')
18
    absoluteErrorTypeSo = pobyso_absolute_so_so()
19
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
20
    currentUpperBoundSa = upperBoundSa
21
    currentLowerBoundSa = lowerBoundSa
22
    # What we want here is the polynomial without the variable change, 
23
    # since our actual variable will be x-intervalCenter defined over the 
24
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
25
    (polySo, intervalCenterSo, maxErrorSo) = \
26
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
27
                                                    currentRangeSo, 
28
                                                    absoluteErrorTypeSo)
29
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
30
    while maxErrorSa > approxPrecSa:
31
        sollya_lib_clear_obj(maxErrorSo)
32
        errorRatioSa = 1/(maxErrorSa/approxPrecSa).log2()
33
        #print "Error ratio: ", errorRatioSa
34
        if errorRatioSa > intervalShrinkConstFactorSa:
35
            currentUpperBoundSa = currentLowerBoundSa + \
36
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
37
                                   intervalShrinkConstFactorSa
38
        else:
39
            currentUpperBoundSa = currentLowerBoundSa + \
40
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
41
                                  intervalShrinkConstFactorSa
42
            currentUpperBoundSa = currentLowerBoundSa + \
43
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
44
                                  errorRatioSa
45
        #print "Current upper bound:", currentUpperBoundSa
46
        sollya_lib_clear_obj(currentRangeSo)
47
        sollya_lib_clear_obj(polySo)
48
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
49
                                                      currentUpperBoundSa)
50
        (polySo, intervalCenterSo, maxErrorSo) = \
51
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
52
                                                        currentRangeSo, 
53
                                                        absoluteErrorTypeSo)
54
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
55
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
56
    sollya_lib_clear_obj(absoluteErrorTypeSo)
57
    return((polySo, currentRangeSo, intervalCenterSo, maxErrorSo))
58
    # End slz_compute_polynomial_and_interval
59

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

    
109
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
110
    # Create a polynomial over the rationals.
111
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
112
    return(polynomialRing(polyOfFloat))
113
# End slz_float_poly_of_float_to_rat_poly_of_rat
114
     
115
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
116
                                      lowerBoundSa, 
117
                                      upperBoundSa, floatingPointPrecSa, 
118
                                      internalSollyaPrecSa, approxPrecSa):
119
    """
120
    Under the assumption that:
121
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
122
    - lowerBound and upperBound belong to the same binade.
123
    from a:
124
    - function;
125
    - a degree
126
    - a pair of bounds;
127
    - the floating-point precision we work on;
128
    - the internal Sollya precision;
129
    - the requested approximation error
130
    The initial interval is, possibly, splitted into smaller intervals.
131
    It return a list of tuples, each made of:
132
    - a first polynomial (without the changed variable f(x) = p(x-x0));
133
    - a second polynomial (with a changed variable f(x) = q(x))
134
    - the approximation interval;
135
    - the center, x0, of the interval;
136
    - the corresponding approximation error.
137
    """
138
    x = functionSa.variables()[0] # Actual variable name can be anything.
139
    (fff, scaledLowerBoundSa, scaledUpperBoundSa, \
140
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) = \
141
                slz_compute_scaled_function(functionSa, \
142
                                            lowerBoundSa, \
143
                                            upperBoundSa, \
144
                                            floatingPointPrecSa)
145
    #
146
    resultArray = []
147
    #
148
    print "Approximation precision: ", RR(approxPrecSa)
149
    # Prepare the arguments for the Taylor expansion computation with Sollya.
150
    functionSo = pobyso_parse_string_sa_so(fff._assume_str())
151
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
152
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
153
                                                  scaledUpperBoundSa)
154
    # Compute the first Taylor expansion.
155
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
156
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
157
                                        scaledLowerBoundSa, scaledUpperBoundSa,
158
                                        approxPrecSa, internalSollyaPrecSa)
159
    # Change variable stuff
160
    changeVarExpressionSo = sollya_lib_build_function_sub(
161
                              sollya_lib_build_function_free_variable(), 
162
                              sollya_lib_copy_obj(intervalCenterSo))
163
    polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
164
    resultArray.append((polySo, polyVarChangedSo, boundsSo, intervalCenterSo,\
165
                         maxErrorSo))
166
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
167
                                              upperBoundSa.parent().precision()))
168
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
169
    # Compute the other expansions.
170
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
171
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
172
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
173
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
174
                                            currentScaledLowerBoundSa, 
175
                                            scaledUpperBoundSa, approxPrecSa, 
176
                                            internalSollyaPrecSa)
177
        # Change variable stuff
178
        changeVarExpressionSo = sollya_lib_build_function_sub(
179
                                  sollya_lib_build_function_free_variable(), 
180
                                  sollya_lib_copy_obj(intervalCenterSo))
181
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
182
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
183
                            intervalCenterSo, maxErrorSo))
184
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
185
    sollya_lib_clear_obj(functionSo)
186
    sollya_lib_clear_obj(degreeSo)
187
    sollya_lib_clear_obj(scaledBoundsSo)
188
    return(resultArray)
189
    # End slz_get_intervals_and_polynomials
190

    
191
def slz_interval_scaling_expression(boundsInterval, expVar):
192
    """
193
    Compute the scaling expression to map an interval that span only
194
    a binade to [1, 2) and the inverse expression as well.
195
    Not very sure that the transformation makes sense for negative numbers.
196
    """
197
    # The scaling offset is only used for negative numbers.
198
    if abs(boundsInterval.endpoints()[0]) < 1:
199
        if boundsInterval.endpoints()[0] >= 0:
200
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
201
            invScalingCoeff = 1/scalingCoeff
202
            return((scalingCoeff * expVar, 
203
                    invScalingCoeff * expVar))
204
        else:
205
            scalingCoeff = \
206
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
207
            scalingOffset = -3 * scalingCoeff
208
            return((scalingCoeff * expVar + scalingOffset,
209
                    1/scalingCoeff * expVar + 3))
210
    else: 
211
        if boundsInterval.endpoints()[0] >= 0:
212
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
213
            scalingOffset = 0
214
            return((scalingCoeff * expVar, 
215
                    1/scalingCoeff * expVar))
216
        else:
217
            scalingCoeff = \
218
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
219
            scalingOffset =  -3 * scalingCoeff
220
            #scalingOffset = 0
221
            return((scalingCoeff * expVar + scalingOffset,
222
                    1/scalingCoeff * expVar + 3))
223

    
224
   
225
def slz_polynomial_and_interval_to_sage(polyRangeCenterErrorSo):
226
    """
227
    Compute the Sage version of the Taylor polynomial and it's
228
    companion data (interval, center...)
229
    The input parameter is a five elements tuple:
230
    - [0]: the polyomial (without variable change), as polynomial over a
231
           real ring;
232
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
233
           over a real ring;
234
    - [2]: the interval (as Sollya range);
235
    - [3]: the interval center;
236
    - [4]: the approximation error. 
237
    
238
    The function return a 5 elements tuple: formed with all the 
239
    input elements converted into their Sollya counterpart.
240
    """
241
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
242
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
243
    intervalSa = \
244
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
245
    centerSa = \
246
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
247
    errorSa = \
248
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
249
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
250
    # End slz_polynomial_and_interval_to_sage
251

    
252
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
253
                                                precision,
254
                                                targetHardnessToRound,
255
                                                variable1,
256
                                                variable2):
257
    """
258
    Creates a new polynomial with integer coefficients for use with the
259
    Coppersmith method.
260
    A the same time it computes :
261
    - 2^K (N);
262
    - 2^k
263
    - lcm
264
    """
265
    # Create a new integer polynomial ring.
266
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
267
    # Coefficients are issued in the increasing power order.
268
    ratPolyCoefficients = ratPolyOfInt.coefficients()
269
    # Build the list of number we compute the lcmm of.
270
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
271
    coefficientDenominators.append(2^precision)
272
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
273
    leastCommonMultiple = sro_lcmm(coefficientDenominators)
274
    # Compute the lcm
275
    leastCommonMultiple = sro_lcmm(coefficientDenominators)
276
    # Compute the expression corresponding to the new polynomial
277
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
278
    print coefficientNumerators
279
    polynomialExpression = 0
280
    power = 0
281
    # Iterate over two lists at the same time, stop when the shorter is
282
    # exhausted.
283
    for numerator, denominator in \
284
                            zip(coefficientNumerators, coefficientDenominators):
285
        multiplicator = leastCommonMultiple / denominator 
286
        newCoefficient = numerator * multiplicator
287
        polynomialExpression += newCoefficient * variable1^power
288
        power +=1
289
    polynomialExpression += - variable2
290
    return (IP(polynomialExpression),
291
            leastCommonMultiple / 2^precision, # 2^K or N.
292
            leastCommonMultiple / 2 ^(targetHardnessToRound + 1), # tBound
293
            leastCommonMultiple)
294
        
295
# End slz_ratPoly_of_int_to_poly_for_coppersmith
296

    
297
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
298
                                          precision):
299
    """
300
    Makes a variable substitution into the input polynomial so that the output
301
    polynomial can take integer arguments.
302
    All variables of the input polynomial "have precision p". That is to say
303
    that they are rationals with denominator == 2^precision: x = y/2^precision
304
    We "incorporate" these denominators into the coefficients with, 
305
    respectively, the "right" power.
306
    """
307
    polynomialField = ratPolyOfRat.parent()
308
    polynomialVariable = rationalPolynomial.variables()[0]
309
    print "The polynomial field is:", polynomialField
310
    return \
311
        polynomialField(rationalPolynomial.subs({polynomialVariable : \
312
                                   polynomialVariable/2^(precision-1)}))
313
    
314
    # Return a tuple:
315
    # - the bivariate integer polynomial in (i,j);
316
    # - 2^K
317
# End slz_rat_poly_of_rat_to_rat_poly_of_int
318

    
319
print "sageSLZ loaded..."