Statistiques
| Branche: | Révision :

cvp / src / functions_for_cvp.sage @ 2669308a

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