Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (21,26 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_reduced_polynomials(reducedMatrix,
87
                                    knownMonomials,
88
                                    polynomialRing,
89
                                    var1Bound,
90
                                    var2Bound, ):
91
    """
92
    From a reduced matrix, holding the coefficients, from a monomials list,
93
    from the bounds of each variable, compute the corresponding polynomials
94
    scaled back by dividing by the "right" powers of the variables bounds.
95
    """
96
    # TODO: check input arguments.
97
    if len(knownMonomials) == 0:
98
        return []
99
    (var1, var2) = knownMonomials[0].variables()[0:2]
100
    print "Variable 1:", var1;
101
    print "Variable 2:", var2
102
    reducedPolynomials = []
103
    for matrixRow in reducedMatrix.rows():
104
        currentExpression = 0
105
        for colIndex in xrange(0, len(knownMonomials)):
106
            currentCoefficient = matrixRow[colIndex]
107
            print knownMonomials[colIndex]
108
            currentMonomialAsMvp = polynomialRing(knownMonomials[colIndex])
109
            print "Monomial as multivariate polynomial:", \
110
            currentMonomialAsMvp, type(currentMonomialAsMvp)
111
            degreeInVar1 = currentMonomialAsMvp.degree(var1)
112
            print "Degree in var", var1, ":", degreeInVar1
113
            degreeInVar2 = currentMonomialAsMvp.degree(var2)
114
            if degreeInVar1 != 0:
115
                currentCoefficient  /= (var1Bound^degreeInVar1)
116
            if degreeInVar2 != 0:
117
                currentCoefficient /= (var2Bound^degreeInVar2)
118
            print "Current Expression:", currentExpression
119
            currentExpression += currentCoefficient * \
120
                currentMonomialAsMvp
121
        reducedPolynomials.append(currentExpression)
122
    return reducedPolynomials 
123
# End slz_compute_reduced_polynomials
124

    
125
def slz_compute_scaled_function(functionSa, \
126
                                      lowerBoundSa, \
127
                                      upperBoundSa, \
128
                                      floatingPointPrecSa):
129
    """
130
    From a function, compute the scaled function whose domain
131
    is included in [1, 2) and whose image is also included in [1,2).
132
    Return a tuple: 
133
    [0]: the scaled function
134
    [1]: the scaled domain lower bound
135
    [2]: the scaled domain upper bound
136
    [3]: the scaled image lower bound
137
    [4]: the scaled image upper bound
138
    """
139
    x = functionSa.variables()[0]
140
    # Reassert f as a function (an not a mere expression).
141
    
142
    # Scalling the domain -> [1,2[.
143
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
144
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
145
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
146
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
147
    print "domainScalingExpression for argument :", domainScalingExpressionSa
148
    print "f: ", f
149
    ff = f.subs({x : domainScalingExpressionSa})
150
    #ff = f.subs_expr(x==domainScalingExpressionSa)
151
    domainScalingFunction(x) = invDomainScalingExpressionSa
152
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
153
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
154
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
155
    #
156
    # Scalling the image -> [1,2[.
157
    flbSa = f(lowerBoundSa).n()
158
    fubSa = f(upperBoundSa).n()
159
    if flbSa <= fubSa: # Increasing
160
        imageBinadeBottomSa = floor(flbSa.log2())
161
    else: # Decreasing
162
        imageBinadeBottomSa = floor(fubSa.log2())
163
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
164
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
165
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
166
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
167
    iis = invImageScalingExpressionSa.function(x)
168
    fff = iis.subs({x:ff})
169
    print "fff:", fff,
170
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
171
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
172
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
173

    
174
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
175
    # Create a polynomial over the rationals.
176
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
177
    return(polynomialRing(polyOfFloat))
178
# End slz_float_poly_of_float_to_rat_poly_of_rat.
179

    
180
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
181
                                      lowerBoundSa, 
182
                                      upperBoundSa, floatingPointPrecSa, 
183
                                      internalSollyaPrecSa, approxPrecSa):
184
    """
185
    Under the assumption that:
186
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
187
    - lowerBound and upperBound belong to the same binade.
188
    from a:
189
    - function;
190
    - a degree
191
    - a pair of bounds;
192
    - the floating-point precision we work on;
193
    - the internal Sollya precision;
194
    - the requested approximation error
195
    The initial interval is, possibly, splitted into smaller intervals.
196
    It return a list of tuples, each made of:
197
    - a first polynomial (without the changed variable f(x) = p(x-x0));
198
    - a second polynomial (with a changed variable f(x) = q(x))
199
    - the approximation interval;
200
    - the center, x0, of the interval;
201
    - the corresponding approximation error.
202
    """
203
    currentSollyaPrecSo = pobyso_get_prec_so()
204
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
205
    if internalSollyaPrecSa > currentSollyaPrecSa:
206
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
207
    x = functionSa.variables()[0] # Actual variable name can be anything.
208
    (fff, scaledLowerBoundSa, scaledUpperBoundSa, \
209
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) = \
210
                slz_compute_scaled_function(functionSa, \
211
                                            lowerBoundSa, \
212
                                            upperBoundSa, \
213
                                            floatingPointPrecSa)
214
    #
215
    resultArray = []
216
    #
217
    print "Approximation precision: ", RR(approxPrecSa)
218
    # Prepare the arguments for the Taylor expansion computation with Sollya.
219
    functionSo = pobyso_parse_string_sa_so(fff._assume_str())
220
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
221
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
222
                                                  scaledUpperBoundSa)
223
    # Compute the first Taylor expansion.
224
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
225
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
226
                                        scaledLowerBoundSa, scaledUpperBoundSa,
227
                                        approxPrecSa, internalSollyaPrecSa)
228
    if polySo is None:
229
        print "Aborting"
230
        return None
231
    # Change variable stuff
232
    changeVarExpressionSo = sollya_lib_build_function_sub(
233
                              sollya_lib_build_function_free_variable(), 
234
                              sollya_lib_copy_obj(intervalCenterSo))
235
    polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
236
    resultArray.append((polySo, polyVarChangedSo, boundsSo, intervalCenterSo,\
237
                         maxErrorSo))
238
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
239
                                              upperBoundSa.parent().precision()))
240
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
241
    # Compute the next upper bound.
242
    # If the error of approximation is more than half of the target,
243
    # use the same interval.
244
    # If it is less, increase it a bit.
245
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
246
    currentErrorRatio = approxPrecSa / errorSa
247
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
248
    if currentErrorRatio  < 2 :
249
        currentScaledUpperBoundSa += \
250
            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0])
251
    else:
252
        currentScaledUpperBoundSa += \
253
            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
254
                         * currentErrorRatio.log2() * 2
255
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
256
        currentScaledUpperBoundSa = scaledUpperBoundSa
257
    # Compute the other expansions.
258
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
259
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
260
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
261
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
262
                                            currentScaledLowerBoundSa, 
263
                                            currentScaledUpperBoundSa, 
264
                                            approxPrecSa, 
265
                                            internalSollyaPrecSa)
266
        # Change variable stuff
267
        changeVarExpressionSo = sollya_lib_build_function_sub(
268
                                  sollya_lib_build_function_free_variable(), 
269
                                  sollya_lib_copy_obj(intervalCenterSo))
270
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
271
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
272
                            intervalCenterSo, maxErrorSo))
273
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
274
        # Compute the next upper bound.
275
        # If the error of approximation is more than half of the target,
276
        # use the same interval.
277
        # If it is less, increase it a bit.
278
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
279
        currentErrorRatio = approxPrecSa / errorSa
280
        if currentErrorRatio  < RR('1.5') :
281
            currentScaledUpperBoundSa = \
282
                            boundsSa.endpoints()[1] + \
283
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0])
284
        elif currentErrorRatio < 2:
285
            currentScaledUpperBoundSa = \
286
                            boundsSa.endpoints()[1] + \
287
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
288
                             * currentErrorRatio.log2()
289
        else:
290
            currentScaledUpperBoundSa = \
291
                            boundsSa.endpoints()[1] + \
292
                            (boundsSa.endpoints()[1] - boundsSa.endpoints()[0]) \
293
                             * currentErrorRatio.log2() * 2
294
        # Test for insufficient precision.
295
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
296
            print "Can't shrink the interval anymore!"
297
            print "You should consider increasing the Sollya internal precision"
298
            print "or the polynomial degree."
299
            print "Giving up!"
300
            sollya_lib_clear_obj(functionSo)
301
            sollya_lib_clear_obj(degreeSo)
302
            sollya_lib_clear_obj(scaledBoundsSo)
303
            return None
304
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
305
            currentScaledUpperBoundSa = scaledUpperBoundSa
306
    sollya_lib_clear_obj(functionSo)
307
    sollya_lib_clear_obj(degreeSo)
308
    sollya_lib_clear_obj(scaledBoundsSo)
309
    if internalSollyaPrecSa > currentSollyaPrecSa:
310
        pobyso_set_prec_so_so(currentSollyaPrecSo)
311
    return(resultArray)
312
# End slz_get_intervals_and_polynomials
313

    
314

    
315
def slz_interval_scaling_expression(boundsInterval, expVar):
316
    """
317
    Compute the scaling expression to map an interval that span only
318
    a binade to [1, 2) and the inverse expression as well.
319
    Not very sure that the transformation makes sense for negative numbers.
320
    """
321
    # The scaling offset is only used for negative numbers.
322
    if abs(boundsInterval.endpoints()[0]) < 1:
323
        if boundsInterval.endpoints()[0] >= 0:
324
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
325
            invScalingCoeff = 1/scalingCoeff
326
            return((scalingCoeff * expVar, 
327
                    invScalingCoeff * expVar))
328
        else:
329
            scalingCoeff = \
330
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
331
            scalingOffset = -3 * scalingCoeff
332
            return((scalingCoeff * expVar + scalingOffset,
333
                    1/scalingCoeff * expVar + 3))
334
    else: 
335
        if boundsInterval.endpoints()[0] >= 0:
336
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
337
            scalingOffset = 0
338
            return((scalingCoeff * expVar, 
339
                    1/scalingCoeff * expVar))
340
        else:
341
            scalingCoeff = \
342
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
343
            scalingOffset =  -3 * scalingCoeff
344
            #scalingOffset = 0
345
            return((scalingCoeff * expVar + scalingOffset,
346
                    1/scalingCoeff * expVar + 3))
347

    
348
   
349
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
350
    """
351
    Compute the Sage version of the Taylor polynomial and it's
352
    companion data (interval, center...)
353
    The input parameter is a five elements tuple:
354
    - [0]: the polyomial (without variable change), as polynomial over a
355
           real ring;
356
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
357
           over a real ring;
358
    - [2]: the interval (as Sollya range);
359
    - [3]: the interval center;
360
    - [4]: the approximation error. 
361
    
362
    The function return a 5 elements tuple: formed with all the 
363
    input elements converted into their Sollya counterpart.
364
    """
365
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
366
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
367
    intervalSa = \
368
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
369
    centerSa = \
370
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
371
    errorSa = \
372
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
373
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
374
    # End slz_interval_and_polynomial_to_sage
375

    
376
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
377
                                                precision,
378
                                                targetHardnessToRound,
379
                                                variable1,
380
                                                variable2):
381
    """
382
    Creates a new multivariate polynomial with integer coefficients for use
383
     with the Coppersmith method.
384
    A the same time it computes :
385
    - 2^K (N);
386
    - 2^k (bound on the second variable)
387
    - lcm
388

    
389
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
390
                         variables.
391
    :param precision: the precision of the floating-point coefficients.
392
    :param targetHardnessToRound: the hardness to round we want to check.
393
    :param variable1: the first variable of the polynomial (an expression).
394
    :param variable2: the second variable of the polynomial (an expression).
395
    
396
    :returns: a 4 elements tuple:
397
                - the polynomial;
398
                - the modulus (N);
399
                - the t bound;
400
                - the lcm used to compute the integral coefficients and the 
401
                  module.
402
    """
403
    # Create a new integer polynomial ring.
404
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
405
    # Coefficients are issued in the increasing power order.
406
    ratPolyCoefficients = ratPolyOfInt.coefficients()
407
    # Print the reversed list for debugging.
408
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
409
    # Build the list of number we compute the lcm of.
410
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
411
    coefficientDenominators.append(2^precision)
412
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
413
    leastCommonMultiple = lcm(coefficientDenominators)
414
    # Compute the expression corresponding to the new polynomial
415
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
416
    #print coefficientNumerators
417
    polynomialExpression = 0
418
    power = 0
419
    # Iterate over two lists at the same time, stop when the shorter is
420
    # exhausted.
421
    for numerator, denominator in \
422
                        zip(coefficientNumerators, coefficientDenominators):
423
        multiplicator = leastCommonMultiple / denominator 
424
        newCoefficient = numerator * multiplicator
425
        polynomialExpression += newCoefficient * variable1^power
426
        power +=1
427
    polynomialExpression += - variable2
428
    return (IP(polynomialExpression),
429
            leastCommonMultiple / 2^precision, # 2^K or N.
430
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
431
            leastCommonMultiple) # If we want to make test computations.
432
        
433
# End slz_ratPoly_of_int_to_poly_for_coppersmith
434

    
435
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
436
                                          precision):
437
    """
438
    Makes a variable substitution into the input polynomial so that the output
439
    polynomial can take integer arguments.
440
    All variables of the input polynomial "have precision p". That is to say
441
    that they are rationals with denominator == 2^precision: x = y/2^precision
442
    We "incorporate" these denominators into the coefficients with, 
443
    respectively, the "right" power.
444
    """
445
    polynomialField = ratPolyOfRat.parent()
446
    polynomialVariable = ratPolyOfRat.variables()[0]
447
    #print "The polynomial field is:", polynomialField
448
    return \
449
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
450
                                   polynomialVariable/2^(precision-1)}))
451
    
452
    # Return a tuple:
453
    # - the bivariate integer polynomial in (i,j);
454
    # - 2^K
455
# End slz_rat_poly_of_rat_to_rat_poly_of_int
456

    
457
print "\t...sageSLZ loaded"