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>> &mu;
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