Statistiques
| Branche: | Révision :

cvp / src / functions_for_cvp.sage @ 9cf1fb38

Historique | Voir | Annoter | Télécharger (32,95 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
print "Functions for CVP loading..."
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_zeros_1k(numPoints, realField, contFracMaxErr = None):
197
    """
198
    Compute the Chebyshev zeros 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
    zerosList = []
214
    ## formulas: cos (((2*i - 1) * pi) / (2*numPoints)) for i = 1,...,numPoints.
215
    #  If i is -1-shifted, as in the following loop, formula is 
216
    #  cos (((2*i + 1) * pi) / (2*numPoints)) for i = 0,...,numPoints-1.
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
                zerosList.append(realField(0))
223
            else:
224
                zerosList.append(0)
225
        else:
226
            currentCheby = \
227
                ((realField(2*index+1) * realField(pi)) / 
228
                 realField(2*numPoints)).cos()
229
            #print  "Current Cheby:", currentCheby
230
            ## Result is negated to have an increasing order list.
231
            if contFracMaxErr is None:
232
                zerosList.append(-currentCheby)
233
            else:
234
                zerosList.append(-currentCheby.nearby_rational(contFracMaxErr))
235
            #print "Relative error:", (currentCheby/zerosList[index])
236
    return zerosList
237
# End cvp_chebyshev_zeros_1k.
238
#
239
def cvp_chebyshev_zeros_for_interval(lowerBound, 
240
                                     upperBound, 
241
                                     numPoints,
242
                                     realField = None, 
243
                                     contFracMaxErr = None):
244
    """
245
    Compute the Chebyshev zeros for some polynomial degree (numPoints, for the
246
    zeros) 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
    chebyshevZerosList = \
268
        cvp_chebyshev_zeros_1k(numPoints, realField)
269
    scalingFactor, offset = cvp_affine_from_chebyshev(lowerBound, upperBound)
270
    for index in xrange(0, len(chebyshevZerosList)):
271
        chebyshevZerosList[index] = \
272
            chebyshevZerosList[index] * scalingFactor + offset
273
        if not contFracMaxErr is None:
274
            chebyshevZerosList[index] = \
275
                chebyshevZerosList[index].nearby_rational(contFracMaxErr)
276
    return chebyshevZerosList                                                             
277
# End cvp_chebyshev_zeros_for_interval.
278
#
279
def cvp_common_scaling_exp(precision, 
280
                           precisionFraction = None):
281
    """
282
    Compute the common scaling factor (a power of 2).
283
    The exponent is a fraction of the precision;
284
    A extra factor is computed for the vector,
285
    exra factors are computed for the basis vectors, all in the corresponding
286
    functions.
287
    """
288
    ## Set a default value for the precisionFraction to 1/2.
289
    if precisionFraction is None:
290
        precisionFraction = 3/4
291
    """
292
    minBasisVectsNorm = oo
293
    currentNorm = 0
294
    for vect in floatBasis.rows():
295
        currentNorm = vect.norm()
296
        print "Current norm:", currentNorm
297
        if currentNorm < minBasisVectsNorm:
298
            minBasisVectsNorm = currentNorm
299
    currentNorm = funcVect.norm()
300
    print "Current norm:", currentNorm
301
    if currentNorm < minBasisVectsNorm:
302
        minBasisVectsNorm = currentNorm
303
    ## Check if a push is need because the smallest norm is below 0.    
304
    powerForMinNorm = floor(log(minBasisVectsNorm)/log2)
305
    print "Power for min norm :", powerForMinNorm
306
    print "Power for precision:", ceil(precision*precisionFraction)
307
    if powerForMinNorm < 0:
308
        return 2^(ceil(precision*precisionFraction) - powerFromMinNorm)
309
    else:
310
    """
311
    return ceil(precision * precisionFraction)
312
# End cvp_common_scaling_factor.
313
#
314
def cvp_evaluation_points_list(funct, 
315
                               maxDegree, 
316
                               lowerBound, 
317
                               upperBound, 
318
                               realField = None):
319
    """
320
    Compute a list of points for the vector creation.
321
    Strategy:
322
    - compute the zeros of the function-remez;
323
    - compute the zeros of the function-rounded_remez;
324
    - compute the Chebyshev points.
325
    Merge the whole thing.
326
    """
327
    
328
# End cvp_evaluation_points_list.
329
#
330
def cvp_extra_basis_scaling_exps(precsList, minExponentsList):
331
    """
332
    Computes the exponents for the extra scaling down of the elements of the
333
    basis.
334
    This extra scaling down is necessary when there are elements of the 
335
    basis that have negative exponents.
336
    """
337
    extraScalingExpsList = []
338
    for minExp, prec in zip(minExponentsList, precsList):
339
        if minExp - prec < 0:
340
            extraScalingExpsList.append(minExp - prec)
341
        else:
342
            extraScalingExpsList.append(0)
343
    return extraScalingExpsList
344
# End cvp_extra_basis_scaling_exps.
345
#
346
def cvp_extra_function_scaling_exp(floatBasis,
347
                                   floatFuncVect,
348
                                   maxExponentsList):
349
    """
350
    Compute the extra scaling exponent for the function vector in ordre to 
351
    guarantee that the maximum exponent can be reached for each element of the
352
    basis.
353
    """
354
    ## Check arguments
355
    if floatBasis.nrows() == 0 or \
356
       floatBasis.ncols() == 0 or \
357
       len(floatFuncVect)      == 0:
358
        return 1
359
    if len(maxExponentsList) != floatBasis.nrows():
360
        return None
361
    ## Compute the log of the norm of the function vector.
362
    funcVectNormLog  = log(floatFuncVect.norm())
363
    ## Compute the extra scaling factor for each vector of the basis.
364
    #  for norms ratios an maxExponentsList. 
365
    extraScalingExp = 0
366
    for (row, maxExp) in zip(floatBasis.rows(),maxExponentsList):
367
        rowNormLog     = log(row.norm())
368
        normsRatioLog2 = floor((funcVectNormLog - rowNormLog) / log2) 
369
        #print "Current norms ratio log2:", normsRatioLog2
370
        scalingExpCandidate = normsRatioLog2 - maxExp 
371
        #print "Current scaling exponent candidate:", scalingExpCandidate
372
        if scalingExpCandidate < 0:
373
            if -scalingExpCandidate > extraScalingExp:
374
                extraScalingExp = -scalingExpCandidate
375
    return extraScalingExp
376
# End cvp_extra_function_scaling_exp.
377
#
378
def cvp_float_basis(monomialsList, pointsList, realField):
379
    """
380
    For a given monomials list and points list, compute the floating-point
381
    basis matrix.
382
    """
383
    numRows    = len(monomialsList)
384
    numCols    = len(pointsList)
385
    if numRows == 0 or numCols == 0:
386
        return matrix(realField, 0, 0)
387
    #
388
    floatBasis = matrix(realField, numRows, numCols)
389
    for rIndex in xrange(0, numRows):
390
        for cIndex in xrange(0, numCols):
391
            floatBasis[rIndex,cIndex] = \
392
                monomialsList[rIndex](realField(pointsList[cIndex]))
393
    return floatBasis
394
# End cvp_float_basis
395
#
396
def cvp_float_vector_for_approx(func, 
397
                                basisPointsList,
398
                                realField):
399
    """
400
    Compute a floating-point vector for the function and the points list.
401
    """
402
    #
403
    ## Some local variables.
404
    basisPointsNum = len(basisPointsList)
405
    #
406
    ##
407
    vVect = vector(realField, basisPointsNum)
408
    for vIndex in xrange(0,basisPointsNum):
409
        vVect[vIndex] = \
410
            func(basisPointsList[vIndex])
411
    return vVect
412
# End cvp_float_vector_for_approx.
413
#
414
def cvp_hkz_reduce(intBasis):
415
    """
416
    Thin and simplistic wrapper the HKZ function of the FP_LLL module.
417
    """
418
    fplllIntBasis = FP_LLL(intBasis)
419
    fplllIntBasis.HKZ()
420
    return fplllIntBasis._sage_()
421
# End cvp_hkz_reduce.
422
#
423
def cvp_int_basis(floatBasis, 
424
                  commonScalingExp, 
425
                  extraScalingExpsList):
426
    """
427
    From the float basis, the common scaling factor and the extra exponents 
428
    compute the integral basis.
429
    """
430
    ## Checking arguments.
431
    if floatBasis.nrows() != len(extraScalingExpsList):
432
        return None
433
    intBasis = matrix(ZZ, floatBasis.nrows(), floatBasis.ncols())
434
    for rIndex in xrange(0, floatBasis.nrows()):
435
        for cIndex in xrange(0, floatBasis.ncols()):
436
            intBasis[rIndex, cIndex] = \
437
            (floatBasis[rIndex, cIndex] * \
438
            2^(commonScalingExp + extraScalingExpsList[rIndex])).round()
439
    return intBasis
440
# End cvp_int_basis.
441
#
442
def cvp_int_vector_for_approx(floatVect, commonScalingExp, extraScalingExp):
443
    """
444
    Compute the integral version of the function vector.
445
    """
446
    totalScalingFactor = 2^(commonScalingExp + extraScalingExp)
447
    intVect = vector(ZZ, len(floatVect))
448
    for index in xrange(0, len(floatVect)):
449
        intVect[index] = (floatVect[index] * totalScalingFactor).round()
450
    return intVect
451
# End cvp_int_vector_for_approx.
452
#
453
def cvp_lll_reduce(intBasis):
454
    """
455
    Thin and simplistic wrapper around the LLL function
456
    """
457
    return intBasis.LLL()
458
# End cvp_lll_reduce.
459
#
460
def cvp_monomials_list(exponentsList, varName = None):
461
    """
462
    Create a list of monomials corresponding to the given exponentsList.
463
    Monomials are defined as functions.
464
    """
465
    monomialsList = []
466
    for exponent in exponentsList:
467
        if varName is None:
468
            monomialsList.append((x^exponent).function(x))
469
        else:
470
            monomialsList.append((varName^exponent).function(varName))
471
    return monomialsList
472
# End cvp_monomials_list.
473
#
474
def cvp_polynomial_coeffs_from_vect(cvpVectInBasis,
475
                                    coeffsPrecList, 
476
                                    minCoeffsExpList,
477
                                    extraFuncScalingExp):
478
    """
479
    Computes polynomial coefficients out of the elements of the CVP vector.
480
    @todo
481
    Rewrite the code with a single exponentiation, at the very end.
482
    """
483
    numElements = len(cvpVectInBasis)
484
    ## Arguments check.
485
    if numElements != len(minCoeffsExpList) or \
486
       numElements != len(coeffsPrecList):
487
        return None
488
    polynomialCoeffsList = []
489
    for index in xrange(0, numElements):
490
        currentCoeff = cvp_round_to_bits(cvpVectInBasis[index],
491
                                         coeffsPrecList[index])
492
        print "cvp_polynomial_coeffs - current coefficient rounded:", currentCoeff
493
        currentCoeff *= 2^(minCoeffsExpList[index])
494
        print "cvp_polynomial_coeffs - current coefficient scaled :", currentCoeff
495
        currentCoeff *= 2^(-coeffsPrecList[index])
496
        print "cvp_polynomial_coeffs - current coefficient prec corrected :", currentCoeff
497
        currentCoeff *= 2^(-extraFuncScalingExp)
498
        print "cvp_polynomial_coeffs - current coefficient extra func scaled :", currentCoeff
499
        polynomialCoeffsList.append(currentCoeff)
500
    return polynomialCoeffsList
501
# En polynomial_coeffs_from_vect.
502
#
503
def cvp_polynomial_from_coeffs_and_exps(coeffsList, 
504
                                        expsList, 
505
                                        polyField = None,
506
                                        theVar = None):
507
    """
508
    Build a polynomial in the classical monomials (possibly lacunary) basis 
509
    from a list of coefficients and a list of exponents. The polynomial is in
510
    the polynomial field given as argument.
511
    """
512
    if len(coeffsList) != len(expsList):
513
        return None
514
    ## If no variable is given, "x" is used.
515
    ## If no polyField is given, we fall back on a polynomial field on RR.
516
    if theVar is None:
517
        if polyField is None:
518
            theVar = x
519
            polyField = RR[theVar]
520
        else:
521
            theVar = var(polyField.variable_name())
522
    else: # theVar is set.
523
        if polyField is None:
524
            polyField = RR[theVar]
525
        else: # Both the polyFiled and theVar are set: create a new polyField
526
            # with theVar. The original polyField is not affected by the change.
527
            polyField = polyField.change_var(theVar)  
528
    ## Seed the polynomial.
529
    poly = polyField(0)
530
    ## Append the terms.
531
    for coeff, exp in zip(coeffsList, expsList):
532
        poly += polyField(coeff * theVar^exp)
533
    return poly
534
# End cvp_polynomial_from_coeffs_and_exps.
535
#
536
def cvp_remez_all_poly_error_func_maxis(funct, 
537
                                        maxDegree, 
538
                                        lowerBound, 
539
                                        upperBound,
540
                                        precsList, 
541
                                        realField = None,
542
                                        contFracMaxErr = None):
543
    """
544
    For a given function f, a degree and an interval, compute the 
545
    maxima of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
546
    function over the interval.
547
    """
548
    ## If no realField argument is given try to retrieve it from the 
549
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
550
    if realField is None:
551
        try:
552
            ### Force failure if parent does not have prec() member.
553
            lowerBound.parent().prec()
554
            ### If no exception is thrown, set realField.
555
            realField = lowerBound.parent()
556
        except:
557
            realField = RR
558
    #print "Real field:", realField
559
    funcSa         = func
560
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
561
    print "Function as a string:", funcAsStringSa
562
    ## Create the Sollya version of the function and compute the
563
    #  Remez approximant
564
    funcSo  = pobyso_parse_string(funcAsStringSa)
565
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
566
                                           maxDegree, 
567
                                           lowerBound,
568
                                           upperBound)
569
    ## Compute the error function and its derivative
570
    ### First create the error function with copies since they are needed later.
571
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
572
                                                sollya_lib_copy_obj(funcSo))
573
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
574
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
575
    pobyso_clear_obj(errorFuncSo)
576
    ## Compute the zeros of the derivative.
577
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
578
    pobyso_clear_obj(diffErrorFuncSo)
579
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
580
    pobyso_clear_obj(errorFuncZerosSo)
581
    ## Compute the truncated Remez polynomial and the error function.
582
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
583
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
584
    pobyso_clear_obj(pStarSo)
585
    ### Compute the error function. This time we consume both the function
586
    #   and the polynomial.
587
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, funcSo)
588
    ## Compute the derivative.
589
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
590
    pobyso_clear_obj(errorFuncSo)
591
    ## Compute the zeros of the derivative.
592
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
593
    pobyso_clear_obj(diffErrorFuncSo)
594
    pobyso_clear_obj(intervalSo)
595
    errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
596
    pobyso_clear_obj(errorFuncZerosSo)
597
    errorFuncZerosSa += errorFuncTruncZerosSa
598
    errorFuncZerosSa.sort()
599
    ## If required, convert the numbers to rational numbers.
600
    if not contFracMaxErr is None:
601
        for index in xrange(0, len(errorFuncZerosSa)):
602
            errorFuncZerosSa[index] = \
603
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
604
    return errorFuncZerosSa
605
# End cvp_remez_all_poly_error_func_maxis.
606
#
607
def cvp_remez_all_poly_error_func_zeros(funct, 
608
                                        maxDegree, 
609
                                        lowerBound, 
610
                                        upperBound,
611
                                        precsList, 
612
                                        realField = None,
613
                                        contFracMaxErr = None):
614
    """
615
    For a given function f, a degree and an interval, compute the 
616
    zeros of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
617
    function over the interval.
618
    """
619
    ## If no realField argument is given try to retrieve it from the 
620
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
621
    if realField is None:
622
        try:
623
            ### Force failure if parent does not have prec() member.
624
            lowerBound.parent().prec()
625
            ### If no exception is thrown, set realField.
626
            realField = lowerBound.parent()
627
        except:
628
            realField = RR
629
    #print "Real field:", realField
630
    funcSa         = func
631
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
632
    print "Function as a string:", funcAsStringSa
633
    ## Create the Sollya version of the function and compute the
634
    #  Remez approximant
635
    funcSo  = pobyso_parse_string(funcAsStringSa)
636
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
637
                                           maxDegree, 
638
                                           lowerBound,
639
                                           upperBound)
640
    ## Compute the zeroes of the error function.
641
    ### First create the error function with copies since they are needed later.
642
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
643
                                                sollya_lib_copy_obj(funcSo))
644
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
645
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
646
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
647
    #print "Zeroes of the error function from Sollya:"
648
    #pobyso_autoprint(errorFuncZerosSo)
649
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
650
    pobyso_clear_obj(errorFuncZerosSo)
651
    #print "\nZeroes of the error function from Sage:"
652
    #print errorFuncZerosSa
653
    #
654
    ## Deal with the truncated polynomial now.
655
    ### Create the formats list. Notice the "*" before the list variable name.
656
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
657
    #print "Precisions list as Sollya object:", 
658
    #pobyso_autoprint(truncFormatListSo)
659
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
660
    #print "Truncated polynomial:", ; pobyso_autoprint(pTruncSo)
661
    ### Compute the error function. This time we consume both the function
662
    #   and the polynomial.
663
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, 
664
                                                funcSo)
665
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
666
    pobyso_clear_obj(pStarSo)
667
    #print "Zeroes of the error function for the truncated polynomial from Sollya:"
668
    #pobyso_autoprint(errorFuncZerosSo)
669
    errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
670
    pobyso_clear_obj(errorFuncZerosSo)
671
    ## Sollya objects clean up.
672
    pobyso_clear_obj(intervalSo)
673
    errorFuncZerosSa += errorFuncTruncZerosSa
674
    errorFuncZerosSa.sort()
675
    ## If required, convert the numbers to rational numbers.
676
    if not contFracMaxErr is None:
677
        for index in xrange(0, len(errorFuncZerosSa)):
678
            errorFuncZerosSa[index] = \
679
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
680
    return errorFuncZerosSa
681
    #
682
# End remez_all_poly_error_func_zeros
683
#
684
def cvp_remez_poly_error_func_zeros(funct, 
685
                                    maxDegree, 
686
                                    lowerBound, 
687
                                    upperBound,
688
                                    realField = None,
689
                                    contFracMaxErr = None):
690
    """
691
    For a given function f, a degree and an interval, compute the 
692
    zeros of the (f-remez_d(f)) function.
693
    """
694
    ## If no realField argument is given try to retrieve it from the 
695
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
696
    if realField is None:
697
        try:
698
            ### Force failure if parent does not have prec() member.
699
            lowerBound.parent().prec()
700
            ### If no exception is thrown, set realField.
701
            realField = lowerBound.parent()
702
        except:
703
            realField = RR
704
    #print "Real field:", realField
705
    funcSa         = func
706
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
707
    #print "Function as a string:", funcAsStringSa
708
    ## Create the Sollya version of the function and compute the
709
    #  Remez approximant
710
    funcSo  = pobyso_parse_string(funcAsStringSa)
711
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
712
                                           maxDegree, 
713
                                           lowerBound,
714
                                           upperBound)
715
    ## Compute the zeroes of the error function.
716
    ### Error function creation consumes both pStarSo and funcSo.
717
    errorFuncSo = sollya_lib_build_function_sub(pStarSo, funcSo)
718
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
719
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
720
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
721
    #print "Zeroes of the error function from Sollya:"
722
    #pobyso_autoprint(errorFuncZerosSo)
723
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
724
    pobyso_clear_obj(errorFuncZerosSo)
725
    ## Sollya objects clean up.
726
    pobyso_clear_obj(intervalSo)
727
    ## Converting to rational numbers, if required.
728
    if not contFracMaxErr is None:
729
        for index in xrange(0, len(errorFuncZerosSa)):
730
            errorFuncZerosSa[index] = \
731
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
732
    return errorFuncZerosSa
733
    #
734
# End remez_poly_error_func_zeros
735
#
736
def cvp_round_to_bits(num, numBits):
737
    """
738
    Round "num" to "numBits" bits.
739
    @param num    : the number to round;
740
    @param numBits: the number of bits to round to.
741
    """
742
    if num == 0:
743
        return num
744
    log2ofNum  = 0
745
    numRounded = 0
746
    ## Avoid conversion to floating-point if not necessary.
747
    try:
748
        log2OfNum = num.abs().log2()
749
    except:
750
        log2OfNum = RR(num).abs().log2()
751
    ## Works equally well for num >= 1 and num < 1.
752
    log2OfNum = ceil(log2OfNum)
753
    # print "cvp_round_to_bits - Log2OfNum:", log2OfNum
754
    divMult = log2OfNum - numBits
755
    #print "cvp_round_to_bits - DivMult:", divMult
756
    if divMult != 0:
757
        numRounded = round(num/2^divMult) * 2^divMult
758
    else:
759
        numRounded = num
760
    return numRounded
761
# End cvp_round_to_bits
762
#
763
def cvp_vector_in_basis(vect, basis):
764
    """
765
    Compute the coordinates of "vect" in "basis" by
766
    solving a linear system.
767
    @param vect  the vector we want to compute the coordinates of
768
                  in coordinates of the ambient space;
769
    @param basis the basis we want to compute the coordinates in
770
                  as a matrix relative to the ambient space.
771
    """
772
    ## Create the variables for the linear equations.
773
    varDeclString = ""
774
    basisIterationsRange = xrange(0, basis.nrows())
775
    ### Building variables declaration string.
776
    for index in basisIterationsRange:
777
        varName = "a" + str(index)
778
        if varDeclString == "":
779
            varDeclString += varName
780
        else:
781
            varDeclString += "," + varName
782
    ### Variables declaration
783
    varsList = var(varDeclString)
784
    sage_eval("var('" + varDeclString + "')")
785
    ## Create the equations
786
    equationString = ""
787
    equationsList = []
788
    for vIndex in xrange(0, len(vect)):
789
        equationString = ""
790
        for bIndex in basisIterationsRange:
791
            if equationString != "":
792
                equationString += "+"
793
            equationString += str(varsList[bIndex]) + "*" + str(basis[bIndex,vIndex])
794
        equationString += " == " + str(vect[vIndex])
795
        equationsList.append(sage_eval(equationString))
796
    ## Solve the equations system. The solution dictionary is needed
797
    #  to recover the values of the solutions.
798
    solutions = solve(equationsList,varsList, solution_dict=True)
799
    ## This code is deactivated: did not solve the problem.
800
    """
801
    if len(solutions) == 0:
802
        print "Trying Maxima to_poly_sove."
803
        solutions = solve(equationsList,varsList, solution_dict=True, to_poly_solve = 'force')
804
    """
805
    #print "Solutions:", s
806
    ## Recover solutions in rational components vector.
807
    vectInBasis = vector(QQ, basis.nrows())
808
    ### There can be several solution, in the general case.
809
    #   Here, there is only one (or none). For each solution, each 
810
    #   variable has to be searched for.
811
    for sol in solutions:
812
        for bIndex in basisIterationsRange:
813
            vectInBasis[bIndex] = sol[varsList[bIndex]]
814
    return vectInBasis
815
# End cpv_vector_in_basis.
816
#
817
print "... functions for CVP loaded."