Lattice Land Core Library
Loading...
Searching...
No Matches
pre_zub.hpp
Go to the documentation of this file.
1// Copyright 2022 Pierre Talbot
2
3#ifndef LALA_CORE_PRE_ZUB_HPP
4#define LALA_CORE_PRE_ZUB_HPP
5
6#include "../logic/logic.hpp"
7
8namespace lala {
9
10template<class VT>
11struct PreZLB;
12
13/** `PreZUB` is a pre-abstract universe \f$ \langle \{-\infty, \ldots, -2, -1, 0, 1, 2, \ldots, \infty\}, \leq \rangle \f$ totally ordered by the natural arithmetic comparison operator.
14 It is used to represent constraints of the form \f$ x \leq k \f$ where \f$ k \f$ is an integer.
15*/
16template<class VT>
17struct PreZUB {
20 using value_type = VT;
23
24 static_assert(std::is_integral_v<value_type> /* && std::is_signed_v<value_type> */, "PreZUB only works over signed integer types.");
25
26 constexpr static const bool is_totally_ordered = true;
27
28 /** `true` if \f$ \gamma(\bot) = \{\} \f$. */
29 constexpr static const bool preserve_bot = true;
30
31 /** `true` if \f$ \gamma(\top) = U \f$. */
32 constexpr static const bool preserve_top = true;
33
34 /** `true` if \f$ \gamma(a \sqcup b) = \gamma(a) \cup \gamma(b) \f$ .*/
35 constexpr static const bool preserve_join = true;
36
37 /** `true` if \f$ \gamma(a \sqcap b) = \gamma(a) \cap \gamma(b) \f$ .*/
38 constexpr static const bool preserve_meet = true;
39
40 /** The concretization is injective when each abstract element maps to a distinct concrete element.
41 This is important for the correctness of `prev` and `next` because we suppose \f$ \gamma(x) != \gamma(\mathit{next}(x)) \f$ when \f$ x \neq \bot \land x \neq \top \f$. */
42 constexpr static const bool injective_concretization = true;
43
44 /** `true` if concrete covers are preserved by the concretization function, i.e., \f$ \gamma(\mathit{next}(x)) \f$ is a cover of \f$ \gamma(x) \f$, and dually for \f$ \mathit{prev}(x) \f$.
45 * \remark `preserve_concrete_covers` implies `injective_concretization`.
46 */
47 constexpr static const bool preserve_concrete_covers = true;
48
49 /** `true` if this lattice aims to represents lower bounds of concrete sets. */
50 constexpr static const bool is_lower_bound = false;
51 constexpr static const bool is_upper_bound = true;
52
53 constexpr static const char* name = "ZUB";
54
55 constexpr static const bool is_arithmetic = true;
56 CUDA constexpr static value_type zero() { return 0; }
57 CUDA constexpr static value_type one() { return 1; }
58
59private:
60 template<bool diagnose, bool is_tell, bool dualize, class F>
61 CUDA NI static bool interpret(const F& f, value_type& k, IDiagnostics& diagnostics) {
62 if(f.is(F::Z)) {
63 auto z = f.z();
64 if(z == bot() || z == top()) {
65 RETURN_INTERPRETATION_ERROR("Constant of sort `Int` with the minimal or maximal representable value of the underlying integer type. We use those values to model negative and positive infinities. Example: Suppose we use a byte type, `x >= 256` is interpreted as `x >= INF` which is always false and thus is different from the intended constraint.");
66 }
67 k = z;
68 return true;
69 }
70 else if(f.is(F::R)) {
71 if constexpr(dualize) {
72 if constexpr(is_tell) {
73 k = battery::ru_cast<value_type>(battery::get<0>(f.r()));
74 }
75 else {
76 k = battery::ru_cast<value_type>(battery::get<1>(f.r()));
77 }
78 }
79 else {
80 if constexpr(is_tell) {
81 k = battery::rd_cast<value_type>(battery::get<1>(f.r()));
82 }
83 else {
84 k = battery::rd_cast<value_type>(battery::get<0>(f.r()));
85 }
86 }
87 return true;
88 }
89 else if(f.is(F::B)) {
90 k = f.b() ? one() : zero();
91 return true;
92 }
93 RETURN_INTERPRETATION_ERROR("Only constants of sorts `Int`, `Bool` and `Real` can be interpreted by an integer abstract universe.");
94 }
95
96public:
97 /** Interpret a constant in the lattice of increasing integers according to the downset semantics.
98 Overflows are not verified.
99 Interpretations:
100 * Formulas of kind `F::Z` are interpreted exactly: \f$ [\![ x:\mathbb{Z} \leq k:\mathbb{Z} ]\!] = k \f$.
101 * Formulas of kind `F::R` are over-approximated: \f$ [\![ x:\mathbb{Z} \leq [l..u]:\mathbb{R} ]\!] = \lfloor u \rfloor \f$.
102 Examples:
103 * \f$ [\![x <= [3.5..3.5]:R ]\!] = 3 \f$: there is no integer greater than 3 satisfying this constraint.
104 * \f$ [\![x <= [2.9..3.1]:R ]\!] = 3 \f$.
105 */
106 template<bool diagnose, class F, bool dualize = false>
107 CUDA static bool interpret_tell(const F& f, value_type& tell, IDiagnostics& diagnostics) {
108 return interpret<diagnose, true, dualize>(f, tell, diagnostics);
109 }
110
111 /** Similar to `interpret_tell` but the formula is under-approximated, in particular: \f$ [\![ x:\mathbb{Z} \leq [l..u]:\mathbb{R} ]\!] = \lfloor u \rfloor \f$.
112 Examples:
113 * \f$ [\![x <= [3.5..3.5]:R ]\!] = 3 \f$.
114 * \f$ [\![x <= [2.9..3.1]:R ]\!] = 2 \f$: the constraint is entailed only when x is less or equal to 2.9. */
115 template<bool diagnose, class F, bool dualize = false>
116 CUDA static bool interpret_ask(const F& f, value_type& ask, IDiagnostics& diagnostics) {
117 return interpret<diagnose, false, dualize>(f, ask, diagnostics);
118 }
119
120 /** Verify if the type of a variable, introduced by an existential quantifier, is compatible with the current abstract universe.
121 Variables of type `Int` are interpreted exactly (\f$ \mathbb{Z} = \gamma(\top) \f$).
122 Note that we assume there is no overflow, that might be taken into account the future. */
123 template<bool diagnose, class F, bool dualize = false>
124 CUDA NI static bool interpret_type(const F& f, value_type& k, IDiagnostics& diagnostics) {
125 assert(f.is(F::E));
126 const auto& sort = battery::get<1>(f.exists());
127 if(sort.is_int()) {
128 k = dualize ? bot() : top();
129 return true;
130 }
131 else {
132 const auto& vname = battery::get<0>(f.exists());
133 RETURN_INTERPRETATION_ERROR("The type of `" + vname + "` can only be `Int`.")
134 }
135 }
136
137 /** Given an Integer value, create a logical constant representing that value.
138 * Note that the lattice order has no influence here.
139 * \pre `v != bot()` and `v != top()`.
140 */
141 template<class F>
142 CUDA static F deinterpret(const value_type& v) {
143 return F::make_z(v);
144 }
145
146 /** The logical predicate symbol corresponding to the order of this pre-universe.
147 We have \f$ a \leq_\mathit{ZUB} b \Leftrightarrow a \leq b \f$.
148 \return The logical symbol `LEQ`. */
149 CUDA static constexpr Sig sig_order() { return LEQ; }
150
151 /** The logical predicate symbol corresponding to the strict order of this pre-universe.
152 We have \f$ a <_\mathit{ZUB} b \Leftrightarrow a < b \f$.
153 \return The logical symbol `LT`. */
154 CUDA static constexpr Sig sig_strict_order() { return LT; }
155
156private:
157 CUDA INLINE static constexpr value_type inf() {
158 return battery::limits<value_type>::inf();
159 }
160
161 CUDA INLINE static constexpr value_type neg_inf() {
162 return battery::limits<value_type>::neg_inf();
163 }
164
165public:
166
167 /** \f$ \bot \f$ is represented by the minimal representable value of the underlying value type. */
168 CUDA INLINE static constexpr value_type bot() {
169 return neg_inf();
170 }
171
172 /** \f$ \top \f$ is represented by the maximal representable value of the underlying value type. */
173 CUDA INLINE static constexpr value_type top() {
174 return inf();
175 }
176
177 /** \return \f$ x \sqcup y \f$ defined as \f$ \mathit{max}(x, y) \f$. */
178 CUDA INLINE static constexpr value_type join(value_type x, value_type y) { return battery::max(x, y); }
179
180 /** \return \f$ x \sqcap y \f$ defined as \f$ \mathit{min}(x, y) \f$. */
181 CUDA INLINE static constexpr value_type meet(value_type x, value_type y) { return battery::min(x, y); }
182
183 /** \return \f$ \mathit{true} \f$ if \f$ x \leq_\mathit{ZUB} y \f$ where the order \f$ \leq_\mathit{ZUB} \f$ is the natural arithmetic ordering, otherwise returns \f$ \mathit{false} \f$. */
184 CUDA INLINE static constexpr bool order(value_type x, value_type y) { return x <= y; }
185
186 /** \return \f$ \mathit{true} \f$ if \f$ x <_\mathit{ZUB} y \f$ where the order \f$ <_\mathit{ZUB} \f$ is the natural arithmetic ordering, otherwise returns \f$ \mathit{false} \f$. */
187 CUDA INLINE static constexpr bool strict_order(value_type x, value_type y) { return x < y; }
188
189 /** From a lattice perspective, `next: ZUB -> ZUB` returns an element `next(x)` such that `x` is covered by `next(x)`.
190 \param x The element covering the returned element.
191 \return The next value of `x` in the discrete increasing chain `bot, ..., -2, -1, 0, 1, ..., top`.
192 \remark The next value of `bot` is `bot` and the next value of `top` is `top`.
193 \remark There is no element \f$ x \neq \top \f$ such that \f$ \mathit{next}(x) = \top \f$, but it can occur in case of overflow which is not checked. */
194 CUDA static constexpr value_type next(value_type x) {
195 return x + has_not_inf(x);
196 }
197
198 /** From a lattice perspective, `prev: ZUB -> ZUB` returns an element `prev(x)` such that `x` covers `prev(x)`.
199 \param x The element covered by the returned element.
200 \return The previous value of `x` in the discrete increasing chain `bot, ..., -2, -1, 0, 1, ..., top`.
201 \remark The previous value of `bot` is `bot` and the previous value of `top` is `top`.
202 \remark There is no element \f$ x \neq \bot \f$ such that \f$ \mathit{prev}(x) = \bot \f$, but it can occur in case of overflow which is not checked. */
203 CUDA static constexpr value_type prev(value_type x)
204 {
205 return x - has_not_inf(x);
206 }
207
208private:
209 CUDA INLINE static constexpr bool has_not_inf(value_type x) {
210 return x != inf() && x != neg_inf();
211 }
212
213 CUDA INLINE static constexpr bool has_inf(value_type x) {
214 return x == inf() || x == neg_inf();
215 }
216
217 CUDA INLINE static constexpr int sign(value_type x) {
218 return battery::signum(x) + 1;
219 }
220
221 // Little bitwise trick to know if one of x or y is -inf or inf.
222 CUDA INLINE static constexpr bool has_inf(value_type x, value_type y) {
223 return has_inf(x) || has_inf(y);
224 }
225
226 template<bool dualize>
227 CUDA static constexpr value_type dtop() {
228 return dualize ? bot() : top();
229 }
230
231public:
232 template <bool dualize>
233 CUDA static constexpr value_type dproject(Sig fun, value_type x) {
234 switch(fun) {
235 case NEG: return has_inf(x) ? x : -x;
236 default: return dtop<dualize>();
237 }
238 }
239
240 /** `project: value_type -> value_type` is an abstract function on `ZUB` over-approximating the function denoted by `fun` on the concrete domain.
241 * \tparam fun The signature of the function to over-approximate (only `NEG`).
242 * \param x The argument of the function, which is a constant value in the underlying universe of discourse, or top().
243 */
244 CUDA static constexpr value_type project(Sig fun, value_type x) {
245 return dproject<false>(fun, x);
246 }
247
248private:
249 CUDA static constexpr value_type infs(size_t i, size_t j) {
250 /** Look-up table for multiplication (division) between x * y (x / y) in presence of infinities. */
251 const value_type values[3][3] = { // static doesn't work with constexpr functions.
252 // y < 0, y == 0, y > 0
253 /* x < 0 */ {inf(), 0, neg_inf()},
254 /* x = 0 */ {0, 0, 0},
255 /* x > 0 */ {neg_inf(), 0, inf()}
256 };
257 return values[i][j];
258 }
259
260public:
261 /** The projection of a function `fun` on two arguments `x` and `y` is simply the standard application of the operation, with specific cases for infinities.
262 * When the function `fun` is not supported, we return `top()`.
263 *
264 * We deal with infinities in the "standard" way. The rules below are tried in order (so we apply inf - X even if X is inf or -inf).
265 * For addition: inf + X = inf, -inf + X = -inf, X + inf = inf, X + -inf = -inf.
266 * For subtraction: inf - X = inf, -inf - X = -inf, X - inf = -inf, X - -inf = inf.
267 * For multiplication and division, we look at the sign (see the table `infs`).
268 * For division, we expect y != 0 (but it returns 0 if it happens).
269 * For division, when dividing by infinity, it actually depends on whether we are computing a lower or upper bound and the sign of infinity; not sure of the exact rules so we overapproximate to top.
270 * For min and max, it is trivial.
271 * For modulus and ipow, we expect x and y to be non-infinities.
272 */
273 template <bool dualize>
274 CUDA static constexpr value_type dproject(Sig fun, value_type x, value_type y) {
275 switch(fun) {
276 case ADD: return has_inf(x, y) ? (has_inf(x) ? x : y) : x + y;
277 case SUB: return has_inf(x, y) ? (has_inf(x) ? x : -y) : x - y;
278 case MUL: return has_inf(x, y) ? infs(sign(x), sign(y)) : x * y;
279 // Truncated division and modulus, by default in C++.
280 case TDIV: return has_inf(x, y) ? (has_inf(x) ? infs(sign(x), sign(y)) : dtop<dualize>()) : x / y;
281 case TMOD: return x % y;
282 // Floor division and modulus, see (Leijen D. (2003). Division and Modulus for Computer Scientists).
283 case FDIV: return has_inf(x, y) ? (has_inf(x) ? infs(sign(x), sign(y)) : dtop<dualize>()) : battery::fdiv(x, y);
284 case FMOD: return battery::fmod(x, y);
285 // Ceil division and modulus.
286 case CDIV: return has_inf(x, y) ? (has_inf(x) ? infs(sign(x), sign(y)) : dtop<dualize>()) : battery::cdiv(x, y);
287 case CMOD: return battery::cmod(x, y);
288 // Euclidean division and modulus, see (Leijen D. (2003). Division and Modulus for Computer Scientists).
289 case EDIV: return has_inf(x, y) ? (has_inf(x) ? infs(sign(x), sign(y)) : dtop<dualize>()) : battery::ediv(x, y);
290 case EMOD: return battery::emod(x, y);
291 case POW: return battery::ipow(x, y);
292 case MIN: return battery::min(x, y);
293 case MAX: return battery::max(x, y);
294 case EQ: return x == y;
295 case NEQ: return x != y;
296 case LEQ: return x <= y;
297 case GEQ: return x >= y;
298 case LT: return x < y;
299 case GT: return x >= y;
300 default: return dtop<dualize>();
301 }
302 }
303
304 CUDA static constexpr value_type project(Sig fun, value_type x, value_type y) {
305 return dproject<false>(fun, x, y);
306 }
307};
308
309} // namespace lala
310
311#endif
Definition diagnostics.hpp:19
#define RETURN_INTERPRETATION_ERROR(MSG)
Definition diagnostics.hpp:155
Definition abstract_deps.hpp:14
Sig
Definition ast.hpp:94
@ NEQ
Equality relations.
Definition ast.hpp:112
@ LEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ 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
@ GEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ ADD
Unary arithmetic function symbols.
Definition ast.hpp:97
@ 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
@ GT
Arithmetic comparison predicates. When applied to set, it corresponds to the lexicographic ordering o...
Definition ast.hpp:113
@ 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
@ LT
Unary arithmetic function symbols.
Definition ast.hpp:113
@ TDIV
Unary arithmetic function symbols.
Definition ast.hpp:102
@ FDIV
Unary arithmetic function symbols.
Definition ast.hpp:103
@ 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
Definition pre_zub.hpp:11
Definition pre_zub.hpp:17
static CUDA bool interpret_ask(const F &f, value_type &ask, IDiagnostics &diagnostics)
Definition pre_zub.hpp:116
CUDA static INLINE constexpr value_type top()
Definition pre_zub.hpp:173
static constexpr const bool is_totally_ordered
Definition pre_zub.hpp:26
static CUDA bool interpret_tell(const F &f, value_type &tell, IDiagnostics &diagnostics)
Definition pre_zub.hpp:107
CUDA static INLINE constexpr bool order(value_type x, value_type y)
Definition pre_zub.hpp:184
CUDA static INLINE constexpr bool strict_order(value_type x, value_type y)
Definition pre_zub.hpp:187
static CUDA constexpr Sig sig_order()
Definition pre_zub.hpp:149
static constexpr const bool preserve_bot
Definition pre_zub.hpp:29
CUDA static NI bool interpret_type(const F &f, value_type &k, IDiagnostics &diagnostics)
Definition pre_zub.hpp:124
static CUDA constexpr value_type next(value_type x)
Definition pre_zub.hpp:194
static CUDA constexpr value_type dproject(Sig fun, value_type x, value_type y)
Definition pre_zub.hpp:274
static constexpr const bool preserve_meet
Definition pre_zub.hpp:38
CUDA static constexpr value_type one()
Definition pre_zub.hpp:57
static constexpr const char * name
Definition pre_zub.hpp:53
CUDA static INLINE constexpr value_type join(value_type x, value_type y)
Definition pre_zub.hpp:178
static CUDA constexpr value_type prev(value_type x)
Definition pre_zub.hpp:203
PreZLB< VT > dual_type
Definition pre_zub.hpp:19
CUDA static INLINE constexpr value_type bot()
Definition pre_zub.hpp:168
static CUDA constexpr value_type dproject(Sig fun, value_type x)
Definition pre_zub.hpp:233
static CUDA F deinterpret(const value_type &v)
Definition pre_zub.hpp:142
static constexpr const bool injective_concretization
Definition pre_zub.hpp:42
VT value_type
Definition pre_zub.hpp:20
static constexpr const bool preserve_concrete_covers
Definition pre_zub.hpp:47
static CUDA constexpr value_type project(Sig fun, value_type x)
Definition pre_zub.hpp:244
CUDA static INLINE constexpr value_type meet(value_type x, value_type y)
Definition pre_zub.hpp:181
static constexpr const bool preserve_join
Definition pre_zub.hpp:35
static constexpr const bool is_upper_bound
Definition pre_zub.hpp:51
static constexpr const bool is_lower_bound
Definition pre_zub.hpp:50
static CUDA constexpr Sig sig_strict_order()
Definition pre_zub.hpp:154
static constexpr const bool preserve_top
Definition pre_zub.hpp:32
PreZUB< VT > this_type
Definition pre_zub.hpp:18
static constexpr const bool is_arithmetic
Definition pre_zub.hpp:55
static CUDA constexpr value_type project(Sig fun, value_type x, value_type y)
Definition pre_zub.hpp:304
CUDA static constexpr value_type zero()
Definition pre_zub.hpp:56