Statistiques
| Révision :

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

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

    
88
# End slz_check_htr_value.
89

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

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

    
448
def slz_compute_reduced_polynomial(matrixRow,
449
                                    knownMonomials,
450
                                    var1,
451
                                    var1Bound,
452
                                    var2,
453
                                    var2Bound):
454
    """
455
    Compute a polynomial from a reduced matrix row.
456
    This function was introduced in order to avoid the computation of the
457
    polynomials (even those built from rows that do no verify the Coppersmith
458
    condition.
459
    """
460
    ## Check arguments.
461
    if len(knownMonomials) == 0:
462
        return None
463
    # varNounds can be zero since 0^0 returns 1.
464
    if (var1Bound < 0) or (var2Bound < 0):
465
        return None
466
    ## Initialisations. 
467
    polynomialRing = knownMonomials[0].parent() 
468
    currentPolynomial = polynomialRing(0)
469
    # TODO: use zip instead of indices.
470
    for colIndex in xrange(0, len(knownMonomials)):
471
        currentCoefficient = matrixRow[colIndex]
472
        if currentCoefficient != 0:
473
            #print "Current coefficient:", currentCoefficient
474
            currentMonomial = knownMonomials[colIndex]
475
            #print "Monomial as multivariate polynomial:", \
476
            #currentMonomial, type(currentMonomial)
477
            degreeInVar1 = currentMonomial.degree(var1)
478
            #print "Degree in var1", var1, ":", degreeInVar1
479
            degreeInVar2 = currentMonomial.degree(var2)
480
            #print "Degree in var2", var2, ":", degreeInVar2
481
            if degreeInVar1 > 0:
482
                currentCoefficient = \
483
                    currentCoefficient / (var1Bound^degreeInVar1)
484
                #print "varBound1 in degree:", var1Bound^degreeInVar1
485
                #print "Current coefficient(1)", currentCoefficient
486
            if degreeInVar2 > 0:
487
                currentCoefficient = \
488
                    currentCoefficient / (var2Bound^degreeInVar2)
489
                #print "Current coefficient(2)", currentCoefficient
490
            #print "Current reduced monomial:", (currentCoefficient * \
491
            #                                    currentMonomial)
492
            currentPolynomial += (currentCoefficient * currentMonomial)
493
            #print "Current polynomial:", currentPolynomial
494
        # End if
495
    # End for colIndex.
496
    #print "Type of the current polynomial:", type(currentPolynomial)
497
    return(currentPolynomial)
498
# End slz_compute_reduced_polynomial
499
#
500
def slz_compute_reduced_polynomials(reducedMatrix,
501
                                        knownMonomials,
502
                                        var1,
503
                                        var1Bound,
504
                                        var2,
505
                                        var2Bound):
506
    """
507
    Legacy function, use slz_compute_reduced_polynomials_list
508
    """
509
    return(slz_compute_reduced_polynomials_list(reducedMatrix,
510
                                                knownMonomials,
511
                                                var1,
512
                                                var1Bound,
513
                                                var2,
514
                                                var2Bound)
515
    )
516
def slz_compute_reduced_polynomials_list(reducedMatrix,
517
                                        knownMonomials,
518
                                        var1,
519
                                        var1Bound,
520
                                        var2,
521
                                        var2Bound):
522
    """
523
    From a reduced matrix, holding the coefficients, from a monomials list,
524
    from the bounds of each variable, compute the corresponding polynomials
525
    scaled back by dividing by the "right" powers of the variables bounds.
526
    
527
    The elements in knownMonomials must be of the "right" polynomial type.
528
    They set the polynomial type of the output polynomials list.
529
    """
530
    
531
    # TODO: check input arguments.
532
    reducedPolynomials = []
533
    #print "type var1:", type(var1), " - type var2:", type(var2)
534
    for matrixRow in reducedMatrix.rows():
535
        currentPolynomial = 0
536
        for colIndex in xrange(0, len(knownMonomials)):
537
            currentCoefficient = matrixRow[colIndex]
538
            if currentCoefficient != 0:
539
                #print "Current coefficient:", currentCoefficient
540
                currentMonomial = knownMonomials[colIndex]
541
                parentRing = currentMonomial.parent()
542
                #print "Monomial as multivariate polynomial:", \
543
                #currentMonomial, type(currentMonomial)
544
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
545
                #print "Degree in var", var1, ":", degreeInVar1
546
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
547
                #print "Degree in var", var2, ":", degreeInVar2
548
                if degreeInVar1 > 0:
549
                    currentCoefficient = \
550
                        currentCoefficient / var1Bound^degreeInVar1
551
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
552
                    #print "Current coefficient(1)", currentCoefficient
553
                if degreeInVar2 > 0:
554
                    currentCoefficient = \
555
                        currentCoefficient / var2Bound^degreeInVar2
556
                    #print "Current coefficient(2)", currentCoefficient
557
                #print "Current reduced monomial:", (currentCoefficient * \
558
                #                                    currentMonomial)
559
                currentPolynomial += (currentCoefficient * currentMonomial)
560
                #print "Current polynomial:", currentPolynomial
561
            # End if
562
        # End for colIndex.
563
        #print "Type of the current polynomial:", type(currentPolynomial)
564
        reducedPolynomials.append(currentPolynomial)
565
    return reducedPolynomials 
566
# End slz_compute_reduced_polynomials.
567

    
568
def slz_compute_scaled_function(functionSa,
569
                                lowerBoundSa,
570
                                upperBoundSa,
571
                                floatingPointPrecSa):
572
    """
573
    From a function, compute the scaled function whose domain
574
    is included in [1, 2) and whose image is also included in [1,2).
575
    Return a tuple: 
576
    [0]: the scaled function
577
    [1]: the scaled domain lower bound
578
    [2]: the scaled domain upper bound
579
    [3]: the scaled image lower bound
580
    [4]: the scaled image upper bound
581
    """
582
    x = functionSa.variables()[0]
583
    # Reassert f as a function (an not a mere expression).
584
    
585
    # Scalling the domain -> [1,2[.
586
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
587
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
588
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
589
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
590
    print "domainScalingExpression for argument :", domainScalingExpressionSa
591
    print "f: ", f
592
    ff = f.subs({x : domainScalingExpressionSa})
593
    #ff = f.subs_expr(x==domainScalingExpressionSa)
594
    domainScalingFunction(x) = invDomainScalingExpressionSa
595
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
596
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
597
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
598
    #
599
    # Scalling the image -> [1,2[.
600
    flbSa = f(lowerBoundSa).n()
601
    fubSa = f(upperBoundSa).n()
602
    if flbSa <= fubSa: # Increasing
603
        imageBinadeBottomSa = floor(flbSa.log2())
604
    else: # Decreasing
605
        imageBinadeBottomSa = floor(fubSa.log2())
606
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
607
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
608
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
609
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
610
    iis = invImageScalingExpressionSa.function(x)
611
    fff = iis.subs({x:ff})
612
    print "fff:", fff,
613
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
614
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
615
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
616

    
617
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
618
    # Create a polynomial over the rationals.
619
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
620
    return(polynomialRing(polyOfFloat))
621
# End slz_float_poly_of_float_to_rat_poly_of_rat.
622

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

    
785

    
786
def slz_interval_scaling_expression(boundsInterval, expVar):
787
    """
788
    Compute the scaling expression to map an interval that span at most
789
    a single binade to [1, 2) and the inverse expression as well.
790
    Not very sure that the transformation makes sense for negative numbers.
791
    """
792
    # The scaling offset is only used for negative numbers.
793
    if abs(boundsInterval.endpoints()[0]) < 1:
794
        if boundsInterval.endpoints()[0] >= 0:
795
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
796
            invScalingCoeff = 1/scalingCoeff
797
            return((scalingCoeff * expVar, 
798
                    invScalingCoeff * expVar))
799
        else:
800
            scalingCoeff = \
801
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
802
            scalingOffset = -3 * scalingCoeff
803
            return((scalingCoeff * expVar + scalingOffset,
804
                    1/scalingCoeff * expVar + 3))
805
    else: 
806
        if boundsInterval.endpoints()[0] >= 0:
807
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
808
            scalingOffset = 0
809
            return((scalingCoeff * expVar, 
810
                    1/scalingCoeff * expVar))
811
        else:
812
            scalingCoeff = \
813
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
814
            scalingOffset =  -3 * scalingCoeff
815
            #scalingOffset = 0
816
            return((scalingCoeff * expVar + scalingOffset,
817
                    1/scalingCoeff * expVar + 3))
818

    
819
   
820
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
821
    """
822
    Compute the Sage version of the Taylor polynomial and it's
823
    companion data (interval, center...)
824
    The input parameter is a five elements tuple:
825
    - [0]: the polyomial (without variable change), as polynomial over a
826
           real ring;
827
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
828
           over a real ring;
829
    - [2]: the interval (as Sollya range);
830
    - [3]: the interval center;
831
    - [4]: the approximation error. 
832
    
833
    The function return a 5 elements tuple: formed with all the 
834
    input elements converted into their Sollya counterpart.
835
    """
836
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
837
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
838
    intervalSa = \
839
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
840
    centerSa = \
841
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
842
    errorSa = \
843
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
844
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
845
    # End slz_interval_and_polynomial_to_sage
846

    
847
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
848
                                                precision,
849
                                                targetHardnessToRound,
850
                                                variable1,
851
                                                variable2):
852
    """
853
    Creates a new multivariate polynomial with integer coefficients for use
854
     with the Coppersmith method.
855
    A the same time it computes :
856
    - 2^K (N);
857
    - 2^k (bound on the second variable)
858
    - lcm
859

    
860
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
861
                         variables.
862
    :param precision: the precision of the floating-point coefficients.
863
    :param targetHardnessToRound: the hardness to round we want to check.
864
    :param variable1: the first variable of the polynomial (an expression).
865
    :param variable2: the second variable of the polynomial (an expression).
866
    
867
    :returns: a 4 elements tuple:
868
                - the polynomial;
869
                - the modulus (N);
870
                - the t bound;
871
                - the lcm used to compute the integral coefficients and the 
872
                  module.
873
    """
874
    # Create a new integer polynomial ring.
875
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
876
    # Coefficients are issued in the increasing power order.
877
    ratPolyCoefficients = ratPolyOfInt.coefficients()
878
    # Print the reversed list for debugging.
879
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
880
    # Build the list of number we compute the lcm of.
881
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
882
    coefficientDenominators.append(2^precision)
883
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
884
    leastCommonMultiple = lcm(coefficientDenominators)
885
    # Compute the expression corresponding to the new polynomial
886
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
887
    #print coefficientNumerators
888
    polynomialExpression = 0
889
    power = 0
890
    # Iterate over two lists at the same time, stop when the shorter is
891
    # exhausted.
892
    for numerator, denominator in \
893
                        zip(coefficientNumerators, coefficientDenominators):
894
        multiplicator = leastCommonMultiple / denominator 
895
        newCoefficient = numerator * multiplicator
896
        polynomialExpression += newCoefficient * variable1^power
897
        power +=1
898
    polynomialExpression += - variable2
899
    return (IP(polynomialExpression),
900
            leastCommonMultiple / 2^precision, # 2^K or N.
901
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
902
            leastCommonMultiple) # If we want to make test computations.
903
        
904
# End slz_ratPoly_of_int_to_poly_for_coppersmith
905

    
906
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
907
                                          precision):
908
    """
909
    Makes a variable substitution into the input polynomial so that the output
910
    polynomial can take integer arguments.
911
    All variables of the input polynomial "have precision p". That is to say
912
    that they are rationals with denominator == 2^(precision - 1): 
913
    x = y/2^(precision - 1).
914
    We "incorporate" these denominators into the coefficients with, 
915
    respectively, the "right" power.
916
    """
917
    polynomialField = ratPolyOfRat.parent()
918
    polynomialVariable = ratPolyOfRat.variables()[0]
919
    #print "The polynomial field is:", polynomialField
920
    return \
921
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
922
                                   polynomialVariable/2^(precision-1)}))
923
    
924
    # Return a tuple:
925
    # - the bivariate integer polynomial in (i,j);
926
    # - 2^K
927
# End slz_rat_poly_of_rat_to_rat_poly_of_int
928

    
929

    
930
print "\t...sageSLZ loaded"
931
print