Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (63,35 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_compute_binade(number):
14
    """"
15
    For a given number, compute the "binade" that is integer m such that
16
    2^m <= number < 2^(m+1). If number == 0 return None.
17
    """
18
    # Checking the parameter.
19
    # The exception construction is used to detect if number is a RealNumber
20
    # since not all numbers have
21
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
22
    try:
23
        classTree = [number.__class__] + number.mro()
24
        # If the number is not a RealNumber (or offspring thereof) try
25
        # to transform it.
26
        if not sage.rings.real_mpfr.RealNumber in classTree:
27
            numberAsRR = RR(number)
28
        else:
29
            numberAsRR = number
30
    except AttributeError:
31
        return None
32
    # Zero special case.
33
    if numberAsRR == 0:
34
        return RR(-infinity)
35
    else:
36
        realField           = numberAsRR.parent()
37
        numberLog2          = numberAsRR.abs().log2()
38
        floorNumberLog2     = floor(numberLog2)
39
        ## Do not get caught by rounding of log2() both ways.
40
        ## When numberLog2 is an integer, compare numberAsRR
41
        #  with 2^numberLog2.
42
        if floorNumberLog2 == numberLog2:
43
            if numberAsRR.abs() < realField(2^floorNumberLog2):
44
                return floorNumberLog2 - 1
45
            else:
46
                return floorNumberLog2
47
        else:
48
            return floorNumberLog2
49
# End slz_compute_binade
50

    
51
#
52
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
53
    """
54
    For given "real number", compute the bounds of the binade it belongs to.
55
    
56
    NOTE::
57
        When number >= 2^(emax+1), we return the "fake" binade 
58
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
59
        with interval [-infinity, -2^(emax+1)]. We want to distinguish
60
        this case from that of "really" invalid arguments.
61
        
62
    """
63
    # Check the parameters.
64
    # RealNumbers or RealNumber offspring only.
65
    # The exception construction is necessary since not all objects have
66
    # the mro() method. sage.rings.real_mpfr.RealNumber do.
67
    try:
68
        classTree = [number.__class__] + number.mro()
69
        if not sage.rings.real_mpfr.RealNumber in classTree:
70
            return None
71
    except AttributeError:
72
        return None
73
    # Non zero negative integers only for emin.
74
    if emin >= 0 or int(emin) != emin:
75
        return None
76
    # Non zero positive integers only for emax.
77
    if emax <= 0 or int(emax) != emax:
78
        return None
79
    precision = number.precision()
80
    RF  = RealField(precision)
81
    if number == 0:
82
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
83
    # A more precise RealField is needed to avoid unwanted rounding effects
84
    # when computing number.log2().
85
    RRF = RealField(max(2048, 2 * precision))
86
    # number = 0 special case, the binade bounds are 
87
    # [0, 2^emin - 2^(emin-precision)]
88
    # Begin general case
89
    l2 = RRF(number).abs().log2()
90
    # Another special one: beyond largest representable -> "Fake" binade.
91
    if l2 >= emax + 1:
92
        if number > 0:
93
            return (RF(2^(emax+1)), RF(+infinity) )
94
        else:
95
            return (RF(-infinity), -RF(2^(emax+1)))
96
    # Regular case cont'd.
97
    offset = int(l2)
98
    # number.abs() >= 1.
99
    if l2 >= 0:
100
        if number >= 0:
101
            lb = RF(2^offset)
102
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
103
        else: #number < 0
104
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
105
            ub = -RF(2^offset)
106
    else: # log2 < 0, number.abs() < 1.
107
        if l2 < emin: # Denormal
108
           # print "Denormal:", l2
109
            if number >= 0:
110
                lb = RF(0)
111
                ub = RF(2^(emin)) - RF(2^(emin-precision))
112
            else: # number <= 0
113
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
114
                ub = RF(0)
115
        elif l2 > emin: # Normal number other than +/-2^emin.
116
            if number >= 0:
117
                if int(l2) == l2:
118
                    lb = RF(2^(offset))
119
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
120
                else:
121
                    lb = RF(2^(offset-1))
122
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
123
            else: # number < 0
124
                if int(l2) == l2: # Binade limit.
125
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
126
                    ub = -RF(2^(offset))
127
                else:
128
                    lb = -RF(2^(offset) - 2^(-precision+offset))
129
                    ub = -RF(2^(offset-1))
130
        else: # l2== emin, number == +/-2^emin
131
            if number >= 0:
132
                lb = RF(2^(offset))
133
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
134
            else: # number < 0
135
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
136
                ub = -RF(2^(offset))
137
    return (lb, ub)
138
# End slz_compute_binade_bounds
139
#
140
def slz_compute_coppersmith_reduced_polynomials(inputPolynomial,
141
                                                 alpha,
142
                                                 N,
143
                                                 iBound,
144
                                                 tBound):
145
    """
146
    For a given set of arguments (see below), compute a list
147
    of "reduced polynomials" that could be used to compute roots
148
    of the inputPolynomial.
149
    INPUT:
150
    
151
    - "inputPolynomial" -- (no default) a bivariate integer polynomial;
152
    - "alpha" -- the alpha parameter of the Coppersmith algorithm;
153
    - "N" -- the modulus;
154
    - "iBound" -- the bound on the first variable;
155
    - "tBound" -- the bound on the second variable.
156
    
157
    OUTPUT:
158
    
159
    A list of bivariate integer polynomial obtained using the Coppersmith
160
    algorithm. The polynomials correspond to the rows of the LLL-reduce
161
    reduced base that comply with the Coppersmith condition.
162
    """
163
    # Arguments check.
164
    if iBound == 0 or tBound == 0:
165
        return None
166
    # End arguments check.
167
    nAtAlpha = N^alpha
168
    ## Building polynomials for matrix.
169
    polyRing   = inputPolynomial.parent()
170
    # Whatever the 2 variables are actually called, we call them
171
    # 'i' and 't' in all the variable names.
172
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
173
    #print polyVars[0], type(polyVars[0])
174
    initialPolynomial = inputPolynomial.subs({iVariable:iVariable * iBound,
175
                                              tVariable:tVariable * tBound})
176
    polynomialsList = \
177
        spo_polynomial_to_polynomials_list_8(initialPolynomial,
178
                                             alpha,
179
                                             N,
180
                                             iBound,
181
                                             tBound,
182
                                             0)
183
    #print "Polynomials list:", polynomialsList
184
    ## Building the proto matrix.
185
    knownMonomials = []
186
    protoMatrix    = []
187
    for poly in polynomialsList:
188
        spo_add_polynomial_coeffs_to_matrix_row(poly,
189
                                                knownMonomials,
190
                                                protoMatrix,
191
                                                0)
192
    matrixToReduce = spo_proto_to_row_matrix(protoMatrix)
193
    #print matrixToReduce
194
    ## Reduction and checking.
195
    ## S.T. changed 'fp' to None as of Sage 6.6 complying to
196
    #  error message issued when previous code was used.
197
    #reducedMatrix = matrixToReduce.LLL(fp='fp')
198
    reducedMatrix = matrixToReduce.LLL(fp=None)
199
    isLLLReduced  = reducedMatrix.is_LLL_reduced()
200
    if not isLLLReduced:
201
        return None
202
    monomialsCount     = len(knownMonomials)
203
    monomialsCountSqrt = sqrt(monomialsCount)
204
    #print "Monomials count:", monomialsCount, monomialsCountSqrt.n()
205
    #print reducedMatrix
206
    ## Check the Coppersmith condition for each row and build the reduced 
207
    #  polynomials.
208
    ccReducedPolynomialsList = []
209
    for row in reducedMatrix.rows():
210
        l2Norm = row.norm(2)
211
        if (l2Norm * monomialsCountSqrt) < nAtAlpha:
212
            #print (l2Norm * monomialsCountSqrt).n()
213
            #print l2Norm.n()
214
            ccReducedPolynomial = \
215
                slz_compute_reduced_polynomial(row,
216
                                               knownMonomials,
217
                                               iVariable,
218
                                               iBound,
219
                                               tVariable,
220
                                               tBound)
221
            if not ccReducedPolynomial is None:
222
                ccReducedPolynomialsList.append(ccReducedPolynomial)
223
        else:
224
            #print l2Norm.n() , ">", nAtAlpha
225
            pass
226
    if len(ccReducedPolynomialsList) < 2:
227
        print "Less than 2 Coppersmith condition compliant vectors."
228
        return ()
229
    #print ccReducedPolynomialsList
230
    return ccReducedPolynomialsList
231
# End slz_compute_coppersmith_reduced_polynomials
232

    
233
def slz_compute_integer_polynomial_modular_roots(inputPolynomial,
234
                                                 alpha,
235
                                                 N,
236
                                                 iBound,
237
                                                 tBound):
238
    """
239
    For a given set of arguments (see below), compute the polynomial modular 
240
    roots, if any.
241
    
242
    """
243
    # Arguments check.
244
    if iBound == 0 or tBound == 0:
245
        return set()
246
    # End arguments check.
247
    nAtAlpha = N^alpha
248
    ## Building polynomials for matrix.
249
    polyRing   = inputPolynomial.parent()
250
    # Whatever the 2 variables are actually called, we call them
251
    # 'i' and 't' in all the variable names.
252
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
253
    ccReducedPolynomialsList = \
254
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
255
                                                     alpha,
256
                                                     N,
257
                                                     iBound,
258
                                                     tBound)
259
    if len(ccReducedPolynomialsList) == 0:
260
        return set()   
261
    ## Create the valid (poly1 and poly2 are algebraically independent) 
262
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
263
    # Try to mix and match all the polynomial pairs built from the 
264
    # ccReducedPolynomialsList to obtain non zero resultants.
265
    resultantsInITuplesList = []
266
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
267
        for polyInnerIndex in xrange(polyOuterIndex+1, 
268
                                     len(ccReducedPolynomialsList)):
269
            # Compute the resultant in resultants in the
270
            # first variable (is it the optimal choice?).
271
            resultantInI = \
272
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
273
                                                                  ccReducedPolynomialsList[0].parent(str(iVariable)))
274
            #print "Resultant", resultantInI
275
            # Test algebraic independence.
276
            if not resultantInI.is_zero():
277
                resultantsInITuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
278
                                                 ccReducedPolynomialsList[polyInnerIndex],
279
                                                 resultantInI))
280
    # If no non zero resultant was found: we can't get no algebraically 
281
    # independent polynomials pair. Give up!
282
    if len(resultantsInITuplesList) == 0:
283
        return set()
284
    #print resultantsInITuplesList
285
    # Compute the roots.
286
    Zi = ZZ[str(iVariable)]
287
    Zt = ZZ[str(tVariable)]
288
    polynomialRootsSet = set()
289
    # First, solve in the second variable since resultants are in the first
290
    # variable.
291
    for resultantInITuple in resultantsInITuplesList:
292
        tRootsList = Zt(resultantInITuple[2]).roots()
293
        # For each tRoot, compute the corresponding iRoots and check 
294
        # them in the input polynomial.
295
        for tRoot in tRootsList:
296
            #print "tRoot:", tRoot
297
            # Roots returned by root() are (value, multiplicity) tuples.
298
            iRootsList = \
299
                Zi(resultantInITuple[0].subs({resultantInITuple[0].variables()[1]:tRoot[0]})).roots()
300
            print iRootsList
301
            # The iRootsList can be empty, hence the test.
302
            if len(iRootsList) != 0:
303
                for iRoot in iRootsList:
304
                    polyEvalModN = inputPolynomial(iRoot[0], tRoot[0]) / N
305
                    # polyEvalModN must be an integer.
306
                    if polyEvalModN == int(polyEvalModN):
307
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
308
    return polynomialRootsSet
309
# End slz_compute_integer_polynomial_modular_roots.
310
#
311
def slz_compute_integer_polynomial_modular_roots_2(inputPolynomial,
312
                                                 alpha,
313
                                                 N,
314
                                                 iBound,
315
                                                 tBound):
316
    """
317
    For a given set of arguments (see below), compute the polynomial modular 
318
    roots, if any.
319
    This version differs in the way resultants are computed.   
320
    """
321
    # Arguments check.
322
    if iBound == 0 or tBound == 0:
323
        return set()
324
    # End arguments check.
325
    nAtAlpha = N^alpha
326
    ## Building polynomials for matrix.
327
    polyRing   = inputPolynomial.parent()
328
    # Whatever the 2 variables are actually called, we call them
329
    # 'i' and 't' in all the variable names.
330
    (iVariable, tVariable) = inputPolynomial.variables()[:2]
331
    #print polyVars[0], type(polyVars[0])
332
    ccReducedPolynomialsList = \
333
        slz_compute_coppersmith_reduced_polynomials (inputPolynomial,
334
                                                     alpha,
335
                                                     N,
336
                                                     iBound,
337
                                                     tBound)
338
    if len(ccReducedPolynomialsList) == 0:
339
        return set()   
340
    ## Create the valid (poly1 and poly2 are algebraically independent) 
341
    # resultant tuples (poly1, poly2, resultant(poly1, poly2)).
342
    # Try to mix and match all the polynomial pairs built from the 
343
    # ccReducedPolynomialsList to obtain non zero resultants.
344
    resultantsInTTuplesList = []
345
    for polyOuterIndex in xrange(0, len(ccReducedPolynomialsList)-1):
346
        for polyInnerIndex in xrange(polyOuterIndex+1, 
347
                                     len(ccReducedPolynomialsList)):
348
            # Compute the resultant in resultants in the
349
            # first variable (is it the optimal choice?).
350
            resultantInT = \
351
               ccReducedPolynomialsList[polyOuterIndex].resultant(ccReducedPolynomialsList[polyInnerIndex],
352
                                                                  ccReducedPolynomialsList[0].parent(str(tVariable)))
353
            #print "Resultant", resultantInT
354
            # Test algebraic independence.
355
            if not resultantInT.is_zero():
356
                resultantsInTTuplesList.append((ccReducedPolynomialsList[polyOuterIndex],
357
                                                 ccReducedPolynomialsList[polyInnerIndex],
358
                                                 resultantInT))
359
    # If no non zero resultant was found: we can't get no algebraically 
360
    # independent polynomials pair. Give up!
361
    if len(resultantsInTTuplesList) == 0:
362
        return set()
363
    #print resultantsInITuplesList
364
    # Compute the roots.
365
    Zi = ZZ[str(iVariable)]
366
    Zt = ZZ[str(tVariable)]
367
    polynomialRootsSet = set()
368
    # First, solve in the second variable since resultants are in the first
369
    # variable.
370
    for resultantInTTuple in resultantsInTTuplesList:
371
        iRootsList = Zi(resultantInTTuple[2]).roots()
372
        # For each iRoot, compute the corresponding tRoots and check 
373
        # them in the input polynomial.
374
        for iRoot in iRootsList:
375
            #print "iRoot:", iRoot
376
            # Roots returned by root() are (value, multiplicity) tuples.
377
            tRootsList = \
378
                Zt(resultantInTTuple[0].subs({resultantInTTuple[0].variables()[0]:iRoot[0]})).roots()
379
            print tRootsList
380
            # The tRootsList can be empty, hence the test.
381
            if len(tRootsList) != 0:
382
                for tRoot in tRootsList:
383
                    polyEvalModN = inputPolynomial(iRoot[0],tRoot[0]) / N
384
                    # polyEvalModN must be an integer.
385
                    if polyEvalModN == int(polyEvalModN):
386
                        polynomialRootsSet.add((iRoot[0],tRoot[0]))
387
    return polynomialRootsSet
388
# End slz_compute_integer_polynomial_modular_roots_2.
389
#
390
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
391
                                        upperBoundSa, approxPrecSa, 
392
                                        sollyaPrecSa=None):
393
    """
394
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
395
    a polynomial that approximates the function on a an interval starting
396
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
397
    approximates with the expected precision.
398
    The interval upper bound is lowered until the expected approximation 
399
    precision is reached.
400
    The polynomial, the bounds, the center of the interval and the error
401
    are returned.
402
    OUTPUT:
403
    A tuple made of 4 Sollya objects:
404
    - a polynomial;
405
    - an range (an interval, not in the sense of number given as an interval);
406
    - the center of the interval;
407
    - the maximum error in the approximation of the input functionSo by the
408
      output polynomial ; this error <= approxPrecSaS.
409
    
410
    """
411
    ## Superficial argument check.
412
    if lowerBoundSa > upperBoundSa:
413
        return None
414
    RRR = lowerBoundSa.parent()
415
    intervalShrinkConstFactorSa = RRR('0.9')
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.n()
430
        sollya_lib_clear_obj(polySo)
431
        sollya_lib_clear_obj(intervalCenterSo)
432
        sollya_lib_clear_obj(maxErrorSo)
433
        # Very empirical shrinking factor.
434
        shrinkFactorSa = 1 /  (maxErrorSa/approxPrecSa).log2().abs()
435
        print "Shrink factor:", \
436
              shrinkFactorSa.n(), \
437
              intervalShrinkConstFactorSa
438
        print
439
        #errorRatioSa = approxPrecSa/maxErrorSa
440
        #print "Error ratio: ", errorRatioSa
441
        # Make sure interval shrinks.
442
        if shrinkFactorSa > intervalShrinkConstFactorSa:
443
            actualShrinkFactorSa = intervalShrinkConstFactorSa
444
            #print "Fixed"
445
        else:
446
            actualShrinkFactorSa = shrinkFactorSa
447
            #print "Computed",shrinkFactorSa,maxErrorSa
448
            #print shrinkFactorSa, maxErrorSa
449
        #print "Shrink factor", actualShrinkFactorSa
450
        currentUpperBoundSa = currentLowerBoundSa + \
451
                                (currentUpperBoundSa - currentLowerBoundSa) * \
452
                                actualShrinkFactorSa
453
        #print "Current upper bound:", currentUpperBoundSa
454
        sollya_lib_clear_obj(currentRangeSo)
455
        # Check what is left with the bounds.
456
        if currentUpperBoundSa <= currentLowerBoundSa or \
457
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
458
            sollya_lib_clear_obj(absoluteErrorTypeSo)
459
            print "Can't find an interval."
460
            print "Use either or both a higher polynomial degree or a higher",
461
            print "internal precision."
462
            print "Aborting!"
463
            return (None, None, None, None)
464
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
465
                                                      currentUpperBoundSa)
466
        # print "New interval:",
467
        # pobyso_autoprint(currentRangeSo)
468
        #print "Second Taylor expansion call."
469
        (polySo, intervalCenterSo, maxErrorSo) = \
470
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
471
                                                        currentRangeSo, 
472
                                                        absoluteErrorTypeSo)
473
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
474
        #print "Max errorSo:",
475
        #pobyso_autoprint(maxErrorSo)
476
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
477
        #print "Max errorSa:", maxErrorSa
478
        #print "Sollya prec:",
479
        #pobyso_autoprint(sollya_lib_get_prec(None))
480
    sollya_lib_clear_obj(absoluteErrorTypeSo)
481
    return (polySo, currentRangeSo, intervalCenterSo, maxErrorSo)
482
# End slz_compute_polynomial_and_interval
483

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

    
615
def slz_compute_reduced_polynomials_list_from_rows(rowsList,
616
                                                   knownMonomials,
617
                                                   var1,
618
                                                   var1Bound,
619
                                                   var2,
620
                                                   var2Bound):
621
    """
622
    From a list of rows, holding the coefficients, from a monomials list,
623
    from the bounds of each variable, compute the corresponding polynomials
624
    scaled back by dividing by the "right" powers of the variables bounds.
625
    
626
    The elements in knownMonomials must be of the "right" polynomial type.
627
    They set the polynomial type of the output polynomials in list.
628
    @param rowsList:       the reduced matrix as output from LLL;
629
    @param kwnonMonomials: the ordered list of the monomials used to
630
                           build the polynomials;
631
    @param var1:           the first variable (of the "right" type);
632
    @param var1Bound:      the first variable bound;
633
    @param var2:           the second variable (of the "right" type);
634
    @param var2Bound:      the second variable bound.
635
    @return: a list of polynomials obtained with the reduced coefficients
636
             and scaled down with the bounds
637
    """
638
    
639
    # TODO: check input arguments.
640
    reducedPolynomials = []
641
    #print "type var1:", type(var1), " - type var2:", type(var2)
642
    for matrixRow in rowsList:
643
        currentPolynomial = 0
644
        for colIndex in xrange(0, len(knownMonomials)):
645
            currentCoefficient = matrixRow[colIndex]
646
            if currentCoefficient != 0:
647
                #print "Current coefficient:", currentCoefficient
648
                currentMonomial = knownMonomials[colIndex]
649
                parentRing = currentMonomial.parent()
650
                #print "Monomial as multivariate polynomial:", \
651
                #currentMonomial, type(currentMonomial)
652
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
653
                #print "Degree in var", var1, ":", degreeInVar1
654
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
655
                #print "Degree in var", var2, ":", degreeInVar2
656
                if degreeInVar1 > 0:
657
                    currentCoefficient /= var1Bound^degreeInVar1
658
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
659
                    #print "Current coefficient(1)", currentCoefficient
660
                if degreeInVar2 > 0:
661
                    currentCoefficient /= var2Bound^degreeInVar2
662
                    #print "Current coefficient(2)", currentCoefficient
663
                #print "Current reduced monomial:", (currentCoefficient * \
664
                #                                    currentMonomial)
665
                currentPolynomial += (currentCoefficient * currentMonomial)
666
                #if degreeInVar1 == 0 and degreeInVar2 == 0:
667
                    #print "!!!! constant term !!!!"
668
                #print "Current polynomial:", currentPolynomial
669
            # End if
670
        # End for colIndex.
671
        #print "Type of the current polynomial:", type(currentPolynomial)
672
        reducedPolynomials.append(currentPolynomial)
673
    return reducedPolynomials 
674
# End slz_compute_reduced_polynomials_list_from_rows.
675
#
676
def slz_compute_scaled_function(functionSa,
677
                                lowerBoundSa,
678
                                upperBoundSa,
679
                                floatingPointPrecSa,
680
                                debug=False):
681
    """
682
    From a function, compute the scaled function whose domain
683
    is included in [1, 2) and whose image is also included in [1,2).
684
    Return a tuple: 
685
    [0]: the scaled function
686
    [1]: the scaled domain lower bound
687
    [2]: the scaled domain upper bound
688
    [3]: the scaled image lower bound
689
    [4]: the scaled image upper bound
690
    """
691
    ## The variable can be called anything.
692
    x = functionSa.variables()[0]
693
    # Scalling the domain -> [1,2[.
694
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
695
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
696
    (invDomainScalingExpressionSa, domainScalingExpressionSa) = \
697
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
698
    if debug:
699
        print "domainScalingExpression for argument :", \
700
        invDomainScalingExpressionSa
701
        print "function: ", functionSa
702
    ff = functionSa.subs({x : domainScalingExpressionSa})
703
    ## Bless expression as a function.
704
    ff = ff.function(x)
705
    #ff = f.subs_expr(x==domainScalingExpressionSa)
706
    #domainScalingFunction(x) = invDomainScalingExpressionSa
707
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
708
    scaledLowerBoundSa = \
709
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
710
    scaledUpperBoundSa = \
711
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
712
    if debug:
713
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
714
              scaledUpperBoundSa
715
    #
716
    # Scalling the image -> [1,2[.
717
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
718
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
719
    if flbSa <= fubSa: # Increasing
720
        imageBinadeBottomSa = floor(flbSa.log2())
721
    else: # Decreasing
722
        imageBinadeBottomSa = floor(fubSa.log2())
723
    if debug:
724
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
725
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
726
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
727
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
728
    if debug:
729
        print "imageScalingExpression for argument :", \
730
              invImageScalingExpressionSa
731
    iis = invImageScalingExpressionSa.function(x)
732
    fff = iis.subs({x:ff})
733
    if debug:
734
        print "fff:", fff,
735
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
736
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
737
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
738
# End slz_compute_scaled_function
739

    
740
def slz_fix_bounds_for_binades(lowerBound, 
741
                               upperBound, 
742
                               func = None, 
743
                               domainDirection = -1,
744
                               imageDirection  = -1):
745
    """
746
    Assuming the function is increasing or decreasing over the 
747
    [lowerBound, upperBound] interval, return a lower bound lb and 
748
    an upper bound ub such that:
749
    - lb and ub belong to the same binade;
750
    - func(lb) and func(ub) belong to the same binade.
751
    domainDirection indicate how bounds move to fit into the same binade:
752
    - a negative value move the upper bound down;
753
    - a positive value move the lower bound up.
754
    imageDirection indicate how bounds move in order to have their image
755
    fit into the same binade, variation of the function is also condidered.
756
    For an increasing function:
757
    - negative value moves the upper bound down (and its image value as well);
758
    - a positive values moves the lower bound up (and its image value as well);
759
    For a decreasing function it is the other way round.
760
    """
761
    ## Arguments check
762
    if lowerBound > upperBound:
763
        return None
764
    if lowerBound == upperBound:
765
        return (lowerBound, upperBound)
766
    if func is None:
767
        return None
768
    #
769
    #varFunc = func.variables()[0]
770
    lb = lowerBound
771
    ub = upperBound
772
    lbBinade = slz_compute_binade(lb) 
773
    ubBinade = slz_compute_binade(ub)
774
    ## Domain binade.
775
    while lbBinade != ubBinade:
776
        newIntervalWidth = (ub - lb) / 2
777
        if domainDirection < 0:
778
            ub = lb + newIntervalWidth
779
            ubBinade = slz_compute_binade(ub)
780
        else:
781
            lb = lb + newIntervalWidth
782
            lbBinade = slz_compute_binade(lb) 
783
    ## Image binade.
784
    if lb == ub:
785
        return (lb, ub)
786
    lbImg = func(lb)
787
    ubImg = func(ub)
788
    funcIsInc = (ubImg >= lbImg)
789
    lbImgBinade = slz_compute_binade(lbImg)
790
    ubImgBinade = slz_compute_binade(ubImg)
791
    while lbImgBinade != ubImgBinade:
792
        newIntervalWidth = (ub - lb) / 2
793
        if imageDirection < 0:
794
            if funcIsInc:
795
                ub = lb + newIntervalWidth
796
                ubImgBinade = slz_compute_binade(func(ub))
797
                #print ubImgBinade
798
            else:
799
                lb = lb + newIntervalWidth
800
                lbImgBinade = slz_compute_binade(func(lb))
801
                #print lbImgBinade
802
        else:
803
            if funcIsInc:
804
                lb = lb + newIntervalWidth
805
                lbImgBinade = slz_compute_binade(func(lb))
806
                #print lbImgBinade
807
            else:
808
                ub = lb + newIntervalWidth
809
                ubImgBinade = slz_compute_binade(func(ub))
810
                #print ubImgBinade
811
    # End while lbImgBinade != ubImgBinade:
812
    return (lb, ub)
813
# End slz_fix_bounds_for_binades.
814

    
815
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
816
    # Create a polynomial over the rationals.
817
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
818
    return(ratPolynomialRing(polyOfFloat))
819
# End slz_float_poly_of_float_to_rat_poly_of_rat.
820

    
821
def slz_float_poly_of_float_to_rat_poly_of_rat_pow_two(polyOfFloat):
822
    """
823
     Create a polynomial over the rationals where all denominators are
824
     powers of two.
825
    """
826
    polyVariable = polyOfFloat.variables()[0] 
827
    RPR = QQ[str(polyVariable)]
828
    polyCoeffs      = polyOfFloat.coefficients()
829
    #print polyCoeffs
830
    polyExponents   = polyOfFloat.exponents()
831
    #print polyExponents
832
    polyDenomPtwoCoeffs = []
833
    for coeff in polyCoeffs:
834
        polyDenomPtwoCoeffs.append(sno_float_to_rat_pow_of_two_denom(coeff))
835
        #print "Converted coefficient:", sno_float_to_rat_pow_of_two_denom(coeff),
836
        #print type(sno_float_to_rat_pow_of_two_denom(coeff))
837
    ratPoly = RPR(0)
838
    #print type(ratPoly)
839
    ## !!! CAUTION !!! Do not use the RPR(coeff * polyVariagle^exponent)
840
    #  The coefficient becomes plainly wrong when exponent == 0.
841
    #  No clue as to why.
842
    for coeff, exponent in zip(polyDenomPtwoCoeffs, polyExponents):
843
        ratPoly += coeff * RPR(polyVariable^exponent)
844
    return ratPoly
845
# End slz_float_poly_of_float_to_rat_poly_of_rat.
846

    
847
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
848
                                      lowerBoundSa, 
849
                                      upperBoundSa, floatingPointPrecSa, 
850
                                      internalSollyaPrecSa, approxPrecSa):
851
    """
852
    Under the assumption that:
853
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
854
    - lowerBound and upperBound belong to the same binade.
855
    from a:
856
    - function;
857
    - a degree
858
    - a pair of bounds;
859
    - the floating-point precision we work on;
860
    - the internal Sollya precision;
861
    - the requested approximation error
862
    The initial interval is, possibly, splitted into smaller intervals.
863
    It return a list of tuples, each made of:
864
    - a first polynomial (without the changed variable f(x) = p(x-x0));
865
    - a second polynomial (with a changed variable f(x) = q(x))
866
    - the approximation interval;
867
    - the center, x0, of the interval;
868
    - the corresponding approximation error.
869
    TODO: fix endless looping for some parameters sets.
870
    """
871
    resultArray = []
872
    # Set Sollya to the necessary internal precision.
873
    precChangedSa = False
874
    currentSollyaPrecSo = pobyso_get_prec_so()
875
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
876
    if internalSollyaPrecSa > currentSollyaPrecSa:
877
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
878
        precChangedSa = True
879
    #
880
    x = functionSa.variables()[0] # Actual variable name can be anything.
881
    # Scaled function: [1=,2] -> [1,2].
882
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
883
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
884
                slz_compute_scaled_function(functionSa,                       \
885
                                            lowerBoundSa,                     \
886
                                            upperBoundSa,                     \
887
                                            floatingPointPrecSa)
888
    # In case bounds were in the negative real one may need to
889
    # switch scaled bounds.
890
    if scaledLowerBoundSa > scaledUpperBoundSa:
891
        scaledLowerBoundSa, scaledUpperBoundSa = \
892
            scaledUpperBoundSa, scaledLowerBoundSa
893
        #print "Switching!"
894
    print "Approximation precision: ", RR(approxPrecSa)
895
    # Prepare the arguments for the Taylor expansion computation with Sollya.
896
    functionSo = \
897
      pobyso_parse_string_sa_so(fff._assume_str().replace('_SAGE_VAR_', ''))
898
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
899
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
900
                                                  scaledUpperBoundSa)
901

    
902
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
903
                                              upperBoundSa.parent().precision()))
904
    currentScaledLowerBoundSa = scaledLowerBoundSa
905
    currentScaledUpperBoundSa = scaledUpperBoundSa
906
    while True:
907
        ## Compute the first Taylor expansion.
908
        print "Computing a Taylor expansion..."
909
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
910
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
911
                                        currentScaledLowerBoundSa, 
912
                                        currentScaledUpperBoundSa,
913
                                        approxPrecSa, internalSollyaPrecSa)
914
        print "...done."
915
        ## If slz_compute_polynomial_and_interval fails, it returns None.
916
        #  This value goes to the first variable: polySo. Other variables are
917
        #  not assigned and should not be tested.
918
        if polySo is None:
919
            print "slz_get_intervals_and_polynomials: Aborting and returning None!"
920
            if precChangedSa:
921
                pobyso_set_prec_so_so(currentSollyaPrecSo)
922
                sollya_lib_clear_obj(currentSollyaPrecSo)
923
            sollya_lib_clear_obj(functionSo)
924
            sollya_lib_clear_obj(degreeSo)
925
            sollya_lib_clear_obj(scaledBoundsSo)
926
            return None
927
        ## Add to the result array.
928
        ### Change variable stuff in Sollya x -> x0-x.
929
        changeVarExpressionSo = \
930
            sollya_lib_build_function_sub( \
931
                              sollya_lib_build_function_free_variable(), 
932
                              sollya_lib_copy_obj(intervalCenterSo))
933
        polyVarChangedSo = \
934
                    sollya_lib_evaluate(polySo, changeVarExpressionSo)
935
        #### Get rid of the variable change Sollya stuff. 
936
        sollya_lib_clear_obj(changeVarExpressionSo)
937
        resultArray.append((polySo, polyVarChangedSo, boundsSo,
938
                            intervalCenterSo, maxErrorSo))
939
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
940
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
941
        print "Computed approximation error:", errorSa.n(digits=10)
942
        # If the error and interval are OK a the first try, just return.
943
        if (boundsSa.endpoints()[1] >= scaledUpperBoundSa) and \
944
            (errorSa <= approxPrecSa):
945
            if precChangedSa:
946
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
947
            sollya_lib_clear_obj(currentSollyaPrecSo)
948
            sollya_lib_clear_obj(functionSo)
949
            sollya_lib_clear_obj(degreeSo)
950
            sollya_lib_clear_obj(scaledBoundsSo)
951
            #print "Approximation error:", errorSa
952
            return resultArray
953
        ## The returned interval upper bound does not reach the requested upper
954
        #  upper bound: compute the next upper bound.
955
        ## The following ratio is always >= 1. If errorSa, we may want to
956
        #  enlarge the interval
957
        currentErrorRatio = approxPrecSa / errorSa
958
        ## --|--------------------------------------------------------------|--
959
        #  --|--------------------|--------------------------------------------
960
        #  --|----------------------------|------------------------------------
961
        ## Starting point for the next upper bound.
962
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
963
        # Compute the increment. 
964
        newBoundsWidthSa = \
965
                        ((currentErrorRatio.log() / 10) + 1) * boundsWidthSa
966
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
967
        currentScaledUpperBoundSa = boundsSa.endpoints()[1] + newBoundsWidthSa
968
        # Take into account the original interval upper bound.                     
969
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
970
            currentScaledUpperBoundSa = scaledUpperBoundSa
971
        if currentScaledUpperBoundSa == currentScaledLowerBoundSa:
972
            print "Can't shrink the interval anymore!"
973
            print "You should consider increasing the Sollya internal precision"
974
            print "or the polynomial degree."
975
            print "Giving up!"
976
            if precChangedSa:
977
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
978
            sollya_lib_clear_obj(currentSollyaPrecSo)
979
            sollya_lib_clear_obj(functionSo)
980
            sollya_lib_clear_obj(degreeSo)
981
            sollya_lib_clear_obj(scaledBoundsSo)
982
            return None
983
        # Compute the other expansions.
984
        # Test for insufficient precision.
985
# End slz_get_intervals_and_polynomials
986

    
987
def slz_interval_scaling_expression(boundsInterval, expVar):
988
    """
989
    Compute the scaling expression to map an interval that spans at most
990
    a single binade into [1, 2) and the inverse expression as well.
991
    If the interval spans more than one binade, result is wrong since
992
    scaling is only based on the lower bound.
993
    Not very sure that the transformation makes sense for negative numbers.
994
    """
995
    # The "one of the bounds is 0" special case. Here we consider
996
    # the interval as the subnormals binade.
997
    if boundsInterval.endpoints()[0] == 0 or boundsInterval.endpoints()[1] == 0:
998
        # The upper bound is (or should be) positive.
999
        if boundsInterval.endpoints()[0] == 0:
1000
            if boundsInterval.endpoints()[1] == 0:
1001
                return None
1002
            binade = slz_compute_binade(boundsInterval.endpoints()[1])
1003
            l2     = boundsInterval.endpoints()[1].abs().log2()
1004
            # The upper bound is a power of two
1005
            if binade == l2:
1006
                scalingCoeff    = 2^(-binade)
1007
                invScalingCoeff = 2^(binade)
1008
                scalingOffset   = 1
1009
                return \
1010
                  ((scalingCoeff * expVar + scalingOffset).function(expVar),
1011
                  ((expVar - scalingOffset) * invScalingCoeff).function(expVar))
1012
            else:
1013
                scalingCoeff    = 2^(-binade-1)
1014
                invScalingCoeff = 2^(binade+1)
1015
                scalingOffset   = 1
1016
                return((scalingCoeff * expVar + scalingOffset),\
1017
                    ((expVar - scalingOffset) * invScalingCoeff))
1018
        # The lower bound is (or should be) negative.
1019
        if boundsInterval.endpoints()[1] == 0:
1020
            if boundsInterval.endpoints()[0] == 0:
1021
                return None
1022
            binade = slz_compute_binade(boundsInterval.endpoints()[0])
1023
            l2     = boundsInterval.endpoints()[0].abs().log2()
1024
            # The upper bound is a power of two
1025
            if binade == l2:
1026
                scalingCoeff    = -2^(-binade)
1027
                invScalingCoeff = -2^(binade)
1028
                scalingOffset   = 1
1029
                return((scalingCoeff * expVar + scalingOffset),\
1030
                    ((expVar - scalingOffset) * invScalingCoeff))
1031
            else:
1032
                scalingCoeff    = -2^(-binade-1)
1033
                invScalingCoeff = -2^(binade+1)
1034
                scalingOffset   = 1
1035
                return((scalingCoeff * expVar + scalingOffset),\
1036
                   ((expVar - scalingOffset) * invScalingCoeff))
1037
    # General case
1038
    lbBinade = slz_compute_binade(boundsInterval.endpoints()[0])
1039
    ubBinade = slz_compute_binade(boundsInterval.endpoints()[1])
1040
    # We allow for a single exception in binade spanning is when the
1041
    # "outward bound" is a power of 2 and its binade is that of the
1042
    # "inner bound" + 1.
1043
    if boundsInterval.endpoints()[0] > 0:
1044
        ubL2 = boundsInterval.endpoints()[1].abs().log2()
1045
        if lbBinade != ubBinade:
1046
            print "Different binades."
1047
            if ubL2 != ubBinade:
1048
                print "Not a power of 2."
1049
                return None
1050
            elif abs(ubBinade - lbBinade) > 1:
1051
                print "Too large span:", abs(ubBinade - lbBinade)
1052
                return None
1053
    else:
1054
        lbL2 = boundsInterval.endpoints()[0].abs().log2()
1055
        if lbBinade != ubBinade:
1056
            print "Different binades."
1057
            if lbL2 != lbBinade:
1058
                print "Not a power of 2."
1059
                return None
1060
            elif abs(ubBinade - lbBinade) > 1:
1061
                print "Too large span:", abs(ubBinade - lbBinade)
1062
                return None
1063
    #print "Lower bound binade:", binade
1064
    if boundsInterval.endpoints()[0] > 0:
1065
        return \
1066
            ((2^(-lbBinade) * expVar).function(expVar),
1067
             (2^(lbBinade) * expVar).function(expVar))
1068
    else:
1069
        return \
1070
            ((-(2^(-ubBinade)) * expVar).function(expVar),
1071
             (-(2^(ubBinade)) * expVar).function(expVar))
1072
"""
1073
    # Code sent to attic. Should be the base for a 
1074
    # "slz_interval_translate_expression" rather than scale.
1075
    # Extra control and special cases code  added in  
1076
    # slz_interval_scaling_expression could (should ?) be added to
1077
    # this new function.
1078
    # The scaling offset is only used for negative numbers.
1079
    # When the absolute value of the lower bound is < 0.
1080
    if abs(boundsInterval.endpoints()[0]) < 1:
1081
        if boundsInterval.endpoints()[0] >= 0:
1082
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1083
            invScalingCoeff = 1/scalingCoeff
1084
            return((scalingCoeff * expVar, 
1085
                    invScalingCoeff * expVar))
1086
        else:
1087
            scalingCoeff = \
1088
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
1089
            scalingOffset = -3 * scalingCoeff
1090
            return((scalingCoeff * expVar + scalingOffset,
1091
                    1/scalingCoeff * expVar + 3))
1092
    else: 
1093
        if boundsInterval.endpoints()[0] >= 0:
1094
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
1095
            scalingOffset = 0
1096
            return((scalingCoeff * expVar, 
1097
                    1/scalingCoeff * expVar))
1098
        else:
1099
            scalingCoeff = \
1100
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
1101
            scalingOffset =  -3 * scalingCoeff
1102
            #scalingOffset = 0
1103
            return((scalingCoeff * expVar + scalingOffset,
1104
                    1/scalingCoeff * expVar + 3))
1105
"""
1106
# End slz_interval_scaling_expression
1107
   
1108
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
1109
    """
1110
    Compute the Sage version of the Taylor polynomial and it's
1111
    companion data (interval, center...)
1112
    The input parameter is a five elements tuple:
1113
    - [0]: the polyomial (without variable change), as polynomial over a
1114
           real ring;
1115
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
1116
           over a real ring;
1117
    - [2]: the interval (as Sollya range);
1118
    - [3]: the interval center;
1119
    - [4]: the approximation error. 
1120
    
1121
    The function return a 5 elements tuple: formed with all the 
1122
    input elements converted into their Sollya counterpart.
1123
    """
1124
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
1125
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
1126
    intervalSa = \
1127
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
1128
    centerSa = \
1129
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
1130
    errorSa = \
1131
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
1132
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
1133
    # End slz_interval_and_polynomial_to_sage
1134

    
1135
def slz_is_htrn(argument, function, targetAccuracy, targetRF = None, 
1136
            targetPlusOnePrecRF = None, quasiExactRF = None):
1137
    """
1138
      Check if an element (argument) of the domain of function (function)
1139
      yields a HTRN case (rounding to next) for the target precision 
1140
      (as impersonated by targetRF) for a given accuracy (targetAccuracy). 
1141
    """
1142
    ## Arguments filtering. 
1143
    ## TODO: filter the first argument for consistence.
1144
    if targetRF is None:
1145
        targetRF = argument.parent()
1146
    ## Ditto for the real field holding the midpoints.
1147
    if targetPlusOnePrecRF is None:
1148
        targetPlusOnePrecRF = RealField(targetRF.prec()+1)
1149
    ## If no quasiExactField is provided, create a high accuracy RealField.
1150
    if quasiExactRF is None:
1151
        quasiExactRF = RealField(1536)
1152
    #functionVariable              = function.variables()[0]
1153
    exactArgument                 = quasiExactRF(argument)
1154
    quasiExactValue               = function(exactArgument)
1155
    roundedValue                  = targetRF(quasiExactValue)
1156
    roundedValuePrecPlusOne       = targetPlusOnePrecRF(roundedValue)
1157
    # Upper midpoint.
1158
    roundedValuePrecPlusOneNext   = roundedValuePrecPlusOne.nextabove()
1159
    # Lower midpoint.
1160
    roundedValuePrecPlusOnePrev   = roundedValuePrecPlusOne.nextbelow()
1161
    binade                        = slz_compute_binade(roundedValue)
1162
    binadeCorrectedTargetAccuracy = targetAccuracy * 2^binade
1163
    #print "Argument:", argument
1164
    #print "f(x):", roundedValue, binade, floor(binade), ceil(binade)
1165
    #print "Binade:", binade
1166
    #print "binadeCorrectedTargetAccuracy:", \
1167
    #binadeCorrectedTargetAccuracy.n(prec=100)
1168
    #print "binadeCorrectedTargetAccuracy:", \
1169
    #  binadeCorrectedTargetAccuracy.n(prec=100).str(base=2)
1170
    #print "Upper midpoint:", \
1171
    #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1172
    #print "Rounded value :", \
1173
    #  roundedValuePrecPlusOne.n(prec=targetPlusOnePrecRF.prec()).str(base=2), \
1174
    #  roundedValuePrecPlusOne.ulp().n(prec=2).str(base=2)
1175
    #print "QuasiEx value :", quasiExactValue.n(prec=250).str(base=2)
1176
    #print "Lower midpoint:", \
1177
    #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1178
    ## Begining of the general case : check with the midpoint with 
1179
    #  greatest absolute value.
1180
    if quasiExactValue > 0:
1181
        if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) <\
1182
           binadeCorrectedTargetAccuracy:
1183
            #print "Too close to the upper midpoint: ", \
1184
            #abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1185
            #print argument.n().str(base=16, \
1186
            #  no_sci=False)
1187
            #print "Lower midpoint:", \
1188
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1189
            #print "Value         :", \
1190
            # quasiExactValue.n(prec=200).str(base=2)
1191
            #print "Upper midpoint:", \
1192
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1193
            return True
1194
    else:
1195
        if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1196
           binadeCorrectedTargetAccuracy:
1197
            #print "Too close to the upper midpoint: ", \
1198
            #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100)
1199
            #print argument.n().str(base=16, \
1200
            #  no_sci=False)
1201
            #print "Lower midpoint:", \
1202
            #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1203
            #print "Value         :", \
1204
            #  quasiExactValue.n(prec=200).str(base=2)
1205
            #print "Upper midpoint:", \
1206
            #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1207
            #print
1208
            return True
1209
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1210
    ## For the midpoint of smaller absolute value, 
1211
    #  split cases with the powers of 2.
1212
    if sno_abs_is_power_of_two(roundedValue):
1213
        if quasiExactValue > 0:        
1214
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) <\
1215
               binadeCorrectedTargetAccuracy / 2:
1216
                #print "Lower midpoint:", \
1217
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1218
                #print "Value         :", \
1219
                #  quasiExactValue.n(prec=200).str(base=2)
1220
                #print "Upper midpoint:", \
1221
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1222
                #print
1223
                return True
1224
        else:
1225
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1226
              binadeCorrectedTargetAccuracy / 2:
1227
                #print "Lower midpoint:", \
1228
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1229
                #print "Value         :", 
1230
                #  quasiExactValue.n(prec=200).str(base=2)
1231
                #print "Upper midpoint:", 
1232
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1233
                #print
1234
                return True
1235
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
1236
    else: ## Not a power of 2, regular comparison with the midpoint of 
1237
          #  smaller absolute value.
1238
        if quasiExactValue > 0:        
1239
            if abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue) < \
1240
              binadeCorrectedTargetAccuracy:
1241
                #print "Lower midpoint:", \
1242
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1243
                #print "Value         :", \
1244
                #  quasiExactValue.n(prec=200).str(base=2)
1245
                #print "Upper midpoint:", \
1246
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1247
                #print
1248
                return True
1249
        else: # quasiExactValue <= 0
1250
            if abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue) < \
1251
              binadeCorrectedTargetAccuracy:
1252
                #print "Lower midpoint:", \
1253
                #  roundedValuePrecPlusOnePrev.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1254
                #print "Value         :", \
1255
                #  quasiExactValue.n(prec=200).str(base=2)
1256
                #print "Upper midpoint:", \
1257
                #  roundedValuePrecPlusOneNext.n(prec=targetPlusOnePrecRF.prec()).str(base=2)
1258
                #print
1259
                return True
1260
    #print "distance to the upper midpoint:", \
1261
    #  abs(quasiExactRF(roundedValuePrecPlusOneNext) - quasiExactValue).n(prec=100).str(base=2)
1262
    #print "distance to the lower midpoint:", \
1263
    #  abs(quasiExactRF(roundedValuePrecPlusOnePrev) - quasiExactValue).n(prec=100).str(base=2)
1264
    return False
1265
# End slz_is_htrn
1266

    
1267
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
1268
                                                precision,
1269
                                                targetHardnessToRound,
1270
                                                variable1,
1271
                                                variable2):
1272
    """
1273
    Creates a new multivariate polynomial with integer coefficients for use
1274
     with the Coppersmith method.
1275
    A the same time it computes :
1276
    - 2^K (N);
1277
    - 2^k (bound on the second variable)
1278
    - lcm
1279

    
1280
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
1281
                         variables.
1282
    :param precision: the precision of the floating-point coefficients.
1283
    :param targetHardnessToRound: the hardness to round we want to check.
1284
    :param variable1: the first variable of the polynomial (an expression).
1285
    :param variable2: the second variable of the polynomial (an expression).
1286
    
1287
    :returns: a 4 elements tuple:
1288
                - the polynomial;
1289
                - the modulus (N);
1290
                - the t bound;
1291
                - the lcm used to compute the integral coefficients and the 
1292
                  module.
1293
    """
1294
    # Create a new integer polynomial ring.
1295
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
1296
    # Coefficients are issued in the increasing power order.
1297
    ratPolyCoefficients = ratPolyOfInt.coefficients()
1298
    # Print the reversed list for debugging.
1299
    #print
1300
    #print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
1301
    # Build the list of number we compute the lcm of.
1302
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
1303
    #print "Coefficient denominators:", coefficientDenominators
1304
    coefficientDenominators.append(2^precision)
1305
    coefficientDenominators.append(2^(targetHardnessToRound))
1306
    leastCommonMultiple = lcm(coefficientDenominators)
1307
    # Compute the expression corresponding to the new polynomial
1308
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
1309
    #print coefficientNumerators
1310
    polynomialExpression = 0
1311
    power = 0
1312
    # Iterate over two lists at the same time, stop when the shorter
1313
    # (is this case coefficientsNumerators) is 
1314
    # exhausted. Both lists are ordered in the order of increasing powers
1315
    # of variable1.
1316
    for numerator, denominator in \
1317
                        zip(coefficientNumerators, coefficientDenominators):
1318
        multiplicator = leastCommonMultiple / denominator 
1319
        newCoefficient = numerator * multiplicator
1320
        polynomialExpression += newCoefficient * variable1^power
1321
        power +=1
1322
    polynomialExpression += - variable2
1323
    return (IP(polynomialExpression),
1324
            leastCommonMultiple / 2^precision, # 2^K aka N.
1325
            #leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
1326
            leastCommonMultiple / 2^(targetHardnessToRound), # tBound
1327
            leastCommonMultiple) # If we want to make test computations.
1328
        
1329
# End slz_rat_poly_of_int_to_poly_for_coppersmith
1330

    
1331
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
1332
                                          precision):
1333
    """
1334
    Makes a variable substitution into the input polynomial so that the output
1335
    polynomial can take integer arguments.
1336
    All variables of the input polynomial "have precision p". That is to say
1337
    that they are rationals with denominator == 2^(precision - 1): 
1338
    x = y/2^(precision - 1).
1339
    We "incorporate" these denominators into the coefficients with, 
1340
    respectively, the "right" power.
1341
    """
1342
    polynomialField = ratPolyOfRat.parent()
1343
    polynomialVariable = ratPolyOfRat.variables()[0]
1344
    #print "The polynomial field is:", polynomialField
1345
    return \
1346
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
1347
                                   polynomialVariable/2^(precision-1)}))
1348
    
1349
# End slz_rat_poly_of_rat_to_rat_poly_of_int
1350

    
1351
def slz_reduce_and_test_base(matrixToReduce,
1352
                             nAtAlpha,
1353
                             monomialsCountSqrt):
1354
    """
1355
    Reduces the basis, tests the Coppersmith condition and returns
1356
    a list of rows that comply with the condition.
1357
    """
1358
    ccComplientRowsList = []
1359
    reducedMatrix = matrixToReduce.LLL(None)
1360
    if not reducedMatrix.is_LLL_reduced():
1361
        raise Exception("reducedMatrix is not actually reduced. Aborting!")
1362
    else:
1363
        print "reducedMatrix is actually reduced."
1364
    print "N^alpha:", nAtAlpha.n()
1365
    rowIndex = 0
1366
    for row in reducedMatrix.rows():
1367
        l2Norm = row.norm(2)
1368
        print "L_2 norm for vector # ", rowIndex, "= ", RR(l2Norm), "*", \
1369
                monomialsCountSqrt.n(), ". Is vector OK?", 
1370
        if (l2Norm * monomialsCountSqrt < nAtAlpha):
1371
            ccComplientRowsList.append(row)
1372
            print "True"
1373
        else:
1374
            print "False"
1375
    # End for
1376
    return ccComplientRowsList
1377
# End slz_reduce_and_test_base
1378

    
1379
def slz_resultant_tuple(poly1, poly2, elimVar):
1380
    """
1381
    Compute the resultant for two polynomials for a given variable
1382
    and return the (poly1, poly2, resultant) if the resultant
1383
    is not 0.
1384
    Return () otherwise.
1385
    """
1386
    polynomialRing0    = poly1.parent()
1387
    resultantInElimVar = poly1.resultant(poly2,polynomialRing0(elimVar))
1388
    if resultantInElimVar.is_zero():
1389
        return ()
1390
    else:
1391
        return (poly1, poly2, resultantInElimVar)
1392
# End slz_resultant_tuple.
1393
#
1394
print "\t...sageSLZ loaded"