Quickstart Guide

First, create a cvc5 TermManager instance:

TermManager tm;

Then, create a cvc5 Solver instance:

Solver solver(tm);

We will ask the solver to produce models and unsat cores in the following, and for this we have to enable the following options.

solver.setOption("produce-models", "true");
solver.setOption("produce-unsat-cores", "true");

Next we set the logic. The simplest way to set a logic for the solver is to choose “ALL”. This enables all logics in the solver. Alternatively, "QF_ALL" enables all logics without quantifiers. To optimize the solver’s behavior for a more specific logic, use the logic name, e.g. "QF_BV" or "QF_AUFBV".

solver.setLogic("ALL");

In the following, we will define constraints of reals and integers. For this, we first query the solver for the corresponding sorts.

Sort realSort = tm.getRealSort();
Sort intSort = tm.getIntegerSort();

Now, we create two constants x and y of sort Real, and two constants a and b of sort Integer. Notice that these are symbolic constants, but not actual values.

Term x = tm.mkConst(realSort, "x");
Term y = tm.mkConst(realSort, "y");
Term a = tm.mkConst(intSort, "a");
Term b = tm.mkConst(intSort, "b");

We define the following constraints regarding x and y:

\[(0 < x) \wedge (0 < y) \wedge (x + y < 1) \wedge (x \leq y)\]

We construct the required terms and assert them as follows:

// Formally, constraints are also terms. Their sort is Boolean.
// We will construct these constraints gradually,
// by defining each of their components.
// We start with the constant numerals 0 and 1:
Term zero = tm.mkReal(0);
Term one = tm.mkReal(1);

// Next, we construct the term x + y
Term xPlusY = tm.mkTerm(Kind::ADD, {x, y});

// Now we can define the constraints.
// They use the operators +, <=, and <.
// In the API, these are denoted by ADD, LEQ, and LT.
// A list of available operators is available in:
// src/api/cpp/cvc5_kind.h
Term constraint1 = tm.mkTerm(Kind::LT, {zero, x});
Term constraint2 = tm.mkTerm(Kind::LT, {zero, y});
Term constraint3 = tm.mkTerm(Kind::LT, {xPlusY, one});
Term constraint4 = tm.mkTerm(Kind::LEQ, {x, y});

// Now we assert the constraints to the solver.
solver.assertFormula(constraint1);
solver.assertFormula(constraint2);
solver.assertFormula(constraint3);
solver.assertFormula(constraint4);

Now we check if the asserted formula is satisfiable, that is, we check if there exist values of sort Real for x and y that satisfy all the constraints.

Result r1 = solver.checkSat();

The result we get from this satisfiability check is either sat, unsat or unknown. It’s status can be queried via cvc5::Result::isSat(), cvc5::Result::isUnsat() and cvc5::Result::isSatUnknown(). Alternatively, it can also be printed.

std::cout << "expected: sat" << std::endl;
std::cout << "result: " << r1 << std::endl;

This will print:

expected: sat
result: sat

Now, we query the solver for the values for x and y that satisfy the constraints.

Term xVal = solver.getValue(x);
Term yVal = solver.getValue(y);

It is also possible to get values for terms that do not appear in the original formula.

Term xMinusY = tm.mkTerm(Kind::SUB, {x, y});
Term xMinusYVal = solver.getValue(xMinusY);

We can retrieve the string representation of these values as follows.

std::string xStr = xVal.getRealValue();
std::string yStr = yVal.getRealValue();
std::string xMinusYStr = xMinusYVal.getRealValue();

std::cout << "value for x: " << xStr << std::endl;
std::cout << "value for y: " << yStr << std::endl;
std::cout << "value for x - y: " << xMinusYStr << std::endl;

This will print the following:

value for x: 1/6
value for y: 1/6
value for x - y: 0.0

We can convert these values to C++ types.

// Further, we can convert the values to cpp types
std::pair<int64_t, uint64_t> xPair = xVal.getReal64Value();
std::pair<int64_t, uint64_t> yPair = yVal.getReal64Value();
std::pair<int64_t, uint64_t> xMinusYPair = xMinusYVal.getReal64Value();

std::cout << "value for x: " << xPair.first << "/" << xPair.second
          << std::endl;
std::cout << "value for y: " << yPair.first << "/" << yPair.second
          << std::endl;
std::cout << "value for x - y: " << xMinusYPair.first << "/"
          << xMinusYPair.second << std::endl;

Another way to independently compute the value of x - y would be to perform the (rational) arithmetic manually. However, for more complex terms, it is easier to let the solver do the evaluation.

std::pair<int64_t, uint64_t> xMinusYComputed = {
  xPair.first * yPair.second - xPair.second * yPair.first,
  xPair.second * yPair.second
};
uint64_t g = std::gcd(xMinusYComputed.first, xMinusYComputed.second);
xMinusYComputed = { xMinusYComputed.first / g, xMinusYComputed.second / g };
if (xMinusYComputed == xMinusYPair)
{
  std::cout << "computed correctly" << std::endl;
}
else
{
  std::cout << "computed incorrectly" << std::endl;
}

This will print:

computed correctly

Next, we will check satisfiability of the same formula, only this time over integer variables a and b. For this, we first reset the assertions added to the solver.

solver.resetAssertions();

Next, we assert the same assertions as above, but with integers. This time, we inline the construction of terms to the assertion command.

solver.assertFormula(tm.mkTerm(Kind::LT, {tm.mkInteger(0), a}));
solver.assertFormula(tm.mkTerm(Kind::LT, {tm.mkInteger(0), b}));
solver.assertFormula(
    tm.mkTerm(Kind::LT, {tm.mkTerm(Kind::ADD, {a, b}), tm.mkInteger(1)}));
solver.assertFormula(tm.mkTerm(Kind::LEQ, {a, b}));

Now, we check whether the revised assertion is satisfiable.

Result r2 = solver.checkSat();
std::cout << "expected: unsat" << std::endl;
std::cout << "result: " << r2 << std::endl;

This time the asserted formula is unsatisfiable:

expected: unsat
result: unsat

We can query the solver for an unsatisfiable core, that is, a subset of the assertions that is already unsatisfiable.

std::vector<Term> unsatCore = solver.getUnsatCore();
std::cout << "unsat core size: " << unsatCore.size() << std::endl;
std::cout << "unsat core: " << std::endl;
for (const Term& t : unsatCore)
{
  std::cout << t << std::endl;
}

This will print:

unsat core size: 3
unsat core:
(< 0 a)
(< 0 b)
(< (+ a b) 1)

Example

examples/api/cpp/quickstart.cpp

  1/******************************************************************************
  2 * This file is part of the cvc5 project.
  3 *
  4 * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
  5 * in the top-level source directory and their institutional affiliations.
  6 * All rights reserved.  See the file COPYING in the top-level source
  7 * directory for licensing information.
  8 * ****************************************************************************
  9 *
 10 * A simple demonstration of the API capabilities of cvc5.
 11 *
 12 */
 13
 14#include <cvc5/cvc5.h>
 15
 16#include <iostream>
 17#include <numeric>
 18
 19using namespace cvc5;
 20
 21int main()
 22{
 23  // Create a term manager
 24  //! [docs-cpp-quickstart-0 start]
 25  TermManager tm;
 26  //! [docs-cpp-quickstart-0 end]
 27  // Create a solver
 28  //! [docs-cpp-quickstart-1 start]
 29  Solver solver(tm);
 30  //! [docs-cpp-quickstart-1 end]
 31
 32  // We will ask the solver to produce models and unsat cores,
 33  // hence these options should be turned on.
 34  //! [docs-cpp-quickstart-2 start]
 35  solver.setOption("produce-models", "true");
 36  solver.setOption("produce-unsat-cores", "true");
 37  //! [docs-cpp-quickstart-2 end]
 38
 39  // The simplest way to set a logic for the solver is to choose "ALL".
 40  // This enables all logics in the solver.
 41  // Alternatively, "QF_ALL" enables all logics without quantifiers.
 42  // To optimize the solver's behavior for a more specific logic,
 43  // use the logic name, e.g. "QF_BV" or "QF_AUFBV".
 44
 45  // Set the logic
 46  //! [docs-cpp-quickstart-3 start]
 47  solver.setLogic("ALL");
 48  //! [docs-cpp-quickstart-3 end]
 49
 50  // In this example, we will define constraints over reals and integers.
 51  // Hence, we first obtain the corresponding sorts.
 52  //! [docs-cpp-quickstart-4 start]
 53  Sort realSort = tm.getRealSort();
 54  Sort intSort = tm.getIntegerSort();
 55  //! [docs-cpp-quickstart-4 end]
 56
 57  // x and y will be real variables, while a and b will be integer variables.
 58  // Formally, their cpp type is Term,
 59  // and they are called "constants" in SMT jargon:
 60  //! [docs-cpp-quickstart-5 start]
 61  Term x = tm.mkConst(realSort, "x");
 62  Term y = tm.mkConst(realSort, "y");
 63  Term a = tm.mkConst(intSort, "a");
 64  Term b = tm.mkConst(intSort, "b");
 65  //! [docs-cpp-quickstart-5 end]
 66
 67  // Our constraints regarding x and y will be:
 68  //
 69  //   (1)  0 < x
 70  //   (2)  0 < y
 71  //   (3)  x + y < 1
 72  //   (4)  x <= y
 73  //
 74
 75  //! [docs-cpp-quickstart-6 start]
 76  // Formally, constraints are also terms. Their sort is Boolean.
 77  // We will construct these constraints gradually,
 78  // by defining each of their components.
 79  // We start with the constant numerals 0 and 1:
 80  Term zero = tm.mkReal(0);
 81  Term one = tm.mkReal(1);
 82
 83  // Next, we construct the term x + y
 84  Term xPlusY = tm.mkTerm(Kind::ADD, {x, y});
 85
 86  // Now we can define the constraints.
 87  // They use the operators +, <=, and <.
 88  // In the API, these are denoted by ADD, LEQ, and LT.
 89  // A list of available operators is available in:
 90  // src/api/cpp/cvc5_kind.h
 91  Term constraint1 = tm.mkTerm(Kind::LT, {zero, x});
 92  Term constraint2 = tm.mkTerm(Kind::LT, {zero, y});
 93  Term constraint3 = tm.mkTerm(Kind::LT, {xPlusY, one});
 94  Term constraint4 = tm.mkTerm(Kind::LEQ, {x, y});
 95
 96  // Now we assert the constraints to the solver.
 97  solver.assertFormula(constraint1);
 98  solver.assertFormula(constraint2);
 99  solver.assertFormula(constraint3);
100  solver.assertFormula(constraint4);
101  //! [docs-cpp-quickstart-6 end]
102
103  // Check if the formula is satisfiable, that is,
104  // are there real values for x and y that satisfy all the constraints?
105  //! [docs-cpp-quickstart-7 start]
106  Result r1 = solver.checkSat();
107  //! [docs-cpp-quickstart-7 end]
108
109  // The result is either SAT, UNSAT, or UNKNOWN.
110  // In this case, it is SAT.
111  //! [docs-cpp-quickstart-8 start]
112  std::cout << "expected: sat" << std::endl;
113  std::cout << "result: " << r1 << std::endl;
114  //! [docs-cpp-quickstart-8 end]
115
116  // We can get the values for x and y that satisfy the constraints.
117  //! [docs-cpp-quickstart-9 start]
118  Term xVal = solver.getValue(x);
119  Term yVal = solver.getValue(y);
120  //! [docs-cpp-quickstart-9 end]
121
122  // It is also possible to get values for compound terms,
123  // even if those did not appear in the original formula.
124  //! [docs-cpp-quickstart-10 start]
125  Term xMinusY = tm.mkTerm(Kind::SUB, {x, y});
126  Term xMinusYVal = solver.getValue(xMinusY);
127  //! [docs-cpp-quickstart-10 end]
128
129  // We can now obtain the string representations of the values.
130  //! [docs-cpp-quickstart-11 start]
131  std::string xStr = xVal.getRealValue();
132  std::string yStr = yVal.getRealValue();
133  std::string xMinusYStr = xMinusYVal.getRealValue();
134
135  std::cout << "value for x: " << xStr << std::endl;
136  std::cout << "value for y: " << yStr << std::endl;
137  std::cout << "value for x - y: " << xMinusYStr << std::endl;
138  //! [docs-cpp-quickstart-11 end]
139
140  //! [docs-cpp-quickstart-12 start]
141  // Further, we can convert the values to cpp types
142  std::pair<int64_t, uint64_t> xPair = xVal.getReal64Value();
143  std::pair<int64_t, uint64_t> yPair = yVal.getReal64Value();
144  std::pair<int64_t, uint64_t> xMinusYPair = xMinusYVal.getReal64Value();
145
146  std::cout << "value for x: " << xPair.first << "/" << xPair.second
147            << std::endl;
148  std::cout << "value for y: " << yPair.first << "/" << yPair.second
149            << std::endl;
150  std::cout << "value for x - y: " << xMinusYPair.first << "/"
151            << xMinusYPair.second << std::endl;
152  //! [docs-cpp-quickstart-12 end]
153
154  // Another way to independently compute the value of x - y would be
155  // to perform the (rational) arithmetic manually.
156  // However, for more complex terms,
157  // it is easier to let the solver do the evaluation.
158  //! [docs-cpp-quickstart-13 start]
159  std::pair<int64_t, uint64_t> xMinusYComputed = {
160    xPair.first * yPair.second - xPair.second * yPair.first,
161    xPair.second * yPair.second
162  };
163  uint64_t g = std::gcd(xMinusYComputed.first, xMinusYComputed.second);
164  xMinusYComputed = { xMinusYComputed.first / g, xMinusYComputed.second / g };
165  if (xMinusYComputed == xMinusYPair)
166  {
167    std::cout << "computed correctly" << std::endl;
168  }
169  else
170  {
171    std::cout << "computed incorrectly" << std::endl;
172  }
173  //! [docs-cpp-quickstart-13 end]
174
175  // Next, we will check satisfiability of the same formula,
176  // only this time over integer variables a and b.
177
178  // We start by resetting assertions added to the solver.
179  //! [docs-cpp-quickstart-14 start]
180  solver.resetAssertions();
181  //! [docs-cpp-quickstart-14 end]
182
183  // Next, we assert the same assertions above with integers.
184  // This time, we inline the construction of terms
185  // to the assertion command.
186  //! [docs-cpp-quickstart-15 start]
187  solver.assertFormula(tm.mkTerm(Kind::LT, {tm.mkInteger(0), a}));
188  solver.assertFormula(tm.mkTerm(Kind::LT, {tm.mkInteger(0), b}));
189  solver.assertFormula(
190      tm.mkTerm(Kind::LT, {tm.mkTerm(Kind::ADD, {a, b}), tm.mkInteger(1)}));
191  solver.assertFormula(tm.mkTerm(Kind::LEQ, {a, b}));
192  //! [docs-cpp-quickstart-15 end]
193
194  // We check whether the revised assertion is satisfiable.
195  //! [docs-cpp-quickstart-16 start]
196  Result r2 = solver.checkSat();
197  //! [docs-cpp-quickstart-16 end]
198
199  // This time the formula is unsatisfiable
200  //! [docs-cpp-quickstart-17 start]
201  std::cout << "expected: unsat" << std::endl;
202  std::cout << "result: " << r2 << std::endl;
203  //! [docs-cpp-quickstart-17 end]
204
205  // We can query the solver for an unsatisfiable core, i.e., a subset
206  // of the assertions that is already unsatisfiable.
207  //! [docs-cpp-quickstart-18 start]
208  std::vector<Term> unsatCore = solver.getUnsatCore();
209  std::cout << "unsat core size: " << unsatCore.size() << std::endl;
210  std::cout << "unsat core: " << std::endl;
211  for (const Term& t : unsatCore)
212  {
213    std::cout << t << std::endl;
214  }
215  //! [docs-cpp-quickstart-18 end]
216
217  return 0;
218}