Lattice Land Powerdomains Library
Loading...
Searching...
No Matches
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
13namespace lala {
14template <class A, class B> class BAB;
15namespace 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
26template <class A, class B = A>
27class BAB {
28public:
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{}):
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
82private:
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
90public:
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
131public:
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();
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 TFormula< Alloc2 > deinterpret_best_bound(const Alloc2 &alloc=Alloc2{}) const
Definition bab.hpp:204
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
static constexpr const bool preserve_meet
Definition bab.hpp:43
static constexpr const bool preserve_bot
Definition bab.hpp:39
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 bool deduce(const tell_type< Alloc > &t)
Definition bab.hpp:177
CUDA bool is_maximization() const
Definition bab.hpp:301
CUDA local::B is_bot() const
Definition bab.hpp:123
static constexpr const bool is_abstract_universe
Definition bab.hpp:36
A sub_type
Definition bab.hpp:30
B best_type
Definition bab.hpp:32
CUDA AVar objective_var() const
Definition bab.hpp:305
typename A::allocator_type allocator_type
Definition bab.hpp:29
static constexpr const bool is_totally_ordered
Definition bab.hpp:38
static constexpr const bool preserve_join
Definition bab.hpp:42
static constexpr const bool preserve_concrete_covers
Definition bab.hpp:45
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 const best_type & optimum() const
Definition bab.hpp:281
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
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
static constexpr const bool preserve_top
Definition bab.hpp:40
static constexpr const char * name
Definition bab.hpp:46
static constexpr const bool injective_concretization
Definition bab.hpp:44
static constexpr const bool sequential
Definition bab.hpp:37
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
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
CUDA NI TFormula< Alloc2 > deinterpret_best_bound(const typename best_type::universe_type &best_bound, const Alloc2 &alloc=Alloc2{}) const
Definition bab.hpp:189
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
CUDA NI tell_type(const BABTellType &other, const Alloc &alloc=Alloc{})
Definition bab.hpp:62
AVar x
Definition bab.hpp:51
tell_type & operator=(tell_type< Alloc > &&)=default
tell_type(tell_type< Alloc > &&)=default