Datatypes
Overview
To manipulate instances of a datatype, one must first declare the datatype itself. Declaration happens in three phases. Let’s consider declaring a cons-list of integers.
First, we initialize the datatype with its name
>>> IntList = Datatype('IntList')
Second, we declare constructors for the datatype, giving the constructor name and field names and sorts. Here is the empty list constructor:
>>> IntList.declare('nil', ())
Here is the cons constructor:
>>> IntList.declare('cons', ('val', IntSort()), ('tail', IntList))
Third, after all constructors are declared, we can create the datatype, finishing its declaration.
>>> IntList = IntList.create()
Now, one has access to a number of tools for interacting with integer lists.
IntList.nil
refers to the SMT term that is an empty list, andIntList.cons
refers to the cons constructor.IntList.is_nil
andIntList.is_cons
are testors (a.k.a., recognizers) for those constructors.IntList.val
andIntList.tail
are selectors (a.k.a. accessors) for the cons constructor.
If constructor, accessor, or selector names are ambiguous (e.g., if different
constructors have selectors of the same name), then see the methods on
cvc5.pythonic.DatatypeSortRef
to unambiguously access a specific
function.
To create mutually recursive datatypes, see
cvc5.pythonic.CreateDatatypes()
.
To create a codataype (e.g., a possibly infinite stream of integers), pass the
isCoDatatype=True
argument to the cvc5.pythonic.Datatype
constructor.
>>> IntStream = Datatype('IntStream', isCoDatatype=True)
Declaration Utilities
- cvc5.pythonic.CreateDatatypes(*ds)
Create mutually recursive SMT datatypes using 1 or more Datatype helper objects. In the following example we define a Tree-List using two mutually recursive datatypes.
>>> TreeList = Datatype('TreeList') >>> Tree = Datatype('Tree') >>> # Tree has two constructors: leaf and node >>> Tree.declare('leaf', ('val', IntSort())) >>> # a node contains a list of trees >>> Tree.declare('node', ('children', TreeList)) >>> TreeList.declare('nil') >>> TreeList.declare('cons', ('car', Tree), ('cdr', TreeList)) >>> Tree, TreeList = CreateDatatypes(Tree, TreeList) >>> Tree.val(Tree.leaf(10)) val(leaf(10)) >>> simplify(Tree.val(Tree.leaf(10))) 10 >>> l1 = TreeList.cons(Tree.leaf(10), TreeList.cons(Tree.leaf(20), TreeList.nil)) >>> n1 = Tree.node(TreeList.cons(Tree.leaf(10), TreeList.cons(Tree.leaf(20), TreeList.nil))) >>> n1 node(cons(leaf(10), cons(leaf(20), nil))) >>> n2 = Tree.node(TreeList.cons(n1, TreeList.nil)) >>> simplify(n2 == n1) False >>> simplify(TreeList.car(Tree.children(n2)) == n1) True
- cvc5.pythonic.TupleSort(name, sorts, ctx=None)
Create a named tuple sort base on a set of underlying sorts
Returns the tuple datatype, its constructor, and a list of accessors, in order.
>>> pair, mk_pair, (first, second) = TupleSort("pair", [IntSort(), BoolSort()]) >>> b = Bool('b') >>> i = Int('i') >>> p = mk_pair(i, b) >>> p pair(i, b) >>> solve([b != second(p)]) no solution
- cvc5.pythonic.DisjointSum(name, sorts, ctx=None)
Create a named tagged union sort base on a set of underlying sorts.
See this page <https://en.wikipedia.org/wiki/Tagged_union> for information about tagged unions.
Returns the created datatype and a tuple of (injector, extractor) pairs for the different variants.
>>> sum, ((inject0, extract0), (inject1, extract1)) = DisjointSum("+", [IntSort(), BoolSort()]) >>> b = Bool('b') >>> i, j = Ints('i j') >>> solve([inject0(i) == inject1(b)]) no solution >>> solve([inject0(i) == inject0(j), extract0(inject0(i)) == 5]) [i = 5, j = 5]
Classes
- class cvc5.pythonic.Datatype(name, ctx=None, isCoDatatype=False)
Helper class for declaring datatypes.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a declaration >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) >>> List.cons(10, List.nil).sort() List >>> cons = List.cons >>> nil = List.nil >>> car = List.car >>> cdr = List.cdr >>> n = cons(1, cons(0, nil)) >>> n cons(1, cons(0, nil)) >>> simplify(cdr(n)) cons(0, nil) >>> simplify(car(n)) 1
- __init__(name, ctx=None, isCoDatatype=False)
- __repr__()
Return repr(self).
- __weakref__
list of weak references to the object (if defined)
- create()
Create an SMT datatype based on the constructors declared using the method declare(). The function CreateDatatypes() must be used to define mutually recursive datatypes.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.nil nil >>> List.cons(10, List.nil) cons(10, nil) >>> Stream = Datatype('Stream', isCoDatatype=True) >>> Stream.declare('cons', ('car', IntSort()), ('cdr', Stream)) >>> Stream.declare('nil') >>> Stream = Stream.create() >>> a = Const('a', Stream) >>> b = Const('b', Stream) >>> s = Solver() >>> s += a == Stream.cons(0, a) >>> s.check() sat >>> s = Solver() >>> s += a == Stream.cons(0, a) >>> s += b == Stream.cons(0, b) >>> s += a != b >>> s.check() unsat
- declare(name, *args)
Declare constructor named name with the given accessors args. Each accessor is a pair (name, sort), where name is a string and sort an SMT sort or a reference to the datatypes being declared. In the following example List.declare(‘cons’, (‘car’, IntSort()), (‘cdr’, List)) declares the constructor named cons that builds a new List using an integer and a List. It also declares the accessors car and cdr. The accessor car extracts the integer of a cons cell, and cdr the list of a cons cell. After all constructors were declared, we use the method create() to create the actual datatype in SMT.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create()
- class cvc5.pythonic.DatatypeSortRef(ast, ctx=None)
Datatype sorts.
- __init__(ast, ctx=None)
- accessor(i, j)
In SMT, each constructor has 0 or more accessor. The number of accessors is equal to the arity of the constructor.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> num_accs = List.constructor(0).arity() >>> num_accs 2 >>> List.accessor(0, 0) car >>> List.accessor(0, 1) cdr >>> List.constructor(1) nil >>> num_accs = List.constructor(1).arity() >>> num_accs 0
- constructor(idx)
Return a constructor of the datatype self.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a declaration >>> List.num_constructors() 2 >>> List.constructor(0) cons >>> List.constructor(1) nil
- num_constructors()
Return the number of constructors in the given datatype.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a declaration >>> List.num_constructors() 2
- recognizer(idx)
In SMT, each constructor has an associated recognizer predicate. If the constructor is named name, then the recognizer is_name.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> # List is now a SMT declaration >>> List.num_constructors() 2 >>> List.recognizer(0) is_cons >>> List.recognizer(1) is_nil >>> simplify(List.is_nil(List.cons(10, List.nil))) False >>> simplify(List.is_cons(List.cons(10, List.nil))) True >>> l = Const('l', List) >>> simplify(List.is_cons(l)) is_cons(l)
- class cvc5.pythonic.DatatypeConstructorRef(datatype, ctx=None, r=False)
- __call__(*args)
Apply this constructor.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0)(1, List.nil) cons(1, nil)
The arguments must be SMT expressions. This method assumes that the sorts of the elements in args match the sorts of the domain. Limited coercion is supported. For example, if args[0] is a Python integer, and the function expects a SMT integer, then the argument is automatically converted into a SMT integer.
- __init__(datatype, ctx=None, r=False)
- arity()
Return the number of arguments of a constructor.
The number of accessors is equal to the arity of the constructor.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0).arity() 2
- domain(i)
Return the sort of the argument i of a constructor. This method assumes that 0 <= i < self.arity().
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0).domain(0) Int
- range()
Return the sort of the range of a function declaration. For constants, this is the sort of the constant.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> List.num_constructors() 2 >>> List.constructor(0).range() List
- class cvc5.pythonic.DatatypeSelectorRef(datatype, ctx=None, r=False)
- __call__(*args)
Apply this selector.
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> l = List.cons(1, List.nil) >>> solve([1 != List.car(l)]) no solution
The arguments must be SMT expressions. This method assumes that the sorts of the elements in args match the sorts of the domain. Limited coercion is supported. For example, if args[0] is a Python integer, and the function expects a SMT integer, then the argument is automatically converted into a SMT integer.
- __init__(datatype, ctx=None, r=False)
- arity()
Return the number of arguments of a selector (always 1).
- domain(i)
Return the sort of the argument i of a selector. This method assumes that 0 <= i < self.arity().
- range()
Return the sort of the range of a function declaration. For constants, this is the sort of the constant.
- class cvc5.pythonic.DatatypeRecognizerRef(constructor, ctx=None, r=False)
- __call__(*args)
Apply this tester (a.k.a., recognizer).
>>> List = Datatype('List') >>> List.declare('cons', ('car', IntSort()), ('cdr', List)) >>> List.declare('nil') >>> List = List.create() >>> l = List.cons(1, List.nil) >>> solve([List.is_nil(l)]) no solution
The arguments must be SMT expressions. This method assumes that the sorts of the elements in args match the sorts of the domain. Limited coercion is supported. For example, if args[0] is a Python integer, and the function expects a SMT integer, then the argument is automatically converted into a SMT integer.
- __init__(constructor, ctx=None, r=False)
- arity()
Return the number of arguments of a selector (always 1).
- domain(i)
Return the sort of the argument i of a selector. This method assumes that 0 <= i < self.arity().
- range()
Return the sort of the range of a function declaration. For constants, this is the sort of the constant.