Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
57cfd21
Allow setting custom options in tests with automatic re-initializatio…
baierd Mar 18, 2026
e8abb4e
Add ability to access most Z3 options and their types and description…
baierd Mar 18, 2026
f9a680b
Add Z3 options for logic, engine and "further options" with arbitrary…
baierd Mar 18, 2026
072c528
Add ability to Z3 provers to switch to HORN solving with spacer
baierd Mar 18, 2026
74b3ea7
Add todo about option types in Z3SolverContext
baierd Mar 18, 2026
dc19034
Add prototype for SolverNativeOptionsTest
baierd Mar 18, 2026
fe195a9
Simplify Z3 options resolving by adding the only cases in which the o…
baierd Mar 19, 2026
333d6be
Remove done TODO and rename method that transforms "further" options …
baierd Mar 19, 2026
cc399c6
Add Z3 tactic options (info) getter and remove done TODO
baierd Mar 19, 2026
3612918
Add more option constants and block them for users, and add the optio…
baierd Mar 19, 2026
6045d93
Remove options info getter in Z3FormulaManager to declutter the manager
baierd Mar 19, 2026
4ac91fd
Use solver with logic in all cases in Z3 that are not "all" and add c…
baierd Mar 19, 2026
b73d0ad
Rework and add more tests to SolverNativeOptionsTest
baierd Mar 20, 2026
55bd6e1
Unify setting additional options in SolverBasedTest0 and add more ver…
baierd Mar 20, 2026
1f468eb
Add explicit Z3 Prover constructor arguments for engine and logic (as…
baierd Mar 20, 2026
2bfd8fe
Refactor logic, engine and additional option resolving in Z3SolverCon…
baierd Mar 20, 2026
a96051e
Add Z3 specific logic tests that show that setting a logic can be ben…
baierd Mar 20, 2026
5613c8f
Add smt2 file provided by Thomas Haar (Dartagnan) for tests
baierd Mar 21, 2026
c91e7d0
Add Z3 native API tests for engine spacer and options
baierd Mar 21, 2026
62dcd1e
Merge branch 'master' into 613-feature-request-setting-solver-logic-v…
baierd Mar 24, 2026
c66319e
Add doc for (Z3) native tests and remove test that exists twice + add…
baierd Mar 24, 2026
42c797f
Add another smt2 file and a test that shows that Z3 solves much faste…
baierd Mar 24, 2026
4cfc98e
Modify SMTLIB2 file for tests so that the current parser accepts it
baierd Mar 24, 2026
de46233
Use formater of checkArgument to make message gen lazy in Z3SolverCon…
baierd Mar 24, 2026
dbaba17
Rename Z3_ENGINE into ENGINE (as it is only used in Z3 and checkstyle…
baierd Mar 24, 2026
a7dd394
Simplify method for creating new test environments with set configura…
baierd Mar 24, 2026
4cbac9f
Increase allowed amount of arguments in constructors in checkstyle to…
baierd Mar 24, 2026
97af0da
Fix naming of vars and JavaDoc in Z3NativeApiTest
baierd Mar 24, 2026
374aec8
Add timeouts to tests that change the default solving time to actuall…
baierd Mar 24, 2026
9d9551b
Switch assertion to hasSize() in SolverNativeOptionsTest
baierd Mar 24, 2026
cb48958
Allow Z3NativeApiTest to fail loading the libraries and not fail the …
baierd Mar 24, 2026
fa9ad2d
Apply refaster suggestions to Z3NativeApiTest
baierd Mar 24, 2026
c61c093
Merge branch 'master' into 613-feature-request-setting-solver-logic-v…
baierd Apr 7, 2026
7dd2127
Use new """ type string block for HORN SMT2 problem in SolverNativeOp…
baierd Apr 7, 2026
395da31
Add license files for 2 SMT2 files provided by Thomas Haas via Dartagnan
baierd Apr 7, 2026
ce0b38f
Improve license files for 2 SMT2 files provided by Thomas Haas via Da…
baierd Apr 7, 2026
e7b9e09
Add CC-BY-4.0 license to reuse LICENSE folder
baierd Apr 7, 2026
2836687
Improve string composition in SolverNativeOptionsTest test
baierd Apr 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions LICENSES/CC-BY-4.0.txt

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion build/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ SPDX-License-Identifier: Apache-2.0
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<module name="OuterTypeNumber"/>
<module name="ParameterNumber"/>
<module name="ParameterNumber">
<property name="max" value="8"/>
</module>
<module name="GenericWhitespace"/>
<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad">
Expand Down
8 changes: 8 additions & 0 deletions src/org/sosy_lab/java_smt/solvers/z3/Z3AbstractProver.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@
import org.sosy_lab.java_smt.api.SolverException;
import org.sosy_lab.java_smt.basicimpl.AbstractProverWithAllSat;
import org.sosy_lab.java_smt.basicimpl.CachingModel;
import org.sosy_lab.java_smt.solvers.z3.Z3SolverContext.ENGINE;

abstract class Z3AbstractProver extends AbstractProverWithAllSat<Void> {

protected final Z3FormulaCreator creator;
protected final long z3context;
protected final Z3FormulaManager mgr;

protected final ENGINE engine;
protected final Optional<String> logic;

private final UniqueIdGenerator trackId = new UniqueIdGenerator();
@Nullable private final Deque<PersistentMap<String, BooleanFormula>> storedConstraints;

Expand All @@ -51,12 +55,16 @@ abstract class Z3AbstractProver extends AbstractProverWithAllSat<Void> {
Z3AbstractProver(
Z3FormulaCreator pCreator,
Z3FormulaManager pMgr,
Optional<String> pLogic,
ENGINE pEngine,
Set<ProverOptions> pOptions,
@Nullable PathCounterTemplate pLogfile,
ShutdownNotifier pShutdownNotifier) {
super(pOptions, pMgr.getBooleanFormulaManager(), pShutdownNotifier);
creator = pCreator;
z3context = creator.getEnv();
logic = pLogic;
engine = pEngine;

if (pOptions.contains(ProverOptions.GENERATE_UNSAT_CORE)) {
storedConstraints = new ArrayDeque<>();
Expand Down
12 changes: 12 additions & 0 deletions src/org/sosy_lab/java_smt/solvers/z3/Z3FormulaManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,18 @@ assert getFormulaCreator().getFormulaType(expr) == FormulaType.BooleanType
return serialized;
}

/**
* Options string for all available options tied to the environment of the solver. The options are
* returned 1 per line, with the pattern:
*
* <p>optionName{.detailedName} (type) infoText that may include brackets (default: defaultValue)
*
* <p>e.g.: local_ctx (bool) perform local (i.e., cheap) context simplifications (default: false)
*/
String getAllZ3Options() {
return Native.simplifyGetHelp(getEnvironment());
}

@Override
protected Long simplify(Long pF) throws InterruptedException {
try {
Expand Down
196 changes: 196 additions & 0 deletions src/org/sosy_lab/java_smt/solvers/z3/Z3NativeApiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* This file is part of JavaSMT,
* an API wrapper for a collection of SMT solvers:
* https://github.com/sosy-lab/java-smt
*
* SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org>
*
* SPDX-License-Identifier: Apache-2.0
*/

package org.sosy_lab.java_smt.solvers.z3;

import static com.google.common.truth.Truth.assertThat;
import static org.sosy_lab.java_smt.solvers.z3.Z3SolverContext.doubleOptions;

import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.microsoft.z3.BoolSort;
import com.microsoft.z3.Context;
import com.microsoft.z3.Expr;
import com.microsoft.z3.FuncDecl;
import com.microsoft.z3.Global;
import com.microsoft.z3.IntSort;
import com.microsoft.z3.Model;
import com.microsoft.z3.Params;
import com.microsoft.z3.Solver;
import com.microsoft.z3.Sort;
import com.microsoft.z3.Status;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.AssumptionViolatedException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.sosy_lab.common.NativeLibraries;

public class Z3NativeApiTest {

@BeforeClass
public static void loadLibraries() {
try {
NativeLibraries.loadLibrary("z3");
NativeLibraries.loadLibrary("z3java");
} catch (UnsatisfiedLinkError e) {
throw new AssumptionViolatedException("Z3 is not available", e);
}

System.setProperty("z3.skipLibraryLoad", "true");
}

@Test
public void interpolationTest() {
// Translation of an example by Arie Gurfinkel for calculating interpolants with spacer in Z3
// See https://github.com/agurfinkel/spacer-on-jupyter/blob/master/Dagstuhl2019.ipynb for the
// original Python code

try (Context env = new Context()) {
Global.setParameter("smt.random_seed", "42");
Global.setParameter("model.compact", "false");

Expr<IntSort> a = env.mkIntConst("a");
Expr<IntSort> b = env.mkIntConst("b");
Expr<IntSort> x = env.mkIntConst("x");

Expr<BoolSort> formulasA = env.mkAnd(env.mkLt(a, x), env.mkLt(x, b));
Expr<BoolSort> formulasB = env.mkLt(b, a);

Expr<BoolSort> interpolant = interpolate(env, formulasA, formulasB);

assertThat(validate(env, formulasA, formulasB, interpolant)).isTrue();
}
}

// Test that we correctly discern between int and double for options set in Z3, even after
// updates! This is very much a white-box test that utilizes internal info about the
// Z3SolverContext implementation.
// If this fails: update the doubleOptions set with the missing option!
@Test
public void z3OptionsTypeTest() {
try (Context env = new Context()) {
String options = env.mkSolver().getHelp();
options += env.mkSimplifier("propagate-values").getHelp();
options += env.mkTactic("simplify").getHelp();
options += env.mkFixedpoint().getHelp();
options += env.mkOptimize().getHelp();

Set<String> optionsByLine = ImmutableSet.copyOf(Splitter.on('\n').splitToList(options));
ImmutableSet.Builder<String> doubleOptionsExtracted = ImmutableSet.builder();
for (String line : optionsByLine) {
// Z3 options encode the type always following the option name, e.g.: option.name (type)
if (line.contains("(double)")) {
List<String> optionAndSuffix = Splitter.on(" (double)").splitToList(line);
// Make sure there is only one (double) type declaration in the line
assertThat(optionAndSuffix).hasSize(2);
doubleOptionsExtracted.add(optionAndSuffix.get(0));
}
}

assertThat(doubleOptions).containsExactlyElementsIn(doubleOptionsExtracted.build());
}
}

private Set<Expr<?>> getFreeVars(Expr<?> expr) {
Deque<Expr<?>> worklist = new ArrayDeque<>();
worklist.push(expr);

Set<Expr<?>> vars = new HashSet<>();

while (!worklist.isEmpty()) {
Expr<?> f = worklist.removeFirst();
if (f.isConst()) {
vars.add(f);
}
for (Expr<?> c : f.getArgs()) {
worklist.addLast(c);
}
}
return vars;
}

@SuppressWarnings("unchecked")
private Model solveHornClauses(Context env, List<Expr<BoolSort>> chc) {
Params opts = env.mkParams();
opts.add("engine", "spacer");
opts.add("spacer.order_children", 2);
opts.add("xform.inline_eager", false);
opts.add("xform.inline_linear", false);
opts.add("xform.slice", false);
opts.add("spacer.max_level", 10);

Solver solver = env.mkSolver("HORN");
solver.setParameters(opts);

for (Expr<BoolSort> c : chc) {
solver.add(c);
}
Status status = solver.check();

assertThat(status).isEqualTo(Status.SATISFIABLE);

return solver.getModel();
}

private Expr<BoolSort> interpolate(
Context env, Expr<BoolSort> formulasA, Expr<BoolSort> formulasB) {
Set<Expr<?>> varsA = getFreeVars(formulasA);
Set<Expr<?>> varsB = getFreeVars(formulasB);

Expr<?>[] shared = Sets.intersection(varsA, varsB).toArray(new Expr<?>[0]);

FuncDecl<BoolSort> symbolItp =
env.mkFuncDecl(
"Itp", Stream.of(shared).map(Expr::getSort).toArray(Sort[]::new), env.mkBoolSort());
Expr<BoolSort> constant = symbolItp.apply(shared);

Expr<BoolSort> left =
env.mkForall(
varsA.toArray(new Expr<?>[0]),
env.mkImplies(formulasA, constant),
1,
null,
null,
null,
null);

Expr<BoolSort> right =
env.mkForall(
varsB.toArray(new Expr<?>[0]),
env.mkImplies(constant, env.mkNot(formulasB)),
1,
null,
null,
null,
null);

Model model = solveHornClauses(env, ImmutableList.of(left, right));
return model.eval(constant, false);
}

/** Validate that itp is an interpolant for formula(s) A and formula(s) B. */
private boolean validate(
Context env, Expr<BoolSort> formulasA, Expr<BoolSort> formulasB, Expr<BoolSort> itp) {
Solver solver = env.mkSolver("QF_LIA");
return solver.check(env.mkNot(env.mkImplies(formulasA, itp))).equals(Status.UNSATISFIABLE)
&& solver
.check(env.mkNot(env.mkImplies(itp, env.mkNot(formulasB))))
.equals(Status.UNSATISFIABLE)
&& Sets.intersection(getFreeVars(formulasA), getFreeVars(formulasB))
.containsAll(getFreeVars(itp));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.sosy_lab.java_smt.api.OptimizationProverEnvironment;
import org.sosy_lab.java_smt.api.SolverContext.ProverOptions;
import org.sosy_lab.java_smt.api.SolverException;
import org.sosy_lab.java_smt.solvers.z3.Z3SolverContext.ENGINE;

class Z3OptimizationProver extends Z3AbstractProver implements OptimizationProverEnvironment {

Expand All @@ -42,11 +43,13 @@ class Z3OptimizationProver extends Z3AbstractProver implements OptimizationProve
Z3FormulaCreator creator,
LogManager pLogger,
Z3FormulaManager pMgr,
Optional<String> pLogic,
ENGINE pEngine,
Set<ProverOptions> pOptions,
ImmutableMap<String, Object> pSolverOptions,
@Nullable PathCounterTemplate pLogfile,
ShutdownNotifier pShutdownNotifier) {
super(creator, pMgr, pOptions, pLogfile, pShutdownNotifier);
super(creator, pMgr, pLogic, pEngine, pOptions, pLogfile, pShutdownNotifier);
z3optSolver = Native.mkOptimize(z3context);
Native.optimizeIncRef(z3context, z3optSolver);
logger = pLogger;
Expand Down
Loading
Loading