/* Copyright (C) 2008-2011 Xavier Pujol.
This file is part of fplll. fplll is free software: you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation,
either version 2.1 of the License, or (at your option) any later version.
fplll is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with fplll. If not, see . */
#include "evaluator.h"
FPLLL_BEGIN_NAMESPACE
void ErrorBoundedEvaluator::init_delta_def(int prec, double rho, bool withRoundingToEnumf)
{
/* Computes error bounds on GSO
For all 0 <= i < d and 0 <= j <= i we have:
|r~_i - r_i| / r_i <= maxRelDR = d * rho ^ (i + 1) * 2 ^ (2 - prec)
|mu~_(i,j) - mu_(i,j)| <= d * rho ^ (i + 1) * 2 ^ (4 - prec)
The following formula is used to bound absolute error on r_i:
|r~_i - r_i| <= r_i * maxRelDR <= (r~_i + |r~_i - r_i| * maxRelDR)
==> |r~_i - r_i| <= r~_i * maxRelDR / (1 - maxRelDR) */
FP_NR rhoPow, maxRelDR, ftmp1;
input_error_defined = true;
for (int j = 0; j < d; j++)
{
rhoPow = rho;
rhoPow.pow_si(rhoPow, j + 1, GMP_RNDU); // >= rho ^ (j + 1)
ftmp1 = d;
ftmp1.mul_2si(ftmp1, 2 - prec); // = d * 2 ^ (2 - prec)
maxRelDR.mul(ftmp1, rhoPow, GMP_RNDU); // >= |r~_j - r_j| / r_j
ftmp1 = 1.0;
ftmp1.sub(ftmp1, maxRelDR, GMP_RNDD); // <= 1 - maxRelDR
ftmp1.div(maxRelDR, ftmp1, GMP_RNDU); // >= maxRelDR/(1-maxRelDR)
ftmp1.mul(ftmp1, r(j, j)); // >= |r~_j - r_j|
max_dr_diag[j] = ftmp1;
ftmp1 = d;
ftmp1.mul_2si(ftmp1, 4 - prec); // = d * 2 ^ (4 - prec)
ftmp1.mul(ftmp1, rhoPow, GMP_RNDU); // >= |mu~_(?,j) - mu_(?,j)|
max_dm_u[j] = ftmp1;
}
if (withRoundingToEnumf)
{
FP_NR halfULP;
halfULP = numeric_limits::epsilon() * 0.5;
for (int j = 0; j < d; j++)
{
max_dr_diag[j].addmul(r(j, j), halfULP, GMP_RNDU); // >= |double(r~_j) - r_j|
max_dm_u[j].add(max_dm_u[j], halfULP, GMP_RNDU);
}
}
}
/* Main function for error evaluation
Returns maxDE such that in each loop of the enumeration, if
exact_squared_norm(vector) <= max_dist (when boundOnExactVal = true)
or
computed_squared_norm(vector) <= max_dist (when boundOnExactVal = false)
then
|exact_squared_norm(vector) - computed_squared_norm(vector)| <= maxDE
(computed_squared_norm(vector) is the variable 'new_dist' is enumerateLoop)
In the following comments:
- The symbols +~ and *~ represent rounded fp addition and multiplication
- a,b,c,... represent exact values
- a~,b~,c~,... are approx. values used or computed by the fp algorithm */
bool ErrorBoundedEvaluator::get_max_error_aux(const FP_NR &max_dist, bool boundOnExactVal,
FP_NR &maxDE)
{
FPLLL_CHECK(input_error_defined,
"Evaluator: error evaluation failed because the input error is undefined");
FP_NR ulp, halfULP, K, tmp1, tmp2;
FP_NR rdiagTilde, minRDiag, maxRDiag, muTilde, maxMu, maxMuTildeX;
FP_NR maxC, maxCTilde, maxY, maxYTilde, maxY2Tilde, maxRY2Tilde;
FP_NR maxDC, maxDY, maxDY2, maxDRY2;
vector> maxX(d);
ulp = numeric_limits::epsilon();
halfULP.mul_2si(ulp, -1);
K = 1.0;
K.add(K, halfULP, GMP_RNDU);
maxDE = 0.0;
for (int i = d - 1; i >= 0; i--)
{
maxC = 0.0;
maxCTilde = 0.0;
maxDC = 0.0;
long rdiagExp = r(i, i).exponent();
tmp1.mul_2si(r(i, i), -rdiagExp);
tmp1 = tmp1.get_d();
rdiagTilde.mul_2si(tmp1, rdiagExp); // = r~_i
/* Computes bounds on:
C = mu(d-1,i) * x_(d-1) + ... + mu(j,i) * x_j
C~ = mu~(d-1,i) *~ x_(d-1) +~ ... +~ mu~(j,i) *~ x_j
DC = |C - C~| */
for (int j = d - 1; j > i; j--)
{
maxMu.abs(mu(j, i));
maxMu.add(maxMu, max_dm_u[i], GMP_RNDU); // >= |mu(j,i)|
maxC.addmul(maxMu, maxX[j], GMP_RNDU);
// now maxC >= |mu(d-1,i)| * |x_(d-1)| + ... + |mu(j,i)| * |x_j|
muTilde = fabs(mu(j, i).get_d()); // = |mu~(j,i)|
maxMuTildeX.mul(muTilde, maxX[j], GMP_RNDU); // >= mu~(j,i) * x_j
maxDC.addmul(max_dm_u[i], maxX[j]); // err1: initial error on mu
maxDC.addmul(maxMuTildeX, halfULP, GMP_RNDU); // err2: rounding after *
maxMuTildeX.mul(maxMuTildeX, K, GMP_RNDU); // >= mu~(j,i) *~ x_j
maxCTilde.addmul(maxMuTildeX, K, GMP_RNDU);
// now maxCTilde >= |mu(d-1,i)| *~ |x_(d-1)| +~ ... + |mu(j,i)| *~ |x_j|
maxDC.addmul(maxCTilde, halfULP, GMP_RNDU); // err3: rounding after +
maxCTilde.mul(maxCTilde, K, GMP_RNDU);
// now maxCTilde >= |mu(d-1,i)| *~ |x_(d-1)| +~ ... +~ |mu(j,i)| *~ |x_j|
}
if (boundOnExactVal)
{
// We have dist <= max_dist
minRDiag.sub(r(i, i), max_dr_diag[i], GMP_RNDD); // <= r_i
if (minRDiag <= 0.0)
return false;
tmp1.div(max_dist, minRDiag, GMP_RNDU); // >= dist / r_i
maxY.sqrt(tmp1, GMP_RNDU); // >= |y_i|
/* DY = |y_i - y~_i|
<= DC + |x_i - C~| * halfULP
<= DC + (|x_i - C| + DC) * halfULP
= DC * K + y * halfULP */
maxDY.mul(maxY, halfULP, GMP_RNDU);
maxDY.addmul(maxDC, K, GMP_RNDU); // >= |y_i - y~_i|
maxYTilde.add(maxY, maxDY, GMP_RNDU); // >= |y~_i|
tmp1.add(maxY, maxC, GMP_RNDD); // >= |x_i|
maxX[i].floor(tmp1); // x_i is an integer
tmp1 = maxY;
}
else
{
// We have dist~ <= max_dist
/* dist~ >= (y~_i *~ y~_i) *~ r~_i
>= (y~_i *~ y~_i) * r~_i - dist~ * halfULP
==> y~_i *~ y~_i <= dist~ * K / r~_i */
tmp1.mul(max_dist, K, GMP_RNDU);
tmp1.div(tmp1, rdiagTilde, GMP_RNDU); // >= y~_i *~ y~_i
/* tmp1 >= y~_i *~ y~_i >= y~_i * y~_i - tmp1 * halfULP
==> y~_i <= sqrt(tmp1 * K) */
tmp1.mul(tmp1, K, GMP_RNDU);
maxYTilde.sqrt(tmp1, GMP_RNDU); // >= y~_i
maxDY.mul(maxYTilde, halfULP, GMP_RNDU); // err2: rounding after +
maxDY.add(maxDY, maxDC, GMP_RNDU); // err1: initial error on C
// now maxDY >= |y_i - y~_i|
/* maxYTilde >= |C~_i -~ x_i| >= |C~_i - x_i| - maxYTilde * halfULP
==> |x_i| <= C~_i + maxYTilde * K */
tmp1 = maxCTilde;
tmp1.addmul(maxYTilde, K, GMP_RNDD); // >= |x_i|
maxX[i].floor(tmp1); // x_i is an integer
tmp1 = maxYTilde;
}
maxDY2.mul(maxDY, tmp1);
maxDY2.mul_2si(maxDY2, 1);
maxDY2.addmul(maxDY, maxDY, GMP_RNDU);
/* now, we have maxDY2 <= |y~_i * y~_i - y_i * y_i|
Case 1: tmp1 = maxY
|y~_i * y~_i - y_i * y_i| <= (maxY + maxDY) ^ 2 - maxY ^ 2
Case 2: tmp1 = maxY~
|y~_i * y~_i - y_i * y_i| <= (maxY~ + maxDY) ^ 2 - maxY~ ^ 2 */
maxY2Tilde.mul(maxYTilde, maxYTilde, GMP_RNDU); // >= y~_i * y~_i
maxDY2.addmul(maxY2Tilde, halfULP, GMP_RNDU);
/* now, we have maxDY2 <= |y~_i *~ y~_i - y_i * y_i| */
maxY2Tilde.mul(maxY2Tilde, K, GMP_RNDU); // >= y~_i *~ y~_i
maxRDiag.add(r(i, i), max_dr_diag[i], GMP_RNDU); // >= r_i
maxRY2Tilde.mul(rdiagTilde, maxY2Tilde, GMP_RNDU); // >= y~_i *~ y~_i * r~_i
maxDRY2.mul(maxRDiag, maxDY2, GMP_RNDU);
maxDRY2.addmul(maxY2Tilde, max_dr_diag[i], GMP_RNDU);
maxDRY2.addmul(maxRY2Tilde, halfULP, GMP_RNDU);
/* now maxDRY2 >= |y~_i *~ y~_i - y_i * y_i| * r_i +
|y~_i *~ y~_i| * |r~_i - r_i| +
y~_i *~ y~_i * r~_i * halfULP
>= |y~_i *~ y~_i * r~_i - y_i * y_i * r_i| +
y~_i *~ y~_i * r~_i * halfULP */
/* Let u = dist_(i+1), u~ = dist~_(i+1), v = y_i * y_i * r_i and
v~ = y~_i *~ y~_i *~ r~_i. We have:
|dist_i - dist~_i| = |(u~ +~ v~) - (u + v)|
<= |u~ +~ v~) - (u~ + v~)| + |(u~ + v~) - (u + v)|
Case 1: dist <= max_dist
|dist_i - dist~_i| <= (u~ + v~) * halfULP + |(u~ + v~) - (u + v)|
<= (u + v) * halfULP + K * |(u~ + v~) - (u + v)|
<= max_dist * halfULP + K * |(u~ + v~) - (u + v)|
Case 2: dist~ <= max_dist
|dist_i - dist~_i| <= (u~ +~ v~) * halfULP + |(u~ + v~) - (u + v)|
<= (u~ +~ v~) * halfULP + K * |(u~ + v~) - (u + v)|
<= max_dist * halfULP + K * |(u~ + v~) - (u + v)| */
maxDE.add(maxDE, maxDRY2, GMP_RNDU);
maxDE.mul(maxDE, K, GMP_RNDU);
maxDE.addmul(max_dist, halfULP, GMP_RNDU);
}
return true;
}
void FastErrorBoundedEvaluator::eval_sol(const vector> &new_sol_coord,
const enumf &new_partial_dist, enumf &max_dist)
{
// Assumes that the solution is valid
if (eval_mode == EVALMODE_SV)
{
FP_NR dist(new_partial_dist);
dist.mul_2si(dist, normExp);
this->process_sol(dist, new_sol_coord, max_dist);
}
else if (eval_mode == EVALMODE_PRINT)
{
std::cout << new_sol_coord << "\n";
}
}
void FastErrorBoundedEvaluator::eval_sub_sol(int offset,
const vector> &new_sub_sol_coord,
const enumf &sub_dist)
{
FP_NR dist = sub_dist;
dist.mul_2si(dist, normExp);
sub_solutions.resize(std::max(sub_solutions.size(), std::size_t(offset + 1)));
if (sub_solutions[offset].second.empty() || dist < sub_solutions[offset].first)
{
sub_solutions[offset].first = dist;
sub_solutions[offset].second = new_sub_sol_coord;
for (int i = 0; i < offset; ++i)
sub_solutions[offset].second[i] = 0.0;
}
}
bool FastErrorBoundedEvaluator::get_max_error(FP_NR &max_error,
const FP_NR &last_partial_dist)
{
FP_NR maxE, maxDE, maxOptDE, minOptE, one;
if (solutions.empty() || !input_error_defined)
return false;
if (!get_max_error_aux(last_partial_dist, false, maxDE))
return false;
// Exact norm of an optimal solution <= Exact norm of the result <= maxE
maxE.add(last_partial_dist, maxDE, GMP_RNDU);
// Error on the norm of an optimal solution <= maxOptDE
if (!get_max_error_aux(maxE, true, maxOptDE))
return false;
// Approx. norm of an optimal solution >= minOptE
minOptE.sub(last_partial_dist, maxOptDE, GMP_RNDD);
one = 1.0;
max_error.div(maxE, minOptE, GMP_RNDU);
max_error.sub(max_error, one, GMP_RNDU);
return true;
}
bool ExactErrorBoundedEvaluator::get_max_error(FP_NR &max_error, const FP_NR &)
{
max_error = 0.0;
return true;
}
void ExactErrorBoundedEvaluator::eval_sol(const vector> &new_sol_coord, const enumf &,
enumf &max_dist)
{
int n = gso.get_cols_of_b();
Z_NR new_sol_dist;
vector> new_sol, coord;
gen_zero_vect(new_sol, n);
gen_zero_vect(coord, n);
new_sol_dist = 0;
// Computes the distance between x and zero
for (int i = 0; i < d; i++)
{
coord[i].set_f(new_sol_coord[i]);
}
gso.sqnorm_coordinates(new_sol_dist, coord);
if (int_max_dist < 0 || new_sol_dist <= int_max_dist)
{
if (eval_mode == EVALMODE_SV)
{
int_max_dist = new_sol_dist;
this->process_sol(int_dist2Float(int_max_dist), new_sol_coord, max_dist);
}
else if (eval_mode == EVALMODE_PRINT)
{
cout << new_sol_coord << "\n";
}
}
}
void ExactErrorBoundedEvaluator::eval_sub_sol(int offset,
const vector> &new_sub_sol_coord,
const enumf &)
{
Z_NR minusone;
minusone = -1;
int n = gso.get_cols_of_b();
Z_NR new_sol_dist;
vector> new_sol, coord;
gen_zero_vect(new_sol, n);
gen_zero_vect(coord, n);
new_sol_dist = 0;
// Computes the distance between x[[offset,d)] and zero
for (int i = offset; i < d; i++)
{
coord[i].set_f(new_sub_sol_coord[i]);
}
gso.sqnorm_coordinates(new_sol_dist, coord);
FP_NR subdist = int_dist2Float(new_sol_dist);
sub_solutions.resize(std::max(sub_solutions.size(), std::size_t(offset + 1)));
if (sub_solutions[offset].second.empty() || subdist <= sub_solutions[offset].first)
{
sub_solutions[offset].first = subdist;
sub_solutions[offset].second = new_sub_sol_coord;
for (int i = 0; i < offset; ++i)
sub_solutions[offset].second[i] = 0.0;
}
}
FP_NR ExactErrorBoundedEvaluator::int_dist2Float(Z_NR int_dist)
{
FP_NR fMaxDist, maxDE;
fMaxDist.set_z(int_dist, GMP_RNDU);
bool result = get_max_error_aux(fMaxDist, true, maxDE);
FPLLL_CHECK(result, "ExactEvaluator: error cannot be bounded");
FPLLL_CHECK(maxDE <= r(0, 0), "ExactEvaluator: max error is too large");
fMaxDist.add(fMaxDist, maxDE);
// fMaxDist.mul_2si(fMaxDist, -normExp);
return fMaxDist;
}
FPLLL_END_NAMESPACE