Statistiques
| Branche: | Révision :

cvp / src / functions_for_cvp.sage @ c7bf1784

Historique | Voir | Annoter | Télécharger (43,32 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_float_basis(monomialsList, pointsList, realField):
525
    """
526
    For a given monomials list and points list, compute the floating-point
527
    basis matrix.
528
    """
529
    numRows    = len(monomialsList)
530
    numCols    = len(pointsList)
531
    if numRows == 0 or numCols == 0:
532
        return matrix(realField, 0, 0)
533
    #
534
    floatBasis = matrix(realField, numRows, numCols)
535
    for rIndex in xrange(0, numRows):
536
        for cIndex in xrange(0, numCols):
537
            floatBasis[rIndex,cIndex] = \
538
                monomialsList[rIndex](realField(pointsList[cIndex]))
539
    return floatBasis
540
# End cvp_float_basis
541
#
542
def cvp_float_vector_for_approx(func, 
543
                                basisPointsList,
544
                                realField):
545
    """
546
    Compute a floating-point vector for the function and the points list.
547
    """
548
    #
549
    ## Some local variables.
550
    basisPointsNum = len(basisPointsList)
551
    #
552
    ##
553
    vVect = vector(realField, basisPointsNum)
554
    for vIndex in xrange(0,basisPointsNum):
555
        vVect[vIndex] = \
556
            func(basisPointsList[vIndex])
557
    return vVect
558
# End cvp_float_vector_for_approx.
559
#
560
def cvp_func_abs_max_for_points(func, pointsList):
561
    """
562
    Compute the maximum absolute value of a function for a list of 
563
    points.
564
    If several points yield the same value and this value is the maximum,
565
    an unspecified point of this set is returned.
566
    @return (theMaxPoint, theMaxValue) 
567
    """
568
    evalDict = dict()
569
    if len(pointsList) == 0:
570
        return None
571
    #
572
    for pt in pointsList:
573
        evalDict[abs(func(pt))] = pt
574
    maxAbs = max(evalDict.keys())
575
    return (evalDict[maxAbs], maxAbs)
576
# End cvp_func_abs_max_for_points
577
#
578
def cvp_hkz_reduce(intBasis):
579
    """
580
    Thin and simplistic wrapper the HKZ function of the FP_LLL module.
581
    """
582
    fplllIntBasis = FP_LLL(intBasis)
583
    fplllIntBasis.HKZ()
584
    return fplllIntBasis._sage_()
585
# End cvp_hkz_reduce.
586
#
587
def cvp_int_basis(floatBasis, 
588
                  commonScalingExp, 
589
                  extraScalingExpsList):
590
    """
591
    From the float basis, the common scaling factor and the extra exponents 
592
    compute the integral basis.
593
    """
594
    ## Checking arguments.
595
    if floatBasis.nrows() != len(extraScalingExpsList):
596
        return None
597
    intBasis = matrix(ZZ, floatBasis.nrows(), floatBasis.ncols())
598
    for rIndex in xrange(0, floatBasis.nrows()):
599
        for cIndex in xrange(0, floatBasis.ncols()):
600
            intBasis[rIndex, cIndex] = \
601
            (floatBasis[rIndex, cIndex] * \
602
            2^(commonScalingExp + extraScalingExpsList[rIndex])).round()
603
    return intBasis
604
# End cvp_int_basis.
605
#
606
def cvp_int_vector_for_approx(floatVect, commonScalingExp, extraScalingExp):
607
    """
608
    Compute the integral version of the function vector.
609
    """
610
    totalScalingFactor = 2^(commonScalingExp + extraScalingExp)
611
    intVect = vector(ZZ, len(floatVect))
612
    for index in xrange(0, len(floatVect)):
613
        intVect[index] = (floatVect[index] * totalScalingFactor).round()
614
    return intVect
615
# End cvp_int_vector_for_approx.
616
#
617
def cvp_lll_reduce(intBasis):
618
    """
619
    Thin and simplistic wrapper around the LLL function
620
    """
621
    return intBasis.LLL()
622
# End cvp_lll_reduce.
623
#
624
def cvp_monomials_list(exponentsList, varName = None):
625
    """
626
    Create a list of monomials corresponding to the given exponentsList.
627
    Monomials are defined as functions.
628
    """
629
    monomialsList = []
630
    for exponent in exponentsList:
631
        if varName is None:
632
            monomialsList.append((x^exponent).function(x))
633
        else:
634
            monomialsList.append((varName^exponent).function(varName))
635
    return monomialsList
636
# End cvp_monomials_list.
637
#
638
def cvp_polynomial_coeffs_from_vect(cvpVectInBasis,
639
                                    coeffsPrecList, 
640
                                    minCoeffsExpList,
641
                                    extraFuncScalingExp):
642
    """
643
    Computes polynomial coefficients out of the elements of the CVP vector.
644
    @todo
645
    Rewrite the code with a single exponentiation, at the very end.
646
    """
647
    numElements = len(cvpVectInBasis)
648
    ## Arguments check.
649
    if numElements != len(minCoeffsExpList) or \
650
       numElements != len(coeffsPrecList):
651
        return None
652
    polynomialCoeffsList = []
653
    for index in xrange(0, numElements):
654
        currentCoeff = cvp_round_to_bits(cvpVectInBasis[index],
655
                                         coeffsPrecList[index])
656
        #print "cvp_polynomial_coeffs - current coefficient rounded:", currentCoeff
657
        currentCoeff *= 2^(minCoeffsExpList[index])
658
        #print "cvp_polynomial_coeffs - current coefficient scaled :", currentCoeff
659
        currentCoeff *= 2^(-coeffsPrecList[index])
660
        #print "cvp_polynomial_coeffs - current coefficient prec corrected :", currentCoeff
661
        currentCoeff *= 2^(-extraFuncScalingExp)
662
        #print "cvp_polynomial_coeffs - current coefficient extra func scaled :", currentCoeff
663
        polynomialCoeffsList.append(currentCoeff)
664
    return polynomialCoeffsList
665
# En polynomial_coeffs_from_vect.
666
#
667
def cvp_polynomial_error_func_maxis(funcSa,
668
                                    polySa, 
669
                                    lowerBoundSa, 
670
                                    upperBoundSa,
671
                                    realField = None,
672
                                    contFracMaxErr = None):
673
    """
674
    For a given function, approximation polynomial and interval (given 
675
    as bounds) compute the maxima of the functSa - polySo on the interval.
676
    Also computes the infnorm of the error function.
677
    """
678
    ## Arguments check.
679
    # @todo.
680
    ## If no realField argument is given try to retrieve it from the 
681
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
682
    if realField is None:
683
        try:
684
            ### Force failure if parent does not have prec() member.
685
            lowerBound.parent().prec()
686
            ### If no exception is thrown, set realField.
687
            realField = lowerBound.parent()
688
        except:
689
            realField = RR
690
    #print "Real field:", realField
691
    ## Compute the Sollya version of the function.
692
    funcAsStringSa = funcSa._assume_str().replace("_SAGE_VAR_","",100)
693
    funcSo  = pobyso_parse_string(funcAsStringSa)
694
    if pobyso_is_error_so_sa(funcSo):
695
        pobyso_clear_obj(funcSo)
696
        return None
697
    ## Compute the Sollya version of the polynomial.
698
    ## The conversion is made from a floating-point coefficients polynomial.
699
    try:
700
        polySa.base_ring().prec()
701
        convFloatPolySa = polySa
702
    except:
703
        convFloatPolySa = realField[polySa.variables()[0]](polySa)
704
    polySo = pobyso_float_poly_sa_so(convFloatPolySa)
705
    if pobyso_is_error_so_sa(funcSo):
706
        pobyso_clear_obj(funcSo)
707
        pobyso_clear_obj(polySo)
708
        return None
709
    ## Copy both funcSo and polySo as they are needed later for the infnorm..
710
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(funcSo), 
711
                                                sollya_lib_copy_obj(polySo))
712
    if pobyso_is_error_so_sa(errorFuncSo):
713
        pobyso_clear_obj(errorFuncSo)
714
        return None
715
    ## Compute the derivative.
716
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
717
    pobyso_clear_obj(errorFuncSo)
718
    if pobyso_is_error_so_sa(diffErrorFuncSo):
719
        pobyso_clear_obj(diffErrorFuncSo)
720
        return None
721
    ## Compute the interval.
722
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
723
    if pobyso_is_error_so_sa(intervalSo):
724
        pobyso_clear_obj(diffErrorFuncSo)
725
        pobyso_clear_obj(intervalSo)
726
        return None
727
    ## Compute the infnorm of the error function.
728
    errFuncSupNormSa = pobyso_supnorm_so_sa(polySo, 
729
                                            funcSo, 
730
                                            intervalSo, 
731
                                            realFieldSa = realField)
732
    pobyso_clear_obj(polySo)
733
    pobyso_clear_obj(funcSo)
734
    ## Compute the zeros of the derivative.
735
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
736
    pobyso_clear_obj(diffErrorFuncSo)
737
    pobyso_clear_obj(intervalSo)
738
    if pobyso_is_error_so_sa(errorFuncMaxisSo):
739
        pobyso_clear_obj(errorFuncMaxisSo)
740
        return None
741
    errorFuncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
742
    pobyso_clear_obj(errorFuncMaxisSo)
743
    ## If required, convert the numbers to rational numbers.
744
    if not contFracMaxErr is None:
745
        for index in xrange(0, len(errorFuncMaxisSa)):
746
            errorFuncMaxisSa[index] = \
747
                errorFuncMaxisSa[index].nearby_rational(contFracMaxErr)
748
    return (errorFuncMaxisSa, errFuncSupNormSa)
749
# End cvp_polynomial_error_func_maxis
750
#
751
def cvp_polynomial_from_coeffs_and_exps(coeffsList, 
752
                                        expsList, 
753
                                        polyField = None,
754
                                        theVar = None):
755
    """
756
    Build a polynomial in the classical monomials (possibly lacunary) basis 
757
    from a list of coefficients and a list of exponents. The polynomial is in
758
    the polynomial field given as argument.
759
    """
760
    if len(coeffsList) != len(expsList):
761
        return None
762
    ## If no variable is given, "x" is used.
763
    ## If no polyField is given, we fall back on a polynomial field on RR.
764
    if theVar is None:
765
        if polyField is None:
766
            theVar = x
767
            polyField = RR[theVar]
768
        else:
769
            theVar = var(polyField.variable_name())
770
    else: ### theVar is set.
771
        ### No polyField, fallback on RR[theVar].
772
        if polyField is None:
773
            polyField = RR[theVar]
774
        else: ### Both the polyFiled and theVar are set: create a new polyField
775
              #   with theVar. The original polyField is not affected by the 
776
              #   change.
777
            polyField = polyField.change_var(theVar)  
778
    ## Seed the polynomial.
779
    poly = polyField(0)
780
    ## Append the terms.
781
    for coeff, exp in zip(coeffsList, expsList):
782
        poly += polyField(coeff * theVar^exp)
783
    return poly
784
# End cvp_polynomial_from_coeffs_and_exps.
785
#
786
def cvp_remez_all_poly_error_func_maxis(funct, 
787
                                        maxDegree, 
788
                                        lowerBound, 
789
                                        upperBound,
790
                                        precsList, 
791
                                        realField = None,
792
                                        contFracMaxErr = None):
793
    """
794
    For a given function f, a degree and an interval, compute the 
795
    maxima of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
796
    function over the interval.
797
    """
798
    ## If no realField argument is given try to retrieve it from the 
799
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
800
    if realField is None:
801
        try:
802
            ### Force failure if parent does not have prec() member.
803
            lowerBound.parent().prec()
804
            ### If no exception is thrown, set realField.
805
            realField = lowerBound.parent()
806
        except:
807
            realField = RR
808
    #print "Real field:", realField
809
    funcSa         = func
810
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
811
    print "Function as a string:", funcAsStringSa
812
    ## Create the Sollya version of the function and compute the
813
    #  Remez approximant
814
    funcSo  = pobyso_parse_string(funcAsStringSa)
815
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
816
                                           maxDegree, 
817
                                           lowerBound,
818
                                           upperBound)
819
    ## Compute the error function and its derivative
820
    ### First create the error function with copies since they are needed later.
821
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
822
                                                sollya_lib_copy_obj(funcSo))
823
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
824
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
825
    pobyso_clear_obj(errorFuncSo)
826
    ## Compute the zeros of the derivative.
827
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
828
    pobyso_clear_obj(diffErrorFuncSo)
829
    errorFuncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
830
    pobyso_clear_obj(errorFuncMaxisSo)
831
    ## Compute the truncated Remez polynomial and the error function.
832
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
833
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
834
    pobyso_clear_obj(pStarSo)
835
    ### Compute the error function. This time we consume both the function
836
    #   and the polynomial.
837
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, funcSo)
838
    ## Compute the derivative.
839
    diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo)
840
    pobyso_clear_obj(errorFuncSo)
841
    ## Compute the zeros of the derivative.
842
    errorFuncMaxisSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo)
843
    pobyso_clear_obj(diffErrorFuncSo)
844
    pobyso_clear_obj(intervalSo)
845
    errorFuncTruncMaxisSa = pobyso_float_list_so_sa(errorFuncMaxisSo)
846
    pobyso_clear_obj(errorFuncMaxisSo)
847
    ## Merge with the first list, removing duplicates if any.
848
    errorFuncMaxisSa.extend(elem for elem in errorFuncTruncMaxisSa \
849
                            if elem not in errorFuncMaxisSa)
850
    errorFuncMaxisSa.sort()
851
    ## If required, convert the numbers to rational numbers.
852
    if not contFracMaxErr is None:
853
        for index in xrange(0, len(errorFuncMaxisSa)):
854
            errorFuncMaxisSa[index] = \
855
                errorFuncMaxisSa[index].nearby_rational(contFracMaxErr)
856
    return errorFuncMaxisSa
857
# End cvp_remez_all_poly_error_func_maxis.
858
#
859
def cvp_remez_all_poly_error_func_zeros(funct, 
860
                                        maxDegree, 
861
                                        lowerBound, 
862
                                        upperBound,
863
                                        precsList, 
864
                                        realField = None,
865
                                        contFracMaxErr = None):
866
    """
867
    For a given function f, a degree and an interval, compute the 
868
    zeros of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) 
869
    function over the interval.
870
    """
871
    ## If no realField argument is given try to retrieve it from the 
872
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
873
    if realField is None:
874
        try:
875
            ### Force failure if parent does not have prec() member.
876
            lowerBound.parent().prec()
877
            ### If no exception is thrown, set realField.
878
            realField = lowerBound.parent()
879
        except:
880
            realField = RR
881
    #print "Real field:", realField
882
    funcSa         = func
883
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
884
    print "Function as a string:", funcAsStringSa
885
    ## Create the Sollya version of the function and compute the
886
    #  Remez approximant
887
    funcSo  = pobyso_parse_string(funcAsStringSa)
888
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
889
                                           maxDegree, 
890
                                           lowerBound,
891
                                           upperBound)
892
    ## Compute the zeroes of the error function.
893
    ### First create the error function with copies since they are needed later.
894
    errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), 
895
                                                sollya_lib_copy_obj(funcSo))
896
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
897
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
898
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
899
    #print "Zeroes of the error function from Sollya:"
900
    #pobyso_autoprint(errorFuncZerosSo)
901
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
902
    pobyso_clear_obj(errorFuncZerosSo)
903
    #print "\nZeroes of the error function from Sage:"
904
    #print errorFuncZerosSa
905
    #
906
    ## Deal with the truncated polynomial now.
907
    ### Create the formats list. Notice the "*" before the list variable name.
908
    truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList)
909
    #print "Precisions list as Sollya object:", 
910
    #pobyso_autoprint(truncFormatListSo)
911
    pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo)
912
    #print "Truncated polynomial:", ; pobyso_autoprint(pTruncSo)
913
    ### Compute the error function. This time we consume both the function
914
    #   and the polynomial.
915
    errorFuncSo = sollya_lib_build_function_sub(pTruncSo, 
916
                                                funcSo)
917
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
918
    pobyso_clear_obj(pStarSo)
919
    #print "Zeroes of the error function for the truncated polynomial from Sollya:"
920
    #pobyso_autoprint(errorFuncZerosSo)
921
    errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
922
    pobyso_clear_obj(errorFuncZerosSo)
923
    ## Sollya objects clean up.
924
    pobyso_clear_obj(intervalSo)
925
    errorFuncZerosSa += errorFuncTruncZerosSa
926
    errorFuncZerosSa.sort()
927
    ## If required, convert the numbers to rational numbers.
928
    if not contFracMaxErr is None:
929
        for index in xrange(0, len(errorFuncZerosSa)):
930
            errorFuncZerosSa[index] = \
931
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
932
    return errorFuncZerosSa
933
    #
934
# End remez_all_poly_error_func_zeros
935
#
936
def cvp_remez_poly_error_func_zeros(funct, 
937
                                    maxDegree, 
938
                                    lowerBound, 
939
                                    upperBound,
940
                                    realField = None,
941
                                    contFracMaxErr = None):
942
    """
943
    For a given function f, a degree and an interval, compute the 
944
    zeros of the (f-remez_d(f)) function.
945
    """
946
    ## If no realField argument is given try to retrieve it from the 
947
    #  bounds. If impossible (e.g. rational bound), fall back on RR. 
948
    if realField is None:
949
        try:
950
            ### Force failure if parent does not have prec() member.
951
            lowerBound.parent().prec()
952
            ### If no exception is thrown, set realField.
953
            realField = lowerBound.parent()
954
        except:
955
            realField = RR
956
    #print "Real field:", realField
957
    funcSa         = func
958
    funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100)
959
    #print "Function as a string:", funcAsStringSa
960
    ## Create the Sollya version of the function and compute the
961
    #  Remez approximant
962
    funcSo  = pobyso_parse_string(funcAsStringSa)
963
    pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, 
964
                                           maxDegree, 
965
                                           lowerBound,
966
                                           upperBound)
967
    ## Compute the zeroes of the error function.
968
    ### Error function creation consumes both pStarSo and funcSo.
969
    errorFuncSo = sollya_lib_build_function_sub(pStarSo, funcSo)
970
    intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound)
971
    errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo)
972
    pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well.
973
    #print "Zeroes of the error function from Sollya:"
974
    #pobyso_autoprint(errorFuncZerosSo)
975
    errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo)
976
    pobyso_clear_obj(errorFuncZerosSo)
977
    ## Sollya objects clean up.
978
    pobyso_clear_obj(intervalSo)
979
    ## Converting to rational numbers, if required.
980
    if not contFracMaxErr is None:
981
        for index in xrange(0, len(errorFuncZerosSa)):
982
            errorFuncZerosSa[index] = \
983
                errorFuncZerosSa[index].nearby_rational(contFracMaxErr)
984
    return errorFuncZerosSa
985
    #
986
# End remez_poly_error_func_zeros
987
#
988
def cvp_round_to_bits(num, numBits):
989
    """
990
    Round "num" to "numBits" bits.
991
    @param num    : the number to round;
992
    @param numBits: the number of bits to round to.
993
    """
994
    if num == 0:
995
        return num
996
    log2ofNum  = 0
997
    numRounded = 0
998
    ## Avoid conversion to floating-point if not necessary.
999
    try:
1000
        log2OfNum = num.abs().log2()
1001
    except:
1002
        log2OfNum = RR(num).abs().log2()
1003
    ## Works equally well for num >= 1 and num < 1.
1004
    log2OfNum = ceil(log2OfNum)
1005
    # print "cvp_round_to_bits - Log2OfNum:", log2OfNum
1006
    divMult = log2OfNum - numBits
1007
    #print "cvp_round_to_bits - DivMult:", divMult
1008
    if divMult != 0:
1009
        numRounded = round(num/2^divMult) * 2^divMult
1010
    else:
1011
        numRounded = num
1012
    return numRounded
1013
# End cvp_round_to_bits
1014
#
1015
def cvp_vector_in_basis(vect, basis):
1016
    """
1017
    Compute the coordinates of "vect" in "basis" by
1018
    solving a linear system.
1019
    @param vect  the vector we want to compute the coordinates of
1020
                  in coordinates of the ambient space;
1021
    @param basis the basis we want to compute the coordinates in
1022
                  as a matrix relative to the ambient space.
1023
    """
1024
    ## Create the variables for the linear equations.
1025
    varDeclString = ""
1026
    basisIterationsRange = xrange(0, basis.nrows())
1027
    ### Building variables declaration string.
1028
    for index in basisIterationsRange:
1029
        varName = "a" + str(index)
1030
        if varDeclString == "":
1031
            varDeclString += varName
1032
        else:
1033
            varDeclString += "," + varName
1034
    ### Variables declaration
1035
    varsList = var(varDeclString)
1036
    sage_eval("var('" + varDeclString + "')")
1037
    ## Create the equations
1038
    equationString = ""
1039
    equationsList = []
1040
    for vIndex in xrange(0, len(vect)):
1041
        equationString = ""
1042
        for bIndex in basisIterationsRange:
1043
            if equationString != "":
1044
                equationString += "+"
1045
            equationString += str(varsList[bIndex]) + "*" + str(basis[bIndex,vIndex])
1046
        equationString += " == " + str(vect[vIndex])
1047
        equationsList.append(sage_eval(equationString))
1048
    ## Solve the equations system. The solution dictionary is needed
1049
    #  to recover the values of the solutions.
1050
    solutions = solve(equationsList,varsList, solution_dict=True)
1051
    ## This code is deactivated: did not solve the problem.
1052
    """
1053
    if len(solutions) == 0:
1054
        print "Trying Maxima to_poly_sove."
1055
        solutions = solve(equationsList,varsList, solution_dict=True, to_poly_solve = 'force')
1056
    """
1057
    #print "Solutions:", s
1058
    ## Recover solutions in rational components vector.
1059
    vectInBasis = vector(QQ, basis.nrows())
1060
    ### There can be several solution, in the general case.
1061
    #   Here, there is only one (or none). For each solution, each 
1062
    #   variable has to be searched for.
1063
    for sol in solutions:
1064
        for bIndex in basisIterationsRange:
1065
            vectInBasis[bIndex] = sol[varsList[bIndex]]
1066
    return vectInBasis
1067
# End cpv_vector_in_basis.
1068
#
1069
sys.stderr.write("... functions for CVP loaded.\n")