Lattice Land Core Library
ast.hpp
Go to the documentation of this file.
1 // Copyright 2021 Pierre Talbot
2 
3 #ifndef LALA_CORE_AST_HPP
4 #define LALA_CORE_AST_HPP
5 
6 #include "battery/utility.hpp"
7 #include "battery/vector.hpp"
8 #include "battery/string.hpp"
9 #include "battery/tuple.hpp"
10 #include "battery/variant.hpp"
11 #include "battery/shared_ptr.hpp"
12 #include "battery/unique_ptr.hpp"
13 #include "sort.hpp"
14 #include <optional>
15 
16 #ifdef _WINDOWS
17 # undef DIFFERENCE // Avoid clash with #define in WinUser.h
18 # undef IN // Avoid clash with #define in ntdef.h
19 #endif
20 
21 namespace lala {
22 
23 /** A "logical variable" is just the name of the variable. */
24 template<class Allocator>
25 using LVar = battery::string<Allocator>;
26 
27 /** The kind of interpretation operations. */
28 enum class IKind {
29  ASK,
30  TELL
31 };
32 
33 /** We call an "abstract variable" the representation of a logical variable in an abstract domain.
34 It is a pair of integers `(aty, vid)` where `aty` is the UID of the abstract element and `vid` is an internal identifier of the variable inside the abstract element.
35 The mapping between logical variables and abstract variables is maintained in an environment (see `env.hpp`).
36 An abstract variable always has a single name (or no name if it is not explicitly represented in the initial formula).
37 But a logical variable can be represented by several abstract variables when the variable occurs in different abstract elements. */
38 class AVar {
39  int value;
40 public:
41  CUDA constexpr AVar(): value(-1) {}
42  constexpr AVar(const AVar&) = default;
43  constexpr AVar(AVar&&) = default;
44  constexpr AVar& operator=(const AVar&) = default;
45  constexpr AVar& operator=(AVar&&) = default;
46  CUDA constexpr bool operator==(const AVar& other) const {
47  return value == other.value;
48  }
49  CUDA constexpr bool operator!=(const AVar& other) const {
50  return value != other.value;
51  }
52 
53  CUDA constexpr AVar(AType atype, int var_id): value((var_id << 8) | atype) {
54  assert(atype >= 0);
55  assert(var_id >= 0);
56  assert(atype < (1 << 8));
57  assert(var_id < (1 << 23));
58  }
59  CUDA constexpr AVar(AType atype, size_t var_id): AVar(atype, static_cast<int>(var_id)) {}
60 
61  CUDA constexpr bool is_untyped() const {
62  return value == -1;
63  }
64 
65  CUDA constexpr int aty() const {
66  return is_untyped() ? -1 : (value & ((1 << 8) - 1));
67  }
68 
69  CUDA constexpr int vid() const {
70  return is_untyped() ? -1 : value >> 8;
71  }
72 
73  CUDA constexpr void type_as(AType aty) {
74  value = (vid() << 8) | aty;
75  }
76 };
77 
78 /** A first-order signature is a triple \f$ (X, F, P) \f$ where \f$ X \f$ is the set of variables, \f$ F \f$ the set of function symbols and \f$ P \f$ the set of predicates.
79  We represent \f$ X \f$ by strings (see `LVar`), while \f$ F \f$ and \f$ P \f$ are described in the following enumeration `Sig`.
80  For programming conveniency, we suppose that logical connectors are included in the set of predicates and thus are in the signature as well.
81  Finally, function symbols and predicates are at the "same level".
82  Hence a predicate can occur as the argument of a function, which is convenient when modelling, consider for example a cardinality constraint: \f$ ((x > 4) + (y < 4) + (z = 3)) \neq 2 \f$.
83 
84  Symbols are sometimes overloaded across different universe of discourse.
85  For instance, `ADD` can be used over integers, reals and even set of integers (pairwise addition).
86 
87  Division and modulus are defined as usual over continuous domains such as rational and real numbers.
88  However, it gets more tricky when defined over discrete domains such as integers and floating-point numbers, since there is not a single definition of division and modulus.
89  The various kinds of discrete divisions are explained in (Leijend D. (2003). Division and Modulus for Computer Scientists), and we add four of those definitions to the logical signature.
90  There are several use-cases of modulus and division:
91  * If you write a constraint model, you probably want to use euclidean division and modulus (EDIV, EMOD) as this is the most "mathematical" definition.
92  * If you intend to model the semantics of a programming language, you should use the same kind of division as the one present in your programming language (most likely truncated division).
93  */
94 enum Sig {
95  ///@{
96  NEG, ABS, ///< Unary arithmetic function symbols.
97  ADD, SUB, MUL, POW, MIN, MAX, ///< Binary arithmetic function symbols.
98  SQRT, EXP, LN, ///< Square root, natural exponential and natural logarithm function symbols.
99  NROOT, LOG, ///< nth root and logarithm to base (both binary function symbols).
100  SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, COSH, TANH, ASINH, ACOSH, ATANH, ///< Trigonometric unary function symbols.
101  DIV, MOD, ///< Division and modulus over continuous domains (e.g., floating-point numbers and rational).
102  TDIV, TMOD, ///< Truncated division, present in most programming languages, is defined as \f$ a\,\mathbf{tdiv}\,b = \mathit{trunc}(a/b) \f$, i.e., it rounds towards zero. Modulus is defined as \f$ a\,\mathbf{tmod}\,b = a - b * (a\,\mathbf{tdiv}\,b) \f$.
103  FDIV, FMOD, ///< Floor division (Knuth D. (1972). The Art of Computer Programming, Vol 1, Fundamental Algorithms), is defined as \f$ a\,\mathbf{fdiv}\,b = \lfloor a/b \rfloor \f$, i.e., it rounds towards negative infinity. Modulus is defined as \f$ a\,\mathbf{fmod}\,b = a - b * (a\,\mathbf{fdiv}\,b) \f$.
104  CDIV, CMOD, ///< Ceil division is defined as \f$ a\,\mathbf{cdiv}\,b = \lceil a/b \rceil \f$. Modulus is defined as \f$ a\,\mathbf{cmod}\,b = a - b * (a\,\mathbf{cdiv}\,b) \f$.
105  EDIV, EMOD, ///< Euclidean division (Boute T. R. (1992). The Euclidean definition of the functions div and mod). The properties satisfy by this division are: (1) \f$ a\,\mathbf{ediv}\,b \in \mathbb{Z} \f$, (2) \f$ a = b * (a\,\mathbf{ediv}\,b) + (a\,\mathbf{emod}\,b) \f$ and (3) \f$ 0 \leq a\,\mathbf{emod}\,b < |b|\f$. Further, note that Euclidean division satisfies \f$ a\,\mathbf{ediv}\,(-b) = -(a\,\mathbf{ediv}\,b) \f$ and \f$ a\,\mathbf{emod}\,(-b) = a\,\mathbf{emod}\,b \f$.
107  SUBSET, SUBSETEQ, SUPSET, SUPSETEQ, ///< Set inclusion predicates.
108  IN, ///< Set membership predicate.
109  CARD, ///< Cardinality function from set to integer.
110  HULL, ///< Unary function performing the convex hull of a set, e.g., \f$ \mathit{hull}(s) = \{x \;|\; \mathit{min}(s) \leq x \leq \mathit{max}(s) \} \f$.
111  CONVEX, ///< Unary predicate, requiring \f$ s = \mathit{hull}(s) \f$.
112  EQ, NEQ, ///< Equality relations.
113  LEQ, GEQ, LT, GT, ///< Arithmetic comparison predicates. When applied to set, it corresponds to the lexicographic ordering of the sorted set according the underlying natural ordering of the elements in the set.
114  AND, OR, IMPLY, EQUIV, NOT, XOR, ///< Logical connector.
115  ITE, ///< If-then-else
116  MAXIMIZE, ///< Unary "meta-predicate" indicating that its argument must be maximized, according to the increasing ordering of the underlying universe of discourse. This is not a predicate because it is defined on the solutions space of the whole formulas.
117  MINIMIZE ///< Same as MAXIMIZE, but for minimization.
118  ///@}
119 };
120 
121 CUDA NI inline const char* string_of_sig(Sig sig) {
122  switch(sig) {
123  case NEG: return "-";
124  case ABS: return "abs";
125  case ADD: return "+";
126  case SUB: return "-";
127  case MUL: return "*";
128  case DIV: return "/";
129  case TDIV: return "tdiv";
130  case FDIV: return "fdiv";
131  case CDIV: return "cdiv";
132  case EDIV: return "ediv";
133  case MOD: return "%%";
134  case TMOD: return "tmod";
135  case FMOD: return "fmod";
136  case CMOD: return "cmod";
137  case EMOD: return "emod";
138  case POW: return "^";
139  case SQRT: return "sqrt";
140  case EXP: return "exp";
141  case LN: return "ln";
142  case NROOT: return "nroot";
143  case LOG: return "log";
144  case SIN: return "sin";
145  case COS: return "cos";
146  case TAN: return "tan";
147  case ASIN: return "asin";
148  case ACOS: return "acos";
149  case ATAN: return "atan";
150  case SINH: return "sinh";
151  case COSH: return "cosh";
152  case TANH: return "tanh";
153  case ASINH: return "asinh";
154  case ACOSH: return "acosh";
155  case ATANH: return "atanh";
156  case MIN: return "min";
157  case MAX: return "max";
158  case UNION: return "\u222A";
159  case INTERSECTION: return "\u2229";
160  case DIFFERENCE: return "\\";
161  case SYMMETRIC_DIFFERENCE: return "\u2296";
162  case COMPLEMENT: return "complement";
163  case IN: return "\u2208";
164  case SUBSET: return "\u2282";
165  case SUBSETEQ: return "\u2286";
166  case SUPSET: return "\u2283";
167  case SUPSETEQ: return "\u2287";
168  case CARD: return "card";
169  case HULL: return "hull";
170  case CONVEX: return "convex";
171  case EQ: return "=";
172  case NEQ: return "\u2260";
173  case LEQ: return "\u2264";
174  case GEQ: return "\u2265";
175  case LT: return "<";
176  case GT: return ">";
177  case AND: return "\u2227";
178  case OR: return "\u2228";
179  case IMPLY: return "\u21D2";
180  case EQUIV: return "\u21D4";
181  case NOT: return "\u00AC";
182  case XOR: return "\u2295";
183  case ITE: return "ite";
184  case MAXIMIZE: return "maximize";
185  case MINIMIZE: return "minimize";
186  default:
187  assert(false);
188  return "<bug! unknown sig>";
189  }
190  }
191 
192  CUDA NI inline constexpr bool is_prefix(Sig sig) {
193  return sig == ABS || sig == SQRT || sig == EXP || sig == LN || sig == NROOT || sig == LOG || sig == SIN || sig == COS || sig == TAN || sig == ASIN || sig == ACOS || sig == ATAN || sig == SINH || sig == COSH || sig == TANH || sig == ASINH || sig == ACOSH || sig == ATANH || sig == MIN || sig == MAX || sig == COMPLEMENT || sig == CARD || sig == HULL || sig == CONVEX || sig == ITE || sig == MAXIMIZE || sig == MINIMIZE;
194  }
195 
196  CUDA NI inline constexpr bool is_division(Sig sig) {
197  return sig == DIV || sig == TDIV || sig == EDIV || sig == FDIV || sig == CDIV;
198  }
199 
200  CUDA NI inline constexpr bool is_modulo(Sig sig) {
201  return sig == MOD || sig == TMOD || sig == EMOD || sig == FMOD || sig == CMOD;
202  }
203 
204  CUDA NI inline constexpr bool is_associative(Sig sig) {
205  return sig == ADD || sig == MUL || sig == AND || sig == OR || sig == EQUIV || sig == XOR
206  || sig == UNION || sig == INTERSECTION || sig == MAX || sig == MIN;
207  }
208 
209  CUDA NI inline constexpr bool is_arithmetic_comparison(Sig sig) {
210  return sig == LEQ || sig == GEQ || sig == EQ || sig == NEQ || sig == GT || sig == LT;
211  }
212 
213  CUDA NI inline constexpr bool is_logical(Sig sig) {
214  return sig == AND || sig == OR || sig == IMPLY || sig == EQUIV || sig == NOT || sig == XOR;
215  }
216 
217  CUDA NI inline constexpr bool is_predicate(Sig sig) {
218  return sig == IN || sig == SUBSET || sig == SUBSETEQ || sig == SUPSET || sig == SUPSETEQ ||
219  sig == EQ || sig == NEQ || sig == LEQ || sig == GEQ || sig == LT || sig == GT;
220  }
221 }
222 
223 namespace battery {
224  template<>
225  CUDA NI inline void print(const lala::Sig& sig) {
226  printf("%s", lala::string_of_sig(sig));
227  }
228 }
229 
230 namespace lala {
231 
232 /** `TFormula` represents the AST of a typed first-order logical formula.
233 In our context, the type of a formula is an integer representing the UID of an abstract domain in which the formula should to be interpreted.
234 It defaults to the value `UNTYPED` if the formula is not (yet) typed.
235 By default, the types of integer and real number are supported.
236 The supported symbols can be extended with the template parameter `ExtendedSig`.
237 This extended signature can also be used for representing exactly constant such as real numbers using a string.
238 The AST of a formula is represented by a variant, where each alternative is described below.
239 We represent everything at the same level (term, formula, predicate, variable, constant).
240 This is generally convenient when modelling to avoid creating intermediate boolean variables when reifying.
241 We can have `x + (x > y \/ y > x + 4) >= 1`.
242 
243 Differently from first-order logic, the existential quantifier does not have a subformula, i.e., we write \f$ \exists{x:Int} \land \exists{y:Int} \land x < y\f$.
244 This semantics comes from "dynamic predicate logic" where a formula is interpreted in a context (here the abstract element).
245 (The exact connection of our framework to dynamic predicate logic is not yet perfectly clear.) */
246 template<class Allocator, class ExtendedSig = battery::string<Allocator>>
247 class TFormula {
248 public:
249  using allocator_type = Allocator;
251  using Sequence = battery::vector<this_type, Allocator>;
252  using Existential = battery::tuple<LVar<Allocator>, Sort<Allocator>>;
254  using Formula = battery::variant<
255  logic_bool, ///< Representation of Booleans.
256  logic_int, ///< Representation of integers.
257  logic_real, ///< Approximation of real numbers.
258  LogicSet, ///< Set of Booleans, integers, reals or sets.
259  AVar, ///< Abstract variable
260  LVar<Allocator>, ///< Logical variable
261  Existential, ///< Existential quantifier
262  battery::tuple<Sig, Sequence>, ///< ADD, SUB, ..., EQ, ..., AND, .., NOT
263  battery::tuple<ExtendedSig, Sequence> ///< see above
264  >;
265 
266  /** Index of Booleans in the variant type `Formula` (called kind below). */
267  static constexpr size_t B = 0;
268 
269  /** Index of integers in the variant type `Formula` (called kind below). */
270  static constexpr size_t Z = B + 1;
271 
272  /** Index of real numbers in the variant type `Formula` (called kind below). */
273  static constexpr size_t R = Z + 1;
274 
275  /** Index of sets in the variant type `Formula` (called kind below). */
276  static constexpr size_t S = R + 1;
277 
278  /** Index of abstract variables in the variant type `Formula` (called kind below). */
279  static constexpr size_t V = S + 1;
280 
281  /** Index of logical variables in the variant type `Formula` (called kind below). */
282  static constexpr size_t LV = V + 1;
283 
284  /** Index of existential quantifier in the variant type `Formula` (called kind below). */
285  static constexpr size_t E = LV + 1;
286 
287  /** Index of n-ary operators in the variant type `Formula` (called kind below). */
288  static constexpr size_t Seq = E + 1;
289 
290  /** Index of n-ary operators where the operator is an extended signature in the variant type `Formula` (called kind below). */
291  static constexpr size_t ESeq = Seq + 1;
292 
293 private:
294  AType type_;
295  Formula formula;
296 
297 public:
298  /** By default, we initialize the formula to `true`. */
299  CUDA TFormula(): type_(UNTYPED), formula(Formula::template create<B>(true)) {}
300  CUDA TFormula(Formula&& formula): type_(UNTYPED), formula(std::move(formula)) {}
301  CUDA TFormula(AType uid, Formula&& formula): type_(uid), formula(std::move(formula)) {}
302 
303  CUDA TFormula(const this_type& other): type_(other.type_), formula(other.formula) {}
304  CUDA TFormula(this_type&& other): type_(other.type_), formula(std::move(other.formula)) {}
305 
306  template <class Alloc2, class ExtendedSig2>
307  friend class TFormula;
308 
309  template <class Alloc2, class ExtendedSig2>
310  CUDA NI TFormula(const TFormula<Alloc2, ExtendedSig2>& other, const Allocator& allocator = Allocator())
311  : type_(other.type_), formula(Formula::template create<B>(true))
312  {
313  switch(other.formula.index()) {
314  case B: formula = Formula::template create<B>(other.b()); break;
315  case Z: formula = Formula::template create<Z>(other.z()); break;
316  case R: formula = Formula::template create<R>(other.r()); break;
317  case S: formula = Formula::template create<S>(LogicSet(other.s(), allocator));
318  break;
319  case V: formula = Formula::template create<V>(other.v()); break;
320  case LV: formula = Formula::template create<LV>(LVar<Allocator>(other.lv(), allocator)); break;
321  case E: formula = Formula::template create<E>(
322  battery::make_tuple(
323  LVar<Allocator>(battery::get<0>(other.exists()), allocator),
324  battery::get<1>(other.exists())));
325  break;
326  case Seq:
327  formula = Formula::template create<Seq>(
328  battery::make_tuple(
329  other.sig(),
330  Sequence(other.seq(), allocator)));
331  break;
332  case ESeq:
333  formula = Formula::template create<ESeq>(
334  battery::make_tuple(
335  ExtendedSig(other.esig(), allocator),
336  Sequence(other.eseq(), allocator)));
337  break;
338  default: printf("print: formula not handled.\n"); assert(false); break;
339  }
340  }
341 
342  CUDA void swap(this_type& other) {
343  ::battery::swap(type_, other.type_);
344  ::battery::swap(formula, other.formula);
345  }
346 
347  CUDA this_type& operator=(const this_type& rhs) {
348  this_type(rhs).swap(*this);
349  return *this;
350  }
351 
353  this_type(std::move(rhs)).swap(*this);
354  return *this;
355  }
356 
357  CUDA Formula& data() { return formula; }
358  CUDA const Formula& data() const { return formula; }
359  CUDA AType type() const { return type_; }
360  CUDA void type_as(AType ty) {
361  type_ = ty;
362  if(is(V)) {
363  v().type_as(ty);
364  }
365  }
366 
367  CUDA constexpr bool is_untyped() const {
368  return type() == UNTYPED;
369  }
370 
371  CUDA NI std::optional<Sort<allocator_type>> sort() const {
372  using sort_t = Sort<allocator_type>;
373  switch(formula.index()) {
374  case B: return sort_t(sort_t::Bool);
375  case Z: return sort_t(sort_t::Int);
376  case R: return sort_t(sort_t::Real);
377  case S: {
378  if(s().empty()) {
379  return {}; // Impossible to get the sort of the empty set.
380  }
381  for(int i = 0; i < s().size(); ++i) {
382  auto subsort = battery::get<0>(s()[i]).sort();
383  if(!subsort.has_value()) {
384  subsort = battery::get<1>(s()[i]).sort();
385  }
386  if(subsort.has_value()) {
387  return sort_t(sort_t::Set, std::move(*subsort) , s().get_allocator());
388  }
389  }
390  break;
391  }
392  case E: return battery::get<1>(exists());
393  }
394  return {};
395  }
396 
397  CUDA static this_type make_true() { return TFormula(); }
398  CUDA static this_type make_false() { return TFormula(Formula::template create<B>(false)); }
399 
400  CUDA static this_type make_bool(logic_bool b, AType atype = UNTYPED) {
401  return this_type(atype, Formula::template create<B>(b));
402  }
403 
404  CUDA static this_type make_z(logic_int i, AType atype = UNTYPED) {
405  return this_type(atype, Formula::template create<Z>(i));
406  }
407 
408  /** Create a term representing a real number which is approximated by interval [lb..ub].
409  By default the real number is supposedly over-approximated. */
410  CUDA static this_type make_real(double lb, double ub, AType atype = UNTYPED) {
411  return this_type(atype, Formula::template create<R>(battery::make_tuple(lb, ub)));
412  }
413 
414  CUDA static this_type make_real(logic_real r, AType atype = UNTYPED) {
415  return this_type(atype, Formula::template create<R>(r));
416  }
417 
418  CUDA static this_type make_set(LogicSet set, AType atype = UNTYPED) {
419  return this_type(atype, Formula::template create<S>(std::move(set)));
420  }
421 
422  /** The type of the formula is embedded in `v`. */
423  CUDA static this_type make_avar(AVar v) {
424  return this_type(v.aty(), Formula::template create<V>(v));
425  }
426 
427  CUDA static this_type make_avar(AType ty, int vid) {
428  return make_avar(AVar(ty, vid));
429  }
430 
431  CUDA static this_type make_lvar(AType ty, LVar<Allocator> lvar) {
432  return this_type(ty, Formula::template create<LV>(std::move(lvar)));
433  }
434 
436  return this_type(ty, Formula::template create<E>(battery::make_tuple(std::move(lvar), std::move(ctype))));
437  }
438 
439  /** If `flatten` is `true` it will try to merge the children together to avoid nested formula. */
440  CUDA NI static this_type make_nary(Sig sig, Sequence children, AType atype = UNTYPED, bool flatten=true)
441  {
442  if(is_associative(sig) && flatten) {
443  Sequence seq{children.get_allocator()};
444  for(size_t i = 0; i < children.size(); ++i) {
445  if(children[i].is(Seq) && children[i].sig() == sig && children[i].type() == atype) {
446  for(size_t j = 0; j < children[i].seq().size(); ++j) {
447  seq.push_back(std::move(children[i].seq(j)));
448  }
449  }
450  else {
451  seq.push_back(std::move(children[i]));
452  }
453  }
454  return this_type(atype, Formula::template create<Seq>(battery::make_tuple(sig, std::move(seq))));
455  }
456  else {
457  return this_type(atype, Formula::template create<Seq>(battery::make_tuple(sig, std::move(children))));
458  }
459  /** The following code leads to bugs (accap_a4 segfaults, wordpress7_500 gives wrong answers).
460  * I don't know why, but it seems that the simplification is not always correct, perhaps in reified context?
461  * Or perhaps it is a question of atype? */
462  // if(seq.size() == 0) {
463  // if(sig == AND) return make_true();
464  // if(sig == OR) return make_false();
465  // }
466  // if(seq.size() == 1 && (sig == AND || sig == OR)) {
467  // return std::move(seq[0]);
468  // }
469  }
470 
471  CUDA static this_type make_unary(Sig sig, TFormula child, AType atype = UNTYPED, const Allocator& allocator = Allocator()) {
472  return make_nary(sig, Sequence({std::move(child)}, allocator), atype);
473  }
474 
475  CUDA static this_type make_binary(TFormula lhs, Sig sig, TFormula rhs, AType atype = UNTYPED, const Allocator& allocator = Allocator(), bool flatten=true) {
476  return make_nary(sig, Sequence({std::move(lhs), std::move(rhs)}, allocator), atype, flatten);
477  }
478 
479  CUDA static this_type make_nary(ExtendedSig esig, Sequence children, AType atype = UNTYPED) {
480  return this_type(atype, Formula::template create<ESeq>(battery::make_tuple(std::move(esig), std::move(children))));
481  }
482 
483  CUDA size_t index() const {
484  return formula.index();
485  }
486 
487  CUDA bool is(size_t kind) const {
488  return formula.index() == kind;
489  }
490 
491  CUDA bool is_true() const {
492  return (is(B) && b()) || (is(Z) && z() != 0);
493  }
494 
495  CUDA bool is_false() const {
496  return (is(B) && !b()) || (is(Z) && z() == 0);
497  }
498 
499  CUDA logic_int to_z() const {
500  assert(is(B) || is(Z));
501  return (is(B) ? (b() ? 1 : 0) : z());
502  }
503 
504  CUDA bool is_constant() const {
505  return is(B) || is(Z) || is(R) || is(S);
506  }
507 
508  CUDA bool is_variable() const {
509  return is(LV) || is(V);
510  }
511 
512  CUDA bool is_unary() const {
513  return is(Seq) && seq().size() == 1;
514  }
515 
516  CUDA bool is_binary() const {
517  return is(Seq) && seq().size() == 2;
518  }
519 
520  CUDA logic_bool b() const {
521  return battery::get<B>(formula);
522  }
523 
524  CUDA logic_int z() const {
525  return battery::get<Z>(formula);
526  }
527 
528  CUDA const logic_real& r() const {
529  return battery::get<R>(formula);
530  }
531 
532  CUDA const LogicSet& s() const {
533  return battery::get<S>(formula);
534  }
535 
536  CUDA AVar v() const {
537  return battery::get<V>(formula);
538  }
539 
540  CUDA const LVar<Allocator>& lv() const {
541  return battery::get<LV>(formula);
542  }
543 
544  CUDA const Existential& exists() const {
545  return battery::get<E>(formula);
546  }
547 
548  CUDA Sig sig() const {
549  return battery::get<0>(battery::get<Seq>(formula));
550  }
551 
552  CUDA NI bool is_logical() const {
553  if(is(Seq)) {
555  }
556  return false;
557  }
558 
559  CUDA NI bool is_predicate() const {
560  if(is(Seq)) {
562  }
563  return false;
564  }
565 
566  CUDA NI bool is_comparison() const {
567  if(is(Seq)) {
568  Sig s = sig();
569  return is_arithmetic_comparison(s);
570  }
571  return false;
572  }
573 
574  CUDA const ExtendedSig& esig() const {
575  return battery::get<0>(battery::get<ESeq>(formula));
576  }
577 
578  CUDA const Sequence& seq() const {
579  return battery::get<1>(battery::get<Seq>(formula));
580  }
581 
582  CUDA const this_type& seq(size_t i) const {
583  return seq()[i];
584  }
585 
586  CUDA const Sequence& eseq() const {
587  return battery::get<1>(battery::get<ESeq>(formula));
588  }
589 
590  CUDA const this_type& eseq(size_t i) const {
591  return eseq()[i];
592  }
593 
594  CUDA logic_bool& b() {
595  return battery::get<B>(formula);
596  }
597 
598  CUDA logic_int& z() {
599  return battery::get<Z>(formula);
600  }
601 
602  CUDA logic_real& r() {
603  return battery::get<R>(formula);
604  }
605 
606  CUDA LogicSet& s() {
607  return battery::get<S>(formula);
608  }
609 
610  CUDA AVar& v() {
611  return battery::get<V>(formula);
612  }
613 
614  CUDA Sig& sig() {
615  return battery::get<0>(battery::get<Seq>(formula));
616  }
617 
618  CUDA ExtendedSig& esig() {
619  return battery::get<0>(battery::get<ESeq>(formula));
620  }
621 
622  CUDA Sequence& seq() {
623  return battery::get<1>(battery::get<Seq>(formula));
624  }
625 
626  CUDA this_type& seq(size_t i) {
627  return seq()[i];
628  }
629 
630  CUDA Sequence& eseq() {
631  return battery::get<1>(battery::get<ESeq>(formula));
632  }
633 
634  CUDA this_type& eseq(size_t i) {
635  return eseq()[i];
636  }
637 
638  CUDA NI this_type map_sig(Sig sig) const {
639  assert(is(Seq));
640  this_type f = *this;
641  f.sig() = sig;
642  return std::move(f);
643  }
644 
645  CUDA NI this_type map_atype(AType aty) const {
646  this_type f = *this;
647  f.type_as(aty);
648  return std::move(f);
649  }
650 
651 private:
652  template <class Fun>
653  CUDA NI void inplace_map_(Fun fun, const this_type& parent) {
654  switch(formula.index()) {
655  case Seq: {
656  for(int i = 0; i < seq().size(); ++i) {
657  seq(i).inplace_map_(fun, *this);
658  }
659  break;
660  }
661  case ESeq: {
662  for(int i = 0; i < eseq().size(); ++i) {
663  eseq(i).inplace_map_(fun, *this);
664  }
665  break;
666  }
667  default: {
668  fun(*this, parent);
669  break;
670  }
671  }
672  }
673 
674 public:
675 
676  /** In-place map of each leaf of the formula to a new leaf according to `fun`. */
677  template <class Fun>
678  CUDA NI void inplace_map(Fun fun) {
679  return inplace_map_(fun, *this);
680  }
681 
682  /** Map of each leaf of the formula to a new leaf according to `fun`. */
683  template <class Fun>
684  CUDA NI this_type map(Fun fun) {
685  this_type copy(*this);
686  copy.inplace_map([&](this_type& f, const this_type& parent){ f = fun(f, parent); });
687  return std::move(copy);
688  }
689 
690 private:
691  template<size_t n>
692  CUDA NI void print_sequence(bool print_atype, bool top_level = false) const {
693  const auto& op = battery::get<0>(battery::get<n>(formula));
694  const auto& children = battery::get<1>(battery::get<n>(formula));
695  if(children.size() == 0) {
696  ::battery::print(op);
697  return;
698  }
699  if constexpr(n == Seq) {
700  if(children.size() == 1) {
701  if(op == ABS) printf("|");
702  else if(op == CARD) printf("#(");
703  else printf("%s(", string_of_sig(op));
704  children[0].print_impl(print_atype);
705  if(op == ABS) printf("|");
706  else if(op == CARD) printf(")");
707  else printf(")");
708  return;
709  }
710  }
711  bool isprefix = true;
712  if constexpr(n == Seq) {
713  isprefix = is_prefix(op);
714  }
715  if(isprefix) {
716  ::battery::print(op);
717  }
718  printf("(");
719  for(int i = 0; i < children.size(); ++i) {
720  children[i].print_impl(print_atype);
721  if(i < children.size() - 1) {
722  if(!isprefix) {
723  printf(" ");
724  ::battery::print(op);
725  printf(" ");
726  }
727  else {
728  printf(", ");
729  }
730  }
731  if constexpr(n == Seq) {
732  if(op == AND && top_level) {
733  printf("\n");
734  }
735  }
736  }
737  printf(")");
738  }
739 
740  CUDA NI void print_impl(bool print_atype = true, bool top_level = false) const {
741  switch(formula.index()) {
742  case B:
743  printf("%s", b() ? "true" : "false");
744  break;
745  case Z:
746  printf("%lld", z());
747  break;
748  case R: {
749  const auto& r_lb = battery::get<0>(r());
750  const auto& r_ub = battery::get<1>(r());
751  if(r_lb == r_ub) {
752  printf("%.50lf", r_lb);
753  }
754  else {
755  printf("[%.50lf..%.50lf]", r_lb, r_ub);
756  }
757  break;
758  }
759  case S:
760  printf("{");
761  for(int i = 0; i < s().size(); ++i) {
762  const auto& lb = battery::get<0>(s()[i]);
763  const auto& ub = battery::get<1>(s()[i]);
764  if(lb == ub) {
765  lb.print(print_atype);
766  }
767  else {
768  printf("[");
769  lb.print(print_atype);
770  printf("..");
771  ub.print(print_atype);
772  printf("]");
773  }
774  if(i < s().size() - 1) {
775  printf(", ");
776  }
777  }
778  printf("}");
779  break;
780  case V:
781  printf("var(%d, %d)", v().vid(), v().aty());
782  break;
783  case LV:
784  lv().print();
785  break;
786  case E: {
787  if(print_atype) { printf("("); }
788  const auto& e = exists();
789  printf("var ");
790  battery::get<0>(e).print();
791  printf(":");
792  battery::get<1>(e).print();
793  if(print_atype) { printf(")"); }
794  break;
795  }
796  case Seq: print_sequence<Seq>(print_atype, top_level); break;
797  case ESeq: print_sequence<ESeq>(print_atype, top_level); break;
798  default: printf("print: formula not handled.\n"); assert(false); break;
799  }
800  if(print_atype) {
801  printf(":%d", type_);
802  }
803  }
804 
805 public:
806  CUDA void print(bool print_atype = true) const {
807  print_impl(print_atype, true);
808  }
809 };
810 
811 template<typename Allocator, typename ExtendedSig>
813  return lhs.type() == rhs.type() && lhs.data() == rhs.data();
814 }
815 
816 template<typename Allocator, typename ExtendedSig>
817 CUDA bool operator!=(const TFormula<Allocator, ExtendedSig>& lhs, const TFormula<Allocator, ExtendedSig>& rhs) {
818  return !(lhs == rhs);
819 }
820 
821 } // namespace lala
822 #endif
Definition: ast.hpp:38
constexpr AVar(const AVar &)=default
constexpr CUDA bool is_untyped() const
Definition: ast.hpp:61
constexpr CUDA int aty() const
Definition: ast.hpp:65
constexpr AVar & operator=(const AVar &)=default
constexpr CUDA AVar(AType atype, int var_id)
Definition: ast.hpp:53
constexpr AVar & operator=(AVar &&)=default
constexpr CUDA void type_as(AType aty)
Definition: ast.hpp:73
constexpr CUDA int vid() const
Definition: ast.hpp:69
constexpr CUDA AVar()
Definition: ast.hpp:41
constexpr AVar(AVar &&)=default
constexpr CUDA AVar(AType atype, size_t var_id)
Definition: ast.hpp:59
constexpr CUDA bool operator==(const AVar &other) const
Definition: ast.hpp:46
Definition: b.hpp:10
Definition: ast.hpp:247
CUDA const LogicSet & s() const
Definition: ast.hpp:532
static constexpr size_t Seq
Definition: ast.hpp:288
CUDA logic_int to_z() const
Definition: ast.hpp:499
CUDA Formula & data()
Definition: ast.hpp:357
battery::vector< this_type, Allocator > Sequence
Definition: ast.hpp:251
CUDA Sig sig() const
Definition: ast.hpp:548
CUDA TFormula(this_type &&other)
Definition: ast.hpp:304
friend class TFormula
Definition: ast.hpp:307
CUDA TFormula(Formula &&formula)
Definition: ast.hpp:300
CUDA AVar & v()
Definition: ast.hpp:610
static CUDA this_type make_exists(AType ty, LVar< Allocator > lvar, Sort< Allocator > ctype)
Definition: ast.hpp:435
CUDA NI this_type map_sig(Sig sig) const
Definition: ast.hpp:638
CUDA bool is_binary() const
Definition: ast.hpp:516
CUDA NI void inplace_map(Fun fun)
Definition: ast.hpp:678
CUDA this_type & eseq(size_t i)
Definition: ast.hpp:634
CUDA logic_bool & b()
Definition: ast.hpp:594
CUDA NI TFormula(const TFormula< Alloc2, ExtendedSig2 > &other, const Allocator &allocator=Allocator())
Definition: ast.hpp:310
static CUDA this_type make_set(LogicSet set, AType atype=UNTYPED)
Definition: ast.hpp:418
CUDA logic_int & z()
Definition: ast.hpp:598
static constexpr size_t R
Definition: ast.hpp:273
CUDA const Existential & exists() const
Definition: ast.hpp:544
CUDA ExtendedSig & esig()
Definition: ast.hpp:618
CUDA const this_type & seq(size_t i) const
Definition: ast.hpp:582
CUDA const ExtendedSig & esig() const
Definition: ast.hpp:574
static CUDA this_type make_false()
Definition: ast.hpp:398
static CUDA this_type make_true()
Definition: ast.hpp:397
constexpr CUDA bool is_untyped() const
Definition: ast.hpp:367
CUDA this_type & operator=(this_type &&rhs)
Definition: ast.hpp:352
TFormula< Allocator, ExtendedSig > this_type
Definition: ast.hpp:250
CUDA bool is_true() const
Definition: ast.hpp:491
CUDA const Sequence & eseq() const
Definition: ast.hpp:586
static CUDA this_type make_z(logic_int i, AType atype=UNTYPED)
Definition: ast.hpp:404
CUDA LogicSet & s()
Definition: ast.hpp:606
CUDA Sequence & eseq()
Definition: ast.hpp:630
CUDA bool is_false() const
Definition: ast.hpp:495
static CUDA this_type make_real(double lb, double ub, AType atype=UNTYPED)
Definition: ast.hpp:410
battery::tuple< LVar< Allocator >, Sort< Allocator > > Existential
Definition: ast.hpp:252
static constexpr size_t S
Definition: ast.hpp:276
static CUDA this_type make_unary(Sig sig, TFormula child, AType atype=UNTYPED, const Allocator &allocator=Allocator())
Definition: ast.hpp:471
CUDA const LVar< Allocator > & lv() const
Definition: ast.hpp:540
CUDA NI bool is_logical() const
Definition: ast.hpp:552
CUDA logic_bool b() const
Definition: ast.hpp:520
CUDA bool is_variable() const
Definition: ast.hpp:508
CUDA bool is_unary() const
Definition: ast.hpp:512
CUDA bool is(size_t kind) const
Definition: ast.hpp:487
static CUDA this_type make_nary(ExtendedSig esig, Sequence children, AType atype=UNTYPED)
Definition: ast.hpp:479
static constexpr size_t V
Definition: ast.hpp:279
static CUDA this_type make_bool(logic_bool b, AType atype=UNTYPED)
Definition: ast.hpp:400
CUDA bool is_constant() const
Definition: ast.hpp:504
CUDA void type_as(AType ty)
Definition: ast.hpp:360
CUDA const this_type & eseq(size_t i) const
Definition: ast.hpp:590
CUDA NI std::optional< Sort< allocator_type > > sort() const
Definition: ast.hpp:371
CUDA const Sequence & seq() const
Definition: ast.hpp:578
static CUDA this_type make_binary(TFormula lhs, Sig sig, TFormula rhs, AType atype=UNTYPED, const Allocator &allocator=Allocator(), bool flatten=true)
Definition: ast.hpp:475
CUDA Sig & sig()
Definition: ast.hpp:614
CUDA const logic_real & r() const
Definition: ast.hpp:528
CUDA AType type() const
Definition: ast.hpp:359
static constexpr size_t B
Definition: ast.hpp:267
CUDA TFormula(const this_type &other)
Definition: ast.hpp:303
CUDA void print(bool print_atype=true) const
Definition: ast.hpp:806
Allocator allocator_type
Definition: ast.hpp:249
static CUDA this_type make_real(logic_real r, AType atype=UNTYPED)
Definition: ast.hpp:414
static constexpr size_t ESeq
Definition: ast.hpp:291
logic_set< this_type > LogicSet
Definition: ast.hpp:253
static CUDA this_type make_lvar(AType ty, LVar< Allocator > lvar)
Definition: ast.hpp:431
CUDA AVar v() const
Definition: ast.hpp:536
CUDA logic_real & r()
Definition: ast.hpp:602
CUDA this_type & seq(size_t i)
Definition: ast.hpp:626
CUDA NI this_type map_atype(AType aty) const
Definition: ast.hpp:645
CUDA NI bool is_comparison() const
Definition: ast.hpp:566
CUDA TFormula(AType uid, Formula &&formula)
Definition: ast.hpp:301
CUDA TFormula()
Definition: ast.hpp:299
CUDA NI bool is_predicate() const
Definition: ast.hpp:559
static CUDA this_type make_avar(AType ty, int vid)
Definition: ast.hpp:427
CUDA void swap(this_type &other)
Definition: ast.hpp:342
CUDA logic_int z() const
Definition: ast.hpp:524
CUDA const Formula & data() const
Definition: ast.hpp:358
CUDA Sequence & seq()
Definition: ast.hpp:622
battery::variant< logic_bool, logic_int, logic_real, LogicSet, AVar, LVar< Allocator >, Existential, battery::tuple< Sig, Sequence >, battery::tuple< ExtendedSig, Sequence > > Formula
Definition: ast.hpp:264
CUDA NI this_type map(Fun fun)
Definition: ast.hpp:684
CUDA size_t index() const
Definition: ast.hpp:483
static constexpr size_t E
Definition: ast.hpp:285
static constexpr size_t LV
Definition: ast.hpp:282
static CUDA this_type make_avar(AVar v)
Definition: ast.hpp:423
CUDA static NI this_type make_nary(Sig sig, Sequence children, AType atype=UNTYPED, bool flatten=true)
Definition: ast.hpp:440
CUDA this_type & operator=(const this_type &rhs)
Definition: ast.hpp:347
static constexpr size_t Z
Definition: ast.hpp:270
Definition: ast.hpp:223
CUDA NI void print(const lala::Sig &sig)
Definition: ast.hpp:225
Definition: abstract_deps.hpp:14
battery::tuple< double, double > logic_real
Definition: sort.hpp:119
CUDA constexpr NI bool is_predicate(Sig sig)
Definition: ast.hpp:217
bool logic_bool
Definition: sort.hpp:109
CUDA NI const char * string_of_sig(Sig sig)
Definition: ast.hpp:121
constexpr CUDA bool operator==(const CartesianProduct< As... > &a, const CartesianProduct< Bs... > &b)
Definition: cartesian_product.hpp:504
long long int logic_int
Definition: sort.hpp:114
CUDA NI bool is_arithmetic_comparison(const F &f)
Definition: algorithm.hpp:338
Sig
Definition: ast.hpp:94
@ HULL
Unary function performing the convex hull of a set, e.g., .
Definition: ast.hpp:110
@ XOR
Logical connector.
Definition: ast.hpp:114
@ SIN
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ ITE
If-then-else.
Definition: ast.hpp:115
@ NEQ
Equality relations.
Definition: ast.hpp:112
@ SYMMETRIC_DIFFERENCE
Unary arithmetic function symbols.
Definition: ast.hpp:106
@ LEQ
Unary arithmetic function symbols.
Definition: ast.hpp:113
@ NOT
Unary arithmetic function symbols.
Definition: ast.hpp:114
@ ACOS
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ IMPLY
Unary arithmetic function symbols.
Definition: ast.hpp:114
@ TANH
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ TMOD
Truncated division, present in most programming languages, is defined as , i.e., it rounds towards ze...
Definition: ast.hpp:102
@ ATAN
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ ASIN
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ CARD
Cardinality function from set to integer.
Definition: ast.hpp:109
@ SUPSETEQ
Set inclusion predicates.
Definition: ast.hpp:107
@ MOD
Division and modulus over continuous domains (e.g., floating-point numbers and rational).
Definition: ast.hpp:101
@ DIFFERENCE
Unary arithmetic function symbols.
Definition: ast.hpp:106
@ ATANH
Trigonometric unary function symbols.
Definition: ast.hpp:100
@ CONVEX
Unary predicate, requiring .
Definition: ast.hpp:111
@ UNION
Unary arithmetic function symbols.
Definition: ast.hpp:106
@ OR
Unary arithmetic function symbols.
Definition: ast.hpp:114
@ EQ
Unary arithmetic function symbols.
Definition: ast.hpp:112
@ ACOSH
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ NROOT
Unary arithmetic function symbols.
Definition: ast.hpp:99
@ GEQ
Unary arithmetic function symbols.
Definition: ast.hpp:113
@ ADD
Unary arithmetic function symbols.
Definition: ast.hpp:97
@ IN
Set membership predicate.
Definition: ast.hpp:108
@ COS
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ POW
Unary arithmetic function symbols.
Definition: ast.hpp:97
@ MIN
Unary arithmetic function symbols.
Definition: ast.hpp:97
@ INTERSECTION
Unary arithmetic function symbols.
Definition: ast.hpp:106
@ LN
Square root, natural exponential and natural logarithm function symbols.
Definition: ast.hpp:98
@ SINH
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ EDIV
Unary arithmetic function symbols.
Definition: ast.hpp:105
@ DIV
Unary arithmetic function symbols.
Definition: ast.hpp:101
@ 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
@ SQRT
Unary arithmetic function symbols.
Definition: ast.hpp:98
@ 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
@ EXP
Unary arithmetic function symbols.
Definition: ast.hpp:98
@ COMPLEMENT
Set functions.
Definition: ast.hpp:106
@ TAN
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ SUBSETEQ
Unary arithmetic function symbols.
Definition: ast.hpp:107
@ AND
Unary arithmetic function symbols.
Definition: ast.hpp:114
@ MAXIMIZE
Unary "meta-predicate" indicating that its argument must be maximized, according to the increasing or...
Definition: ast.hpp:116
@ SUBSET
Unary arithmetic function symbols.
Definition: ast.hpp:107
@ MINIMIZE
Same as MAXIMIZE, but for minimization.
Definition: ast.hpp:117
@ EQUIV
Unary arithmetic function symbols.
Definition: ast.hpp:114
@ ABS
Unary arithmetic function symbols.
Definition: ast.hpp:96
@ ASINH
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ SUB
Unary arithmetic function symbols.
Definition: ast.hpp:97
@ SUPSET
Unary arithmetic function symbols.
Definition: ast.hpp:107
@ COSH
Unary arithmetic function symbols.
Definition: ast.hpp:100
@ MUL
Unary arithmetic function symbols.
Definition: ast.hpp:97
@ CMOD
Ceil division is defined as . Modulus is defined as .
Definition: ast.hpp:104
@ LOG
nth root and logarithm to base (both binary function symbols).
Definition: ast.hpp:99
@ 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 NI bool is_logical(Sig sig)
Definition: ast.hpp:213
battery::vector< battery::tuple< F, F >, typename F::allocator_type > logic_set
Definition: sort.hpp:127
CUDA constexpr NI bool is_associative(Sig sig)
Definition: ast.hpp:204
int AType
Definition: sort.hpp:18
CUDA constexpr NI bool is_modulo(Sig sig)
Definition: ast.hpp:200
CUDA constexpr NI bool is_division(Sig sig)
Definition: ast.hpp:196
IKind
Definition: ast.hpp:28
battery::string< Allocator > LVar
Definition: ast.hpp:25
CUDA constexpr NI bool is_prefix(Sig sig)
Definition: ast.hpp:192
#define UNTYPED
Definition: sort.hpp:21
Definition: sort.hpp:26