Lattice Land Core Library
Loading...
Searching...
No Matches
primitive_upset.hpp
Go to the documentation of this file.
1// Copyright 2022 Pierre Talbot
2
3#ifndef LALA_CORE_PRIMITIVE_UPSET_HPP
4#define LALA_CORE_PRIMITIVE_UPSET_HPP
5
6#include <type_traits>
7#include <utility>
8#include <cmath>
9#include <iostream>
10#include "../logic/logic.hpp"
11#include "pre_binc.hpp"
12#include "pre_finc.hpp"
13#include "pre_zinc.hpp"
14#include "pre_bdec.hpp"
15#include "pre_fdec.hpp"
16#include "pre_zdec.hpp"
17#include "battery/memory.hpp"
18
19/** A pre-abstract universe is a lattice (with usual operations join, order, ...) equipped with a simple logical interpretation function and a next/prev functions.
20 We consider totally ordered pre-abstract universes with an upset semantics.
21 For any lattice \f$ L \f$, we consider an element \f$ a \in L \f$ to represent all the concrete elements equal to or above it.
22 This set is called the upset of \f$ a \f$ and is denoted \f$ \mathord{\uparrow}{a} \f$.
23 The concretization function \f$ \gamma \f$ formalizes this idea: \f$ \gamma(a) = \{x \mapsto b \;|\; b \in \mathord{\uparrow}{a} \cap U \} \f$ where \f$ U \f$ is the universe of discourse.
24 The intersection with \f$ U \f$ is necessary to remove potential elements in the abstract universe that are not in the concrete universe of discourse (e.g., \f$ -\infty, \infty \f$ below).
25
26 The upset semantics associates each element of a lattice to its concrete upset.
27 It is possible to decide that each element is associated to the concrete downset instead.
28 Doing so will reverse our usage of the lattice-theoretic operations (join instead of meet, <= instead of >=, etc.).
29 Instead of considering the upset semantics, it is more convenient to consider the downset semantics of the dual lattice.
30
31 Example:
32 * The lattice of increasing integer \f$ \mathit{ZInc} = \langle \{-\infty, \ldots, -2, -1, 0, 1, 2, \ldots, \infty\}, \leq \rangle \f$ is ordered by the natural arithmetic comparison operator.
33 Using the upset semantics, we can represent simple constraints such as \f$ x \geq 3 \f$, in which case the upset \f$ \mathord{\uparrow}{3} = \{3, 4, \ldots\} \f$ represents all the values of \f$ x \f$ satisfying the constraints \f$ x \geq 3 \f$, that is, the solutions of the constraints.
34 * By taking the downset semantics of \f$ \mathit{ZInc} \f$, we can represent constraints such as \f$ x \leq 3 \f$.
35 * Alternatively, we can take the dual lattice of decreasing integers \f$ \mathit{ZDec} = \langle \{\infty, \ldots, 2, 1, 0, -1, -2, \ldots, -\infty\}, \geq \rangle \f$.
36 The upset semantics of \f$ \mathit{ZDec} \f$ corresponds to the downset semantics of \f$ \mathit{ZInc} \f$.
37
38 From a pre-abstract universe, we obtain an abstract universe using the `Universe` class below.
39 We also define various aliases to abstract universes such as `ZInc`, `ZDec`, etc.
40*/
41
42namespace lala {
43
44template<class PreUniverse, class Mem>
45class FlatUniverse;
46
47template<class PreUniverse, class Mem>
48class PrimitiveUpset;
49
50/** Lattice of increasing integers.
51Concretization function: \f$ \gamma(x) = \{_ \mapsto y \;|\; x \leq y\} \f$. */
52template<class VT, class Mem>
54
55/** Lattice of decreasing integers.
56Concretization function: \f$ \gamma(x) = \{_ \mapsto y \;|\; x \geq y\} \f$. */
57template<class VT, class Mem>
59
60/** Lattice of increasing floating-point numbers.
61Concretization function: \f$ \gamma(x) = \{_ \mapsto y \;|\; y \in \mathbb{R}, x \leq y\} \f$. */
62template<class VT, class Mem>
64
65/** Lattice of decreasing floating-point numbers.
66Concretization function: \f$ \gamma(x) = \{_ \mapsto y \;|\; y \in \mathbb{R}, x \geq y\} \f$. */
67template<class VT, class Mem>
69
70/** Lattice of increasing Boolean where \f$ \mathit{false} \leq \mathit{true} \f$. */
71template<class Mem>
73
74/** Lattice of decreasing Boolean where \f$ \mathit{true} \leq \mathit{false} \f$. */
75template<class Mem>
77
78/** Aliases for lattice allocated on the stack (as local variable) and accessed by only one thread.
79 * To make things simpler, the underlying type is also chosen (when required). */
80namespace local {
87}
88
89namespace impl {
90 template<class T>
91 struct is_primitive_upset {
92 static constexpr bool value = false;
93 };
94
95 template<class PreUniverse, class Mem>
96 struct is_primitive_upset<PrimitiveUpset<PreUniverse, Mem>> {
97 static constexpr bool value = true;
98 };
99
100 template <class T>
101 inline constexpr bool is_primitive_upset_v = is_primitive_upset<T>::value;
102}
103
104/** This function is useful when we need to convert a value to its dual.
105 The dual is the downset of the current element, therefore, if we have \f$ x >= 10 \f$, the dual is given by the formula \f$ x <= 10 \f$ interpreted in the dual lattice.
106 In that case, it just changes the type of the lattice without changing the value.
107 A difference occurs on the bottom and top element.
108 Indeed, by our representation of bot and top, the bottom value in a lattice L equals the top value in its dual, but we need them to remain the same, so the dual of `L::bot()` is `LDual::bot()`.*/
109template <class LDual, class L>
110CUDA constexpr LDual dual(const L& x) {
111 if(x.is_bot()) return LDual::bot();
112 if(x.is_top()) return LDual::top();
113 return LDual(x.value());
114}
115
116template<class PreUniverse, class Mem>
118{
119 using U = PreUniverse;
120public:
121 using pre_universe = PreUniverse;
122 using value_type = typename pre_universe::value_type;
123 using memory_type = Mem;
126
127 template<class M>
129
131
132 template<class M>
134
135 constexpr static const bool is_abstract_universe = true;
136 constexpr static const bool sequential = Mem::sequential;
137 constexpr static const bool is_totally_ordered = pre_universe::is_totally_ordered;
138 constexpr static const bool preserve_bot = pre_universe::preserve_bot;
139 constexpr static const bool preserve_top = pre_universe::preserve_top;
140 constexpr static const bool preserve_join = pre_universe::preserve_join;
141 constexpr static const bool preserve_meet = pre_universe::preserve_meet;
142 constexpr static const bool injective_concretization = pre_universe::injective_concretization;
143 constexpr static const bool preserve_concrete_covers = pre_universe::preserve_concrete_covers;
144 constexpr static const bool complemented = pre_universe::complemented;
145 constexpr static const bool increasing = pre_universe::increasing;
146 constexpr static const char* name = pre_universe::name;
147
148 constexpr static const bool is_arithmetic = pre_universe::is_arithmetic;
149
150 static_assert(is_totally_ordered, "The underlying pre-universe must be totally ordered.");
151
152 /** A pre-interpreted formula `x >= value` ready to use.
153 * This is mainly for optimization purpose to avoid calling `interpret` each time we need it. */
154 CUDA static constexpr this_type geq_k(value_type k) {
155 if constexpr(increasing && is_arithmetic) {
156 return this_type(k);
157 }
158 else {
159 static_assert(increasing && is_arithmetic,
160 "The pre-interpreted formula x >= k is only available over arithmetic universe such as integers, floating-point numbers and Boolean.\
161 Moreover, the underlying abstract universe must be increasing, otherwise this formula is not supported.");
162 }
163 }
164
165 CUDA static constexpr this_type leq_k(value_type k) {
166 if constexpr(!increasing && is_arithmetic) {
167 return this_type(k);
168 }
169 else {
170 static_assert(!increasing && is_arithmetic,
171 "The pre-interpreted formula x <= k is only available over arithmetic universe such as integers, floating-point numbers and Boolean.\
172 Moreover, the underlying abstract universe must be decreasing, otherwise this formula is not supported.");
173 }
174 }
175
176private:
177 using atomic_type = memory_type::template atomic_type<value_type>;
178 atomic_type val;
179
180public:
181 /** Similar to \f$[\![\mathit{true}]\!]\f$ if `preserve_bot` is true. */
182 CUDA static constexpr local_type bot() { return local_type(); }
183
184 /** Similar to \f$[\![\mathit{false}]\!]\f$ if `preserve_top` is true. */
185 CUDA static constexpr local_type top() { return local_type(U::top()); }
186 /** Initialize an upset universe to bottom. */
187 CUDA constexpr PrimitiveUpset(): val(U::bot()) {}
188 /** Similar to \f$[\![x \geq_A i]\!]\f$ for any name `x` where \f$ \geq_A \f$ is the lattice order. */
189 CUDA constexpr PrimitiveUpset(value_type x): val(x) {}
190 CUDA constexpr PrimitiveUpset(const this_type& other): PrimitiveUpset(other.value()) {}
191 constexpr PrimitiveUpset(this_type&& other) = default;
192
193 template <class M>
194 CUDA constexpr PrimitiveUpset(const this_type2<M>& other): PrimitiveUpset(other.value()) {}
195
196 /** The assignment operator can only be used in a sequential context.
197 * It is monotone but not extensive. */
198 template <class M>
199 CUDA constexpr this_type& operator=(const this_type2<M>& other) {
200 if constexpr(sequential) {
201 memory_type::store(val, other.value());
202 return *this;
203 }
204 else {
205 static_assert(sequential, "The operator= in `PrimitiveUpset` can only be used when the underlying memory is `sequential`.");
206 }
207 }
208
209 CUDA constexpr this_type& operator=(const this_type& other) {
210 if constexpr(sequential) {
211 memory_type::store(val, other.value());
212 return *this;
213 }
214 else {
215 static_assert(sequential, "The operator= in `PrimitiveUpset` can only be used when the underlying memory is `sequential`.");
216 }
217 }
218
219 CUDA constexpr value_type value() const { return memory_type::load(val); }
220
221 CUDA constexpr operator value_type() const { return value(); }
222
223 /** `true` whenever \f$ a = \top \f$, `false` otherwise. */
224 CUDA constexpr local::BInc is_top() const {
225 return value() == U::top();
226 }
227
228 /** `true` whenever \f$ a = \bot \f$, `false` otherwise. */
229 CUDA constexpr local::BDec is_bot() const {
230 return value() == U::bot();
231 }
232
233 CUDA constexpr this_type& tell_top() {
234 memory_type::store(val, U::top());
235 return *this;
236 }
237
238 template<class M1, class M2>
239 CUDA constexpr this_type& tell(const this_type2<M1>& other, BInc<M2>& has_changed) {
240 value_type r1 = value();
241 value_type r2 = other.value();
242 if(U::strict_order(r1, r2)) {
243 memory_type::store(val, r2);
244 has_changed.tell_top();
245 }
246 return *this;
247 }
248
249 template<class M1>
250 CUDA constexpr this_type& tell(const this_type2<M1>& other) {
251 value_type r1 = value();
252 value_type r2 = other.value();
253 if(U::strict_order(r1, r2)) {
254 memory_type::store(val, r2);
255 }
256 return *this;
257 }
258
259 CUDA constexpr this_type& dtell_bot() {
260 memory_type::store(val, U::bot());
261 return *this;
262 }
263
264 template<class M1, class M2>
265 CUDA constexpr this_type& dtell(const this_type2<M1>& other, BInc<M2>& has_changed) {
266 value_type r1 = value();
267 value_type r2 = other.value();
268 if(U::strict_order(r2, r1)) {
269 memory_type::store(val, r2);
270 has_changed.tell_top();
271 }
272 return *this;
273 }
274
275 template<class M1>
276 CUDA constexpr this_type& dtell(const this_type2<M1>& other) {
277 value_type r1 = value();
278 value_type r2 = other.value();
279 if(U::strict_order(r2, r1)) {
280 memory_type::store(val, r2);
281 }
282 return *this;
283 }
284
285 /** \return \f$ x \geq i \f$ where `x` is a variable's name and `i` the current value.
286 If `U` preserves bottom `true` is returned whenever \f$ a = \bot \f$, if it preserves top `false` is returned whenever \f$ a = \top \f$.
287 We always return an exact approximation, hence for any formula \f$ \llbracket \varphi \rrbracket = a \f$, we must have \f$ a = \llbracket \rrbracket a \llbracket \rrbracket \f$ where \f$ \rrbracket a \llbracket \f$ is the deinterpretation function.
288 */
289 template<class Env>
290 CUDA NI TFormula<typename Env::allocator_type> deinterpret(AVar avar, const Env& env) const {
292 if(preserve_top && is_top()) {
293 return F::make_false();
294 }
295 else if(preserve_bot && is_bot()) {
296 return F::make_true();
297 }
298 return F::make_binary(
299 deinterpret<F>(),
300 U::sig_order(),
301 F::make_avar(avar),
302 UNTYPED, env.get_allocator());
303 }
304
305 /** Deinterpret the current value to a logical constant. */
306 template<class F>
307 CUDA NI F deinterpret() const {
308 return pre_universe::template deinterpret<F>(value());
309 }
310
311 /** Under-approximates the current element \f$ a \f$ w.r.t. \f$ \rrbracket a \llbracket \f$ into `ua`.
312 * For this abstract universe, it always returns `true` since the current element \f$ a \f$ is an exact representation of \f$ \rrbracket a \llbracket \f$. */
313 CUDA constexpr bool extract(local_type& ua) const {
314 ua.val = value();
315 return true;
316 }
317
318 /** Print the current element. */
319 CUDA NI void print() const {
320 if(is_bot()) {
321 printf("\u22A5");
322 }
323 else if(is_top()) {
324 printf("\u22A4");
325 }
326 else {
328 }
329 }
330
331private:
332 /** Interpret a formula of the form `k <sig> x`. */
333 template<bool diagnose = false, class F, class M2>
334 CUDA NI static bool interpret_tell_k_op_x(const F& f, const F& k, Sig sig, this_type2<M2>& tell, IDiagnostics& diagnostics) {
335 value_type value = pre_universe::bot();
336 bool res = pre_universe::template interpret_tell<diagnose>(k, value, diagnostics);
337 if(res) {
338 if(sig == EQ || sig == U::sig_order()) { // e.g., x <= 4 or x >= 4.24
340 }
341 else if(sig == U::sig_strict_order()) { // e.g., x < 4 or x > 4.24
342 if constexpr(preserve_concrete_covers) {
343 tell.tell(local_type(pre_universe::next(value)));
344 }
345 else {
347 }
348 }
349 else {
350 RETURN_INTERPRETATION_ERROR("The symbol `" + LVar<typename F::allocator_type>(string_of_sig(sig)) + "` is not supported in the tell language of this universe.");
351 }
352 }
353 return res;
354 }
355
356 /** Interpret a formula of the form `k <sig> x`. */
357 template<bool diagnose = false, class F, class M2>
358 CUDA NI static bool interpret_ask_k_op_x(const F& f, const F& k, Sig sig, this_type2<M2>& tell, IDiagnostics& diagnostics) {
359 value_type value = pre_universe::bot();
360 bool res = pre_universe::template interpret_ask<diagnose>(k, value, diagnostics);
361 if(res) {
362 if(sig == U::sig_order()) {
364 }
365 else if(sig == NEQ || sig == U::sig_strict_order()) {
366 // We could actually do a little bit better in the case of FInc/FDec.
367 // If the real number `k` is approximated by `[f, g]`, it actually means `]f, g[` so we could safely choose `r` since it already under-approximates `k`.
368 tell.tell(local_type(pre_universe::next(value)));
369 }
370 else {
371 RETURN_INTERPRETATION_ERROR("The symbol `" + LVar<typename F::allocator_type>(string_of_sig(sig)) + "` is not supported in the ask language of this universe.");
372 }
373 }
374 return res;
375 }
376
377 template<bool diagnose = false, class F, class M2>
378 CUDA NI static bool interpret_tell_set(const F& f, const F& k, this_type2<M2>& tell, IDiagnostics& diagnostics) {
379 const auto& set = k.s();
380 if(set.size() == 0) {
381 tell.tell_top();
382 return true;
383 }
384 value_type meet_s = pre_universe::top();
385 constexpr int bound_index = increasing ? 0 : 1;
386 // We interpret each component of the set and take the meet of all the results.
387 for(int i = 0; i < set.size(); ++i) {
388 auto bound = battery::get<bound_index>(set[i]);
389 value_type set_element = pre_universe::bot();
390 bool res = pre_universe::template interpret_tell<diagnose>(bound, set_element, diagnostics);
391 if(!res) {
392 return false;
393 }
394 meet_s = pre_universe::meet(meet_s, set_element);
395 }
396 tell.tell(local_type(meet_s));
397 return true;
398 }
399
400public:
401 /** Expects a predicate of the form `x <op> k`, `k <op> x` or `x in k`, where `x` is any variable's name, and `k` a constant.
402 * The symbol `<op>` is expected to be `U::sig_order()`, `U::sig_strict_order()` and `=`.
403 * Existential formula \f$ \exists{x:T} \f$ can also be interpreted (only to bottom) depending on the underlying pre-universe.
404 */
405 template<bool diagnose = false, class F, class Env, class M2>
406 CUDA NI static bool interpret_tell(const F& f, const Env&, this_type2<M2>& tell, IDiagnostics& diagnostics) {
407 if(f.is(F::E)) {
408 typename U::value_type val;
409 bool res = pre_universe::template interpret_type<diagnose>(f, val, diagnostics);
410 if(res) {
411 tell.tell(local_type(val));
412 }
413 return res;
414 }
415 else {
416 if(f.is_binary()) {
417 int idx_constant = f.seq(0).is_constant() ? 0 : (f.seq(1).is_constant() ? 1 : 100);
418 int idx_variable = f.seq(0).is_variable() ? 0 : (f.seq(1).is_variable() ? 1 : 100);
419 if(idx_constant + idx_variable != 1) {
420 RETURN_INTERPRETATION_ERROR("Only binary formulas of the form `t1 <sig> t2` where if t1 is a constant and t2 is a variable (or conversely) are supported.")
421 }
422 const auto& k = f.seq(idx_constant);
423 if(f.sig() == IN) {
424 if(idx_constant == 0) { // `k in x` is equivalent to `{k} \subseteq x`.
425 RETURN_INTERPRETATION_ERROR("The formula `k in x` is not supported in this abstract universe (`x in k` is supported).")
426 }
427 else { // `x in k` is equivalent to `x >= meet k` where `>=` is the lattice order `U::sig_order()`.
428 return interpret_tell_set<diagnose>(f, k, tell, diagnostics);
429 }
430 }
431 else if(is_comparison(f)) {
432 Sig sig = idx_constant == 1 ? converse_comparison(f.sig()) : f.sig();
433 return interpret_tell_k_op_x<diagnose>(f, k, sig, tell, diagnostics);
434 }
435 else {
436 RETURN_INTERPRETATION_ERROR("This symbol is not supported.")
437 }
438 }
439 else {
440 RETURN_INTERPRETATION_ERROR("Only binary constraints are supported.")
441 }
442 }
443 }
444
445 /** Expects a predicate of the form `x <op> k` or `k <op> x`, where `x` is any variable's name, and `k` a constant.
446 * The symbol `<op>` is expected to be `U::sig_order()`, `U::sig_strict_order()` or `!=`.
447 */
448 template<bool diagnose = false, class F, class Env, class M2>
449 CUDA NI static bool interpret_ask(const F& f, const Env&, this_type2<M2>& ask, IDiagnostics& diagnostics) {
450 if(f.is_binary()) {
451 int idx_constant = f.seq(0).is_constant() ? 0 : (f.seq(1).is_constant() ? 1 : 100);
452 int idx_variable = f.seq(0).is_variable() ? 0 : (f.seq(1).is_variable() ? 1 : 100);
453 if(idx_constant + idx_variable != 1) {
454 RETURN_INTERPRETATION_ERROR("Only binary formulas of the form `t1 <sig> t2` where if t1 is a constant and t2 is a variable (or conversely) are supported.");
455 }
456 const auto& k = f.seq(idx_constant);
457 if(is_comparison(f)) {
458 Sig sig = idx_constant == 1 ? converse_comparison(f.sig()) : f.sig();
459 return interpret_ask_k_op_x<diagnose>(f, k, sig, ask, diagnostics);
460 }
461 else {
462 RETURN_INTERPRETATION_ERROR("This symbol is not supported.");
463 }
464 }
465 else {
466 RETURN_INTERPRETATION_ERROR("Only binary constraints are supported.");
467 }
468 }
469
470 template<IKind kind, bool diagnose = false, class F, class Env, class M2>
471 CUDA NI static bool interpret(const F& f, const Env& env, this_type2<M2>& value, IDiagnostics& diagnostics) {
472 if constexpr(kind == IKind::TELL) {
473 return interpret_tell<diagnose>(f, env, value, diagnostics);
474 }
475 else {
476 return interpret_ask<diagnose>(f, env, value, diagnostics);
477 }
478 }
479
480 CUDA static constexpr bool is_supported_fun(Sig sig) {
481 return pre_universe::is_supported_fun(sig);
482 }
483
484public:
485 CUDA static constexpr local_type next(const this_type2<Mem>& a) {
486 return local_type(pre_universe::next(a.value()));
487 }
488
489 CUDA static constexpr local_type prev(const this_type2<Mem>& a) {
490 return local_type(pre_universe::prev(a.value()));
491 }
492
493 /** Unary function of type `Sig: FlatUniverse -> PrimitiveUpset`.
494 * \return If `a` is `bot`, we return the bottom element of the upset lattice; and dually for `top`.
495 * Otherwise, we apply the function `Sig` to `a` and return the result.
496 * \remark The result of the function is always over-approximated (or exact when possible).
497 */
498 template <Sig sig, class M>
499 CUDA static constexpr local_type fun(const flat_type<M>& a) {
500 using local_flat_type = flat_type<battery::local_memory>;
501 local_flat_type r1(a);
502 if(r1.is_top()) {
503 return local_type::top();
504 }
505 else if(r1.is_bot()) {
506 return local_type::bot();
507 }
508 return pre_universe::template fun<sig>(r1);
509 }
510
511 /** Binary functions of type `Sig: FlatUniverse x FlatUniverse -> PrimitiveUpset`.
512 * \return If `a` or `b` is `bot`, we return the bottom element of the upset lattice; and dually for `top`.
513 * Otherwise, we apply the function `Sig` to `a` and `b` and return the result.
514 * \remark The result of the function is always over-approximated (or exact when possible).
515 */
516 template<Sig sig, class M1, class M2>
517 CUDA static constexpr local_type fun(const flat_type<M1>& a, const flat_type<M2>& b) {
518 using local_flat_type = flat_type<battery::local_memory>;
519 local_flat_type r1(a);
520 local_flat_type r2(b);
521 if(r1.is_top() || r2.is_top()) {
522 return local_type::top();
523 }
524 else if(r1.is_bot() || r2.is_bot()) {
525 return local_type::bot();
526 }
527 if constexpr(is_division(sig)) {
528 if(r2.value() == pre_universe::zero()) {
529 return local_type::top();
530 }
531 }
532 return pre_universe::template fun<sig>(r1, r2);
533 }
534
535 /** Given two values `a` and `b`, we perform the division while taking care of the case where `b == 0`.
536 * When `b != 0`, the result is `fun<sig>(a, b)`.
537 * Otherwise, the result depends on the type of `a` and `b`:
538 * `a` | `b` | local_type | Result
539 * ------ | ------ | ---------- | ------
540 * Inc | Inc | Inc | 0
541 * Inc | Dec | Dec | 0
542 * Dec | Dec | Inc | 0
543 * Dec | Inc | Dec | 0
544 * - | - | - | bot()
545 */
546 template<Sig sig, class Pre1, class Mem1, class Pre2, class Mem2>
547 CUDA static constexpr local_type guarded_div(
549 {
552 using local_flat_type = flat_type<battery::local_memory>;
553 local_flat_type r1(a);
554 local_flat_type r2(b);
555 if (r2 != B::pre_universe::zero())
556 {
557 return fun<sig>(r1, r2);
558 }
559 else {
560 if constexpr(B::preserve_concrete_covers) {
561 // When `b` is "integer-like" we can just skip the value 0 and go to `-1` or `1` depending on the type `B`.
562 return fun<sig>(r1, local_flat_type(B::next(r2.value())));
563 }
564 else if constexpr(
565 (A::increasing && B::increasing == increasing) || // Inc X T -> T where T is either Inc or Dec.
566 (!A::increasing && !B::increasing && increasing) || // Dec X Dec -> Inc
567 (!A::increasing && B::increasing && !increasing)) // Dec X Inc -> Dec
568 {
569 return local_type(pre_universe::zero());
570 }
571 else {
572 return bot();
573 }
574 }
575 }
576
577 template <Sig sig, class M1, class M2>
578 CUDA static constexpr local_type fun(const this_type2<M1> &a, const this_type2<M2> &b)
579 {
580 static_assert(pre_universe::is_supported_fun(sig), "Function unsupported by the current upset universe.");
581 static_assert(sig == MIN || sig == MAX, "Only MIN and MAX are supported on Upset elements.");
582 using local_flat_type = flat_type<battery::local_memory>;
583 local_flat_type r1(a);
584 local_flat_type r2(b);
585 if (r1.is_top() || r2.is_top())
586 {
587 return local_type::top();
588 }
589 else if (r1.is_bot() || r2.is_bot())
590 {
591 if constexpr((sig == MAX && increasing) || (sig == MIN && !increasing)) {
592 return r1.is_bot() ? b : a;
593 }
594 else {
595 return local_type::bot();
596 }
597 }
598 return pre_universe::template fun<sig>(r1, r2);
599 }
600
601 template<class Pre2, class Mem2>
602 friend class PrimitiveUpset;
603};
604
605// Lattice operators
606
607template<class Pre, class M1, class M2>
609 return Pre::join(a, b);
610}
611
612template<class Pre, class M1, class M2>
614 return Pre::meet(a, b);
615}
616
617template<class Pre, class M1, class M2>
618CUDA constexpr bool operator<=(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
619 return Pre::order(a, b);
620}
621
622template<class Pre, class M1, class M2>
623CUDA constexpr bool operator<(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
624 return Pre::strict_order(a, b);
625}
626
627template<class Pre, class M1, class M2>
628CUDA constexpr bool operator>=(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
629 return Pre::order(b, a);
630}
631
632template<class Pre, class M1, class M2>
633CUDA constexpr bool operator>(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
634 return Pre::strict_order(b, a);
635}
636
637template<class Pre, class M1, class M2>
638CUDA constexpr bool operator==(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
639 return a.value() == b.value();
640}
641
642template<class Pre, class M1, class M2>
643CUDA constexpr bool operator!=(const PrimitiveUpset<Pre, M1>& a, const PrimitiveUpset<Pre, M2>& b) {
644 return a.value() != b.value();
645}
646
647template<class Pre, class M>
648std::ostream& operator<<(std::ostream &s, const PrimitiveUpset<Pre, M> &upset) {
649 if(upset.is_bot()) {
650 s << "\u22A5";
651 }
652 else if(upset.is_top()) {
653 s << "\u22A4";
654 }
655 else {
656 s << upset.value();
657 }
658 return s;
659}
660
661} // namespace lala
662
663#endif
Definition ast.hpp:38
Definition flat_universe.hpp:30
Definition diagnostics.hpp:19
Definition primitive_upset.hpp:118
static constexpr const bool is_totally_ordered
Definition primitive_upset.hpp:137
static constexpr const bool increasing
Definition primitive_upset.hpp:145
CUDA constexpr this_type & operator=(const this_type &other)
Definition primitive_upset.hpp:209
CUDA static NI bool interpret_ask(const F &f, const Env &, this_type2< M2 > &ask, IDiagnostics &diagnostics)
Definition primitive_upset.hpp:449
CUDA constexpr this_type & operator=(const this_type2< M > &other)
Definition primitive_upset.hpp:199
CUDA constexpr PrimitiveUpset(const this_type2< M > &other)
Definition primitive_upset.hpp:194
static CUDA constexpr local_type guarded_div(const PrimitiveUpset< Pre1, Mem1 > &a, const PrimitiveUpset< Pre2, Mem2 > &b)
Definition primitive_upset.hpp:547
CUDA constexpr local::BInc is_top() const
Definition primitive_upset.hpp:224
CUDA NI void print() const
Definition primitive_upset.hpp:319
CUDA constexpr value_type value() const
Definition primitive_upset.hpp:219
CUDA constexpr PrimitiveUpset(value_type x)
Definition primitive_upset.hpp:189
static constexpr const bool is_abstract_universe
Definition primitive_upset.hpp:135
CUDA constexpr this_type & dtell(const this_type2< M1 > &other, BInc< M2 > &has_changed)
Definition primitive_upset.hpp:265
CUDA constexpr bool extract(local_type &ua) const
Definition primitive_upset.hpp:313
static constexpr const char * name
Definition primitive_upset.hpp:146
CUDA NI F deinterpret() const
Definition primitive_upset.hpp:307
CUDA constexpr PrimitiveUpset(const this_type &other)
Definition primitive_upset.hpp:190
static CUDA constexpr local_type prev(const this_type2< Mem > &a)
Definition primitive_upset.hpp:489
static CUDA constexpr local_type fun(const flat_type< M > &a)
Definition primitive_upset.hpp:499
CUDA constexpr this_type & tell(const this_type2< M1 > &other)
Definition primitive_upset.hpp:250
static CUDA constexpr this_type geq_k(value_type k)
Definition primitive_upset.hpp:154
static CUDA constexpr local_type fun(const flat_type< M1 > &a, const flat_type< M2 > &b)
Definition primitive_upset.hpp:517
static CUDA constexpr local_type bot()
Definition primitive_upset.hpp:182
constexpr PrimitiveUpset(this_type &&other)=default
static CUDA constexpr this_type leq_k(value_type k)
Definition primitive_upset.hpp:165
CUDA constexpr local::BDec is_bot() const
Definition primitive_upset.hpp:229
CUDA constexpr this_type & dtell(const this_type2< M1 > &other)
Definition primitive_upset.hpp:276
static constexpr const bool preserve_meet
Definition primitive_upset.hpp:141
CUDA constexpr this_type & dtell_bot()
Definition primitive_upset.hpp:259
static CUDA constexpr local_type fun(const this_type2< M1 > &a, const this_type2< M2 > &b)
Definition primitive_upset.hpp:578
static CUDA constexpr local_type next(const this_type2< Mem > &a)
Definition primitive_upset.hpp:485
typename pre_universe::value_type value_type
Definition primitive_upset.hpp:122
static CUDA constexpr bool is_supported_fun(Sig sig)
Definition primitive_upset.hpp:480
CUDA constexpr PrimitiveUpset()
Definition primitive_upset.hpp:187
PreUniverse pre_universe
Definition primitive_upset.hpp:121
CUDA static NI bool interpret(const F &f, const Env &env, this_type2< M2 > &value, IDiagnostics &diagnostics)
Definition primitive_upset.hpp:471
Mem memory_type
Definition primitive_upset.hpp:123
static constexpr const bool complemented
Definition primitive_upset.hpp:144
static constexpr const bool preserve_bot
Definition primitive_upset.hpp:138
this_type2< battery::local_memory > local_type
Definition primitive_upset.hpp:130
static CUDA constexpr local_type top()
Definition primitive_upset.hpp:185
CUDA NI TFormula< typename Env::allocator_type > deinterpret(AVar avar, const Env &env) const
Definition primitive_upset.hpp:290
CUDA static NI bool interpret_tell(const F &f, const Env &, this_type2< M2 > &tell, IDiagnostics &diagnostics)
Definition primitive_upset.hpp:406
PrimitiveUpset< pre_universe, memory_type > this_type
Definition primitive_upset.hpp:124
static constexpr const bool is_arithmetic
Definition primitive_upset.hpp:148
static constexpr const bool preserve_join
Definition primitive_upset.hpp:140
static constexpr const bool sequential
Definition primitive_upset.hpp:136
CUDA constexpr this_type & tell_top()
Definition primitive_upset.hpp:233
static constexpr const bool injective_concretization
Definition primitive_upset.hpp:142
static constexpr const bool preserve_top
Definition primitive_upset.hpp:139
static constexpr const bool preserve_concrete_covers
Definition primitive_upset.hpp:143
CUDA constexpr this_type & tell(const this_type2< M1 > &other, BInc< M2 > &has_changed)
Definition primitive_upset.hpp:239
Definition ast.hpp:234
#define RETURN_INTERPRETATION_ERROR(MSG)
Definition diagnostics.hpp:155
CUDA NI void print(const lala::Sig &sig)
Definition ast.hpp:212
Definition abstract_deps.hpp:14
std::ostream & operator<<(std::ostream &s, const CartesianProduct< A, As... > &cp)
Definition cartesian_product.hpp:597
CUDA constexpr LDual dual(const L &x)
Definition primitive_upset.hpp:110
CUDA NI Sig converse_comparison(Sig sig)
Definition algorithm.hpp:377
CUDA constexpr bool operator==(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:572
Sig
Definition ast.hpp:94
@ NEQ
Equality relations.
Definition ast.hpp:112
@ EQ
Unary arithmetic function symbols.
Definition ast.hpp:112
@ IN
Set membership predicate.
Definition ast.hpp:108
@ MIN
Unary arithmetic function symbols.
Definition ast.hpp:97
@ MAX
Binary arithmetic function symbols.
Definition ast.hpp:97
CUDA constexpr auto join(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:534
CUDA constexpr auto meet(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:541
CUDA constexpr bool operator<(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:554
CUDA NI bool is_comparison(const F &f)
Definition algorithm.hpp:356
CUDA NI const char * string_of_sig(Sig sig)
Definition ast.hpp:121
CUDA NI constexpr bool is_division(Sig sig)
Definition ast.hpp:196
CUDA constexpr bool operator>(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:566
#define UNTYPED
Definition sort.hpp:21