cvp / src / functions_for_cvp.sage @ 9cf1fb38
Historique | Voir | Annoter | Télécharger (32,95 ko)
1 |
""" |
---|---|
2 |
@file functions_for_cvp.sage |
3 |
@package functions_for_cvp |
4 |
Auxiliary functions used for CVP. |
5 |
|
6 |
@author S.T. |
7 |
""" |
8 |
print "Functions for CVP loading..." |
9 |
# |
10 |
def cvp_affine_from_chebyshev(lowerBound, upperBound): |
11 |
""" |
12 |
Compute the affine parameters to map [-1, 1] to the interval given |
13 |
as argument. |
14 |
@param: lowerBound (of the target interval) |
15 |
@param: upperBound (of the target interval) |
16 |
@return the (scalingCoefficient, offset) tuple. |
17 |
""" |
18 |
## Check bounds consistency. Bounds must differ. |
19 |
if lowerBound >= upperBound: |
20 |
return None |
21 |
scalingCoefficient = (upperBound - lowerBound) / 2 |
22 |
offset = (lowerBound + upperBound) / 2 |
23 |
return (scalingCoefficient, offset) |
24 |
# End cvp_affine_from_chebyshev |
25 |
# |
26 |
def cvp_affine_to_chebyshev(lowerBound, upperBound): |
27 |
""" |
28 |
Compute the affine parameters to map the interval given |
29 |
as argument to [-1, 1]. |
30 |
@param lowerBound (of the target interval) |
31 |
@param upperBound (of the target interval) |
32 |
@return the (scalingCoefficient, offset) tuple. |
33 |
""" |
34 |
## Check bounds consistency. Bounds must differ. |
35 |
if lowerBound >= upperBound: |
36 |
return None |
37 |
scalingCoefficient = 2 / (upperBound - lowerBound) |
38 |
## If bounds are symmetrical with relation to 0, return 0 straight before |
39 |
# attempting a division by 0. |
40 |
if lowerBound == -upperBound: |
41 |
offset = 0 |
42 |
else: |
43 |
offset = (lowerBound + upperBound) / (lowerBound - upperBound) |
44 |
return (scalingCoefficient, offset) |
45 |
# End cvp_affine_to_chebyshev |
46 |
# |
47 |
def cvp_babai(redBasis, redBasisGso, vect): |
48 |
""" |
49 |
Closest plane vector implementation as per Babaï. |
50 |
@param redBasis : a lattice basis, preferably a reduced one; |
51 |
@param redBasisGSO: the GSO of the previous basis; |
52 |
@param vector : a vector, in coordinated in the ambient |
53 |
space of the lattice |
54 |
@return the closest vector to the input, in coordinates in the |
55 |
ambient space of the lattice. |
56 |
""" |
57 |
## A deep copy is not necessary here. |
58 |
curVect = copy(vect) |
59 |
print "cvp_babai - Vector:", vect |
60 |
## From index of last row down to 0. |
61 |
for vIndex in xrange(len(redBasis.rows())-1, -1, -1): |
62 |
curRBGSO = redBasisGso.row(vIndex) |
63 |
curVect = curVect - \ |
64 |
(round((curVect * curRBGSO) / (curRBGSO * curRBGSO)) * \ |
65 |
redBasis.row(vIndex)) |
66 |
return vect - curVect |
67 |
# End cvp_babai |
68 |
# |
69 |
def cvp_bkz_reduce(intBasis, blockSize): |
70 |
""" |
71 |
Thin and simplistic wrapper the HKZ function of the FP_LLL module. |
72 |
""" |
73 |
fplllIntBasis = FP_LLL(intBasis) |
74 |
fplllIntBasis.BKZ(blockSize) |
75 |
return fplllIntBasis._sage_() |
76 |
# End cvp_hkz_reduce. |
77 |
# |
78 |
def cvp_candidate_vectors(intVect, diffsList): |
79 |
""" |
80 |
Computes a list of best approximation vectors based on the CVP |
81 |
computed vector using the differences between this vector and the |
82 |
target function vector. |
83 |
@param intVect the vector from which candidates are built |
84 |
@param diffsList the list of the values that will be appended to the |
85 |
initial vector to form the candidates |
86 |
@return A matrix made of candidates rows or None if there is some invalid |
87 |
argument. |
88 |
@par How it works |
89 |
The idea is to create all the possible vectors based on the computed CVP |
90 |
vector and its differences to the target. Those are collected in |
91 |
diffsList.@n |
92 |
From the vector we create all the possible combinations between the original |
93 |
value and the differences in diffsList. Some of those can be equal to 0. |
94 |
They are not taken into account. If nonZeroDiffsCount is the number of |
95 |
diffsList element different from 0, there are 2^nonZeroDiffsCount |
96 |
possible variants.@n |
97 |
To compute those, we build a 2^nonZeroDiffsCount rows matrix. For the |
98 |
first element in the vector corresponding to a non-zero in diffsList, we |
99 |
switch values in the matrix at every row.@n |
100 |
For the second such element (non-zero corresponding diffsList element), we |
101 |
switch values in the matrix at every second row for a string of two similar |
102 |
values.@n |
103 |
For the third such element, change happens at every fourth row, for a |
104 |
string of four similar values. And so on... |
105 |
@par Example |
106 |
@verbatim |
107 |
vect = [10, 20, 30, 40, 50] |
108 |
diffsList = [ 1, 0, -1, 0, 1] |
109 |
vectCandMat = [[10, 20, 30, 40, 50], |
110 |
[11, 20, 30, 40, 50], |
111 |
[10, 20, 29, 40, 50], |
112 |
[11, 20, 29, 40, 50], |
113 |
[10, 20, 30, 40, 51], |
114 |
[11, 20, 30, 40, 51], |
115 |
[10, 20, 29, 40, 51], |
116 |
[11, 20, 29, 40, 51]] |
117 |
@endverbatim |
118 |
""" |
119 |
## Arguments check. |
120 |
vectLen = len(intVect) |
121 |
if vectLen != len(diffsList): |
122 |
return None |
123 |
# |
124 |
## Compute the number of diffsList elements different from 0. |
125 |
nonZeroDiffsCount = 0 |
126 |
for elem in diffsList: |
127 |
if elem != 0: |
128 |
nonZeroDiffsCount += 1 |
129 |
if nonZeroDiffsCount == 0: |
130 |
return matrix(ZZ, 0, vectLen) |
131 |
numRows = 2^nonZeroDiffsCount |
132 |
vectMat = matrix(ZZ, numRows, vectLen) |
133 |
for rowIndex in xrange(0,numRows): |
134 |
effColIndex = 0 |
135 |
for colIndex in xrange(0,vectLen): |
136 |
vectMat[rowIndex, colIndex] = intVect[colIndex] |
137 |
if diffsList[colIndex] != 0: |
138 |
if (rowIndex % 2^(effColIndex + 1)) >= 2^effColIndex: |
139 |
vectMat[rowIndex, colIndex] += diffsList[colIndex] |
140 |
effColIndex += 1 |
141 |
return vectMat |
142 |
# End cvp_candidate_vectors |
143 |
# |
144 |
def cvp_coefficients_bounds_projection(executablePath, arguments): |
145 |
""" |
146 |
Compute the min and max of the coefficients with linear |
147 |
programming projection. |
148 |
@param executablePath: the path to the binary program; |
149 |
@param arguments: a list of arguments to be givent to the binary |
150 |
@return: the min and max coefficients value arrays (in this order). |
151 |
""" |
152 |
from subprocess import check_output |
153 |
commandLine = [executablePath] + arguments |
154 |
## Get rid of output on stderr. |
155 |
devNull = open("/dev/null", "rw") |
156 |
## Run ther program. |
157 |
otp = check_output(commandLine, stderr=devNull) |
158 |
devNull.close() |
159 |
## Recover the results |
160 |
bounds = sage_eval(otp) |
161 |
minCoeffsExpList = [] |
162 |
maxCoeffsExpList = [] |
163 |
#print "bounds:", bounds |
164 |
for boundsPair in bounds: |
165 |
minCoeffsExpList.append(boundsPair[0]) |
166 |
maxCoeffsExpList.append(boundsPair[1]) |
167 |
print "minCoeffsExpList:", minCoeffsExpList |
168 |
print "maxCoeffsExpList:", maxCoeffsExpList |
169 |
return (minCoeffsExpList, maxCoeffsExpList) |
170 |
# End cvp_coefficients_bounds_projection |
171 |
|
172 |
def cvp_coefficients_bounds_projection_exps(executablePath, |
173 |
arguments, |
174 |
realField = None): |
175 |
""" |
176 |
Compute the min and max exponents of the coefficients with linear |
177 |
programming projection. |
178 |
@param executablePath: the path to the binary program; |
179 |
@param arguments: a list of arguments to be givent to the binary |
180 |
@param realField: the realField to use for floating-point conversion. |
181 |
@return: the min and max exponents arrays (in this order). |
182 |
""" |
183 |
## If no realField is givne, fall back on RR. |
184 |
if realField is None: |
185 |
realField = RR |
186 |
(minCoeffsExpList, maxCoeffsExpList) = \ |
187 |
cvp_coefficients_bounds_projection(executablePath,arguments) |
188 |
for index in xrange(0, len(minCoeffsExpList)): |
189 |
minCoeffsExpList[index] = \ |
190 |
realField(minCoeffsExpList[index]).abs().log2().floor() |
191 |
maxCoeffsExpList[index] = \ |
192 |
realField(maxCoeffsExpList[index]).abs().log2().floor() |
193 |
return (minCoeffsExpList, maxCoeffsExpList) |
194 |
# End cvp_coefficients_bounds_projection_exps |
195 |
|
196 |
def cvp_chebyshev_zeros_1k(numPoints, realField, contFracMaxErr = None): |
197 |
""" |
198 |
Compute the Chebyshev zeros for some polynomial degree (numPoints, for the |
199 |
zeros) scaled to the [lowerBound, upperBound] interval. |
200 |
The list is returned as row floating-point numbers is contFracMaxErr is None. |
201 |
Otherwise elements are transformed into rational numbers. |
202 |
""" |
203 |
if numPoints < 1: |
204 |
return None |
205 |
if numPoints == 0: |
206 |
return [0] |
207 |
## Check that realField has a "prec()" member. |
208 |
try: |
209 |
realField.prec() |
210 |
except: |
211 |
return None |
212 |
# |
213 |
zerosList = [] |
214 |
## formulas: cos (((2*i - 1) * pi) / (2*numPoints)) for i = 1,...,numPoints. |
215 |
# If i is -1-shifted, as in the following loop, formula is |
216 |
# cos (((2*i + 1) * pi) / (2*numPoints)) for i = 0,...,numPoints-1. |
217 |
for index in xrange(0, numPoints): |
218 |
## When number of points is odd (then, the Chebyshev degree is odd two), |
219 |
# the central point is 0. */ |
220 |
if (numPoints % 2 == 1) and ((2 * index + 1) == numPoints): |
221 |
if contFracMaxErr is None: |
222 |
zerosList.append(realField(0)) |
223 |
else: |
224 |
zerosList.append(0) |
225 |
else: |
226 |
currentCheby = \ |
227 |
((realField(2*index+1) * realField(pi)) / |
228 |
realField(2*numPoints)).cos() |
229 |
#print "Current Cheby:", currentCheby |
230 |
## Result is negated to have an increasing order list. |
231 |
if contFracMaxErr is None: |
232 |
zerosList.append(-currentCheby) |
233 |
else: |
234 |
zerosList.append(-currentCheby.nearby_rational(contFracMaxErr)) |
235 |
#print "Relative error:", (currentCheby/zerosList[index]) |
236 |
return zerosList |
237 |
# End cvp_chebyshev_zeros_1k. |
238 |
# |
239 |
def cvp_chebyshev_zeros_for_interval(lowerBound, |
240 |
upperBound, |
241 |
numPoints, |
242 |
realField = None, |
243 |
contFracMaxErr = None): |
244 |
""" |
245 |
Compute the Chebyshev zeros for some polynomial degree (numPoints, for the |
246 |
zeros) scaled to the [lowerBound, upperBound] interval. |
247 |
If no contFracMaxErr argument is given, we return the list as "raw" |
248 |
floating-points. Otherwise, rational numbers are returned. |
249 |
""" |
250 |
## Arguments check. |
251 |
if lowerBound >= upperBound: |
252 |
return None |
253 |
if numPoints < 1: |
254 |
return None |
255 |
## If no realField argument is given try to retrieve it from the |
256 |
# bounds. If impossible (e.g. rational bound), fall back on RR. |
257 |
if realField is None: |
258 |
try: |
259 |
### Force failure if parent does not have prec() member. |
260 |
lowerBound.parent().prec() |
261 |
### If no exception is thrown, set realField. |
262 |
realField = lowerBound.parent() |
263 |
except: |
264 |
realField = RR |
265 |
#print "Real field:", realField |
266 |
## We want "raw"floating-point nodes to only have one final rounding. |
267 |
chebyshevZerosList = \ |
268 |
cvp_chebyshev_zeros_1k(numPoints, realField) |
269 |
scalingFactor, offset = cvp_affine_from_chebyshev(lowerBound, upperBound) |
270 |
for index in xrange(0, len(chebyshevZerosList)): |
271 |
chebyshevZerosList[index] = \ |
272 |
chebyshevZerosList[index] * scalingFactor + offset |
273 |
if not contFracMaxErr is None: |
274 |
chebyshevZerosList[index] = \ |
275 |
chebyshevZerosList[index].nearby_rational(contFracMaxErr) |
276 |
return chebyshevZerosList |
277 |
# End cvp_chebyshev_zeros_for_interval. |
278 |
# |
279 |
def cvp_common_scaling_exp(precision, |
280 |
precisionFraction = None): |
281 |
""" |
282 |
Compute the common scaling factor (a power of 2). |
283 |
The exponent is a fraction of the precision; |
284 |
A extra factor is computed for the vector, |
285 |
exra factors are computed for the basis vectors, all in the corresponding |
286 |
functions. |
287 |
""" |
288 |
## Set a default value for the precisionFraction to 1/2. |
289 |
if precisionFraction is None: |
290 |
precisionFraction = 3/4 |
291 |
""" |
292 |
minBasisVectsNorm = oo |
293 |
currentNorm = 0 |
294 |
for vect in floatBasis.rows(): |
295 |
currentNorm = vect.norm() |
296 |
print "Current norm:", currentNorm |
297 |
if currentNorm < minBasisVectsNorm: |
298 |
minBasisVectsNorm = currentNorm |
299 |
currentNorm = funcVect.norm() |
300 |
print "Current norm:", currentNorm |
301 |
if currentNorm < minBasisVectsNorm: |
302 |
minBasisVectsNorm = currentNorm |
303 |
## Check if a push is need because the smallest norm is below 0. |
304 |
powerForMinNorm = floor(log(minBasisVectsNorm)/log2) |
305 |
print "Power for min norm :", powerForMinNorm |
306 |
print "Power for precision:", ceil(precision*precisionFraction) |
307 |
if powerForMinNorm < 0: |
308 |
return 2^(ceil(precision*precisionFraction) - powerFromMinNorm) |
309 |
else: |
310 |
""" |
311 |
return ceil(precision * precisionFraction) |
312 |
# End cvp_common_scaling_factor. |
313 |
# |
314 |
def cvp_evaluation_points_list(funct, |
315 |
maxDegree, |
316 |
lowerBound, |
317 |
upperBound, |
318 |
realField = None): |
319 |
""" |
320 |
Compute a list of points for the vector creation. |
321 |
Strategy: |
322 |
- compute the zeros of the function-remez; |
323 |
- compute the zeros of the function-rounded_remez; |
324 |
- compute the Chebyshev points. |
325 |
Merge the whole thing. |
326 |
""" |
327 |
|
328 |
# End cvp_evaluation_points_list. |
329 |
# |
330 |
def cvp_extra_basis_scaling_exps(precsList, minExponentsList): |
331 |
""" |
332 |
Computes the exponents for the extra scaling down of the elements of the |
333 |
basis. |
334 |
This extra scaling down is necessary when there are elements of the |
335 |
basis that have negative exponents. |
336 |
""" |
337 |
extraScalingExpsList = [] |
338 |
for minExp, prec in zip(minExponentsList, precsList): |
339 |
if minExp - prec < 0: |
340 |
extraScalingExpsList.append(minExp - prec) |
341 |
else: |
342 |
extraScalingExpsList.append(0) |
343 |
return extraScalingExpsList |
344 |
# End cvp_extra_basis_scaling_exps. |
345 |
# |
346 |
def cvp_extra_function_scaling_exp(floatBasis, |
347 |
floatFuncVect, |
348 |
maxExponentsList): |
349 |
""" |
350 |
Compute the extra scaling exponent for the function vector in ordre to |
351 |
guarantee that the maximum exponent can be reached for each element of the |
352 |
basis. |
353 |
""" |
354 |
## Check arguments |
355 |
if floatBasis.nrows() == 0 or \ |
356 |
floatBasis.ncols() == 0 or \ |
357 |
len(floatFuncVect) == 0: |
358 |
return 1 |
359 |
if len(maxExponentsList) != floatBasis.nrows(): |
360 |
return None |
361 |
## Compute the log of the norm of the function vector. |
362 |
funcVectNormLog = log(floatFuncVect.norm()) |
363 |
## Compute the extra scaling factor for each vector of the basis. |
364 |
# for norms ratios an maxExponentsList. |
365 |
extraScalingExp = 0 |
366 |
for (row, maxExp) in zip(floatBasis.rows(),maxExponentsList): |
367 |
rowNormLog = log(row.norm()) |
368 |
normsRatioLog2 = floor((funcVectNormLog - rowNormLog) / log2) |
369 |
#print "Current norms ratio log2:", normsRatioLog2 |
370 |
scalingExpCandidate = normsRatioLog2 - maxExp |
371 |
#print "Current scaling exponent candidate:", scalingExpCandidate |
372 |
if scalingExpCandidate < 0: |
373 |
if -scalingExpCandidate > extraScalingExp: |
374 |
extraScalingExp = -scalingExpCandidate |
375 |
return extraScalingExp |
376 |
# End cvp_extra_function_scaling_exp. |
377 |
# |
378 |
def cvp_float_basis(monomialsList, pointsList, realField): |
379 |
""" |
380 |
For a given monomials list and points list, compute the floating-point |
381 |
basis matrix. |
382 |
""" |
383 |
numRows = len(monomialsList) |
384 |
numCols = len(pointsList) |
385 |
if numRows == 0 or numCols == 0: |
386 |
return matrix(realField, 0, 0) |
387 |
# |
388 |
floatBasis = matrix(realField, numRows, numCols) |
389 |
for rIndex in xrange(0, numRows): |
390 |
for cIndex in xrange(0, numCols): |
391 |
floatBasis[rIndex,cIndex] = \ |
392 |
monomialsList[rIndex](realField(pointsList[cIndex])) |
393 |
return floatBasis |
394 |
# End cvp_float_basis |
395 |
# |
396 |
def cvp_float_vector_for_approx(func, |
397 |
basisPointsList, |
398 |
realField): |
399 |
""" |
400 |
Compute a floating-point vector for the function and the points list. |
401 |
""" |
402 |
# |
403 |
## Some local variables. |
404 |
basisPointsNum = len(basisPointsList) |
405 |
# |
406 |
## |
407 |
vVect = vector(realField, basisPointsNum) |
408 |
for vIndex in xrange(0,basisPointsNum): |
409 |
vVect[vIndex] = \ |
410 |
func(basisPointsList[vIndex]) |
411 |
return vVect |
412 |
# End cvp_float_vector_for_approx. |
413 |
# |
414 |
def cvp_hkz_reduce(intBasis): |
415 |
""" |
416 |
Thin and simplistic wrapper the HKZ function of the FP_LLL module. |
417 |
""" |
418 |
fplllIntBasis = FP_LLL(intBasis) |
419 |
fplllIntBasis.HKZ() |
420 |
return fplllIntBasis._sage_() |
421 |
# End cvp_hkz_reduce. |
422 |
# |
423 |
def cvp_int_basis(floatBasis, |
424 |
commonScalingExp, |
425 |
extraScalingExpsList): |
426 |
""" |
427 |
From the float basis, the common scaling factor and the extra exponents |
428 |
compute the integral basis. |
429 |
""" |
430 |
## Checking arguments. |
431 |
if floatBasis.nrows() != len(extraScalingExpsList): |
432 |
return None |
433 |
intBasis = matrix(ZZ, floatBasis.nrows(), floatBasis.ncols()) |
434 |
for rIndex in xrange(0, floatBasis.nrows()): |
435 |
for cIndex in xrange(0, floatBasis.ncols()): |
436 |
intBasis[rIndex, cIndex] = \ |
437 |
(floatBasis[rIndex, cIndex] * \ |
438 |
2^(commonScalingExp + extraScalingExpsList[rIndex])).round() |
439 |
return intBasis |
440 |
# End cvp_int_basis. |
441 |
# |
442 |
def cvp_int_vector_for_approx(floatVect, commonScalingExp, extraScalingExp): |
443 |
""" |
444 |
Compute the integral version of the function vector. |
445 |
""" |
446 |
totalScalingFactor = 2^(commonScalingExp + extraScalingExp) |
447 |
intVect = vector(ZZ, len(floatVect)) |
448 |
for index in xrange(0, len(floatVect)): |
449 |
intVect[index] = (floatVect[index] * totalScalingFactor).round() |
450 |
return intVect |
451 |
# End cvp_int_vector_for_approx. |
452 |
# |
453 |
def cvp_lll_reduce(intBasis): |
454 |
""" |
455 |
Thin and simplistic wrapper around the LLL function |
456 |
""" |
457 |
return intBasis.LLL() |
458 |
# End cvp_lll_reduce. |
459 |
# |
460 |
def cvp_monomials_list(exponentsList, varName = None): |
461 |
""" |
462 |
Create a list of monomials corresponding to the given exponentsList. |
463 |
Monomials are defined as functions. |
464 |
""" |
465 |
monomialsList = [] |
466 |
for exponent in exponentsList: |
467 |
if varName is None: |
468 |
monomialsList.append((x^exponent).function(x)) |
469 |
else: |
470 |
monomialsList.append((varName^exponent).function(varName)) |
471 |
return monomialsList |
472 |
# End cvp_monomials_list. |
473 |
# |
474 |
def cvp_polynomial_coeffs_from_vect(cvpVectInBasis, |
475 |
coeffsPrecList, |
476 |
minCoeffsExpList, |
477 |
extraFuncScalingExp): |
478 |
""" |
479 |
Computes polynomial coefficients out of the elements of the CVP vector. |
480 |
@todo |
481 |
Rewrite the code with a single exponentiation, at the very end. |
482 |
""" |
483 |
numElements = len(cvpVectInBasis) |
484 |
## Arguments check. |
485 |
if numElements != len(minCoeffsExpList) or \ |
486 |
numElements != len(coeffsPrecList): |
487 |
return None |
488 |
polynomialCoeffsList = [] |
489 |
for index in xrange(0, numElements): |
490 |
currentCoeff = cvp_round_to_bits(cvpVectInBasis[index], |
491 |
coeffsPrecList[index]) |
492 |
print "cvp_polynomial_coeffs - current coefficient rounded:", currentCoeff |
493 |
currentCoeff *= 2^(minCoeffsExpList[index]) |
494 |
print "cvp_polynomial_coeffs - current coefficient scaled :", currentCoeff |
495 |
currentCoeff *= 2^(-coeffsPrecList[index]) |
496 |
print "cvp_polynomial_coeffs - current coefficient prec corrected :", currentCoeff |
497 |
currentCoeff *= 2^(-extraFuncScalingExp) |
498 |
print "cvp_polynomial_coeffs - current coefficient extra func scaled :", currentCoeff |
499 |
polynomialCoeffsList.append(currentCoeff) |
500 |
return polynomialCoeffsList |
501 |
# En polynomial_coeffs_from_vect. |
502 |
# |
503 |
def cvp_polynomial_from_coeffs_and_exps(coeffsList, |
504 |
expsList, |
505 |
polyField = None, |
506 |
theVar = None): |
507 |
""" |
508 |
Build a polynomial in the classical monomials (possibly lacunary) basis |
509 |
from a list of coefficients and a list of exponents. The polynomial is in |
510 |
the polynomial field given as argument. |
511 |
""" |
512 |
if len(coeffsList) != len(expsList): |
513 |
return None |
514 |
## If no variable is given, "x" is used. |
515 |
## If no polyField is given, we fall back on a polynomial field on RR. |
516 |
if theVar is None: |
517 |
if polyField is None: |
518 |
theVar = x |
519 |
polyField = RR[theVar] |
520 |
else: |
521 |
theVar = var(polyField.variable_name()) |
522 |
else: # theVar is set. |
523 |
if polyField is None: |
524 |
polyField = RR[theVar] |
525 |
else: # Both the polyFiled and theVar are set: create a new polyField |
526 |
# with theVar. The original polyField is not affected by the change. |
527 |
polyField = polyField.change_var(theVar) |
528 |
## Seed the polynomial. |
529 |
poly = polyField(0) |
530 |
## Append the terms. |
531 |
for coeff, exp in zip(coeffsList, expsList): |
532 |
poly += polyField(coeff * theVar^exp) |
533 |
return poly |
534 |
# End cvp_polynomial_from_coeffs_and_exps. |
535 |
# |
536 |
def cvp_remez_all_poly_error_func_maxis(funct, |
537 |
maxDegree, |
538 |
lowerBound, |
539 |
upperBound, |
540 |
precsList, |
541 |
realField = None, |
542 |
contFracMaxErr = None): |
543 |
""" |
544 |
For a given function f, a degree and an interval, compute the |
545 |
maxima of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) |
546 |
function over the interval. |
547 |
""" |
548 |
## If no realField argument is given try to retrieve it from the |
549 |
# bounds. If impossible (e.g. rational bound), fall back on RR. |
550 |
if realField is None: |
551 |
try: |
552 |
### Force failure if parent does not have prec() member. |
553 |
lowerBound.parent().prec() |
554 |
### If no exception is thrown, set realField. |
555 |
realField = lowerBound.parent() |
556 |
except: |
557 |
realField = RR |
558 |
#print "Real field:", realField |
559 |
funcSa = func |
560 |
funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100) |
561 |
print "Function as a string:", funcAsStringSa |
562 |
## Create the Sollya version of the function and compute the |
563 |
# Remez approximant |
564 |
funcSo = pobyso_parse_string(funcAsStringSa) |
565 |
pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, |
566 |
maxDegree, |
567 |
lowerBound, |
568 |
upperBound) |
569 |
## Compute the error function and its derivative |
570 |
### First create the error function with copies since they are needed later. |
571 |
errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), |
572 |
sollya_lib_copy_obj(funcSo)) |
573 |
intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound) |
574 |
diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo) |
575 |
pobyso_clear_obj(errorFuncSo) |
576 |
## Compute the zeros of the derivative. |
577 |
errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo) |
578 |
pobyso_clear_obj(diffErrorFuncSo) |
579 |
errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo) |
580 |
pobyso_clear_obj(errorFuncZerosSo) |
581 |
## Compute the truncated Remez polynomial and the error function. |
582 |
truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList) |
583 |
pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo) |
584 |
pobyso_clear_obj(pStarSo) |
585 |
### Compute the error function. This time we consume both the function |
586 |
# and the polynomial. |
587 |
errorFuncSo = sollya_lib_build_function_sub(pTruncSo, funcSo) |
588 |
## Compute the derivative. |
589 |
diffErrorFuncSo = pobyso_diff_so_so(errorFuncSo) |
590 |
pobyso_clear_obj(errorFuncSo) |
591 |
## Compute the zeros of the derivative. |
592 |
errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(diffErrorFuncSo, intervalSo) |
593 |
pobyso_clear_obj(diffErrorFuncSo) |
594 |
pobyso_clear_obj(intervalSo) |
595 |
errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo) |
596 |
pobyso_clear_obj(errorFuncZerosSo) |
597 |
errorFuncZerosSa += errorFuncTruncZerosSa |
598 |
errorFuncZerosSa.sort() |
599 |
## If required, convert the numbers to rational numbers. |
600 |
if not contFracMaxErr is None: |
601 |
for index in xrange(0, len(errorFuncZerosSa)): |
602 |
errorFuncZerosSa[index] = \ |
603 |
errorFuncZerosSa[index].nearby_rational(contFracMaxErr) |
604 |
return errorFuncZerosSa |
605 |
# End cvp_remez_all_poly_error_func_maxis. |
606 |
# |
607 |
def cvp_remez_all_poly_error_func_zeros(funct, |
608 |
maxDegree, |
609 |
lowerBound, |
610 |
upperBound, |
611 |
precsList, |
612 |
realField = None, |
613 |
contFracMaxErr = None): |
614 |
""" |
615 |
For a given function f, a degree and an interval, compute the |
616 |
zeros of the (f-remez_d(f)) function and those of the (f-truncRemez_d(f)) |
617 |
function over the interval. |
618 |
""" |
619 |
## If no realField argument is given try to retrieve it from the |
620 |
# bounds. If impossible (e.g. rational bound), fall back on RR. |
621 |
if realField is None: |
622 |
try: |
623 |
### Force failure if parent does not have prec() member. |
624 |
lowerBound.parent().prec() |
625 |
### If no exception is thrown, set realField. |
626 |
realField = lowerBound.parent() |
627 |
except: |
628 |
realField = RR |
629 |
#print "Real field:", realField |
630 |
funcSa = func |
631 |
funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100) |
632 |
print "Function as a string:", funcAsStringSa |
633 |
## Create the Sollya version of the function and compute the |
634 |
# Remez approximant |
635 |
funcSo = pobyso_parse_string(funcAsStringSa) |
636 |
pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, |
637 |
maxDegree, |
638 |
lowerBound, |
639 |
upperBound) |
640 |
## Compute the zeroes of the error function. |
641 |
### First create the error function with copies since they are needed later. |
642 |
errorFuncSo = sollya_lib_build_function_sub(sollya_lib_copy_obj(pStarSo), |
643 |
sollya_lib_copy_obj(funcSo)) |
644 |
intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound) |
645 |
errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo) |
646 |
pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well. |
647 |
#print "Zeroes of the error function from Sollya:" |
648 |
#pobyso_autoprint(errorFuncZerosSo) |
649 |
errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo) |
650 |
pobyso_clear_obj(errorFuncZerosSo) |
651 |
#print "\nZeroes of the error function from Sage:" |
652 |
#print errorFuncZerosSa |
653 |
# |
654 |
## Deal with the truncated polynomial now. |
655 |
### Create the formats list. Notice the "*" before the list variable name. |
656 |
truncFormatListSo = pobyso_build_list_of_ints_sa_so(*precsList) |
657 |
#print "Precisions list as Sollya object:", |
658 |
#pobyso_autoprint(truncFormatListSo) |
659 |
pTruncSo = pobyso_round_coefficients_so_so(pStarSo, truncFormatListSo) |
660 |
#print "Truncated polynomial:", ; pobyso_autoprint(pTruncSo) |
661 |
### Compute the error function. This time we consume both the function |
662 |
# and the polynomial. |
663 |
errorFuncSo = sollya_lib_build_function_sub(pTruncSo, |
664 |
funcSo) |
665 |
errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo) |
666 |
pobyso_clear_obj(pStarSo) |
667 |
#print "Zeroes of the error function for the truncated polynomial from Sollya:" |
668 |
#pobyso_autoprint(errorFuncZerosSo) |
669 |
errorFuncTruncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo) |
670 |
pobyso_clear_obj(errorFuncZerosSo) |
671 |
## Sollya objects clean up. |
672 |
pobyso_clear_obj(intervalSo) |
673 |
errorFuncZerosSa += errorFuncTruncZerosSa |
674 |
errorFuncZerosSa.sort() |
675 |
## If required, convert the numbers to rational numbers. |
676 |
if not contFracMaxErr is None: |
677 |
for index in xrange(0, len(errorFuncZerosSa)): |
678 |
errorFuncZerosSa[index] = \ |
679 |
errorFuncZerosSa[index].nearby_rational(contFracMaxErr) |
680 |
return errorFuncZerosSa |
681 |
# |
682 |
# End remez_all_poly_error_func_zeros |
683 |
# |
684 |
def cvp_remez_poly_error_func_zeros(funct, |
685 |
maxDegree, |
686 |
lowerBound, |
687 |
upperBound, |
688 |
realField = None, |
689 |
contFracMaxErr = None): |
690 |
""" |
691 |
For a given function f, a degree and an interval, compute the |
692 |
zeros of the (f-remez_d(f)) function. |
693 |
""" |
694 |
## If no realField argument is given try to retrieve it from the |
695 |
# bounds. If impossible (e.g. rational bound), fall back on RR. |
696 |
if realField is None: |
697 |
try: |
698 |
### Force failure if parent does not have prec() member. |
699 |
lowerBound.parent().prec() |
700 |
### If no exception is thrown, set realField. |
701 |
realField = lowerBound.parent() |
702 |
except: |
703 |
realField = RR |
704 |
#print "Real field:", realField |
705 |
funcSa = func |
706 |
funcAsStringSa = func._assume_str().replace("_SAGE_VAR_","",100) |
707 |
#print "Function as a string:", funcAsStringSa |
708 |
## Create the Sollya version of the function and compute the |
709 |
# Remez approximant |
710 |
funcSo = pobyso_parse_string(funcAsStringSa) |
711 |
pStarSo = pobyso_remez_canonical_sa_so(funcAsStringSa, |
712 |
maxDegree, |
713 |
lowerBound, |
714 |
upperBound) |
715 |
## Compute the zeroes of the error function. |
716 |
### Error function creation consumes both pStarSo and funcSo. |
717 |
errorFuncSo = sollya_lib_build_function_sub(pStarSo, funcSo) |
718 |
intervalSo = pobyso_range_from_bounds_sa_so(lowerBound, upperBound) |
719 |
errorFuncZerosSo = pobyso_dirty_find_zeros_so_so(errorFuncSo, intervalSo) |
720 |
pobyso_clear_obj(errorFuncSo) # Clears funcSo and pStarSo as well. |
721 |
#print "Zeroes of the error function from Sollya:" |
722 |
#pobyso_autoprint(errorFuncZerosSo) |
723 |
errorFuncZerosSa = pobyso_float_list_so_sa(errorFuncZerosSo) |
724 |
pobyso_clear_obj(errorFuncZerosSo) |
725 |
## Sollya objects clean up. |
726 |
pobyso_clear_obj(intervalSo) |
727 |
## Converting to rational numbers, if required. |
728 |
if not contFracMaxErr is None: |
729 |
for index in xrange(0, len(errorFuncZerosSa)): |
730 |
errorFuncZerosSa[index] = \ |
731 |
errorFuncZerosSa[index].nearby_rational(contFracMaxErr) |
732 |
return errorFuncZerosSa |
733 |
# |
734 |
# End remez_poly_error_func_zeros |
735 |
# |
736 |
def cvp_round_to_bits(num, numBits): |
737 |
""" |
738 |
Round "num" to "numBits" bits. |
739 |
@param num : the number to round; |
740 |
@param numBits: the number of bits to round to. |
741 |
""" |
742 |
if num == 0: |
743 |
return num |
744 |
log2ofNum = 0 |
745 |
numRounded = 0 |
746 |
## Avoid conversion to floating-point if not necessary. |
747 |
try: |
748 |
log2OfNum = num.abs().log2() |
749 |
except: |
750 |
log2OfNum = RR(num).abs().log2() |
751 |
## Works equally well for num >= 1 and num < 1. |
752 |
log2OfNum = ceil(log2OfNum) |
753 |
# print "cvp_round_to_bits - Log2OfNum:", log2OfNum |
754 |
divMult = log2OfNum - numBits |
755 |
#print "cvp_round_to_bits - DivMult:", divMult |
756 |
if divMult != 0: |
757 |
numRounded = round(num/2^divMult) * 2^divMult |
758 |
else: |
759 |
numRounded = num |
760 |
return numRounded |
761 |
# End cvp_round_to_bits |
762 |
# |
763 |
def cvp_vector_in_basis(vect, basis): |
764 |
""" |
765 |
Compute the coordinates of "vect" in "basis" by |
766 |
solving a linear system. |
767 |
@param vect the vector we want to compute the coordinates of |
768 |
in coordinates of the ambient space; |
769 |
@param basis the basis we want to compute the coordinates in |
770 |
as a matrix relative to the ambient space. |
771 |
""" |
772 |
## Create the variables for the linear equations. |
773 |
varDeclString = "" |
774 |
basisIterationsRange = xrange(0, basis.nrows()) |
775 |
### Building variables declaration string. |
776 |
for index in basisIterationsRange: |
777 |
varName = "a" + str(index) |
778 |
if varDeclString == "": |
779 |
varDeclString += varName |
780 |
else: |
781 |
varDeclString += "," + varName |
782 |
### Variables declaration |
783 |
varsList = var(varDeclString) |
784 |
sage_eval("var('" + varDeclString + "')") |
785 |
## Create the equations |
786 |
equationString = "" |
787 |
equationsList = [] |
788 |
for vIndex in xrange(0, len(vect)): |
789 |
equationString = "" |
790 |
for bIndex in basisIterationsRange: |
791 |
if equationString != "": |
792 |
equationString += "+" |
793 |
equationString += str(varsList[bIndex]) + "*" + str(basis[bIndex,vIndex]) |
794 |
equationString += " == " + str(vect[vIndex]) |
795 |
equationsList.append(sage_eval(equationString)) |
796 |
## Solve the equations system. The solution dictionary is needed |
797 |
# to recover the values of the solutions. |
798 |
solutions = solve(equationsList,varsList, solution_dict=True) |
799 |
## This code is deactivated: did not solve the problem. |
800 |
""" |
801 |
if len(solutions) == 0: |
802 |
print "Trying Maxima to_poly_sove." |
803 |
solutions = solve(equationsList,varsList, solution_dict=True, to_poly_solve = 'force') |
804 |
""" |
805 |
#print "Solutions:", s |
806 |
## Recover solutions in rational components vector. |
807 |
vectInBasis = vector(QQ, basis.nrows()) |
808 |
### There can be several solution, in the general case. |
809 |
# Here, there is only one (or none). For each solution, each |
810 |
# variable has to be searched for. |
811 |
for sol in solutions: |
812 |
for bIndex in basisIterationsRange: |
813 |
vectInBasis[bIndex] = sol[varsList[bIndex]] |
814 |
return vectInBasis |
815 |
# End cpv_vector_in_basis. |
816 |
# |
817 |
print "... functions for CVP loaded." |