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 refines 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 refinement.
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<ZDec<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 CUDA local::BInc is_top() const {
107 return sub->is_top();
108 }
109
110 /** Returns the number of variables currently represented by this abstract element. */
111 CUDA size_t vars() const {
112 return equivalence_classes.size();
113 }
114
115 template <class Alloc>
116 struct tell_type {
120 tell_type(const Alloc& alloc = Alloc())
121 : num_vars(0), formulas(alloc), env(nullptr)
122 {}
123 };
124
125public:
126 template <bool diagnose = false, class F, class Env, class Alloc2>
127 CUDA NI bool interpret_tell(const F& f, Env& env, tell_type<Alloc2>& tell, IDiagnostics& diagnostics) const {
128 if(f.is(F::E)) {
129 AVar avar;
130 if(env.interpret(f.map_atype(aty()), avar, diagnostics)) {
131 tell.num_vars++;
132 tell.env = &env;
133 return true;
134 }
135 return false;
136 }
137 else {
138 tell.formulas.push_back(f);
139 tell.env = &env;
140 return true;
141 }
142 }
143
144 template <IKind kind, bool diagnose = false, class F, class Env, class Alloc2>
145 CUDA bool interpret(const F& f, Env& env, tell_type<Alloc2>& tell, IDiagnostics& diagnostics) const {
146 return interpret_tell<diagnose>(f, env, tell, diagnostics);
147 }
148
149 template <class Alloc2, class Mem>
150 CUDA this_type& tell(tell_type<Alloc2>&& t, BInc<Mem>& has_changed) {
151 assert(t.env != nullptr);
152 env = *(t.env);
153 eliminated_variables.resize(t.num_vars);
154 eliminated_formulas.resize(t.formulas.size());
155 constants.resize(t.num_vars);
156 equivalence_classes.resize(t.num_vars);
157 for(int i = 0; i < equivalence_classes.size(); ++i) {
158 equivalence_classes[i].tell(local::ZDec(i));
159 }
160 formulas = std::move(t.formulas);
161 simplified_formulas.resize(formulas.size());
162 has_changed.tell_top();
163 return *this;
164 }
165
166 template <class Alloc2>
168 local::BInc has_changed;
169 return tell(std::move(t), has_changed);
170 }
171
172private:
173 // Return the abstract variable of the subdomain from the abstract variable `x` of this domain.
174 // In the environment, all variables should have been interpreted by the sub-domain, and we assume avars[0] contains the sub abstract variable.
175 CUDA AVar to_sub_var(AVar x) const {
176 assert(env[x].avars.size() > 0);
177 return env[x].avars[0];
178 }
179
180 CUDA AVar to_sub_var(size_t vid) const {
181 return to_sub_var(AVar{aty(), vid});
182 }
183
184 // `f` must be a formula from `formulas`.
185 CUDA AVar var_of(const TFormula<allocator_type>& f) const {
186 using F = TFormula<allocator_type>;
187 if(f.is(F::LV)) {
188 assert(env.variable_of(f.lv()).has_value());
189 assert(env.variable_of(f.lv())->avar_of(aty()).has_value());
190 return env.variable_of(f.lv())->avar_of(aty()).value();
191 }
192 else {
193 assert(f.is(F::V));
194 assert(env[f.v()].avar_of(aty()).has_value());
195 return env[f.v()].avar_of(aty()).value();
196 }
197 }
198
199public:
200 /** Print the abstract universe of `vname` taking into account simplifications (representative variable and constant).
201 */
202 template <class Alloc, class B, class Env>
203 CUDA void print_variable(const LVar<Alloc>& vname, const Env& benv, const B& b) const {
204 const auto& local_var = *(env.variable_of(vname));
205 int rep = equivalence_classes[local_var.avar_of(aty())->vid()];
206 const auto& rep_name = env.name_of(AVar{aty(), rep});
207 const auto& benv_variable = benv.variable_of(rep_name);
208 if(benv_variable.has_value()) {
209 benv_variable->sort.print_value(b.project(benv_variable->avars[0]));
210 }
211 else {
212 local_var.sort.print_value(constants[rep]);
213 }
214 }
215
216private:
217 template <class Mem>
218 CUDA void eliminate(battery::dynamic_bitset<memory_type, allocator_type>& mask, size_t i, BInc<Mem>& has_changed) {
219 if(!mask.test(i)) {
220 mask.set(i, true);
221 has_changed.tell_top();
222 }
223 }
224
225 // We eliminate the representative of the variable `i` if it is a singleton.
226 template <class Mem>
227 CUDA void vrefine(size_t i, BInc<Mem>& has_changed) {
228 const auto& u = sub->project(to_sub_var(i));
229 size_t j = equivalence_classes[i];
230 constants[j].tell(u, has_changed);
231 if(constants[j].lb().value() == constants[j].ub().value()) {
232 eliminate(eliminated_variables, j, has_changed);
233 }
234 }
235
236 template <class Mem>
237 CUDA void crefine(size_t i, BInc<Mem>& has_changed) {
238 using F = TFormula<allocator_type>;
239 // Eliminate constraint of the form x = y, and add x,y in the same equivalence class.
240 if(is_var_equality(formulas[i])) {
241 AVar x = var_of(formulas[i].seq(0));
242 AVar y = var_of(formulas[i].seq(1));
243 equivalence_classes[x.vid()].tell(local::ZDec(equivalence_classes[y.vid()]), has_changed);
244 equivalence_classes[y.vid()].tell(local::ZDec(equivalence_classes[x.vid()]), has_changed);
245 eliminate(eliminated_formulas, i, has_changed);
246 }
247 else {
248 // Eliminate entailed formulas.
249 IDiagnostics diagnostics;
250 typename sub_type::template ask_type<allocator_type> ask;
251#ifdef _MSC_VER // Avoid MSVC compiler bug. See https://stackoverflow.com/questions/77144003/use-of-template-keyword-before-dependent-template-name
252 if(sub->interpret_ask(formulas[i], env, ask, diagnostics)) {
253#else
254 if(sub->template interpret_ask(formulas[i], env, ask, diagnostics)) {
255#endif
256 if(sub->ask(ask)) {
257 eliminate(eliminated_formulas, i, has_changed);
258 return;
259 }
260 }
261 // Replace assigned variables by constants.
262 // Note that since everything is in a fixed point loop, both the constant and the equivalence class might be updated later on.
263 // 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).
264 auto f = formulas[i].map([&](const F& f, const F& parent) {
265 if(f.is_variable()) {
266 AVar x = var_of(f);
267 if(eliminated_variables.test(x.vid())) {
268 auto k = constants[x.vid()].template deinterpret<F>();
269 if(env[x].sort.is_bool() && k.is(F::Z) && parent.is_logical()) {
270 return k.z() == 0 ? F::make_false() : F::make_true();
271 }
272 return std::move(k);
273 }
274 else if(equivalence_classes[x.vid()] != x.vid()) {
275 return F::make_lvar(UNTYPED, env.name_of(AVar{aty(), equivalence_classes[x.vid()]}));
276 }
277 return f.map_atype(UNTYPED);
278 }
279 return f;
280 });
281 f = eval(f);
282 if(f.is_true()) {
283 eliminate(eliminated_formulas, i, has_changed);
284 }
285 if(f != simplified_formulas[i]) {
286 simplified_formulas[i] = f;
287 has_changed.tell_top();
288 }
289 }
290 }
291
292public:
293 /** We have one refinement operator per variable and one per constraint in the interpreted formula. */
294 CUDA size_t num_refinements() const {
295 return constants.size() + formulas.size();
296 }
297
298 template <class Mem>
299 CUDA void refine(size_t i, BInc<Mem>& has_changed) {
300 assert(i < num_refinements());
301 if(i < constants.size()) {
302 vrefine(i, has_changed);
303 }
304 else {
305 crefine(i - constants.size(), has_changed);
306 }
307 }
308
309 CUDA size_t num_eliminated_variables() const {
310 size_t keep = 0;
311 for(int i = 0; i < equivalence_classes.size(); ++i) {
312 if(equivalence_classes[i] == i && !eliminated_variables.test(i)) {
313 ++keep;
314 }
315 }
316 return equivalence_classes.size() - keep;
317 }
318
319 CUDA size_t num_eliminated_formulas() const {
320 return eliminated_formulas.count();
321 }
322
324 using F = TFormula<allocator_type>;
325 typename F::Sequence seq(get_allocator());
326
327 // A representative variable is eliminated if all variables in its equivalence class must be eliminated.
328 for(int i = 0; i < equivalence_classes.size(); ++i) {
329 eliminated_variables.set(equivalence_classes[i], eliminated_variables.test(equivalence_classes[i]) && eliminated_variables.test(i));
330 }
331
332 // Deinterpret the existential quantifiers (only one per equivalence classes), and the domain of each variable.
333 for(int i = 0; i < equivalence_classes.size(); ++i) {
334 if(equivalence_classes[i] == i && !eliminated_variables.test(i)) {
335 const auto& x = env[AVar{aty(), i}];
336 seq.push_back(F::make_exists(UNTYPED, x.name, x.sort));
337 auto domain_constraint = constants[i].deinterpret(AVar(aty(), i), env);
338 map_avar_to_lvar(domain_constraint, env, true);
339 seq.push_back(domain_constraint);
340 }
341 }
342
343 // Deinterpret the simplified formulas.
344 for(int i = 0; i < simplified_formulas.size(); ++i) {
345 if(!eliminated_formulas.test(i)) {
346 seq.push_back(simplified_formulas[i]);
347 }
348 }
349 if(seq.size() == 0) {
350 return F::make_true();
351 }
352 else {
353 return F::make_nary(AND, std::move(seq));
354 }
355 }
356};
357
358} // namespace lala
359
360#endif
Definition ast.hpp:38
Definition diagnostics.hpp:19
Definition primitive_upset.hpp:118
CUDA constexpr local::BInc is_top() const
Definition primitive_upset.hpp:224
CUDA constexpr this_type & tell_top()
Definition primitive_upset.hpp:233
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 Simplifier(this_type &&other)
Definition simplifier.hpp:79
Allocator allocator_type
Definition simplifier.hpp:26
CUDA void refine(size_t i, BInc< Mem > &has_changed)
Definition simplifier.hpp:299
CUDA this_type & tell(tell_type< Alloc2 > &&t)
Definition simplifier.hpp:167
CUDA size_t num_refinements() const
Definition simplifier.hpp:294
A sub_type
Definition simplifier.hpp:27
CUDA size_t vars() const
Definition simplifier.hpp:111
CUDA this_type & tell(tell_type< Alloc2 > &&t, BInc< Mem > &has_changed)
Definition simplifier.hpp:150
static constexpr const bool preserve_join
Definition simplifier.hpp:39
static constexpr const bool injective_concretization
Definition simplifier.hpp:41
CUDA local::BInc is_top() const
Definition simplifier.hpp:106
CUDA NI bool interpret_tell(const F &f, Env &env, tell_type< Alloc2 > &tell, IDiagnostics &diagnostics) const
Definition simplifier.hpp:127
CUDA size_t num_eliminated_variables() const
Definition simplifier.hpp:309
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:145
CUDA AType aty() const
Definition simplifier.hpp:102
static constexpr const bool preserve_top
Definition simplifier.hpp:38
CUDA void print_variable(const LVar< Alloc > &vname, const Env &benv, const B &b) const
Definition simplifier.hpp:203
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
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:323
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:319
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:263
CUDA NI thrust::optional< const variable_type & > variable_of(const char *lv) const
Definition env.hpp:456
CUDA const bstring & name_of(AVar av) const
Definition env.hpp:495
::lala::ZDec< int, battery::local_memory > ZDec
Definition primitive_upset.hpp:82
Definition abstract_deps.hpp:14
CUDA NI void map_avar_to_lvar(F &f, const Env &env, bool erase_type=false)
Definition algorithm.hpp:418
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:794
#define UNTYPED
Definition sort.hpp:21
Definition simplifier.hpp:86
Definition simplifier.hpp:116
tell_type(const Alloc &alloc=Alloc())
Definition simplifier.hpp:120
int num_vars
Definition simplifier.hpp:117
VarEnv< Alloc > * env
Definition simplifier.hpp:119
formula_sequence formulas
Definition simplifier.hpp:118