QUBO++ Library with QUBO Solver APIs
Author: Koji Nakano, License: Non-commercial research and evaluation purposes without any guarantees.
qbpp_exhaustive_solver.hpp
Go to the documentation of this file.
1 
13 #ifndef QBPP_EXHAUSTIVE_SOLVER_HPP
14 #define QBPP_EXHAUSTIVE_SOLVER_HPP
15 
16 #include <tbb/blocked_range.h>
17 #include <tbb/parallel_for.h>
18 
19 #include <boost/circular_buffer.hpp>
20 #include <forward_list>
21 #include <random>
22 #include <set>
23 
24 #include "qbpp.hpp"
25 
26 namespace qbpp {
27 
28 namespace exhaustive_solver {
29 
30 
31 class SolDelta;
32 class Sol;
33 class SearchAlgorithm;
34 class ExhaustiveSolver;
35 
36 class Sol : public qbpp::Sol {
37  bool is_all_solutions_ = false;
38 
39  bool is_optimal_solutions_ = false;
40 
41  std::list<qbpp::Sol> all_solutions_;
42 
43  public:
44  explicit Sol(const QuadModel &quad_model) : qbpp::Sol(quad_model) {};
45 
46  Sol(const Sol &) = default;
47 
48  Sol(Sol &&) = default;
49 
50  Sol &operator=(const Sol &) = default;
51 
52  Sol &operator=(Sol &&) = default;
53 
54  std::list<qbpp::Sol> &get_all_solutions() { return all_solutions_; }
55 
56  qbpp::Sol get_sol() const { return all_solutions_.front(); }
57 
59  is_optimal_solutions_ = false;
60  is_all_solutions_ = true;
61  }
62 
64  is_optimal_solutions_ = true;
65  is_all_solutions_ = false;
66  }
67 
68  void set_all_solutions(std::list<qbpp::Sol> &&all_solutions) {
69  all_solutions_ = all_solutions;
71  }
72 
73  std::string str() const {
75  std::ostringstream oss;
76  uint32_t count = 0;
77  for (const auto &sol : all_solutions_) {
78  oss << count++ << ":" << sol;
79  if (count < all_solutions_.size()) oss << std::endl;
80  }
81  return oss.str();
82  }
83  return qbpp::str(static_cast<qbpp::Sol>(*this));
84  }
85 
86  std::list<qbpp::Sol>::const_iterator begin() const {
87  return all_solutions_.begin();
88  }
89 
90  std::list<qbpp::Sol>::const_iterator end() const {
91  return all_solutions_.end();
92  }
93 };
94 
95 inline std::ostream &operator<<(std::ostream &os, const Sol &sol) {
96  os << sol.str();
97  return os;
98 }
99 
101  protected:
103 
105 
106  public:
107 
108  ExhaustiveSolver(const QuadModel &quad_model) : quad_model_(quad_model) {}
109 
110  mutable std::mutex callback_mutex_;
111 
112  virtual ~ExhaustiveSolver() = default;
113 
114  virtual void callback(const SolHolder &sol_holder) const {
115  static std::optional<energy_t> prev_energy = std::nullopt;
116  std::lock_guard<std::mutex> lock(callback_mutex_);
118  if (!prev_energy.has_value() ||
119  sol_holder.get_energy() < prev_energy.value()) {
120  prev_energy = sol_holder.get_energy();
121  std::cout << "TTS = " << std::fixed << std::setprecision(3)
122  << std::setfill('0') << sol_holder.get_tts()
123  << "s Energy = " << sol_holder.get_energy() << std::endl;
124  }
125  }
126  }
127 
128  vindex_t var_count() const { return quad_model_.var_count(); }
129 
130  qbpp::Sol search();
131 
133 
135 
136  void enable_default_callback(bool enable = true) {
137  enable_default_callback_ = enable;
138  }
139 
140 
141  const QuadModel &get_quad_model() const { return quad_model_; }
142 };
143 
146 
148 
149  const std::vector<vindex_t> var_order_;
150 
152 
153  bool is_all_solutions_ = false;
154 
155  bool is_optimal_solutions_ = false;
156 
157  std::list<qbpp::Sol> all_solutions_;
158 
159  mutable std::mutex all_solutions_mutex_;
160 
161  std::vector<SolDelta> sol_deltas;
162 
163  std::vector<vindex_t> init_var_order(const QuadModel &quad_model) {
164  std::vector<std::pair<vindex_t, vindex_t>> degree_var;
165  degree_var.resize(quad_model.var_count());
166  for (vindex_t i = 0; i < quad_model.var_count(); ++i) {
167  degree_var[i] = std::make_pair(quad_model.get_degree(i), i);
168  }
169  std::sort(degree_var.begin(), degree_var.end(), std::greater<>());
170  std::vector<vindex_t> var_order;
171  var_order.resize(quad_model.var_count());
172  for (vindex_t i = 0; i < quad_model.var_count(); ++i) {
173  var_order[i] = degree_var[i].second;
174  }
175  return var_order;
176  }
177 
178  public:
179  explicit SearchAlgorithm(const ExhaustiveSolver &exhaustive_solver)
180  : exhaustive_solver_(exhaustive_solver),
184 
185  const QuadModel &get_quad_model() const { return quad_model_; }
186 
187  const std::vector<vindex_t> &get_var_order() const { return var_order_; }
188 
189  vindex_t var_count() const { return quad_model_.var_count(); }
190 
191  std::list<qbpp::Sol> &get_all_solutions() { return all_solutions_; }
192 
194 
195  void register_new_sol(const qbpp::Sol &sol) {
197  return;
198  std::lock_guard<std::mutex> lock(all_solutions_mutex_);
199  if (is_all_solutions_) {
200  all_solutions_.push_front(sol);
201  } else if (is_optimal_solutions_) {
202  if (sol_holder_.get_energy() == sol.get_energy()) {
203  all_solutions_.push_front(sol);
204  } else if (sol_holder_.get_energy() > sol.get_energy()) {
205  all_solutions_.clear();
206  all_solutions_.push_front(sol);
207  }
208  }
209  if (sol_holder_.set_if_better(sol)) {
211  }
212  }
213 
214  void search();
215 
217 
218  void search_all_solutions();
219 
220  void gen_sol_deltas(SolDelta &sol_delta, vindex_t index);
221 };
222 
223 class SolDelta : public qbpp::Sol {
225 
226  const std::vector<vindex_t> &var_order_ = search_algorithm_.get_var_order();
227 
228  std::vector<energy_t> delta_;
229 
230  public:
231  explicit SolDelta(SearchAlgorithm &search_algorithm)
232  : Sol(search_algorithm.get_quad_model()),
233  search_algorithm_(search_algorithm) {
235  delta_.resize(quad_model_.var_count());
236  for (vindex_t i = 0; i < quad_model_.var_count(); ++i) {
237  delta_[i] = quad_model_.get_linear(i);
238  }
239  }
240 
241  SolDelta(const SolDelta &) = default;
242 
243  vindex_t var_count() const { return quad_model_.var_count(); }
244 
245  void flip(vindex_t index) override {
246  vindex_t flip_index = var_order_[index];
247  if (flip_index >= var_count()) {
248  throw std::out_of_range(
249  THROW_MESSAGE("Sol: flip_index (", flip_index, ") out of range"));
250  }
251  energy_ = get_energy() + delta_[flip_index];
252  for (vindex_t j = 0; j < quad_model_.get_degree(flip_index); ++j) {
253  auto [k, coeff] = quad_model_.get_quadratic(flip_index, j);
254  delta_[k] += (2 * get(flip_index) - 1) * (2 * get(k) - 1) * coeff;
255  }
256  delta_[flip_index] = -delta_[flip_index];
257  if (!energy_.has_value()) {
258  energy_ = comp_energy();
259  }
260  bit_vector_.flip(flip_index);
261  }
262 
263  void search(vindex_t index) {
264  if (index >= 1) search(index - 1);
265  flip(index);
267  if (index >= 1) search(index - 1);
268  }
269 
270  void search() {
273  }
274 };
275 
276 
278  SearchAlgorithm search_algorithm(*this);
279  search_algorithm.search();
280  return search_algorithm.get_sol_holder().get_sol();
281 }
282 
284  SearchAlgorithm search_algorithm(*this);
285  search_algorithm.search_optimal_solutions();
286  Sol sol(quad_model_);
287  sol.optimal_solution_mode();
288  sol.set_all_solutions(std::move(search_algorithm.get_all_solutions()));
289  return sol;
290 }
291 
293  SearchAlgorithm search_algorithm(*this);
294  search_algorithm.search_all_solutions();
295  Sol sol(quad_model_);
296  sol.all_solution_mode();
297  sol.set_all_solutions(std::move(search_algorithm.get_all_solutions()));
298  return sol;
299 }
300 
301 
303  vindex_t index) {
304  if (var_count() == index) return;
305  gen_sol_deltas(sol_delta, index + 1);
306  sol_delta.flip(index);
307  sol_deltas.push_back(sol_delta);
308  gen_sol_deltas(sol_delta, index + 1);
309 }
310 
311 inline void SearchAlgorithm::search() {
312  const int parallel_param = 8;
313  SolDelta sol_delta(*this);
314  if (quad_model_.term_count() == 0) {
315  register_new_sol(sol_delta);
316  return;
317  }
318  if (var_count() <= 16) {
319  register_new_sol(sol_delta);
320  sol_delta.search(var_count() - 1);
321  return;
322  }
323  sol_deltas.push_back(sol_delta);
324  gen_sol_deltas(sol_delta, var_count() - parallel_param);
325 
326  tbb::parallel_for(
327  tbb::blocked_range<size_t>(0, sol_deltas.size()),
328  [&](const tbb::blocked_range<size_t> &range) {
329  for (size_t i = range.begin(); i < range.end(); ++i) {
330  register_new_sol(sol_deltas[i]);
331  sol_deltas[i].search(var_count() - (parallel_param + 1));
332  }
333  });
334 }
335 
336 inline void SearchAlgorithm::search_optimal_solutions() {
337  is_optimal_solutions_ = true;
338  search();
339  all_solutions_.sort();
340 }
341 
342 inline void SearchAlgorithm::search_all_solutions() {
343  is_all_solutions_ = true;
344  search();
345  all_solutions_.sort();
346 }
347 
348 }
349 }
350 
351 #endif
energy_t get_constant() const
Definition: qbpp.hpp:1168
vindex_t var_count() const
Definition: qbpp.hpp:1154
size_t term_count(vindex_t deg) const
Definition: qbpp.hpp:1258
const std::vector< std::vector< std::pair< vindex_t, coeff_t > > > & get_quadratic() const
Definition: qbpp.hpp:1245
const std::vector< coeff_t > & get_linear() const
Definition: qbpp.hpp:1241
const std::vector< vindex_t > & get_degree() const
Definition: qbpp.hpp:1243
energy_t get_energy() const
Definition: qbpp.hpp:1637
double get_tts() const
Definition: qbpp.hpp:1653
virtual std::optional< double > set_if_better(const qbpp::Sol &new_sol, const std::string &solver="")
Definition: qbpp.hpp:1600
const Sol & get_sol() const
Definition: qbpp.hpp:1639
const QuadModel quad_model_
Definition: qbpp.hpp:1444
energy_t get_energy() const
Definition: qbpp.hpp:1545
Sol & operator=(const Sol &sol)
Definition: qbpp.hpp:1469
std::optional< energy_t > energy_
Definition: qbpp.hpp:1448
energy_t comp_energy() const
Definition: qbpp.hpp:2809
impl::BitVector bit_vector_
Definition: qbpp.hpp:1446
var_val_t get(vindex_t index) const
Definition: qbpp.hpp:1500
virtual void callback(const SolHolder &sol_holder) const
const std::vector< vindex_t > & get_var_order() const
std::vector< vindex_t > init_var_order(const QuadModel &quad_model)
SearchAlgorithm(const ExhaustiveSolver &exhaustive_solver)
void gen_sol_deltas(SolDelta &sol_delta, vindex_t index)
SolDelta(const SolDelta &)=default
const std::vector< vindex_t > & var_order_
SolDelta(SearchAlgorithm &search_algorithm)
void flip(vindex_t index) override
std::list< qbpp::Sol >::const_iterator begin() const
std::list< qbpp::Sol > all_solutions_
std::list< qbpp::Sol > & get_all_solutions()
void set_all_solutions(std::list< qbpp::Sol > &&all_solutions)
Sol & operator=(Sol &&)=default
Sol & operator=(const Sol &)=default
Sol(const QuadModel &quad_model)
Sol(const Sol &)=default
std::list< qbpp::Sol >::const_iterator end() const
void flip(vindex_t index)
Definition: qbpp.hpp:1398
std::ostream & operator<<(std::ostream &os, const Sol &sol)
Generates a QUBO Expression for the Graph Coloring Problem using QUBO++ library.
Definition: qbpp.hpp:53
std::string str(Var var)
Definition: qbpp.hpp:1715
uint32_t vindex_t
Definition: qbpp.hpp:122
QUBO++, a C++ library for generating expressions for binary and spin variables.
#define THROW_MESSAGE(...)
Definition: qbpp.hpp:86