Lattice Land Core Library
Loading...
Searching...
No Matches
interval.hpp
Go to the documentation of this file.
1// Copyright 2021 Pierre Talbot
2
3#ifndef LALA_CORE_INTERVAL_HPP
4#define LALA_CORE_INTERVAL_HPP
5
8
9namespace lala {
10
11template <class U>
12class Interval;
13
14namespace impl {
15 template <class LB, class UB>
16 constexpr typename Interval<LB>::local_type make_itv(CartesianProduct<LB, UB> cp) {
17 return typename Interval<LB>::local_type(project<0>(cp), project<1>(cp));
18 }
19
20 template <class U> constexpr const typename Interval<U>::LB& lb(const Interval<U>& itv) { return itv.lb(); }
21 template <class L> constexpr const L& lb(const L& other) { return other; }
22 template <class U> constexpr const typename Interval<U>::UB& ub(const Interval<U>& itv) { return itv.ub(); }
23 template <class L> constexpr const L& ub(const L& other) { return other; }
24}
25
26/** An interval is a Cartesian product of a lower and upper bounds, themselves represented as lattices.
27 One difference, is that the \f$ \top \f$ can be represented by multiple interval elements, whenever \f$ l > u \f$, therefore some operations are different than on the Cartesian product, e.g., \f$ [3..2] \equiv [4..1] \f$ in the interval lattice. */
28template <class U>
29class Interval {
30public:
31 using LB = U;
32 using UB = typename LB::dual_type;
36 using value_type = typename CP::value_type;
37 using memory_type = typename CP::memory_type;
38
39 template <class A>
40 friend class Interval;
41
42 constexpr static const bool is_abstract_universe = true;
43 constexpr static const bool sequential = CP::sequential;
44 constexpr static const bool is_totally_ordered = false;
45 constexpr static const bool preserve_bot = CP::preserve_bot;
46 constexpr static const bool preserve_top = true;
47 constexpr static const bool preserve_join = true;
48 constexpr static const bool preserve_meet = false;
51 constexpr static const bool complemented = false;
52 constexpr static const char* name = "Interval";
53
54private:
55 using LB2 = typename LB::local_type;
56 using UB2 = typename UB::local_type;
57 CP cp;
58 CUDA constexpr Interval(const CP& cp): cp(cp) {}
59 CUDA constexpr local_type lb2() const { return local_type(lb(), dual<UB2>(lb())); }
60 CUDA constexpr local_type ub2() const { return local_type(dual<LB2>(ub()), ub()); }
61public:
62 /** Initialize the interval to bottom using the default constructor of the bounds. */
63 CUDA constexpr Interval() {}
64 /** Given a value \f$ x \in U \f$ where \f$ U \f$ is the universe of discourse, we initialize a singleton interval \f$ [x..x] \f$. */
65 CUDA constexpr Interval(const typename U::value_type& x): cp(x, x) {}
66 CUDA constexpr Interval(const LB& lb, const UB& ub): cp(lb, ub) {}
67
68 template<class A>
69 CUDA constexpr Interval(const Interval<A>& other): cp(other.cp) {}
70
71 template<class A>
72 CUDA constexpr Interval(Interval<A>&& other): cp(std::move(other.cp)) {}
73
74 /** The assignment operator can only be used in a sequential context.
75 * It is monotone but not extensive. */
76 template <class A>
77 CUDA constexpr this_type& operator=(const Interval<A>& other) {
78 cp = other.cp;
79 return *this;
80 }
81
82 CUDA constexpr this_type& operator=(const this_type& other) {
83 cp = other.cp;
84 return *this;
85 }
86
87 /** Pre-interpreted formula `x == 0`. */
88 CUDA constexpr static local_type eq_zero() { return local_type(LB::geq_k(LB::pre_universe::zero()), UB::leq_k(UB::pre_universe::zero())); }
89 /** Pre-interpreted formula `x == 1`. */
90 CUDA constexpr static local_type eq_one() { return local_type(LB::geq_k(LB::pre_universe::one()), UB::leq_k(UB::pre_universe::one())); }
91
92 CUDA constexpr static local_type bot() { return Interval(CP::bot()); }
93 CUDA constexpr static local_type top() { return Interval(CP::top()); }
94 CUDA constexpr local::BInc is_top() const { return cp.is_top() || (!ub().is_bot() && lb() > dual<LB2>(ub())); }
95 CUDA constexpr local::BDec is_bot() const { return cp.is_bot(); }
96 CUDA constexpr const CP& as_product() const { return cp; }
97 CUDA constexpr value_type value() const { return cp.value(); }
98
99private:
100 template<Sig sig, class A, class B>
101 CUDA constexpr static local_type flat_fun(const Interval<A>& a, const Interval<B>& b) {
102 return local_type(
103 LB2::template fun<sig>(
104 typename A::template flat_type<battery::local_memory>(a.lb()),
105 typename B::template flat_type<battery::local_memory>(b.lb())),
106 UB2::template fun<sig>(
107 typename A::template flat_type<battery::local_memory>(a.ub()),
108 typename B::template flat_type<battery::local_memory>(b.ub())));
109 }
110
111 template<Sig sig, class A>
112 CUDA constexpr static local_type flat_fun(const Interval<A>& a) {
113 return local_type(
114 LB2::template fun<sig>(typename A::template flat_type<battery::local_memory>(a.lb())),
115 UB2::template fun<sig>(typename A::template flat_type<battery::local_memory>(a.ub())));
116 }
117
118public:
119 /** Support the same language than the Cartesian product, and more:
120 * * `var x:B` when the underlying universe is arithmetic and preserve concrete covers.
121 * Therefore, the element `k` is always in \f$ \gamma(lb) \cap \gamma(ub) \f$. */
122 template<bool diagnose = false, class F, class Env, class U2>
123 CUDA NI static bool interpret_tell(const F& f, const Env& env, Interval<U2>& k, IDiagnostics& diagnostics) {
124 if constexpr(LB::preserve_concrete_covers && LB::is_arithmetic) {
125 if(f.is(F::E)) {
126 auto sort = f.sort();
127 if(sort.has_value() && sort->is_bool()) {
128 k.tell(local_type(LB::geq_k(LB::pre_universe::zero()), UB::leq_k(UB::pre_universe::one())));
129 return true;
130 }
131 }
132 }
133 return CP::template interpret_tell<diagnose>(f, env, k.cp, diagnostics);
134 }
135
136 /** Support the same language than the Cartesian product, and more:
137 * * `x != k` is under-approximated by interpreting `x != k` in the lower bound.
138 * * `x == k` is interpreted by over-approximating `x == k` in both bounds and then verifying both bounds are the same.
139 * * `x in {[l..u]} is interpreted by under-approximating `x >= l` and `x <= u`. */
140 template<bool diagnose = false, class F, class Env, class U2>
141 CUDA NI static bool interpret_ask(const F& f, const Env& env, Interval<U2>& k, IDiagnostics& diagnostics) {
143 if(f.is_binary() && f.sig() == NEQ) {
144 return LB::template interpret_ask<diagnose>(f, env, k.lb(), diagnostics);
145 }
146 else if(f.is_binary() && f.sig() == EQ) {
148 "When interpreting equality, the underlying bounds LB and UB failed to agree on the same value.",
149 (LB::template interpret_tell<diagnose>(f, env, itv.lb(), diagnostics) &&
150 UB::template interpret_tell<diagnose>(f, env, itv.ub(), diagnostics) &&
151 itv.lb() == itv.ub()),
152 (k.tell(itv)));
153 }
154 else if(f.is_binary() && f.sig() == IN && f.seq(0).is_variable()
155 && f.seq(1).is(F::S) && f.seq(1).s().size() == 1)
156 {
157 const auto& lb = battery::get<0>(f.seq(1).s()[0]);
158 const auto& ub = battery::get<1>(f.seq(1).s()[0]);
159 if(lb == ub) {
161 "Failed to interpret the decomposition of set membership `x in {[v..v]}` into equality `x == v`.",
162 (interpret_ask<diagnose>(F::make_binary(f.seq(0), EQ, lb), env, k, diagnostics)));
163 }
165 "Failed to interpret the decomposition of set membership `x in {[l..u]}` into `x >= l /\\ x <= u`.",
166 (LB::template interpret_ask<diagnose>(F::make_binary(f.seq(0), geq_of_constant(lb), lb), env, itv.lb(), diagnostics) &&
167 UB::template interpret_ask<diagnose>(F::make_binary(f.seq(0), leq_of_constant(ub), ub), env, itv.ub(), diagnostics)),
168 (k.tell(itv))
169 );
170 }
171 return CP::template interpret_ask<diagnose>(f, env, k.cp, diagnostics);
172 }
173
174 template<IKind kind, bool diagnose = false, class F, class Env, class U2>
175 CUDA NI static bool interpret(const F& f, const Env& env, Interval<U2>& k, IDiagnostics& diagnostics) {
176 if constexpr(kind == IKind::ASK) {
177 return interpret_ask<diagnose>(f, env, k, diagnostics);
178 }
179 else {
180 return interpret_tell<diagnose>(f, env, k, diagnostics);
181 }
182 }
183
184 /** You must use the lattice interface (tell methods) to modify the lower and upper bounds, if you use assignment you violate the PCCP model. */
185 CUDA constexpr LB& lb() { return project<0>(cp); }
186 CUDA constexpr UB& ub() { return project<1>(cp); }
187
188 CUDA constexpr const LB& lb() const { return project<0>(cp); }
189 CUDA constexpr const UB& ub() const { return project<1>(cp); }
190
191 CUDA constexpr this_type& tell_top() {
192 cp.tell_top();
193 return *this;
194 }
195
196 template<class A, class M>
197 CUDA constexpr this_type& tell_lb(const A& lb, BInc<M>& has_changed) {
198 cp.template tell<0>(lb, has_changed);
199 return *this;
200 }
201
202 template<class A, class M>
203 CUDA constexpr this_type& tell_ub(const A& ub, BInc<M>& has_changed) {
204 cp.template tell<1>(ub, has_changed);
205 return *this;
206 }
207
208 template<class A>
209 CUDA constexpr this_type& tell_lb(const A& lb) {
210 cp.template tell<0>(lb);
211 return *this;
212 }
213
214 template<class A>
215 CUDA constexpr this_type& tell_ub(const A& ub) {
216 cp.template tell<1>(ub);
217 return *this;
218 }
219
220 template<class A, class M>
221 CUDA constexpr this_type& tell(const Interval<A>& other, BInc<M>& has_changed) {
222 cp.tell(other.cp, has_changed);
223 return *this;
224 }
225
226 template<class A>
227 CUDA constexpr this_type& tell(const Interval<A>& other) {
228 cp.tell(other.cp);
229 return *this;
230 }
231
232 CUDA constexpr this_type& dtell_bot() {
233 cp.dtell_bot();
234 return *this;
235 }
236
237 template<class A, class M>
238 CUDA constexpr this_type& dtell_lb(const A& lb, BInc<M>& has_changed) {
239 cp.template dtell<0>(lb, has_changed);
240 return *this;
241 }
242
243 template<class A, class M>
244 CUDA constexpr this_type& dtell_ub(const A& ub, BInc<M>& has_changed) {
245 cp.template dtell<1>(ub, has_changed);
246 return *this;
247 }
248
249 template<class A>
250 CUDA constexpr this_type& dtell_lb(const A& lb) {
251 cp.template dtell<0>(lb);
252 return *this;
253 }
254
255 template<class A>
256 CUDA constexpr this_type& dtell_ub(const A& ub) {
257 cp.template dtell<1>(ub);
258 return *this;
259 }
260
261 template<class A, class M>
262 CUDA constexpr this_type& dtell(const Interval<A>& other, BInc<M>& has_changed) {
263 cp.dtell(other.cp, has_changed);
264 return *this;
265 }
266
267 template<class A>
268 CUDA constexpr this_type& dtell(const Interval<A>& other) {
269 cp.dtell(other.cp);
270 return *this;
271 }
272
273 template <class A>
274 CUDA constexpr bool extract(Interval<A>& ua) const {
275 return cp.extract(ua.cp);
276 }
277
278 template<class Env>
281 if(lb().is_top() || ub().is_top() || lb().is_bot() || ub().is_bot()) {
282 return cp.deinterpret(x, env);
283 }
284 F logical_lb = lb().template deinterpret<F>();
285 F logical_ub = ub().template deinterpret<F>();
286 logic_set<F> logical_set(1, env.get_allocator());
287 logical_set[0] = battery::make_tuple(std::move(logical_lb), std::move(logical_ub));
288 F set = F::make_set(std::move(logical_set));
289 F var = F::make_avar(x);
290 return F::make_binary(var, IN, std::move(set), UNTYPED, env.get_allocator());
291 }
292
293 /** Deinterpret the current value to a logical constant.
294 * The lower bound is deinterpreted, and it is up to the user to check that interval is a singleton.
295 * A special case is made for real numbers where the both bounds are used, since the logical interpretation uses interval.
296 */
297 template<class F>
298 CUDA NI F deinterpret() const {
299 F logical_lb = lb().template deinterpret<F>();
300 if(logical_lb.is(F::R)) {
301 F logical_ub = ub().template deinterpret<F>();
302 battery::get<1>(logical_lb.r()) = battery::get<0>(logical_ub.r());
303 }
304 return logical_lb;
305 }
306
307 CUDA NI void print() const {
308 printf("[");
309 lb().print();
310 printf("..");
311 ub().print();
312 printf("]");
313 }
314
315 CUDA NI constexpr static bool is_supported_fun(Sig sig) {
316 switch(sig) {
317 case ABS: return CP::is_supported_fun(NEG);
319 case NEG:
320 case ADD:
321 case MUL:
322 case TDIV:
323 case TMOD:
324 case FDIV:
325 case FMOD:
326 case CDIV:
327 case CMOD:
328 case EDIV:
329 case EMOD:
330 case POW:
331 case MIN:
332 case MAX: return CP::is_supported_fun(sig);
333 default: return false;
334 }
335 }
336
337 /** The additive inverse is obtained by pairwise negation of the components.
338 * Equivalent to `neg(reverse(x))`.
339 * Note that the inverse of `bot` is `bot`, simply because `bot` has no mathematical inverse. */
340 CUDA constexpr static local_type additive_inverse(const this_type& x) {
341 static_assert(LB::is_supported_fun(NEG) && UB::is_supported_fun(NEG),
342 "Negation of interval bounds are required to compute the additive inverse.");
343 return flat_fun<NEG>(x);
344 }
345
346public:
347 template<class L>
348 CUDA constexpr static local_type reverse(const Interval<L>& x) {
349 return local_type(dual<LB2>(x.ub()), dual<UB2>(x.lb()));
350 }
351
352 template<class L>
353 CUDA constexpr static local_type neg(const Interval<L>& x) {
354 return reverse(flat_fun<NEG>(x));
355 }
356
357 // This operation preserves top, i.e., \f$ abs(x) \in [\top] \f$ if \f$ x \in [\top] \f$, \f$ [\top] \f$ being the equivalence class of top elements.
358 template<class L>
359 CUDA constexpr static local_type abs(const Interval<L>& x) {
360 switch(sign(x)) {
361 case PP: return x;
362 case NP: return local_type( // [0..max(-lb, ub)]
363 LB2::geq_k(LB2::pre_universe::zero()),
364 meet(dual<UB2>(LB2::template fun<ABS>(typename L::template flat_type<battery::local_memory>(x.lb()))), x.ub()));
365 case NN: return neg(x);
366 case PN: return local_type(x.lb(), meet(UB2::leq_k(UB2::pre_universe::zero()), x.ub()));
367 }
368 assert(0); // all cases should be covered:
369 return top();
370 }
371
372 template<Sig sig, class L>
373 CUDA constexpr static local_type fun(const Interval<L>& x) {
374 static_assert(sig == NEG || sig == ABS, "Unsupported unary function.");
375 switch(sig) {
376 case NEG: return neg(x);
377 case ABS: return abs(x);
378 default:
379 assert(0); return x;
380 }
381 }
382
383public:
384 template<class L, class K>
385 CUDA constexpr static local_type add(const Interval<L>& x, const Interval<K>& y) {
386 return flat_fun<ADD>(x, y);
387 }
388
389 template<class L, class K>
390 CUDA constexpr static local_type sub(const Interval<L>& x, const Interval<K>& y) {
391 return add(x, neg(y));
392 }
393
394private:
395 /** Characterization of the sign of the bounds (e.g., NP = lower bound is negative, upper bound is positive).
396 * It can viewed as a lattice with the order NP < PP < PN and NP < NN < PN. */
397 enum bounds_sign {
398 PP, NN, NP, PN
399 };
400
401 /** The sign function is monotone w.r.t. the order of `Interval<A>` and `bounds_sign`. */
402 template<class A>
403 CUDA constexpr static bounds_sign sign(const Interval<A>& a) {
404 if(a.lb() >= LB2::geq_k(LB2::pre_universe::zero())) {
405 if(a.ub() > UB2::leq_k(UB2::pre_universe::zero())) {
406 return PN;
407 }
408 else {
409 return PP;
410 }
411 }
412 else {
413 if(a.ub() >= UB2::leq_k(UB2::pre_universe::zero())) {
414 return NN;
415 }
416 else {
417 return NP;
418 }
419 }
420 }
421
422 template<class A, class B>
423 CUDA constexpr static local_type mul2(const Interval<A>& a, const Interval<B>& b) {
424 return flat_fun<MUL>(a, b);
425 }
426
427 template<Sig sig, class R, class A, class B>
428 CUDA constexpr static R flat_fun2(const A& a, const B& b) {
429 return R::template fun<sig>(
430 typename A::template flat_type<battery::local_memory>(a),
431 typename B::template flat_type<battery::local_memory>(b));
432 }
433
434public:
435 template<class L, class K>
436 CUDA constexpr static local_type mul(const Interval<L>& l, const Interval<K>& k) {
437 auto a = typename Interval<L>::local_type(l);
438 auto b = typename Interval<K>::local_type(k);
439 // Interval multiplication case, [al..au] * [bl..bu]
440 switch(sign(a)) {
441 case PP:
442 switch(sign(b)) {
443 case PP: return mul2(a, b);
444 case NP: return mul2(a.ub2(), b);
445 case NN: return mul2(reverse(a), b);
446 case PN:
447 if(b.as_product().is_top()) { return top(); }
448 else { return mul2(a.lb2(), b); }
449 }
450 case NP:
451 switch(sign(b)) {
452 case PP: return mul2(a, b.ub2());
453 // Note: we use meet for both bounds because UB is the dual of LB (e.g., if meet in LB is min, then meet in UB is max).
454 case NP: return local_type(
455 meet(flat_fun2<MUL, LB2>(a.lb(), b.ub()), flat_fun2<MUL, LB2>(a.ub(), b.lb())),
456 meet(flat_fun2<MUL, UB2>(a.lb(), b.lb()), flat_fun2<MUL, UB2>(a.ub(), b.ub())));
457 case NN: return mul2(reverse(a), b.lb2());
458 case PN:
459 if(b.as_product().is_top()) { return top(); }
460 else { return eq_zero(); }
461 }
462 case NN:
463 switch(sign(b)) {
464 case PP: return mul2(a, reverse(b));
465 case NP: return mul2(a.lb2(), reverse(b));
466 case NN: return mul2(reverse(a), reverse(b));
467 case PN:
468 if(b.as_product().is_top()) { return top(); }
469 else { return mul2(a.ub2(), reverse(b)); }
470 }
471 case PN:
472 if(a.as_product().is_top()) { return top(); }
473 else {
474 switch(sign(b)) {
475 case PP: return mul2(a, b.lb2());
476 case NP: return eq_zero();
477 case NN: return mul2(reverse(a), b.ub2());
478 case PN:
479 if(b.as_product().is_top()) { return top(); }
480 else {
481 return local_type(
482 join(flat_fun2<MUL, LB2>(a.lb(), b.lb()), flat_fun2<MUL, LB2>(a.ub(), b.ub())),
483 join(flat_fun2<MUL, UB2>(a.lb(), b.ub()), flat_fun2<MUL, UB2>(a.ub(), b.lb())));
484 }
485 }
486 }
487 }
488 assert(0); // All cases should be covered.
489 return top();
490 }
491
492private:
493 /** For division, we cannot change the type of the bounds due to its importance in the underlying domain (usually PrimitiveUpset) when computing with zeroes. */
494 template<Sig divsig, class AL, class AU, class BL, class BU>
495 CUDA constexpr static local_type div2(const AL& al, const AU& au, const BL& bl, const BU& bu) {
496 return local_type(LB2::template guarded_div<divsig>(al, bl),
497 UB2::template guarded_div<divsig>(au, bu));
498 }
499
500public:
501 template<Sig divsig, class L, class K>
502 CUDA constexpr static local_type div(const Interval<L>& l, const Interval<K>& k) {
503 auto a = typename Interval<L>::local_type(l);
504 auto b = typename Interval<K>::local_type(k);
505 // Below, you can find cases where a or b are top, similar to multiplication above, but this has bugs and need to be studied in more depth.
506 if(a.is_top() || b.is_top()) { return top(); }
507 using UB_K = typename Interval<K>::UB::local_type;
508 constexpr auto leq_zero = UB_K::leq_k(UB_K::pre_universe::zero());
509 // Interval division, [al..au] / [bl..bu]
510 switch(sign(b)) {
511 case PP:
512 if(b.ub() >= leq_zero) { return top(); } // b is a singleton equal to zero.
513 switch(sign(a)) {
514 case PP: return div2<divsig>(a.lb(), a.ub(), b.ub(), b.lb());
515 case NP: return div2<divsig>(a.lb(), a.ub(), b.lb(), b.lb());
516 case NN: return div2<divsig>(a.lb(), a.ub(), b.lb(), b.ub());
517 case PN:
518 if(a.as_product().is_top()) { return top(); }
519 else { return div2<divsig>(a.lb(), a.ub(), b.ub(), b.ub()); }
520 }
521 case NP:
522 if(a.is_top()) { return top(); }
523 else {
524 if constexpr(L::preserve_concrete_covers && K::preserve_concrete_covers) { // In the discrete case, division can be more precise.
525 switch(sign(a)) {
526 case PP: return div2<divsig>(a.ub(), a.ub(), b.lb(), b.ub());
527 case NP: return local_type(
528 meet(LB2::template guarded_div<divsig>(a.lb(), b.ub()), LB2::template guarded_div<divsig>(a.ub(), b.lb())),
529 meet(UB2::template guarded_div<divsig>(a.lb(), b.lb()), UB2::template guarded_div<divsig>(a.ub(), b.ub())));
530 case NN: return div2<divsig>(a.lb(), a.lb(), b.ub(), b.lb());
531 case PN: return (a.as_product().is_top()) ? top() : eq_zero();
532 }
533 }
534 else {
535 return bot();
536 }
537 }
538 case NN:
539 switch(sign(a)) {
540 case PP: return div2<divsig>(a.ub(), a.lb(), b.ub(), b.lb());
541 case NP: return div2<divsig>(a.ub(), a.lb(), b.ub(), b.ub());
542 case NN: return div2<divsig>(a.ub(), a.lb(), b.lb(), b.ub());
543 case PN:
544 if(a.as_product().is_top()) { return top(); }
545 else { return div2<divsig>(a.ub(), a.lb(), b.lb(), b.lb()); }
546 }
547 case PN:
548 if(b.as_product().is_top()) { return top(); }
549 if constexpr(L::preserve_concrete_covers && K::preserve_concrete_covers) {
550 switch(sign(a)) {
551 case PP: return div2<divsig>(a.lb(), a.lb(), b.ub(), b.lb());
552 case NP: return eq_zero();
553 case NN: return div2<divsig>(a.ub(), a.ub(), b.ub(), b.lb());
554 case PN:
555 if(a.as_product().is_top()) { return top(); }
556 else {
557 return local_type(
558 join(LB2::template guarded_div<divsig>(a.lb(), b.ub()), LB2::template guarded_div<divsig>(a.ub(), b.lb())),
559 join(UB2::template guarded_div<divsig>(a.lb(), b.lb()), UB2::template guarded_div<divsig>(a.ub(), b.ub())));
560 }
561 }
562 }
563 else {
564 return top(); /* This is by duality with the case above, but I am unsure if it makes sense. */
565 }
566 }
567 assert(0); // All cases should be covered.
568 return top();
569 }
570
571 template<Sig modsig, class L, class K>
572 CUDA constexpr static local_type mod(const Interval<L>& l, const Interval<K>& k) {
573 auto a = typename Interval<L>::local_type(l);
574 auto b = typename Interval<K>::local_type(k);
575 if(a.is_top() || b.is_top()) { return top(); }
576 if(a.lb() == dual<LB2>(a.ub()) && b.lb() == dual<LB2>(b.ub())) {
577 return flat_fun<modsig>(a, b);
578 }
579 else {
580 return bot();
581 }
582 }
583
584 template<class L, class K>
585 CUDA constexpr static local_type pow(const Interval<L>& l, const Interval<K>& k) {
586 auto a = typename Interval<L>::local_type(l);
587 auto b = typename Interval<K>::local_type(k);
588 if(a.is_top() || b.is_top()) { return top(); }
589 if(a.lb() == dual<LB2>(a.ub()) && b.lb() == dual<LB2>(b.ub())) {
590 return flat_fun<POW>(a, b);
591 }
592 else {
593 return bot();
594 }
595 }
596
597 template<Sig sig, class L, class K>
598 CUDA constexpr static local_type fun(const Interval<L>& x, const Interval<K>& y) {
599 if constexpr(sig == ADD) { return add(x, y); }
600 else if constexpr(sig == SUB) { return sub(x, y); }
601 else if constexpr(sig == MUL) { return mul(x, y); }
602 else if constexpr(is_division(sig)) { return div<sig>(x, y); }
603 else if constexpr(is_modulo(sig)) { return mod<sig>(x, y); }
604 else if constexpr(sig == POW) { return pow(x, y); }
605 else if constexpr(sig == MIN || sig == MAX) { return CP::template fun<sig>(x.as_product(), y.as_product()); }
606 else { static_assert(
607 sig == ADD || sig == SUB || sig == MUL || sig == TDIV || sig == TMOD || sig == FDIV || sig == FMOD || sig == CDIV || sig == CMOD || sig == EDIV || sig == EMOD || sig == POW || sig == MIN || sig == MAX,
608 "Unsupported binary function.");
609 }
610 }
611
612 CUDA constexpr local_type width() const {
613 static_assert(LB::is_totally_ordered && LB::is_arithmetic,
614 "Width is only defined for totally ordered arithmetic intervals.");
615 return sub(ub2(), lb2());
616 }
617
618 /** \return The median value of the interval, which is computed by `lb() + ((ub() - lb()) / 2)`. */
619 CUDA constexpr local_type median() const {
620 static_assert(LB::is_totally_ordered && LB::is_arithmetic,
621 "Median function is only defined for totally ordered arithmetic intervals.");
622 auto x = sub(ub2(), lb2());
623 return
624 add(lb2(), meet(div<FDIV>(x, local_type(2,2)), div<CDIV>(x, local_type(2,2))));
625 }
626};
627
628// Lattice operations
629
630template<class L, class K>
631CUDA constexpr auto join(const Interval<L>& a, const Interval<K>& b)
632{
633 return impl::make_itv(join(a.as_product(), b.as_product()));
634}
635
636template<class L, class K>
637CUDA constexpr auto meet(const Interval<L>& a, const Interval<K>& b)
638{
639 if(a.is_top()) { return b; }
640 if(b.is_top()) { return a; }
641 return impl::make_itv(meet(a.as_product(), b.as_product()));
642}
643
644template<class L, class K>
645CUDA constexpr bool operator<=(const Interval<L>& a, const Interval<K>& b)
646{
647 return b.is_top() || a.as_product() <= b.as_product();
648}
649
650template<class L, class K>
651CUDA constexpr bool operator<(const Interval<L>& a, const Interval<K>& b)
652{
653 return (b.is_top() && !a.is_top()) || a.as_product() < b.as_product();
654}
655
656template<class L, class K>
657CUDA constexpr bool operator>=(const Interval<L>& a, const Interval<K>& b)
658{
659 return b <= a;
660}
661
662template<class L, class K>
663CUDA constexpr bool operator>(const Interval<L>& a, const Interval<K>& b)
664{
665 return b < a;
666}
667
668template<class L, class K>
669CUDA constexpr bool operator==(const Interval<L>& a, const Interval<K>& b)
670{
671 return a.as_product() == b.as_product() || (a.is_top() && b.is_top());
672}
673
674template<class L, class K>
675CUDA constexpr bool operator!=(const Interval<L>& a, const Interval<K>& b)
676{
677 return a.as_product() != b.as_product() && !(a.is_top() && b.is_top());
678}
679
680template<class L>
681std::ostream& operator<<(std::ostream &s, const Interval<L> &itv) {
682 return s << "[" << itv.lb() << ".." << itv.ub() << "]";
683}
684
685} // namespace lala
686
687#endif
Definition ast.hpp:38
CUDA constexpr this_type & dtell(const CartesianProduct< Bs... > &other, BInc< M > &has_changed)
Definition cartesian_product.hpp:336
CUDA constexpr bool extract(CartesianProduct< Bs... > &ua) const
Definition cartesian_product.hpp:359
CUDA constexpr local::BDec is_bot() const
Definition cartesian_product.hpp:223
CUDA constexpr value_type value() const
Definition cartesian_product.hpp:213
CUDA constexpr local::BInc is_top() const
Definition cartesian_product.hpp:218
CUDA constexpr this_type & dtell_bot()
Definition cartesian_product.hpp:330
static constexpr const bool injective_concretization
Definition cartesian_product.hpp:83
CUDA constexpr this_type & tell_top()
Definition cartesian_product.hpp:303
static constexpr const bool sequential
Definition cartesian_product.hpp:77
static constexpr const bool preserve_bot
Definition cartesian_product.hpp:79
CUDA constexpr this_type & tell(const CartesianProduct< Bs... > &other, BInc< M > &has_changed)
Definition cartesian_product.hpp:309
battery::tuple< typename As::value_type... > value_type
Definition cartesian_product.hpp:74
static CUDA constexpr local_type top()
Definition cartesian_product.hpp:133
typename type_of< 0 >::memory_type memory_type
Definition cartesian_product.hpp:70
static constexpr const bool preserve_concrete_covers
Definition cartesian_product.hpp:84
static CUDA constexpr local_type bot()
Definition cartesian_product.hpp:128
CUDA TFormula< typename Env::allocator_type > deinterpret(AVar x, const Env &env) const
Definition cartesian_product.hpp:444
static CUDA constexpr bool is_supported_fun(Sig sig)
Definition cartesian_product.hpp:391
Definition diagnostics.hpp:19
Definition interval.hpp:29
CUDA constexpr this_type & operator=(const this_type &other)
Definition interval.hpp:82
CUDA static constexpr local_type additive_inverse(const this_type &x)
Definition interval.hpp:340
CUDA constexpr this_type & dtell_lb(const A &lb, BInc< M > &has_changed)
Definition interval.hpp:238
typename LB::dual_type UB
Definition interval.hpp:32
typename CP::value_type value_type
Definition interval.hpp:36
CUDA static NI bool interpret(const F &f, const Env &env, Interval< U2 > &k, IDiagnostics &diagnostics)
Definition interval.hpp:175
CUDA static constexpr local_type eq_one()
Definition interval.hpp:90
CUDA static constexpr local_type bot()
Definition interval.hpp:92
CUDA static constexpr local_type abs(const Interval< L > &x)
Definition interval.hpp:359
CUDA NI static constexpr bool is_supported_fun(Sig sig)
Definition interval.hpp:315
static constexpr const bool sequential
Definition interval.hpp:43
CUDA constexpr bool extract(Interval< A > &ua) const
Definition interval.hpp:274
CUDA static constexpr local_type reverse(const Interval< L > &x)
Definition interval.hpp:348
CUDA constexpr Interval()
Definition interval.hpp:63
CUDA constexpr UB & ub()
Definition interval.hpp:186
CUDA constexpr this_type & tell(const Interval< A > &other)
Definition interval.hpp:227
static constexpr const bool injective_concretization
Definition interval.hpp:49
CUDA static constexpr local_type mod(const Interval< L > &l, const Interval< K > &k)
Definition interval.hpp:572
static constexpr const bool preserve_concrete_covers
Definition interval.hpp:50
CUDA constexpr this_type & dtell_bot()
Definition interval.hpp:232
CUDA constexpr this_type & dtell_lb(const A &lb)
Definition interval.hpp:250
CUDA constexpr this_type & tell_lb(const A &lb, BInc< M > &has_changed)
Definition interval.hpp:197
CUDA static constexpr local_type neg(const Interval< L > &x)
Definition interval.hpp:353
typename CP::memory_type memory_type
Definition interval.hpp:37
CUDA static constexpr local_type fun(const Interval< L > &x)
Definition interval.hpp:373
static constexpr const bool is_totally_ordered
Definition interval.hpp:44
U LB
Definition interval.hpp:31
static constexpr const char * name
Definition interval.hpp:52
CUDA static constexpr local_type eq_zero()
Definition interval.hpp:88
static constexpr const bool preserve_top
Definition interval.hpp:46
CUDA constexpr this_type & dtell(const Interval< A > &other, BInc< M > &has_changed)
Definition interval.hpp:262
static constexpr const bool preserve_join
Definition interval.hpp:47
CUDA constexpr Interval(const typename U::value_type &x)
Definition interval.hpp:65
CUDA constexpr const UB & ub() const
Definition interval.hpp:189
CUDA constexpr this_type & dtell_ub(const A &ub, BInc< M > &has_changed)
Definition interval.hpp:244
CUDA constexpr this_type & tell_lb(const A &lb)
Definition interval.hpp:209
CUDA constexpr this_type & dtell_ub(const A &ub)
Definition interval.hpp:256
CUDA constexpr this_type & tell_top()
Definition interval.hpp:191
CUDA NI void print() const
Definition interval.hpp:307
static constexpr const bool preserve_meet
Definition interval.hpp:48
Interval< typename LB::local_type > local_type
Definition interval.hpp:34
CUDA constexpr Interval(const LB &lb, const UB &ub)
Definition interval.hpp:66
CUDA static constexpr local_type mul(const Interval< L > &l, const Interval< K > &k)
Definition interval.hpp:436
CUDA static constexpr local_type top()
Definition interval.hpp:93
CUDA static constexpr local_type add(const Interval< L > &x, const Interval< K > &y)
Definition interval.hpp:385
static constexpr const bool is_abstract_universe
Definition interval.hpp:42
CUDA constexpr local::BInc is_top() const
Definition interval.hpp:94
CUDA static constexpr local_type div(const Interval< L > &l, const Interval< K > &k)
Definition interval.hpp:502
CUDA static NI bool interpret_ask(const F &f, const Env &env, Interval< U2 > &k, IDiagnostics &diagnostics)
Definition interval.hpp:141
CUDA constexpr local_type width() const
Definition interval.hpp:612
static constexpr const bool complemented
Definition interval.hpp:51
CUDA constexpr local_type median() const
Definition interval.hpp:619
CUDA constexpr value_type value() const
Definition interval.hpp:97
CUDA static constexpr local_type pow(const Interval< L > &l, const Interval< K > &k)
Definition interval.hpp:585
friend class Interval
Definition interval.hpp:40
CUDA constexpr local::BDec is_bot() const
Definition interval.hpp:95
CUDA static constexpr local_type fun(const Interval< L > &x, const Interval< K > &y)
Definition interval.hpp:598
CUDA NI F deinterpret() const
Definition interval.hpp:298
CUDA TFormula< typename Env::allocator_type > deinterpret(AVar x, const Env &env) const
Definition interval.hpp:279
CUDA static constexpr local_type sub(const Interval< L > &x, const Interval< K > &y)
Definition interval.hpp:390
CUDA constexpr LB & lb()
Definition interval.hpp:185
CUDA constexpr Interval(const Interval< A > &other)
Definition interval.hpp:69
CUDA constexpr Interval(Interval< A > &&other)
Definition interval.hpp:72
CUDA constexpr this_type & tell_ub(const A &ub, BInc< M > &has_changed)
Definition interval.hpp:203
CUDA constexpr this_type & tell(const Interval< A > &other, BInc< M > &has_changed)
Definition interval.hpp:221
CUDA constexpr this_type & tell_ub(const A &ub)
Definition interval.hpp:215
CUDA constexpr this_type & operator=(const Interval< A > &other)
Definition interval.hpp:77
CUDA constexpr this_type & dtell(const Interval< A > &other)
Definition interval.hpp:268
CUDA static NI bool interpret_tell(const F &f, const Env &env, Interval< U2 > &k, IDiagnostics &diagnostics)
Definition interval.hpp:123
CUDA constexpr const CP & as_product() const
Definition interval.hpp:96
CUDA constexpr const LB & lb() const
Definition interval.hpp:188
static constexpr const bool preserve_bot
Definition interval.hpp:45
Definition primitive_upset.hpp:118
Definition ast.hpp:234
#define CALL_WITH_ERROR_CONTEXT(MSG, CALL)
Definition diagnostics.hpp:180
#define CALL_WITH_ERROR_CONTEXT_WITH_MERGE(MSG, CALL, MERGE)
Definition diagnostics.hpp:167
Definition abstract_deps.hpp:14
std::ostream & operator<<(std::ostream &s, const CartesianProduct< A, As... > &cp)
Definition cartesian_product.hpp:597
battery::vector< battery::tuple< F, F >, typename F::allocator_type > logic_set
Definition sort.hpp:127
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
@ TMOD
Truncated division, present in most programming languages, is defined as , i.e., it rounds towards ze...
Definition ast.hpp:102
@ EQ
Unary arithmetic function symbols.
Definition ast.hpp:112
@ ADD
Unary arithmetic function symbols.
Definition ast.hpp:97
@ IN
Set membership predicate.
Definition ast.hpp:108
@ POW
Unary arithmetic function symbols.
Definition ast.hpp:97
@ MIN
Unary arithmetic function symbols.
Definition ast.hpp:97
@ EDIV
Unary arithmetic function symbols.
Definition ast.hpp:105
@ FMOD
Floor division (Knuth D. (1972). The Art of Computer Programming, Vol 1, Fundamental Algorithms),...
Definition ast.hpp:103
@ MAX
Binary arithmetic function symbols.
Definition ast.hpp:97
@ TDIV
Unary arithmetic function symbols.
Definition ast.hpp:102
@ FDIV
Unary arithmetic function symbols.
Definition ast.hpp:103
@ ABS
Unary arithmetic function symbols.
Definition ast.hpp:96
@ SUB
Unary arithmetic function symbols.
Definition ast.hpp:97
@ MUL
Unary arithmetic function symbols.
Definition ast.hpp:97
@ CMOD
Ceil division is defined as . Modulus is defined as .
Definition ast.hpp:104
@ CDIV
Unary arithmetic function symbols.
Definition ast.hpp:104
@ NEG
Unary arithmetic function symbols.
Definition ast.hpp:96
@ EMOD
Euclidean division (Boute T. R. (1992). The Euclidean definition of the functions div and mod)....
Definition ast.hpp:105
CUDA constexpr auto join(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition cartesian_product.hpp:534
CUDA Sig leq_of_constant(const TFormula< Allocator > &f)
Definition algorithm.hpp:60
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 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
CUDA Sig geq_of_constant(const TFormula< Allocator > &f)
Definition algorithm.hpp:53
CUDA NI constexpr bool is_modulo(Sig sig)
Definition ast.hpp:200
#define UNTYPED
Definition sort.hpp:21