Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (43,61 ko)

1
r"""
2
Sage core functions needed for the implementation of SLZ.
3

    
4
AUTHORS:
5
- S.T. (2013-08): initial version
6

    
7
Examples:
8

    
9
TODO::
10
"""
11
print "sageSLZ loading..."
12
#
13
def slz_check_htr_value(function, htrValue, lowerBound, upperBound, precision, \
14
                        degree, targetHardnessToRound, alpha):
15
    """
16
    Check an Hard-to-round value.
17
    TODO::
18
    Full rewriting: this is hardly a draft.
19
    """
20
    polyApproxPrec = targetHardnessToRound + 1
21
    polyTargetHardnessToRound = targetHardnessToRound + 1
22
    internalSollyaPrec = ceil((RR('1.5') * targetHardnessToRound) / 64) * 64
23
    RRR = htrValue.parent()
24
    #
25
    ## Compute the scaled function.
26
    fff = slz_compute_scaled_function(f, lowerBound, upperBound, precision)[0]
27
    print "Scaled function:", fff
28
    #
29
    ## Compute the scaling.
30
    boundsIntervalRifSa = RealIntervalField(precision)
31
    domainBoundsInterval = boundsIntervalRifSa(lowerBound, upperBound)
32
    scalingExpressions = \
33
        slz_interval_scaling_expression(domainBoundsInterval, i)
34
    #
35
    ## Get the polynomials, bounds, etc. for all the interval.
36
    resultListOfTuplesOfSo = \
37
        slz_get_intervals_and_polynomials(f, degree, lowerBound, upperBound, \
38
                                          precision, internalSollyaPrec,\
39
                                          2^-(polyApproxPrec))
40
    #
41
    ## We only want one interval.
42
    if len(resultListOfTuplesOfSo) > 1:
43
        print "Too many intervals! Aborting!"
44
        exit
45
    #
46
    ## Get the first tuple of Sollya objects as Sage objects. 
47
    firstTupleSa = \
48
        slz_interval_and_polynomial_to_sage(resultListOfTuplesOfSo[0])
49
    pobyso_set_canonical_on()
50
    #
51
    print "Floatting point polynomial:", firstTupleSa[0]
52
    print "with coefficients precision:", firstTupleSa[0].base_ring().prec()
53
    #
54
    ## From a polynomial over a real ring, create a polynomial over the 
55
    #  rationals ring.
56
    rationalPolynomial = \
57
        slz_float_poly_of_float_to_rat_poly_of_rat(firstTupleSa[0])
58
    print "Rational polynomial:", rationalPolynomial
59
    #
60
    ## Create a polynomial over the rationals that will take integer 
61
    # variables instead of rational. 
62
    rationalPolynomialOfIntegers = \
63
        slz_rat_poly_of_rat_to_rat_poly_of_int(rationalPolynomial, precision)
64
    print "Type:", type(rationalPolynomialOfIntegers)
65
    print "Rational polynomial of integers:", rationalPolynomialOfIntegers
66
    #
67
    ## Check the rational polynomial of integers variables.
68
    # (check against the scaled function).
69
    toIntegerFactor = 2^(precision-1)
70
    intervalCenterAsIntegerSa = int(firstTupleSa[3] * toIntegerFactor)
71
    print "Interval center as integer:", intervalCenterAsIntegerSa
72
    lowerBoundAsIntegerSa = int(firstTupleSa[2].endpoints()[0] * \
73
                                toIntegerFactor) - intervalCenterAsIntegerSa
74
    upperBoundAsIntegerSa = int(firstTupleSa[2].endpoints()[1] * \
75
                                toIntegerFactor) - intervalCenterAsIntegerSa
76
    print "Lower bound as integer:", lowerBoundAsIntegerSa
77
    print "Upper bound as integer:", upperBoundAsIntegerSa
78
    print "Image of the lower bound by the scaled function", \
79
            fff(firstTupleSa[2].endpoints()[0])
80
    print "Image of the lower bound by the approximation polynomial of ints:", \
81
            RRR(rationalPolynomialOfIntegers(lowerBoundAsIntegerSa))
82
    print "Image of the center by the scaled function", fff(firstTupleSa[3])
83
    print "Image of the center by the approximation polynomial of ints:", \
84
            RRR(rationalPolynomialOfIntegers(0))
85
    print "Image of the upper bound by the scaled function", \
86
            fff(firstTupleSa[2].endpoints()[1])
87
    print "Image of the upper bound by the approximation polynomial of ints:", \
88
            RRR(rationalPolynomialOfIntegers(upperBoundAsIntegerSa))
89

    
90
# End slz_check_htr_value.
91

    
92
#
93
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
94
    """
95
    For given "real number", compute the bounds of the binade it belongs to.
96
    
97
    NOTE::
98
        When number >= 2^(emax+1), we return the "fake" binade 
99
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
100
        with interval [-infinity, -2^(emax+1)].
101
        
102
    """
103
    # Check the parameters.
104
    # RealNumbers only.
105
    try:
106
        classTree = [number.__class__] + number.mro()
107
        if not sage.rings.real_mpfr.RealNumber in classTree:
108
            return None
109
    except AttributeError:
110
        return None
111
    # Non zero negative integers only for emin.
112
    if emin >= 0 or int(emin) != emin:
113
        return None
114
    # Non zero positive integers only for emax.
115
    if emax <= 0 or int(emax) != emax:
116
        return None
117
    precision = number.precision()
118
    RF  = RealField(precision)
119
    # A more precise RealField is needed to avoid unwanted rounding effects
120
    # when computing number.log2().
121
    RRF = RealField(max(2048, 2 * precision))
122
    # number = 0 special case, the binade bounds are 
123
    # [0, 2^emin - 2^(emin-precision)]
124
    if number == 0:
125
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
126
    # Begin general case
127
    l2 = RRF(number).abs().log2()
128
    # Another special one: beyond largest representable -> "Fake" binade.
129
    if l2 >= emax + 1:
130
        if number > 0:
131
            return (RF(2^(emax+1)), RRR(+infinity) )
132
        else:
133
            return (RF(-infinity), -RF(2^(emax+1)))
134
    offset = int(l2)
135
    # number.abs() >= 1.
136
    if l2 >= 0:
137
        if number >= 0:
138
            lb = RF(2^offset)
139
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
140
        else: #number < 0
141
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
142
            ub = -RF(2^offset)
143
    else: # log2 < 0, number.abs() < 1.
144
        if l2 < emin: # Denormal
145
           # print "Denormal:", l2
146
            if number >= 0:
147
                lb = RF(0)
148
                ub = RF(2^(emin)) - RF(2^(emin-precision))
149
            else: # number <= 0
150
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
151
                ub = RF(0)
152
        elif l2 > emin: # Normal number other than +/-2^emin.
153
            if number >= 0:
154
                if int(l2) == l2:
155
                    lb = RF(2^(offset))
156
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
157
                else:
158
                    lb = RF(2^(offset-1))
159
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
160
            else: # number < 0
161
                if int(l2) == l2: # Binade limit.
162
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
163
                    ub = -RF(2^(offset))
164
                else:
165
                    lb = -RF(2^(offset) - 2^(-precision+offset))
166
                    ub = -RF(2^(offset-1))
167
        else: # l2== emin, number == +/-2^emin
168
            if number >= 0:
169
                lb = RF(2^(offset))
170
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
171
            else: # number < 0
172
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
173
                ub = -RF(2^(offset))
174
    return (lb, ub)
175
# End slz_compute_binade_bounds
176
#
177
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
178
                                                 alpha,
179
                                                 N,
180
                                                 iBound,
181
                                                 tBound):
182
    """
183
    For a given set of arguments (see below), compute a list
184
    of "reduced polynomials" that could be used to compute roots
185
    of the inputPolynomial.
186
    INPUT:
187
    
188
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
189
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
190
    - "N" -- the modulus;
191
    - "iBound" -- the bound on the first variable;
192
    - "tBound" -- the bound on the second variable.
193
    
194
    OUTPUT:
195
    
196
    A list of bivariate integer polynomial obtained using the Coppersmith
197
    algorithm. The polynomials correspond to the rows of the LLL-reduce
198
    reduced base that comply with the Coppersmith condition.
199
    
200
    TODO::
201
    Full rewrite, this is barely a draft.
202
    """
203
    # Arguments check.
204
    if iBound == 0 or tBound == 0:
205
        return ()
206
    # End arguments check.
207
    nAtAlpha = N^alpha
208
    ## Building polynomials for matrix.
209
    polyRing   = inputPolynomial.parent()
210
    # Whatever the 2 variables are actually called, we call them
211
    # 'i' and 't' in all the variable names.
212
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
213
    #print polyVars[0], type(polyVars[0])
214
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
215
                                              tVariable:tVariable * tBound})
216
    polynomialsList = \
217
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
218
                                             alpha,
219
                                             N,
220
                                             iBound,
221
                                             tBound,
222
                                             0)
223
    #print "Polynomials list:", polynomialsList
224
    ## Building the proto matrix.
225
    knownMonomials = []
226
    protoMatrix    = []
227
    for poly in polynomialsList:
228
        spo_add_polynomial_coeffs_to_matrix_row(poly,
229
                                                knownMonomials,
230
                                                protoMatrix,
231
                                                0)
232
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
233
    #print matrixToReduce
234
    ## Reduction and checking.
235
    reducedMatrix = matrixToReduce.LLL(fp='fp')
236
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
237
    if not isLLLReduced:
238
        return ()
239
    monomialsCount     = len(knownMonomials)
240
    monomialsCountSqrt = sqrt(monomialsCount)
241
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
242
    #print reducedMatrix
243
    ## Check the Coppersmith condition for each row and build the reduced 
244
    #  polynomials.
245
    ccReducedPolynomialsList = []
246
    for row in reducedMatrix.rows():
247
        l2Norm = row.norm(2)
248
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
249
            #print (l2Norm * monomialsCountSqrt).n()
250
            print l2Norm.n()
251
            ccReducedPolynomial = \
252
                slz_compute_reduced_polynomial(row,
253
                                               knownMonomials,
254
                                               iVariable,
255
                                               iBound,
256
                                               tVariable,
257
                                               tBound)
258
            if not ccReducedPolynomial is None:
259
                ccReducedPolynomialsList.append(ccReducedPolynomial)
260
        else:
261
            print l2Norm.n() , ">", nAtAlpha
262
            pass
263
    if len(ccReducedPolynomialsList) < 2:
264
        print "***Less than 2 Coppersmith condition compliant vectors.***"
265
        return ()
266
    return ccReducedPolynomialsList
267
# End slz_compute_coppersmith_reduced_polynomials
268

    
269
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
270
                                                 alpha,
271
                                                 N,
272
                                                 iBound,
273
                                                 tBound):
274
    """
275
    For a given set of arguments (see below), compute the polynomial modular 
276
    roots, if any.
277
    
278
    """
279
    # Arguments check.
280
    if iBound == 0 or tBound == 0:
281
        return set()
282
    # End arguments check.
283
    nAtAlpha = N^alpha
284
    ## Building polynomials for matrix.
285
    polyRing   = inputPolynomial.parent()
286
    # Whatever the 2 variables are actually called, we call them
287
    # 'i' and 't' in all the variable names.
288
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
289
    #print polyVars[0], type(polyVars[0])
290
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
291
                                              tVariable:tVariable * tBound})
292
    polynomialsList = \
293
        spo_polynomial_to_polynomials_list_5(initialPolynomial,
294
                                             alpha,
295
                                             N,
296
                                             iBound,
297
                                             tBound,
298
                                             0)
299
    #print "Polynomials list:", polynomialsList
300
    ## Building the proto matrix.
301
    knownMonomials = []
302
    protoMatrix    = []
303
    for poly in polynomialsList:
304
        spo_add_polynomial_coeffs_to_matrix_row(poly,
305
                                                knownMonomials,
306
                                                protoMatrix,
307
                                                0)
308
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
309
    #print matrixToReduce
310
    ## Reduction and checking.
311
    reducedMatrix = matrixToReduce.LLL(fp='fp')
312
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
313
    if not isLLLReduced:
314
        return set()
315
    monomialsCount     = len(knownMonomials)
316
    monomialsCountSqrt = sqrt(monomialsCount)
317
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
318
    #print reducedMatrix
319
    ## Check the Coppersmith condition for each row and build the reduced 
320
    #  polynomials.
321
    ccReducedPolynomialsList = []
322
    for row in reducedMatrix.rows():
323
        l2Norm = row.norm(2)
324
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
325
            #print (l2Norm * monomialsCountSqrt).n()
326
            #print l2Norm.n()
327
            ccReducedPolynomial = \
328
                slz_compute_reduced_polynomial(row,
329
                                               knownMonomials,
330
                                               iVariable,
331
                                               iBound,
332
                                               tVariable,
333
                                               tBound)
334
            if not ccReducedPolynomial is None:
335
                ccReducedPolynomialsList.append(ccReducedPolynomial)
336
        else:
337
            #print l2Norm.n() , ">", nAtAlpha
338
            pass
339
    if len(ccReducedPolynomialsList) < 2:
340
        print "Less than 2 Coppersmith condition compliant vectors."
341
        return set()
342
    #print ccReducedPolynomialsList
343
    ## Create the valid (poly1 and poly2 are algebraically independent) 
344
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
345
    # Try to mix and match all the polynomial pairs built from the 
346
    # ccReducedPolynomialsList to obtain non zero resultants.
347
    resultantsInITuplesList = []
348
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
349
        for polyInnerIndex in xrange(polyOuterIndex+1, 
350
                                     len(ccReducedPolynomialsList)):
351
            # Compute the resultant in resultants in the
352
            # first variable (is it the optimal choice?).
353
            resultantInI = \
354
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
355
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
356
            #print "Resultant", resultantInI
357
            # Test algebraic independence.
358
            if not resultantInI.is_zero():
359
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
360
                                                 ccReducedPolynomialsList[polyInnerIndex],
361
                                                 resultantInI))
362
    # If no non zero resultant was found: we can't get no algebraically 
363
    # independent polynomials pair. Give up!
364
    if len(resultantsInITuplesList) == 0:
365
        return set()
366
    #print resultantsInITuplesList
367
    # Compute the roots.
368
    Zi = ZZ[str(iVariable)]
369
    Zt = ZZ[str(tVariable)]
370
    polynomialRootsSet = set()
371
    # First, solve in the second variable since resultants are in the first
372
    # variable.
373
    for resultantInITuple in resultantsInITuplesList:
374
        tRootsList = Zt(resultantInITuple[2]).roots()
375
        # For each tRoot, compute the corresponding iRoots and check 
376
        # them in the input polynomial.
377
        for tRoot in tRootsList:
378
            #print "tRoot:", tRoot
379
            # Roots returned by root() are (value, multiplicity) tuples.
380
            iRootsList = \
381
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
382
            print iRootsList
383
            # The iRootsList can be empty, hence the test.
384
            if len(iRootsList) != 0:
385
                for iRoot in iRootsList:
386
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
387
                    # polyEvalModN must be an integer.
388
                    if polyEvalModN == int(polyEvalModN):
389
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
390
    return polynomialRootsSet
391
# End slz_compute_integer_polynomial_modular_roots.
392
#
393
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
394
                                        upperBoundSa, approxPrecSa, 
395
                                        sollyaPrecSa=None):
396
    """
397
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
398
    a polynomial that approximates the function on a an interval starting
399
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
400
    approximates with the expected precision.
401
    The interval upper bound is lowered until the expected approximation 
402
    precision is reached.
403
    The polynomial, the bounds, the center of the interval and the error
404
    are returned.
405
    OUTPU:
406
    A tuple made of 4 Sollya objects:
407
    - a polynomial;
408
    - an range (an interval, not in the sense of number given as an interval);
409
    - the center of the interval;
410
    - the maximum error in the approximation of the input functionSo by the
411
      output polynomial ; this error <= approxPrecSaS.
412
    
413
    """
414
    RRR = lowerBoundSa.parent()
415
    intervalShrinkConstFactorSa = RRR('0.5')
416
    absoluteErrorTypeSo = pobyso_absolute_so_so()
417
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
418
    currentUpperBoundSa = upperBoundSa
419
    currentLowerBoundSa = lowerBoundSa
420
    # What we want here is the polynomial without the variable change, 
421
    # since our actual variable will be x-intervalCenter defined over the 
422
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
423
    (polySo, intervalCenterSo, maxErrorSo) = \
424
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
425
                                                    currentRangeSo, 
426
                                                    absoluteErrorTypeSo)
427
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
428
    while maxErrorSa > approxPrecSa:
429
        #print "++Approximation error:", maxErrorSa
430
        sollya_lib_clear_obj(polySo)
431
        sollya_lib_clear_obj(intervalCenterSo)
432
        sollya_lib_clear_obj(maxErrorSo)
433
        shrinkFactorSa = RRR('5')/(maxErrorSa/approxPrecSa).log2().abs()
434
        #shrinkFactorSa = 1.5/(maxErrorSa/approxPrecSa)
435
        #errorRatioSa = approxPrecSa/maxErrorSa
436
        #print "Error ratio: ", errorRatioSa
437
        if shrinkFactorSa > intervalShrinkConstFactorSa:
438
            actualShrinkFactorSa = intervalShrinkConstFactorSa
439
            #print "Fixed"
440
        else:
441
            actualShrinkFactorSa = shrinkFactorSa
442
            #print "Computed",shrinkFactorSa,maxErrorSa
443
            #print shrinkFactorSa, maxErrorSa
444
        #print "Shrink factor", actualShrinkFactorSa
445
        currentUpperBoundSa = currentLowerBoundSa + \
446
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
447
                                   actualShrinkFactorSa
448
        #print "Current upper bound:", currentUpperBoundSa
449
        sollya_lib_clear_obj(currentRangeSo)
450
        if currentUpperBoundSa <= currentLowerBoundSa or \
451
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
452
            sollya_lib_clear_obj(absoluteErrorTypeSo)
453
            print "Can't find an interval."
454
            print "Use either or both a higher polynomial degree or a higher",
455
            print "internal precision."
456
            print "Aborting!"
457
            return (None, None, None, None)
458
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
459
                                                      currentUpperBoundSa)
460
        # print "New interval:",
461
        # pobyso_autoprint(currentRangeSo)
462
        #print "Second Taylor expansion call."
463
        (polySo, intervalCenterSo, maxErrorSo) = \
464
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
465
                                                        currentRangeSo, 
466
                                                        absoluteErrorTypeSo)
467
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
468
        #print "Max errorSo:",
469
        #pobyso_autoprint(maxErrorSo)
470
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
471
        #print "Max errorSa:", maxErrorSa
472
        #print "Sollya prec:",
473
        #pobyso_autoprint(sollya_lib_get_prec(None))
474
    sollya_lib_clear_obj(absoluteErrorTypeSo)
475
    return((polySo, currentRangeSo, intervalCenterSo, maxErrorSo))
476
# End slz_compute_polynomial_and_interval
477

    
478
def slz_compute_reduced_polynomial(matrixRow,
479
                                    knownMonomials,
480
                                    var1,
481
                                    var1Bound,
482
                                    var2,
483
                                    var2Bound):
484
    """
485
    Compute a polynomial from a reduced matrix row.
486
    This function was introduced in order to avoid the computation of the
487
    polynomials (even those built from rows that do no verify the Coppersmith
488
    condition.
489
    """
490
    ## Check arguments.
491
    if len(knownMonomials) == 0:
492
        return None
493
    # varNounds can be zero since 0^0 returns 1.
494
    if (var1Bound < 0) or (var2Bound < 0):
495
        return None
496
    ## Initialisations. 
497
    polynomialRing = knownMonomials[0].parent() 
498
    currentPolynomial = polynomialRing(0)
499
    # TODO: use zip instead of indices.
500
    for colIndex in xrange(0, len(knownMonomials)):
501
        currentCoefficient = matrixRow[colIndex]
502
        if currentCoefficient != 0:
503
            #print "Current coefficient:", currentCoefficient
504
            currentMonomial = knownMonomials[colIndex]
505
            #print "Monomial as multivariate polynomial:", \
506
            #currentMonomial, type(currentMonomial)
507
            degreeInVar1 = currentMonomial.degree(var1)
508
            #print "Degree in var1", var1, ":", degreeInVar1
509
            degreeInVar2 = currentMonomial.degree(var2)
510
            #print "Degree in var2", var2, ":", degreeInVar2
511
            if degreeInVar1 > 0:
512
                currentCoefficient = \
513
                    currentCoefficient / (var1Bound^degreeInVar1)
514
                #print "varBound1 in degree:", var1Bound^degreeInVar1
515
                #print "Current coefficient(1)", currentCoefficient
516
            if degreeInVar2 > 0:
517
                currentCoefficient = \
518
                    currentCoefficient / (var2Bound^degreeInVar2)
519
                #print "Current coefficient(2)", currentCoefficient
520
            #print "Current reduced monomial:", (currentCoefficient * \
521
            #                                    currentMonomial)
522
            currentPolynomial += (currentCoefficient * currentMonomial)
523
            #print "Current polynomial:", currentPolynomial
524
        # End if
525
    # End for colIndex.
526
    #print "Type of the current polynomial:", type(currentPolynomial)
527
    return(currentPolynomial)
528
# End slz_compute_reduced_polynomial
529
#
530
def slz_compute_reduced_polynomials(reducedMatrix,
531
                                        knownMonomials,
532
                                        var1,
533
                                        var1Bound,
534
                                        var2,
535
                                        var2Bound):
536
    """
537
    Legacy function, use slz_compute_reduced_polynomials_list
538
    """
539
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
540
                                                knownMonomials,
541
                                                var1,
542
                                                var1Bound,
543
                                                var2,
544
                                                var2Bound)
545
    )
546
def slz_compute_reduced_polynomials_list(reducedMatrix,
547
                                        knownMonomials,
548
                                        var1,
549
                                        var1Bound,
550
                                        var2,
551
                                        var2Bound):
552
    """
553
    From a reduced matrix, holding the coefficients, from a monomials list,
554
    from the bounds of each variable, compute the corresponding polynomials
555
    scaled back by dividing by the "right" powers of the variables bounds.
556
    
557
    The elements in knownMonomials must be of the "right" polynomial type.
558
    They set the polynomial type of the output polynomials list.
559
    """
560
    
561
    # TODO: check input arguments.
562
    reducedPolynomials = []
563
    #print "type var1:", type(var1), " - type var2:", type(var2)
564
    for matrixRow in reducedMatrix.rows():
565
        currentPolynomial = 0
566
        for colIndex in xrange(0, len(knownMonomials)):
567
            currentCoefficient = matrixRow[colIndex]
568
            if currentCoefficient != 0:
569
                #print "Current coefficient:", currentCoefficient
570
                currentMonomial = knownMonomials[colIndex]
571
                parentRing = currentMonomial.parent()
572
                #print "Monomial as multivariate polynomial:", \
573
                #currentMonomial, type(currentMonomial)
574
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
575
                #print "Degree in var", var1, ":", degreeInVar1
576
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
577
                #print "Degree in var", var2, ":", degreeInVar2
578
                if degreeInVar1 > 0:
579
                    currentCoefficient = \
580
                        currentCoefficient / var1Bound^degreeInVar1
581
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
582
                    #print "Current coefficient(1)", currentCoefficient
583
                if degreeInVar2 > 0:
584
                    currentCoefficient = \
585
                        currentCoefficient / var2Bound^degreeInVar2
586
                    #print "Current coefficient(2)", currentCoefficient
587
                #print "Current reduced monomial:", (currentCoefficient * \
588
                #                                    currentMonomial)
589
                currentPolynomial += (currentCoefficient * currentMonomial)
590
                #print "Current polynomial:", currentPolynomial
591
            # End if
592
        # End for colIndex.
593
        #print "Type of the current polynomial:", type(currentPolynomial)
594
        reducedPolynomials.append(currentPolynomial)
595
    return reducedPolynomials 
596
# End slz_compute_reduced_polynomials.
597

    
598
def slz_compute_scaled_function(functionSa,
599
                                lowerBoundSa,
600
                                upperBoundSa,
601
                                floatingPointPrecSa):
602
    """
603
    From a function, compute the scaled function whose domain
604
    is included in [1, 2) and whose image is also included in [1,2).
605
    Return a tuple: 
606
    [0]: the scaled function
607
    [1]: the scaled domain lower bound
608
    [2]: the scaled domain upper bound
609
    [3]: the scaled image lower bound
610
    [4]: the scaled image upper bound
611
    """
612
    x = functionSa.variables()[0]
613
    # Reassert f as a function (an not a mere expression).
614
    
615
    # Scalling the domain -> [1,2[.
616
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
617
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
618
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
619
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
620
    print "domainScalingExpression for argument :", domainScalingExpressionSa
621
    print "f: ", f
622
    ff = f.subs({x : domainScalingExpressionSa})
623
    #ff = f.subs_expr(x==domainScalingExpressionSa)
624
    domainScalingFunction(x) = invDomainScalingExpressionSa
625
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
626
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
627
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
628
    #
629
    # Scalling the image -> [1,2[.
630
    flbSa = f(lowerBoundSa).n()
631
    fubSa = f(upperBoundSa).n()
632
    if flbSa <= fubSa: # Increasing
633
        imageBinadeBottomSa = floor(flbSa.log2())
634
    else: # Decreasing
635
        imageBinadeBottomSa = floor(fubSa.log2())
636
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
637
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
638
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
639
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
640
    iis = invImageScalingExpressionSa.function(x)
641
    fff = iis.subs({x:ff})
642
    print "fff:", fff,
643
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
644
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
645
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
646

    
647
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
648
    # Create a polynomial over the rationals.
649
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
650
    return(polynomialRing(polyOfFloat))
651
# End slz_float_poly_of_float_to_rat_poly_of_rat.
652

    
653
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
654
                                      lowerBoundSa, 
655
                                      upperBoundSa, floatingPointPrecSa, 
656
                                      internalSollyaPrecSa, approxPrecSa):
657
    """
658
    Under the assumption that:
659
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
660
    - lowerBound and upperBound belong to the same binade.
661
    from a:
662
    - function;
663
    - a degree
664
    - a pair of bounds;
665
    - the floating-point precision we work on;
666
    - the internal Sollya precision;
667
    - the requested approximation error
668
    The initial interval is, possibly, splitted into smaller intervals.
669
    It return a list of tuples, each made of:
670
    - a first polynomial (without the changed variable f(x) = p(x-x0));
671
    - a second polynomial (with a changed variable f(x) = q(x))
672
    - the approximation interval;
673
    - the center, x0, of the interval;
674
    - the corresponding approximation error.
675
    TODO: fix endless looping for some parameters sets.
676
    """
677
    resultArray = []
678
    # Set Sollya to the necessary internal precision.
679
    precChangedSa = False
680
    currentSollyaPrecSo = pobyso_get_prec_so()
681
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
682
    if internalSollyaPrecSa > currentSollyaPrecSa:
683
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
684
        precChangedSa = True
685
    #
686
    x = functionSa.variables()[0] # Actual variable name can be anything.
687
    # Scaled function: [1=,2] -> [1,2].
688
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
689
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
690
                slz_compute_scaled_function(functionSa,                       \
691
                                            lowerBoundSa,                     \
692
                                            upperBoundSa,                     \
693
                                            floatingPointPrecSa)
694
    #
695
    print "Approximation precision: ", RR(approxPrecSa)
696
    # Prepare the arguments for the Taylor expansion computation with Sollya.
697
    functionSo = pobyso_parse_string_sa_so(fff._assume_str())
698
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
699
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
700
                                                  scaledUpperBoundSa)
701
    # Compute the first Taylor expansion.
702
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
703
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
704
                                        scaledLowerBoundSa, scaledUpperBoundSa,
705
                                        approxPrecSa, internalSollyaPrecSa)
706
    if polySo is None:
707
        print "slz_get_intervals_and_polynomials: Aborting and returning None!"
708
        if precChangedSa:
709
            pobyso_set_prec_so_so(currentSollyaPrecSo)
710
            sollya_lib_clear_obj(currentSollyaPrecSo)
711
        sollya_lib_clear_obj(functionSo)
712
        sollya_lib_clear_obj(degreeSo)
713
        sollya_lib_clear_obj(scaledBoundsSo)
714
        return None
715
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
716
                                              upperBoundSa.parent().precision()))
717
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
718
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
719
    #print "First approximation error:", errorSa
720
    # If the error and interval are OK a the first try, just return.
721
    if boundsSa.endpoints()[1] >= scaledUpperBoundSa:
722
        # Change variable stuff in Sollya x -> x0-x.
723
        changeVarExpressionSo = sollya_lib_build_function_sub( \
724
                              sollya_lib_build_function_free_variable(), \
725
                              sollya_lib_copy_obj(intervalCenterSo))
726
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
727
        sollya_lib_clear_obj(changeVarExpressionSo)
728
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
729
                            intervalCenterSo, maxErrorSo))
730
        if internalSollyaPrecSa != currentSollyaPrecSa:
731
            pobyso_set_prec_sa_so(currentSollyaPrecSa)
732
        sollya_lib_clear_obj(currentSollyaPrecSo)
733
        sollya_lib_clear_obj(functionSo)
734
        sollya_lib_clear_obj(degreeSo)
735
        sollya_lib_clear_obj(scaledBoundsSo)
736
        #print "Approximation error:", errorSa
737
        return resultArray
738
    # The returned interval upper bound does not reach the requested upper
739
    # upper bound: compute the next upper bound.
740
    # The following ratio is always >= 1
741
    currentErrorRatio = approxPrecSa / errorSa
742
    # Starting point for the next upper bound.
743
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
744
    boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
745
    # Compute the increment. 
746
    if currentErrorRatio > RR('1000'): # ]1.5, infinity[
747
        currentScaledUpperBoundSa += \
748
                        currentErrorRatio * boundsWidthSa * 2
749
    else:  # [1, 1.5]
750
        currentScaledUpperBoundSa += \
751
                        (RR('1.0') + currentErrorRatio.log() / 500) * boundsWidthSa
752
    # Take into account the original interval upper bound.                     
753
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
754
        currentScaledUpperBoundSa = scaledUpperBoundSa
755
    # Compute the other expansions.
756
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
757
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
758
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
759
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
760
                                            currentScaledLowerBoundSa, 
761
                                            currentScaledUpperBoundSa, 
762
                                            approxPrecSa, 
763
                                            internalSollyaPrecSa)
764
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
765
        if  errorSa < approxPrecSa:
766
            # Change variable stuff
767
            #print "Approximation error:", errorSa
768
            changeVarExpressionSo = sollya_lib_build_function_sub(
769
                                    sollya_lib_build_function_free_variable(), 
770
                                    sollya_lib_copy_obj(intervalCenterSo))
771
            polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
772
            sollya_lib_clear_obj(changeVarExpressionSo)
773
            resultArray.append((polySo, polyVarChangedSo, boundsSo, \
774
                                intervalCenterSo, maxErrorSo))
775
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
776
        # Compute the next upper bound.
777
        # The following ratio is always >= 1
778
        currentErrorRatio = approxPrecSa / errorSa
779
        # Starting point for the next upper bound.
780
        currentScaledUpperBoundSa = boundsSa.endpoints()[1]
781
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
782
        # Compute the increment. 
783
        if currentErrorRatio > RR('1000'): # ]1.5, infinity[
784
            currentScaledUpperBoundSa += \
785
                            currentErrorRatio * boundsWidthSa * 2
786
        else:  # [1, 1.5]
787
            currentScaledUpperBoundSa += \
788
                            (RR('1.0') + currentErrorRatio.log()/500) * boundsWidthSa
789
        #print "currentErrorRatio:", currentErrorRatio
790
        #print "currentScaledUpperBoundSa", currentScaledUpperBoundSa
791
        # Test for insufficient precision.
792
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
793
            print "Can't shrink the interval anymore!"
794
            print "You should consider increasing the Sollya internal precision"
795
            print "or the polynomial degree."
796
            print "Giving up!"
797
            if internalSollyaPrecSa != currentSollyaPrecSa:
798
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
799
            sollya_lib_clear_obj(currentSollyaPrecSo)
800
            sollya_lib_clear_obj(functionSo)
801
            sollya_lib_clear_obj(degreeSo)
802
            sollya_lib_clear_obj(scaledBoundsSo)
803
            return None
804
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
805
            currentScaledUpperBoundSa = scaledUpperBoundSa
806
    if internalSollyaPrecSa > currentSollyaPrecSa:
807
        pobyso_set_prec_so_so(currentSollyaPrecSo)
808
    sollya_lib_clear_obj(currentSollyaPrecSo)
809
    sollya_lib_clear_obj(functionSo)
810
    sollya_lib_clear_obj(degreeSo)
811
    sollya_lib_clear_obj(scaledBoundsSo)
812
    return(resultArray)
813
# End slz_get_intervals_and_polynomials
814

    
815

    
816
def slz_interval_scaling_expression(boundsInterval, expVar):
817
    """
818
    Compute the scaling expression to map an interval that span at most
819
    a single binade to [1, 2) and the inverse expression as well.
820
    Not very sure that the transformation makes sense for negative numbers.
821
    """
822
    # The scaling offset is only used for negative numbers.
823
    if abs(boundsInterval.endpoints()[0]) < 1:
824
        if boundsInterval.endpoints()[0] >= 0:
825
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
826
            invScalingCoeff = 1/scalingCoeff
827
            return((scalingCoeff * expVar, 
828
                    invScalingCoeff * expVar))
829
        else:
830
            scalingCoeff = \
831
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
832
            scalingOffset = -3 * scalingCoeff
833
            return((scalingCoeff * expVar + scalingOffset,
834
                    1/scalingCoeff * expVar + 3))
835
    else: 
836
        if boundsInterval.endpoints()[0] >= 0:
837
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
838
            scalingOffset = 0
839
            return((scalingCoeff * expVar, 
840
                    1/scalingCoeff * expVar))
841
        else:
842
            scalingCoeff = \
843
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
844
            scalingOffset =  -3 * scalingCoeff
845
            #scalingOffset = 0
846
            return((scalingCoeff * expVar + scalingOffset,
847
                    1/scalingCoeff * expVar + 3))
848

    
849
   
850
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
851
    """
852
    Compute the Sage version of the Taylor polynomial and it's
853
    companion data (interval, center...)
854
    The input parameter is a five elements tuple:
855
    - [0]: the polyomial (without variable change), as polynomial over a
856
           real ring;
857
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
858
           over a real ring;
859
    - [2]: the interval (as Sollya range);
860
    - [3]: the interval center;
861
    - [4]: the approximation error. 
862
    
863
    The function return a 5 elements tuple: formed with all the 
864
    input elements converted into their Sollya counterpart.
865
    """
866
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
867
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
868
    intervalSa = \
869
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
870
    centerSa = \
871
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
872
    errorSa = \
873
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
874
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
875
    # End slz_interval_and_polynomial_to_sage
876

    
877
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
878
                                                precision,
879
                                                targetHardnessToRound,
880
                                                variable1,
881
                                                variable2):
882
    """
883
    Creates a new multivariate polynomial with integer coefficients for use
884
     with the Coppersmith method.
885
    A the same time it computes :
886
    - 2^K (N);
887
    - 2^k (bound on the second variable)
888
    - lcm
889

    
890
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
891
                         variables.
892
    :param precision: the precision of the floating-point coefficients.
893
    :param targetHardnessToRound: the hardness to round we want to check.
894
    :param variable1: the first variable of the polynomial (an expression).
895
    :param variable2: the second variable of the polynomial (an expression).
896
    
897
    :returns: a 4 elements tuple:
898
                - the polynomial;
899
                - the modulus (N);
900
                - the t bound;
901
                - the lcm used to compute the integral coefficients and the 
902
                  module.
903
    """
904
    # Create a new integer polynomial ring.
905
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
906
    # Coefficients are issued in the increasing power order.
907
    ratPolyCoefficients = ratPolyOfInt.coefficients()
908
    # Print the reversed list for debugging.
909
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
910
    # Build the list of number we compute the lcm of.
911
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
912
    coefficientDenominators.append(2^precision)
913
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
914
    leastCommonMultiple = lcm(coefficientDenominators)
915
    # Compute the expression corresponding to the new polynomial
916
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
917
    #print coefficientNumerators
918
    polynomialExpression = 0
919
    power = 0
920
    # Iterate over two lists at the same time, stop when the shorter is
921
    # exhausted.
922
    for numerator, denominator in \
923
                        zip(coefficientNumerators, coefficientDenominators):
924
        multiplicator = leastCommonMultiple / denominator 
925
        newCoefficient = numerator * multiplicator
926
        polynomialExpression += newCoefficient * variable1^power
927
        power +=1
928
    polynomialExpression += - variable2
929
    return (IP(polynomialExpression),
930
            leastCommonMultiple / 2^precision, # 2^K or N.
931
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
932
            leastCommonMultiple) # If we want to make test computations.
933
        
934
# End slz_ratPoly_of_int_to_poly_for_coppersmith
935

    
936
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
937
                                          precision):
938
    """
939
    Makes a variable substitution into the input polynomial so that the output
940
    polynomial can take integer arguments.
941
    All variables of the input polynomial "have precision p". That is to say
942
    that they are rationals with denominator == 2^(precision - 1): 
943
    x = y/2^(precision - 1).
944
    We "incorporate" these denominators into the coefficients with, 
945
    respectively, the "right" power.
946
    """
947
    polynomialField = ratPolyOfRat.parent()
948
    polynomialVariable = ratPolyOfRat.variables()[0]
949
    #print "The polynomial field is:", polynomialField
950
    return \
951
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
952
                                   polynomialVariable/2^(precision-1)}))
953
    
954
    # Return a tuple:
955
    # - the bivariate integer polynomial in (i,j);
956
    # - 2^K
957
# End slz_rat_poly_of_rat_to_rat_poly_of_int
958

    
959

    
960
print "\t...sageSLZ loaded"
961
print