Lattice Land Powerdomains Library
bab.hpp
Go to the documentation of this file.
1 // Copyright 2022 Pierre Talbot
2 
3 #ifndef LALA_POWER_BAB_HPP
4 #define LALA_POWER_BAB_HPP
5 
6 #include "battery/vector.hpp"
7 #include "battery/shared_ptr.hpp"
8 #include "lala/logic/logic.hpp"
9 #include "lala/universes/arith_bound.hpp"
10 #include "lala/abstract_deps.hpp"
11 #include "lala/vstore.hpp"
12 
13 namespace lala {
14 template <class A, class B> class BAB;
15 namespace impl {
16  template <class>
17  struct is_bab_like {
18  static constexpr bool value = false;
19  };
20  template<class A, class B>
21  struct is_bab_like<BAB<A, B>> {
22  static constexpr bool value = true;
23  };
24 }
25 
26 template <class A, class B = A>
27 class BAB {
28 public:
29  using allocator_type = typename A::allocator_type;
30  using sub_type = A;
31  using sub_ptr = abstract_ptr<sub_type>;
32  using best_type = B;
33  using best_ptr = abstract_ptr<best_type>;
35 
36  constexpr static const bool is_abstract_universe = false;
37  constexpr static const bool sequential = sub_type::sequential;
38  constexpr static const bool is_totally_ordered = false;
39  constexpr static const bool preserve_bot = true;
40  constexpr static const bool preserve_top = true;
41  // The next properties should be checked more seriously, relying on the sub-domain might be uneccessarily restrictive.
42  constexpr static const bool preserve_join = sub_type::preserve_join;
43  constexpr static const bool preserve_meet = sub_type::preserve_meet;
44  constexpr static const bool injective_concretization = sub_type::injective_concretization;
45  constexpr static const bool preserve_concrete_covers = sub_type::preserve_concrete_covers;
46  constexpr static const char* name = "BAB";
47 
48  template <class Alloc>
49  struct tell_type {
50  using sub_tell_type = sub_type::template tell_type<Alloc>;
51  AVar x;
54  tell_type(const Alloc& alloc = Alloc{}): sub_tell(alloc) {}
57  tell_type(const tell_type<Alloc>&) = default;
58  CUDA NI tell_type(AVar x, bool opt, const Alloc& alloc = Alloc{}):
59  x(x), optimization_mode(opt), sub_tell(alloc) {}
60 
61  template <class BABTellType>
62  CUDA NI tell_type(const BABTellType& other, const Alloc& alloc = Alloc{}):
63  x(other.x), optimization_mode(other.optimization_mode),
64  sub_tell(other.sub_tell, alloc)
65  {}
66 
67  using allocator_type = Alloc;
69  return sub_tell.get_allocator();
70  }
71 
72  template <class Alloc2>
73  friend struct tell_type;
74  };
75 
76  template <class Alloc2>
77  using ask_type = typename sub_type::template ask_type<Alloc2>;
78 
79  template <class A2, class B2>
80  friend class BAB;
81 
82 private:
83  AType atype;
84  sub_ptr sub;
85  best_ptr best;
86  AVar x;
87  bool optimization_mode; // `true` for minimization, `false` for maximization.
88  int solutions_found;
89 
90 public:
91  CUDA BAB(AType atype, sub_ptr sub, best_ptr best)
92  : atype(atype), sub(std::move(sub)), best(std::move(best)), x(),
93  solutions_found(0)
94  {
95  assert(this->sub);
96  assert(this->best);
97  }
98 
99  /** Construct BAB by copying `other`.
100  * The best solution is copied using a fresh AbstractDeps, and thus is not intended to be shared with other abstract domains.
101  * For instance, if `best` is a VStore, it shares the same AType than the VStore underlying `sub`.
102  * Hence, if we copy it using `deps`, both VStore will be shared which is not the intended behavior.
103  */
104  template<class A2, class B2, class... Allocators>
105  CUDA NI BAB(const BAB<A2, B2>& other, AbstractDeps<Allocators...>& deps)
106  : atype(other.atype)
107  , sub(deps.template clone<sub_type>(other.sub))
108  , x(other.x)
109  , optimization_mode(other.optimization_mode)
110  {
111  AbstractDeps<Allocators...> deps_best(false, deps.template get_allocator<Allocators>()...);
112  best = deps_best.template clone<best_type>(other.best);
113  }
114 
115  CUDA AType aty() const {
116  return atype;
117  }
118 
120  return sub->get_allocator();
121  }
122 
123  CUDA local::B is_bot() const {
124  return sub->is_bot();
125  }
126 
127  CUDA local::B is_top() const {
128  return x.is_untyped() && sub->is_top();
129  }
130 
131 public:
132  template <bool diagnose = false, class F, class Env, class Alloc2>
133  CUDA NI bool interpret_tell(const F& f, Env& env, tell_type<Alloc2>& tell, IDiagnostics& diagnostics) const {
134  if(f.is_untyped() || f.type() == aty()) {
135  if(f.is(F::Seq) && (f.sig() == MAXIMIZE || f.sig() == MINIMIZE)) {
136  if(f.seq(0).is_variable()) {
137  if(env.interpret(f.seq(0), tell.x, diagnostics)) {
138  tell.optimization_mode = f.sig() == MINIMIZE;
139  return true;
140  }
141  else {
142  return false;
143  }
144  }
145  // If the objective variable is already fixed to a constant, we ignore this predicate.
146  // If there is only one objective, it becomes a satisfaction problem.
147  else if(num_vars(f.seq(0)) == 0) {
148  RETURN_INTERPRETATION_WARNING("This objective is already fixed to a constant, thus it is ignored.");
149  }
150  else {
151  RETURN_INTERPRETATION_ERROR("Optimization predicates expect a variable to optimize (not an expression). Instead, you can create a new variable with the expression to optimize.");
152  }
153  }
154  else if(f.type() == aty()) {
155  RETURN_INTERPRETATION_ERROR("This formula has the type of BAB but it is not supported in this abstract domain.");
156  }
157  }
158  return sub->template interpret_tell<diagnose>(f, env, tell.sub_tell, diagnostics);
159  }
160 
161  template <bool diagnose = false, class F, class Env, class Alloc2>
162  CUDA NI bool interpret_ask(const F& f, const Env& env, ask_type<Alloc2>& ask, IDiagnostics& diagnostics) const {
163  return sub->template interpret_ask<diagnose>(f, env, ask, diagnostics);
164  }
165 
166  template <IKind kind, bool diagnose = false, class F, class Env, class I>
167  CUDA NI bool interpret(const F& f, Env& env, I& intermediate, IDiagnostics& diagnostics) const {
168  if constexpr(kind == IKind::TELL) {
169  return interpret_tell<diagnose>(f, env, intermediate, diagnostics);
170  }
171  else {
172  return interpret_ask<diagnose>(f, env, intermediate, diagnostics);
173  }
174  }
175 
176  template <class Alloc>
177  CUDA bool deduce(const tell_type<Alloc>& t) {
178  bool has_changed = sub->deduce(t.sub_tell);
179  if(!t.x.is_untyped()) {
180  assert(x.is_untyped()); // multi-objective optimization not yet supported.
181  x = t.x;
182  optimization_mode = t.optimization_mode;
183  return true;
184  }
185  return has_changed;
186  }
187 
188  template <class Alloc2>
189  CUDA NI TFormula<Alloc2> deinterpret_best_bound(const typename best_type::universe_type& best_bound, const Alloc2& alloc = Alloc2{}) const {
190  using F = TFormula<Alloc2>;
191  if((is_minimization() && best_bound.lb().is_top())
192  ||(is_maximization() && best_bound.ub().is_top()))
193  {
194  return F::make_true();
195  }
196  Sig optimize_sig = is_minimization() ? LT : GT;
197  F constant = is_minimization()
198  ? best_bound.lb().template deinterpret<F>()
199  : best_bound.ub().template deinterpret<F>();
200  return F::make_binary(F::make_avar(x), optimize_sig, constant, UNTYPED, alloc);
201  }
202 
203  template <class Alloc2>
204  CUDA TFormula<Alloc2> deinterpret_best_bound(const Alloc2& alloc = Alloc2{}) const {
205  return deinterpret_best_bound(best->project(x), alloc);
206  }
207 
208  /** Update the variable to optimize `objective_var()` with a new bound. */
209  CUDA local::B deduce(const typename best_type::universe_type& best_bound) {
210  VarEnv<allocator_type> empty_env{};
211  using F = TFormula<allocator_type>;
212  F bound_formula = deinterpret_best_bound(best_bound, get_allocator());
213  IDiagnostics diagnostics;
214  typename sub_type::template tell_type<allocator_type> t;
215  bool res = sub->interpret_tell(bound_formula, empty_env, t, diagnostics);
216  assert(res);
217  return sub->deduce(t);
218  }
219 
220  /** Compare the best bound of two stores on the objective variable represented in this BAB abstract element.
221  * \pre `is_optimization()` must be `true`.
222  * \return `true` if `store1` is strictly better than `store2`, false otherwise.
223  */
224  template <class Store1, class Store2>
225  CUDA bool compare_bound(const Store1& store1, const Store2& store2) const {
226  assert(is_optimization());
227  using Univ1 = typename Store1::local_universe;
228  using Univ2 = typename Store2::local_universe;
229  Univ1 bound1 = store1.project(x);
230  Univ2 bound2 = store2.project(x);
231  // When minimizing, the best bound is getting smaller and smaller.
232  if(is_minimization()) {
233  return (!bound1.is_top() && bound2.is_top()) || bound1.lb() > bound2.lb(); // note that `>` reflects the order of LB.
234  }
235  // And dually for maximization.
236  else {
237  return (!bound1.is_top() && bound2.is_top()) || bound1.ub() > bound2.ub();
238  }
239  }
240 
241  /** This deduction operator performs "branch-and-bound" by adding a constraint to the root node of the search tree to ensure the next solution is better than the current one, and store the best solution found.
242  * \pre The current subelement must be extractable, and if it is an optimization problem, have a better bound than `best` (this is not checked here).
243  * Beware this deduction operator is not idempotent (it must only be called once on each new solution).
244  */
245  CUDA local::B deduce() {
246  sub->extract(*best);
247  solutions_found++;
248  if(is_optimization()) {
249  deduce(best->project(x));
250  }
251  return true;
252  }
253 
254  CUDA int solutions_count() const {
255  return solutions_found;
256  }
257 
258  /** Given an optimization problem, it is extractable only when we have explored the whole state space (indicated by the subdomain being equal to top), we have found one solution, and that solution is extractable. */
259  template <class ExtractionStrategy = NonAtomicExtraction>
260  CUDA bool is_extractable(const ExtractionStrategy& strategy = ExtractionStrategy()) const {
261  return solutions_found > 0 && sub->is_bot() && best->is_extractable(strategy);
262  }
263 
264  /** Extract the best solution found in `ua`.
265  * \pre `is_extractable()` must return `true`. */
266  template <class AbstractBest>
267  CUDA void extract(AbstractBest& ua) const {
268  if constexpr(impl::is_bab_like<AbstractBest>::value) {
269  best->extract(*(ua.best));
270  ua.solutions_found = solutions_found;
271  ua.x = x;
272  ua.optimization_mode = optimization_mode;
273  }
274  else {
275  return best->extract(ua);
276  }
277  }
278 
279  /** If `is_extractable()` is not `true`, the returned element might not be an optimum, and should be seen as the best optimum found so far.
280  */
281  CUDA const best_type& optimum() const {
282  return *best;
283  }
284 
285  CUDA best_ptr optimum_ptr() const {
286  return best;
287  }
288 
289  CUDA bool is_satisfaction() const {
290  return x.is_untyped();
291  }
292 
293  CUDA bool is_optimization() const {
294  return !is_satisfaction();
295  }
296 
297  CUDA bool is_minimization() const {
298  return is_optimization() && optimization_mode;
299  }
300 
301  CUDA bool is_maximization() const {
302  return is_optimization() && !optimization_mode;
303  }
304 
305  CUDA AVar objective_var() const {
306  return x;
307  }
308 };
309 
310 }
311 
312 #endif
Definition: bab.hpp:27
CUDA NI bool interpret_tell(const F &f, Env &env, tell_type< Alloc2 > &tell, IDiagnostics &diagnostics) const
Definition: bab.hpp:133
CUDA int solutions_count() const
Definition: bab.hpp:254
CUDA bool is_minimization() const
Definition: bab.hpp:297
CUDA bool is_optimization() const
Definition: bab.hpp:293
typename sub_type::template ask_type< Alloc2 > ask_type
Definition: bab.hpp:77
abstract_ptr< sub_type > sub_ptr
Definition: bab.hpp:31
CUDA allocator_type get_allocator() const
Definition: bab.hpp:119
CUDA NI TFormula< Alloc2 > deinterpret_best_bound(const typename best_type::universe_type &best_bound, const Alloc2 &alloc=Alloc2{}) const
Definition: bab.hpp:189
CUDA bool deduce(const tell_type< Alloc > &t)
Definition: bab.hpp:177
CUDA bool is_maximization() const
Definition: bab.hpp:301
CUDA const best_type & optimum() const
Definition: bab.hpp:281
CUDA local::B is_bot() const
Definition: bab.hpp:123
constexpr static const bool preserve_top
Definition: bab.hpp:40
A sub_type
Definition: bab.hpp:30
B best_type
Definition: bab.hpp:32
constexpr static const bool preserve_join
Definition: bab.hpp:42
CUDA AVar objective_var() const
Definition: bab.hpp:305
typename A::allocator_type allocator_type
Definition: bab.hpp:29
constexpr static const bool sequential
Definition: bab.hpp:37
abstract_ptr< best_type > best_ptr
Definition: bab.hpp:33
CUDA local::B deduce()
Definition: bab.hpp:245
CUDA best_ptr optimum_ptr() const
Definition: bab.hpp:285
CUDA NI BAB(const BAB< A2, B2 > &other, AbstractDeps< Allocators... > &deps)
Definition: bab.hpp:105
CUDA void extract(AbstractBest &ua) const
Definition: bab.hpp:267
constexpr static const bool preserve_bot
Definition: bab.hpp:39
CUDA AType aty() const
Definition: bab.hpp:115
CUDA bool compare_bound(const Store1 &store1, const Store2 &store2) const
Definition: bab.hpp:225
CUDA BAB(AType atype, sub_ptr sub, best_ptr best)
Definition: bab.hpp:91
constexpr static const bool preserve_meet
Definition: bab.hpp:43
CUDA TFormula< Alloc2 > deinterpret_best_bound(const Alloc2 &alloc=Alloc2{}) const
Definition: bab.hpp:204
constexpr static const bool is_totally_ordered
Definition: bab.hpp:38
CUDA bool is_satisfaction() const
Definition: bab.hpp:289
CUDA local::B is_top() const
Definition: bab.hpp:127
CUDA local::B deduce(const typename best_type::universe_type &best_bound)
Definition: bab.hpp:209
CUDA NI bool interpret_ask(const F &f, const Env &env, ask_type< Alloc2 > &ask, IDiagnostics &diagnostics) const
Definition: bab.hpp:162
constexpr static const char * name
Definition: bab.hpp:46
constexpr static const bool is_abstract_universe
Definition: bab.hpp:36
CUDA NI bool interpret(const F &f, Env &env, I &intermediate, IDiagnostics &diagnostics) const
Definition: bab.hpp:167
CUDA bool is_extractable(const ExtractionStrategy &strategy=ExtractionStrategy()) const
Definition: bab.hpp:260
constexpr static const bool injective_concretization
Definition: bab.hpp:44
constexpr static const bool preserve_concrete_covers
Definition: bab.hpp:45
Definition: bab.hpp:13
Definition: bab.hpp:49
bool optimization_mode
Definition: bab.hpp:52
tell_type(const Alloc &alloc=Alloc{})
Definition: bab.hpp:54
tell_type(const tell_type< Alloc > &)=default
CUDA NI tell_type(AVar x, bool opt, const Alloc &alloc=Alloc{})
Definition: bab.hpp:58
sub_tell_type sub_tell
Definition: bab.hpp:53
Alloc allocator_type
Definition: bab.hpp:67
sub_type::template tell_type< Alloc > sub_tell_type
Definition: bab.hpp:50
CUDA allocator_type get_allocator() const
Definition: bab.hpp:68
tell_type & operator=(tell_type< Alloc > &&)=default
CUDA NI tell_type(const BABTellType &other, const Alloc &alloc=Alloc{})
Definition: bab.hpp:62
AVar x
Definition: bab.hpp:51
tell_type(tell_type< Alloc > &&)=default