use crate::expr::{Context, ExprRef};
use crate::smt::parser::{
SmtParserError, count_parens, parse_get_unsat_assumptions_response, parse_get_value_response,
};
use crate::smt::serialize::serialize_cmd;
use rustc_hash::FxHashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter};
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("[smt] I/O operation failed")]
Io(#[from] std::io::Error),
#[error("[smt] cannot pop because the stack is already empty")]
StackUnderflow,
#[error("[smt] {0} reported an error:\n{1}")]
FromSolver(String, String),
#[error("[smt]{0} is unreachable, the process might have died")]
SolverDead(String),
#[error("[smt] {0} returned an unexpected response:\n{1}")]
UnexpectedResponse(String, String),
#[error("[smt] failed to parse a response")]
Parser(#[from] SmtParserError),
}
pub type Result<T> = std::result::Result<T, Error>;
type SymbolTable = FxHashMap<String, ExprRef>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Logic {
All,
QfAufbv,
QfAbv,
QfBv,
}
impl Logic {
pub(crate) fn to_smt_str(&self) -> &'static str {
match self {
Logic::All => "ALL",
Logic::QfAufbv => "QF_AUFBV",
Logic::QfAbv => "QF_ABV",
Logic::QfBv => "QF_BV",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum SmtCommand {
Exit,
CheckSat,
SetLogic(Logic),
SetOption(String, String),
SetInfo(String, String),
Assert(ExprRef),
DeclareConst(ExprRef),
DefineConst(ExprRef, ExprRef),
CheckSatAssuming(Vec<ExprRef>),
Push(u64),
Pop(u64),
GetValue(ExprRef),
GetUnsatAssumptions,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum CheckSatResponse {
Sat,
Unsat,
Unknown,
}
pub trait SolverMetaData {
fn name(&self) -> &str;
fn supports_check_assuming(&self) -> bool;
fn supports_uf(&self) -> bool;
fn supports_const_array(&self) -> bool;
fn supports_get_unsat_assumptions(&self) -> bool;
}
pub trait Solver: SolverMetaData {
type Context: SolverContext;
fn start(&self, replay_file: Option<File>) -> Result<Self::Context>;
}
pub trait SolverContext: SolverMetaData {
fn restart(&mut self) -> Result<()>;
fn set_logic(&mut self, option: Logic) -> Result<()>;
fn assert(&mut self, ctx: &Context, e: ExprRef) -> Result<()>;
fn declare_const(&mut self, ctx: &Context, symbol: ExprRef) -> Result<()>;
fn define_const(&mut self, ctx: &Context, symbol: ExprRef, expr: ExprRef) -> Result<()>;
fn check_sat_assuming(
&mut self,
ctx: &Context,
props: impl IntoIterator<Item = ExprRef>,
) -> Result<CheckSatResponse>;
fn check_sat(&mut self) -> Result<CheckSatResponse>;
fn push(&mut self) -> Result<()>;
fn pop(&mut self) -> Result<()>;
fn get_value(&mut self, ctx: &mut Context, e: ExprRef) -> Result<ExprRef>;
fn get_unsat_assumptions(&mut self, ctx: &mut Context) -> Result<Vec<ExprRef>>;
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SmtLibSolver {
name: &'static str,
args: &'static [&'static str],
options: &'static [&'static str],
supports_uf: bool,
supports_check_assuming: bool,
supports_const_array: bool,
supports_unsat_assumptions: bool,
}
impl SolverMetaData for SmtLibSolver {
fn name(&self) -> &str {
self.name
}
fn supports_check_assuming(&self) -> bool {
self.supports_check_assuming
}
fn supports_uf(&self) -> bool {
self.supports_uf
}
fn supports_const_array(&self) -> bool {
self.supports_const_array
}
fn supports_get_unsat_assumptions(&self) -> bool {
self.supports_unsat_assumptions
}
}
impl Solver for SmtLibSolver {
type Context = SmtLibSolverCtx;
fn start(&self, replay_file: Option<File>) -> Result<Self::Context> {
let mut proc = Command::new(self.name)
.args(self.args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let stdin = BufWriter::new(proc.stdin.take().unwrap());
let stdout = BufReader::new(proc.stdout.take().unwrap());
let stderr = proc.stderr.take().unwrap();
let mut solver = SmtLibSolverCtx {
name: self.name.to_string(),
proc,
stdin,
stdout,
stderr,
stack_depth: 0,
response: String::new(),
replay_file: replay_file.map(BufWriter::new),
has_error: false,
solver_args: self.args.iter().map(|a| a.to_string()).collect(),
solver_options: self.options.iter().map(|a| a.to_string()).collect(),
supports_uf: self.supports_uf,
supports_check_assuming: self.supports_check_assuming,
supports_const_array: self.supports_const_array,
supports_get_unsat_assumptions: self.supports_unsat_assumptions,
symbols: vec![SymbolTable::default()],
last_query_unsat: false,
};
for option in self.options.iter() {
solver.write_cmd(
None,
&SmtCommand::SetOption(option.to_string(), "true".to_string()),
)?
}
Ok(solver)
}
}
pub struct SmtLibSolverCtx {
name: String,
proc: std::process::Child,
stdin: BufWriter<std::process::ChildStdin>,
stdout: BufReader<std::process::ChildStdout>,
stderr: std::process::ChildStderr,
stack_depth: usize,
response: String,
replay_file: Option<BufWriter<File>>,
has_error: bool,
solver_args: Vec<String>,
solver_options: Vec<String>,
supports_uf: bool,
supports_check_assuming: bool,
supports_const_array: bool,
supports_get_unsat_assumptions: bool,
symbols: Vec<SymbolTable>,
last_query_unsat: bool,
}
impl SmtLibSolverCtx {
#[inline]
fn write_cmd(&mut self, ctx: Option<&Context>, cmd: &SmtCommand) -> Result<()> {
if let Some(rf) = self.replay_file.as_mut() {
serialize_cmd(rf, ctx, cmd)?;
}
serialize_cmd(&mut self.stdin, ctx, cmd)?;
if let Some(rf) = self.replay_file.as_mut() {
rf.flush()?;
}
match self.stdin.flush() {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
let _ = self.replay_file.take();
match self.read_response() {
Err(e @ Error::FromSolver(_, _)) => Err(e),
_ => Err(Error::SolverDead(self.name.clone())),
}
}
Err(other) => Err(other.into()),
Ok(_) => Ok(()),
}
}
fn read_response(&mut self) -> Result<()> {
self.response.clear();
self.stdout.read_line(&mut self.response)?;
while count_parens(&self.response) > 0 {
self.response.push(' ');
self.stdout.read_line(&mut self.response)?;
}
if self.response.trim_start().starts_with("(error") {
let trimmed = self.response.trim();
let start = "(error ".len();
let msg = &trimmed[start..(trimmed.len() - start - 1)];
self.has_error = true;
Err(Error::FromSolver(self.name.clone(), msg.to_string()))
} else {
match self.proc.try_wait() {
Ok(Some(status)) if !status.success() => {
let mut err = vec![];
self.stderr.read_to_end(&mut err)?;
self.has_error = true;
Err(Error::FromSolver(
self.name.clone(),
String::from_utf8_lossy(&err).to_string(),
))
}
_ => Ok(()),
}
}
}
fn read_sat_response(&mut self) -> Result<CheckSatResponse> {
self.stdin.flush()?; self.read_response()?;
let response = self.response.trim();
match response {
"sat" => Ok(CheckSatResponse::Sat),
"unsat" => Ok(CheckSatResponse::Unsat),
other => Err(Error::UnexpectedResponse(
self.name.clone(),
other.to_string(),
)),
}
}
}
impl Drop for SmtLibSolverCtx {
fn drop(&mut self) {
shut_down_solver(self);
}
}
fn shut_down_solver(solver: &mut SmtLibSolverCtx) {
if solver.write_cmd(None, &SmtCommand::Exit).is_ok() {
let _status = solver
.proc
.wait()
.expect("failed to wait for SMT solver to exit");
}
}
impl SolverMetaData for SmtLibSolverCtx {
fn name(&self) -> &str {
&self.name
}
fn supports_uf(&self) -> bool {
self.supports_uf
}
fn supports_check_assuming(&self) -> bool {
self.supports_check_assuming
}
fn supports_const_array(&self) -> bool {
self.supports_const_array
}
fn supports_get_unsat_assumptions(&self) -> bool {
self.supports_get_unsat_assumptions
}
}
impl SolverContext for SmtLibSolverCtx {
fn restart(&mut self) -> Result<()> {
shut_down_solver(self);
let mut proc = Command::new(&self.name)
.args(&self.solver_args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let stdin = BufWriter::new(proc.stdin.take().unwrap());
let stdout = BufReader::new(proc.stdout.take().unwrap());
let stderr = proc.stderr.take().unwrap();
self.proc = proc;
self.stdin = stdin;
self.stdout = stdout;
self.stderr = stderr;
for option in self.solver_options.clone() {
self.write_cmd(None, &SmtCommand::SetOption(option, "true".to_string()))?;
}
self.symbols = vec![SymbolTable::default()];
self.last_query_unsat = false;
Ok(())
}
fn set_logic(&mut self, logic: Logic) -> Result<()> {
self.write_cmd(None, &SmtCommand::SetLogic(logic))
}
fn assert(&mut self, ctx: &Context, e: ExprRef) -> Result<()> {
self.write_cmd(Some(ctx), &SmtCommand::Assert(e))
}
fn declare_const(&mut self, ctx: &Context, symbol: ExprRef) -> Result<()> {
self.symbols
.last_mut()
.unwrap()
.insert(ctx.get_symbol_name(symbol).unwrap().to_string(), symbol);
self.write_cmd(Some(ctx), &SmtCommand::DeclareConst(symbol))
}
fn define_const(&mut self, ctx: &Context, symbol: ExprRef, expr: ExprRef) -> Result<()> {
self.symbols
.last_mut()
.unwrap()
.insert(ctx.get_symbol_name(symbol).unwrap().to_string(), symbol);
self.write_cmd(Some(ctx), &SmtCommand::DefineConst(symbol, expr))
}
fn check_sat_assuming(
&mut self,
ctx: &Context,
props: impl IntoIterator<Item = ExprRef>,
) -> Result<CheckSatResponse> {
let props: Vec<ExprRef> = props.into_iter().collect();
self.write_cmd(Some(ctx), &SmtCommand::CheckSatAssuming(props))?;
let res = self.read_sat_response()?;
self.last_query_unsat = matches!(res, CheckSatResponse::Unsat);
Ok(res)
}
fn check_sat(&mut self) -> Result<CheckSatResponse> {
self.write_cmd(None, &SmtCommand::CheckSat)?;
let res = self.read_sat_response()?;
self.last_query_unsat = matches!(res, CheckSatResponse::Unsat);
Ok(res)
}
fn push(&mut self) -> Result<()> {
self.write_cmd(None, &SmtCommand::Push(1))?;
self.symbols.push(SymbolTable::default());
self.stack_depth += 1;
Ok(())
}
fn pop(&mut self) -> Result<()> {
if self.stack_depth > 0 {
self.write_cmd(None, &SmtCommand::Pop(1))?;
self.symbols.pop();
self.stack_depth -= 1;
Ok(())
} else {
Err(Error::StackUnderflow)
}
}
fn get_value(&mut self, ctx: &mut Context, e: ExprRef) -> Result<ExprRef> {
self.write_cmd(Some(ctx), &SmtCommand::GetValue(e))?;
self.stdin.flush()?; self.read_response()?;
let response = self.response.trim();
let expr = parse_get_value_response(ctx, response.as_bytes())?;
Ok(expr)
}
fn get_unsat_assumptions(&mut self, ctx: &mut Context) -> Result<Vec<ExprRef>> {
if !self.last_query_unsat {
return Err(Error::FromSolver(
self.name.clone(),
"Previous query not UNSAT".into(),
));
}
self.write_cmd(None, &SmtCommand::GetUnsatAssumptions)?;
self.stdin.flush()?;
self.read_response()?;
let response = self.response.trim();
let mut st = SymbolTable::default();
for st_ctx in &self.symbols {
st.extend(st_ctx.iter().map(|(k, &v)| (k.clone(), v)));
}
Ok(parse_get_unsat_assumptions_response(
ctx,
&st,
response.as_bytes(),
)?)
}
}
pub const BITWUZLA: SmtLibSolver = SmtLibSolver {
name: "bitwuzla",
args: &[],
options: &["incremental", "produce-models", "produce-unsat-assumptions"],
supports_uf: false,
supports_check_assuming: true,
supports_const_array: true,
supports_unsat_assumptions: true,
};
pub const YICES2: SmtLibSolver = SmtLibSolver {
name: "yices-smt2",
args: &["--incremental"],
options: &[],
supports_uf: false, supports_check_assuming: false,
supports_const_array: false,
supports_unsat_assumptions: false,
};
pub const Z3: SmtLibSolver = SmtLibSolver {
name: "z3",
args: &["-in"],
options: &["produce-unsat-assumptions"],
supports_uf: true,
supports_check_assuming: true,
supports_const_array: true,
supports_unsat_assumptions: true,
};
pub const CVC5: SmtLibSolver = SmtLibSolver {
name: "cvc5",
args: &["--incremental", "--produce-models"],
options: &["produce-unsat-assumptions"],
supports_uf: true,
supports_check_assuming: true,
supports_const_array: true,
supports_unsat_assumptions: true,
};
pub const TEST_SOLVER_ENV: &str = "PATRONUS_TEST_SOLVER";
pub fn solver_from_env() -> SmtLibSolver {
match std::env::var(TEST_SOLVER_ENV).ok().as_deref() {
None | Some("" | "bitwuzla") => BITWUZLA,
Some("yices2") => YICES2,
Some("cvc5") => CVC5,
Some("z3") => Z3,
Some(other) => panic!(
"unrecognized {TEST_SOLVER_ENV}={other:?}; expected one of: bitwuzla, yices2, cvc5, z3"
),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error() {
let mut ctx = Context::default();
let mut solver = solver_from_env().start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
let a = ctx.bv_symbol("a", 3);
let e = ctx.build(|c| c.equal(a, c.bit_vec_val(3, 3)));
solver.assert(&ctx, e).unwrap();
let res = solver.check_sat();
assert!(res.is_err(), "a was not declared!");
let _res = solver.declare_const(&ctx, a);
}
#[test]
fn test_check_sat_assuming() {
let backend = solver_from_env();
if !backend.supports_check_assuming() {
return;
}
let mut ctx = Context::default();
let a = ctx.bv_symbol("a", 3);
let e = ctx.build(|c| c.equal(a, c.bit_vec_val(3, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, a).unwrap();
let res = solver.check_sat_assuming(&ctx, [e]);
assert_eq!(res.unwrap(), CheckSatResponse::Sat);
let value_of_a = solver.get_value(&mut ctx, a).unwrap();
assert_eq!(value_of_a, ctx.bit_vec_val(3, 3));
}
#[test]
fn test_unsat_assumptions_basic() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let a = ctx.bv_symbol("a", 3);
let eq3 = ctx.build(|c| c.equal(a, c.bit_vec_val(3, 3)));
let eq4 = ctx.build(|c| c.equal(a, c.bit_vec_val(4, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, a).unwrap();
let res = solver.check_sat_assuming(&ctx, [eq3, eq4]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert_eq!(core.len(), 2);
assert!(core.contains(&eq3));
assert!(core.contains(&eq4));
}
#[test]
fn test_unsat_assumptions_false() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let smt_false = ctx.get_false();
let a = ctx.bv_symbol("a", 3);
let ge3 = ctx.build(|c| c.greater_or_equal(a, c.bit_vec_val(3, 3)));
let ge5 = ctx.build(|c| c.greater_or_equal(a, c.bit_vec_val(5, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, a).unwrap();
let res = solver
.check_sat_assuming(&ctx, [smt_false, ge3, ge5])
.unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&smt_false));
}
#[test]
fn test_unsat_assumptions_subset() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let a = ctx.bv_symbol("a", 3);
let b = ctx.bv_symbol("b", 3);
let eq3 = ctx.build(|c| c.equal(a, c.bit_vec_val(3, 3)));
let eq4 = ctx.build(|c| c.equal(a, c.bit_vec_val(4, 3)));
let b_is_1 = ctx.build(|c| c.equal(b, c.bit_vec_val(1, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, a).unwrap();
solver.declare_const(&ctx, b).unwrap();
let res = solver.check_sat_assuming(&ctx, [eq3, eq4, b_is_1]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&eq3) && core.contains(&eq4));
}
#[test]
fn test_unsat_assumptions_act_lits() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let x = ctx.bv_symbol("x", 3);
let eq2 = ctx.build(|c| c.equal(x, c.bit_vec_val(2, 3)));
let ge5 = ctx.build(|c| c.greater_or_equal(x, c.bit_vec_val(5, 3)));
let ge1 = ctx.build(|c| c.greater_or_equal(x, c.bit_vec_val(1, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, x).unwrap();
let mut act_lits = Vec::with_capacity(3);
for (idx, expr) in [eq2, ge1, ge5].iter().enumerate() {
let lit = ctx.bv_symbol(format!("a_{idx}").as_str(), 1);
let imp = ctx.implies(lit, *expr);
act_lits.push(lit);
solver.declare_const(&ctx, lit).unwrap();
solver.assert(&ctx, imp).unwrap();
}
let res = solver.check_sat_assuming(&ctx, act_lits.clone()).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&act_lits[0]) && core.contains(&act_lits[2]));
}
#[test]
fn test_unsat_assumptions_empty() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let a = ctx.bv_symbol("a", 3);
let b = ctx.bv_symbol("b", 3);
let eq3 = ctx.build(|c| c.equal(a, c.bit_vec_val(3, 3)));
let eq4 = ctx.build(|c| c.equal(a, c.bit_vec_val(4, 3)));
let b_is_1 = ctx.build(|c| c.equal(b, c.bit_vec_val(1, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, a).unwrap();
solver.declare_const(&ctx, b).unwrap();
solver.assert(&ctx, eq3).unwrap();
solver.assert(&ctx, eq4).unwrap();
solver.assert(&ctx, b_is_1).unwrap();
let res = solver.check_sat_assuming(&ctx, [b_is_1]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.is_empty());
}
#[test]
fn test_push_pop() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let x = ctx.bv_symbol("x", 3);
let eq2 = ctx.build(|c| c.equal(x, c.bit_vec_val(2, 3)));
let ge5 = ctx.build(|c| c.greater_or_equal(x, c.bit_vec_val(5, 3)));
let ge1 = ctx.build(|c| c.greater_or_equal(x, c.bit_vec_val(1, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, x).unwrap();
let res = solver.check_sat_assuming(&ctx, [eq2, ge5, ge1]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&eq2) && core.contains(&ge5));
solver.push().unwrap();
let y = ctx.bv_symbol("y", 3);
let y_is_1 = ctx.build(|c| c.equal(y, c.bit_vec_val(1, 3)));
solver.declare_const(&ctx, y).unwrap();
let res = solver
.check_sat_assuming(&ctx, [y_is_1, eq2, ge5, ge1])
.unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&eq2) && core.contains(&ge5));
solver.push().unwrap();
let y_is_2 = ctx.build(|c| c.equal(y, c.bit_vec_val(2, 3)));
let res = solver.check_sat_assuming(&ctx, [y_is_1, y_is_2]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert_eq!(core.len(), 2);
assert!(core.contains(&y_is_1) && core.contains(&y_is_2));
solver.pop().unwrap();
solver.push().unwrap();
let z = ctx.bv_symbol("z", 3);
let z_is_1 = ctx.build(|c| c.equal(z, c.bit_vec_val(1, 3)));
let z_is_2 = ctx.build(|c| c.equal(z, c.bit_vec_val(2, 3)));
solver.declare_const(&ctx, z).unwrap();
let res = solver
.check_sat_assuming(&ctx, [z_is_1, z_is_2, y_is_1])
.unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert!(core.contains(&z_is_1) && core.contains(&z_is_2));
solver.pop().unwrap();
solver.pop().unwrap();
let err = solver.check_sat_assuming(&ctx, [y_is_1, y_is_2]);
assert!(err.is_err());
}
#[test]
fn test_assert_over_push_pop() {
let mut ctx = Context::default();
let x = ctx.bv_symbol("x", 3);
let eq2 = ctx.build(|c| c.equal(x, c.bit_vec_val(2, 3)));
let mut solver = solver_from_env().start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, x).unwrap();
solver.assert(&ctx, eq2).unwrap();
solver.push().unwrap();
let eq3 = ctx.build(|c| c.equal(x, c.bit_vec_val(3, 3)));
solver.assert(&ctx, eq3).unwrap();
let res = solver.check_sat().unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
solver.pop().unwrap();
}
#[test]
fn test_unsat_assumptions_fail() {
let backend = solver_from_env();
if !backend.supports_get_unsat_assumptions() {
return;
}
let mut ctx = Context::default();
let x = ctx.bv_symbol("x", 3);
let eq2 = ctx.build(|c| c.equal(x, c.bit_vec_val(2, 3)));
let mut solver = backend.start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.declare_const(&ctx, x).unwrap();
let res = solver.check_sat_assuming(&ctx, [eq2]).unwrap();
assert_eq!(res, CheckSatResponse::Sat);
let core = solver.get_unsat_assumptions(&mut ctx);
assert!(core.is_err());
let eq3 = ctx.build(|c| c.equal(x, c.bit_vec_val(3, 3)));
let res = solver.check_sat_assuming(&ctx, [eq2, eq3]).unwrap();
assert_eq!(res, CheckSatResponse::Unsat);
let core = solver.get_unsat_assumptions(&mut ctx).unwrap();
assert_eq!(core.len(), 2);
assert!(core.contains(&eq2) && core.contains(&eq3));
}
#[test]
fn test_restart() {
let mut ctx = Context::default();
let a = ctx.bv_symbol("a", 3);
let mut solver = solver_from_env().start(None).unwrap();
solver.set_logic(Logic::QfBv).unwrap();
let three = ctx.bit_vec_val(3, 3);
let four = ctx.bit_vec_val(3, 3);
solver.define_const(&ctx, a, three).unwrap();
let _res = solver.check_sat().unwrap();
let value_of_a = solver.get_value(&mut ctx, a).unwrap();
assert_eq!(value_of_a, three);
solver.restart().unwrap();
solver.set_logic(Logic::QfBv).unwrap();
solver.define_const(&ctx, a, four).unwrap();
let _res = solver.check_sat().unwrap();
let value_of_a = solver.get_value(&mut ctx, a).unwrap();
assert_eq!(value_of_a, four);
}
}