Provide builders functions for all theories, which perform simplifications.
Provide builders functions for all theories, which perform simplifications.
We use the convention that capital letter operations (apply) do not perform any syntactic simplification and build exactly the tree corresponding to its definition, while corresponding functions with lower case letter can potentially return a different representation than what it is called with.
An example is the And
vs and
operation. The And
needs a minimum of two
arguments, and will always produce a function application (and e1 e2 ...) with
the exact same element as provided. But the and
constructor is more flexible
and will accept 0, 1 or more arguments, and will perform many simplifications. If
called with 0 argument, it would return true
, while if called with 1 it would return
the element itself. Notice how in both case, even though you call the and
constructor
you don't get an and
in the resulting expression. Similarly, an and
with many
arguments might simplify away true
literals, or reduce the whole expression to
false
if one element is false
. The And
apply method will always keep literals
without simplifying them away.
So why not always use simplifying methods? First, they are slightly more expensive than the corresponding capital letter constructors, as they will inspect the expressions and do some processing on them. Second, there are times where you might want precise control over the syntax, and having syntax-only methods as primitive is very useful. One main difference is that those constructors perform some semantics transformations, while capital letter constructors are purely syntactic.
Finally note that one cannot assume anything more than semantics equivalence for the returned term. The constructor is free to perform any sort of rewriting, so even if you expect that calling and(true, false, x) should return false, it might still return something like and(false, x). However, it will only return syntactically correct term, so you would not get And(false). Usually, the above expression will correctly get simplified to false, but the general way of thinking about these constructors is as a best-effort simplification: it will do some relatively easy and cheap simplification, but the exact contract does not specify the level of simplification. This limitation is there as, in general, it is impossible to properly specify the "correct" simplified form for a given formula.
General patterns for building and extracting FunctionApplication in theories.
General patterns for building and extracting FunctionApplication in theories.
Most SMT-LIB theories are a definition of many built-in functions. We do not wish to extend the core abstract syntax tree of SMT-LIB with theory specific operations, so a theory definition is simply providing Constructors and Extractors to build the proper trees out of core SMT-LIB FunctionApplication and Identifier.
this object provides traits to facilitate the definition of custom FunctionApplication with apply and unapply methods. They provide the proper signatures for different arities.
Refer to any theory definition to see examples of how to use these traits.
Provides constructors and extractors to build theory specific trees
Theory trees are not concrete abstract syntax trees, but simply helpers to properly construct the underlying low level {{smtlib.parser.Terms AST}}.
This design choice is made to reflect how the SMT-LIB format is described, and also seems to work well as most operations on trees only need to be defined for the core ASTs. Many theories operations such as "+", "-", "div" are really a special case of the general {{smtlib.parser.Terms.FunctionApplication FunctionApplication}}.
Each theory is defined in its own object named after the theory, e.g. {theories.Ints}, which contains objects with
apply
andunapply
(usually factored out in some abstract {{theory.Operations}} traits) to build and extract correct expressions in the theory.