Lattice Land Core Library
Loading...
Searching...
No Matches
simplifier.hpp
Go to the documentation of this file.
1// Copyright 2023 Pierre Talbot
2
3#ifndef LALA_CORE_SIMPLIFIER_HPP
4#define LALA_CORE_SIMPLIFIER_HPP
5
6#include "logic/logic.hpp"
8#include "abstract_deps.hpp"
9#include "battery/dynamic_bitset.hpp"
10
11namespace lala {
12
13/** This abstract domain works at the level of logical formulas.
14 * It deduces the formula by performing a number of simplifications w.r.t. an underlying abstract domain including:
15 * 1. Removing assigned variables.
16 * 2. Removing unused variables.
17 * 3. Removing entailed formulas.
18 * 4. Removing variable equality by tracking equivalence classes.
19 *
20 * The simplified formula can be obtained by calling `deinterpret()`.
21 * Given a solution to the simplified formula, the extended model (with the variables deleted) can be obtained by calling `representative()` to obtain the representative variable of each equivalence class.
22 */
23template<class A, class Allocator>
25public:
26 using allocator_type = Allocator;
27 using sub_type = A;
28 using sub_allocator_type = typename sub_type::allocator_type;
29 using universe_type = typename sub_type::universe_type;
30 using memory_type = typename universe_type::memory_type;
32
33 constexpr static const bool is_abstract_universe = false;
34 constexpr static const bool sequential = universe_type::sequential;
35 constexpr static const bool is_totally_ordered = false;
36 // Note that I did not define the concretization function formally yet... This is not yet an fully fledged abstract domain, we need to work more on it!
37 constexpr static const bool preserve_bot = true;
38 constexpr static const bool preserve_top = true;
39 constexpr static const bool preserve_join = true;
40 constexpr static const bool preserve_meet = true;
41 constexpr static const bool injective_concretization = true;
42 constexpr static const bool preserve_concrete_covers = true;
43 constexpr static const char* name = "Simplifier";
44
45 template<class A2, class Alloc2>
46 friend class Simplifier;
47
48 using formula_sequence = battery::vector<TFormula<allocator_type>, allocator_type>;
49
50private:
51 AType atype;
53 // We keep a copy of the variable environment in which the formula has been initially interpreted.
54 // This is necessary to project the variables and ask constraints in the subdomain during deduction.
56 // Read-only conjunctive formula, where each is treated independently.
57 formula_sequence formulas;
58 // Write-only (accessed in only 1 thread because this is not a parallel lattice entity) conjunctive formula, the main operation is a map between formulas and simplified_formulas.
59 formula_sequence simplified_formulas;
60 // eliminated_variables[i] is `true` when the variable `i` can be removed because it is assigned to a constant.
61 battery::dynamic_bitset<memory_type, allocator_type> eliminated_variables;
62 // eliminated_formulas[i] is `true` when the formula `i` is entailed.
63 battery::dynamic_bitset<memory_type, allocator_type> eliminated_formulas;
64 // `equivalence_classes[i]` contains the index of the representative variable in the equivalence class of the variable `i`.
65 battery::vector<ZUB<int, memory_type>, allocator_type> equivalence_classes;
66 // `constants[i]` contains the universe value of the representative variables `i`, aggregated by join on the values of all variables in the equivalence class.
67 battery::vector<universe_type, allocator_type> constants;
68
69public:
70 CUDA Simplifier(AType atype
72 , const allocator_type& alloc = allocator_type())
73 : atype(atype), sub(sub), env(alloc)
74 , formulas(alloc), simplified_formulas(alloc)
75 , eliminated_variables(alloc), eliminated_formulas(alloc)
76 , equivalence_classes(alloc), constants(alloc)
77 {}
78
79 CUDA Simplifier(this_type&& other)
80 : atype(other.atype), sub(std::move(other.sub)), env(other.env)
81 , formulas(std::move(other.formulas)), simplified_formulas(std::move(other.simplified_formulas))
82 , eliminated_variables(std::move(other.eliminated_variables)), eliminated_formulas(std::move(other.eliminated_formulas))
83 , equivalence_classes(std::move(other.equivalence_classes)), constants(std::move(other.constants))
84 {}
85
86 struct light_copy_tag {};
87
88 // This return a light copy of `other`, basically just keeping the equivalence classes and the environment to be able to print solutions and call `representative`.
89 template<class A2, class Alloc2>
91 : atype(other.atype)
92 , sub(sub)
93 , env(other.env, alloc)
94 , equivalence_classes(other.equivalence_classes, alloc)
95 , constants(other.constants, alloc)
96 {}
97
99 return formulas.get_allocator();
100 }
101
102 CUDA AType aty() const {
103 return atype;
104 }
105
106 /** @parallel @order-preserving @increasing */
107 CUDA local::B is_bot() const {
108 return sub->is_bot();
109 }
110
111 /** Returns the number of variables currently represented by this abstract element. */
112 CUDA size_t vars() const {
113 return equivalence_classes.size();
114 }
115
116 template <class Alloc>
117 struct tell_type {
121 tell_type(const Alloc& alloc = Alloc())
122 : num_vars(0), formulas(alloc), env(nullptr)
123 {}
124 };
125
126public:
127 template <bool diagnose = false, class F, class Env, class Alloc2>
128 CUDA NI bool interpret_tell(const F& f, Env& env, tell_type<Alloc2>& tell, IDiagnostics& diagnostics) const {
129 if(f.is(F::E)) {
130 AVar avar;
131 if(env.interpret(f.map_atype(aty()), avar, diagnostics)) {
132 tell.num_vars++;
133 tell.env = &env;
134 return true;
135 }
136 return false;
137 }
138 else {
139 tell.formulas.push_back(f);
140 tell.env = &env;
141 return true;
142 }
143 }
144
145 template <IKind kind, bool diagnose = false, class F, class Env, class Alloc2>
146 CUDA bool interpret(const F& f, Env& env, tell_type<Alloc2>& tell, IDiagnostics& diagnostics) const {
147 return interpret_tell<diagnose>(f, env, tell, diagnostics);
148 }
149
150 /** @sequential */
151 template <class Alloc2>
152 CUDA bool deduce(tell_type<Alloc2>&& t) {
153 if(t.env != nullptr) { // could be nullptr if the interpreted formula is true.
154 env = *(t.env);
155 eliminated_variables.resize(t.num_vars);
156 eliminated_formulas.resize(t.formulas.size());
157 constants.resize(t.num_vars);
158 equivalence_classes.resize(t.num_vars);
159 for(int i = 0; i < equivalence_classes.size(); ++i) {
160 equivalence_classes[i].meet(local::ZUB(i));
161 }
162 formulas = std::move(t.formulas);
163 simplified_formulas.resize(formulas.size());
164 return true;
165 }
166 return false;
167 }
168
169private:
170 // Return the abstract variable of the subdomain from the abstract variable `x` of this domain.
171 // In the environment, all variables should have been interpreted by the sub-domain, and we assume avars[0] contains the sub abstract variable.
172 CUDA AVar to_sub_var(AVar x) const {
173 assert(env[x].avars.size() > 0);
174 return env[x].avars[0];
175 }
176
177 CUDA AVar to_sub_var(size_t vid) const {
178 return to_sub_var(AVar{aty(), vid});
179 }
180
181 // `f` must be a formula from `formulas`.
182 CUDA AVar var_of(const TFormula<allocator_type>& f) const {
183 using F = TFormula<allocator_type>;
184 if(f.is(F::LV)) {
185 assert(env.variable_of(f.lv()).has_value());
186 assert(env.variable_of(f.lv())->get().avar_of(aty()).has_value());
187 return env.variable_of(f.lv())->get().avar_of(aty()).value();
188 }
189 else {
190 assert(f.is(F::V));
191 assert(env[f.v()].avar_of(aty()).has_value());
192 return env[f.v()].avar_of(aty()).value();
193 }
194 }
195
196public:
197 /** Print the abstract universe of `vname` taking into account simplifications (representative variable and constant).
198 */
199 template <class Alloc, class Abs, class Env>
200 CUDA void print_variable(const LVar<Alloc>& vname, const Env& benv, const Abs& b) const {
201 const auto& local_var = env.variable_of(vname)->get();
202 int rep = equivalence_classes[local_var.avar_of(aty())->vid()];
203 const auto& rep_name = env.name_of(AVar{aty(), rep});
204 auto benv_variable = benv.variable_of(rep_name);
205 if(benv_variable.has_value()) {
206 benv_variable->get().sort.print_value(b.project(benv_variable->get().avars[0]));
207 }
208 else {
209 local_var.sort.print_value(constants[rep]);
210 }
211 }
212
213private:
214 /** \return `true` if mask[i] was changed. */
215 CUDA local::B eliminate(battery::dynamic_bitset<memory_type, allocator_type>& mask, size_t i) {
216 if(!mask.test(i)) {
217 mask.set(i, true);
218 return true;
219 }
220 return false;
221 }
222
223 // We eliminate the representative of the variable `i` if it is a singleton.
224 CUDA local::B vdeduce(size_t i) {
225 const auto& u = sub->project(to_sub_var(i));
226 size_t j = equivalence_classes[i];
227 local::B has_changed = constants[j].meet(u);
228 if(!constants[j].is_bot() && constants[j].lb() == dual<typename universe_type::LB>(constants[j].ub())) {
229 has_changed |= eliminate(eliminated_variables, j);
230 }
231 return has_changed;
232 }
233
234 CUDA local::B cons_deduce(size_t i) {
235 using F = TFormula<allocator_type>;
236 // Eliminate constraint of the form x = y, and add x,y in the same equivalence class.
237 if(is_var_equality(formulas[i])) {
238 AVar x = var_of(formulas[i].seq(0));
239 AVar y = var_of(formulas[i].seq(1));
240 local::B has_changed = equivalence_classes[x.vid()].meet(local::ZUB(equivalence_classes[y.vid()]));
241 has_changed |= equivalence_classes[y.vid()].meet(local::ZUB(equivalence_classes[x.vid()]));
242 has_changed |= eliminate(eliminated_formulas, i);
243 return has_changed;
244 }
245 else {
246 // Eliminate entailed formulas.
247 IDiagnostics diagnostics;
248 typename sub_type::template ask_type<allocator_type> ask;
249#ifdef _MSC_VER // Avoid MSVC compiler bug. See https://stackoverflow.com/questions/77144003/use-of-template-keyword-before-dependent-template-name
250 if(sub->interpret_ask(formulas[i], env, ask, diagnostics)) {
251#else
252 if(sub->template interpret_ask(formulas[i], env, ask, diagnostics)) {
253#endif
254 if(sub->ask(ask)) {
255 return eliminate(eliminated_formulas, i);
256 }
257 }
258 // Replace assigned variables by constants.
259 // Note that since everything is in a fixed point loop, both the constant and the equivalence class might be updated later on.
260 // This is one of the reasons we cannot update `formulas` in-place: we would not be able to update the constant a second time (since the variable would be eliminated).
261 auto f = formulas[i].map([&](const F& f, const F& parent) {
262 if(f.is_variable()) {
263 AVar x = var_of(f);
264 if(eliminated_variables.test(x.vid())) {
265 auto k = constants[x.vid()].template deinterpret<F>();
266 if(env[x].sort.is_bool() && k.is(F::Z) && parent.is_logical()) {
267 return k.z() == 0 ? F::make_false() : F::make_true();
268 }
269 return std::move(k);
270 }
271 else if(equivalence_classes[x.vid()] != x.vid()) {
272 return F::make_lvar(UNTYPED, env.name_of(AVar{aty(), equivalence_classes[x.vid()]}));
273 }
274 return f.map_atype(UNTYPED);
275 }
276 return f;
277 });
278 f = eval(f);
279 if(f.is_true()) {
280 return eliminate(eliminated_formulas, i);
281 }
282 if(f != simplified_formulas[i]) {
283 simplified_formulas[i] = f;
284 return true;
285 }
286 return false;
287 }
288 }
289
290public:
291 /** We have one deduction operator per variable and one per constraint in the interpreted formula. */
292 CUDA size_t num_deductions() const {
293 return constants.size() + formulas.size();
294 }
295
296 CUDA local::B deduce(size_t i) {
297 assert(i < num_deductions());
298 if(i < constants.size()) {
299 return vdeduce(i);
300 }
301 else {
302 return cons_deduce(i - constants.size());
303 }
304 }
305
306 CUDA size_t num_eliminated_variables() const {
307 size_t keep = 0;
308 for(int i = 0; i < equivalence_classes.size(); ++i) {
309 if(equivalence_classes[i] == i && !eliminated_variables.test(i)) {
310 ++keep;
311 }
312 }
313 return equivalence_classes.size() - keep;
314 }
315
316 CUDA size_t num_eliminated_formulas() const {
317 return eliminated_formulas.count();
318 }
319
321 using F = TFormula<allocator_type>;
322 typename F::Sequence seq(get_allocator());
323
324 if(is_bot()) {
325 return F::make_false();
326 }
327
328 // A representative variable is eliminated if all variables in its equivalence class must be eliminated.
329 for(int i = 0; i < equivalence_classes.size(); ++i) {
330 eliminated_variables.set(equivalence_classes[i], eliminated_variables.test(equivalence_classes[i]) && eliminated_variables.test(i));
331 }
332
333 // Deinterpret the existential quantifiers (only one per equivalence classes), and the domain of each variable.
334 for(int i = 0; i < equivalence_classes.size(); ++i) {
335 if(equivalence_classes[i] == i && !eliminated_variables.test(i)) {
336 const auto& x = env[AVar{aty(), i}];
337 seq.push_back(F::make_exists(UNTYPED, x.name, x.sort));
338 auto domain_constraint = constants[i].deinterpret(AVar(aty(), i), env, get_allocator());
339 map_avar_to_lvar(domain_constraint, env, true);
340 seq.push_back(domain_constraint);
341 }
342 }
343
344 // Deinterpret the simplified formulas.
345 for(int i = 0; i < simplified_formulas.size(); ++i) {
346 if(!eliminated_formulas.test(i)) {
347 seq.push_back(simplified_formulas[i]);
348 }
349 }
350 if(seq.size() == 0) {
351 return F::make_true();
352 }
353 else {
354 return F::make_nary(AND, std::move(seq));
355 }
356 }
357};
358
359} // namespace lala
360
361#endif
Definition ast.hpp:38
Definition arith_bound.hpp:118
Definition b.hpp:10
Definition diagnostics.hpp:19
Definition simplifier.hpp:24
CUDA allocator_type get_allocator() const
Definition simplifier.hpp:98
static constexpr const bool preserve_bot
Definition simplifier.hpp:37
CUDA local::B deduce(size_t i)
Definition simplifier.hpp:296
CUDA Simplifier(this_type &&other)
Definition simplifier.hpp:79
CUDA bool deduce(tell_type< Alloc2 > &&t)
Definition simplifier.hpp:152
Allocator allocator_type
Definition simplifier.hpp:26
A sub_type
Definition simplifier.hpp:27
CUDA size_t vars() const
Definition simplifier.hpp:112
static constexpr const bool preserve_join
Definition simplifier.hpp:39
CUDA local::B is_bot() const
Definition simplifier.hpp:107
static constexpr const bool injective_concretization
Definition simplifier.hpp:41
CUDA NI bool interpret_tell(const F &f, Env &env, tell_type< Alloc2 > &tell, IDiagnostics &diagnostics) const
Definition simplifier.hpp:128
CUDA size_t num_eliminated_variables() const
Definition simplifier.hpp:306
battery::vector< TFormula< allocator_type >, allocator_type > formula_sequence
Definition simplifier.hpp:48
CUDA bool interpret(const F &f, Env &env, tell_type< Alloc2 > &tell, IDiagnostics &diagnostics) const
Definition simplifier.hpp:146
CUDA AType aty() const
Definition simplifier.hpp:102
static constexpr const bool preserve_top
Definition simplifier.hpp:38
static constexpr const char * name
Definition simplifier.hpp:43
static constexpr const bool is_abstract_universe
Definition simplifier.hpp:33
static constexpr const bool is_totally_ordered
Definition simplifier.hpp:35
CUDA size_t num_deductions() const
Definition simplifier.hpp:292
typename sub_type::allocator_type sub_allocator_type
Definition simplifier.hpp:28
typename universe_type::memory_type memory_type
Definition simplifier.hpp:30
CUDA NI TFormula< allocator_type > deinterpret()
Definition simplifier.hpp:320
CUDA Simplifier(AType atype, abstract_ptr< sub_type > sub, const allocator_type &alloc=allocator_type())
Definition simplifier.hpp:70
CUDA size_t num_eliminated_formulas() const
Definition simplifier.hpp:316
CUDA void print_variable(const LVar< Alloc > &vname, const Env &benv, const Abs &b) const
Definition simplifier.hpp:200
CUDA Simplifier(const Simplifier< A2, Alloc2 > &other, light_copy_tag tag, abstract_ptr< sub_type > sub, const allocator_type &alloc=allocator_type())
Definition simplifier.hpp:90
static constexpr const bool sequential
Definition simplifier.hpp:34
typename sub_type::universe_type universe_type
Definition simplifier.hpp:29
static constexpr const bool preserve_concrete_covers
Definition simplifier.hpp:42
static constexpr const bool preserve_meet
Definition simplifier.hpp:40
Definition ast.hpp:234
Definition env.hpp:264
CUDA NI std::optional< std::reference_wrapper< const variable_type > > variable_of(const char *lv) const
Definition env.hpp:457
CUDA const bstring & name_of(AVar av) const
Definition env.hpp:496
::lala::ZUB< int, battery::local_memory > ZUB
Definition arith_bound.hpp:69
::lala::B<::battery::local_memory > B
Definition b.hpp:12
Definition abstract_deps.hpp:14
CUDA NI void map_avar_to_lvar(F &f, const Env &env, bool erase_type=false)
Definition algorithm.hpp:398
CUDA constexpr LDual dual(const L &x)
Definition arith_bound.hpp:110
int AType
Definition sort.hpp:18
@ AND
Unary arithmetic function symbols.
Definition ast.hpp:114
CUDA NI bool is_var_equality(const TFormula< Allocator, ExtendedSig > &f)
Definition algorithm.hpp:32
battery::string< Allocator > LVar
Definition ast.hpp:25
battery::shared_ptr< A, typename A::allocator_type > abstract_ptr
Definition abstract_deps.hpp:17
CUDA NI F eval(const F &f)
Definition algorithm.hpp:774
#define UNTYPED
Definition sort.hpp:21
Definition simplifier.hpp:86
Definition simplifier.hpp:117
tell_type(const Alloc &alloc=Alloc())
Definition simplifier.hpp:121
int num_vars
Definition simplifier.hpp:118
VarEnv< Alloc > * env
Definition simplifier.hpp:120
formula_sequence formulas
Definition simplifier.hpp:119