Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (21,69 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
                                    var1Bound,
89
                                    var2Bound):
90
    """
91
    From a reduced matrix, holding the coefficients, from a monomials list,
92
    from the bounds of each variable, compute the corresponding polynomials
93
    scaled back by dividing by the "right" powers of the variables bounds.
94
    
95
    The elements in knownMonomials must be of the "right" polynomial type.
96
    """
97
    
98
    # TODO: check input arguments.
99
    if len(knownMonomials) == 0:
100
        return []
101
    # Search in knowMonomials until we find a bivariate one, otherwise
102
    # the call to variables does not give the expected result.
103
    monomialIndex = 1
104
    while len(knownMonomials[monomialIndex].variables()) != 2 :
105
        monomialIndex +=1
106
    (var1, var2) = knownMonomials[monomialIndex].variables()[0:2]
107
    #print "Variable 1:", var1
108
    #print "Variable 2:", var2
109
    reducedPolynomials = []
110
    currentPolynomial = 0
111
    for matrixRow in reducedMatrix.rows():
112
        for colIndex in xrange(0, len(knownMonomials)):
113
            currentCoefficient = matrixRow[colIndex]
114
            #print knownMonomials[colIndex]
115
            currentMonomial = knownMonomials[colIndex]
116
            #print "Monomial as multivariate polynomial:", \
117
            currentMonomial, type(currentMonomial)
118
            degreeInVar1 = currentMonomial.degree(var1)
119
            #print "Degree in var", var1, ":", degreeInVar1
120
            degreeInVar2 = currentMonomial.degree(var2)
121
            #print "Degree in var", var2, ":", degreeInVar2
122
            if degreeInVar1 != 0:
123
                currentCoefficient  /= (var1Bound^degreeInVar1)
124
            if degreeInVar2 != 0:
125
                currentCoefficient /= (var2Bound^degreeInVar2)
126
            #print "Current reduced monomial:", (currentCoefficient * \
127
            #                                    currentMonomial)
128
            currentPolynomial += (currentCoefficient * currentMonomial)
129
        #print "Type of the current polynomial:", type(currentPolynomial)
130
        reducedPolynomials.append(currentPolynomial)
131
    return reducedPolynomials 
132
# End slz_compute_reduced_polynomials.
133

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

    
183
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
184
    # Create a polynomial over the rationals.
185
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
186
    return(polynomialRing(polyOfFloat))
187
# End slz_float_poly_of_float_to_rat_poly_of_rat.
188

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

    
323

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

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

    
385
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
386
                                                precision,
387
                                                targetHardnessToRound,
388
                                                variable1,
389
                                                variable2):
390
    """
391
    Creates a new multivariate polynomial with integer coefficients for use
392
     with the Coppersmith method.
393
    A the same time it computes :
394
    - 2^K (N);
395
    - 2^k (bound on the second variable)
396
    - lcm
397

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

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

    
466
print "\t...sageSLZ loaded"