JavaSMT is a common API layer for accessing various SMT solvers. The API is optimized for performance (using JavaSMT has very little runtime overhead compared to using the solver API directly), customizability (features and settings exposed by various solvers should be visible through the wrapping layer) and type-safety (it shouldn't be possible to add boolean terms to integer ones at compile time) sometimes at the cost of verbosity.
Getting Started | Documentation | Known Issues | Documentation for Developers | Changelog | Configuration Options
JavaSMT can express formulas in the following theories:
The concrete support for a certain theory depends on the underlying SMT solver. Only a few SMT solvers provide support for theories like Arrays, Floating Point, String or RegEx.
JavaSMT supports several SMT solvers (see Getting Started for installation):
| SMT Solver | Linux x64 | Linux arm64 | Windows x64 | Windows arm64 | MacOS x64 | MacOS arm64 | Description |
|---|---|---|---|---|---|---|---|
| Bitwuzla | :heavy_check_mark:² | :heavy_check_mark:² | :heavy_check_mark: | a fast solver for bitvector logic | |||
| Boolector | :heavy_check_mark: | a fast solver for bitvector logic, misses formula introspection, deprecated | |||||
| CVC4 | :heavy_check_mark: | ||||||
| CVC5 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | |
| MathSAT5 | :heavy_check_mark:³ | :heavy_check_mark:³ | :heavy_check_mark: | maybe⁴ | |||
| OpenSMT | :heavy_check_mark:² | :heavy_check_mark:² | |||||
| OptiMathSAT | :heavy_check_mark: | based on MathSAT5, with support for optimization queries | |||||
| Princess | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Java-based SMT solver |
| SMTInterpol | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Java-based SMT solver |
| Yices2 | :heavy_check_mark: | :heavy_check_mark: | maybe⁴ | ||||
| Z3 | :heavy_check_mark:³ | :heavy_check_mark:³ | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | mature and well-known solver |
| Z3_WITH_INTERPOLATION | :heavy_check_mark: | :heavy_check_mark: | an older version of Z3 that still provides interpolation support |
We support a reasonable list of operating systems and versions.
On all operating systems and architectures, JavaSMT requires Java 11 or newer.
Unless otherwise noted, the solver requires a minimum of GLIBC_2.28 on Linux,
available with Ubuntu 18.04 or later.
² Solver requires at least GLIBC_2.29/GLIBCXX_3.4.26 or GLIBC_2.34/GLIBCXX_3.4.29,
available with Ubuntu 22.04 or later.
³ Solver requires at least GLIBC_2.38/GLIBCXX_3.4.31,
available with Ubuntu 24.04 or later.
⁴ We do not provide a signed solver library for macOS. The user needs to compile and sign it.
The following features are supported (depending on the used SMT solver):
We aim for supporting more important features, more SMT solvers, and more systems. If something specific is missing, please look for or file an issue.
| SMT Solver | Concurrent context usage⁵ | Concurrent prover usage⁶ |
|---|---|---|
| Bitwuzla | :heavy_check_mark: | |
| Boolector | :heavy_check_mark: | |
| CVC4 | :heavy_check_mark: | :heavy_check_mark: |
| CVC5 | :heavy_check_mark: | |
| MathSAT5 | :heavy_check_mark: | |
| OpenSMT | :heavy_check_mark: | |
| OptiMathSAT | :heavy_check_mark: | |
| Princess | :heavy_check_mark: | |
| SMTInterpol | :heavy_check_mark: | |
| Yices2 | :heavy_check_mark: | |
| Z3 | :heavy_check_mark: |
Interruption using a ShutdownNotifier may be used to interrupt a solver from any thread. Formulas are translatable in between contexts/provers/threads using FormulaManager.translateFrom().
⁵ Multiple contexts, but all operations on each context only from a single thread.
⁶ Multiple provers on one or more contexts, with each prover using its own thread.
JavaSMT exposes an API for performing garbage collection on solvers implemented in a native language. As a native solver has no way of knowing whether the created formula object is still referenced by the client application, this API is necessary to avoid leaking memory. Note that several solvers already support hash consing and thus, there is never more than one copy of an identical formula object in memory. Consequently, if all created formulas are later re-used (or re-created) in the application, it is not necessary to perform any garbage collection at all. Additionally, the memory for formulas created on user-side (i.e., via JavaSMT) is negligible compared to solver-internal memory-consumption when solving a hard SMT query.
solver.z3.usePhantomReferences may be used to control
whether JavaSMT will attempt to decrease references on Z3 formula objects
once they are no longer referenced.Installation is possible via Maven, Ivy, or manually. Please see our Getting Started Guide.
// Instantiate JavaSMT with SMTInterpol as backend (for dependencies cf. documentation)
try (SolverContext context = SolverContextFactory.createSolverContext(
config, logger, shutdownNotifier, Solvers.SMTINTERPOL)) {
IntegerFormulaManager imgr = context.getFormulaManager().getIntegerFormulaManager();
// Create formula "a = b" with two integer variables
IntegerFormula a = imgr.makeVariable("a");
IntegerFormula b = imgr.makeVariable("b");
BooleanFormula f = imgr.equal(a, b);
// Solve formula, get model, and print variable assignment
try (ProverEnvironment prover = context.newProverEnvironment(ProverOptions.GENERATE_MODELS)) {
prover.addConstraint(f);
boolean isUnsat = prover.isUnsat();
assert !isUnsat;
try (Model model = prover.getModel()) {
System.out.printf("SAT with a = %s, b = %s", model.evaluate(a), model.evaluate(b));
}
}
}
You can find several example implementations in the folder org/sosy_lab/java_smt/example.
These include examples for the usage of many of JavaSMTs solvers and features, for example:
nqueens_user_propagator.Furthermore, JavaSMT provides users with additional features available for many solvers:
useDebugMode=true, in the configuration used to create a
SolverContext, applies additional checks to catch common usage errors. The checks
performed by this option are solver-sensitive and throw exceptions on operations that
are disallowed by a particular solver. For example, for most solvers, adding a constraint to a
ProverEnvironment may only be allowed iff the constraint has been built by the same
SolverContext that also created the used ProverEnvironment. Violating this rule while in
debug-mode with a solver that does not allow this throws a IllegalArgumentException with
information about the problem.synchronize=true, in the configuration used to create a
SolverContext, all solver actions are synchronized with the owning instance being the
SolverContext. This allows concurrent access, but strictly sequentializes all operations.collectStatistics=true, in the configuration used to create a
SolverContext, counts all operations and interactions towards the SMT solver of this context.
These statistics can be access in the SolverContext.useLogger=true, in the configuration used to create a
SolverContext, logs all solver actions. Logging operations might slow down
usage of JavaSMT though.BooleanFormulaManager.toDisjunctionArgs(), returning a set of formulas such that a
disjunction over them is equivalent to the input formula, or FormulaManager. extractVariablesAndUFs(), which can be used to extract the names of all free variables and
UFs in a formula. The visitor-pattern can also be used with user-defined operations.Many more options are available to configure SMT solvers by setting them in the Configuration
given to an SolverContext, including SMT solvers native configuration options.
(top 30 of 36)
SMT
57.4%
Java
34.3%
C++
5.0%
C
1.6%
JavaSMT is a common API layer for accessing various SMT solvers. The API is optimized for performance (using JavaSMT has very little runtime overhead compared to using the solver API directly), customizability (features and settings exposed by various solvers should be visible through the wrapping layer) and type-safety (it shouldn't be possible to add boolean terms to integer ones at compile time) sometimes at the cost of verbosity.
Getting Started | Documentation | Known Issues | Documentation for Developers | Changelog | Configuration Options
JavaSMT can express formulas in the following theories:
The concrete support for a certain theory depends on the underlying SMT solver. Only a few SMT solvers provide support for theories like Arrays, Floating Point, String or RegEx.
JavaSMT supports several SMT solvers (see Getting Started for installation):
| SMT Solver | Linux x64 | Linux arm64 | Windows x64 | Windows arm64 | MacOS x64 | MacOS arm64 | Description |
|---|---|---|---|---|---|---|---|
| Bitwuzla | :heavy_check_mark:² | :heavy_check_mark:² | :heavy_check_mark: | a fast solver for bitvector logic | |||
| Boolector | :heavy_check_mark: | a fast solver for bitvector logic, misses formula introspection, deprecated | |||||
| CVC4 | :heavy_check_mark: | ||||||
| CVC5 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | |
| MathSAT5 | :heavy_check_mark:³ | :heavy_check_mark:³ | :heavy_check_mark: | maybe⁴ | |||
| OpenSMT | :heavy_check_mark:² | :heavy_check_mark:² | |||||
| OptiMathSAT | :heavy_check_mark: | based on MathSAT5, with support for optimization queries | |||||
| Princess | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Java-based SMT solver |
| SMTInterpol | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Java-based SMT solver |
| Yices2 | :heavy_check_mark: | :heavy_check_mark: | maybe⁴ | ||||
| Z3 | :heavy_check_mark:³ | :heavy_check_mark:³ | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | mature and well-known solver |
| Z3_WITH_INTERPOLATION | :heavy_check_mark: | :heavy_check_mark: | an older version of Z3 that still provides interpolation support |
We support a reasonable list of operating systems and versions.
On all operating systems and architectures, JavaSMT requires Java 11 or newer.
Unless otherwise noted, the solver requires a minimum of GLIBC_2.28 on Linux,
available with Ubuntu 18.04 or later.
² Solver requires at least GLIBC_2.29/GLIBCXX_3.4.26 or GLIBC_2.34/GLIBCXX_3.4.29,
available with Ubuntu 22.04 or later.
³ Solver requires at least GLIBC_2.38/GLIBCXX_3.4.31,
available with Ubuntu 24.04 or later.
⁴ We do not provide a signed solver library for macOS. The user needs to compile and sign it.
The following features are supported (depending on the used SMT solver):
We aim for supporting more important features, more SMT solvers, and more systems. If something specific is missing, please look for or file an issue.
| SMT Solver | Concurrent context usage⁵ | Concurrent prover usage⁶ |
|---|---|---|
| Bitwuzla | :heavy_check_mark: | |
| Boolector | :heavy_check_mark: | |
| CVC4 | :heavy_check_mark: | :heavy_check_mark: |
| CVC5 | :heavy_check_mark: | |
| MathSAT5 | :heavy_check_mark: | |
| OpenSMT | :heavy_check_mark: | |
| OptiMathSAT | :heavy_check_mark: | |
| Princess | :heavy_check_mark: | |
| SMTInterpol | :heavy_check_mark: | |
| Yices2 | :heavy_check_mark: | |
| Z3 | :heavy_check_mark: |
Interruption using a ShutdownNotifier may be used to interrupt a solver from any thread. Formulas are translatable in between contexts/provers/threads using FormulaManager.translateFrom().
⁵ Multiple contexts, but all operations on each context only from a single thread.
⁶ Multiple provers on one or more contexts, with each prover using its own thread.
JavaSMT exposes an API for performing garbage collection on solvers implemented in a native language. As a native solver has no way of knowing whether the created formula object is still referenced by the client application, this API is necessary to avoid leaking memory. Note that several solvers already support hash consing and thus, there is never more than one copy of an identical formula object in memory. Consequently, if all created formulas are later re-used (or re-created) in the application, it is not necessary to perform any garbage collection at all. Additionally, the memory for formulas created on user-side (i.e., via JavaSMT) is negligible compared to solver-internal memory-consumption when solving a hard SMT query.
solver.z3.usePhantomReferences may be used to control
whether JavaSMT will attempt to decrease references on Z3 formula objects
once they are no longer referenced.Installation is possible via Maven, Ivy, or manually. Please see our Getting Started Guide.
// Instantiate JavaSMT with SMTInterpol as backend (for dependencies cf. documentation)
try (SolverContext context = SolverContextFactory.createSolverContext(
config, logger, shutdownNotifier, Solvers.SMTINTERPOL)) {
IntegerFormulaManager imgr = context.getFormulaManager().getIntegerFormulaManager();
// Create formula "a = b" with two integer variables
IntegerFormula a = imgr.makeVariable("a");
IntegerFormula b = imgr.makeVariable("b");
BooleanFormula f = imgr.equal(a, b);
// Solve formula, get model, and print variable assignment
try (ProverEnvironment prover = context.newProverEnvironment(ProverOptions.GENERATE_MODELS)) {
prover.addConstraint(f);
boolean isUnsat = prover.isUnsat();
assert !isUnsat;
try (Model model = prover.getModel()) {
System.out.printf("SAT with a = %s, b = %s", model.evaluate(a), model.evaluate(b));
}
}
}
You can find several example implementations in the folder org/sosy_lab/java_smt/example.
These include examples for the usage of many of JavaSMTs solvers and features, for example:
nqueens_user_propagator.Furthermore, JavaSMT provides users with additional features available for many solvers:
useDebugMode=true, in the configuration used to create a
SolverContext, applies additional checks to catch common usage errors. The checks
performed by this option are solver-sensitive and throw exceptions on operations that
are disallowed by a particular solver. For example, for most solvers, adding a constraint to a
ProverEnvironment may only be allowed iff the constraint has been built by the same
SolverContext that also created the used ProverEnvironment. Violating this rule while in
debug-mode with a solver that does not allow this throws a IllegalArgumentException with
information about the problem.synchronize=true, in the configuration used to create a
SolverContext, all solver actions are synchronized with the owning instance being the
SolverContext. This allows concurrent access, but strictly sequentializes all operations.collectStatistics=true, in the configuration used to create a
SolverContext, counts all operations and interactions towards the SMT solver of this context.
These statistics can be access in the SolverContext.useLogger=true, in the configuration used to create a
SolverContext, logs all solver actions. Logging operations might slow down
usage of JavaSMT though.BooleanFormulaManager.toDisjunctionArgs(), returning a set of formulas such that a
disjunction over them is equivalent to the input formula, or FormulaManager. extractVariablesAndUFs(), which can be used to extract the names of all free variables and
UFs in a formula. The visitor-pattern can also be used with user-defined operations.Many more options are available to configure SMT solvers by setting them in the Configuration
given to an SolverContext, including SMT solvers native configuration options.
(top 30 of 36)
SMT
57.4%
Java
34.3%
C++
5.0%
C
1.6%