Theory of Datatypes

examples/api/cpp/datatypes.cpp

  1 /******************************************************************************
  2  * Top contributors (to current version):
  3  *   Aina Niemetz, Morgan Deters, Andrew Reynolds
  4  *
  5  * This file is part of the cvc5 project.
  6  *
  7  * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
  8  * in the top-level source directory and their institutional affiliations.
  9  * All rights reserved.  See the file COPYING in the top-level source
 10  * directory for licensing information.
 11  * ****************************************************************************
 12  *
 13  * An example of using inductive datatypes in cvc5.
 14  */
 15 
 16 #include <cvc5/cvc5.h>
 17 
 18 #include <iostream>
 19 
 20 using namespace cvc5;
 21 
 22 void test(Solver& slv, Sort& consListSort)
 23 {
 24   // Now our old "consListSpec" is useless--the relevant information
 25   // has been copied out, so we can throw that spec away.  We can get
 26   // the complete spec for the datatype from the DatatypeSort, and
 27   // this Datatype object has constructor symbols (and others) filled in.
 28 
 29   const Datatype& consList = consListSort.getDatatype();
 30 
 31   // t = cons 0 nil
 32   //
 33   // Here, consList["cons"] gives you the DatatypeConstructor.  To get
 34   // the constructor symbol for application, use .getConstructor("cons"),
 35   // which is equivalent to consList["cons"].getConstructor().  Note that
 36   // "nil" is a constructor too, so it needs to be applied with
 37   // APPLY_CONSTRUCTOR, even though it has no arguments.
 38   Term t = slv.mkTerm(APPLY_CONSTRUCTOR,
 39                       {consList.getConstructor("cons").getTerm(),
 40                        slv.mkInteger(0),
 41                        slv.mkTerm(APPLY_CONSTRUCTOR,
 42                                   {consList.getConstructor("nil").getTerm()})});
 43 
 44   std::cout << "t is " << t << std::endl
 45             << "sort of cons is "
 46             << consList.getConstructor("cons").getTerm().getSort() << std::endl
 47             << "sort of nil is "
 48             << consList.getConstructor("nil").getTerm().getSort() << std::endl;
 49 
 50   // t2 = head(cons 0 nil), and of course this can be evaluated
 51   //
 52   // Here we first get the DatatypeConstructor for cons (with
 53   // consList["cons"]) in order to get the "head" selector symbol
 54   // to apply.
 55   Term t2 = slv.mkTerm(APPLY_SELECTOR,
 56                        {consList["cons"].getSelector("head").getTerm(), t});
 57 
 58   std::cout << "t2 is " << t2 << std::endl
 59             << "simplify(t2) is " << slv.simplify(t2) << std::endl
 60             << std::endl;
 61 
 62   // You can also iterate over a Datatype to get all its constructors,
 63   // and over a DatatypeConstructor to get all its "args" (selectors)
 64   for (Datatype::const_iterator i = consList.begin(); i != consList.end(); ++i)
 65   {
 66     std::cout << "ctor: " << *i << std::endl;
 67     for (DatatypeConstructor::const_iterator j = (*i).begin(); j != (*i).end();
 68          ++j)
 69     {
 70       std::cout << " + arg: " << *j << std::endl;
 71     }
 72   }
 73   std::cout << std::endl;
 74 
 75   // Alternatively, you can use for each loops.
 76   for (const auto& c : consList)
 77   {
 78     std::cout << "ctor: " << c << std::endl;
 79     for (const auto& s : c)
 80     {
 81       std::cout << " + arg: " << s << std::endl;
 82     }
 83   }
 84   std::cout << std::endl;
 85 
 86   // You can also define a tester term for constructor 'cons': (_ is cons)
 87   Term t_is_cons =
 88       slv.mkTerm(APPLY_TESTER, {consList["cons"].getTesterTerm(), t});
 89   std::cout << "t_is_cons is " << t_is_cons << std::endl << std::endl;
 90   slv.assertFormula(t_is_cons);
 91   // Updating t at 'head' with value 1 is defined as follows:
 92   Term t_updated = slv.mkTerm(
 93       APPLY_UPDATER,
 94       {consList["cons"]["head"].getUpdaterTerm(), t, slv.mkInteger(1)});
 95   std::cout << "t_updated is " << t_updated << std::endl << std::endl;
 96   slv.assertFormula(slv.mkTerm(DISTINCT, {t, t_updated}));
 97 
 98   // You can also define parameterized datatypes.
 99   // This example builds a simple parameterized list of sort T, with one
100   // constructor "cons".
101   Sort sort = slv.mkParamSort("T");
102   DatatypeDecl paramConsListSpec =
103       slv.mkDatatypeDecl("paramlist", {sort});  // give the datatype a name
104   DatatypeConstructorDecl paramCons = slv.mkDatatypeConstructorDecl("cons");
105   DatatypeConstructorDecl paramNil = slv.mkDatatypeConstructorDecl("nil");
106   paramCons.addSelector("head", sort);
107   paramCons.addSelectorSelf("tail");
108   paramConsListSpec.addConstructor(paramCons);
109   paramConsListSpec.addConstructor(paramNil);
110 
111   Sort paramConsListSort = slv.mkDatatypeSort(paramConsListSpec);
112   Sort paramConsIntListSort =
113       paramConsListSort.instantiate(std::vector<Sort>{slv.getIntegerSort()});
114 
115   const Datatype& paramConsList = paramConsListSort.getDatatype();
116 
117   std::cout << "parameterized datatype sort is " << std::endl;
118   for (const DatatypeConstructor& ctor : paramConsList)
119   {
120     std::cout << "ctor: " << ctor << std::endl;
121     for (const DatatypeSelector& stor : ctor)
122     {
123       std::cout << " + arg: " << stor << std::endl;
124     }
125   }
126 
127   Term a = slv.mkConst(paramConsIntListSort, "a");
128   std::cout << "term " << a << " is of sort " << a.getSort() << std::endl;
129 
130   Term head_a = slv.mkTerm(
131       APPLY_SELECTOR, {paramConsList["cons"].getSelector("head").getTerm(), a});
132   std::cout << "head_a is " << head_a << " of sort " << head_a.getSort()
133             << std::endl
134             << "sort of cons is "
135             << paramConsList.getConstructor("cons").getTerm().getSort()
136             << std::endl
137             << std::endl;
138 
139   Term assertion = slv.mkTerm(GT, {head_a, slv.mkInteger(50)});
140   std::cout << "Assert " << assertion << std::endl;
141   slv.assertFormula(assertion);
142   std::cout << "Expect sat." << std::endl;
143   std::cout << "cvc5: " << slv.checkSat() << std::endl;
144 }
145 
146 int main()
147 {
148   Solver slv;
149   // This example builds a simple "cons list" of integers, with
150   // two constructors, "cons" and "nil."
151 
152   // Building a datatype consists of two steps.
153   // First, the datatype is specified.
154   // Second, it is "resolved" to an actual sort, at which point function
155   // symbols are assigned to its constructors, selectors, and testers.
156 
157   DatatypeDecl consListSpec =
158       slv.mkDatatypeDecl("list");  // give the datatype a name
159   DatatypeConstructorDecl cons = slv.mkDatatypeConstructorDecl("cons");
160   cons.addSelector("head", slv.getIntegerSort());
161   cons.addSelectorSelf("tail");
162   consListSpec.addConstructor(cons);
163   DatatypeConstructorDecl nil = slv.mkDatatypeConstructorDecl("nil");
164   consListSpec.addConstructor(nil);
165 
166   std::cout << "spec is:" << std::endl << consListSpec << std::endl;
167 
168   // Keep in mind that "DatatypeDecl" is the specification class for
169   // datatypes---"DatatypeDecl" is not itself a cvc5 Sort.
170   // Now that our Datatype is fully specified, we can get a Sort for it.
171   // This step resolves the "SelfSort" reference and creates
172   // symbols for all the constructors, etc.
173 
174   Sort consListSort = slv.mkDatatypeSort(consListSpec);
175 
176   test(slv, consListSort);
177 
178   std::cout << std::endl
179             << ">>> Alternatively, use declareDatatype" << std::endl;
180   std::cout << std::endl;
181 
182   DatatypeConstructorDecl cons2 = slv.mkDatatypeConstructorDecl("cons");
183   cons2.addSelector("head", slv.getIntegerSort());
184   cons2.addSelectorSelf("tail");
185   DatatypeConstructorDecl nil2 = slv.mkDatatypeConstructorDecl("nil");
186   std::vector<DatatypeConstructorDecl> ctors = {cons2, nil2};
187   Sort consListSort2 = slv.declareDatatype("list2", ctors);
188   test(slv, consListSort2);
189 
190   return 0;
191 }