Statistiques
| Révision :

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

Historique | Voir | Annoter | Télécharger (30,53 ko)

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

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

    
7
Examples:
8

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

    
88
# End slz_check_htr_value.
89
#
90
def slz_compute_binade_bounds(number, emin, emax=sys.maxint):
91
    """
92
    For given "real number", compute the bounds of the binade it belongs to.
93
    
94
    NOTE::
95
        When number >= 2^(emax+1), we return the "fake" binade 
96
        [2^(emax+1), +infinity]. Ditto for number <= -2^(emax+1)
97
        with interval [-infinity, -2^(emax+1)].
98
        
99
    """
100
    # Check the parameters.
101
    # RealNumbers only.
102
    classTree = [number.__class__] + number.mro()
103
    if not sage.rings.real_mpfr.RealNumber in classTree:
104
        return None
105
    # Non zero negative integers only for emin.
106
    if emin >= 0 or int(emin) != emin:
107
        return None
108
    # Non zero positive integers only for emax.
109
    if emax <= 0 or int(emax) != emax:
110
        return None
111
    precision = number.precision()
112
    RF  = RealField(precision)
113
    # A more precise RealField is needed to avoid unwanted rounding effects
114
    # when computing number.log2().
115
    RRF = RealField(max(2048, 2 * precision))
116
    # number = 0 special case, the binade bounds are 
117
    # [0, 2^emin - 2^(emin-precision)]
118
    if number == 0:
119
        return (RF(0),RF(2^(emin)) - RF(2^(emin-precision)))
120
    # Begin general case
121
    l2 = RRF(number).abs().log2()
122
    # Another special one: beyond largest representable -> "Fake" binade.
123
    if l2 >= emax + 1:
124
        if number > 0:
125
            return (RF(2^(emax+1)), RRR(+infinity) )
126
        else:
127
            return (RF(-infinity), -RF(2^(emax+1)))
128
    offset = int(l2)
129
    # number.abs() >= 1.
130
    if l2 >= 0:
131
        if number >= 0:
132
            lb = RF(2^offset)
133
            ub = RF(2^(offset + 1) - 2^(-precision+offset+1))
134
        else: #number < 0
135
            lb = -RF(2^(offset + 1) - 2^(-precision+offset+1))
136
            ub = -RF(2^offset)
137
    else: # log2 < 0, number.abs() < 1.
138
        if l2 < emin: # Denormal
139
           # print "Denormal:", l2
140
            if number >= 0:
141
                lb = RF(0)
142
                ub = RF(2^(emin)) - RF(2^(emin-precision))
143
            else: # number <= 0
144
                lb = - RF(2^(emin)) + RF(2^(emin-precision))
145
                ub = RF(0)
146
        elif l2 > emin: # Normal number other than +/-2^emin.
147
            if number >= 0:
148
                if int(l2) == l2:
149
                    lb = RF(2^(offset))
150
                    ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
151
                else:
152
                    lb = RF(2^(offset-1))
153
                    ub = RF(2^(offset)) - RF(2^(-precision+offset))
154
            else: # number < 0
155
                if int(l2) == l2: # Binade limit.
156
                    lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
157
                    ub = -RF(2^(offset))
158
                else:
159
                    lb = -RF(2^(offset) - 2^(-precision+offset))
160
                    ub = -RF(2^(offset-1))
161
        else: # l2== emin, number == +/-2^emin
162
            if number >= 0:
163
                lb = RF(2^(offset))
164
                ub = RF(2^(offset+1)) - RF(2^(-precision+offset+1))
165
            else: # number < 0
166
                lb = -RF(2^(offset+1) - 2^(-precision+offset+1))
167
                ub = -RF(2^(offset))
168
    return (lb, ub)
169
# End slz_compute_binade_bounds
170
#
171
def slz_compute_polynomial_and_interval(functionSo, degreeSo, lowerBoundSa, 
172
                                        upperBoundSa, approxPrecSa, 
173
                                        sollyaPrecSa=None):
174
    """
175
    Under the assumptions listed for slz_get_intervals_and_polynomials, compute
176
    a polynomial that approximates the function on a an interval starting
177
    at lowerBoundSa and finishing at a value that guarantees that the polynomial
178
    approximates with the expected precision.
179
    The interval upper bound is lowered until the expected approximation 
180
    precision is reached.
181
    The polynomial, the bounds, the center of the interval and the error
182
    are returned.
183
    """
184
    RRR = lowerBoundSa.parent()
185
    intervalShrinkConstFactorSa = RRR('0.5')
186
    absoluteErrorTypeSo = pobyso_absolute_so_so()
187
    currentRangeSo = pobyso_bounds_to_range_sa_so(lowerBoundSa, upperBoundSa)
188
    currentUpperBoundSa = upperBoundSa
189
    currentLowerBoundSa = lowerBoundSa
190
    # What we want here is the polynomial without the variable change, 
191
    # since our actual variable will be x-intervalCenter defined over the 
192
    # domain [lowerBound-intervalCenter , upperBound-intervalCenter]. 
193
    (polySo, intervalCenterSo, maxErrorSo) = \
194
        pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
195
                                                    currentRangeSo, 
196
                                                    absoluteErrorTypeSo)
197
    maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
198
    while maxErrorSa > approxPrecSa:
199
        #print "++Approximation error:", maxErrorSa
200
        sollya_lib_clear_obj(polySo)
201
        sollya_lib_clear_obj(intervalCenterSo)
202
        sollya_lib_clear_obj(maxErrorSo)
203
        shrinkFactorSa = RRR('5')/(maxErrorSa/approxPrecSa).log2().abs()
204
        #shrinkFactorSa = 1.5/(maxErrorSa/approxPrecSa)
205
        #errorRatioSa = approxPrecSa/maxErrorSa
206
        #print "Error ratio: ", errorRatioSa
207
        if shrinkFactorSa > intervalShrinkConstFactorSa:
208
            actualShrinkFactorSa = intervalShrinkConstFactorSa
209
            #print "Fixed"
210
        else:
211
            actualShrinkFactorSa = shrinkFactorSa
212
            #print "Computed",shrinkFactorSa,maxErrorSa
213
            #print shrinkFactorSa, maxErrorSa
214
        #print "Shrink factor", actualShrinkFactorSa
215
        currentUpperBoundSa = currentLowerBoundSa + \
216
                                  (currentUpperBoundSa - currentLowerBoundSa) * \
217
                                   actualShrinkFactorSa
218
        #print "Current upper bound:", currentUpperBoundSa
219
        sollya_lib_clear_obj(currentRangeSo)
220
        if currentUpperBoundSa <= currentLowerBoundSa or \
221
              currentUpperBoundSa == currentLowerBoundSa.nextabove():
222
            sollya_lib_clear_obj(absoluteErrorTypeSo)
223
            print "Can't find an interval."
224
            print "Use either or both a higher polynomial degree or a higher",
225
            print "internal precision."
226
            print "Aborting!"
227
            return (None, None, None, None)
228
        currentRangeSo = pobyso_bounds_to_range_sa_so(currentLowerBoundSa, 
229
                                                      currentUpperBoundSa)
230
        # print "New interval:",
231
        # pobyso_autoprint(currentRangeSo)
232
        #print "Second Taylor expansion call."
233
        (polySo, intervalCenterSo, maxErrorSo) = \
234
            pobyso_taylor_expansion_no_change_var_so_so(functionSo, degreeSo, 
235
                                                        currentRangeSo, 
236
                                                        absoluteErrorTypeSo)
237
        #maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo, RRR)
238
        #print "Max errorSo:",
239
        #pobyso_autoprint(maxErrorSo)
240
        maxErrorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
241
        #print "Max errorSa:", maxErrorSa
242
        #print "Sollya prec:",
243
        #pobyso_autoprint(sollya_lib_get_prec(None))
244
    sollya_lib_clear_obj(absoluteErrorTypeSo)
245
    return((polySo, currentRangeSo, intervalCenterSo, maxErrorSo))
246
# End slz_compute_polynomial_and_interval
247

    
248
def slz_compute_reduced_polynomials(reducedMatrix,
249
                                    knownMonomials,
250
                                    var1,
251
                                    var1Bound,
252
                                    var2,
253
                                    var2Bound):
254
    """
255
    From a reduced matrix, holding the coefficients, from a monomials list,
256
    from the bounds of each variable, compute the corresponding polynomials
257
    scaled back by dividing by the "right" powers of the variables bounds.
258
    
259
    The elements in knownMonomials must be of the "right" polynomial type.
260
    They set the polynomial type of the output polynomials list.
261
    """
262
    
263
    # TODO: check input arguments.
264
    reducedPolynomials = []
265
    #print "type var1:", type(var1), " - type var2:", type(var2)
266
    for matrixRow in reducedMatrix.rows():
267
        currentPolynomial = 0
268
        for colIndex in xrange(0, len(knownMonomials)):
269
            currentCoefficient = matrixRow[colIndex]
270
            if currentCoefficient != 0:
271
                #print "Current coefficient:", currentCoefficient
272
                currentMonomial = knownMonomials[colIndex]
273
                parentRing = currentMonomial.parent()
274
                #print "Monomial as multivariate polynomial:", \
275
                #currentMonomial, type(currentMonomial)
276
                degreeInVar1 = currentMonomial.degree(parentRing(var1))
277
                #print "Degree in var", var1, ":", degreeInVar1
278
                degreeInVar2 = currentMonomial.degree(parentRing(var2))
279
                #print "Degree in var", var2, ":", degreeInVar2
280
                if degreeInVar1 > 0:
281
                    currentCoefficient = \
282
                        currentCoefficient / var1Bound^degreeInVar1
283
                    #print "varBound1 in degree:", var1Bound^degreeInVar1
284
                    #print "Current coefficient(1)", currentCoefficient
285
                if degreeInVar2 > 0:
286
                    currentCoefficient = \
287
                        currentCoefficient / var2Bound^degreeInVar2
288
                    #print "Current coefficient(2)", currentCoefficient
289
                #print "Current reduced monomial:", (currentCoefficient * \
290
                #                                    currentMonomial)
291
                currentPolynomial += (currentCoefficient * currentMonomial)
292
                #print "Current polynomial:", currentPolynomial
293
            # End if
294
        # End for colIndex.
295
        #print "Type of the current polynomial:", type(currentPolynomial)
296
        reducedPolynomials.append(currentPolynomial)
297
    return reducedPolynomials 
298
# End slz_compute_reduced_polynomials.
299

    
300
def slz_compute_scaled_function(functionSa,
301
                                lowerBoundSa,
302
                                upperBoundSa,
303
                                floatingPointPrecSa):
304
    """
305
    From a function, compute the scaled function whose domain
306
    is included in [1, 2) and whose image is also included in [1,2).
307
    Return a tuple: 
308
    [0]: the scaled function
309
    [1]: the scaled domain lower bound
310
    [2]: the scaled domain upper bound
311
    [3]: the scaled image lower bound
312
    [4]: the scaled image upper bound
313
    """
314
    x = functionSa.variables()[0]
315
    # Reassert f as a function (an not a mere expression).
316
    
317
    # Scalling the domain -> [1,2[.
318
    boundsIntervalRifSa = RealIntervalField(floatingPointPrecSa)
319
    domainBoundsIntervalSa = boundsIntervalRifSa(lowerBoundSa, upperBoundSa)
320
    (domainScalingExpressionSa, invDomainScalingExpressionSa) = \
321
        slz_interval_scaling_expression(domainBoundsIntervalSa, x)
322
    print "domainScalingExpression for argument :", domainScalingExpressionSa
323
    print "f: ", f
324
    ff = f.subs({x : domainScalingExpressionSa})
325
    #ff = f.subs_expr(x==domainScalingExpressionSa)
326
    domainScalingFunction(x) = invDomainScalingExpressionSa
327
    scaledLowerBoundSa = domainScalingFunction(lowerBoundSa).n()
328
    scaledUpperBoundSa = domainScalingFunction(upperBoundSa).n()
329
    print 'ff:', ff, "- Domain:", scaledLowerBoundSa, scaledUpperBoundSa
330
    #
331
    # Scalling the image -> [1,2[.
332
    flbSa = f(lowerBoundSa).n()
333
    fubSa = f(upperBoundSa).n()
334
    if flbSa <= fubSa: # Increasing
335
        imageBinadeBottomSa = floor(flbSa.log2())
336
    else: # Decreasing
337
        imageBinadeBottomSa = floor(fubSa.log2())
338
    print 'ff:', ff, '- Image:', flbSa, fubSa, imageBinadeBottomSa
339
    imageBoundsIntervalSa = boundsIntervalRifSa(flbSa, fubSa)
340
    (imageScalingExpressionSa, invImageScalingExpressionSa) = \
341
        slz_interval_scaling_expression(imageBoundsIntervalSa, x)
342
    iis = invImageScalingExpressionSa.function(x)
343
    fff = iis.subs({x:ff})
344
    print "fff:", fff,
345
    print " - Image:", fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)
346
    return([fff, scaledLowerBoundSa, scaledUpperBoundSa, \
347
            fff(scaledLowerBoundSa), fff(scaledUpperBoundSa)])
348

    
349
def slz_float_poly_of_float_to_rat_poly_of_rat(polyOfFloat):
350
    # Create a polynomial over the rationals.
351
    polynomialRing = QQ[str(polyOfFloat.variables()[0])]
352
    return(polynomialRing(polyOfFloat))
353
# End slz_float_poly_of_float_to_rat_poly_of_rat.
354

    
355
def slz_get_intervals_and_polynomials(functionSa, degreeSa, 
356
                                      lowerBoundSa, 
357
                                      upperBoundSa, floatingPointPrecSa, 
358
                                      internalSollyaPrecSa, approxPrecSa):
359
    """
360
    Under the assumption that:
361
    - functionSa is monotonic on the [lowerBoundSa, upperBoundSa] interval;
362
    - lowerBound and upperBound belong to the same binade.
363
    from a:
364
    - function;
365
    - a degree
366
    - a pair of bounds;
367
    - the floating-point precision we work on;
368
    - the internal Sollya precision;
369
    - the requested approximation error
370
    The initial interval is, possibly, splitted into smaller intervals.
371
    It return a list of tuples, each made of:
372
    - a first polynomial (without the changed variable f(x) = p(x-x0));
373
    - a second polynomial (with a changed variable f(x) = q(x))
374
    - the approximation interval;
375
    - the center, x0, of the interval;
376
    - the corresponding approximation error.
377
    TODO: fix endless looping for some parameters sets.
378
    """
379
    resultArray = []
380
    # Set Sollya to the necessary internal precision.
381
    precChangedSa = False
382
    currentSollyaPrecSo = pobyso_get_prec_so()
383
    currentSollyaPrecSa = pobyso_constant_from_int_so_sa(currentSollyaPrecSo)
384
    if internalSollyaPrecSa > currentSollyaPrecSa:
385
        pobyso_set_prec_sa_so(internalSollyaPrecSa)
386
        precChangedSa = True
387
    #
388
    x = functionSa.variables()[0] # Actual variable name can be anything.
389
    # Scaled function: [1=,2] -> [1,2].
390
    (fff, scaledLowerBoundSa, scaledUpperBoundSa,                             \
391
            scaledLowerBoundImageSa, scaledUpperBoundImageSa) =               \
392
                slz_compute_scaled_function(functionSa,                       \
393
                                            lowerBoundSa,                     \
394
                                            upperBoundSa,                     \
395
                                            floatingPointPrecSa)
396
    #
397
    print "Approximation precision: ", RR(approxPrecSa)
398
    # Prepare the arguments for the Taylor expansion computation with Sollya.
399
    functionSo = pobyso_parse_string_sa_so(fff._assume_str())
400
    degreeSo = pobyso_constant_from_int_sa_so(degreeSa)
401
    scaledBoundsSo = pobyso_bounds_to_range_sa_so(scaledLowerBoundSa, 
402
                                                  scaledUpperBoundSa)
403
    # Compute the first Taylor expansion.
404
    (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
405
        slz_compute_polynomial_and_interval(functionSo, degreeSo,
406
                                        scaledLowerBoundSa, scaledUpperBoundSa,
407
                                        approxPrecSa, internalSollyaPrecSa)
408
    if polySo is None:
409
        print "slz_get_intervals_and_polynomials: Aborting and returning None!"
410
        if precChangedSa:
411
            pobyso_set_prec_so_so(currentSollyaPrecSo)
412
            sollya_lib_clear_obj(currentSollyaPrecSo)
413
        sollya_lib_clear_obj(functionSo)
414
        sollya_lib_clear_obj(degreeSo)
415
        sollya_lib_clear_obj(scaledBoundsSo)
416
        return None
417
    realIntervalField = RealIntervalField(max(lowerBoundSa.parent().precision(),
418
                                              upperBoundSa.parent().precision()))
419
    boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
420
    errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
421
    #print "First approximation error:", errorSa
422
    # If the error and interval are OK a the first try, just return.
423
    if boundsSa.endpoints()[1] >= scaledUpperBoundSa:
424
        # Change variable stuff in Sollya x -> x0-x.
425
        changeVarExpressionSo = sollya_lib_build_function_sub( \
426
                              sollya_lib_build_function_free_variable(), \
427
                              sollya_lib_copy_obj(intervalCenterSo))
428
        polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
429
        sollya_lib_clear_obj(changeVarExpressionSo)
430
        resultArray.append((polySo, polyVarChangedSo, boundsSo, \
431
                            intervalCenterSo, maxErrorSo))
432
        if internalSollyaPrecSa != currentSollyaPrecSa:
433
            pobyso_set_prec_sa_so(currentSollyaPrecSa)
434
        sollya_lib_clear_obj(currentSollyaPrecSo)
435
        sollya_lib_clear_obj(functionSo)
436
        sollya_lib_clear_obj(degreeSo)
437
        sollya_lib_clear_obj(scaledBoundsSo)
438
        #print "Approximation error:", errorSa
439
        return resultArray
440
    # The returned interval upper bound does not reach the requested upper
441
    # upper bound: compute the next upper bound.
442
    # The following ratio is always >= 1
443
    currentErrorRatio = approxPrecSa / errorSa
444
    # Starting point for the next upper bound.
445
    currentScaledUpperBoundSa = boundsSa.endpoints()[1]
446
    boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
447
    # Compute the increment. 
448
    if currentErrorRatio > RR('1000'): # ]1.5, infinity[
449
        currentScaledUpperBoundSa += \
450
                        currentErrorRatio * boundsWidthSa * 2
451
    else:  # [1, 1.5]
452
        currentScaledUpperBoundSa += \
453
                        (RR('1.0') + currentErrorRatio.log() / 500) * boundsWidthSa
454
    # Take into account the original interval upper bound.                     
455
    if currentScaledUpperBoundSa > scaledUpperBoundSa:
456
        currentScaledUpperBoundSa = scaledUpperBoundSa
457
    # Compute the other expansions.
458
    while boundsSa.endpoints()[1] < scaledUpperBoundSa:
459
        currentScaledLowerBoundSa = boundsSa.endpoints()[1]
460
        (polySo, boundsSo, intervalCenterSo, maxErrorSo) = \
461
            slz_compute_polynomial_and_interval(functionSo, degreeSo,
462
                                            currentScaledLowerBoundSa, 
463
                                            currentScaledUpperBoundSa, 
464
                                            approxPrecSa, 
465
                                            internalSollyaPrecSa)
466
        errorSa = pobyso_get_constant_as_rn_with_rf_so_sa(maxErrorSo)
467
        if  errorSa < approxPrecSa:
468
            # Change variable stuff
469
            #print "Approximation error:", errorSa
470
            changeVarExpressionSo = sollya_lib_build_function_sub(
471
                                    sollya_lib_build_function_free_variable(), 
472
                                    sollya_lib_copy_obj(intervalCenterSo))
473
            polyVarChangedSo = sollya_lib_evaluate(polySo, changeVarExpressionSo) 
474
            sollya_lib_clear_obj(changeVarExpressionSo)
475
            resultArray.append((polySo, polyVarChangedSo, boundsSo, \
476
                                intervalCenterSo, maxErrorSo))
477
        boundsSa = pobyso_range_to_interval_so_sa(boundsSo, realIntervalField)
478
        # Compute the next upper bound.
479
        # The following ratio is always >= 1
480
        currentErrorRatio = approxPrecSa / errorSa
481
        # Starting point for the next upper bound.
482
        currentScaledUpperBoundSa = boundsSa.endpoints()[1]
483
        boundsWidthSa = boundsSa.endpoints()[1] - boundsSa.endpoints()[0]
484
        # Compute the increment. 
485
        if currentErrorRatio > RR('1000'): # ]1.5, infinity[
486
            currentScaledUpperBoundSa += \
487
                            currentErrorRatio * boundsWidthSa * 2
488
        else:  # [1, 1.5]
489
            currentScaledUpperBoundSa += \
490
                            (RR('1.0') + currentErrorRatio.log()/500) * boundsWidthSa
491
        #print "currentErrorRatio:", currentErrorRatio
492
        #print "currentScaledUpperBoundSa", currentScaledUpperBoundSa
493
        # Test for insufficient precision.
494
        if currentScaledUpperBoundSa == scaledLowerBoundSa:
495
            print "Can't shrink the interval anymore!"
496
            print "You should consider increasing the Sollya internal precision"
497
            print "or the polynomial degree."
498
            print "Giving up!"
499
            if internalSollyaPrecSa != currentSollyaPrecSa:
500
                pobyso_set_prec_sa_so(currentSollyaPrecSa)
501
            sollya_lib_clear_obj(currentSollyaPrecSo)
502
            sollya_lib_clear_obj(functionSo)
503
            sollya_lib_clear_obj(degreeSo)
504
            sollya_lib_clear_obj(scaledBoundsSo)
505
            return None
506
        if currentScaledUpperBoundSa > scaledUpperBoundSa:
507
            currentScaledUpperBoundSa = scaledUpperBoundSa
508
    if internalSollyaPrecSa > currentSollyaPrecSa:
509
        pobyso_set_prec_so_so(currentSollyaPrecSo)
510
    sollya_lib_clear_obj(currentSollyaPrecSo)
511
    sollya_lib_clear_obj(functionSo)
512
    sollya_lib_clear_obj(degreeSo)
513
    sollya_lib_clear_obj(scaledBoundsSo)
514
    return(resultArray)
515
# End slz_get_intervals_and_polynomials
516

    
517

    
518
def slz_interval_scaling_expression(boundsInterval, expVar):
519
    """
520
    Compute the scaling expression to map an interval that span at most
521
    a single binade to [1, 2) and the inverse expression as well.
522
    Not very sure that the transformation makes sense for negative numbers.
523
    """
524
    # The scaling offset is only used for negative numbers.
525
    if abs(boundsInterval.endpoints()[0]) < 1:
526
        if boundsInterval.endpoints()[0] >= 0:
527
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
528
            invScalingCoeff = 1/scalingCoeff
529
            return((scalingCoeff * expVar, 
530
                    invScalingCoeff * expVar))
531
        else:
532
            scalingCoeff = \
533
                2^(floor((-boundsInterval.endpoints()[0]).log2()) - 1)
534
            scalingOffset = -3 * scalingCoeff
535
            return((scalingCoeff * expVar + scalingOffset,
536
                    1/scalingCoeff * expVar + 3))
537
    else: 
538
        if boundsInterval.endpoints()[0] >= 0:
539
            scalingCoeff = 2^floor(boundsInterval.endpoints()[0].log2())
540
            scalingOffset = 0
541
            return((scalingCoeff * expVar, 
542
                    1/scalingCoeff * expVar))
543
        else:
544
            scalingCoeff = \
545
                2^(floor((-boundsInterval.endpoints()[1]).log2()))
546
            scalingOffset =  -3 * scalingCoeff
547
            #scalingOffset = 0
548
            return((scalingCoeff * expVar + scalingOffset,
549
                    1/scalingCoeff * expVar + 3))
550

    
551
   
552
def slz_interval_and_polynomial_to_sage(polyRangeCenterErrorSo):
553
    """
554
    Compute the Sage version of the Taylor polynomial and it's
555
    companion data (interval, center...)
556
    The input parameter is a five elements tuple:
557
    - [0]: the polyomial (without variable change), as polynomial over a
558
           real ring;
559
    - [1]: the polyomial (with variable change done in Sollya), as polynomial
560
           over a real ring;
561
    - [2]: the interval (as Sollya range);
562
    - [3]: the interval center;
563
    - [4]: the approximation error. 
564
    
565
    The function return a 5 elements tuple: formed with all the 
566
    input elements converted into their Sollya counterpart.
567
    """
568
    polynomialSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[0])
569
    polynomialChangedVarSa = pobyso_get_poly_so_sa(polyRangeCenterErrorSo[1])
570
    intervalSa = \
571
            pobyso_get_interval_from_range_so_sa(polyRangeCenterErrorSo[2])
572
    centerSa = \
573
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[3])
574
    errorSa = \
575
            pobyso_get_constant_as_rn_with_rf_so_sa(polyRangeCenterErrorSo[4])
576
    return((polynomialSa, polynomialChangedVarSa, intervalSa, centerSa, errorSa))
577
    # End slz_interval_and_polynomial_to_sage
578

    
579
def slz_rat_poly_of_int_to_poly_for_coppersmith(ratPolyOfInt, 
580
                                                precision,
581
                                                targetHardnessToRound,
582
                                                variable1,
583
                                                variable2):
584
    """
585
    Creates a new multivariate polynomial with integer coefficients for use
586
     with the Coppersmith method.
587
    A the same time it computes :
588
    - 2^K (N);
589
    - 2^k (bound on the second variable)
590
    - lcm
591

    
592
    :param ratPolyOfInt: a polynomial with rational coefficients and integer
593
                         variables.
594
    :param precision: the precision of the floating-point coefficients.
595
    :param targetHardnessToRound: the hardness to round we want to check.
596
    :param variable1: the first variable of the polynomial (an expression).
597
    :param variable2: the second variable of the polynomial (an expression).
598
    
599
    :returns: a 4 elements tuple:
600
                - the polynomial;
601
                - the modulus (N);
602
                - the t bound;
603
                - the lcm used to compute the integral coefficients and the 
604
                  module.
605
    """
606
    # Create a new integer polynomial ring.
607
    IP = PolynomialRing(ZZ, name=str(variable1) + "," + str(variable2))
608
    # Coefficients are issued in the increasing power order.
609
    ratPolyCoefficients = ratPolyOfInt.coefficients()
610
    # Print the reversed list for debugging.
611
    print "Rational polynomial coefficients:", ratPolyCoefficients[::-1]
612
    # Build the list of number we compute the lcm of.
613
    coefficientDenominators = sro_denominators(ratPolyCoefficients)
614
    coefficientDenominators.append(2^precision)
615
    coefficientDenominators.append(2^(targetHardnessToRound + 1))
616
    leastCommonMultiple = lcm(coefficientDenominators)
617
    # Compute the expression corresponding to the new polynomial
618
    coefficientNumerators =  sro_numerators(ratPolyCoefficients)
619
    #print coefficientNumerators
620
    polynomialExpression = 0
621
    power = 0
622
    # Iterate over two lists at the same time, stop when the shorter is
623
    # exhausted.
624
    for numerator, denominator in \
625
                        zip(coefficientNumerators, coefficientDenominators):
626
        multiplicator = leastCommonMultiple / denominator 
627
        newCoefficient = numerator * multiplicator
628
        polynomialExpression += newCoefficient * variable1^power
629
        power +=1
630
    polynomialExpression += - variable2
631
    return (IP(polynomialExpression),
632
            leastCommonMultiple / 2^precision, # 2^K or N.
633
            leastCommonMultiple / 2^(targetHardnessToRound + 1), # tBound
634
            leastCommonMultiple) # If we want to make test computations.
635
        
636
# End slz_ratPoly_of_int_to_poly_for_coppersmith
637

    
638
def slz_rat_poly_of_rat_to_rat_poly_of_int(ratPolyOfRat, 
639
                                          precision):
640
    """
641
    Makes a variable substitution into the input polynomial so that the output
642
    polynomial can take integer arguments.
643
    All variables of the input polynomial "have precision p". That is to say
644
    that they are rationals with denominator == 2^(precision - 1): 
645
    x = y/2^(precision - 1).
646
    We "incorporate" these denominators into the coefficients with, 
647
    respectively, the "right" power.
648
    """
649
    polynomialField = ratPolyOfRat.parent()
650
    polynomialVariable = ratPolyOfRat.variables()[0]
651
    #print "The polynomial field is:", polynomialField
652
    return \
653
        polynomialField(ratPolyOfRat.subs({polynomialVariable : \
654
                                   polynomialVariable/2^(precision-1)}))
655
    
656
    # Return a tuple:
657
    # - the bivariate integer polynomial in (i,j);
658
    # - 2^K
659
# End slz_rat_poly_of_rat_to_rat_poly_of_int
660

    
661

    
662
print "\t...sageSLZ loaded"