Statistiques
| Branche: | Révision :

cvp / src / functions_for_cvp.sage @ 6a967c2d

Historique | Voir | Annoter | Télécharger (47,57 ko)

1
"""
2
@file functions_for_cvp.sage
3
@package functions_for_cvp
4
Auxiliary functions used for CVP.
5

    
6
@author S.T.
7
"""
8
sys.stderr.write("Functions for CVP loading...\n")
9
#
10
def cvp_affine_from_chebyshev(lowerBound, upperBound):
11
    """
12
    Compute the affine parameters to map [-1, 1] to the interval given
13
    as argument.
14
    @param: lowerBound (of the target interval)
15
    @param: upperBound (of the target interval)
16
    @return the (scalingCoefficient, offset) tuple.
17
    """
18
    ## Check bounds consistency. Bounds must differ.
19
    if lowerBound >= upperBound:
20
        return None
21
    scalingCoefficient = (upperBound - lowerBound) / 2
22
    offset             = (lowerBound + upperBound) / 2
23
    return (scalingCoefficient, offset)
24
# End cvp_affine_from_chebyshev
25
#
26
def cvp_affine_to_chebyshev(lowerBound, upperBound):
27
    """
28
    Compute the affine parameters to map the interval given
29
    as argument to  [-1, 1].
30
    @param lowerBound (of the target interval)
31
    @param upperBound (of the target interval)
32
    @return the (scalingCoefficient, offset) tuple.
33
    """
34
    ## Check bounds consistency. Bounds must differ.
35
    if lowerBound >= upperBound:
36
        return None
37
    scalingCoefficient = 2 / (upperBound - lowerBound)
38
    ## If bounds are symmetrical with relation to 0, return 0 straight before
39
    #  attempting a division by 0.
40
    if lowerBound == -upperBound:
41
        offset         = 0
42
    else:
43
        offset         =  (lowerBound + upperBound) / (lowerBound - upperBound)
44
    return (scalingCoefficient, offset)
45
# End cvp_affine_to_chebyshev
46
#
47
def cvp_babai(readBasis, readBasisGso, vect):
48
    """
49
    Closest plane vector implementation as per Babaï.
50
    @param readBasis   : a lattice basis, preferably a reduced one; 
51
    @param readBasisGSO: the GSO of the previous basis;
52
    @param vect        : a vector, in coordinated in the ambient 
53
                        space of the lattice
54
    @return the closest vector to the input, in coordinates in the 
55
                        ambient space of the lattice.
56
    """
57
    ## A deep copy is not necessary here.
58
    curVect = copy(vect)
59
    #print "cvp_babai - Vector:", vect
60
    ## From index of last row down to 0.
61
    for vIndex in xrange(len(readBasis.rows())-1, -1, -1):
62
        curRBGSO = readBasisGso.row(vIndex)
63
        curVect = curVect - \
64
                  (round((curVect * curRBGSO)  / (curRBGSO * curRBGSO)) * \
65
                   readBasis.row(vIndex)) 
66
    return vect - curVect
67
# End cvp_babai
68
#
69
def cvp_bkz_reduce(intBasis, blockSize):
70
    """
71
    Thin and simplistic wrapper the HKZ function of the FP_LLL module.
72
    """
73
    fplllIntBasis = FP_LLL(intBasis)
74
    fplllIntBasis.BKZ(blockSize)
75
    return fplllIntBasis._sage_()
76
# End cvp_hkz_reduce.
77
#
78
def cvp_candidate_vectors(intVect, diffsList):
79
    """
80
    Computes a list of best approximation vectors based on the CVP
81
    computed vector using the differences between this vector and the 
82
    target function vector.
83
    @param intVect the vector from which candidates are built
84
    @param diffsList the list of the values that will be appended to the
85
           initial vector to form the candidates
86
    @return A matrix made of candidates rows or None if there is some invalid
87
            argument.
88
    @par How it works
89
    The idea is to create all the possible vectors based on the computed CVP
90
    vector and its differences to the target. Those are collected in 
91
    diffsList.@n
92
    From the vector we create all the possible combinations between the original
93
    value and the differences in diffsList. Some of those can be equal to 0.
94
    They are not taken into account. If nonZeroDiffsCount is the number of 
95
    diffsList element different from 0, there are 2^nonZeroDiffsCount 
96
    possible variants.@n
97
    To compute those, we build a 2^nonZeroDiffsCount rows matrix. For the
98
    first element in the vector corresponding to a non-zero in diffsList, we
99
    switch values in the matrix at every row.@n
100
    For the second such element (non-zero corresponding diffsList element), we
101
    switch values in the matrix at every second row for a string of two similar 
102
    values.@n
103
    For the third such element, change happens at every fourth row, for a 
104
    string of four similar values. And so on...
105
    @par Example
106
    @verbatim
107
    vect        =  [10, 20, 30, 40, 50]
108
    diffsList   =  [ 1,  0, -1,  0,  1]
109
    vectCandMat = [[10, 20, 30, 40, 50],
110
                   [11, 20, 30, 40, 50],
111
                   [10, 20, 29, 40, 50],
112
                   [11, 20, 29, 40, 50],
113
                   [10, 20, 30, 40, 51],
114
                   [11, 20, 30, 40, 51],
115
                   [10, 20, 29, 40, 51],
116
                   [11, 20, 29, 40, 51]]
117
    @endverbatim
118
"""
119
    ## Arguments check.
120
    vectLen = len(intVect)
121
    if vectLen != len(diffsList):
122
        return None
123
    #
124
    ## Compute the number of diffsList elements different from 0.
125
    nonZeroDiffsCount = 0
126
    for elem in diffsList:
127
        if elem != 0:
128
            nonZeroDiffsCount += 1
129
    if nonZeroDiffsCount == 0:
130
        return matrix(ZZ, 0, vectLen)
131
    numRows = 2^nonZeroDiffsCount
132
    vectMat = matrix(ZZ, numRows, vectLen)
133
    for rowIndex in xrange(0,numRows):
134
        effColIndex = 0
135
        for colIndex in xrange(0,vectLen):
136
            vectMat[rowIndex, colIndex] = intVect[colIndex]
137
            if diffsList[colIndex] != 0:
138
                if (rowIndex % 2^(effColIndex + 1)) >= 2^effColIndex:
139
                    vectMat[rowIndex, colIndex] += diffsList[colIndex]
140
                effColIndex += 1
141
    return vectMat
142
# End cvp_candidate_vectors
143
#
144
def cvp_coefficients_bounds_projection(executablePath, arguments):
145
    """
146
    Compute the min and max of the coefficients with linear
147
    programming projection.
148
    @param executablePath: the path to the binary program;
149
    @param arguments: a list of arguments to be givent to the binary
150
    @return: the min and max coefficients value arrays (in this order).
151
    """
152
    from subprocess import check_output
153
    commandLine = [executablePath] + arguments
154
    ## Get rid of output on stderr.
155
    devNull = open("/dev/null", "rw")
156
    ## Run ther program.
157
    otp = check_output(commandLine, stderr=devNull)
158
    devNull.close()
159
    ## Recover the results
160
    bounds = sage_eval(otp)
161
    minCoeffsExpList = []
162
    maxCoeffsExpList = [] 
163
    #print "bounds:", bounds
164
    for boundsPair in bounds:
165
        minCoeffsExpList.append(boundsPair[0])
166
        maxCoeffsExpList.append(boundsPair[1])
167
    print "minCoeffsExpList:", minCoeffsExpList
168
    print "maxCoeffsExpList:", maxCoeffsExpList
169
    return (minCoeffsExpList, maxCoeffsExpList)
170
# End cvp_coefficients_bounds_projection
171

    
172
def cvp_coefficients_bounds_projection_exps(executablePath, 
173
                                            arguments, 
174
                                            realField = None):
175
    """
176
    Compute the min and max exponents of the coefficients with linear
177
    programming projection.
178
    @param executablePath: the path to the binary program;
179
    @param arguments: a list of arguments to be givent to the binary
180
    @param realField: the realField to use for floating-point conversion.
181
    @return: the min and max exponents arrays (in this order).
182
    """
183
    ## If no realField is givne, fall back on RR.
184
    if realField is None:
185
        realField = RR
186
    (minCoeffsExpList, maxCoeffsExpList) = \
187
        cvp_coefficients_bounds_projection(executablePath,arguments)
188
    for index in xrange(0, len(minCoeffsExpList)):
189
        minCoeffsExpList[index] = \
190
            realField(minCoeffsExpList[index]).abs().log2().floor()
191
        maxCoeffsExpList[index] = \
192
            realField(maxCoeffsExpList[index]).abs().log2().floor()
193
    return (minCoeffsExpList, maxCoeffsExpList)
194
# End cvp_coefficients_bounds_projection_exps
195

    
196
def cvp_chebyshev_maxis_1k(numPoints, realField, contFracMaxErr = None):
197
    """
198
    Compute the Chebyshev maxis for some polynomial degree (numPoints, for the
199
    zeros, numPoints + 1 for the maxis) scaled to the [lowerBound, upperBound]
200
    interval.
201
    The list is returned as a row floating-point numbers is contFracMaxErr 
202
    is None.
203
    Otherwise elements are transformed into rational numbers. 
204
    """
205
    if numPoints < 1:
206
        return None
207
    if numPoints == 0:
208
        return [0]
209
    ## Check that realField has a "prec()" member.
210
    try:
211
        realField.prec()
212
    except:
213
        return None
214
    #
215
    maxisList = []
216
    ## n extrema are given by a degree (n-1) Chebyshev polynomial.
217
    #  formulas: cos ((i * pi) / (numPoints - 1)) for i = 0,...,numPoints-1.
218
    #  Source: https://en.wikipedia.org/wiki/Chebyshev_polynomials
219
    for index in xrange(0, numPoints):
220
        ## When number of points is odd (then, the Chebyshev degree is odd two), 
221
        #  the central point is 0. */
222
        if (numPoints % 2 == 1) and ((2 * index + 1) == numPoints):
223
            if contFracMaxErr is None:
224
                maxisList.append(realField(0))
225
            else:
226
                maxisList.append(0)
227
        else:
228
            currentCheby = \
229
                ((realField(index) * realField(pi)) / 
230
                 realField(numPoints-1)).cos()
231
            #print  "Current Cheby:", currentCheby
232
            ## Result is negated to have an increasing order list.
233
            if contFracMaxErr is None:
234
                maxisList.append(-currentCheby)
235
            else:
236
                maxisList.append(-currentCheby.nearby_rational(contFracMaxErr))
237
            #print "Relative error:", (currentCheby/maxisList[index])
238
    return maxisList
239
# End cvp_chebyshev_maxis_1k.
240
#
241
def cvp_chebyshev_maxis_for_interval(lowerBound, 
242
                                     upperBound, 
243
                                     numPoints,
244
                                     realField = None, 
245
                                     contFracMaxErr = None):
246
    """
247
    Compute the Chebyshev maxis for some polynomial degree (numPoints, for the
248
    maxis) scaled to the [lowerBound, upperBound] interval.
249
    If no contFracMaxErr argument is given, we return the list as "raw"
250
    floating-points. Otherwise, rational numbers are returned.
251
    """
252
    ## Arguments check.
253
    if lowerBound >= upperBound:
254
        return None
255
    if numPoints < 1:
256
        return None
257
    ## If no realField argument is given try to retrieve it from the 
258
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
259
    if realField is None:
260
        try:
261
            ### Force failure if parent does not have prec() member.
262
            lowerBound.parent().prec()
263
            ### If no exception is thrown, set realField.
264
            realField = lowerBound.parent()
265
        except:
266
            realField = RR
267
    #print "Real field:", realField
268
    ## We want "raw"floating-point nodes to only have one final rounding.
269
    chebyshevMaxisList = \
270
        cvp_chebyshev_maxis_1k(numPoints, realField)
271
    scalingFactor, offset = cvp_affine_from_chebyshev(lowerBound, upperBound)
272
    for index in xrange(0, len(chebyshevMaxisList)):
273
        chebyshevMaxisList[index] = \
274
            chebyshevMaxisList[index] * scalingFactor + offset
275
        if not contFracMaxErr is None:
276
            chebyshevMaxisList[index] = \
277
                chebyshevMaxisList[index].nearby_rational(contFracMaxErr)
278
    return chebyshevMaxisList                                                             
279
# End cvp_chebyshev_maxis_for_interval.
280
#
281
def cvp_chebyshev_zeros_1k(numPoints, realField, contFracMaxErr = None):
282
    """
283
    Compute the Chebyshev zeros for some polynomial degree (numPoints, for the
284
    zeros) scaled to the [lowerBound, upperBound] interval.
285
    The list is returned as row floating-point numbers is contFracMaxErr is None.
286
    Otherwise elements are transformed into rational numbers. 
287
    """
288
    if numPoints < 1:
289
        return None
290
    if numPoints == 0:
291
        return [0]
292
    ## Check that realField has a "prec()" member.
293
    try:
294
        realField.prec()
295
    except:
296
        return None
297
    #
298
    zerosList = []
299
    ## formulas: cos (((2*i - 1) * pi) / (2*numPoints)) for i = 1,...,numPoints.
300
    #  If i is -1-shifted, as in the following loop, formula is 
301
    #  cos (((2*i + 1) * pi) / (2*numPoints)) for i = 0,...,numPoints-1.
302
    for index in xrange(0, numPoints):
303
        ## When number of points is odd (then, the Chebyshev degree is odd two), 
304
        #  the central point is 0. */
305
        if (numPoints % 2 == 1) and ((2 * index + 1) == numPoints):
306
            if contFracMaxErr is None:
307
                zerosList.append(realField(0))
308
            else:
309
                zerosList.append(0)
310
        else:
311
            currentCheby = \
312
                ((realField(2*index+1) * realField(pi)) / 
313
                 realField(2*numPoints)).cos()
314
            #print  "Current Cheby:", currentCheby
315
            ## Result is negated to have an increasing order list.
316
            if contFracMaxErr is None:
317
                zerosList.append(-currentCheby)
318
            else:
319
                zerosList.append(-currentCheby.nearby_rational(contFracMaxErr))
320
            #print "Relative error:", (currentCheby/zerosList[index])
321
    return zerosList
322
# End cvp_chebyshev_zeros_1k.
323
#
324
def cvp_chebyshev_zeros_for_interval(lowerBound, 
325
                                     upperBound, 
326
                                     numPoints,
327
                                     realField = None, 
328
                                     contFracMaxErr = None):
329
    """
330
    Compute the Chebyshev zeros for some polynomial degree (numPoints, for the
331
    zeros) scaled to the [lowerBound, upperBound] interval.
332
    If no contFracMaxErr argument is given, we return the list as "raw"
333
    floating-points. Otherwise, rational numbers are returned.
334
    """
335
    ## Arguments check.
336
    if lowerBound >= upperBound:
337
        return None
338
    if numPoints < 1:
339
        return None
340
    ## If no realField argument is given try to retrieve it from the 
341
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
342
    if realField is None:
343
        try:
344
            ### Force failure if parent does not have prec() member.
345
            lowerBound.parent().prec()
346
            ### If no exception is thrown, set realField.
347
            realField = lowerBound.parent()
348
        except:
349
            realField = RR
350
    #print "Real field:", realField
351
    ## We want "raw"floating-point nodes to only have one final rounding.
352
    chebyshevZerosList = \
353
        cvp_chebyshev_zeros_1k(numPoints, realField)
354
    scalingFactor, offset = cvp_affine_from_chebyshev(lowerBound, upperBound)
355
    for index in xrange(0, len(chebyshevZerosList)):
356
        chebyshevZerosList[index] = \
357
            chebyshevZerosList[index] * scalingFactor + offset
358
        if not contFracMaxErr is None:
359
            chebyshevZerosList[index] = \
360
                chebyshevZerosList[index].nearby_rational(contFracMaxErr)
361
    return chebyshevZerosList                                                             
362
# End cvp_chebyshev_zeros_for_interval.
363
#
364
def cvp_common_scaling_exp(precision, 
365
                           precisionFraction = None):
366
    """
367
    Compute the common scaling factor (a power of 2).
368
    The exponent is a fraction of the precision;
369
    A extra factor is computed for the vector,
370
    exra factors are computed for the basis vectors, all in the corresponding
371
    functions.
372
    """
373
    ## Set a default value for the precisionFraction to 1/2.
374
    if precisionFraction is None:
375
        precisionFraction = 3/4
376
    """
377
    minBasisVectsNorm = oo
378
    currentNorm = 0
379
    for vect in floatBasis.rows():
380
        currentNorm = vect.norm()
381
        print "Current norm:", currentNorm
382
        if currentNorm < minBasisVectsNorm:
383
            minBasisVectsNorm = currentNorm
384
    currentNorm = funcVect.norm()
385
    print "Current norm:", currentNorm
386
    if currentNorm < minBasisVectsNorm:
387
        minBasisVectsNorm = currentNorm
388
    ## Check if a push is need because the smallest norm is below 0.    
389
    powerForMinNorm = floor(log(minBasisVectsNorm)/log2)
390
    print "Power for min norm :", powerForMinNorm
391
    print "Power for precision:", ceil(precision*precisionFraction)
392
    if powerForMinNorm < 0:
393
        return 2^(ceil(precision*precisionFraction) - powerFromMinNorm)
394
    else:
395
    """
396
    return ceil(precision * precisionFraction)
397
# End cvp_common_scaling_factor.
398
#
399
def cvp_cvp_polynomial(func, 
400
                       lowerBound, 
401
                       upperBound,
402
                       coeffsExpList,
403
                       coeffsPrecList, 
404
                       minCoeffsBoundExpList, 
405
                       maxCoeffsBoundExpList,
406
                       pointsList,
407
                       realField):
408
    """
409
    Compute a polynomial based on a CVP vector for a function approximation. 
410
    """
411
    ## Build the basis for CVP.
412
    ### Common scaling exponent.
413
    commonScalingExp = cvp_common_scaling_exp(realField.prec())
414
    ### Monomials list derived from exponenets list.
415
    monomialsList = cvp_monomials_list(coeffsExpList)
416
    ### Float basis first
417
    floatBasis = cvp_float_basis(monomialsList, pointsList, realField)
418
    ### Integral basis.
419
    extraScalingExpsList = \
420
        cvp_extra_basis_scaling_exps(coeffsPrecList, minCoeffsBoundExpList)
421
    intBasis = \
422
        cvp_int_basis(floatBasis, commonScalingExp, extraScalingExpsList)
423
    ### The reduced basis.
424
    redIntBasis = cvp_lll_reduce(intBasis)
425
    ### The reduced basis GSO.
426
    (redIntBasisGso, mu) = redIntBasis.gram_schmidt()
427
    ## Function vector.
428
    #### Floating-point vector.
429
    floatFuncVect = \
430
        cvp_float_vector_for_approx(func, pointsList, realField)
431
    extraFuncScalingExp = \
432
        cvp_extra_function_scaling_exp(floatBasis, 
433
                                       floatFuncVect, 
434
                                       maxCoeffsBoundExpList)
435
    #### Integral vector.
436
    intFuncVect   = \
437
        cvp_int_vector_for_approx(floatFuncVect, 
438
                                  commonScalingExp, 
439
                                  extraFuncScalingExp)
440
    ## CPV vector
441
    ### In ambient basis.
442
    cvpVectInAmbient = cvp_babai(redIntBasis, redIntBasisGso, intFuncVect)
443
    ### In intBasis.
444
    cvpVectInIntBasis = cvp_vector_in_basis(cvpVectInAmbient, intBasis)
445
    ## Polynomial coefficients
446
    cvpPolynomialCoeffs = \
447
        cvp_polynomial_coeffs_from_vect(cvpVectInIntBasis, 
448
                                        coeffsPrecList, 
449
                                        minCoeffsBoundExpList, 
450
                                        extraFuncScalingExp)
451
    #print "CVP polynomial coefficients:"
452
    #print cvpPolynomialCoeffs
453
    ## Polynomial in Sage.
454
    cvpPolySa =  \
455
        cvp_polynomial_from_coeffs_and_exps(cvpPolynomialCoeffs,
456
                                            coeffsExpList,
457
                                            realField[x])
458
    #print cvpPolySa
459
    return cvpPolySa
460
# End cvp_cvp_polynomial.
461
#
462
def cvp_evaluation_points_list(funct, 
463
                               maxDegree, 
464
                               lowerBound, 
465
                               upperBound, 
466
                               realField = None):
467
    """
468
    Compute a list of points for the vector creation.
469
    Strategy:
470
    - compute the zeros of the function-remez;
471
    - compute the zeros of the function-rounded_remez;
472
    - compute the Chebyshev points.
473
    Merge the whole thing.
474
    """
475
    
476
# End cvp_evaluation_points_list.
477
#
478
def cvp_extra_basis_scaling_exps(precsList, minExponentsList):
479
    """
480
    Computes the exponents for the extra scaling down of the elements of the
481
    basis.
482
    This extra scaling down is necessary when there are elements of the 
483
    basis that have negative exponents.
484
    """
485
    extraScalingExpsList = []
486
    for minExp, prec in zip(minExponentsList, precsList):
487
        if minExp - prec < 0:
488
            extraScalingExpsList.append(minExp - prec)
489
        else:
490
            extraScalingExpsList.append(0)
491
    return extraScalingExpsList
492
# End cvp_extra_basis_scaling_exps.
493
#
494
def cvp_extra_function_scaling_exp(floatBasis,
495
                                   floatFuncVect,
496
                                   maxExponentsList):
497
    """
498
    Compute the extra scaling exponent for the function vector in ordre to 
499
    guarantee that the maximum exponent can be reached for each element of the
500
    basis.
501
    """
502
    ## Check arguments
503
    if floatBasis.nrows() == 0 or \
504
       floatBasis.ncols() == 0 or \
505
       len(floatFuncVect)      == 0:
506
        return 1
507
    if len(maxExponentsList) != floatBasis.nrows():
508
        return None
509
    ## Compute the log of the norm of the function vector.
510
    funcVectNormLog  = log(floatFuncVect.norm())
511
    ## Compute the extra scaling factor for each vector of the basis.
512
    #  for norms ratios an maxExponentsList. 
513
    extraScalingExp = 0
514
    for (row, maxExp) in zip(floatBasis.rows(),maxExponentsList):
515
        rowNormLog     = log(row.norm())
516
        normsRatioLog2 = floor((funcVectNormLog - rowNormLog) / log2) 
517
        #print "Current norms ratio log2:", normsRatioLog2
518
        scalingExpCandidate = normsRatioLog2 - maxExp 
519
        #print "Current scaling exponent candidate:", scalingExpCandidate
520
        if scalingExpCandidate < 0:
521
            if -scalingExpCandidate > extraScalingExp:
522
                extraScalingExp = -scalingExpCandidate
523
    return extraScalingExp
524
# End cvp_extra_function_scaling_exp.
525
#
526
def cvp_filter_out_undefined_image(initialPointList, func):
527
    """
528
    Fom the initial list and function, built a list of points which have a
529
    defined image.
530
    """
531
    finalPointsList = []
532
    for iterPoint in initialPointList:
533
        try:
534
            finalPointsList.append(func(iterPoint))
535
            #print "cfoui - Point", iterPoint, "done."
536
        except ValueError:
537
            pass
538
            #print "cfoui - Point", iterPoint, "failed."
539
    return finalPointsList
540
# End    cvp_filter_out_undefined_image
541
 
542
def cvp_float_basis(monomialsList, pointsList, realField):
543
    """
544
    For a given monomials list and points list, compute the floating-point
545
    basis matrix.
546
    """
547
    numRows    = len(monomialsList)
548
    numCols    = len(pointsList)
549
    if numRows == 0 or numCols == 0:
550
        return matrix(realField, 0, 0)
551
    #
552
    floatBasis = matrix(realField, numRows, numCols)
553
    for rIndex in xrange(0, numRows):
554
        for cIndex in xrange(0, numCols):
555
            floatBasis[rIndex,cIndex] = \
556
                monomialsList[rIndex](realField(pointsList[cIndex]))
557
    return floatBasis
558
# End cvp_float_basis
559
#
560
def cvp_float_vector_for_approx(func, 
561
                                basisPointsList,
562
                                realField):
563
    """
564
    Compute a floating-point vector for the function and the points list.
565
    """
566
    #
567
    ## Some local variables.
568
    basisPointsNum = len(basisPointsList)
569
    #
570
    floatVector = vector(realField, basisPointsNum)
571
    ##
572
    for vIndex in xrange(0,basisPointsNum):
573
        ### We assume the all for all points their image is defined.
574
        floatVector[vIndex] = \
575
                func(basisPointsList[vIndex])
576
    return floatVector
577
# End cvp_float_vector_for_approx.
578
#
579
def cvp_func_abs_max_for_points(func, pointsList, realField):
580
    """
581
    Compute the maximum absolute value of a function for a list of 
582
    points.
583
    If several points yield the same value and this value is the maximum,
584
    the last visited one is returned.
585
    @return (theMaxPoint, theMaxValue) 
586
    """
587
    evalDict = dict()
588
    #
589
    ## An empty points list is really an error. We cannot return an empty tuple.
590
    if len(pointsList) == 0:
591
        return None
592
    #
593
    for pt in pointsList:
594
        try:
595
            evalDict[abs(realField(func(pt)))] = realField(pt)
596
            #print "cfamfp - point:", realField(pt) , "- image:", abs(realField(func(pt)))
597
        except ValueError:
598
            pass
599
    maxAbs = max(evalDict.keys())
600
    #print "cfamfp - maxAbs:", maxAbs, evalDict[maxAbs]
601
    return (evalDict[maxAbs], maxAbs)
602
# End cvp_func_abs_max_for_points
603
#
604
def cvp_hkz_reduce(intBasis):
605
    """
606
    Thin and simplistic wrapper on the HKZ function of the FP_LLL module.
607
    """
608
    fplllIntBasis = FP_LLL(intBasis)
609
    fplllIntBasis.HKZ()
610
    return fplllIntBasis._sage_()
611
# End cvp_hkz_reduce.
612
#
613
def cvp_int_basis(floatBasis, 
614
                  commonScalingExp, 
615
                  extraScalingExpsList):
616
    """
617
    From the float basis, the common scaling factor and the extra exponents 
618
    compute the integral basis.
619
    """
620
    ## Checking arguments.
621
    if floatBasis.nrows() != len(extraScalingExpsList):
622
        return None
623
    intBasis = matrix(ZZ, floatBasis.nrows(), floatBasis.ncols())
624
    for rIndex in xrange(0, floatBasis.nrows()):
625
        for cIndex in xrange(0, floatBasis.ncols()):
626
            intBasis[rIndex, cIndex] = \
627
            (floatBasis[rIndex, cIndex] * \
628
            2^(commonScalingExp + extraScalingExpsList[rIndex])).round()
629
    return intBasis
630
# End cvp_int_basis.
631
#
632
def cvp_int_vector_for_approx(floatVect, commonScalingExp, extraScalingExp):
633
    """
634
    Compute the integral version of the function vector.
635
    """
636
    totalScalingFactor = 2^(commonScalingExp + extraScalingExp)
637
    intVect = vector(ZZ, len(floatVect))
638
    for index in xrange(0, len(floatVect)):
639
        intVect[index] = (floatVect[index] * totalScalingFactor).round()
640
    return intVect
641
# End cvp_int_vector_for_approx.
642
#
643
def cvp_lll_reduce(intBasis):
644
    """
645
    Thin and simplistic wrapper around the LLL function
646
    """
647
    return intBasis.LLL()
648
# End cvp_lll_reduce.
649
#
650
def cvp_monomials_list(exponentsList, varName = None):
651
    """
652
    Create a list of monomials corresponding to the given exponentsList.
653
    Monomials are defined as functions.
654
    """
655
    monomialsList = []
656
    for exponent in exponentsList:
657
        if varName is None:
658
            monomialsList.append((x^exponent).function(x))
659
        else:
660
            monomialsList.append((varName^exponent).function(varName))
661
    return monomialsList
662
# End cvp_monomials_list.
663
#
664
def cvp_polynomial_coeffs_from_vect(cvpVectInBasis,
665
                                    coeffsPrecList, 
666
                                    minCoeffsExpList,
667
                                    extraFuncScalingExp):
668
    """
669
    Computes polynomial coefficients out of the elements of the CVP vector.
670
    @todo
671
    Rewrite the code with a single exponentiation, at the very end.
672
    """
673
    numElements = len(cvpVectInBasis)
674
    ## Arguments check.
675
    if numElements != len(minCoeffsExpList) or \
676
       numElements != len(coeffsPrecList):
677
        return None
678
    polynomialCoeffsList = []
679
    for index in xrange(0, numElements):
680
        currentCoeff = cvp_round_to_bits(cvpVectInBasis[index],
681
                                         coeffsPrecList[index])
682
        #print "cvp_polynomial_coeffs - current coefficient rounded:", currentCoeff
683
        currentCoeff *= 2^(minCoeffsExpList[index])
684
        #print "cvp_polynomial_coeffs - current coefficient scaled :", currentCoeff
685
        currentCoeff *= 2^(-coeffsPrecList[index])
686
        #print "cvp_polynomial_coeffs - current coefficient prec corrected :", currentCoeff
687
        currentCoeff *= 2^(-extraFuncScalingExp)
688
        #print "cvp_polynomial_coeffs - current coefficient extra func scaled :", currentCoeff
689
        polynomialCoeffsList.append(currentCoeff)
690
    return polynomialCoeffsList
691
# En polynomial_coeffs_from_vect.
692
#
693
def cvp_polynomial_error_func_maxis(funcSa,
694
                                    polySa, 
695
                                    lowerBoundSa, 
696
                                    upperBoundSa,
697
                                    realField = None,
698
                                    contFracMaxErr = None):
699
    """
700
    For a given function, approximation polynomial and interval (given 
701
    as bounds) compute the maxima of the functSa - polySo on the interval.
702
    Also computes the infnorm of the error function.
703
    """
704
    ## Arguments check.
705
    # @todo.
706
    ## If no realField argument is given try to retrieve it from the 
707
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
708
    if realField is None:
709
        try:
710
            ### Force failure if parent does not have prec() member.
711
            lowerBound.parent().prec()
712
            ### If no exception is thrown, set realField.
713
            realField = lowerBound.parent()
714
        except:
715
            realField = RR
716
    #print "Real field:", realField
717
    ## Compute the Sollya version of the function.
718
    funcAsStringSa = funcSa._assume_str().replace("_SAGE_VAR_","",100)
719
    #print "cpefa:", funcAsStringSa
720
    funcSo  = pobyso_parse_string(funcAsStringSa)
721
    if pobyso_is_error_so_sa(funcSo):
722
        pobyso_clear_obj(funcSo)
723
        return None
724
    #print "cpefa: function conversion OK."
725
    ## Compute the Sollya version of the polynomial.
726
    ## The conversion is made from a floating-point coefficients polynomial.
727
    try:
728
        polySa.base_ring().prec()
729
        convFloatPolySa = polySa
730
    except:
731
        convFloatPolySa = realField[polySa.variables()[0]](polySa)
732
    polySo = pobyso_float_poly_sa_so(convFloatPolySa)
733
    if pobyso_is_error_so_sa(funcSo):
734
        pobyso_clear_obj(funcSo)
735
        pobyso_clear_obj(polySo)
736
        return None
737
    #print "cpefa: polynomial conversion to Sollya OK."
738
    ## Copy both funcSo and polySo as they are needed later for the infnorm..
739
    errorFuncSo = \
740
        sollya_lib_build_function_sub(sollya_lib_copy_obj(funcSo), 
741
                                      sollya_lib_copy_obj(polySo))
742
    if pobyso_is_error_so_sa(errorFuncSo):
743
        pobyso_clear_obj(errorFuncSo)
744
        return None
745
    #print "cpefa: error function in Sollya OK."
746
    ## Evaluate the error function at both bounds, it may be usefull, if
747
    #  the error function is monotonous
748
    ## Lower bound stuff.
749
    lowerBoundSo = pobyso_constant_sa_so(lowerBoundSa)
750
    if pobyso_is_error_so_sa(lowerBoundSo):
751
        pobyso_clear_obj(errorFuncSo)
752
        pobyso_clear_obj(lowerBoundSo)
753
        return None
754
    errFuncAtLbSa = pobyso_evaluate_so_sa(errorFuncSo, lowerBoundSo)
755
    pobyso_clear_obj(lowerBoundSo)
756
    if errFuncAtLbSa is None:
757
        pobyso_clear_obj(errorFuncSo)
758
        return None
759
    ## The result of the evaluation can be an interval.
760
    try:
761
        errFuncAtLbSa = errFuncAtLbSa.center()
762
    except:
763
        errFuncAtLbSa = errFuncAtLbSa
764
    #print "cpefa: errFuncAtLbSa:", errFuncAtLbSa
765
    ## Upper bound stuff.
766
    upperBoundSo = pobyso_constant_sa_so(upperBoundSa)
767
    if pobyso_is_error_so_sa(upperBoundSo):
768
        pobyso_clear_obj(errorFuncSo)
769
        pobyso_clear_obj(upperBoundSo)
770
        return None
771
    errFuncAtUbSa = pobyso_evaluate_so_sa(errorFuncSo, upperBoundSo)
772
    pobyso_clear_obj(upperBoundSo)
773
    if errFuncAtUbSa is None:
774
        return None
775
    ## The result of the evaluation can be an interval.
776
    try:
777
        errFuncAtUbSa = errFuncAtUbSa.center()
778
    except:
779
        errFuncAtUbSa = errFuncAtUbSa
780
    #print "cpefa: errFuncAtUbSa:", errFuncAtUbSa
781
    ## Compute the derivative.
782
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
783
    pobyso_clear_obj(errorFuncSo)
784
    if pobyso_is_error_so_sa(diffErrorFuncSo):
785
        pobyso_clear_obj(diffErrorFuncSo)
786
        return None
787
        #print "cpefa: polynomial conversion to Sollya OK."
788
    #print "cpefa: error function derivative in Sollya OK."
789
    ## Compute the interval.
790
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
791
    if pobyso_is_error_so_sa(intervalSo):
792
        pobyso_clear_obj(diffErrorFuncSo)
793
        pobyso_clear_obj(intervalSo)
794
        return None
795
    ## Compute the infnorm of the error function.
796
    errFuncSupNormSa = pobyso_supnorm_so_sa(polySo, 
797
                                            funcSo, 
798
                                            intervalSo, 
799
                                            realFieldSa = realField)
800
    pobyso_clear_obj(polySo)
801
    pobyso_clear_obj(funcSo)
802
    ## Compute the zeros of the derivative.
803
    ### Give it a first try with dirtyfindzeros but it may
804
    #   fail.
805
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, 
806
                                                     intervalSo)
807
    if pobyso_is_error_so_sa(errorFuncMaxisSo):
808
        pobyso_clear_obj(diffErrorFuncSo)
809
        pobyso_clear_obj(intervalSo)
810
        pobyso_clear_obj(errorFuncMaxisSo)
811
        return None
812
    #print "cpefa: error function maxis in Sollya OK."
813
    errorFuncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
814
    pobyso_clear_obj(errorFuncMaxisSo)
815
    #### If the list is empty try the more sophisticated
816
    #    findzeros.
817
    if len(errorFuncMaxisSa) == 0:
818
        errorFuncMaxisSo = \
819
            pobyso_find_zeros_so_so(diffErrorFuncSo, 
820
                                    intervalSo)
821
        if pobyso_is_error_so_sa(errorFuncMaxisSo):
822
            pobyso_clear_obj(diffErrorFuncSo)
823
            pobyso_clear_obj(intervalSo)
824
            pobyso_clear_obj(errorFuncMaxisSo)
825
            return None
826
        #print "cpefa: error function maxis in Sollya OK."
827
        #print "cpefa:", ; pobyso_autoprint(errorFuncMaxisSo)
828
        ##### The findzeros functions returns intervals (ranges).
829
        errorFuncRangeMaxisSa = pobyso_range_list_so_sa(errorFuncMaxisSo)
830
        #print "cpefa:", errorFuncRangeMaxisSa
831
        pobyso_clear_obj(errorFuncMaxisSo)
832
        errorFuncMaxisSa = []
833
        if len(errorFuncRangeMaxisSa) != 0:
834
            for interval in errorFuncRangeMaxisSa:
835
                errorFuncMaxisSa.append(interval.center())
836
            #print "cpefa:", errorFuncMaxisSa
837
        else: # The error function is monotonous. It reaches its maximum
838
              # at either of the bounds.
839
              errFuncMaxAtBounds = max(abs(errFuncAtLbSa), abs(errFuncAtUbSa))
840
              if errFuncMaxAtBounds == abs(errFuncAtLbSa):
841
                  errorFuncMaxisSa.append(lowerBoundSa)
842
              else:
843
                  errorFuncMaxisSa.append(upperBoundSa)
844
    pobyso_clear_obj(diffErrorFuncSo)
845
    pobyso_clear_obj(intervalSo)
846
    ## If required, convert the numbers to rational numbers.
847
    #print "cpefa - errorFuncMaxisSa:", errorFuncMaxisSa
848
    if not contFracMaxErr is None:
849
        for index in xrange(0, len(errorFuncMaxisSa)):
850
            errorFuncMaxisSa[index] = \
851
                errorFuncMaxisSa[index].nearby_rational(contFracMaxErr)
852
    return (errorFuncMaxisSa, errFuncSupNormSa)
853
# End cvp_polynomial_error_func_maxis
854
#
855
def cvp_polynomial_from_coeffs_and_exps(coeffsList, 
856
                                        expsList, 
857
                                        polyField = None,
858
                                        theVar = None):
859
    """
860
    Build a polynomial in the classical monomials (possibly lacunary) basis 
861
    from a list of coefficients and a list of exponents. The polynomial is in
862
    the polynomial field given as argument.
863
    """
864
    if len(coeffsList) != len(expsList):
865
        return None
866
    ## If no variable is given, "x" is used.
867
    ## If no polyField is given, we fall back on a polynomial field on RR.
868
    if theVar is None:
869
        if polyField is None:
870
            theVar = x
871
            polyField = RR[theVar]
872
        else:
873
            theVar = var(polyField.variable_name())
874
    else: ### theVar is set.
875
        ### No polyField, fallback on RR[theVar].
876
        if polyField is None:
877
            polyField = RR[theVar]
878
        else: ### Both the polyFiled and theVar are set: create a new polyField
879
              #   with theVar. The original polyField is not affected by the 
880
              #   change.
881
            polyField = polyField.change_var(theVar)  
882
    ## Seed the polynomial.
883
    poly = polyField(0)
884
    ## Append the terms.
885
    for coeff, exp in zip(coeffsList, expsList):
886
        poly += polyField(coeff * theVar^exp)
887
    return poly
888
# End cvp_polynomial_from_coeffs_and_exps.
889
#
890
def cvp_remez_all_poly_error_func_maxis(funct, 
891
                                        maxDegree, 
892
                                        lowerBound, 
893
                                        upperBound,
894
                                        precsList, 
895
                                        realField = None,
896
                                        contFracMaxErr = None):
897
    """
898
    For a given function f, a degree and an interval, compute the 
899
    maxima of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
900
    function over the interval.
901
    """
902
    ## If no realField argument is given try to retrieve it from the 
903
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
904
    if realField is None:
905
        try:
906
            ### Force failure if parent does not have prec() member.
907
            lowerBound.parent().prec()
908
            ### If no exception is thrown, set realField.
909
            realField = lowerBound.parent()
910
        except:
911
            realField = RR
912
    #print "Real field:", realField
913
    funcSa         = func
914
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
915
    print "Function as a string:", funcAsStringSa
916
    ## Create the Sollya version of the function and compute the
917
    #  Remez approximant
918
    funcSo  = pobyso_parse_string(funcAsStringSa)
919
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
920
                                           maxDegree, 
921
                                           lowerBound,
922
                                           upperBound)
923
    ## Compute the error function and its derivative
924
    ### First create the error function with copies since they are needed later.
925
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
926
                                                sollya_lib_copy_obj(funcSo))
927
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
928
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
929
    pobyso_clear_obj(errorFuncSo)
930
    ## Compute the zeros of the derivative.
931
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
932
    pobyso_clear_obj(diffErrorFuncSo)
933
    errorFuncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
934
    pobyso_clear_obj(errorFuncMaxisSo)
935
    ## Compute the truncated Remez polynomial and the error function.
936
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
937
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
938
    pobyso_clear_obj(pStarSo)
939
    ### Compute the error function. This time we consume both the function
940
    #   and the polynomial.
941
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, funcSo)
942
    ## Compute the derivative.
943
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
944
    pobyso_clear_obj(errorFuncSo)
945
    ## Compute the zeros of the derivative.
946
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
947
    pobyso_clear_obj(diffErrorFuncSo)
948
    pobyso_clear_obj(intervalSo)
949
    errorFuncTruncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
950
    pobyso_clear_obj(errorFuncMaxisSo)
951
    ## Merge with the first list, removing duplicates if any.
952
    errorFuncMaxisSa.extend(elem for elem in errorFuncTruncMaxisSa \
953
                            if elem not in errorFuncMaxisSa)
954
    errorFuncMaxisSa.sort()
955
    ## If required, convert the numbers to rational numbers.
956
    if not contFracMaxErr is None:
957
        for index in xrange(0, len(errorFuncMaxisSa)):
958
            errorFuncMaxisSa[index] = \
959
                errorFuncMaxisSa[index].nearby_rational(contFracMaxErr)
960
    return errorFuncMaxisSa
961
# End cvp_remez_all_poly_error_func_maxis.
962
#
963
def cvp_remez_all_poly_error_func_zeros(funct, 
964
                                        maxDegree, 
965
                                        lowerBound, 
966
                                        upperBound,
967
                                        precsList, 
968
                                        realField = None,
969
                                        contFracMaxErr = None):
970
    """
971
    For a given function f, a degree and an interval, compute the 
972
    zeros of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
973
    function over the interval.
974
    """
975
    ## If no realField argument is given try to retrieve it from the 
976
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
977
    if realField is None:
978
        try:
979
            ### Force failure if parent does not have prec() member.
980
            lowerBound.parent().prec()
981
            ### If no exception is thrown, set realField.
982
            realField = lowerBound.parent()
983
        except:
984
            realField = RR
985
    #print "Real field:", realField
986
    funcSa         = func
987
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
988
    print "Function as a string:", funcAsStringSa
989
    ## Create the Sollya version of the function and compute the
990
    #  Remez approximant
991
    funcSo  = pobyso_parse_string(funcAsStringSa)
992
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
993
                                           maxDegree, 
994
                                           lowerBound,
995
                                           upperBound)
996
    ## Compute the zeroes of the error function.
997
    ### First create the error function with copies since they are needed later.
998
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
999
                                                sollya_lib_copy_obj(funcSo))
1000
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
1001
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
1002
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
1003
    #print "Zeroes of the error function from Sollya:"
1004
    #pobyso_autoprint(errorFuncZerosSo)
1005
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
1006
    pobyso_clear_obj(errorFuncZerosSo)
1007
    #print "\nZeroes of the error function from Sage:"
1008
    #print errorFuncZerosSa
1009
    #
1010
    ## Deal with the truncated polynomial now.
1011
    ### Create the formats list. Notice the "*" before the list variable name.
1012
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
1013
    #print "Precisions list as Sollya object:", 
1014
    #pobyso_autoprint(truncFormatListSo)
1015
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
1016
    #print "Truncated polynomial:", ; pobyso_autoprint(pTruncSo)
1017
    ### Compute the error function. This time we consume both the function
1018
    #   and the polynomial.
1019
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, 
1020
                                                funcSo)
1021
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
1022
    pobyso_clear_obj(pStarSo)
1023
    #print "Zeroes of the error function for the truncated polynomial from Sollya:"
1024
    #pobyso_autoprint(errorFuncZerosSo)
1025
    errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
1026
    pobyso_clear_obj(errorFuncZerosSo)
1027
    ## Sollya objects clean up.
1028
    pobyso_clear_obj(intervalSo)
1029
    errorFuncZerosSa += errorFuncTruncZerosSa
1030
    errorFuncZerosSa.sort()
1031
    ## If required, convert the numbers to rational numbers.
1032
    if not contFracMaxErr is None:
1033
        for index in xrange(0, len(errorFuncZerosSa)):
1034
            errorFuncZerosSa[index] = \
1035
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
1036
    return errorFuncZerosSa
1037
    #
1038
# End remez_all_poly_error_func_zeros
1039
#
1040
def cvp_remez_poly_error_func_zeros(funct, 
1041
                                    maxDegree, 
1042
                                    lowerBound, 
1043
                                    upperBound,
1044
                                    realField = None,
1045
                                    contFracMaxErr = None):
1046
    """
1047
    For a given function f, a degree and an interval, compute the 
1048
    zeros of the (f-remez_d(f)) function.
1049
    """
1050
    ## If no realField argument is given try to retrieve it from the 
1051
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
1052
    if realField is None:
1053
        try:
1054
            ### Force failure if parent does not have prec() member.
1055
            lowerBound.parent().prec()
1056
            ### If no exception is thrown, set realField.
1057
            realField = lowerBound.parent()
1058
        except:
1059
            realField = RR
1060
    #print "Real field:", realField
1061
    funcSa         = func
1062
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
1063
    #print "Function as a string:", funcAsStringSa
1064
    ## Create the Sollya version of the function and compute the
1065
    #  Remez approximant
1066
    funcSo  = pobyso_parse_string(funcAsStringSa)
1067
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
1068
                                           maxDegree, 
1069
                                           lowerBound,
1070
                                           upperBound)
1071
    ## Compute the zeroes of the error function.
1072
    ### Error function creation consumes both pStarSo and funcSo.
1073
    errorFuncSo = sollya_lib_build_function_sub(pStarSo, funcSo)
1074
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
1075
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
1076
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
1077
    #print "Zeroes of the error function from Sollya:"
1078
    #pobyso_autoprint(errorFuncZerosSo)
1079
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
1080
    pobyso_clear_obj(errorFuncZerosSo)
1081
    ## Sollya objects clean up.
1082
    pobyso_clear_obj(intervalSo)
1083
    ## Converting to rational numbers, if required.
1084
    if not contFracMaxErr is None:
1085
        for index in xrange(0, len(errorFuncZerosSa)):
1086
            errorFuncZerosSa[index] = \
1087
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
1088
    return errorFuncZerosSa
1089
    #
1090
# End remez_poly_error_func_zeros
1091
#
1092
def cvp_round_to_bits(num, numBits):
1093
    """
1094
    Round "num" to "numBits" bits.
1095
    @param num    : the number to round;
1096
    @param numBits: the number of bits to round to.
1097
    """
1098
    if num == 0:
1099
        return num
1100
    log2ofNum  = 0
1101
    numRounded = 0
1102
    ## Avoid conversion to floating-point if not necessary.
1103
    try:
1104
        log2OfNum = num.abs().log2()
1105
    except:
1106
        log2OfNum = RR(num).abs().log2()
1107
    ## Works equally well for num >= 1 and num < 1.
1108
    log2OfNum = ceil(log2OfNum)
1109
    # print "cvp_round_to_bits - Log2OfNum:", log2OfNum
1110
    divMult = log2OfNum - numBits
1111
    #print "cvp_round_to_bits - DivMult:", divMult
1112
    if divMult != 0:
1113
        numRounded = round(num/2^divMult) * 2^divMult
1114
    else:
1115
        numRounded = num
1116
    return numRounded
1117
# End cvp_round_to_bits
1118
#
1119
def cvp_vector_in_basis(vect, basis):
1120
    """
1121
    Compute the coordinates of "vect" in "basis" by
1122
    solving a linear system.
1123
    @param vect:   the vector we want to compute the coordinates of
1124
                   in coordinates of the ambient space;
1125
    @param basis:  the basis we want to compute the coordinates in
1126
                   as a matrix relative to the ambient space.
1127
    """
1128
    ## Create the variables for the linear equations.
1129
    varDeclString = ""
1130
    basisIterationsRange = xrange(0, basis.nrows())
1131
    ### Building variables declaration string.
1132
    for index in basisIterationsRange:
1133
        varName = "a" + str(index)
1134
        if varDeclString == "":
1135
            varDeclString += varName
1136
        else:
1137
            varDeclString += "," + varName
1138
    ### Variables declaration
1139
    varsList = var(varDeclString)
1140
    sage_eval("var('" + varDeclString + "')")
1141
    ## Create the equations
1142
    equationString = ""
1143
    equationsList = []
1144
    for vIndex in xrange(0, len(vect)):
1145
        equationString = ""
1146
        for bIndex in basisIterationsRange:
1147
            if equationString != "":
1148
                equationString += "+"
1149
            equationString += str(varsList[bIndex]) + "*" + str(basis[bIndex,vIndex])
1150
        equationString += " == " + str(vect[vIndex])
1151
        equationsList.append(sage_eval(equationString))
1152
    ## Solve the equations system. The solution dictionary is needed
1153
    #  to recover the values of the solutions.
1154
    solutions = solve(equationsList,varsList, solution_dict=True)
1155
    ## This code is deactivated: did not solve the problem.
1156
    """
1157
    if len(solutions) == 0:
1158
        print "Trying Maxima to_poly_sove."
1159
        solutions = solve(equationsList,varsList, solution_dict=True, to_poly_solve = 'force')
1160
    """
1161
    #print "Solutions:", s
1162
    ## Recover solutions in rational components vector.
1163
    vectInBasis = vector(QQ, basis.nrows())
1164
    ### There can be several solution, in the general case.
1165
    #   Here, there is only one (or none). For each solution, each 
1166
    #   variable has to be searched for.
1167
    for sol in solutions:
1168
        for bIndex in basisIterationsRange:
1169
            vectInBasis[bIndex] = sol[varsList[bIndex]]
1170
    return vectInBasis
1171
# End cpv_vector_in_basis.
1172
#
1173
sys.stderr.write("... functions for CVP loaded.\n")