Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2e50b5a
Implement execution of swine-z3 on SMT2 files
Feb 11, 2025
57c4725
Add SolverType and CommandError enums, update execute_swine and check…
SeRin-Yang Feb 24, 2025
9b6388a
No functional changes
SeRin-Yang Mar 10, 2025
9120d44
Execute swine directly in the command line
SeRin-Yang Mar 10, 2025
9a0db09
Add SolverType attribute to Prover struct and SMT solver options
SeRin-Yang Mar 19, 2025
6197986
Update check_proof_assuming to return Result for error handling
SeRin-Yang Apr 14, 2025
854e42c
Implement AST evaluation functions for Bool, Int, Real types
SeRin-Yang Apr 30, 2025
793a281
Add swine execution via spawn and parse output into CheckSatResponse …
SeRin-Yang Apr 30, 2025
ae52982
Move code of eval_ast_bool/int/real into SmtEval::eval
SeRin-Yang May 6, 2025
3a41142
Change return type of from_str_to_num_den from (String, String) to (B…
SeRin-Yang May 6, 2025
35b0ef4
Parse counterexample from swine via solver.from_string
SeRin-Yang May 12, 2025
9f44e21
Compare SAT result from swine with 'sat'/'unsat'; return full swine o…
SeRin-Yang May 13, 2025
bc17f1c
Add Cargo.lock file
SeRin-Yang May 13, 2025
ecb9d86
Merge main of moves-rwth/caesar and resolve conflicts
SeRin-Yang May 14, 2025
70f7b1d
Merge remote-tracking branch 'moves-rwth/main'
SeRin-Yang May 16, 2025
4289373
minor changes
SeRin-Yang May 21, 2025
f07c8b7
Remove leftover code
SeRin-Yang May 23, 2025
4af6885
Add check-sat-assuming command for given assumptions
SeRin-Yang May 23, 2025
ecfad75
Add docs for add_check_sat_assuming and add_details_query
SeRin-Yang May 23, 2025
c5464de
Add SolverResult enum
SeRin-Yang May 27, 2025
aaf9c52
Apply the same logic to the SWINE call
SeRin-Yang Jun 4, 2025
b5325b8
Implement check_sat for each SolverType (not working correctly)
SeRin-Yang Jun 5, 2025
fe830be
Fix missing sat check after adding assertions
SeRin-Yang Jun 20, 2025
7709717
Remove print statements used for functon call tracking
SeRin-Yang Jun 20, 2025
6d4259d
Add SMTLIB(String) option to SMTSolverType and SolverType enums
SeRin-Yang Jun 20, 2025
10b522e
no functional change
SeRin-Yang Jul 2, 2025
7f4f5b3
Add CVC5 option to SMTSolverType and SolverType enums
SeRin-Yang Jul 4, 2025
eeac38e
Fix: skip at-most conversion for non-CVC5 solvers
SeRin-Yang Jul 4, 2025
2787eba
Remove SMTLIB option from SMTSolverType and SolverType enums
SeRin-Yang Jul 4, 2025
b33d3b3
Add at_most_k, at_least_k functions
SeRin-Yang Jul 9, 2025
d108056
Clean up unused code
SeRin-Yang Jul 9, 2025
4f7155d
Clean up unused code
SeRin-Yang Jul 9, 2025
2b5c5ad
Restore block_at_least function to original implementation
SeRin-Yang Jul 10, 2025
b42f210
Add SolverType ExternalZ3 and rename SolverType Z3 to InternalZ3
SeRin-Yang Jul 14, 2025
21936b1
Add SolverType Yices
SeRin-Yang Jul 21, 2025
c1cbaf3
Add timeout setting for external SMT solvers
SeRin-Yang Jul 30, 2025
077338d
Change SolverResult::Sat to store a real solver instead of a string
SeRin-Yang Aug 1, 2025
39ffe52
Change solver handling back to match original behavior
SeRin-Yang Aug 18, 2025
98b84e8
Add SolverType parameter to at_most_k
SeRin-Yang Aug 18, 2025
718554a
Rename function to get_solver_type
SeRin-Yang Aug 18, 2025
308846f
Make SolverResult independent from lifetime 'ctx' to fix E0499 error
SeRin-Yang Sep 19, 2025
3a3e33f
Code formatting
SeRin-Yang Sep 19, 2025
50b1814
Add result_solver to Prover for external solver output
SeRin-Yang Sep 29, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 19 additions & 4 deletions src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use crate::{
vcgen::Vcgen,
},
version::write_detailed_version_info,
DebugOptions, SliceOptions, SliceVerifyMethod, VerifyCommand, VerifyError,
DebugOptions, SMTSolverType, SliceOptions, SliceVerifyMethod, VerifyCommand, VerifyError,
};

use ariadne::ReportKind;
Expand All @@ -65,7 +65,7 @@ use z3::{
use z3rro::{
model::InstrumentedModel,
probes::ProbeSummary,
prover::{IncrementalMode, ProveResult, Prover},
prover::{IncrementalMode, ProveResult, Prover, SolverType},
smtlib::Smtlib,
util::{PrefixWriter, ReasonUnknown},
};
Expand Down Expand Up @@ -696,7 +696,13 @@ impl<'ctx> SmtVcUnit<'ctx> {
let span = info_span!("SAT check");
let _entered = span.enter();

let prover = mk_valid_query_prover(limits_ref, ctx, translate, &self.vc);
let prover = mk_valid_query_prover(
limits_ref,
ctx,
translate,
&self.vc,
options.smt_solver_options.smt_solver.clone(),
);

if options.debug_options.probe {
let goal = Goal::new(ctx, false, false, false);
Expand Down Expand Up @@ -823,9 +829,18 @@ fn mk_valid_query_prover<'smt, 'ctx>(
ctx: &'ctx Context,
smt_translate: &TranslateExprs<'smt, 'ctx>,
valid_query: &Bool<'ctx>,
smt_solver: SMTSolverType,
) -> Prover<'ctx> {
let solver_type = match smt_solver {
SMTSolverType::InternalZ3 => SolverType::InternalZ3,
SMTSolverType::ExternalZ3 => SolverType::ExternalZ3,
SMTSolverType::Swine => SolverType::SWINE,
SMTSolverType::CVC5 => SolverType::CVC5,
SMTSolverType::Yices => SolverType::YICES,
};

// create the prover and set the params
let mut prover = Prover::new(ctx, IncrementalMode::Native);
let mut prover = Prover::new(ctx, IncrementalMode::Native, solver_type);
if let Some(remaining) = limits_ref.time_left() {
prover.set_timeout(remaining);
}
Expand Down
36 changes: 35 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ use tokio::task::JoinError;
use tracing::{error, info, warn};

use vc::explain::VcExplanation;
use z3rro::{prover::ProveResult, util::ReasonUnknown};
use z3rro::{
prover::{ProveResult, ProverCommandError},
util::ReasonUnknown,
};

pub mod ast;
mod driver;
Expand Down Expand Up @@ -138,6 +141,9 @@ pub struct VerifyCommand {

#[command(flatten)]
pub debug_options: DebugOptions,

#[command(flatten)]
pub smt_solver_options: SMTSolverOptions,
}

#[derive(Debug, Args)]
Expand Down Expand Up @@ -380,6 +386,28 @@ pub struct DebugOptions {
pub probe: bool,
}

#[derive(Debug, Default, Args)]
#[command(next_help_heading = "SMT Solver Options")]
pub struct SMTSolverOptions {
#[arg(long, default_value = "default")]
pub smt_solver: SMTSolverType,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, ValueEnum)]
pub enum SMTSolverType {
#[default]
#[value(name = "default")]
InternalZ3,
#[value(name = "z3")]
ExternalZ3,
#[value(name = "swine")]
Swine,
#[value(name = "cvc5")]
CVC5,
#[value(name = "yices")]
Yices,
}

#[derive(Debug, Default, Args)]
#[command(next_help_heading = "Slicing Options")]
pub struct SliceOptions {
Expand Down Expand Up @@ -533,6 +561,10 @@ fn finalize_verify_result(
tracing::error!("Interrupted");
ExitCode::from(130) // 130 seems to be a standard exit code for CTRL+C
}
Err(VerifyError::ProverError(err)) => {
eprintln!("{}", err.to_string());
ExitCode::from(1)
}
}
}

Expand Down Expand Up @@ -612,6 +644,8 @@ pub enum VerifyError {
/// The verifier was interrupted.
#[error("interrupted")]
Interrupted,
#[error("{0}")]
ProverError(#[from] ProverCommandError),
}

/// Verify a list of `user_files`. The `options.files` value is ignored here.
Expand Down
12 changes: 7 additions & 5 deletions src/opt/fuzz_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use proptest::{
prelude::*,
test_runner::{TestCaseResult, TestRunner},
};
use z3rro::prover::{IncrementalMode, ProveResult, Prover};

use z3rro::prover::{IncrementalMode, ProveResult, Prover, SolverType};

use crate::{
ast::{
Expand Down Expand Up @@ -201,23 +202,24 @@ fn prove_equiv(expr: Expr, optimized: Expr, tcx: &TyCtx) -> TestCaseResult {
let smt_ctx = SmtCtx::new(&ctx, tcx);
let mut translate = TranslateExprs::new(&smt_ctx);
let eq_expr_z3 = translate.t_bool(&eq_expr);
let mut prover = Prover::new(&ctx, IncrementalMode::Native);
let mut prover = Prover::new(&ctx, IncrementalMode::Native, SolverType::InternalZ3);
translate
.local_scope()
.add_assumptions_to_prover(&mut prover);
prover.add_provable(&eq_expr_z3);
let x = match prover.check_proof() {
ProveResult::Proof => Ok(()),
ProveResult::Counterexample => {
Ok(ProveResult::Proof) => Ok(()),
Ok(ProveResult::Counterexample) => {
let model = prover.get_model().unwrap();
Err(TestCaseError::fail(format!(
"rewrote {} ...into... {}, but those are not equivalent:\n{}",
expr, optimized, model
)))
}
ProveResult::Unknown(reason) => {
Ok(ProveResult::Unknown(reason)) => {
Err(TestCaseError::fail(format!("unknown result ({})", reason)))
}
Err(err) => Err(TestCaseError::fail(format!("{}", err))),
};
x
}
Expand Down
6 changes: 3 additions & 3 deletions src/opt/unfolder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use std::ops::DerefMut;

use z3::SatResult;
use z3rro::prover::{IncrementalMode, Prover};
use z3rro::prover::{IncrementalMode, Prover, SolverType};

use crate::{
ast::{
Expand Down Expand Up @@ -60,7 +60,7 @@ impl<'smt, 'ctx> Unfolder<'smt, 'ctx> {
// it's important that we use the native incremental mode here, because
// the performance benefit from the unfolder relies on many very fast
// SAT checks.
let prover = Prover::new(ctx.ctx(), IncrementalMode::Native);
let prover = Prover::new(ctx.ctx(), IncrementalMode::Native, SolverType::InternalZ3);

Unfolder {
subst: Subst::new(ctx.tcx(), &limits_ref),
Expand Down Expand Up @@ -98,7 +98,7 @@ impl<'smt, 'ctx> Unfolder<'smt, 'ctx> {
// here we want to do a SAT check and not a proof search. if the
// expression is e.g. `false`, then we want to get `Unsat` from the
// solver and not `Proof`!
if this.prover.check_sat() == SatResult::Unsat {
if this.prover.check_sat() == Ok(SatResult::Unsat) {
tracing::trace!(solver=?this.prover, "eliminated zero expr");
None
} else {
Expand Down
58 changes: 35 additions & 23 deletions src/slicing/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use z3::{
};
use z3rro::{
model::{InstrumentedModel, ModelConsistency},
prover::{ProveResult, Prover},
prover::{ProveResult, Prover, ProverCommandError},
util::ReasonUnknown,
};

use crate::{
ast::{ExprBuilder, Span},
resource_limits::{LimitError, LimitsRef},
resource_limits::LimitsRef,
slicing::{
model::{SliceMode, SliceModel},
util::{PartialMinimizeResult, SubsetExploration},
util::{at_most_k, PartialMinimizeResult, SubsetExploration},
},
smt::translate_exprs::TranslateExprs,
VerifyError,
Expand Down Expand Up @@ -218,7 +218,10 @@ impl<'ctx> SliceSolver<'ctx> {
options,
limits_ref,
)?;
if exists_forall_solver.check_sat() == SatResult::Sat {
let sat_res = exists_forall_solver
.check_sat()
.map_err(|err| VerifyError::ProverError(err))?;
if sat_res == SatResult::Sat {
let model = exists_forall_solver.get_model().unwrap();
let slice_model =
SliceModel::from_model(SliceMode::Verify, &self.slice_stmts, selection, &model);
Expand Down Expand Up @@ -251,7 +254,7 @@ impl<'ctx> SliceSolver<'ctx> {
let res = self.prover.check_proof_assuming(&active_toggle_values);

let mut slice_searcher = SliceModelSearch::new(active_toggle_values.clone());
if let ProveResult::Proof = res {
if let Ok(ProveResult::Proof) = res {
slice_searcher.found_active(self.prover.get_unsat_core());
}

Expand Down Expand Up @@ -351,7 +354,10 @@ impl<'ctx> SliceSolver<'ctx> {
self.prover.push();

slice_sat_binary_search(&mut self.prover, &active_toggle_values, options, limits_ref)?;
let res = self.prover.check_proof();
let res = self
.prover
.check_proof()
.map_err(|err| VerifyError::ProverError(err))?;
let model = if let Some(model) = self.prover.get_model() {
assert!(matches!(
res,
Expand Down Expand Up @@ -461,16 +467,16 @@ fn slice_sat_binary_search<'ctx>(
) -> Result<(), VerifyError> {
assert_eq!(prover.level(), 2);

let slice_vars: Vec<(&Bool<'ctx>, i32)> =
active_slice_vars.iter().map(|value| (value, 1)).collect();

let set_at_most_true = |prover: &mut Prover<'ctx>, at_most_n: usize| {
prover.pop();
prover.push();

let ctx = prover.get_context();
let at_most_n_true = Bool::pb_le(ctx, &slice_vars, at_most_n as i32);
prover.add_assumption(&at_most_n_true);
if !active_slice_vars.is_empty() {
let at_most_n_true =
at_most_k(ctx, at_most_n, active_slice_vars, prover.get_solver_type());
prover.add_assumption(&at_most_n_true);
}
};

// TODO: we could have min_least_bound set to 1 if we could conclude for
Expand All @@ -485,7 +491,7 @@ fn slice_sat_binary_search<'ctx>(
// the fix would be to track explicitly whether we can make that assumption
// that min_least_bound is 1.
let min_least_bound = 0;
let mut minimize = PartialMinimizer::new(min_least_bound..=slice_vars.len());
let mut minimize = PartialMinimizer::new(min_least_bound..=active_slice_vars.len());

let mut cur_solver_n = None;
let mut slice_searcher = SliceModelSearch::new(active_slice_vars.to_vec());
Expand All @@ -506,7 +512,10 @@ fn slice_sat_binary_search<'ctx>(
if let Some(timeout) = limits_ref.time_left() {
prover.set_timeout(timeout);
}
let res = prover.check_sat();

let res = prover
.check_sat()
.map_err(|err| VerifyError::ProverError(err))?;

entered.record("res", tracing::field::debug(res));

Expand Down Expand Up @@ -571,7 +580,9 @@ fn slice_sat_binary_search<'ctx>(
if let Some(timeout) = limits_ref.time_left() {
prover.set_timeout(timeout);
}
let res = prover.check_sat();
let res = prover
.check_sat()
.map_err(|err| VerifyError::ProverError(err))?;
if minimize.min_accept().is_some() {
assert!(res == SatResult::Sat || res == SatResult::Unknown);
} else if minimize.max_reject().is_some() {
Expand All @@ -593,7 +604,7 @@ pub fn slice_unsat_search<'ctx>(
prover: &mut Prover<'ctx>,
options: &SliceSolveOptions,
limits_ref: &LimitsRef,
) -> Result<Option<Vec<Bool<'ctx>>>, LimitError> {
) -> Result<Option<Vec<Bool<'ctx>>>, VerifyError> {
let mut slice_searcher =
SliceModelSearch::new(exploration.variables().iter().cloned().collect_vec());
let all_variables = exploration.variables().clone();
Expand All @@ -602,12 +613,12 @@ pub fn slice_unsat_search<'ctx>(
limits_ref.check_limits()?;

match check_proof_seed(&all_variables, prover, limits_ref, &seed) {
ProveResult::Proof => {
Ok(ProveResult::Proof) => {
// now start the shrinking, then block up
let res = exploration.shrink_block_unsat(seed, |seed| {
match check_proof_seed(&all_variables, prover, limits_ref, seed) {
ProveResult::Proof => Some(unsat_core_to_seed(prover, &all_variables)),
ProveResult::Counterexample | ProveResult::Unknown(_) => None,
Ok(ProveResult::Proof) => Some(unsat_core_to_seed(prover, &all_variables)),
_ => None,
}
});

Expand All @@ -620,16 +631,16 @@ pub fn slice_unsat_search<'ctx>(
SliceMinimality::Size => exploration.block_at_least(res.len()),
}
}
ProveResult::Counterexample => {
Ok(ProveResult::Counterexample) => {
// grow the counterexample and then block down
exploration.grow_block_sat(seed, |seed| {
match check_proof_seed(&all_variables, prover, limits_ref, seed) {
ProveResult::Counterexample => true,
ProveResult::Proof | ProveResult::Unknown(_) => false,
Ok(ProveResult::Counterexample) => true,
_ => false,
}
});
}
ProveResult::Unknown(_) => {
Ok(ProveResult::Unknown(_)) => {
exploration.block_this(&seed);

match options.unknown {
Expand All @@ -643,6 +654,7 @@ pub fn slice_unsat_search<'ctx>(
}
}
}
Err(err) => return Err(VerifyError::ProverError(err)),
}
}

Expand All @@ -655,7 +667,7 @@ fn check_proof_seed<'ctx>(
prover: &mut Prover<'ctx>,
limits_ref: &LimitsRef,
seed: &IndexSet<Bool<'ctx>>,
) -> ProveResult {
) -> Result<ProveResult, ProverCommandError> {
let mut timeout = Duration::from_millis(100);
if let Some(time_left) = limits_ref.time_left() {
timeout = timeout.min(time_left);
Expand Down
Loading