Skip to content

Commit 35b0ef4

Browse files
committed
Parse counterexample from swine via solver.from_string
1 parent 3a41142 commit 35b0ef4

File tree

2 files changed

+73
-270
lines changed

2 files changed

+73
-270
lines changed

z3rro/src/model.rs

Lines changed: 46 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,6 @@ use std::{
88

99
use num::{BigInt, BigRational};
1010

11-
use smtlib_lowlevel::{
12-
ast::{
13-
FunctionDef, GetModelResponse, Identifier, ModelResponse, QualIdentifier, SpecConstant,
14-
Term,
15-
},
16-
lexicon::{Decimal, Symbol},
17-
};
1811

1912
use thiserror::Error;
2013

@@ -23,67 +16,21 @@ use z3::{
2316
FuncDecl, FuncInterp, Model,
2417
};
2518

26-
#[derive(Debug)]
27-
pub enum ModelSource<'ctx> {
28-
Z3Model(Model<'ctx>),
29-
SwineModel(GetModelResponse<'ctx>),
30-
}
31-
32-
/// Convert a decimal string `d_str` into a pair of a numerator (`num`) and
33-
/// a denominator (`den`) in the form of '[numeral]' strings such that:
34-
/// d_str = num / den
35-
fn from_str_to_num_den(d_str: &str) -> Option<(BigInt, BigInt)> {
36-
if d_str.is_empty() {
37-
return None;
38-
}
39-
40-
if d_str.ends_with(".0") {
41-
let num = BigInt::from_str(&d_str.replace(".0", "")).ok()?;
42-
Some((num, BigInt::from(1)))
43-
} else if let Some(pos) = d_str.find('.') {
44-
let den = BigInt::from(10).pow((d_str.len() - pos - 1).try_into().unwrap());
45-
let num = BigInt::from_str(&d_str.replace(".", "")).ok()?;
46-
47-
Some((num, den))
48-
} else {
49-
let num = BigInt::from_str(d_str).ok()?;
50-
Some((num, BigInt::from(1)))
51-
}
52-
}
53-
54-
/// Find a `FunctionDef` object defined for a given symbol `symbol` within the `model_res`.
55-
fn find_fun_def<'ctx>(
56-
model_res: &GetModelResponse<'ctx>,
57-
symbol: &str,
58-
) -> Option<&'ctx FunctionDef<'ctx>> {
59-
model_res.0.iter().find_map(|m_res| {
60-
if let ModelResponse::DefineFun(fun_def) = m_res {
61-
if fun_def.0.0 == symbol {
62-
Some(fun_def)
63-
} else {
64-
None
65-
}
66-
} else {
67-
None
68-
}
69-
})
70-
}
71-
7219
/// A [`z3::Model`] which keeps track of the accessed constants. This is useful
7320
/// to later print those constants which were not accessed by any of the
7421
/// [`SmtEval`] implementations (e.g. stuff generated by Z3 we don't know
7522
/// about). This way, we can print the whole model and pretty-print everything
7623
/// we know, and then print the rest of the assignments in the model as well.
7724
#[derive(Debug)]
7825
pub struct InstrumentedModel<'ctx> {
79-
model: ModelSource<'ctx>,
26+
model: Model<'ctx>,
8027
// TODO: turn this into a HashSet of FuncDecls when the type implements Hash
8128
accessed_decls: RefCell<im_rc::HashSet<String>>,
8229
accessed_exprs: RefCell<im_rc::HashSet<Dynamic<'ctx>>>,
8330
}
8431

8532
impl<'ctx> InstrumentedModel<'ctx> {
86-
pub fn new(model: ModelSource<'ctx>) -> Self {
33+
pub fn new(model: Model<'ctx>) -> Self {
8734
InstrumentedModel {
8835
model,
8936
accessed_decls: Default::default(),
@@ -113,20 +60,14 @@ impl<'ctx> InstrumentedModel<'ctx> {
11360
/// the model.
11461
pub fn eval<T: Ast<'ctx>>(&self, ast: &T, model_completion: bool) -> Option<T> {
11562
self.add_children_accessed(Dynamic::from_ast(ast));
116-
match &self.model {
117-
ModelSource::Z3Model(model) => {
118-
let res = model.eval(ast, model_completion)?;
119-
Some(res)
120-
}
121-
ModelSource::SwineModel(_) => todo!(),
122-
}
63+
let res = self.model.eval(ast, model_completion)?;
64+
Some(res)
12365
}
12466

12567
/// Get the function interpretation for this `f`.
12668
pub fn get_func_interp(&self, f: &FuncDecl<'ctx>) -> Option<FuncInterp<'ctx>> {
12769
self.accessed_decls.borrow_mut().insert(f.name());
128-
todo!()
129-
// self.model.get_func_interp(f)
70+
self.model.get_func_interp(f)
13071
}
13172

13273
/// Add this ast node and all its children to the accessed set.
@@ -151,12 +92,9 @@ impl<'ctx> InstrumentedModel<'ctx> {
15192
/// Iterate over all function declarations that were not accessed using
15293
/// `eval` so far.
15394
pub fn iter_unaccessed(&self) -> impl Iterator<Item = FuncDecl<'ctx>> + '_ {
154-
todo!()
155-
/*
15695
self.model
15796
.iter()
15897
.filter(|decl| !self.accessed_decls.borrow().contains(&decl.name()))
159-
*/
16098
}
16199

162100
/// Reset the internally tracked accessed declarations and expressions.
@@ -166,17 +104,15 @@ impl<'ctx> InstrumentedModel<'ctx> {
166104
}
167105

168106
pub fn into_model(self) -> Model<'ctx> {
169-
todo!()
170-
// self.model
107+
self.model
171108
}
172109
}
173110

174111
/// The [`Display`] implementation simply defers to the underlying
175112
/// [`z3::Model`]'s implementation.
176113
impl Display for InstrumentedModel<'_> {
177114
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178-
todo!()
179-
// f.write_fmt(format_args!("{}", &self.model))
115+
f.write_fmt(format_args!("{}", &self.model))
180116
}
181117
}
182118

@@ -200,164 +136,64 @@ impl<'ctx> SmtEval<'ctx> for Bool<'ctx> {
200136
type Value = bool;
201137

202138
fn eval(&self, model: &InstrumentedModel<'ctx>) -> Result<bool, SmtEvalError> {
203-
match model.model {
204-
ModelSource::Z3Model(_) => {
205-
model
206-
.eval(self, true)
207-
.ok_or(SmtEvalError::EvalError)?
208-
.as_bool()
209-
.ok_or(SmtEvalError::ParseError)
210-
},
211-
ModelSource::SwineModel(s_model) => {
212-
213-
let name: String = self.decl().name();
214-
215-
match find_fun_def(&s_model, &name) {
216-
Some(fun_def) => match fun_def.3 {
217-
Term::Identifier(QualIdentifier::Identifier(Identifier::Simple(Symbol(
218-
"true",
219-
)))) => Ok(true),
220-
Term::Identifier(QualIdentifier::Identifier(Identifier::Simple(Symbol(
221-
"false",
222-
)))) => Ok(false),
223-
_ => Err(SmtEvalError::EvalError),
224-
},
225-
None => Err(SmtEvalError::EvalError),
226-
}
227-
},
228-
}
139+
model
140+
.eval(self, true)
141+
.ok_or(SmtEvalError::EvalError)?
142+
.as_bool()
143+
.ok_or(SmtEvalError::ParseError)
229144
}
230145
}
231146

232147
impl<'ctx> SmtEval<'ctx> for Int<'ctx> {
233148
type Value = BigInt;
234149

235150
fn eval(&self, model: &InstrumentedModel<'ctx>) -> Result<BigInt, SmtEvalError> {
236-
match model.model {
237-
ModelSource::Z3Model(_) => {
238-
// TODO: Z3's as_i64 only returns an i64 value. is there something more complete?
239-
let value = model
240-
.eval(self, true)
241-
.ok_or(SmtEvalError::EvalError)?
242-
.as_i64()
243-
.ok_or(SmtEvalError::ParseError)?;
244-
Ok(BigInt::from(value))
245-
}
246-
ModelSource::SwineModel(s_model) => {
247-
let name = self.decl().name();
248-
match find_fun_def(&s_model, &name) {
249-
Some(fun_def) => match fun_def.3 {
250-
Term::SpecConstant(SpecConstant::Numeral(numeral)) => {
251-
if let Ok(n) = numeral.into_u128() {
252-
Ok(BigInt::from(n))
253-
} else {
254-
Err(SmtEvalError::EvalError)
255-
}
256-
}
257-
_ => Err(SmtEvalError::EvalError),
258-
},
259-
None => Err(SmtEvalError::EvalError),
260-
}
261-
},
262-
}
151+
// TODO: Z3's as_i64 only returns an i64 value. is there something more complete?
152+
let value = model
153+
.eval(self, true)
154+
.ok_or(SmtEvalError::EvalError)?
155+
.as_i64()
156+
.ok_or(SmtEvalError::ParseError)?;
157+
Ok(BigInt::from(value))
263158
}
264159
}
265160

266161
impl<'ctx> SmtEval<'ctx> for Real<'ctx> {
267162
type Value = BigRational;
268163

269164
fn eval(&self, model: &InstrumentedModel<'ctx>) -> Result<Self::Value, SmtEvalError> {
270-
match model.model {
271-
ModelSource::Z3Model(_) => {
272-
let res = model
273-
.eval(self, false) // TODO
274-
.ok_or(SmtEvalError::EvalError)?;
275-
276-
// The .as_real() method only returns a pair of i64 values. If the
277-
// results don't fit in these types, we start some funky string parsing.
278-
if let Some((num, den)) = res.as_real() {
279-
Ok(BigRational::new(num.into(), den.into()))
280-
} else {
281-
// we parse a string of the form "(/ num.0 denom.0)"
282-
let division_expr = format!("{:?}", res);
283-
if !division_expr.starts_with("(/ ") || !division_expr.ends_with(".0)") {
284-
return Err(SmtEvalError::ParseError);
285-
}
286-
287-
let mut parts = division_expr.split_ascii_whitespace();
165+
let res = model
166+
.eval(self, false) // TODO
167+
.ok_or(SmtEvalError::EvalError)?;
168+
169+
// The .as_real() method only returns a pair of i64 values. If the
170+
// results don't fit in these types, we start some funky string parsing.
171+
if let Some((num, den)) = res.as_real() {
172+
Ok(BigRational::new(num.into(), den.into()))
173+
} else {
174+
// we parse a string of the form "(/ num.0 denom.0)"
175+
let division_expr = format!("{:?}", res);
176+
if !division_expr.starts_with("(/ ") || !division_expr.ends_with(".0)") {
177+
return Err(SmtEvalError::ParseError);
178+
}
288179

289-
let first_part = parts.next().ok_or(SmtEvalError::ParseError)?;
290-
if first_part != "(/" {
291-
return Err(SmtEvalError::ParseError);
292-
}
180+
let mut parts = division_expr.split_ascii_whitespace();
293181

294-
let second_part = parts.next().ok_or(SmtEvalError::ParseError)?;
295-
let second_part = second_part.replace(".0", "");
296-
let numerator = BigInt::from_str(&second_part).map_err(|_| SmtEvalError::ParseError)?;
182+
let first_part = parts.next().ok_or(SmtEvalError::ParseError)?;
183+
if first_part != "(/" {
184+
return Err(SmtEvalError::ParseError);
185+
}
297186

298-
let third_part = parts.next().ok_or(SmtEvalError::ParseError)?;
299-
let third_part = third_part.replace(".0)", "");
300-
let denominator =
301-
BigInt::from_str(&third_part).map_err(|_| SmtEvalError::ParseError)?;
187+
let second_part = parts.next().ok_or(SmtEvalError::ParseError)?;
188+
let second_part = second_part.replace(".0", "");
189+
let numerator = BigInt::from_str(&second_part).map_err(|_| SmtEvalError::ParseError)?;
302190

303-
Ok(BigRational::new(numerator, denominator))
304-
}
305-
},
306-
ModelSource::SwineModel(s_model) => {
307-
let name = self.decl().name();
191+
let third_part = parts.next().ok_or(SmtEvalError::ParseError)?;
192+
let third_part = third_part.replace(".0)", "");
193+
let denominator =
194+
BigInt::from_str(&third_part).map_err(|_| SmtEvalError::ParseError)?;
308195

309-
match find_fun_def(&s_model, &name) {
310-
Some(fun_def) => match fun_def.3 {
311-
Term::SpecConstant(SpecConstant::Decimal(Decimal(d_str))) => {
312-
let f = (*d_str).parse::<f64>().map_err(|_| SmtEvalError::EvalError)?;
313-
let res = BigRational::from_float(f).ok_or(SmtEvalError::EvalError)?;
314-
Ok(res)
315-
}
316-
Term::Application(QualIdentifier::Identifier(Identifier::Simple(Symbol("/"))), [t1, t2]) => {
317-
let mut num = BigInt::from(1);
318-
let mut den = BigInt::from(1);
319-
match t1 {
320-
Term::SpecConstant(SpecConstant::Numeral(numeral)) => {
321-
let n = numeral.into_u128().map_err(|_| SmtEvalError::EvalError)?;
322-
num *= BigInt::from(n);
323-
},
324-
Term::SpecConstant(SpecConstant::Decimal(Decimal(d_str))) => {
325-
if let Some((n, d)) = from_str_to_num_den(d_str) {
326-
num *= n;
327-
den *= d;
328-
} else {
329-
return Err(SmtEvalError::EvalError);
330-
}
331-
},
332-
_ => {
333-
return Err(SmtEvalError::EvalError);
334-
},
335-
}
336-
match t2 {
337-
Term::SpecConstant(SpecConstant::Numeral(numeral)) => {
338-
let n = numeral.into_u128().map_err(|_| SmtEvalError::EvalError)?;
339-
den *= BigInt::from(n);
340-
},
341-
Term::SpecConstant(SpecConstant::Decimal(Decimal(d_str))) => {
342-
if let Some((n, d)) = from_str_to_num_den(d_str) {
343-
den *= n;
344-
num *= d;
345-
} else {
346-
return Err(SmtEvalError::EvalError);
347-
}
348-
},
349-
_ => {
350-
return Err(SmtEvalError::EvalError);
351-
},
352-
}
353-
Ok(BigRational::new(num, den))
354-
}
355-
_ => Err(SmtEvalError::EvalError),
356-
},
357-
None => Err(SmtEvalError::EvalError),
358-
}
359-
},
196+
Ok(BigRational::new(numerator, denominator))
360197
}
361-
362198
}
363199
}

0 commit comments

Comments
 (0)