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