Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (63,19 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 "f: ", f
702
    ff = f.subs({x : domainScalingExpressionSa})
703
    #ff = f.subs_expr(x==domainScalingExpressionSa)
704
    #domainScalingFunction(x) = invDomainScalingExpressionSa
705
    domainScalingFunction = invDomainScalingExpressionSa.function(x)
706
    scaledLowerBoundSa = \
707
        domainScalingFunction(lowerBoundSa).n(prec=floatingPointPrecSa)
708
    scaledUpperBoundSa = \
709
        domainScalingFunction(upperBoundSa).n(prec=floatingPointPrecSa)
710
    if debug:
711
        print 'ff:', ff, "- Domain:", scaledLowerBoundSa, \
712
              scaledUpperBoundSa
713
    #
714
    # Scalling the image -> [1,2[.
715
    flbSa = ff(scaledLowerBoundSa).n(prec=floatingPointPrecSa)
716
    fubSa = ff(scaledUpperBoundSa).n(prec=floatingPointPrecSa)
717
    if flbSa <= fubSa: # Increasing
718
        imageBinadeBottomSa = floor(flbSa.log2())
719
    else: # Decreasing
720
        imageBinadeBottomSa = floor(fubSa.log2())
721
    if debug:
722
        print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
723
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
724
    (invImageScalingExpressionSa,imageScalingExpressionSa) = \
725
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
726
    if debug:
727
        print "imageScalingExpression for argument :", \
728
              invImageScalingExpressionSa
729
    iis = invImageScalingExpressionSa.function(x)
730
    fff = iis.subs({x:ff})
731
    if debug:
732
        print "fff:", fff,
733
        print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
734
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
735
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
736
# End slz_compute_scaled_function
737

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

    
811
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
812
    # Create a polynomial over the rationals.
813
    ratPolynomialRing = QQ[str(polyOfFloat.variables()[0])]
814
    return(ratPolynomialRing(polyOfFloat))
815
# End slz_float_poly_of_float_to_rat_poly_of_rat.
816

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

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

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

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

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

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

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

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

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

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