Quickstart Guide

First, create a cvc5 Cvc5TermManager instance:

Cvc5TermManager* tm = cvc5_term_manager_new();

Then, create a Cvc5 solver instance:

Cvc5* slv = cvc5_new(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.

cvc5_set_option(slv, "produce-models", "true");
cvc5_set_option(slv, "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".

cvc5_set_logic(slv, "ALL");

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

Cvc5Sort real_sort = cvc5_get_real_sort(tm);
Cvc5Sort int_sort = cvc5_get_integer_sort(tm);

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.

Cvc5Term x = cvc5_mk_const(tm, real_sort, "x");
Cvc5Term y = cvc5_mk_const(tm, real_sort, "y");
Cvc5Term a = cvc5_mk_const(tm, int_sort, "a");
Cvc5Term b = cvc5_mk_const(tm, int_sort, "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:
Cvc5Term zero = cvc5_mk_real_int64(tm, 0);
Cvc5Term one = cvc5_mk_real_int64(tm, 1);

// Next, we construct the term x + y
Cvc5Term args2[2] = {x, y};
Cvc5Term x_plus_y = cvc5_mk_term(tm, CVC5_KIND_ADD, 2, args2);

// 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
args2[0] = zero;
args2[1] = x;
Cvc5Term constraint1 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
args2[1] = y;
Cvc5Term constraint2 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
args2[0] = x_plus_y;
args2[1] = one;
Cvc5Term constraint3 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
args2[0] = x;
args2[1] = y;
Cvc5Term constraint4 = cvc5_mk_term(tm, CVC5_KIND_LEQ, 2, args2);

// Now we assert the constraints to the solver.
cvc5_assert_formula(slv, constraint1);
cvc5_assert_formula(slv, constraint2);
cvc5_assert_formula(slv, constraint3);
cvc5_assert_formula(slv, 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.

Cvc5Result r = cvc5_check_sat(slv);

The result we get from this satisfiability check is either sat, unsat or unknown. It’s status can be queried via cvc5_result_is_sat(), cvc5_result_is_unsat() and cvc5_result_is_unknown(). Alternatively, it can also be printed.

printf("expected: sat\n");
printf("result: %s\n", cvc5_result_to_string(r));

This will print:

expected: sat
result: sat

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

Cvc5Term x_val = cvc5_get_value(slv, x);
Cvc5Term y_val = cvc5_get_value(slv, y);

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

args2[0] = x;
args2[1] = y;
Cvc5Term x_minus_y = cvc5_mk_term(tm, CVC5_KIND_SUB, 2, args2);
Cvc5Term x_minus_y_val = cvc5_get_value(slv, x_minus_y);

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

// Note: The const char* returned by cvc5_term_get_real_value is only valid
//       until the next call to this function.
char* x_str = strdup(cvc5_term_get_real_value(x_val));
char* y_str = strdup(cvc5_term_get_real_value(y_val));
char* x_minus_y_str = strdup(cvc5_term_get_real_value(x_minus_y_val));

printf("value for x: %s\n", x_str);
printf("value for y: %s\n", y_str);
printf("value for x - y: %s\n", x_minus_y_str);

free(y_str);
free(x_str);
free(x_minus_y_str);

// Alternatively, you can directly print the value strings without
// copying them first:
printf("value for x: %s\n", cvc5_term_get_real_value(x_val));
printf("value for y: %s\n", cvc5_term_get_real_value(y_val));
printf("value for x - y: %s\n", cvc5_term_get_real_value(x_minus_y_val));

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
int64_t x_num;
uint64_t x_den;
cvc5_term_get_real64_value(x_val, &x_num, &x_den);
int64_t y_num;
uint64_t y_den;
cvc5_term_get_real64_value(y_val, &y_num, &y_den);
int64_t x_minus_y_num;
uint64_t x_minus_y_den;
cvc5_term_get_real64_value(x_minus_y_val, &x_minus_y_num, &x_minus_y_den);

printf("value for x: %" PRId64 "/%" PRIu64 "\n", x_num, x_den);
printf("value for y: %" PRId64 "/%" PRIu64 "\n", y_num, y_den);
printf("value for x - y: %" PRId64 "/%" PRIu64 "\n", x_minus_y_num, x_minus_y_den);

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.

int64_t x_minus_y_num_computed = x_num * y_den - x_den * y_num;
uint64_t x_minus_y_den_computed = x_den * y_den;
uint64_t g = gcd(x_minus_y_num_computed, x_minus_y_den_computed);
x_minus_y_num_computed = x_minus_y_num_computed / g;
x_minus_y_den_computed = x_minus_y_den_computed / g;
if (x_minus_y_num_computed == x_minus_y_num
    && x_minus_y_den_computed == x_minus_y_den)
{
  printf("computed correctly\n");
}
else
{
  printf("computed incorrectly\n");
}

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.

cvc5_reset_assertions(slv);

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

args2[0] = cvc5_mk_integer_int64(tm, 0);
args2[1] = a;
cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
args2[1] = b;
cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
args2[0] = a;
args2[1] = b;
Cvc5Term add = cvc5_mk_term(tm, CVC5_KIND_ADD, 2, args2);
args2[0] = add;
args2[1] = cvc5_mk_integer_int64(tm, 1);
cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
args2[0] = a;
args2[1] = b;
cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LEQ, 2, args2));

Now, we check whether the revised assertion is satisfiable.

cvc5_result_release(r);  // optional, not needed anymore so we can release
r = cvc5_check_sat(slv);
printf("expected: unsat\n");
printf("result: %s\n", cvc5_result_to_string(r));

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.

size_t size;
const Cvc5Term* unsat_core = cvc5_get_unsat_core(slv, &size);
printf("unsat core size: %zu\n", size);
printf("unsat core: \n");
for (size_t i = 0; i < size; i++)
{
  printf("%s\n", cvc5_term_to_string(unsat_core[i]));
}

This will print:

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

Example

examples/api/c/quickstart.c

  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/c/cvc5.h>
 15#include <stdio.h>
 16#include <stdlib.h>
 17#include <string.h>
 18#include <inttypes.h>
 19
 20int32_t gcd(int32_t a, int32_t b)
 21{
 22  int32_t remainder = a % b;
 23
 24  if (remainder == 0)
 25  {
 26    return b;
 27  }
 28
 29  return gcd(b, remainder);
 30}
 31
 32int main()
 33{
 34  // Create a term manager
 35  //! [docs-c-quickstart-0 start]
 36  Cvc5TermManager* tm = cvc5_term_manager_new();
 37  //! [docs-c-quickstart-0 end]
 38  // Create a solver
 39  //! [docs-c-quickstart-1 start]
 40  Cvc5* slv = cvc5_new(tm);
 41  //! [docs-c-quickstart-1 end]
 42
 43  // We will ask the solver to produce models and unsat cores,
 44  // hence these options should be turned on.
 45  //! [docs-c-quickstart-2 start]
 46  cvc5_set_option(slv, "produce-models", "true");
 47  cvc5_set_option(slv, "produce-unsat-cores", "true");
 48  //! [docs-c-quickstart-2 end]
 49
 50  // The simplest way to set a logic for the solver is to choose "ALL".
 51  // This enables all logics in the solver.
 52  // Alternatively, "QF_ALL" enables all logics without quantifiers.
 53  // To optimize the solver's behavior for a more specific logic,
 54  // use the logic name, e.g. "QF_BV" or "QF_AUFBV".
 55
 56  // Set the logic
 57  //! [docs-c-quickstart-3 start]
 58  cvc5_set_logic(slv, "ALL");
 59  //! [docs-c-quickstart-3 end]
 60
 61  // In this example, we will define constraints over reals and integers.
 62  // Hence, we first obtain the corresponding sorts.
 63  //! [docs-c-quickstart-4 start]
 64  Cvc5Sort real_sort = cvc5_get_real_sort(tm);
 65  Cvc5Sort int_sort = cvc5_get_integer_sort(tm);
 66  //! [docs-c-quickstart-4 end]
 67
 68  // x and y will be real variables, while a and b will be integer variables.
 69  // Formally, their cpp type is Term,
 70  // and they are called "constants" in SMT jargon:
 71  //! [docs-c-quickstart-5 start]
 72  Cvc5Term x = cvc5_mk_const(tm, real_sort, "x");
 73  Cvc5Term y = cvc5_mk_const(tm, real_sort, "y");
 74  Cvc5Term a = cvc5_mk_const(tm, int_sort, "a");
 75  Cvc5Term b = cvc5_mk_const(tm, int_sort, "b");
 76  //! [docs-c-quickstart-5 end]
 77
 78  // Our constraints regarding x and y will be:
 79  //
 80  //   (1)  0 < x
 81  //   (2)  0 < y
 82  //   (3)  x + y < 1
 83  //   (4)  x <= y
 84  //
 85
 86  //! [docs-c-quickstart-6 start]
 87  // Formally, constraints are also terms. Their sort is Boolean.
 88  // We will construct these constraints gradually,
 89  // by defining each of their components.
 90  // We start with the constant numerals 0 and 1:
 91  Cvc5Term zero = cvc5_mk_real_int64(tm, 0);
 92  Cvc5Term one = cvc5_mk_real_int64(tm, 1);
 93
 94  // Next, we construct the term x + y
 95  Cvc5Term args2[2] = {x, y};
 96  Cvc5Term x_plus_y = cvc5_mk_term(tm, CVC5_KIND_ADD, 2, args2);
 97
 98  // Now we can define the constraints.
 99  // They use the operators +, <=, and <.
100  // In the API, these are denoted by ADD, LEQ, and LT.
101  // A list of available operators is available in:
102  // src/api/cpp/cvc5_kind.h
103  args2[0] = zero;
104  args2[1] = x;
105  Cvc5Term constraint1 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
106  args2[1] = y;
107  Cvc5Term constraint2 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
108  args2[0] = x_plus_y;
109  args2[1] = one;
110  Cvc5Term constraint3 = cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2);
111  args2[0] = x;
112  args2[1] = y;
113  Cvc5Term constraint4 = cvc5_mk_term(tm, CVC5_KIND_LEQ, 2, args2);
114
115  // Now we assert the constraints to the solver.
116  cvc5_assert_formula(slv, constraint1);
117  cvc5_assert_formula(slv, constraint2);
118  cvc5_assert_formula(slv, constraint3);
119  cvc5_assert_formula(slv, constraint4);
120  //! [docs-c-quickstart-6 end]
121
122  // Check if the formula is satisfiable, that is,
123  // are there real values for x and y that satisfy all the constraints?
124  //! [docs-c-quickstart-7 start]
125  Cvc5Result r = cvc5_check_sat(slv);
126  //! [docs-c-quickstart-7 end]
127
128  // The result is either SAT, UNSAT, or UNKNOWN.
129  // In this case, it is SAT.
130  //! [docs-c-quickstart-8 start]
131  printf("expected: sat\n");
132  printf("result: %s\n", cvc5_result_to_string(r));
133  //! [docs-c-quickstart-8 end]
134
135  // We can get the values for x and y that satisfy the constraints.
136  //! [docs-c-quickstart-9 start]
137  Cvc5Term x_val = cvc5_get_value(slv, x);
138  Cvc5Term y_val = cvc5_get_value(slv, y);
139  //! [docs-c-quickstart-9 end]
140
141  // It is also possible to get values for compound terms,
142  // even if those did not appear in the original formula.
143  //! [docs-c-quickstart-10 start]
144  args2[0] = x;
145  args2[1] = y;
146  Cvc5Term x_minus_y = cvc5_mk_term(tm, CVC5_KIND_SUB, 2, args2);
147  Cvc5Term x_minus_y_val = cvc5_get_value(slv, x_minus_y);
148  //! [docs-c-quickstart-10 end]
149
150  // We can now obtain the string representations of the values.
151  //! [docs-c-quickstart-11 start]
152  // Note: The const char* returned by cvc5_term_get_real_value is only valid
153  //       until the next call to this function.
154  char* x_str = strdup(cvc5_term_get_real_value(x_val));
155  char* y_str = strdup(cvc5_term_get_real_value(y_val));
156  char* x_minus_y_str = strdup(cvc5_term_get_real_value(x_minus_y_val));
157
158  printf("value for x: %s\n", x_str);
159  printf("value for y: %s\n", y_str);
160  printf("value for x - y: %s\n", x_minus_y_str);
161
162  free(y_str);
163  free(x_str);
164  free(x_minus_y_str);
165
166  // Alternatively, you can directly print the value strings without
167  // copying them first:
168  printf("value for x: %s\n", cvc5_term_get_real_value(x_val));
169  printf("value for y: %s\n", cvc5_term_get_real_value(y_val));
170  printf("value for x - y: %s\n", cvc5_term_get_real_value(x_minus_y_val));
171  //! [docs-c-quickstart-11 end]
172
173  //! [docs-c-quickstart-12 start]
174  // Further, we can convert the values to cpp types
175  int64_t x_num;
176  uint64_t x_den;
177  cvc5_term_get_real64_value(x_val, &x_num, &x_den);
178  int64_t y_num;
179  uint64_t y_den;
180  cvc5_term_get_real64_value(y_val, &y_num, &y_den);
181  int64_t x_minus_y_num;
182  uint64_t x_minus_y_den;
183  cvc5_term_get_real64_value(x_minus_y_val, &x_minus_y_num, &x_minus_y_den);
184
185  printf("value for x: %" PRId64 "/%" PRIu64 "\n", x_num, x_den);
186  printf("value for y: %" PRId64 "/%" PRIu64 "\n", y_num, y_den);
187  printf("value for x - y: %" PRId64 "/%" PRIu64 "\n", x_minus_y_num, x_minus_y_den);
188  //! [docs-c-quickstart-12 end]
189
190  // Another way to independently compute the value of x - y would be
191  // to perform the (rational) arithmetic manually.
192  // However, for more complex terms,
193  // it is easier to let the solver do the evaluation.
194  //! [docs-c-quickstart-13 start]
195  int64_t x_minus_y_num_computed = x_num * y_den - x_den * y_num;
196  uint64_t x_minus_y_den_computed = x_den * y_den;
197  uint64_t g = gcd(x_minus_y_num_computed, x_minus_y_den_computed);
198  x_minus_y_num_computed = x_minus_y_num_computed / g;
199  x_minus_y_den_computed = x_minus_y_den_computed / g;
200  if (x_minus_y_num_computed == x_minus_y_num
201      && x_minus_y_den_computed == x_minus_y_den)
202  {
203    printf("computed correctly\n");
204  }
205  else
206  {
207    printf("computed incorrectly\n");
208  }
209  //! [docs-c-quickstart-13 end]
210
211  // Next, we will check satisfiability of the same formula,
212  // only this time over integer variables a and b.
213
214  // We start by resetting assertions added to the solver.
215  //! [docs-c-quickstart-14 start]
216  cvc5_reset_assertions(slv);
217  //! [docs-c-quickstart-14 end]
218
219  // Next, we assert the same assertions above with integers.
220  // This time, we inline the construction of terms
221  // to the assertion command.
222  //! [docs-c-quickstart-15 start]
223  args2[0] = cvc5_mk_integer_int64(tm, 0);
224  args2[1] = a;
225  cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
226  args2[1] = b;
227  cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
228  args2[0] = a;
229  args2[1] = b;
230  Cvc5Term add = cvc5_mk_term(tm, CVC5_KIND_ADD, 2, args2);
231  args2[0] = add;
232  args2[1] = cvc5_mk_integer_int64(tm, 1);
233  cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LT, 2, args2));
234  args2[0] = a;
235  args2[1] = b;
236  cvc5_assert_formula(slv, cvc5_mk_term(tm, CVC5_KIND_LEQ, 2, args2));
237  //! [docs-c-quickstart-15 end]
238
239  // We check whether the revised assertion is satisfiable.
240  //! [docs-c-quickstart-16 start]
241  cvc5_result_release(r);  // optional, not needed anymore so we can release
242  r = cvc5_check_sat(slv);
243  //! [docs-c-quickstart-16 end]
244
245  // This time the formula is unsatisfiable
246  //! [docs-c-quickstart-17 start]
247  printf("expected: unsat\n");
248  printf("result: %s\n", cvc5_result_to_string(r));
249  //! [docs-c-quickstart-17 end]
250
251  // We can query the solver for an unsatisfiable core, i.e., a subset
252  // of the assertions that is already unsatisfiable.
253  //! [docs-c-quickstart-18 start]
254  size_t size;
255  const Cvc5Term* unsat_core = cvc5_get_unsat_core(slv, &size);
256  printf("unsat core size: %zu\n", size);
257  printf("unsat core: \n");
258  for (size_t i = 0; i < size; i++)
259  {
260    printf("%s\n", cvc5_term_to_string(unsat_core[i]));
261  }
262  //! [docs-c-quickstart-18 end]
263
264  // Delete solver instance.
265  //! [docs-c-quickstart-19 start]
266  cvc5_delete(slv);
267  //! [docs-c-quickstart-19 end]
268
269  // Delete term manager instance.
270  //! [docs-c-quickstart-20 start]
271  cvc5_term_manager_delete(tm);
272  //! [docs-c-quickstart-20 end]
273  return 0;
274}