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
 20using namespace cvc5;
 21
 22void 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(Kind::APPLY_CONSTRUCTOR,
 39                      {consList.getConstructor("cons").getTerm(),
 40                       slv.mkInteger(0),
 41                       slv.mkTerm(Kind::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(Kind::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(Kind::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      Kind::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(Kind::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 =
131      slv.mkTerm(Kind::APPLY_SELECTOR,
132                 {paramConsList["cons"].getSelector("head").getTerm(), a});
133  std::cout << "head_a is " << head_a << " of sort " << head_a.getSort()
134            << std::endl
135            << "sort of cons is "
136            << paramConsList.getConstructor("cons").getTerm().getSort()
137            << std::endl
138            << std::endl;
139
140  Term assertion = slv.mkTerm(Kind::GT, {head_a, slv.mkInteger(50)});
141  std::cout << "Assert " << assertion << std::endl;
142  slv.assertFormula(assertion);
143  std::cout << "Expect sat." << std::endl;
144  std::cout << "cvc5: " << slv.checkSat() << std::endl;
145}
146
147int main()
148{
149  Solver slv;
150  // This example builds a simple "cons list" of integers, with
151  // two constructors, "cons" and "nil."
152
153  // Building a datatype consists of two steps.
154  // First, the datatype is specified.
155  // Second, it is "resolved" to an actual sort, at which point function
156  // symbols are assigned to its constructors, selectors, and testers.
157
158  DatatypeDecl consListSpec =
159      slv.mkDatatypeDecl("list");  // give the datatype a name
160  DatatypeConstructorDecl cons = slv.mkDatatypeConstructorDecl("cons");
161  cons.addSelector("head", slv.getIntegerSort());
162  cons.addSelectorSelf("tail");
163  consListSpec.addConstructor(cons);
164  DatatypeConstructorDecl nil = slv.mkDatatypeConstructorDecl("nil");
165  consListSpec.addConstructor(nil);
166
167  std::cout << "spec is:" << std::endl << consListSpec << std::endl;
168
169  // Keep in mind that "DatatypeDecl" is the specification class for
170  // datatypes---"DatatypeDecl" is not itself a cvc5 Sort.
171  // Now that our Datatype is fully specified, we can get a Sort for it.
172  // This step resolves the "SelfSort" reference and creates
173  // symbols for all the constructors, etc.
174
175  Sort consListSort = slv.mkDatatypeSort(consListSpec);
176
177  test(slv, consListSort);
178
179  std::cout << std::endl
180            << ">>> Alternatively, use declareDatatype" << std::endl;
181  std::cout << std::endl;
182
183  DatatypeConstructorDecl cons2 = slv.mkDatatypeConstructorDecl("cons");
184  cons2.addSelector("head", slv.getIntegerSort());
185  cons2.addSelectorSelf("tail");
186  DatatypeConstructorDecl nil2 = slv.mkDatatypeConstructorDecl("nil");
187  std::vector<DatatypeConstructorDecl> ctors = {cons2, nil2};
188  Sort consListSort2 = slv.declareDatatype("list2", ctors);
189  test(slv, consListSort2);
190
191  return 0;
192}