/****************************************************************************** * Top contributors (to current version): * Yoni Zohar, Aina Niemetz, Gereon Kremer * * This file is part of the cvc5 project. * * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS * in the top-level source directory and their institutional affiliations. * All rights reserved. See the file COPYING in the top-level source * directory for licensing information. * **************************************************************************** * * A simple demonstration of the API capabilities of cvc5. * */ #include #include #include using namespace cvc5; int main() { // Create a term manager //! [docs-cpp-quickstart-0 start] TermManager tm; //! [docs-cpp-quickstart-0 end] // Create a solver //! [docs-cpp-quickstart-1 start] Solver solver(tm); //! [docs-cpp-quickstart-1 end] // We will ask the solver to produce models and unsat cores, // hence these options should be turned on. //! [docs-cpp-quickstart-2 start] solver.setOption("produce-models", "true"); solver.setOption("produce-unsat-cores", "true"); //! [docs-cpp-quickstart-2 end] // 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". // Set the logic //! [docs-cpp-quickstart-3 start] solver.setLogic("ALL"); //! [docs-cpp-quickstart-3 end] // In this example, we will define constraints over reals and integers. // Hence, we first obtain the corresponding sorts. //! [docs-cpp-quickstart-4 start] Sort realSort = tm.getRealSort(); Sort intSort = tm.getIntegerSort(); //! [docs-cpp-quickstart-4 end] // x and y will be real variables, while a and b will be integer variables. // Formally, their cpp type is Term, // and they are called "constants" in SMT jargon: //! [docs-cpp-quickstart-5 start] Term x = tm.mkConst(realSort, "x"); Term y = tm.mkConst(realSort, "y"); Term a = tm.mkConst(intSort, "a"); Term b = tm.mkConst(intSort, "b"); //! [docs-cpp-quickstart-5 end] // Our constraints regarding x and y will be: // // (1) 0 < x // (2) 0 < y // (3) x + y < 1 // (4) x <= y // //! [docs-cpp-quickstart-6 start] // 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); //! [docs-cpp-quickstart-6 end] // Check if the formula is satisfiable, that is, // are there real values for x and y that satisfy all the constraints? //! [docs-cpp-quickstart-7 start] Result r1 = solver.checkSat(); //! [docs-cpp-quickstart-7 end] // The result is either SAT, UNSAT, or UNKNOWN. // In this case, it is SAT. //! [docs-cpp-quickstart-8 start] std::cout << "expected: sat" << std::endl; std::cout << "result: " << r1 << std::endl; //! [docs-cpp-quickstart-8 end] // We can get the values for x and y that satisfy the constraints. //! [docs-cpp-quickstart-9 start] Term xVal = solver.getValue(x); Term yVal = solver.getValue(y); //! [docs-cpp-quickstart-9 end] // It is also possible to get values for compound terms, // even if those did not appear in the original formula. //! [docs-cpp-quickstart-10 start] Term xMinusY = tm.mkTerm(Kind::SUB, {x, y}); Term xMinusYVal = solver.getValue(xMinusY); //! [docs-cpp-quickstart-10 end] // We can now obtain the string representations of the values. //! [docs-cpp-quickstart-11 start] 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; //! [docs-cpp-quickstart-11 end] //! [docs-cpp-quickstart-12 start] // Further, we can convert the values to cpp types std::pair xPair = xVal.getReal64Value(); std::pair yPair = yVal.getReal64Value(); std::pair 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; //! [docs-cpp-quickstart-12 end] // 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. //! [docs-cpp-quickstart-13 start] std::pair 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; } //! [docs-cpp-quickstart-13 end] // Next, we will check satisfiability of the same formula, // only this time over integer variables a and b. // We start by resetting assertions added to the solver. //! [docs-cpp-quickstart-14 start] solver.resetAssertions(); //! [docs-cpp-quickstart-14 end] // Next, we assert the same assertions above with integers. // This time, we inline the construction of terms // to the assertion command. //! [docs-cpp-quickstart-15 start] 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})); //! [docs-cpp-quickstart-15 end] // We check whether the revised assertion is satisfiable. //! [docs-cpp-quickstart-16 start] Result r2 = solver.checkSat(); //! [docs-cpp-quickstart-16 end] // This time the formula is unsatisfiable //! [docs-cpp-quickstart-17 start] std::cout << "expected: unsat" << std::endl; std::cout << "result: " << r2 << std::endl; //! [docs-cpp-quickstart-17 end] // We can query the solver for an unsatisfiable core, i.e., a subset // of the assertions that is already unsatisfiable. //! [docs-cpp-quickstart-18 start] std::vector 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; } //! [docs-cpp-quickstart-18 end] return 0; }