1 /* Copyright (C) 2008-2011 Xavier Pujol. 2 3 This file is part of fplll. fplll is free software: you 4 can redistribute it and/or modify it under the terms of the GNU Lesser 5 General Public License as published by the Free Software Foundation, 6 either version 2.1 of the License, or (at your option) any later version. 7 8 fplll is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License 14 along with fplll. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #ifndef FPLLL_EVALUATOR_H 17 #define FPLLL_EVALUATOR_H 18 19 #include <cassert> 20 #include <fplll/gso_interface.h> 21 #include <fplll/util.h> 22 #include <functional> 23 #include <map> 24 #include <queue> 25 26 FPLLL_BEGIN_NAMESPACE 27 28 enum EvaluatorMode 29 { 30 EVALMODE_SV = 0, 31 EVALMODE_CV = 0, 32 EVALMODE_COUNT = 1, 33 EVALMODE_PRINT = 2 34 }; 35 36 enum EvaluatorStrategy 37 { 38 /* 39 * Strategies for updating the enumeration bound as solutions are found. 40 * Possible values are: 41 * 42 * EVALSTRATEGY_BEST_N_SOLUTIONS 43 * Starting with the max_sols-th solution, every time a new solution is found 44 * the enumeration bound is updated to the length of the longest solution. If 45 * more than max_sols were found, the longest is dropped. 46 * EVALSTRATEGY_OPPORTUNISTIC_N_SOLUTIONS 47 * Every time a solution is found, update the enumeration distance to the length 48 * of the solution. If more than max_sols were found, the longest is dropped. 49 * EVALSTRATEGY_FIRST_N_SOLUTIONS 50 * The enumeration bound is not updated. As soon as max_sols are found, enumeration 51 * stops. 52 */ 53 EVALSTRATEGY_BEST_N_SOLUTIONS = 0, 54 EVALSTRATEGY_OPPORTUNISTIC_N_SOLUTIONS = 1, 55 EVALSTRATEGY_FIRST_N_SOLUTIONS = 2 56 }; 57 58 /** 59 * Evaluator stores the solutions found by enumerate, and updates the enumeration bound 60 * It thus provides an interface to the enumerator, 61 * as well as a basic interface to return solutions. 62 * Specializations will implement specific behaviour and additional interfaces. 63 */ 64 65 template <class FT> class Evaluator 66 { 67 public: 68 Evaluator(size_t nr_solutions = 1, 69 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 70 bool find_subsolutions = false) max_sols(nr_solutions)71 : max_sols(nr_solutions), strategy(update_strategy), findsubsols(find_subsolutions), 72 sol_count(0) 73 { 74 FPLLL_CHECK(nr_solutions > 0, "Evaluator: nr_solutions must be strictly positive!"); 75 FPLLL_CHECK(strategy <= 2, "Evaluator: invalid strategy"); 76 } ~Evaluator()77 virtual ~Evaluator() {} 78 79 /** configuration */ 80 size_t max_sols; 81 EvaluatorStrategy strategy; 82 bool findsubsols; 83 84 /** Solutions found in the lattice */ 85 // multimap storing solutions mapped by their length 86 // the longest solution is the first, the shortest solution last 87 typedef std::multimap<FT, std::vector<FT>, std::greater<FT>> container_t; 88 container_t solutions; 89 size_t sol_count; 90 91 /** Subsolutions found in the lattice */ 92 std::vector<std::pair<FT, std::vector<FT>>> sub_solutions; 93 94 /** interface to resulting solutions */ begin()95 typename container_t::const_reverse_iterator begin() const { return solutions.rbegin(); } end()96 typename container_t::const_reverse_iterator end() const { return solutions.rend(); } begin()97 typename container_t::reverse_iterator begin() { return solutions.rbegin(); } end()98 typename container_t::reverse_iterator end() { return solutions.rend(); } size()99 size_t size() const { return solutions.size(); } empty()100 bool empty() const { return solutions.empty(); } 101 102 /** interface for the enumerator */ 103 virtual void eval_sol(const vector<FT> &new_sol_coord, const enumf &new_partial_dist, 104 enumf &max_dist) = 0; 105 106 virtual void eval_sub_sol(int offset, const vector<FT> &new_sub_sol_coord, 107 const enumf &sub_dist) = 0; 108 set_normexp(long norm_exp)109 virtual void set_normexp(long norm_exp) { normExp = norm_exp; } 110 long normExp; 111 112 protected: 113 /** calculate enumeration bound based on dist */ calc_enum_bound(const FT & dist)114 virtual enumf calc_enum_bound(const FT &dist) const 115 { 116 FT tmp; 117 tmp.mul_2si(dist, -normExp); 118 return tmp.get_d(GMP_RNDU); 119 } 120 121 /** processes solution into multimap and adjusts max_dist according to strategy */ process_sol(const FT & dist,const vector<FT> & coord,enumf & max_dist)122 void process_sol(const FT &dist, const vector<FT> &coord, enumf &max_dist) 123 { 124 ++sol_count; 125 solutions.emplace(dist, coord); 126 switch (strategy) 127 { 128 case EVALSTRATEGY_BEST_N_SOLUTIONS: 129 if (solutions.size() < max_sols) 130 return; 131 // remove the longest solution, and use the new longest dist to update max_dist 132 if (solutions.size() > max_sols) 133 solutions.erase(solutions.begin()); 134 max_dist = calc_enum_bound(solutions.begin()->first); 135 break; 136 137 case EVALSTRATEGY_OPPORTUNISTIC_N_SOLUTIONS: 138 // always use dist to update max_dist 139 max_dist = calc_enum_bound(dist); 140 if (solutions.size() <= max_sols) 141 return; 142 // remove longest solution 143 solutions.erase(solutions.begin()); 144 break; 145 146 case EVALSTRATEGY_FIRST_N_SOLUTIONS: 147 if (solutions.size() < max_sols) 148 return; 149 // when desired nr of solutions has been reached, set enum bound to zero 150 max_dist = 0; 151 break; 152 153 default: 154 FPLLL_CHECK(false, "Evaluator: invalid strategy switch!"); 155 } 156 } 157 }; 158 159 /** 160 * Simple solution evaluator which provides a result without error bound. 161 * The same instance can be used for several calls to enumerate on different 162 * problems. 163 */ 164 template <class FT> class FastEvaluator : public Evaluator<FT> 165 { 166 public: 167 using Evaluator<FT>::max_sols; 168 using Evaluator<FT>::strategy; 169 using Evaluator<FT>::findsubsols; 170 using Evaluator<FT>::normExp; 171 using Evaluator<FT>::sub_solutions; 172 173 FastEvaluator(size_t nr_solutions = 1, 174 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 175 bool find_subsolutions = false) 176 : Evaluator<FT>(nr_solutions, update_strategy, find_subsolutions) 177 { 178 } ~FastEvaluator()179 virtual ~FastEvaluator() {} 180 eval_sol(const vector<FT> & new_sol_coord,const enumf & new_partial_dist,enumf & max_dist)181 virtual void eval_sol(const vector<FT> &new_sol_coord, const enumf &new_partial_dist, 182 enumf &max_dist) 183 { 184 FT dist = new_partial_dist; 185 dist.mul_2si(dist, normExp); 186 187 // store solution and update max_dist according to strategy 188 this->process_sol(dist, new_sol_coord, max_dist); 189 } 190 eval_sub_sol(int offset,const vector<FT> & new_sub_sol_coord,const enumf & sub_dist)191 virtual void eval_sub_sol(int offset, const vector<FT> &new_sub_sol_coord, const enumf &sub_dist) 192 { 193 FT dist = sub_dist; 194 dist.mul_2si(dist, normExp); 195 196 sub_solutions.resize(std::max(sub_solutions.size(), std::size_t(offset + 1))); 197 198 if (sub_solutions[offset].second.empty() || dist < sub_solutions[offset].first) 199 { 200 sub_solutions[offset].first = dist; 201 sub_solutions[offset].second = new_sub_sol_coord; 202 for (int i = 0; i < offset; ++i) 203 sub_solutions[offset].second[i] = 0.0; 204 } 205 } 206 }; 207 208 /** 209 @brief Callback function used by CallbackEvaluator. 210 211 */ 212 213 typedef bool(callback_evaluator_callback)(size_t n, enumf *new_sol_coord, void *ctx); 214 215 /** 216 @brief A FastEvaluator which additionally checks whether the predicate ``callbackf(solution, 217 ctx)`` accepts or rejects. 218 219 @example tests/test_enum.cpp 220 221 */ 222 template <class FT> class CallbackEvaluator : public FastEvaluator<FT> 223 { 224 225 std::function<callback_evaluator_callback> callbackf; 226 void *ctx; 227 enumf new_sol_coordf[FPLLL_MAX_ENUM_DIM]; 228 229 public: 230 using FastEvaluator<FT>::max_sols; 231 using FastEvaluator<FT>::strategy; 232 using FastEvaluator<FT>::findsubsols; 233 using FastEvaluator<FT>::normExp; 234 using FastEvaluator<FT>::sub_solutions; 235 236 CallbackEvaluator(std::function<callback_evaluator_callback> callbackf, void *ctx = NULL, 237 size_t nr_solutions = 1, 238 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 239 bool find_subsolutions = false 240 241 ) 242 : FastEvaluator<FT>(nr_solutions, update_strategy, find_subsolutions), callbackf(callbackf), 243 ctx(ctx) 244 { 245 } ~CallbackEvaluator()246 virtual ~CallbackEvaluator() {} 247 eval_sol(const vector<FT> & new_sol_coord,const enumf & new_partial_dist,enumf & max_dist)248 virtual void eval_sol(const vector<FT> &new_sol_coord, const enumf &new_partial_dist, 249 enumf &max_dist) 250 { 251 assert(new_sol_coord.size() <= FPLLL_MAX_ENUM_DIM); 252 for (size_t i = 0; i < new_sol_coord.size(); i++) 253 { 254 new_sol_coordf[i] = new_sol_coord[i].get_d(); 255 } 256 if (!callbackf(new_sol_coord.size(), new_sol_coordf, this->ctx)) 257 return; 258 259 FastEvaluator<FT>::eval_sol(new_sol_coord, new_partial_dist, max_dist); 260 } 261 }; 262 263 /** 264 * ErrorBoundEvaluator provides an extra interface to provide 265 * information about the accuracy of solutions. 266 */ 267 class ErrorBoundedEvaluator : public Evaluator<FP_NR<mpfr_t>> 268 { 269 public: 270 ErrorBoundedEvaluator(int dim, const Matrix<FP_NR<mpfr_t>> &mmu, const Matrix<FP_NR<mpfr_t>> &mr, 271 EvaluatorMode evalmode, size_t nr_solutions = 1, 272 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 273 bool find_subsolutions = false) Evaluator(nr_solutions,update_strategy,find_subsolutions)274 : Evaluator(nr_solutions, update_strategy, find_subsolutions), eval_mode(evalmode), d(dim), 275 mu(mmu), r(mr), input_error_defined(false) 276 { 277 max_dr_diag.resize(d); 278 max_dm_u.resize(d); 279 } 280 ~ErrorBoundedEvaluator()281 virtual ~ErrorBoundedEvaluator() {} 282 283 /** Configuration */ 284 EvaluatorMode eval_mode; 285 int d; 286 const Matrix<FP_NR<mpfr_t>> μ 287 const Matrix<FP_NR<mpfr_t>> &r; 288 289 /* To enable error estimation, the caller must set 290 input_error_defined=true and fill max_dr_diag and max_dm_u */ 291 bool input_error_defined; 292 vector<FP_NR<mpfr_t>> max_dr_diag, max_dm_u; // Error bounds on input parameters 293 // FP_NR<mpfr_t> last_partial_dist; // Approx. squared norm of the last solution 294 295 void init_delta_def(int prec, double rho, bool withRoundingToEnumf); 296 297 /** 298 * Computes max_error such that 299 * normOfSolution^2 <= (1 + max_error) * lambda_1(L)^2. 300 * The default implementation might fail (i.e. return false). 301 */ 302 virtual bool get_max_error(FP_NR<mpfr_t> &max_error, const FP_NR<mpfr_t> &sol_dist) = 0; 303 304 // Internal use 305 bool get_max_error_aux(const FP_NR<mpfr_t> &max_dist, bool boundOnExactVal, FP_NR<mpfr_t> &maxDE); 306 }; 307 308 /** 309 * Simple solution evaluator which provides a non-certified result, but can 310 * give an error bound. 311 * The same object can be used for several calls to enumerate on different 312 * instances. 313 */ 314 class FastErrorBoundedEvaluator : public ErrorBoundedEvaluator 315 { 316 public: 317 FastErrorBoundedEvaluator(int d = 0, const Matrix<FP_NR<mpfr_t>> &mu = Matrix<FP_NR<mpfr_t>>(), 318 const Matrix<FP_NR<mpfr_t>> &r = Matrix<FP_NR<mpfr_t>>(), 319 EvaluatorMode eval_mode = EVALMODE_SV, size_t nr_solutions = 1, 320 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 321 bool find_subsolutions = false) ErrorBoundedEvaluator(d,mu,r,eval_mode,nr_solutions,update_strategy,find_subsolutions)322 : ErrorBoundedEvaluator(d, mu, r, eval_mode, nr_solutions, update_strategy, find_subsolutions) 323 { 324 } ~FastErrorBoundedEvaluator()325 virtual ~FastErrorBoundedEvaluator() {} 326 327 virtual bool get_max_error(FP_NR<mpfr_t> &max_error, const FP_NR<mpfr_t> &sol_dist); 328 virtual void eval_sol(const vector<FP_NR<mpfr_t>> &new_sol_coord, const enumf &new_partial_dist, 329 enumf &max_dist); 330 virtual void eval_sub_sol(int offset, const vector<FP_NR<mpfr_t>> &new_sub_sol_coord, 331 const enumf &sub_dist); 332 }; 333 334 /** 335 * ExactEvaluator stores the best solution found by enumerate. 336 * The result is guaranteed, but the the evaluation of new solutions is longer. 337 */ 338 class ExactErrorBoundedEvaluator : public ErrorBoundedEvaluator 339 { 340 public: 341 ExactErrorBoundedEvaluator(int d, MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &_gso, 342 EvaluatorMode eval_mode, size_t nr_solutions = 1, 343 EvaluatorStrategy update_strategy = EVALSTRATEGY_BEST_N_SOLUTIONS, 344 bool find_subsolutions = false) 345 : ErrorBoundedEvaluator(d, _gso.get_mu_matrix(), _gso.get_r_matrix(), eval_mode, nr_solutions, 346 update_strategy, find_subsolutions), 347 gso(_gso) 348 { 349 int_max_dist = -1; 350 } 351 ~ExactErrorBoundedEvaluator()352 virtual ~ExactErrorBoundedEvaluator() {} 353 354 /** 355 * Sets max_error to 0: the result is guaranteed. 356 */ 357 virtual bool get_max_error(FP_NR<mpfr_t> &max_error, const FP_NR<mpfr_t> &sol_dist); 358 359 virtual void eval_sol(const vector<FP_NR<mpfr_t>> &new_sol_coord, const enumf &new_partial_dist, 360 enumf &max_dist); 361 362 virtual void eval_sub_sol(int offset, const vector<FP_NR<mpfr_t>> &new_sub_sol_coord, 363 const enumf &sub_dist); 364 365 Z_NR<mpz_t> int_max_dist; // Exact norm of the last vector 366 367 Z_NR<mpz_t> exact_sol_dist(const vector<FP_NR<mpfr_t>> &sol_coord); 368 Z_NR<mpz_t> exact_subsol_dist(int offset, const vector<FP_NR<mpfr_t>> &sol_coord); 369 370 private: 371 FP_NR<mpfr_t> int_dist2Float(Z_NR<mpz_t> int_dist); 372 MatGSOInterface<Z_NR<mpz_t>, FP_NR<mpfr_t>> &gso; 373 }; 374 375 FPLLL_END_NAMESPACE 376 377 #endif 378