Skip to content

Commit e0a21ab

Browse files
committed
simplify: detect xor for aig
1 parent 68c87c0 commit e0a21ab

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

patronus/src/expr/simplify.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,12 +363,80 @@ fn simplify_bv_or(ctx: &mut Context, a: ExprRef, b: ExprRef) -> Option<ExprRef>
363363
let and = ctx.and(*a, *b);
364364
Some(ctx.not(and))
365365
}
366+
// sum of products form
367+
(Expr::BVAnd(a1, b1, _), Expr::BVAnd(a2, b2, _)) => {
368+
simplify_sum_of_products(ctx, *a1, *b1, *a2, *b2)
369+
}
366370
_ => None,
367371
}
368372
}
369373
}
370374
}
371375

376+
// simplifies some sum of products expressions over exactly two products of t variables
377+
fn simplify_sum_of_products(
378+
ctx: &mut Context,
379+
a1: ExprRef,
380+
b1: ExprRef,
381+
a2: ExprRef,
382+
b2: ExprRef,
383+
) -> Option<ExprRef> {
384+
// recognize a / !a
385+
let mut lits_1 = [Lit::from_expr(ctx, a1), Lit::from_expr(ctx, b1)];
386+
let mut lits_2 = [Lit::from_expr(ctx, a2), Lit::from_expr(ctx, b2)];
387+
// sort by inner expression in order to normalize the order since AND is commutative
388+
lits_1.sort_by_key(|l| l.get_var());
389+
lits_2.sort_by_key(|l| l.get_var());
390+
// define a and b
391+
let a = lits_1[0].get_var();
392+
let b = lits_1[1].get_var();
393+
// check that the second product also is over the same a and b
394+
if a == lits_2[0].get_var() && b == lits_2[1].get_var() {
395+
let prod_1 = (lits_1[0].is_neg(), lits_1[1].is_neg());
396+
let prod_2 = (lits_2[0].is_neg(), lits_2[1].is_neg());
397+
// sort products
398+
let mut prods = [prod_1, prod_2];
399+
prods.sort();
400+
401+
match (prods[0], prods[1]) {
402+
// !a & b | a & !b
403+
((false, true), (true, false)) => Some(ctx.xor(a, b)),
404+
// !a & !b | a & b
405+
// TODO: this currently cannot be triggered, because !a & !b gets converted with demorgan first!
406+
((false, false), (true, true)) => Some(ctx.equal(a, b)),
407+
_ => None,
408+
}
409+
} else {
410+
None
411+
}
412+
}
413+
414+
#[derive(Debug, Copy, Clone)]
415+
enum Lit {
416+
Pos(ExprRef),
417+
Neg(ExprRef),
418+
}
419+
420+
impl Lit {
421+
fn from_expr(ctx: &Context, e: ExprRef) -> Self {
422+
if let Expr::BVNot(a, _) = &ctx[e] {
423+
Lit::Neg(*a)
424+
} else {
425+
Lit::Pos(e)
426+
}
427+
}
428+
fn get_var(&self) -> ExprRef {
429+
match self {
430+
Lit::Pos(e) => *e,
431+
Lit::Neg(e) => *e,
432+
}
433+
}
434+
435+
fn is_neg(&self) -> bool {
436+
matches!(self, Lit::Neg(_))
437+
}
438+
}
439+
372440
fn simplify_bv_xor(ctx: &mut Context, a: ExprRef, b: ExprRef) -> Option<ExprRef> {
373441
// a xor a -> 0
374442
if a == b {

patronus/tests/simplify.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ fn test_simplify_and() {
3737
ts(
3838
"and(not(a:bv<3>), not(b:bv<3>))",
3939
"not(or(a:bv<3>, b:bv<3>))",
40-
)
40+
);
41+
42+
// and encoded xor (as one would get from an and-inverter-graph
43+
ts(
44+
"and(not(and(not(a:bv<3>), b:bv<3>)), not(and(not(b), a)))",
45+
"not(xor(a:bv<3>, b:bv<3>))",
46+
);
4147
}
4248

4349
#[test]
@@ -55,7 +61,16 @@ fn test_simplify_or() {
5561
ts(
5662
"or(not(a:bv<3>), not(b:bv<3>))",
5763
"not(and(a:bv<3>, b:bv<3>))",
58-
)
64+
);
65+
66+
// xor truth table (sum of products)
67+
ts(
68+
"or(and(not(a:bv<3>), b:bv<3>), and(not(b), a))",
69+
"xor(a:bv<3>, b:bv<3>)",
70+
);
71+
72+
// equality truth table (sum of products)
73+
// ts("or(and(a:bv<3>, b:bv<3>), and(not(b), not(a)))", "eq(a:bv<3>, b:bv<3>)");
5974
}
6075

6176
#[test]

0 commit comments

Comments
 (0)