-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `Context::forall` and `Context::exists` functions were not structuring the S-expressions for variables correctly. This PR fixes that, and adds an example that uses quantifiers as a means of testing. To demonstrate the problem, run the new example without the changes to `src/context.rs`. The example uses the following `forall` quantifier: ``` ctx.assert( // For any pair of sets, ctx.forall( [("s1", ctx.atom("MySet")), ("s2", ctx.atom("MySet"))], // One of the following is true: ctx.or( ... ) ) ) ``` which produces the following trace: ``` $ RUST_LOG="easy_smt=trace" cargo run --example quantifiers ... [_] -> (declare-fun subset (MySet MySet) Bool) [_] <- success [_] -> (assert (forall (s1 MySet) (s2 MySet) (or (subset s1 s2) (exists (x MyElement) (and (member x s1) (not (member x s2))))))) [_] <- (error "line 8 column 17: invalid sorted variable, '(' expected got s1") ``` The problem is that the variable declarations `(s1 MySet)` and `(s2 MySet)` are not grouped together as a sub-list. The forall expression is supposed to have the shape: ``` (forall ((var1 Sort1) ... (varN SortN)) (body)) ``` The same problem occurs for the `exists` function: the variable declaration `(x MyElement)` in the above trace should be a singleton list `((x MyElement))`. With the changes to `src/context.rs`, the example is fixed: ``` $ RUST_LOG="easy_smt=trace" cargo run --example quantifiers ... [_] -> (declare-fun subset (MySet MySet) Bool) [_] <- success [_] -> (assert (forall ((s1 MySet) (s2 MySet)) (or (subset s1 s2) (exists ((x MyElement)) (and (member x s1) (not (member x s2))))))) [_] <- success ... ``` Thanks for writing this library! Aside from this bug, I've found it easy to get up and running with it.
- Loading branch information
Showing
2 changed files
with
143 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// In this example, we assert quantified formulas over uninterpreted | ||
// sorts and functions. | ||
|
||
fn main() -> std::io::Result<()> { | ||
env_logger::init(); | ||
let mut ctx = easy_smt::ContextBuilder::new() | ||
.solver("z3", ["-smt2", "-in"]) | ||
.build()?; | ||
|
||
// Declare an uninterpreted representation for sets. | ||
ctx.declare_sort("MySet", 0)?; | ||
// Declare representation for elements of those sets. | ||
ctx.declare_sort("MyElement", 0)?; | ||
|
||
// Our sets have a simple interface of predicates: empty(s), | ||
// member(x,s), and subset(s1,s2). | ||
ctx.declare_fun( | ||
"empty", | ||
vec![ctx.atom("MySet")], | ||
ctx.bool_sort(), | ||
)?; | ||
ctx.declare_fun( | ||
"member", | ||
vec![ctx.atom("MyElement"), ctx.atom("MySet")], | ||
ctx.bool_sort(), | ||
)?; | ||
ctx.declare_fun( | ||
"subset", | ||
vec![ctx.atom("MySet"), ctx.atom("MySet")], | ||
ctx.bool_sort(), | ||
)?; | ||
|
||
// We assert some simple axioms on this interface. | ||
|
||
// First, a set s1 is either a subset of s2, or a member of s1 | ||
// exists which is not a member of s2. | ||
ctx.assert( | ||
// For any pair of sets, | ||
ctx.forall( | ||
[("s1", ctx.atom("MySet")), ("s2", ctx.atom("MySet"))], | ||
// One of the following is true: | ||
ctx.or( | ||
// (1) s1 is a subset of s2, or | ||
ctx.list(vec![ | ||
ctx.atom("subset"), | ||
ctx.atom("s1"), | ||
ctx.atom("s2"), | ||
]), | ||
// (2) an element exists which is a member of s1, and | ||
// not a member of s2. | ||
ctx.exists( | ||
[("x", ctx.atom("MyElement"))], | ||
ctx.and( | ||
ctx.list(vec![ | ||
ctx.atom("member"), | ||
ctx.atom("x"), | ||
ctx.atom("s1"), | ||
]), | ||
ctx.not(ctx.list(vec![ | ||
ctx.atom("member"), | ||
ctx.atom("x"), | ||
ctx.atom("s2"), | ||
])), | ||
), | ||
), | ||
), | ||
) | ||
)?; | ||
|
||
// Second, a set is empty iff no member exists. | ||
ctx.assert( | ||
// For any set, | ||
ctx.forall( | ||
[("s1", ctx.atom("MySet"))], | ||
// The following formulas are equivalent: | ||
ctx.eq( | ||
// (1) the set is not empty, and | ||
ctx.not(ctx.list(vec![ctx.atom("empty"), ctx.atom("s1")])), | ||
// (2) an element exists that is a member of the set. | ||
ctx.exists( | ||
[("x", ctx.atom("MyElement"))], | ||
ctx.list(vec![ | ||
ctx.atom("member"), | ||
ctx.atom("x"), | ||
ctx.atom("s1"), | ||
]), | ||
), | ||
), | ||
), | ||
)?; | ||
|
||
// Now, let's check whether a relationship between two sets is | ||
// possible. | ||
ctx.declare_const("s1", ctx.atom("MySet"))?; | ||
ctx.declare_const("s2", ctx.atom("MySet"))?; | ||
|
||
// Is it possible for s1 to be empty, | ||
ctx.assert( | ||
ctx.list(vec![ctx.atom("empty"), ctx.atom("s1")]) | ||
)?; | ||
// while also s1 is not a subset of s2? | ||
ctx.assert( | ||
ctx.not( | ||
ctx.list(vec![ | ||
ctx.atom("subset"), | ||
ctx.atom("s1"), | ||
ctx.atom("s2"), | ||
]) | ||
) | ||
)?; | ||
|
||
// We expect the answer to be no (UNSAT). | ||
match ctx.check()? { | ||
easy_smt::Response::Sat => { | ||
println!("Solver returned SAT. This is unexpected!"); | ||
}, | ||
easy_smt::Response::Unsat => { | ||
println!("Solver returned UNSAT. This is expected."); | ||
}, | ||
easy_smt::Response::Unknown => { | ||
println!("Solver returned UNKNOWN. This is unexpected!"); | ||
}, | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters