Skip to content

Commit d7f4f03

Browse files
committed
simplifier: fix wrong de morgan rewrite
1 parent f751c65 commit d7f4f03

File tree

4 files changed

+89
-36
lines changed

4 files changed

+89
-36
lines changed

patronus-sca/src/lib.rs

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ impl<'a, 'b> Display for PrettyPoly<'a, 'b> {
506506
mod tests {
507507
use super::*;
508508
use patronus::expr::{eval_bv_expr, find_symbols};
509-
use patronus::smt::{SmtCommand, read_command};
509+
use patronus::smt::{BITWUZLA, Logic, SmtCommand, Solver, SolverContext, read_command};
510510
use rustc_hash::FxHashMap;
511511
use std::io::BufReader;
512512

@@ -630,33 +630,6 @@ mod tests {
630630
true
631631
}
632632

633-
#[test]
634-
fn test_coward_three_add_with_rewrite_based_simplifier() {
635-
// there might be a bug in our rewrite rules
636-
let filename = "../inputs/coward/add_three.4_bit.smt2";
637-
let mut ctx = Context::default();
638-
let e = read_first_assert_expr(&mut ctx, filename).unwrap();
639-
let p = find_sca_simplification_candidates(&ctx, e)[0];
640-
// assert!(is_eq_exhaustive(&ctx, p));
641-
642-
let mut simplifier = Simplifier::new(DenseExprMetaData::default());
643-
let simplified_gate = simplifier.simplify(&mut ctx, p.gate_level);
644-
println!(
645-
"gate[2] = {}",
646-
extract_bit(&mut ctx, p.gate_level, 2).serialize_to_str(&ctx)
647-
);
648-
println!(
649-
"gate[2] = {}",
650-
extract_bit(&mut ctx, simplified_gate, 2).serialize_to_str(&ctx)
651-
);
652-
let simplified_p = ScaEqualityProblem {
653-
equality: p.equality,
654-
gate_level: simplified_gate,
655-
word_level: p.word_level,
656-
};
657-
assert!(is_eq_exhaustive(&ctx, simplified_p));
658-
}
659-
660633
#[test]
661634
fn test_full_adder() {
662635
let mut ctx = Context::default();

patronus/src/expr/context.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ impl Context {
244244
pub fn ones(&mut self, width: WidthInt) -> ExprRef {
245245
self.bv_lit(&BitVecValue::ones(width))
246246
}
247+
248+
pub fn distinct(&mut self, a: ExprRef, b: ExprRef) -> ExprRef {
249+
let is_eq = self.equal(a, b);
250+
self.not(is_eq)
251+
}
247252
pub fn equal(&mut self, a: ExprRef, b: ExprRef) -> ExprRef {
248253
debug_assert_eq!(a.get_type(self), b.get_type(self));
249254
if a.get_type(self).is_bit_vector() {

patronus/src/expr/simplify.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
// author: Kevin Laeufer <laeufer@cornell.edu>
55

66
use super::{
7-
BVLitValue, Context, Expr, ExprMap, ExprRef, SparseExprMap, TypeCheck, WidthInt,
8-
do_transform_expr,
7+
BVLitValue, Context, Expr, ExprMap, ExprRef, SerializableIrNode, SparseExprMap, TypeCheck,
8+
WidthInt, do_transform_expr, find_symbols,
99
};
1010
use crate::expr::meta::get_fixed_point;
1111
use crate::expr::transform::ExprTransformMode;
12+
use crate::smt::{CheckSatResponse, SolverContext};
1213
use baa::BitVecOps;
1314
use smallvec::{SmallVec, smallvec};
1415

@@ -38,6 +39,68 @@ impl<T: ExprMap<Option<ExprRef>>> Simplifier<T> {
3839
);
3940
get_fixed_point(&mut self.cache, e).unwrap()
4041
}
42+
43+
/// Uses an SMT solver to check all simplification steps that have been made with this Simplifier.
44+
/// Returns the number of incorrect simplifications.
45+
pub fn verify_simplification(
46+
&self,
47+
ctx: &mut Context,
48+
solver: &mut impl SolverContext,
49+
) -> crate::smt::Result<usize> {
50+
let mut incorrect = 0;
51+
let mut correct = 0;
52+
for (key, &value) in self.cache.iter() {
53+
if let Some(simplified) = value {
54+
let key_symbols = find_symbols(ctx, key);
55+
let simpl_symbols = find_symbols(ctx, simplified);
56+
let symbols: Vec<_> = key_symbols.union(&simpl_symbols).cloned().collect();
57+
solver.push()?;
58+
for &sym in symbols.iter() {
59+
solver.declare_const(ctx, sym)?;
60+
}
61+
let not_eq = ctx.distinct(key, simplified);
62+
solver.assert(ctx, not_eq)?;
63+
match solver.check_sat()? {
64+
CheckSatResponse::Sat => {
65+
let key_value = solver.get_value(ctx, key)?;
66+
let simplified_value = solver.get_value(ctx, simplified)?;
67+
println!(
68+
"{} ({}) =/= ({}) {}",
69+
key.serialize_to_str(ctx),
70+
key_value.serialize_to_str(ctx),
71+
simplified_value.serialize_to_str(ctx),
72+
simplified.serialize_to_str(ctx)
73+
);
74+
let mut syms = vec![];
75+
for &sym in symbols.iter() {
76+
let value = solver.get_value(ctx, sym)?;
77+
syms.push(format!(
78+
"{}={}",
79+
sym.serialize_to_str(ctx),
80+
value.serialize_to_str(ctx)
81+
));
82+
}
83+
println!(" w/ {}", syms.join(", "));
84+
incorrect += 1;
85+
}
86+
CheckSatResponse::Unsat => {
87+
correct += 1;
88+
} // OK
89+
CheckSatResponse::Unknown => {} // OK
90+
}
91+
92+
solver.pop()?;
93+
}
94+
}
95+
if incorrect > 0 {
96+
println!(
97+
"{incorrect} / {} simplifications were incorrect. See log.",
98+
incorrect + correct
99+
);
100+
}
101+
102+
Ok(incorrect)
103+
}
41104
}
42105

43106
/// Simplifies one expression (not its children)
@@ -254,8 +317,11 @@ fn simplify_bv_and(ctx: &mut Context, a: ExprRef, b: ExprRef) -> Option<ExprRef>
254317
// a & !a -> 0
255318
(Expr::BVNot(inner, w), _) if *inner == b => Some(ctx.zero(*w)),
256319
(_, Expr::BVNot(inner, w)) if *inner == a => Some(ctx.zero(*w)),
257-
// !a & !b -> a | b
258-
(Expr::BVNot(a, _), Expr::BVNot(b, _)) => Some(ctx.or(*a, *b)),
320+
// !a & !b -> !(a | b)
321+
(Expr::BVNot(a, _), Expr::BVNot(b, _)) => {
322+
let or = ctx.or(*a, *b);
323+
Some(ctx.not(or))
324+
}
259325
_ => None,
260326
}
261327
}
@@ -290,8 +356,11 @@ fn simplify_bv_or(ctx: &mut Context, a: ExprRef, b: ExprRef) -> Option<ExprRef>
290356
// a | !a -> 1
291357
(Expr::BVNot(inner, w), _) if *inner == b => Some(ctx.ones(*w)),
292358
(_, Expr::BVNot(inner, w)) if *inner == a => Some(ctx.ones(*w)),
293-
// !a | !b -> a & b
294-
(Expr::BVNot(a, _), Expr::BVNot(b, _)) => Some(ctx.and(*a, *b)),
359+
// !a | !b -> !(a & b)
360+
(Expr::BVNot(a, _), Expr::BVNot(b, _)) => {
361+
let and = ctx.and(*a, *b);
362+
Some(ctx.not(and))
363+
}
295364
_ => None,
296365
}
297366
}

patronus/tests/simplify.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ fn test_simplify_and() {
3434
ts("and(not(a : bv<1>), a)", "false");
3535

3636
// de morgan
37-
ts("and(not(a:bv<3>), not(b:bv<3>))", "or(a:bv<3>, b:bv<3>)")
37+
ts(
38+
"and(not(a:bv<3>), not(b:bv<3>))",
39+
"not(or(a:bv<3>, b:bv<3>))",
40+
)
3841
}
3942

4043
#[test]
@@ -49,7 +52,10 @@ fn test_simplify_or() {
4952
ts("or(not(a : bv<1>), a)", "true");
5053

5154
// de morgan
52-
ts("or(not(a:bv<3>), not(b:bv<3>))", "and(a:bv<3>, b:bv<3>)")
55+
ts(
56+
"or(not(a:bv<3>), not(b:bv<3>))",
57+
"not(and(a:bv<3>, b:bv<3>))",
58+
)
5359
}
5460

5561
#[test]

0 commit comments

Comments
 (0)