use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::ast::AstId;
use crate::ast::manager::AstManager;
use crate::ast::node::AstNode;
use crate::rewriter::simplify;
#[derive(Clone, Debug, Default)]
pub struct FuncInterp {
pub entries: Vec<(Vec<AstId>, AstId)>,
pub els: Option<AstId>,
}
impl FuncInterp {
fn get(&self, args: &[AstId]) -> Option<AstId> {
for (k, v) in &self.entries {
if k.as_slice() == args {
return Some(*v);
}
}
self.els
}
}
#[derive(Clone, Debug, Default)]
pub struct Model {
consts: BTreeMap<AstId, AstId>,
funcs: BTreeMap<AstId, FuncInterp>,
}
impl Model {
pub fn new() -> Model {
Model::default()
}
pub fn assign_const(&mut self, decl: AstId, value: AstId) {
self.consts.insert(decl, value);
}
pub fn assign_func(&mut self, decl: AstId, interp: FuncInterp) {
self.funcs.insert(decl, interp);
}
pub fn get_const(&self, decl: AstId) -> Option<AstId> {
self.consts.get(&decl).copied()
}
pub fn get_func(&self, decl: AstId) -> Option<&FuncInterp> {
self.funcs.get(&decl)
}
pub fn num_consts(&self) -> usize {
self.consts.len()
}
pub fn eval(&self, m: &mut AstManager, expr: AstId) -> AstId {
let order = m.postorder(expr);
let mut memo: BTreeMap<AstId, AstId> = BTreeMap::new();
for id in order {
let value = match m.node(id).clone() {
AstNode::App(a) => {
let decl = a.decl;
if a.args.is_empty() {
self.consts.get(&decl).copied().unwrap_or(id)
} else {
let vals: Vec<AstId> = a.args.iter().map(|c| memo[c]).collect();
if let Some(interp) = self.funcs.get(&decl)
&& let Some(v) = interp.get(&vals)
{
v
} else {
let rebuilt = m.mk_app(decl, &vals);
simplify(m, rebuilt)
}
}
}
_ => id,
};
memo.insert(id, value);
}
memo[&expr]
}
pub fn eval_bool(&self, m: &mut AstManager, expr: AstId) -> Option<bool> {
let v = self.eval(m, expr);
if m.is_true(v) {
Some(true)
} else if m.is_false(v) {
Some(false)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use puremp::Rational;
#[test]
fn evaluates_arithmetic_and_predicates() {
let mut m = AstManager::new();
let x = m.mk_int_const("x");
let y = m.mk_int_const("y");
let two = m.mk_int(2);
let two_y = m.mk_mul(&[two, y]);
let sum = m.mk_add(&[x, two_y]);
let lt = m.mk_lt(x, y);
let mut model = Model::new();
let three = m.mk_int(3);
let five = m.mk_int(5);
model.assign_const(m.app_decl(x), three);
model.assign_const(m.app_decl(y), five);
let v = model.eval(&mut m, sum);
assert_eq!(m.as_numeral(v), Some(Rational::from_integer(13.into())));
assert_eq!(model.eval_bool(&mut m, lt), Some(true));
}
#[test]
fn evaluates_function_graph() {
let mut m = AstManager::new();
let int = m.mk_int_sort();
let f = m.mk_func_decl(crate::util::symbol::Symbol::new("f"), &[int], int);
let one = m.mk_int(1);
let two = m.mk_int(2);
let f1 = m.mk_app(f, &[one]);
let f2 = m.mk_app(f, &[two]);
let ten = m.mk_int(10);
let zero = m.mk_int(0);
let mut model = Model::new();
model.assign_func(
f,
FuncInterp {
entries: alloc::vec![(alloc::vec![one], ten)],
els: Some(zero),
},
);
assert_eq!(model.eval(&mut m, f1), ten);
assert_eq!(model.eval(&mut m, f2), zero);
}
#[test]
fn partial_model_is_total_and_symbolic() {
let mut m = AstManager::new();
let x = m.mk_int_const("x");
let zero = m.mk_int(0);
let sum = m.mk_add(&[x, zero]);
let model = Model::new();
assert_eq!(model.eval(&mut m, sum), x);
}
}