Lattice Land Core Library
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 
8 namespace lala {
9 
10 template<class VT>
11 struct 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 */
16 template<class VT>
17 struct 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 
59 private:
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 
96 public:
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 
156 private:
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 
165 public:
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 
208 private:
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 
231 public:
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 
248 private:
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 
260 public:
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
::lala::B<::battery::local_memory > B
Definition: b.hpp:12
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_zlb.hpp:18
Definition: pre_zub.hpp:17
constexpr static const char * name
Definition: pre_zub.hpp:53
static CUDA bool interpret_ask(const F &f, value_type &ask, IDiagnostics &diagnostics)
Definition: pre_zub.hpp:116
static constexpr CUDA Sig sig_order()
Definition: pre_zub.hpp:149
static CUDA bool interpret_tell(const F &f, value_type &tell, IDiagnostics &diagnostics)
Definition: pre_zub.hpp:107
CUDA static constexpr INLINE value_type join(value_type x, value_type y)
Definition: pre_zub.hpp:178
constexpr static const bool preserve_meet
Definition: pre_zub.hpp:38
CUDA static NI bool interpret_type(const F &f, value_type &k, IDiagnostics &diagnostics)
Definition: pre_zub.hpp:124
constexpr static const bool preserve_bot
Definition: pre_zub.hpp:29
constexpr static const bool preserve_top
Definition: pre_zub.hpp:32
static constexpr CUDA value_type project(Sig fun, value_type x, value_type y)
Definition: pre_zub.hpp:304
PreZLB< VT > dual_type
Definition: pre_zub.hpp:19
constexpr static const bool is_totally_ordered
Definition: pre_zub.hpp:26
constexpr static CUDA value_type zero()
Definition: pre_zub.hpp:56
constexpr static const bool is_arithmetic
Definition: pre_zub.hpp:55
static constexpr CUDA value_type dproject(Sig fun, value_type x, value_type y)
Definition: pre_zub.hpp:274
static constexpr CUDA Sig sig_strict_order()
Definition: pre_zub.hpp:154
static CUDA F deinterpret(const value_type &v)
Definition: pre_zub.hpp:142
static constexpr CUDA value_type project(Sig fun, value_type x)
Definition: pre_zub.hpp:244
VT value_type
Definition: pre_zub.hpp:20
static constexpr CUDA value_type dproject(Sig fun, value_type x)
Definition: pre_zub.hpp:233
CUDA static constexpr INLINE value_type top()
Definition: pre_zub.hpp:173
constexpr static const bool is_lower_bound
Definition: pre_zub.hpp:50
constexpr static const bool injective_concretization
Definition: pre_zub.hpp:42
constexpr static const bool preserve_join
Definition: pre_zub.hpp:35
static constexpr CUDA value_type next(value_type x)
Definition: pre_zub.hpp:194
CUDA static constexpr INLINE bool order(value_type x, value_type y)
Definition: pre_zub.hpp:184
constexpr static CUDA value_type one()
Definition: pre_zub.hpp:57
constexpr static const bool is_upper_bound
Definition: pre_zub.hpp:51
CUDA static constexpr INLINE value_type meet(value_type x, value_type y)
Definition: pre_zub.hpp:181
PreZUB< VT > this_type
Definition: pre_zub.hpp:18
constexpr static const bool preserve_concrete_covers
Definition: pre_zub.hpp:47
static constexpr CUDA value_type prev(value_type x)
Definition: pre_zub.hpp:203
CUDA static constexpr INLINE value_type bot()
Definition: pre_zub.hpp:168
CUDA static constexpr INLINE bool strict_order(value_type x, value_type y)
Definition: pre_zub.hpp:187