From f098ccee982add9459ce287efb0d5d712c141dc4 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 28 Jun 2018 19:03:23 +0900 Subject: [PATCH 01/94] datatype declaration (parsing, dtyp construction) --- rsc/inactive/datatypes.smt2 | 30 + src/common/consts.rs | 1 + src/common/macros.rs | 6 +- src/common/mod.rs | 5 +- src/common/smt.rs | 8 +- src/common/wrappers.rs | 2 +- src/dtyp/mod.rs | 440 +++++++++++++ src/hoice.rs | 8 +- src/instance/instance/mod.rs | 8 +- src/instance/mod.rs | 3 +- src/learning/ice/quals.rs | 6 + src/learning/ice/synth/mod.rs | 6 +- src/{instance/parse.rs => parse/mod.rs} | 837 ++++++++++-------------- src/parse/ptterms.rs | 483 ++++++++++++++ src/term/typ.rs | 215 +++++- 15 files changed, 1551 insertions(+), 507 deletions(-) create mode 100644 rsc/inactive/datatypes.smt2 create mode 100644 src/dtyp/mod.rs rename src/{instance/parse.rs => parse/mod.rs} (83%) create mode 100644 src/parse/ptterms.rs diff --git a/rsc/inactive/datatypes.smt2 b/rsc/inactive/datatypes.smt2 new file mode 100644 index 00000000..116c80b7 --- /dev/null +++ b/rsc/inactive/datatypes.smt2 @@ -0,0 +1,30 @@ +(declare-datatypes (T1 T2) ( + (Pair (mk-pair (first T1) (second T2))) +) ) +(declare-datatypes (T1 T2) ( + (Pair2 (mk-pair-2 (first (Pair T1 T2)) (second T2))) +)) + +(declare-datatypes () ( + (S A B C) +)) + +(declare-datatypes (T) ( + (Lst + nil + (cons (hd T) (tl Lst)) + ) +)) + +(declare-datatypes (T) + ( + (Tree + leaf + (node (value T) (children TreeList)) + ) + (TreeList + tnil + (tcons (car Tree) (cdr TreeList)) + ) + ) +) \ No newline at end of file diff --git a/src/common/consts.rs b/src/common/consts.rs index 9be28572..af6f5235 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -151,6 +151,7 @@ pub mod keywords { } keys { + dec_dtyp ("declare-datatypes", doc = "Datatype declaration keyword.") dec_fun ("declare-fun", doc = "Predicate declaration keyword.") def_fun ("define-fun", doc = "Predicate definition keyword.") def_funs ( diff --git a/src/common/macros.rs b/src/common/macros.rs index 6c2f81e0..da384019 100644 --- a/src/common/macros.rs +++ b/src/common/macros.rs @@ -451,6 +451,8 @@ macro_rules! rat_to_smt { + + /// Test macros #[cfg(test)] #[macro_use] @@ -503,4 +505,6 @@ mod test { assert!( res ) }) ; } -} \ No newline at end of file +} + + diff --git a/src/common/mod.rs b/src/common/mod.rs index a436cc8e..7306d143 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -29,6 +29,9 @@ pub use term::{ typ, } ; +pub use dtyp ; +pub use dtyp::DTyp ; + pub use val ; pub use val::Val ; @@ -625,7 +628,7 @@ impl VarIndexed for VarHMap<(VarIdx, Typ)> { ) } } -impl VarIndexed for VarMap<::instance::parse::PTTerms> { +impl VarIndexed for VarMap<::parse::PTTerms> { #[inline] fn var_get(& self, var: VarIdx) -> Option { if self.len() < * var { diff --git a/src/common/smt.rs b/src/common/smt.rs index e19fde9c..b9b732e0 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -446,7 +446,7 @@ impl FullParser { let mut postponed = Vec::new() ; let mut instance = Instance::new() ; - let mut context = ::instance::parse::ParserCxt::new() ; + let mut context = ::parse::ParserCxt::new() ; let dummy_profiler = Profiler::new() ; let mut stuck ; @@ -535,7 +535,7 @@ impl FullParser { debug_assert_eq! { term.typ(), typ } let prev = instance.add_define_fun( name.clone(), var_infos, - ::instance::parse::PTTerms::tterm( TTerm::T(term.clone()) ) + ::parse::PTTerms::tterm( TTerm::T(term.clone()) ) ) ; debug_assert! { prev.is_none() } let prev = fun_defs.insert(name, (nu_sig, term)) ; @@ -579,7 +579,7 @@ impl<'a> IdentParser for FullParser { } } fn parse_type(self, input: & 'a str) -> SmtRes { - let mut cxt = ::instance::parse::ParserCxt::new() ; + let mut cxt = ::parse::ParserCxt::new() ; let dummy_profiler = Profiler::new() ; let mut parser = cxt.parser(input, 0, & dummy_profiler) ; match parser.sort_opt() { @@ -596,7 +596,7 @@ impl<'a> ModelParser for FullParser { self, input: & 'a str, _id: & FPVar, _params: & Vec<(FPVar, Typ)>, _out: & Typ ) -> SmtRes { - let mut cxt = ::instance::parse::ParserCxt::new() ; + let mut cxt = ::parse::ParserCxt::new() ; let dummy_profiler = Profiler::new() ; let mut parser = cxt.parser(input, 0, & dummy_profiler) ; diff --git a/src/common/wrappers.rs b/src/common/wrappers.rs index 7f77eb30..92f55511 100644 --- a/src/common/wrappers.rs +++ b/src/common/wrappers.rs @@ -209,4 +209,4 @@ wrap_usize!{ #[doc = "Total map from TAs to something."] map: TAsMap with iter: TAsMapIter } -unsafe impl Send for TAsIdx {} \ No newline at end of file +unsafe impl Send for TAsIdx {} diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs new file mode 100644 index 00000000..13ce51ae --- /dev/null +++ b/src/dtyp/mod.rs @@ -0,0 +1,440 @@ +//! Datatypes. + +use common::* ; + +wrap_usize!{ + #[doc = "Type parameter indices."] + TPrmIdx + #[doc = "Total map from type parameters to something."] + map: TPrmMap with iter: TPrmMapIter +} + +wrap_usize!{ + #[doc = "Constructor argument indices."] + CArgIdx + #[doc = "Total map from constructor arguments to something."] + map: CArgMap with iter: CArgMapIter +} + +/// A concrete type, a polymorphic type, a datatype or a type parameter. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PartialTyp { + /// Array. + Array(Box, Box), + + // /// List, + // List(PartialTyp), + + /// Datatype. + DTyp(String, ::parse::Pos, TPrmMap), + /// Concrete type. + Typ(Typ), + /// Type parameter. + Param(TPrmIdx), +} +impl From for PartialTyp { + fn from(typ: Typ) -> Self { + PartialTyp::Typ(typ) + } +} +impl PartialTyp { + /// Resolves a partial type against a type. + pub fn unify ( + & self, typ: & Typ, map: & mut TPrmMap + ) -> Res<()> { + let mut stack = vec![ (self, typ) ] ; + + while let Some((ptyp, typ)) = stack.pop() { + use typ::RTyp ; + + match ( ptyp, typ.get() ) { + ( PartialTyp::Array(p_src, p_tgt), RTyp::Array { src, tgt } ) => { + stack.push( (p_src, src) ) ; + stack.push( (p_tgt, tgt) ) + }, + + ( PartialTyp::Typ(p_typ), _ ) => if ! p_typ.is_compatible(typ) { + bail!("cannot unify type {} and {}", ptyp, typ) + }, + + ( PartialTyp::Param(idx), _ ) => if let Some(merged) = typ.merge( + & map[* idx] + ) { + map[* idx] = merged + } else { + bail!("cannot unify type {} and {}", ptyp, typ) + }, + + _ => unimplemented!(), + } + } + + Ok(()) + } + + + + + fn write( + & self, w: & mut W, prms: & TPrmMap + ) -> Res<()> { + let mut stack = vec![ ("", self, "") ] ; + + while let Some((sep, typ, close)) = stack.pop() { + write!(w, "{}", sep) ? ; + match typ { + PartialTyp::Array(src, tgt) => { + stack.push( (" ", & ** tgt, ")") ) ; + stack.push( (" ", & ** src, "") ) + }, + PartialTyp::DTyp(name, _, prms) => { + write!(w, "({}", name) ? ; + let mut first = true ; + for sub in prms.iter().rev() { + let close = if first { + first = false ; + ")" + } else { + "" + } ; + stack.push( (" ", sub, close) ) + } + }, + PartialTyp::Typ(typ) => write!(w, "{}", typ) ?, + PartialTyp::Param(idx) => write!(w, "{}", prms[* idx]) ?, + } + write!(w, "{}", close) ? + } + + Ok(()) + } + + + + /// Checks a partial type is legal. + pub fn check(& self) -> Result<(), (::parse::Pos, String)> { + let mut stack = vec![self] ; + + while let Some(ptyp) = stack.pop() { + if let PartialTyp::DTyp(name, pos, args) = ptyp { + if get(name).is_err() { + return Err(( + * pos, format!("unknown sort `{}`", name) + )) + } + for arg in args { + stack.push(arg) + } + } + } + + Ok(()) + } +} + +impl_fmt! { + PartialTyp(self, fmt) { + let mut stack = vec![ (self, "".to_string()) ] ; + + while let Some((typ, post)) = stack.pop() { + + match typ { + PartialTyp::Array(src, tgt) => { + write!(fmt, "(Array ") ? ; + stack.push( + ( tgt, format!("){}", post) ) + ) ; + stack.push( + ( src, " ".into() ) + ) + }, + + PartialTyp::Typ(typ) => write!(fmt, "{}{}", typ, post) ?, + + PartialTyp::Param(idx) => write!(fmt, "'{}", idx) ?, + + PartialTyp::DTyp(dtyp, _, typs) => { + write!(fmt, "({} ", dtyp) ? ; + let mut first = true ; + for typ in typs.iter().rev() { + let post = if first { + first = false ; + ")" + } else { + " " + } ; + stack.push( (typ, post.into()) ) + } + }, + } + + } + + Ok(()) + } +} + + +/// A datatype. +pub type DTyp = Arc ; + +/// Constructor arguments. +pub type CArgs = Vec<(String, PartialTyp)> ; + + + + +/// Type of the datatype factory. +type Factory = RwLock< BTreeMap > ; +lazy_static! { + /// Datatype factory. + static ref factory: Factory = RwLock::new( + BTreeMap::new() + ) ; + /// Map from constructors to datatypes. + static ref constructor_map: Factory = RwLock::new( + BTreeMap::new() + ) ; +} + + +/// Creates a datatype. +/// +/// Will fail if either +/// +/// - the datatype already exists +/// - one of the constructors of the datatype already exists +/// - can't access the datatype map +pub fn mk(dtyp: RDTyp) -> Res { + let name = dtyp.name.clone() ; + let to_insert = Arc::new(dtyp) ; + let dtyp = to_insert.clone() ; + + // Update constructor map. + if let Ok(mut map) = constructor_map.write() { + for constructor in dtyp.news.keys() { + let prev = map.insert( constructor.clone(), dtyp.clone() ) ; + if let Some(prev) = prev { + bail!( + "constructor `{}` is already used by datatype `{}`", + constructor, prev.name + ) + } + } + } + + + let prev = if let Ok(mut f) = factory.write() { + f.insert(name, to_insert) + } else { + bail!("failed to access datatype factory") + } ; + + if let Some(prev) = prev { + bail!("attempting to redeclare datatype `{}`", prev.name) + } + + Ok(dtyp) +} + + +/// Retrieves a datatype from its name. +/// +/// Will fail if +/// +/// - the datatype does not exist, or +/// - can't access the datatype map. +pub fn get(dtyp: & str) -> Res { + let maybe_res = if let Ok(f) = factory.read() { + f.get(dtyp).cloned() + } else { + bail!("failed to access datatype factory") + } ; + + if let Some(res) = maybe_res { + Ok(res) + } else { + bail!("unknown datatype `{}`", dtyp) + } +} + + +/// All the datatypes. +pub fn get_all() -> impl ::std::ops::Deref< Target = BTreeMap > { + factory.read().expect( + "failed to access datatype factory" + ) +} + + +/// Writes all the datatypes. +pub fn write_all(w: & mut W, pref: & str) -> Res<()> { + let decs = get_all() ; + let mut known = HashSet::new() ; + let dtyp_pref = & format!("{} ", pref) ; + + for (name, dtyp) in decs.iter() { + let is_new = known.insert(name) ; + if ! is_new { + continue + } + + write!(w, "{}({} (", pref, keywords::cmd::dec_dtyp) ? ; + for name in & dtyp.prms { + write!(w, " {}", name) ? + } + writeln!(w, " ) (") ? ; + + dtyp.write(w, dtyp_pref) ? ; + for dtyp in & dtyp.deps { + let is_new = known.insert(dtyp) ; + assert!(is_new) ; + let dtyp = get(dtyp).expect("inconsistent datatype state") ; + dtyp.write(w, dtyp_pref) ? + } + + writeln!(w, "{}) )", pref) ? + } + + Ok(()) +} + + + + +/// Types a constructor application. +pub fn type_constructor( + constructor: & str, args: & [ Term ] +) -> Res< Option > { + let dtyp = if let Ok(f) = factory.read() { + if let Some(dtyp) = f.get(constructor) { + dtyp.clone() + } else { + return Ok(None) + } + } else { + bail!("failed to access datatype factory") + } ; + + let params = { + let dtyp_constructor = dtyp.news.get(constructor).expect( + "inconsistent datatype constructor map" + ) ; + + if args.len() != dtyp_constructor.len() { + bail!( + "constructor `{}` of type `{}` expects {} arguments, got {}", + constructor, dtyp.name, dtyp_constructor.len(), args.len() + ) + } + + let mut params = TPrmMap::with_capacity( dtyp.prms.len() ) ; + for _ in 0 .. dtyp.prms.len() { + params.push( typ::unk() ) + } + + for ( arg, (_, typ) ) in args.iter().zip( + dtyp_constructor.iter() + ) { + typ.unify( & arg.typ(), & mut params ) ? + } + + params + } ; + + Ok( Some( typ::dtyp(dtyp.name.clone(), params) ) ) +} + + + + + + +/// The actual datatype type. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RDTyp { + /// Name of the datatype. + pub name: String, + /// Other datatypes attached to this one. + pub deps: Vec, + /// Type parameters. + pub prms: TPrmMap, + /// Constructors. + pub news: BTreeMap, +} +impl RDTyp { + /// Constructor. + pub fn new>(name: S, prms: TPrmMap) -> Self { + let name = name.into() ; + RDTyp { + name, deps: vec![], prms, news: BTreeMap::new(), + } + } + + /// Writes a single datatype. + pub fn write(& self, w: & mut W, pref: & str) -> Res<()> { + writeln!(w, "{}({}", pref, self.name) ? ; + for (name, args) in & self.news { + if args.is_empty() { + write!(w, "{} {}", pref, name) ? + } else { + write!(w, "{} ({}", pref, name) ? ; + for (name, typ) in args { + write!(w, " ({} ", name) ? ; + typ.write(w, & self.prms) ? ; + write!(w, ")") ? + } + write!(w, ")") ? + } + writeln!(w) ? + } + writeln!(w, "{})", pref) ? ; + Ok(()) + } + + /// Adds a datatype dependency. + pub fn add_dep(& mut self, dep: S) + where S: Into { + self.deps.push( dep.into() ) + } + + /// Checks a datatype is legal. + pub fn check(& self) -> Result<(), (::parse::Pos, String)> { + for (constructor, cargs) in & self.news { + for (selector, ptyp) in cargs { + if let Err((pos, err)) = ptyp.check() { + return Err(( + pos, format!( + "{} on selector `{}` of constructor `{}`", + err, selector, constructor + ) + )) + } + } + } + Ok(()) + } + + /// Adds a constructor. + pub fn add_constructor(& mut self, name: S, args: CArgs) -> Res<()> + where S: Into { + let name = name.into() ; + for (other_name, other_args) in & self.news { + if name == * other_name { + bail!("attempting to redeclare constructor `{}`", name) + } + for (arg, _) in & args { + for (other_arg, _) in other_args { + if arg == other_arg { + bail!("attempting to redeclare selector `{}`", arg) + } + } + } + } + + let _prev = self.news.insert(name, args) ; + debug_assert_eq! { _prev, None } + + Ok(()) + } +} diff --git a/src/hoice.rs b/src/hoice.rs index 701d9ec9..49927dcd 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -34,9 +34,11 @@ pub mod val ; pub mod term ; pub mod fun ; pub mod var_to ; +pub mod dtyp ; pub mod data ; pub mod instance ; +pub mod parse ; pub mod teacher ; pub mod learning ; pub mod check ; @@ -94,7 +96,7 @@ pub fn work() -> Res<()> { pub fn read_and_work( reader: R, file_input: bool, stop_on_check: bool, stop_on_err: bool ) -> Res< (Option, Instance) > { - use instance::parse::ItemRead ; + use parse::ItemRead ; let profiler = Profiler::new() ; @@ -102,7 +104,7 @@ pub fn read_and_work( // String buffer. let buf = & mut String::with_capacity(2000) ; // Parser context. - let mut parser_cxt = ::instance::parse::ParserCxt::new() ; + let mut parser_cxt = ::parse::ParserCxt::new() ; // Line offset of the parser. let mut line_off = 0 ; // Instance. @@ -118,7 +120,7 @@ pub fn read_and_work( let mut unsat = None ; 'parse_work: loop { - use instance::parse::Parsed ; + use parse::Parsed ; profile!{ |profiler| tick "parsing" } diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index dcf43c3f..74d32fa1 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -85,7 +85,7 @@ pub struct Instance { split: Option, /// Define-funs parsed. - define_funs: HashMap, + define_funs: HashMap, /// Maps **original** clause indexes to their optional name. old_names: ClsHMap, @@ -268,14 +268,14 @@ impl Instance { /// Adds a define fun. pub fn add_define_fun>( - & mut self, name: S, sig: VarInfos, body: ::instance::parse::PTTerms - ) -> Option<(VarInfos, ::instance::parse::PTTerms)> { + & mut self, name: S, sig: VarInfos, body: ::parse::PTTerms + ) -> Option<(VarInfos, ::parse::PTTerms)> { self.define_funs.insert(name.into(), (sig, body)) } /// Retrieves a define fun. pub fn get_define_fun( & self, name: & str - ) -> Option<& (VarInfos, ::instance::parse::PTTerms)> { + ) -> Option<& (VarInfos, ::parse::PTTerms)> { self.define_funs.get(name) } diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 14361504..271980a0 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -1,12 +1,11 @@ //! The instance stores the predicates, the clauses, and a lot of information. use common::* ; -use self::info::* ; pub mod info ; #[cfg_attr(feature = "cargo-clippy", allow(module_inception))] mod instance ; -pub mod parse ; + pub mod preproc ; pub use self::instance::{ Clause, Instance, PreInstance } ; \ No newline at end of file diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 9e722909..40df924a 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -631,6 +631,12 @@ impl NuQuals { pred_info.idx ) ? ; }, + + typ::RTyp::DTyp { .. } => (), + + typ::RTyp::Unk => bail!( + "unexpected unknown type" + ), } } } diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index bbcf4859..6a23a59e 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -62,8 +62,10 @@ impl SynthSys { match ** typ { typ::RTyp::Int => int = true, typ::RTyp::Real => real = true, - typ::RTyp::Bool => (), - typ::RTyp::Array { .. } => (), + typ::RTyp::Bool | + typ::RTyp::Array { .. } | + typ::RTyp::DTyp { .. } | + typ::RTyp::Unk => (), } } diff --git a/src/instance/parse.rs b/src/parse/mod.rs similarity index 83% rename from src/instance/parse.rs rename to src/parse/mod.rs index 4863b644..f3d1aa11 100644 --- a/src/instance/parse.rs +++ b/src/parse/mod.rs @@ -1,8 +1,14 @@ //! SMT-LIB 2 horn clause problem parser. use common::* ; -use instance::* ; +use instance::{ + *, info::VarInfo +} ; +mod ptterms ; +pub use self::ptterms::* ; + +use consts::keywords ; @@ -158,7 +164,11 @@ impl ItemRead for T { /// String cursor. pub type Cursor = usize ; /// Position in the text. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive( + Clone, Copy, Debug, + PartialEq, Eq, Hash, + PartialOrd, Ord +)] pub struct Pos(usize) ; impl ::std::ops::Deref for Pos { type Target = usize ; @@ -708,6 +718,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } + + + /// Tries to parse a sort. pub fn sort_opt(& mut self) -> Res> { // Compound type under construction. @@ -819,9 +832,341 @@ impl<'cxt, 's> Parser<'cxt, 's> { } + + + /// Parses a sort. + pub fn nu_sort( + & mut self, type_params: & BTreeMap<& 's str, dtyp::TPrmIdx> + ) -> Res { + if let Some(res) = self.nu_sort_opt(type_params) ? { + Ok(res) + } else { + bail!( + self.error_here( format!("expected sort") ) + ) + } + } + + + + /// Tries to parse a sort. + pub fn nu_sort_opt( + & mut self, type_params: & BTreeMap<& 's str, dtyp::TPrmIdx> + ) -> Res> { + use dtyp::PartialTyp ; + + // Compound type under construction. + // + // The position is always that of the opening paren of the type. + enum CTyp<'a> { + // Array under construction, meaning we're parsing the index sort. + Array { pos: Pos }, + // Array with a source, meaning we're parsing the target sort. + ArraySrc { pos: Pos, src: PartialTyp }, + // Datatype storing the name, the position of the name, and the types. + DTyp { name: & 'a str, pos: Pos, typs: dtyp::TPrmMap< PartialTyp > }, + } + + let mut stack = vec![] ; + + let start_pos = self.pos() ; + + 'go_down: loop { + self.ws_cmt() ; + let current_pos = self.pos() ; + + let mut typ = if self.tag_opt("(") { + self.ws_cmt() ; + // Parsing a compound type. + + if self.tag_opt("Array") { + if ! self.legal_id_char() { + // We're parsing an array type. + stack.push( CTyp::Array { pos: current_pos } ) ; + continue 'go_down + } else { + None + } + } else if let Some((pos, name)) = self.ident_opt() ? { + stack.push( + CTyp::DTyp { name, pos, typs: dtyp::TPrmMap::new() } + ) ; + continue 'go_down + } else { + None + } + + } else if self.tag_opt("Int") { + if ! self.legal_id_char() { + Some( typ::int().into() ) + } else { + None + } + + } else if self.tag_opt("Real") { + if ! self.legal_id_char() { + Some( typ::real().into() ) + } else { + None + } + + } else if self.tag_opt("Bool") { + if ! self.legal_id_char() { + Some( typ::bool().into() ) + } else { + None + } + + } else { + None + } ; + + if typ.is_none() { + if let Some((pos, name)) = self.ident_opt() ? { + // Type parameter? + typ = if let Some(idx) = type_params.get(name) { + Some( PartialTyp::Param(* idx) ) + } else { + Some( + PartialTyp::DTyp( name.into(), pos, vec![].into() ) + ) + } + + } + } + + 'go_up: loop { + + match stack.pop() { + + Some(CTyp::Array { pos }) => if let Some(src) = typ { + stack.push( CTyp::ArraySrc { pos, src } ) ; + // Need to parse the domain now. + continue 'go_down + } else { + Err::<_, Error>( + self.error(pos, "while parsing this array sort").into() + ).chain_err( + || self.error(current_pos, "expected index sort") + ) ? + }, + + Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { + typ = Some( + PartialTyp::Array( Box::new(src), Box::new(tgt) ) + ) ; + + // Parse closing paren. + self.ws_cmt() ; + if ! self.tag_opt(")") { + Err::<_, Error>( + self.error(pos, "while parsing this array sort").into() + ).chain_err( + || self.error( + current_pos, "expected expected closing paren" + ) + ) ? + } + + continue 'go_up + } else { + Err::<_, Error>( + self.error(pos, "while parsing this array sort").into() + ).chain_err( + || self.error(current_pos, "expected domain sort") + ) ? + }, + + Some(CTyp::DTyp { name, pos, mut typs }) => if let Some(t) = typ { + typs.push(t) ; + self.ws_cmt() ; + if self.tag_opt(")") { + typ = Some( PartialTyp::DTyp(name.into(), pos, typs) ) ; + continue 'go_up + } else { + stack.push( CTyp::DTyp { name, pos, typs } ) ; + continue 'go_down + } + } else { + Err::<_, Error>( + self.error(pos, "while parsing this datatype sort").into() + ).chain_err( + || self.error(current_pos, "expected sort") + ) ? + }, + + None => if typ.is_none() { + self.backtrack_to(start_pos) ; + return Ok(None) + } else { + return Ok(typ) + } + } + + } + } + + } + + + + + + + /// Datatype declaration. + fn dtyp_dec(& mut self) -> Res { + if ! self.tag_opt(keywords::cmd::dec_dtyp) { + return Ok(false) + } + + let mut dtyps: Vec<(Pos, dtyp::RDTyp)> = vec![] ; + + let mut params = dtyp::TPrmMap::new() ; + + self.ws_cmt() ; + self.tag("(") ? ; + self.ws_cmt() ; + + let mut params_map = BTreeMap::new() ; + + // Type parameters. + while let Some((pos, ident)) = self.ident_opt() ? { + let idx = params.next_index() ; + if let Some(prev) = params_map.insert(ident, idx) { + bail!( + self.error( + pos, format!( + "type parameters #{} and #{} have the same name `{}`", + idx, prev, ident + ) + ) + ) + } + params.push( ident.to_string() ) ; + self.ws_cmt() + } + + self.tag(")") ? ; + self.ws_cmt() ; + + self.tag("(") ? ; + self.ws_cmt() ; + + // Datatype declarations. + while self.tag_opt("(") { + self.ws_cmt() ; + let (dtyp_pos, dtyp_ident) = self.ident() ? ; + self.ws_cmt() ; + + let mut dtyp = dtyp::RDTyp::new( dtyp_ident, params.clone() ) ; + + // Constructors. + 'constructors: loop { + + let ( + constructor_pos, constructor_ident, selectors + ) = if self.tag_opt("(") { + self.ws_cmt() ; + let (constructor_pos, constructor_ident) = self.ident() ? ; + self.ws_cmt() ; + + let mut selectors = dtyp::CArgs::new() ; + + // Selectors. + while self.tag_opt("(") { + self.ws_cmt() ; + let (selector_pos, selector_ident) = self.ident() ? ; + self.ws_cmt() ; + + if selectors.iter().any( + |(id, _)| id == selector_ident + ) { + let error: Error = self.error( + selector_pos, + format!("found the selector `{}` twice", selector_ident) + ).into() ; + bail!( + error.chain_err( + || self.error( + dtyp_pos, "in this datatype declaration" + ) + ) + ) + } + + let ptyp = self.nu_sort(& params_map) ? ; + selectors.push( + ( selector_ident.to_string(), ptyp ) + ) ; + + self.tag(")") ? ; + self.ws_cmt() + } + + self.tag(")") ? ; + self.ws_cmt() ; + + (constructor_pos, constructor_ident, selectors) + + } else if let Some( + (constructor_pos, constructor_ident) + ) = self.ident_opt() ? { + self.ws_cmt() ; + (constructor_pos, constructor_ident, dtyp::CArgs::new()) + + } else { + break 'constructors + } ; + + + dtyp.add_constructor(constructor_ident, selectors).chain_err( + || self.error(constructor_pos, "in this constructor") + ) ? + + } + + for (_, dt) in & mut dtyps { + dt.add_dep( dtyp.name.clone() ) ; + dtyp.add_dep( dt.name.clone() ) + } + + dtyps.push( (dtyp_pos, dtyp) ) ; + + self.tag_opt(")") ; + self.ws_cmt() + } + + self.tag(")") ? ; + + let mut final_dtyps = Vec::with_capacity( dtyps.len() ) ; + + for (dtyp_pos, dtyp) in dtyps { + final_dtyps.push(( + dtyp_pos, dtyp::mk(dtyp).chain_err( + || self.error(dtyp_pos, "while parsing this datatype constructor") + ) ? + )) + } + + for (dtyp_pos, dtyp) in final_dtyps { + if let Err((pos, err)) = dtyp.check() { + let err: Error = self.error(pos, err).into() ; + bail!( + err.chain_err( + || self.error(dtyp_pos, "in this datatype declaration") + ) + ) + } + } + + Ok(true) + } + + + /// Predicate declaration. fn pred_dec(& mut self, instance: & mut Instance) -> Res { - if ! self.tag_opt("declare-fun") { + if ! self.tag_opt(keywords::cmd::dec_fun) { return Ok(false) } @@ -1497,7 +1842,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn define_fun( & mut self, instance: & mut Instance ) -> Res { - if ! self.tag_opt(::consts::keywords::cmd::def_fun) { + if ! self.tag_opt(keywords::cmd::def_fun) { return Ok(false) } conf.check_timeout() ? ; @@ -2373,7 +2718,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else if self.set_logic() ? || self.pred_dec(instance) ? || self.define_fun(instance) ? - || self.assert(instance) ? { + || self.assert(instance) ? + || self.dtyp_dec() ? { Parsed::Items } else if self.check_sat() { Parsed::CheckSat @@ -2417,484 +2763,3 @@ impl<'cxt, 's> Parser<'cxt, 's> { - - -/// Boolean combination of top terms used by the parser. -#[derive(Clone)] -pub enum PTTerms { - And( Vec ), - Or( Vec ), - NTTerm( TTerm ), - TTerm( TTerm ), -} -impl PTTerms { - /// Type of the top terms. - pub fn typ(& self) -> Typ { - match * self { - PTTerms::And(_) | - PTTerms::Or(_) | - PTTerms::NTTerm(_) => typ::bool(), - PTTerms::TTerm(ref tterm) => tterm.typ(), - } - } - - /// True if the top term is true. - pub fn is_true(& self) -> bool { - if let PTTerms::TTerm(ref tterm) = * self { - tterm.is_true() - } else { - false - } - } - /// True if the top term is false. - pub fn is_false(& self) -> bool { - if let PTTerms::TTerm(ref tterm) = * self { - tterm.is_false() - } else { - false - } - } - - /// The top term true. - pub fn tru() -> Self { - PTTerms::TTerm( TTerm::tru() ) - } - /// The top term false. - pub fn fls() -> Self { - PTTerms::TTerm( TTerm::fls() ) - } - - /// Total substitution over top terms. - /// - /// # TODO - /// - /// - eliminate recursion - pub fn subst_total( - & self, subst: & VarMap - ) -> Res { - let res = match self { - PTTerms::Or(ref kids) => { - let mut nu_kids = Vec::with_capacity( kids.len() ) ; - for kid in kids { - nu_kids.push( kid.subst_total(subst) ? ) - } - PTTerms::or(nu_kids) - }, - PTTerms::And(ref kids) => { - let mut nu_kids = Vec::with_capacity( kids.len() ) ; - for kid in kids { - nu_kids.push( kid.subst_total(subst) ? ) - } - PTTerms::and(nu_kids) - }, - - PTTerms::NTTerm(ref tterm) => { - if let Some(term) = tterm.term() { - if let Some(var) = term.var_idx() { - return Ok( PTTerms::not(subst[var].clone()) ? ) - } - } - PTTerms::NTTerm( tterm.subst_total(subst) ? ) - }, - - PTTerms::TTerm(ref tterm) => { - if let Some(term) = tterm.term() { - if let Some(var) = term.var_idx() { - return Ok( subst[var].clone() ) - } - } - PTTerms::TTerm( tterm.subst_total(subst) ? ) - }, - } ; - Ok(res) - } - - pub fn and(mut tterms: Vec) -> Self { - use std::iter::Extend ; - debug_assert!( ! tterms.is_empty() ) ; - let mut cnt = 0 ; - while cnt < tterms.len() { - if tterms[cnt].is_true() { - tterms.swap_remove(cnt) ; - } else if tterms[cnt].is_false() { - return PTTerms::fls() - } else if let PTTerms::And(_) = tterms[cnt] { - let and = tterms.swap_remove(cnt) ; - if let PTTerms::And(tts) = and { - tterms.extend(tts) - } else { - unreachable!() - } - } else { - cnt += 1 - } - } - - match tterms.len() { - 0 => PTTerms::tru(), - 1 => tterms.pop().unwrap(), - _ => PTTerms::And(tterms), - } - } - - pub fn or(mut tterms: Vec) -> Self { - use std::iter::Extend ; - debug_assert!( ! tterms.is_empty() ) ; - let mut cnt = 0 ; - - while cnt < tterms.len() { - if tterms[cnt].is_false() { - tterms.swap_remove(cnt) ; - } else if tterms[cnt].is_true() { - return PTTerms::tru() - } else if let PTTerms::Or(_) = tterms[cnt] { - let or = tterms.swap_remove(cnt) ; - if let PTTerms::Or(tts) = or { - tterms.extend(tts) - } else { - unreachable!() - } - } else { - cnt += 1 - } - } - - match tterms.len() { - 0 => PTTerms::fls(), - 1 => tterms.pop().unwrap(), - _ => PTTerms::Or(tterms), - } - } - - pub fn not(mut tterms: PTTerms) -> Res { - enum Frame { - And( Vec, Vec ), - Or( Vec, Vec ), - } - let mut stack: Vec = vec![] ; - - 'go_down: loop { - - tterms = match tterms { - PTTerms::And(mut args) => if let Some(first) = args.pop() { - tterms = first ; - args.reverse() ; - let done = Vec::with_capacity( args.len() + 1 ) ; - stack.push( Frame::Or(args, done) ) ; - continue 'go_down - } else { - bail!("nullary conjunction") - }, - - PTTerms::Or(mut args) => if let Some(first) = args.pop() { - tterms = first ; - args.reverse() ; - let done = Vec::with_capacity( args.len() + 1 ) ; - stack.push( Frame::And(args, done) ) ; - continue 'go_down - } else { - bail!("nullary disjunction") - }, - - PTTerms::NTTerm(tt) => PTTerms::TTerm(tt), - - PTTerms::TTerm(tt) => if tt.is_true() { - PTTerms::tterm( TTerm::fls() ) - } else if tt.is_false() { - PTTerms::tterm( TTerm::tru() ) - } else { - PTTerms::NTTerm(tt) - }, - - } ; - - 'go_up: loop { - match stack.pop() { - Some( Frame::And(mut to_do, mut done) ) => { - done.push(tterms) ; - if let Some(tt) = to_do.pop() { - stack.push( Frame::And(to_do, done) ) ; - tterms = tt ; - continue 'go_down - } else { - tterms = Self::and(done) ; - continue 'go_up - } - }, - Some( Frame::Or(mut to_do, mut done) ) => { - done.push(tterms) ; - if let Some(tt) = to_do.pop() { - stack.push( Frame::Or(to_do, done) ) ; - tterms = tt ; - continue 'go_down - } else { - tterms = Self::or(done) ; - continue 'go_up - } - }, - None => break 'go_down Ok(tterms) - } - } - - } - } - - pub fn tterm(tterm: TTerm) -> Self { - PTTerms::TTerm( tterm ) - } - - /// True if `PTTerms` does not contain a non-negated predicate. - pub fn is_legal_lhs(& self) -> bool { - let mut to_do = Vec::with_capacity(37) ; - to_do.push(self) ; - - while let Some(ptterm) = to_do.pop() { - match * ptterm { - PTTerms::And(ref args) => for arg in args { - to_do.push(arg) - }, - PTTerms::Or(ref args) => for arg in args { - to_do.push(arg) - }, - PTTerms::NTTerm(_) => (), - PTTerms::TTerm(ref term) => if term.pred().is_some() { - return false - }, - } - } - - true - } - - pub fn into_clauses(self) -> Res< Vec< (Vec, TTerm) > > { - match self { - PTTerms::TTerm(tterm) => Ok( - vec![ (vec![], tterm) ] - ), - PTTerms::NTTerm(tterm) => Ok( - vec![ (vec![ tterm ], TTerm::fls()) ] - ), - - PTTerms::Or(ptterms) => { - let mut lhs = Vec::with_capacity( ptterms.len() ) ; - let mut multipliers = Vec::with_capacity(3) ; - let mut rhs = None ; - - for ptt in ptterms { - match ptt { - PTTerms::TTerm(tt) => if let TTerm::T(term) = tt { - lhs.push( TTerm::T( term::not(term) ) ) - } else if rhs.is_none() { - rhs = Some( vec![ tt ] ) - } else { - bail!("ill-formed horn clause (or, 1)") - }, - - PTTerms::NTTerm(tt) => lhs.push(tt), - - PTTerms::And(ptts) => { - let mut tts = Vec::with_capacity( ptts.len() ) ; - let mut positive_preds = None ; - for ptt in ptts { - match ptt { - PTTerms::NTTerm(tterm) => tts.push(tterm), - PTTerms::TTerm(tterm) => match tterm { - TTerm::T(term) => tts.push( TTerm::T(term::not(term)) ), - tterm => { - let positive_preds = positive_preds.get_or_insert_with( - || Vec::with_capacity(7) - ) ; - positive_preds.push( tterm ) - }, - }, - ptt => if let Some(term) = ptt.to_term() ? { - tts.push( TTerm::T( term::not(term) ) ) - } else { - bail!("ill-formed horn clause (or, 2)") - }, - } - } - if let Some(pos_preds) = positive_preds { - tts.extend( pos_preds ) ; - rhs = Some( tts ) - } else { - multipliers.push(tts) - } - }, - - _ => bail!("ecountered normalization issue (or, 3)"), - } - } - - let nu_lhs = if multipliers.len() <= 2 { - - let mut nu_lhs = Vec::with_capacity( - multipliers.len() * 2 - ) ; - nu_lhs.push(lhs) ; - let mut tmp_lhs = Vec::with_capacity(nu_lhs.len()) ; - for mut vec in multipliers { - if let Some(last) = vec.pop() { - tmp_lhs.clear() ; - for tterm in vec { - for lhs in & nu_lhs { - let mut lhs = lhs.clone() ; - lhs.push( tterm.clone() ) ; - tmp_lhs.push( lhs ) - } - } - for mut lhs in nu_lhs.drain(0..) { - lhs.push(last.clone()) ; - tmp_lhs.push( lhs ) - } - ::std::mem::swap(& mut nu_lhs, & mut tmp_lhs) - } - } - - nu_lhs - - } else { - - for disj in multipliers { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - for tterm in disj { - if let TTerm::T(term) = tterm { - nu_disj.push(term) - } else { - bail!("error during clause conversion") - } - } - lhs.push( - TTerm::T( term::or(nu_disj) ) - ) - } - vec![ lhs ] - - } ; - - if let Some(rhs) = rhs { - let mut res = Vec::with_capacity( rhs.len() ) ; - let mut rhs = rhs.into_iter() ; - if let Some(last) = rhs.next() { - for rhs in rhs { - for lhs in & nu_lhs { - res.push( (lhs.clone(), rhs.clone()) ) - } - } - for lhs in nu_lhs { - res.push((lhs, last.clone())) - } - } - Ok(res) - } else { - Ok( - nu_lhs.into_iter().map( - |lhs| (lhs, TTerm::fls()) - ).collect() - ) - } - }, - - PTTerms::And(ppterms) => { - let mut clauses = Vec::with_capacity(ppterms.len()) ; - for ppt in ppterms { - clauses.extend( ppt.into_clauses() ? ) - // ^^^^^^^^^^^^^^^^ - // This is a recursive call, but there can be no other rec call - // inside it because - // - // - there can be no `PTTerms::And` inside a `PTTerms::And` by - // construction - // - this is the only place `into_clauses` calls itself. - } - Ok(clauses) - }, - - } - } - - /// Transforms a parser's combination of top terms into a term, if possible. - pub fn to_term(& self) -> Res> { - let mut stack = Vec::with_capacity(17) ; - - let mut ptterm = self ; - - 'go_down: loop { - - let mut term = match * ptterm { - PTTerms::And(ref args) => { - let mut args = args.iter() ; - if let Some(head) = args.next() { - stack.push((Op::And, args, vec![])) ; - ptterm = head ; - continue 'go_down - } else { - bail!("illegal nullary conjunction") - } - }, - PTTerms::Or(ref args) => { - let mut args = args.iter() ; - if let Some(head) = args.next() { - stack.push((Op::Or, args, vec![])) ; - ptterm = head ; - continue 'go_down - } else { - bail!("illegal nullary conjunction") - } - }, - PTTerms::TTerm(ref tterm) => if let Some(term) = tterm.term() { - term.clone() - } else { - return Ok(None) - }, - PTTerms::NTTerm(ref tterm) => if let Some(term) = tterm.term() { - term::not( term.clone() ) - } else { - return Ok(None) - }, - } ; - - 'go_up: loop { - if let Some((op, mut to_do, mut done)) = stack.pop() { - done.push(term) ; - if let Some(next) = to_do.next() { - stack.push( (op, to_do, done) ) ; - ptterm = next ; - continue 'go_down - } else { - term = term::app(op, done) ; - continue 'go_up - } - } else { - break 'go_down Ok( Some(term) ) - } - } - - } - } - - pub fn print(& self, pref: & str) { - match * self { - PTTerms::And(ref args) => { - println!("{}(and", pref) ; - for arg in args { - arg.print(& format!("{} ", pref)) - } - println!("{})", pref) - }, - PTTerms::Or(ref args) => { - println!("{}(or", pref) ; - for arg in args { - arg.print(& format!("{} ", pref)) - } - println!("{})", pref) - }, - PTTerms::NTTerm(ref arg) => println!( - "{}(not {})", pref, arg - ), - PTTerms::TTerm(ref tt) => { - println!("{}{}", pref, tt) - }, - } - } -} \ No newline at end of file diff --git a/src/parse/ptterms.rs b/src/parse/ptterms.rs new file mode 100644 index 00000000..513989bb --- /dev/null +++ b/src/parse/ptterms.rs @@ -0,0 +1,483 @@ +//! Terms built by the parser before constructing the actual terms. + +use common::* ; + +/// Boolean combination of top terms used by the parser. +#[derive(Clone)] +pub enum PTTerms { + And( Vec ), + Or( Vec ), + NTTerm( TTerm ), + TTerm( TTerm ), +} +impl PTTerms { + /// Type of the top terms. + pub fn typ(& self) -> Typ { + match * self { + PTTerms::And(_) | + PTTerms::Or(_) | + PTTerms::NTTerm(_) => typ::bool(), + PTTerms::TTerm(ref tterm) => tterm.typ(), + } + } + + /// True if the top term is true. + pub fn is_true(& self) -> bool { + if let PTTerms::TTerm(ref tterm) = * self { + tterm.is_true() + } else { + false + } + } + /// True if the top term is false. + pub fn is_false(& self) -> bool { + if let PTTerms::TTerm(ref tterm) = * self { + tterm.is_false() + } else { + false + } + } + + /// The top term true. + pub fn tru() -> Self { + PTTerms::TTerm( TTerm::tru() ) + } + /// The top term false. + pub fn fls() -> Self { + PTTerms::TTerm( TTerm::fls() ) + } + + /// Total substitution over top terms. + /// + /// # TODO + /// + /// - eliminate recursion + pub fn subst_total( + & self, subst: & VarMap + ) -> Res { + let res = match self { + PTTerms::Or(ref kids) => { + let mut nu_kids = Vec::with_capacity( kids.len() ) ; + for kid in kids { + nu_kids.push( kid.subst_total(subst) ? ) + } + PTTerms::or(nu_kids) + }, + PTTerms::And(ref kids) => { + let mut nu_kids = Vec::with_capacity( kids.len() ) ; + for kid in kids { + nu_kids.push( kid.subst_total(subst) ? ) + } + PTTerms::and(nu_kids) + }, + + PTTerms::NTTerm(ref tterm) => { + if let Some(term) = tterm.term() { + if let Some(var) = term.var_idx() { + return Ok( PTTerms::not(subst[var].clone()) ? ) + } + } + PTTerms::NTTerm( tterm.subst_total(subst) ? ) + }, + + PTTerms::TTerm(ref tterm) => { + if let Some(term) = tterm.term() { + if let Some(var) = term.var_idx() { + return Ok( subst[var].clone() ) + } + } + PTTerms::TTerm( tterm.subst_total(subst) ? ) + }, + } ; + Ok(res) + } + + pub fn and(mut tterms: Vec) -> Self { + use std::iter::Extend ; + debug_assert!( ! tterms.is_empty() ) ; + let mut cnt = 0 ; + while cnt < tterms.len() { + if tterms[cnt].is_true() { + tterms.swap_remove(cnt) ; + } else if tterms[cnt].is_false() { + return PTTerms::fls() + } else if let PTTerms::And(_) = tterms[cnt] { + let and = tterms.swap_remove(cnt) ; + if let PTTerms::And(tts) = and { + tterms.extend(tts) + } else { + unreachable!() + } + } else { + cnt += 1 + } + } + + match tterms.len() { + 0 => PTTerms::tru(), + 1 => tterms.pop().unwrap(), + _ => PTTerms::And(tterms), + } + } + + pub fn or(mut tterms: Vec) -> Self { + use std::iter::Extend ; + debug_assert!( ! tterms.is_empty() ) ; + let mut cnt = 0 ; + + while cnt < tterms.len() { + if tterms[cnt].is_false() { + tterms.swap_remove(cnt) ; + } else if tterms[cnt].is_true() { + return PTTerms::tru() + } else if let PTTerms::Or(_) = tterms[cnt] { + let or = tterms.swap_remove(cnt) ; + if let PTTerms::Or(tts) = or { + tterms.extend(tts) + } else { + unreachable!() + } + } else { + cnt += 1 + } + } + + match tterms.len() { + 0 => PTTerms::fls(), + 1 => tterms.pop().unwrap(), + _ => PTTerms::Or(tterms), + } + } + + pub fn not(mut tterms: PTTerms) -> Res { + enum Frame { + And( Vec, Vec ), + Or( Vec, Vec ), + } + let mut stack: Vec = vec![] ; + + 'go_down: loop { + + tterms = match tterms { + PTTerms::And(mut args) => if let Some(first) = args.pop() { + tterms = first ; + args.reverse() ; + let done = Vec::with_capacity( args.len() + 1 ) ; + stack.push( Frame::Or(args, done) ) ; + continue 'go_down + } else { + bail!("nullary conjunction") + }, + + PTTerms::Or(mut args) => if let Some(first) = args.pop() { + tterms = first ; + args.reverse() ; + let done = Vec::with_capacity( args.len() + 1 ) ; + stack.push( Frame::And(args, done) ) ; + continue 'go_down + } else { + bail!("nullary disjunction") + }, + + PTTerms::NTTerm(tt) => PTTerms::TTerm(tt), + + PTTerms::TTerm(tt) => if tt.is_true() { + PTTerms::tterm( TTerm::fls() ) + } else if tt.is_false() { + PTTerms::tterm( TTerm::tru() ) + } else { + PTTerms::NTTerm(tt) + }, + + } ; + + 'go_up: loop { + match stack.pop() { + Some( Frame::And(mut to_do, mut done) ) => { + done.push(tterms) ; + if let Some(tt) = to_do.pop() { + stack.push( Frame::And(to_do, done) ) ; + tterms = tt ; + continue 'go_down + } else { + tterms = Self::and(done) ; + continue 'go_up + } + }, + Some( Frame::Or(mut to_do, mut done) ) => { + done.push(tterms) ; + if let Some(tt) = to_do.pop() { + stack.push( Frame::Or(to_do, done) ) ; + tterms = tt ; + continue 'go_down + } else { + tterms = Self::or(done) ; + continue 'go_up + } + }, + None => break 'go_down Ok(tterms) + } + } + + } + } + + pub fn tterm(tterm: TTerm) -> Self { + PTTerms::TTerm( tterm ) + } + + /// True if `PTTerms` does not contain a non-negated predicate. + pub fn is_legal_lhs(& self) -> bool { + let mut to_do = Vec::with_capacity(37) ; + to_do.push(self) ; + + while let Some(ptterm) = to_do.pop() { + match * ptterm { + PTTerms::And(ref args) => for arg in args { + to_do.push(arg) + }, + PTTerms::Or(ref args) => for arg in args { + to_do.push(arg) + }, + PTTerms::NTTerm(_) => (), + PTTerms::TTerm(ref term) => if term.pred().is_some() { + return false + }, + } + } + + true + } + + pub fn into_clauses(self) -> Res< Vec< (Vec, TTerm) > > { + match self { + PTTerms::TTerm(tterm) => Ok( + vec![ (vec![], tterm) ] + ), + PTTerms::NTTerm(tterm) => Ok( + vec![ (vec![ tterm ], TTerm::fls()) ] + ), + + PTTerms::Or(ptterms) => { + let mut lhs = Vec::with_capacity( ptterms.len() ) ; + let mut multipliers = Vec::with_capacity(3) ; + let mut rhs = None ; + + for ptt in ptterms { + match ptt { + PTTerms::TTerm(tt) => if let TTerm::T(term) = tt { + lhs.push( TTerm::T( term::not(term) ) ) + } else if rhs.is_none() { + rhs = Some( vec![ tt ] ) + } else { + bail!("ill-formed horn clause (or, 1)") + }, + + PTTerms::NTTerm(tt) => lhs.push(tt), + + PTTerms::And(ptts) => { + let mut tts = Vec::with_capacity( ptts.len() ) ; + let mut positive_preds = None ; + for ptt in ptts { + match ptt { + PTTerms::NTTerm(tterm) => tts.push(tterm), + PTTerms::TTerm(tterm) => match tterm { + TTerm::T(term) => tts.push( TTerm::T(term::not(term)) ), + tterm => { + let positive_preds = positive_preds.get_or_insert_with( + || Vec::with_capacity(7) + ) ; + positive_preds.push( tterm ) + }, + }, + ptt => if let Some(term) = ptt.to_term() ? { + tts.push( TTerm::T( term::not(term) ) ) + } else { + bail!("ill-formed horn clause (or, 2)") + }, + } + } + if let Some(pos_preds) = positive_preds { + tts.extend( pos_preds ) ; + rhs = Some( tts ) + } else { + multipliers.push(tts) + } + }, + + _ => bail!("ecountered normalization issue (or, 3)"), + } + } + + let nu_lhs = if multipliers.len() <= 2 { + + let mut nu_lhs = Vec::with_capacity( + multipliers.len() * 2 + ) ; + nu_lhs.push(lhs) ; + let mut tmp_lhs = Vec::with_capacity(nu_lhs.len()) ; + for mut vec in multipliers { + if let Some(last) = vec.pop() { + tmp_lhs.clear() ; + for tterm in vec { + for lhs in & nu_lhs { + let mut lhs = lhs.clone() ; + lhs.push( tterm.clone() ) ; + tmp_lhs.push( lhs ) + } + } + for mut lhs in nu_lhs.drain(0..) { + lhs.push(last.clone()) ; + tmp_lhs.push( lhs ) + } + ::std::mem::swap(& mut nu_lhs, & mut tmp_lhs) + } + } + + nu_lhs + + } else { + + for disj in multipliers { + let mut nu_disj = Vec::with_capacity( disj.len() ) ; + for tterm in disj { + if let TTerm::T(term) = tterm { + nu_disj.push(term) + } else { + bail!("error during clause conversion") + } + } + lhs.push( + TTerm::T( term::or(nu_disj) ) + ) + } + vec![ lhs ] + + } ; + + if let Some(rhs) = rhs { + let mut res = Vec::with_capacity( rhs.len() ) ; + let mut rhs = rhs.into_iter() ; + if let Some(last) = rhs.next() { + for rhs in rhs { + for lhs in & nu_lhs { + res.push( (lhs.clone(), rhs.clone()) ) + } + } + for lhs in nu_lhs { + res.push((lhs, last.clone())) + } + } + Ok(res) + } else { + Ok( + nu_lhs.into_iter().map( + |lhs| (lhs, TTerm::fls()) + ).collect() + ) + } + }, + + PTTerms::And(ppterms) => { + let mut clauses = Vec::with_capacity(ppterms.len()) ; + for ppt in ppterms { + clauses.extend( ppt.into_clauses() ? ) + // ^^^^^^^^^^^^^^^^ + // This is a recursive call, but there can be no other rec call + // inside it because + // + // - there can be no `PTTerms::And` inside a `PTTerms::And` by + // construction + // - this is the only place `into_clauses` calls itself. + } + Ok(clauses) + }, + + } + } + + /// Transforms a parser's combination of top terms into a term, if possible. + pub fn to_term(& self) -> Res> { + let mut stack = Vec::with_capacity(17) ; + + let mut ptterm = self ; + + 'go_down: loop { + + let mut term = match * ptterm { + PTTerms::And(ref args) => { + let mut args = args.iter() ; + if let Some(head) = args.next() { + stack.push((Op::And, args, vec![])) ; + ptterm = head ; + continue 'go_down + } else { + bail!("illegal nullary conjunction") + } + }, + PTTerms::Or(ref args) => { + let mut args = args.iter() ; + if let Some(head) = args.next() { + stack.push((Op::Or, args, vec![])) ; + ptterm = head ; + continue 'go_down + } else { + bail!("illegal nullary conjunction") + } + }, + PTTerms::TTerm(ref tterm) => if let Some(term) = tterm.term() { + term.clone() + } else { + return Ok(None) + }, + PTTerms::NTTerm(ref tterm) => if let Some(term) = tterm.term() { + term::not( term.clone() ) + } else { + return Ok(None) + }, + } ; + + 'go_up: loop { + if let Some((op, mut to_do, mut done)) = stack.pop() { + done.push(term) ; + if let Some(next) = to_do.next() { + stack.push( (op, to_do, done) ) ; + ptterm = next ; + continue 'go_down + } else { + term = term::app(op, done) ; + continue 'go_up + } + } else { + break 'go_down Ok( Some(term) ) + } + } + + } + } + + pub fn print(& self, pref: & str) { + match * self { + PTTerms::And(ref args) => { + println!("{}(and", pref) ; + for arg in args { + arg.print(& format!("{} ", pref)) + } + println!("{})", pref) + }, + PTTerms::Or(ref args) => { + println!("{}(or", pref) ; + for arg in args { + arg.print(& format!("{} ", pref)) + } + println!("{})", pref) + }, + PTTerms::NTTerm(ref arg) => println!( + "{}(not {})", pref, arg + ), + PTTerms::TTerm(ref tt) => { + println!("{}{}", pref, tt) + }, + } + } +} \ No newline at end of file diff --git a/src/term/typ.rs b/src/term/typ.rs index 1f2396f4..9aeec1c4 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -31,6 +31,16 @@ pub fn bool() -> Typ { pub fn array(src: Typ, tgt: Typ) -> Typ { factory.mk(RTyp::Array { src, tgt }) } +/// Generates an unknown type. +pub fn unk() -> Typ { + factory.mk(RTyp::Unk) +} +/// Generates a datatype. +pub fn dtyp(dtyp: S, prms: dtyp::TPrmMap) -> Typ +where S: Into { + let dtyp = dtyp.into() ; + factory.mk( RTyp::DTyp { dtyp, prms } ) +} /// A hash-consed type. pub type Typ = HConsed ; @@ -55,6 +65,8 @@ pub type Typ = HConsed ; ) ] pub enum RTyp { + /// Unknown. + Unk, /// Integers. Int, /// Reals. @@ -65,9 +77,16 @@ pub enum RTyp { Array { /// Type of indices. src: Typ, - /// Type of values + /// Type of values. tgt: Typ, - } + }, + /// Datatype. + DTyp { + /// Original datatype. + dtyp: String, + /// Type parameters. + prms: dtyp::TPrmMap, + }, } impl RTyp { /// True if the type is bool. @@ -107,19 +126,198 @@ impl RTyp { } } + /// True if the type is unknown. + pub fn is_unk(& self) -> bool { + RTyp::Unk == * self + } + + /// True if the types are compatible. + /// + /// Two types are compatible if they're the same except for unknown subtypes. + /// + /// # Examples + /// + /// ```rust + /// use hoice::common::* ; + /// + /// let t_1 = typ::array( typ::int(), typ::unk() ) ; + /// let t_2 = typ::array( typ::unk(), typ::real() ) ; + /// debug_assert! { t_1.is_compatible(& t_2) } + /// ``` + pub fn is_compatible(& self, other: & Typ) -> bool { + if self == other.get() { return true } + + let mut stack = vec![ (self, other.get()) ] ; + while let Some((t_1, t_2)) = stack.pop() { + + match (t_1, t_2) { + (RTyp::Int, RTyp::Int) => (), + (RTyp::Real, RTyp::Real) => (), + (RTyp::Bool, RTyp::Bool) => (), + + (RTyp::Unk, _) | (_, RTyp::Unk) => (), + + ( + RTyp::Array { src: src_1, tgt: tgt_1 }, + RTyp::Array { src: src_2, tgt: tgt_2 }, + ) => { + stack.push( (src_1.get(), src_2.get()) ) ; + stack.push( (tgt_1.get(), tgt_2.get()) ) ; + }, + + ( + RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, + RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, + ) => if dtyp_1 == dtyp_2 { + for (t_1, t_2) in prms_1.iter().zip( prms_2.iter() ) { + stack.push( (t_1, t_2) ) + } + } else { + return false + }, + + (RTyp::Int, _) | + (RTyp::Real, _) | + (RTyp::Bool, _) | + (RTyp::Array { .. }, _) | + (RTyp::DTyp { .. }, _) => return false, + } + + } + + true + } + + /// Merges two types. + /// + /// Basically removes unknown types when possible. Returns `None` if the + /// types are not compatible. + pub fn merge(& self, typ: & Typ) -> Option { + use std::slice::Iter ; + use std::iter::Zip ; + + enum Frame<'a, 'b> { + ArrayLft(& 'a Typ, & 'a Typ), + ArrayRgt(Typ), + DTyp(String, dtyp::TPrmMap, Zip< Iter<'b, Typ>, Iter<'b, Typ> >), + } + let slf = factory.mk( self.clone() ) ; + + let (mut lft, mut rgt) = ( & slf, typ ) ; + let mut stack = vec![] ; + + 'go_down: loop { + + let mut typ = match ( lft.get(), rgt.get() ) { + (RTyp::Unk, _) => rgt.clone(), + (_, RTyp::Unk) => lft.clone(), + + ( + RTyp::Array { src: src_1, tgt: tgt_1 }, + RTyp::Array { src: src_2, tgt: tgt_2 }, + ) => { + lft = src_1 ; + rgt = src_2 ; + stack.push( Frame::ArrayLft(tgt_1, tgt_2) ) ; + + continue 'go_down + } + + ( + RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, + RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, + ) => if dtyp_1 != dtyp_2 { + return None + } else { + debug_assert_eq! { prms_1.len(), prms_2.len() } + + let mut prms = prms_1.iter().zip( prms_2.iter() ) ; + + if let Some((l, r)) = prms.next() { + lft = l ; + rgt = r ; + + stack.push( + Frame::DTyp( + dtyp_1.clone(), Vec::with_capacity( prms_1.len() ).into(), prms + ) + ) ; + + continue 'go_down + } else { + lft.clone() + } + }, + + (RTyp::Int, _) | + (RTyp::Real, _) | + (RTyp::Bool, _) | + (RTyp::Array { .. }, _) | + (RTyp::DTyp { .. }, _) => if lft == rgt { + lft.clone() + } else { + return None + }, + + } ; + + 'go_up: loop { + match stack.pop() { + + None => return Some(typ), + + Some( Frame::ArrayLft(l, r) ) => { + stack.push( Frame::ArrayRgt(typ) ) ; + lft = l ; + rgt = r ; + continue 'go_down + }, + + Some( Frame::ArrayRgt(src) ) => { + typ = array(src, typ) ; + continue 'go_up + } + + Some( + Frame::DTyp(dt, mut params, mut zip) + ) => { + params.push(typ) ; + if let Some((l, r)) = zip.next() { + stack.push( Frame::DTyp(dt, params, zip) ) ; + lft = l ; + rgt = r ; + continue 'go_down + } else { + typ = dtyp(dt, params) ; + continue 'go_up + } + }, + + } + } + + } + } + /// Default value of a type. + /// + /// Fails if the type is unknown. pub fn default_val(& self) -> Val { match * self { RTyp::Real => val::real( Rat::zero() ), RTyp::Int => val::int( Int::zero() ), RTyp::Bool => val::bool( true ), RTyp::Array { ref src, ref tgt } => val::array( - src.clone(), tgt.default_val(), + src.clone(), tgt.default_val() ), + RTyp::DTyp { .. } => unimplemented!(), + RTyp::Unk => panic!("unknown type has no default value"), } } /// Default term of a type. + /// + /// Fails if the type is unknown. pub fn default_term(& self) -> Term { match * self { RTyp::Real => term::real( Rat::zero() ), @@ -128,6 +326,8 @@ impl RTyp { RTyp::Array { ref src, ref tgt } => term::cst_array( src.clone(), tgt.default_term() ), + RTyp::DTyp { .. } => unimplemented!(), + RTyp::Unk => panic!("unknown type has no default term"), } } } @@ -151,6 +351,15 @@ impl_fmt!{ RTyp::Int => fmt.write_str("Int") ?, RTyp::Real => fmt.write_str("Real") ?, RTyp::Bool => fmt.write_str("Bool") ?, + RTyp::Unk => fmt.write_str("_") ?, + + RTyp::DTyp { ref dtyp, ref prms } => { + stack.push((typs, sep, end)) ; + write!(fmt, "({}", dtyp) ? ; + let typs: Vec<_> = prms.iter().map(|typ| typ.get()).collect() ; + stack.push( (typs.into_iter(), " ", ")") ) ; + continue 'stack + }, RTyp::Array { ref src, ref tgt } => { stack.push((typs, sep, end)) ; From 6ebbdabe88122a8f3957a138a64f3a60122d4c5e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 29 Jun 2018 13:09:34 +0900 Subject: [PATCH 02/94] partial adt term parsing --- rsc/inactive/adt/list-synasc-unsat.smt2 | 22 + rsc/inactive/adt/list-synasc.smt2 | 21 + rsc/inactive/adt/record-cex.smt2 | 37 ++ rsc/inactive/adt/simple-adt-horn.smt2 | 828 ++++++++++++++++++++++++ src/dtyp/mod.rs | 10 +- src/parse/mod.rs | 119 +++- src/term/typ.rs | 16 +- 7 files changed, 1036 insertions(+), 17 deletions(-) create mode 100644 rsc/inactive/adt/list-synasc-unsat.smt2 create mode 100644 rsc/inactive/adt/list-synasc.smt2 create mode 100644 rsc/inactive/adt/record-cex.smt2 create mode 100644 rsc/inactive/adt/simple-adt-horn.smt2 diff --git a/rsc/inactive/adt/list-synasc-unsat.smt2 b/rsc/inactive/adt/list-synasc-unsat.smt2 new file mode 100644 index 00000000..731d720d --- /dev/null +++ b/rsc/inactive/adt/list-synasc-unsat.smt2 @@ -0,0 +1,22 @@ +(set-logic HORN) + +(declare-datatype IList ( (Nil) (Cons (head Int) (tail IList)) )) + +(declare-fun Concat (IList IList IList) Bool) + +(assert (forall ((y IList)) + (Concat Nil y y))) + +(assert (forall ((x IList)(y IList)(r IList)(i Int)) + (=> (Concat x y r) (Concat (Cons i x) y (Cons i r)) ))) + +(assert (forall ((x IList)(y IList)(r IList)(i Int)) + (=> (Concat x y r) (Concat x y (Cons i r)) ))) + +(assert (forall ((x IList)(y IList)(r IList)) + (=> (and (not (= r Nil)) (Concat x y r)) (or (= (head r) (head x)) (= (head r) (head y)) )))) + +(assert (forall ((x IList)(y IList)(r IList)(nx Int)(ny Int)(nr Int)) + (=> (and (Concat x y r) (= nx (_size x)) (= ny (_size y)) (= nr (_size r))) (= (+ nr 1) (+ nx ny))))) + +(check-sat) diff --git a/rsc/inactive/adt/list-synasc.smt2 b/rsc/inactive/adt/list-synasc.smt2 new file mode 100644 index 00000000..3b578931 --- /dev/null +++ b/rsc/inactive/adt/list-synasc.smt2 @@ -0,0 +1,21 @@ +(set-logic HORN) + +(declare-datatypes () ( + (IList Nil (Cons (head Int) (tail IList)) ) +)) + +(declare-fun Concat (IList IList IList) Bool) + +(assert (forall ((y IList)) + (Concat Nil y y))) + +(assert (forall ((x IList)(y IList)(r IList)(i Int)) + (=> (Concat x y r) (Concat (Cons i x) y (Cons i r)) ))) + +(assert (forall ((x IList)(y IList)(r IList)) + (=> (and (not (= r Nil)) (Concat x y r)) (or (= (head r) (head x)) (= (head r) (head y)) )))) + +(assert (forall ((x IList)(y IList)(r IList)(nx Int)(ny Int)(nr Int)) + (=> (and (Concat x y r) (= nx (_size x)) (= ny (_size y)) (= nr (_size r))) (= (+ nr 1) (+ nx ny))))) + +(check-sat) diff --git a/rsc/inactive/adt/record-cex.smt2 b/rsc/inactive/adt/record-cex.smt2 new file mode 100644 index 00000000..09d943ab --- /dev/null +++ b/rsc/inactive/adt/record-cex.smt2 @@ -0,0 +1,37 @@ +(set-logic HORN) + +(declare-datatype ArRec0 ( (ArRec0 (default Int)) )) + +(define-fun storeRec0 ((new ArRec0) (old ArRec0) (ind Int) (val Int)) Bool + (and (= val (default old)) (= old new))) +(define-fun selectRec0 ((rec ArRec0) (ind Int)) Int + (default rec)) + +(declare-fun p0 ( Int ArRec0 ArRec0) Bool) +(declare-fun p1 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p2 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p3 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p4 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p5 ( Int ArRec0 ArRec0) Bool) +(declare-fun p6 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p7 ( Int ArRec0 ArRec0 Int) Bool) +(declare-fun p8 ( Int ArRec0 ArRec0) Bool) + +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0))(=> (= n 1) (p0 n a1 a2)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p0 n a1 a2) (= a 0)) (p1 n a1 a2 a)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p1 n a1 a2 a) (<= a (- n 1))) (p2 n a1 a2 a)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int) (a1_p ArRec0))(=> (and (p2 n a1 a2 a) (storeRec0 a1_p a1 a 1)) (p3 n a1_p a2 a)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int) (a2_p ArRec0))(=> (and (p3 n a1 a2 a) (storeRec0 a2_p a2 a 1)) (p4 n a1 a2_p a)))) + +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (p4 n a1 a2 a) (let ((ap (+ a 1))) (p1 n a1 a2 ap))))) + +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p1 n a1 a2 a) (>= a n)) (p5 n a1 a2)))) + +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p5 n a1 a2) (= a 0)) (p6 n a1 a2 a)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p6 n a1 a2 a) (<= a (- n 1))) (p7 n a1 a2 a)))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (p7 n a1 a2 a) (not (= (selectRec0 a1 a) (selectRec0 a2 a)))))) +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (and (p6 n a1 a2 a) (>= a n)) (p8 n a1 a2)))) + +(assert (forall ( (n Int) (a1 ArRec0) (a2 ArRec0) (a Int))(=> (p7 n a1 a2 a) (let ((ap (+ a 1))) (p6 n a1 a2 ap))))) + +(check-sat) diff --git a/rsc/inactive/adt/simple-adt-horn.smt2 b/rsc/inactive/adt/simple-adt-horn.smt2 new file mode 100644 index 00000000..d8b196f8 --- /dev/null +++ b/rsc/inactive/adt/simple-adt-horn.smt2 @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eldarica/simple-adt-horn.smt2 at 7c39533d29db9fd035870bdc2a511fe23fb7e1c1 · uuverifiers/eldarica · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Skip to content +
+ + + + + + + + + +
+ +
+ +
+
+ + + +
+
+
+ + + + + + + + + + +
+
+ + + Permalink + + + + + + +
+ +
+ + + +
+ +
+ + Find file + + + Copy path + +
+ +
+ + + +
+ + + 9c7f01d + + Jun 1, 2017 + + + +
+ + +
+ + +
+ + + +
+
+
+ +
+ Raw + Blame + History +
+ + + + +
+ +
+ 18 lines (12 sloc) + + 441 Bytes +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
(set-logic HORN)
+
(declare-datatype Pair ( (P (left Int) (right Bool)) ))
+
(declare-fun I1 (Pair) Bool)
(declare-fun I2 (Pair) Bool)
+
(assert (I1 (P 0 true)))
(assert (forall ((p Pair))
(=> (I1 p) (I2 (P (+ (left p) 1) (not (right p)))))))
(assert (forall ((p Pair))
(=> (I2 p) (I1 (P (* (left p) 2) (not (right p)))))))
+
(assert (forall ((p Pair))
(=> (I1 p) (and (>= (left p) 0) (right p)))))
+
(check-sat)
+ + + +
+ +
+ + + + + +
+ +
+ +
+
+ +
+ + + + + + +
+ + + You can’t perform that action at this time. +
+ + + + + + + + + +
+ + You signed in with another tab or window. Reload to refresh your session. + You signed out in another tab or window. Reload to refresh your session. +
+ + + + +
+ Press h to open a hovercard with more details. +
+ + + + + diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 13ce51ae..70c21e32 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -238,6 +238,14 @@ pub fn mk(dtyp: RDTyp) -> Res { } +/// Retrieves the datatype corresponding to a constructor. +pub fn of_constructor(constructor: & str) -> Option { + constructor_map.read().expect( + "failed to access constructor to datatype map" + ).get(constructor).cloned() +} + + /// Retrieves a datatype from its name. /// /// Will fail if @@ -342,7 +350,7 @@ pub fn type_constructor( params } ; - Ok( Some( typ::dtyp(dtyp.name.clone(), params) ) ) + Ok( Some( typ::dtyp(dtyp, params) ) ) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index f3d1aa11..8a3b7def 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -726,11 +726,13 @@ impl<'cxt, 's> Parser<'cxt, 's> { // Compound type under construction. // // The position is always that of the opening paren of the type. - enum CTyp { + enum CTyp<'a> { // Array under construction, meaning we're parsing the index sort. Array { pos: Pos }, // Array with a source, meaning we're parsing the target sort. ArraySrc { pos: Pos, src: Typ }, + // A datatype application. + DTyp { name: & 'a str, pos: Pos, typs: dtyp::TPrmMap } } let mut stack = vec![] ; @@ -753,6 +755,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else { None } + } else if let Some((pos, name)) = self.ident_opt() ? { + stack.push( + CTyp::DTyp { name, pos, typs: dtyp::TPrmMap::new() } + ) ; + continue 'go_down } else { None } @@ -779,6 +786,22 @@ impl<'cxt, 's> Parser<'cxt, 's> { None } ; + if typ.is_none() { + if let Some((pos, name)) = self.ident_opt() ? { + if let Ok(dtyp) = dtyp::get(name) { + typ = Some( + typ::dtyp( dtyp, vec![].into() ) + ) + } else { + bail!( + self.error( + pos, format!("unknown sort `{}`", conf.bad(name)) + ) + ) + } + } + } + 'go_up: loop { match stack.pop() { @@ -819,6 +842,33 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? }, + Some(CTyp::DTyp { name, pos, mut typs }) => if let Some(prm) = typ { + typs.push(prm) ; + + self.ws_cmt() ; + if self.tag_opt(")") { + if let Ok(dtyp) = dtyp::get(name) { + typ = Some( typ::dtyp(dtyp, typs) ) + } else { + bail!( + self.error( + pos, format!( + "unknown sort `{}`", conf.bad(name) + ) + ) + ) + } + continue 'go_up + } else { + stack.push( CTyp::DTyp { name, pos, typs } ) ; + continue 'go_down + } + } else { + Err::<_, Error>( + self.error(pos, "while parsing this sort").into() + ) ? + }, + None => if typ.is_none() { self.backtrack_to(start_pos) ; return Ok(None) @@ -841,9 +891,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { if let Some(res) = self.nu_sort_opt(type_params) ? { Ok(res) } else { - bail!( - self.error_here( format!("expected sort") ) - ) + bail!( self.error_here("expected sort") ) } } @@ -1099,6 +1147,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { ( selector_ident.to_string(), ptyp ) ) ; + self.ws_cmt() ; self.tag(")") ? ; self.ws_cmt() } @@ -1684,10 +1733,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { term::real(real) } else if let Some(b) = self.bool() { term::bool(b) - } else if let Some((pos, id)) = self.ident_opt()? { + } else if let Some((pos, id)) = self.ident_opt() ? { if let Some(idx) = map.get(id) { term::var(* idx, var_map[* idx].typ.clone()) + } else if let Some(ptterms) = self.get_bind(id) { if let Some(term) = ptterms.to_term().chain_err( || format!("while retrieving binding for {}", conf.emph(id)) @@ -1697,9 +1747,32 @@ impl<'cxt, 's> Parser<'cxt, 's> { // Not in a legal term. break 'read_kids None } + } else if self.cxt.pred_name_map.get(id).is_some() { // Identifier is a predicate, we're not in a legal term. break 'read_kids None + + } else if let Some(datatype) = dtyp::of_constructor(id) { + if let Some(constructor) = datatype.news.get(id) { + if constructor.is_empty() { + bail!( + self.error(pos, "term for datatypes isn't implemented") + ) + } else { + bail!( + self.error( + pos, format!( + "constructor `{}` of datatype `{}` takes {} value(s), \ + applied here to none", + conf.bad(id), conf.emph(& datatype.name), constructor.len() + ) + ) + ) + } + } else { + bail!("inconsistent datatype map internal state") + } + } else { bail!( self.error( @@ -1769,6 +1842,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; self.tag(")") ? ; term::cst_array(src.clone(), term) + } else { bail!( self.error_here("expected term") @@ -1779,6 +1853,30 @@ impl<'cxt, 's> Parser<'cxt, 's> { bail!( self.error_here("unexpected token") ) } + } else if let Some((pos, id)) = self.ident_opt().chain_err( + || "while trying to parse datatype" + ) ? { + let mut trm: Option = None ; + if let Some(datatype) = dtyp::of_constructor(id) { + if let Some(_constructor) = datatype.news.get(id) { + bail!( + self.error(pos, "term for datatypes isn't implemented") + ) + } + } + if let Some(trm) = trm { + trm + } else if self.cxt.term_stack.is_empty() { + self.backtrack_to(pos) ; + break 'read_kids None + } else { + bail!( + self.error( + pos, format!( "unknown identifier `{}`", conf.bad(id) ) + ) + ) + } + } else if self.cxt.term_stack.is_empty() { break 'read_kids None @@ -1787,7 +1885,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } else { - break 'read_kids None } ; @@ -2028,7 +2125,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok( Some( PTTerms::tterm( TTerm::T( term ) ) ) ) - } else if let Some((pos, id)) = self.ident_opt() ? { + } else if let Some((pos, id)) = self.ident_opt().chain_err( + || "while trying to parse a top term (1)" + ) ? { if let Some(idx) = self.cxt.pred_name_map.get(id) { let idx = * idx ; if instance[idx].sig.is_empty() { @@ -2048,8 +2147,10 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ) } + } else if let Some(ptterms) = self.get_bind(id) { Ok( Some( ptterms.clone() ) ) + } else { bail!( self.error( @@ -2070,7 +2171,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { "unable to work on clauses that are not ground".to_string() ) ) - } else if let Some((ident_pos, ident)) = self.ident_opt()? { + } else if let Some((ident_pos, ident)) = self.ident_opt().chain_err( + || "while trying to parse a top term (2)" + ) ? { if let Some(idx) = self.cxt.pred_name_map.get(ident).cloned() { let res = self.pred_args(idx, ident_pos, var_map, map, instance) ? ; diff --git a/src/term/typ.rs b/src/term/typ.rs index 9aeec1c4..37788142 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -36,9 +36,7 @@ pub fn unk() -> Typ { factory.mk(RTyp::Unk) } /// Generates a datatype. -pub fn dtyp(dtyp: S, prms: dtyp::TPrmMap) -> Typ -where S: Into { - let dtyp = dtyp.into() ; +pub fn dtyp(dtyp: dtyp::DTyp, prms: dtyp::TPrmMap) -> Typ { factory.mk( RTyp::DTyp { dtyp, prms } ) } @@ -83,7 +81,7 @@ pub enum RTyp { /// Datatype. DTyp { /// Original datatype. - dtyp: String, + dtyp: dtyp::DTyp, /// Type parameters. prms: dtyp::TPrmMap, }, @@ -168,7 +166,7 @@ impl RTyp { ( RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, - ) => if dtyp_1 == dtyp_2 { + ) => if dtyp_1.name == dtyp_2.name { for (t_1, t_2) in prms_1.iter().zip( prms_2.iter() ) { stack.push( (t_1, t_2) ) } @@ -199,7 +197,9 @@ impl RTyp { enum Frame<'a, 'b> { ArrayLft(& 'a Typ, & 'a Typ), ArrayRgt(Typ), - DTyp(String, dtyp::TPrmMap, Zip< Iter<'b, Typ>, Iter<'b, Typ> >), + DTyp( + dtyp::DTyp, dtyp::TPrmMap, Zip< Iter<'b, Typ>, Iter<'b, Typ> > + ), } let slf = factory.mk( self.clone() ) ; @@ -226,7 +226,7 @@ impl RTyp { ( RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, - ) => if dtyp_1 != dtyp_2 { + ) => if dtyp_1.name != dtyp_2.name { return None } else { debug_assert_eq! { prms_1.len(), prms_2.len() } @@ -355,7 +355,7 @@ impl_fmt!{ RTyp::DTyp { ref dtyp, ref prms } => { stack.push((typs, sep, end)) ; - write!(fmt, "({}", dtyp) ? ; + write!(fmt, "({}", dtyp.name) ? ; let typs: Vec<_> = prms.iter().map(|typ| typ.get()).collect() ; stack.push( (typs.into_iter(), " ", ")") ) ; continue 'stack From de2c2f6d9f2466d107b47b0cd57a04dcd0ccc1a5 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 29 Jun 2018 15:01:36 +0900 Subject: [PATCH 03/94] more work on the frontend, def-funs broken --- Cargo.lock | 32 +- rsc/inactive/adt/list-synasc-unsat.smt2 | 4 +- rsc/inactive/adt/record-cex.smt2 | 4 +- rsc/inactive/adt/simple-adt-horn.smt2 | 837 +----------------------- src/common/config.rs | 1 + src/common/smt.rs | 20 + src/dtyp/mod.rs | 33 +- src/instance/instance/mod.rs | 12 + src/instance/instance/pre_instance.rs | 2 +- src/learning/ice/mod.rs | 4 +- src/parse/mod.rs | 2 +- src/teacher/mod.rs | 2 +- 12 files changed, 102 insertions(+), 851 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 988c05dd..051d7736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,11 +18,11 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -49,19 +49,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.31.2" +version = "2.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -84,7 +84,7 @@ name = "error-chain" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -111,7 +111,7 @@ name = "hoice" version = "1.5.0" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashconsing 0.9.10 (git+https://github.com/AdrienChampion/hashconsing)", @@ -119,7 +119,7 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)", ] @@ -229,7 +229,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -260,7 +260,7 @@ dependencies = [ [[package]] name = "rsmt2" version = "0.9.10" -source = "git+https://github.com/kino-mc/rsmt2#2f19920f254abc691b198821942c7de48138ff27" +source = "git+https://github.com/kino-mc/rsmt2#b4229205230c357f7365fea1fc305c52189be29a" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -292,7 +292,7 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -330,12 +330,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" -"checksum backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbdd17cd962b570302f5297aea8648d5923e22e555c2ed2d8b2e34eca646bf6d" +"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" -"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" -"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" @@ -354,7 +354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum rand 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0d9f869af32e387d9e0f2bdb64326b8ac84c81d5e55459d0bc7526b0fdb3671" +"checksum rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6802c0e883716383777e147b7c21323d5de7527257c8b6dc1365a7f2983e90f6" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" @@ -363,7 +363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" diff --git a/rsc/inactive/adt/list-synasc-unsat.smt2 b/rsc/inactive/adt/list-synasc-unsat.smt2 index 731d720d..54eac2d0 100644 --- a/rsc/inactive/adt/list-synasc-unsat.smt2 +++ b/rsc/inactive/adt/list-synasc-unsat.smt2 @@ -1,6 +1,8 @@ (set-logic HORN) -(declare-datatype IList ( (Nil) (Cons (head Int) (tail IList)) )) +(declare-datatypes () ( + (IList Nil (Cons (head Int) (tail IList)) ) +)) (declare-fun Concat (IList IList IList) Bool) diff --git a/rsc/inactive/adt/record-cex.smt2 b/rsc/inactive/adt/record-cex.smt2 index 09d943ab..53b2ea92 100644 --- a/rsc/inactive/adt/record-cex.smt2 +++ b/rsc/inactive/adt/record-cex.smt2 @@ -1,6 +1,8 @@ (set-logic HORN) -(declare-datatype ArRec0 ( (ArRec0 (default Int)) )) +(declare-datatypes () ( + (ArRec0 (default Int) ) +) ) (define-fun storeRec0 ((new ArRec0) (old ArRec0) (ind Int) (val Int)) Bool (and (= val (default old)) (= old new))) diff --git a/rsc/inactive/adt/simple-adt-horn.smt2 b/rsc/inactive/adt/simple-adt-horn.smt2 index d8b196f8..0155f351 100644 --- a/rsc/inactive/adt/simple-adt-horn.smt2 +++ b/rsc/inactive/adt/simple-adt-horn.smt2 @@ -1,828 +1,19 @@ +(set-logic HORN) +(declare-datatypes () ( + (Pair (P (left Int) (right Bool)) ) +) ) +(declare-fun I1 (Pair) Bool) +(declare-fun I2 (Pair) Bool) +(assert (I1 (P 0 true))) +(assert (forall ((p Pair)) + (=> (I1 p) (I2 (P (+ (left p) 1) (not (right p))))))) +(assert (forall ((p Pair)) + (=> (I2 p) (I1 (P (* (left p) 2) (not (right p))))))) +(assert (forall ((p Pair)) + (=> (I1 p) (and (>= (left p) 0) (right p))))) - - - - - - - - - - - - - - - - - - - - - - - - - eldarica/simple-adt-horn.smt2 at 7c39533d29db9fd035870bdc2a511fe23fb7e1c1 · uuverifiers/eldarica · GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content -
- - - - - - - - - -
- -
- -
-
- - - -
-
-
- - - - - - - - - - -
-
- - - Permalink - - - - - - -
- -
- - - -
- -
- - Find file - - - Copy path - -
- -
- - - -
- - - 9c7f01d - - Jun 1, 2017 - - - -
- - -
- - -
- - - -
-
-
- -
- Raw - Blame - History -
- - - - -
- -
- 18 lines (12 sloc) - - 441 Bytes -
-
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(set-logic HORN)
-
(declare-datatype Pair ( (P (left Int) (right Bool)) ))
-
(declare-fun I1 (Pair) Bool)
(declare-fun I2 (Pair) Bool)
-
(assert (I1 (P 0 true)))
(assert (forall ((p Pair))
(=> (I1 p) (I2 (P (+ (left p) 1) (not (right p)))))))
(assert (forall ((p Pair))
(=> (I2 p) (I1 (P (* (left p) 2) (not (right p)))))))
-
(assert (forall ((p Pair))
(=> (I1 p) (and (>= (left p) 0) (right p)))))
-
(check-sat)
- - - -
- -
- - - - - -
- -
- -
-
- -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - -
- Press h to open a hovercard with more details. -
- - - - - +(check-sat) \ No newline at end of file diff --git a/src/common/config.rs b/src/common/config.rs index fe6381c6..671cbb5b 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -95,6 +95,7 @@ impl SmtConf { ) ? { solver.tee(log) ? } + ::common::smt::init(& mut solver) ? ; Ok(solver) } diff --git a/src/common/smt.rs b/src/common/smt.rs index b9b732e0..7792ee84 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -14,6 +14,26 @@ use common::{ use data::Constraint ; +/// Initial setup for a solver. +/// +/// Declares all the datatypes used in the instance. +pub fn init

(solver: & mut Solver

) -> Res<()> { + dtyp::write_all(solver, "") ? ; + Ok(()) +} + + +/// Resets a smt solver. +/// +/// Use this and not `solver.reset()`. This declares all the datatypes used in +/// the instance. +pub fn reset

(solver: & mut Solver

) -> Res<()> { + solver.reset() ? ; + init(solver) +} + + + /// SMT-prints a term using the default var writer. pub struct SmtTerm<'a> { /// The term. diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 70c21e32..8d704c98 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -77,7 +77,7 @@ impl PartialTyp { fn write( & self, w: & mut W, prms: & TPrmMap - ) -> Res<()> { + ) -> ::std::io::Result<()> { let mut stack = vec![ ("", self, "") ] ; while let Some((sep, typ, close)) = stack.pop() { @@ -88,7 +88,10 @@ impl PartialTyp { stack.push( (" ", & ** src, "") ) }, PartialTyp::DTyp(name, _, prms) => { - write!(w, "({}", name) ? ; + if ! prms.is_empty() { + write!(w, "(") ? + } + write!(w, "{}", name) ? ; let mut first = true ; for sub in prms.iter().rev() { let close = if first { @@ -154,7 +157,10 @@ impl_fmt! { PartialTyp::Param(idx) => write!(fmt, "'{}", idx) ?, PartialTyp::DTyp(dtyp, _, typs) => { - write!(fmt, "({} ", dtyp) ? ; + if ! typs.is_empty() { + write!(fmt, "(") ? + } + write!(fmt, "{} ", dtyp) ? ; let mut first = true ; for typ in typs.iter().rev() { let post = if first { @@ -275,8 +281,23 @@ pub fn get_all() -> impl ::std::ops::Deref< Target = BTreeMap > { } + +/// Writes the map from constructors to datatypes. +pub fn write_constructor_map( + w: & mut W, pref: & str +) -> ::std::io::Result<()> { + for (constructor, dtyp) in constructor_map.read().expect( + "unable to retrieve dtyp constructor map" + ).iter() { + writeln!(w, "{}{:>10} -> {:>10}", pref, constructor, dtyp.name) ? + } + Ok(()) +} + + + /// Writes all the datatypes. -pub fn write_all(w: & mut W, pref: & str) -> Res<()> { +pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { let decs = get_all() ; let mut known = HashSet::new() ; let dtyp_pref = & format!("{} ", pref) ; @@ -380,7 +401,9 @@ impl RDTyp { } /// Writes a single datatype. - pub fn write(& self, w: & mut W, pref: & str) -> Res<()> { + pub fn write( + & self, w: & mut W, pref: & str + ) -> ::std::io::Result<()> { writeln!(w, "{}({}", pref, self.name) ? ; for (name, args) in & self.news { if args.is_empty() { diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 74d32fa1..21f294ce 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -1104,6 +1104,13 @@ impl Instance { writeln!(w, "(set-logic HORN)") ? ; writeln!(w) ? ; + writeln!(w, "; Datatypes:") ? ; + + dtyp::write_all(w, "") ? ; + + dtyp::write_constructor_map(w, "; ") ? ; + writeln!(w) ? ; + for (pred_idx, pred) in self.preds.index_iter() { if self.pred_terms[pred_idx].is_none() { write!( @@ -1650,6 +1657,11 @@ impl<'a> PebcakFmt<'a> for Instance { fn pebcak_io_fmt( & self, w: & mut W, _: () ) -> IoRes<()> { + writeln!(w, "; Datatypes:") ? ; + + dtyp::write_all(w, "") ? ; + + dtyp::write_constructor_map(w, "; ") ? ; for (pred_idx, pred) in self.preds.index_iter() { if self.pred_terms[pred_idx].is_none() { diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index 4977b101..b61fbb37 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -1871,7 +1871,7 @@ impl<'a> PreInstance<'a> { } } - self.solver.reset() ? ; + smt::reset(& mut self.solver) ? ; Ok(true) } diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index c7bb0af9..661b4aba 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -192,7 +192,7 @@ impl<'core> IceLearner<'core> { Ok(data) => { self.count += 1 ; if self.count % 50 == 0 { - self.solver.reset() ? + smt::reset(& mut self.solver) ? } profile! { self "learn steps" => add 1 } if let Some(candidates) = profile!( @@ -247,7 +247,7 @@ impl<'core> IceLearner<'core> { } "sending" ) ? ; // // Reset and clear declaration memory. - // self.solver.reset().chain_err( + // smt::reset(& mut self.solver).chain_err( // || "during solver reset" // ) ? ; for set in self.dec_mem.iter_mut() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 8a3b7def..eb9ea9ac 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2228,7 +2228,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { bail!( self.error( ident_pos, - format!("unknown predicate `{}`", conf.bad(ident)) + format!("unknown identifier `{}`", conf.bad(ident)) ) ) } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index f5c466aa..106e1a40 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -797,7 +797,7 @@ impl<'a> Teacher<'a> { // } if self.count % 100 == 0 { - self.solver.reset() ? + smt::reset(& mut self.solver) ? } else { self.solver.pop(1) ? } From 5750529574e825d2d20025a2483a5e77442fac9f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 29 Jun 2018 17:55:26 +0900 Subject: [PATCH 04/94] selector parsing + fixes --- rsc/inactive/adt/list-synasc-unsat.smt2 | 20 ++++++++------------ rsc/inactive/adt/record-cex.smt2 | 8 +++++--- src/dtyp/mod.rs | 20 ++++++++++++++++++++ src/parse/mod.rs | 25 +++++++++++++++++++++---- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/rsc/inactive/adt/list-synasc-unsat.smt2 b/rsc/inactive/adt/list-synasc-unsat.smt2 index 54eac2d0..b3480c2a 100644 --- a/rsc/inactive/adt/list-synasc-unsat.smt2 +++ b/rsc/inactive/adt/list-synasc-unsat.smt2 @@ -1,24 +1,20 @@ (set-logic HORN) -(declare-datatypes () ( - (IList Nil (Cons (head Int) (tail IList)) ) -)) +(declare-fun Concat ((List Int) (List Int) (List Int)) Bool) -(declare-fun Concat (IList IList IList) Bool) +(assert (forall ((y (List Int))) + (Concat nil y y))) -(assert (forall ((y IList)) - (Concat Nil y y))) - -(assert (forall ((x IList)(y IList)(r IList)(i Int)) +(assert (forall ((x (List Int))(y (List Int))(r (List Int))(i Int)) (=> (Concat x y r) (Concat (Cons i x) y (Cons i r)) ))) -(assert (forall ((x IList)(y IList)(r IList)(i Int)) +(assert (forall ((x (List Int))(y (List Int))(r (List Int))(i Int)) (=> (Concat x y r) (Concat x y (Cons i r)) ))) -(assert (forall ((x IList)(y IList)(r IList)) - (=> (and (not (= r Nil)) (Concat x y r)) (or (= (head r) (head x)) (= (head r) (head y)) )))) +(assert (forall ((x (List Int))(y (List Int))(r (List Int))) + (=> (and (not (= r nil)) (Concat x y r)) (or (= (head r) (head x)) (= (head r) (head y)) )))) -(assert (forall ((x IList)(y IList)(r IList)(nx Int)(ny Int)(nr Int)) +(assert (forall ((x (List Int))(y (List Int))(r (List Int))(nx Int)(ny Int)(nr Int)) (=> (and (Concat x y r) (= nx (_size x)) (= ny (_size y)) (= nr (_size r))) (= (+ nr 1) (+ nx ny))))) (check-sat) diff --git a/rsc/inactive/adt/record-cex.smt2 b/rsc/inactive/adt/record-cex.smt2 index 53b2ea92..97b99e6f 100644 --- a/rsc/inactive/adt/record-cex.smt2 +++ b/rsc/inactive/adt/record-cex.smt2 @@ -1,13 +1,15 @@ (set-logic HORN) (declare-datatypes () ( - (ArRec0 (default Int) ) + (ArRec0 (new (dflt Int)) ) ) ) (define-fun storeRec0 ((new ArRec0) (old ArRec0) (ind Int) (val Int)) Bool - (and (= val (default old)) (= old new))) + (and (= val (dflt old)) (= old new)) +) (define-fun selectRec0 ((rec ArRec0) (ind Int)) Int - (default rec)) + (dflt rec) +) (declare-fun p0 ( Int ArRec0 ArRec0) Bool) (declare-fun p1 ( Int ArRec0 ArRec0 Int) Bool) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 8d704c98..c0a8ea2f 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -201,6 +201,10 @@ lazy_static! { static ref constructor_map: Factory = RwLock::new( BTreeMap::new() ) ; + /// Set of selectors. + static ref selector_set: RwLock> = RwLock::new( + BTreeSet::new() + ) ; } @@ -229,6 +233,15 @@ pub fn mk(dtyp: RDTyp) -> Res { } } + // Update selector set. + if let Ok(mut set) = selector_set.write() { + for selectors in dtyp.news.values() { + for (selector, _) in selectors { + set.insert( selector.clone() ) ; + } + } + } + let prev = if let Ok(mut f) = factory.write() { f.insert(name, to_insert) @@ -251,6 +264,13 @@ pub fn of_constructor(constructor: & str) -> Option { ).get(constructor).cloned() } +/// True if the identifier is known to be a selector. +pub fn is_selector(selector: & str) -> bool { + selector_set.read().expect( + "failed to access selector set" + ).contains(selector) +} + /// Retrieves a datatype from its name. /// diff --git a/src/parse/mod.rs b/src/parse/mod.rs index eb9ea9ac..af6ef773 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1772,7 +1772,12 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else { bail!("inconsistent datatype map internal state") } - + } else if dtyp::is_selector(id) { + bail!( + self.error( + pos, "term datatypes (selector) isn't implemented" + ) + ) } else { bail!( self.error( @@ -1860,9 +1865,17 @@ impl<'cxt, 's> Parser<'cxt, 's> { if let Some(datatype) = dtyp::of_constructor(id) { if let Some(_constructor) = datatype.news.get(id) { bail!( - self.error(pos, "term for datatypes isn't implemented") + self.error( + pos, "term datatypes (constructor) isn't implemented" + ) ) } + } else if dtyp::is_selector(id) { + bail!( + self.error( + pos, "term datatypes (selector) isn't implemented" + ) + ) } if let Some(trm) = trm { trm @@ -1872,7 +1885,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else { bail!( self.error( - pos, format!( "unknown identifier `{}`", conf.bad(id) ) + pos, format!( + "unknown identifier (term) `{}`", conf.bad(id) + ) ) ) } @@ -2228,7 +2243,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { bail!( self.error( ident_pos, - format!("unknown identifier `{}`", conf.bad(ident)) + format!( + "unknown identifier (tterm) `{}`", conf.bad(ident) + ) ) ) } From 471b810274ca267d2910d0bd906a4ec539573bcc Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 2 Jul 2018 18:08:20 +0900 Subject: [PATCH 05/94] moving things around + simplified term enum + started zippin' --- src/errors.rs | 34 + src/parse/mod.rs | 4 +- src/teacher/assistant.rs | 8 +- src/term/factory.rs | 27 +- src/term/mod.rs | 1685 +++----------------------------------- src/term/tterms.rs | 1473 +++++++++++++++++++++++++++++++++ src/term/typ.rs | 10 +- src/term/zip.rs | 41 + src/val/mod.rs | 36 + 9 files changed, 1737 insertions(+), 1581 deletions(-) create mode 100644 src/term/tterms.rs create mode 100644 src/term/zip.rs diff --git a/src/errors.rs b/src/errors.rs index 7f315dc0..c9807a7d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -15,6 +15,40 @@ use common::* ; + + + +/// A term type-checking error. +pub enum TypError { + /// No type info, just an error message. + Msg(String), + /// Type info: + /// + /// - the type expected (if known), + /// - the type obtained, + /// - the index of the argument that caused it. + Typ { + expected: Option, + obtained: Typ, + index: usize, + } +} +impl TypError { + /// Message constructor. + pub fn msg>(s: S) -> Self { + TypError::Msg( s.into() ) + } + + /// Type info constructor. + pub fn typ( + expected: Option, obtained: Typ, index: usize + ) -> Self { + TypError::Typ { expected, obtained, index } + } +} + + + /// Parse error data. #[derive(Debug)] pub struct ParseErrorData { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index af6ef773..565fefc9 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1521,7 +1521,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { match term::try_app(op, args) { Ok(term) => Ok((term, op_pos)), Err( - term::TypError::Typ { expected, obtained, index } + TypError::Typ { expected, obtained, index } ) => if let Some(exp) = expected { err_chain! { self.error( @@ -1542,7 +1542,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { => self.error(op_pos, "in this operator application") } } - Err( term::TypError::Msg(blah) ) => bail!( + Err( TypError::Msg(blah) ) => bail!( self.error(op_pos, blah) ), } diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index f7d43d85..9197ba23 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -378,6 +378,7 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { unknown += 1 ; continue } + match val.get() { val::RVal::B(b) => { write!(w, " ") ? ; @@ -391,6 +392,7 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { write!(w, ")") ? } }, + val::RVal::I(ref i) => { write!(w, " (= ") ? ; arg.write( @@ -400,6 +402,7 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { int_to_smt!(w, i) ? ; write!(w, ")") ? }, + val::RVal::R(ref r) => { write!(w, " (= ") ? ; arg.write( @@ -409,13 +412,16 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { rat_to_smt!(w, r) ? ; write!(w, ")") ? }, - val::RVal::Array { .. } => { + + val::RVal::Array { .. } | + val::RVal::DTypNew { .. } => { write!(w, " (= ") ? ; arg.write( w, |w, v| w.write_all( v.default_str().as_bytes() ) ) ? ; write!(w, " {})", val) ? }, + val::RVal::N(_) => (), } } diff --git a/src/term/factory.rs b/src/term/factory.rs index 2e2986e1..e8ba889c 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -32,13 +32,14 @@ fn scan_vars(t: & Term) -> VarSet { RTerm::Var(_, i) => { let _ = set.insert(* i) ; () }, - RTerm::Int(_) => (), - RTerm::Real(_) => (), - RTerm::Bool(_) => (), + RTerm::Cst(_) => (), RTerm::CArray { ref term, .. } => to_do.push(& * term), RTerm::App{ ref args, .. } => for arg in args { to_do.push(arg) }, + RTerm::DTypNew { ref args, .. } => for arg in args { + to_do.push(arg) + }, } } set.shrink_to_fit() ; @@ -119,21 +120,13 @@ pub fn bool_var>(v: V) -> Term { #[inline(always)] pub fn int>(i: I) -> Term { let i = i.into() ; - factory.mk( RTerm::Int(i) ) + factory.mk( RTerm::Cst( val::int(i) ) ) } /// Creates a real constant. #[inline(always)] pub fn real>(r: R) -> Term { let r = r.into() ; - if r.denom().is_zero() { - panic!("division by zero while constructing real term") - } - let r = if r.numer().is_negative() == r.denom().is_negative() { - r - } else { - - r.abs() - } ; - factory.mk( RTerm::Real(r) ) + factory.mk( RTerm::Cst( val::real(r) ) ) } /// Creates a real constant from a float. #[inline] @@ -164,7 +157,7 @@ pub fn real_one() -> Term { /// Creates a boolean. #[inline(always)] pub fn bool(b: bool) -> Term { - factory.mk( RTerm::Bool(b) ) + factory.mk( RTerm::Cst( val::bool(b) ) ) } /// Creates the constant `true`. #[inline(always)] @@ -267,6 +260,12 @@ pub fn app(op: Op, args: Vec) -> Term { normalize(op, args, typ.clone()) } + +/// Creates a datatype constructor. +pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { + factory.mk( RTerm::DTypNew { typ, name, args } ) +} + /// Creates an operator application. /// /// Error if the application is ill-typed (int will be cast to real diff --git a/src/term/mod.rs b/src/term/mod.rs index 5483afd7..bf933827 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -59,37 +59,48 @@ use hashconsing::* ; use common::* ; -use var_to::terms::VarTermsSet ; #[macro_use] mod op ; -pub use self::op::* ; mod factory ; - +mod tterms ; pub mod simplify ; pub mod typ ; -#[cfg(test)] -mod test ; +mod zip ; +pub use self::op::* ; pub use self::factory::* ; +pub use self::tterms::* ; pub use self::typ::Typ ; +#[cfg(test)] +mod test ; + + + +/// Hash consed term. +pub type Term = HConsed ; + + /// A real term. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RTerm { /// A clause variable. Var(Typ, VarIdx), - /// An integer. - Int(Int), - /// A real (actually a rational). - Real(Rat), - /// A boolean. - Bool(bool), + /// A constant. + Cst(Val), + /// A **constant** array. /// /// The type is the type of **the indices** of the arrays. - CArray { typ: Typ, term: Box }, + CArray { + /// Type of **the indices** (not the array). + typ: Typ, + /// Default term of the array. + term: Box + }, + /// An operator application. App { /// Type of the application. @@ -99,7 +110,20 @@ pub enum RTerm { /// The arguments. args: Vec, }, + + /// A datatype constructor application. + DTypNew { + /// Type of the application. + typ: Typ, + /// Name of the constructor. + name: String, + /// Arguments of the constructor. + args: Vec, + }, } + + + impl RTerm { /// The operator and the kids of a term. pub fn app_inspect(& self) -> Option< (Op, & Vec) > { @@ -199,25 +223,25 @@ impl RTerm { stack.push(term) }, RTerm::CArray { ref term, .. } => stack.push(& * term), + RTerm::DTypNew { ref args, .. } => for term in args { + stack.push(term) + }, RTerm::Var(_, _) | - RTerm::Int(_) | - RTerm::Real(_) | - RTerm::Bool(_) => (), + RTerm::Cst(_) => (), } } } /// Type of the term. pub fn typ(& self) -> Typ { - match * self { - RTerm::Var(ref typ, _) => typ.clone(), - RTerm::Int(_) => typ::int(), - RTerm::Real(_) => typ::real(), - RTerm::Bool(_) => typ::bool(), - RTerm::CArray { ref typ, ref term } => typ::array( + match self { + RTerm::Var(typ, _) => typ.clone(), + RTerm::Cst(val) => val.typ(), + RTerm::CArray { typ, term } => typ::array( typ.clone(), term.typ() ), - RTerm::App { ref typ, .. } => typ.clone(), + RTerm::DTypNew { typ, .. } => typ.clone(), + RTerm::App { typ, .. } => typ.clone(), } } @@ -251,37 +275,45 @@ impl RTerm { // | |~~~~~~ prefix string (written before next element) // |~~~~~~~~~~~ elements to write ] ; + while let Some( (mut to_do, sep, end) ) = stack.pop() { use self::RTerm::* ; + if let Some(this_term) = to_do.pop() { stack.push( (to_do, sep, end) ) ; - match * this_term { + + match this_term { Var(_, v) => { write!(w, "{}", sep) ? ; - write_var(w, v) ? - }, - Int(ref i) => { - write!(w, "{}", sep) ? ; - int_to_smt!(w, i) ? - }, - Real(ref r) => { - write!(w, "{}", sep) ? ; - rat_to_smt!(w, r) ? + write_var(w, * v) ? }, - Bool(b) => write!(w, "{}{}", sep, b) ?, - CArray { ref term, .. } => { + Cst(val) => write!(w, "{}{}", sep, val) ?, + + CArray { term, .. } => { write!(w, "{}((as const {})", sep, this_term.typ()) ? ; stack.push( (vec![term], " ", ")") ) }, - App { op, ref args, .. } => { + + App { op, args, .. } => { write!(w, "{}({}", sep, op) ? ; stack.push( (args.iter().rev().map(|t| t.get()).collect(), " ", ")") ) }, + + DTypNew { name, args, .. } => if args.is_empty() { + write!(w, "{}", name) ? + } else { + write!(w, "({}", name) ? ; + stack.push( + (args.iter().rev().map(|t| t.get()).collect(), " ", ")") + ) + }, } + } else { w.write_all( end.as_bytes() ) ? } } + Ok(()) } @@ -445,9 +477,7 @@ impl RTerm { /// Turns a constant term in a `Val`. pub fn val(& self) -> Option { match * self { - RTerm::Int(ref i) => Some( val::int(i.clone()) ), - RTerm::Real(ref r) => Some( val::real(r.clone()) ), - RTerm::Bool(b) => Some( val::bool(b) ), + RTerm::Cst(ref val) => Some( val.clone() ), _ => None, } } @@ -506,24 +536,30 @@ impl RTerm { 'eval: loop { // Go down applications. let mut evaled = match * current { - App { op, ref args, .. } => { - current = & args[0] ; - stack.push( (op, & args[1..], vec![]) ) ; - continue 'eval - }, - // Rest are leaves, going up. + // Leaves, going up. Var(_, v) => if v < model.len() { model.get(v).clone() } else { bail!("model is too short ({})", model.len()) }, - Int(ref i) => val::int( i.clone() ), - Real(ref r) => val::real( r.clone() ), - Bool(b) => val::bool(b), + Cst(ref val) => val.clone(), + + // Recursive cases. + + App { op, ref args, .. } => { + current = & args[0] ; + stack.push( (op, & args[1..], vec![]) ) ; + continue 'eval + }, + CArray { ref typ, ref term } => { let default = term.eval(model) ? ; val::array(typ.clone(), default) }, + + DTypNew { .. } => bail!( + "datatype constructor evaluation is not implemented" + ), } ; // Go up. @@ -554,11 +590,21 @@ impl RTerm { /// If the term's an integer constant, returns the value. pub fn int_val(& self) -> Option<& Int> { - if let RTerm::Int(ref i) = * self { Some( i ) } else { None } + if let RTerm::Cst(val) = self { + if let val::RVal::I(i) = val.get() { + return Some( i ) + } + } + None } /// If the term's a rational constant, returns the value. pub fn real_val(& self) -> Option<& Rat> { - if let RTerm::Real(ref r) = * self { Some( r ) } else { None } + if let RTerm::Cst(val) = self { + if let val::RVal::R(r) = val.get() { + return Some( r ) + } + } + None } /// The highest variable index appearing in the term. @@ -570,13 +616,17 @@ impl RTerm { RTerm::Var(_, i) => max = Some( ::std::cmp::max( i, max.unwrap_or_else(|| 0.into()) ) ), - RTerm::Int(_) | - RTerm::Real(_) | - RTerm::Bool(_) => (), + RTerm::Cst(_) => (), + RTerm::CArray { ref term, .. } => to_do.push(& * term), + RTerm::App{ ref args, .. } => for arg in args { to_do.push(arg) }, + + RTerm::DTypNew { ref args, .. } => for arg in args { + to_do.push(arg) + }, } } max @@ -593,20 +643,26 @@ impl RTerm { /// Return true if the term mentions at least one variable from `vars`. pub fn mentions_one_of(& self, vars: & VarSet) -> bool { let mut to_do = vec![ self ] ; + while let Some(term) = to_do.pop() { match * term { RTerm::Var(_, var) => if vars.contains(& var) { return true }, - RTerm::Bool(_) | - RTerm::Int(_) | - RTerm::Real(_) | - RTerm::CArray { .. } => (), + RTerm::Cst(_) => (), + + RTerm::CArray { ref term, .. } => to_do.push(term), + RTerm::App { ref args, .. } => for arg in args { to_do.push(arg) }, + + RTerm::DTypNew { ref args, .. } => for arg in args { + to_do.push(arg) + }, } } + false } @@ -680,9 +736,11 @@ impl RTerm { } }, - RTerm::Bool(_) | - RTerm::Real(_) | - RTerm::Int(_) => current.clone(), + RTerm::DTypNew { .. } => panic!( + "substitution in datatype constructors is not implemented" + ), + + RTerm::Cst(_) => current.clone(), } ; // Go up. @@ -956,13 +1014,18 @@ impl RTerm { } }, RTerm::Var(_, v) => return Some((v, solution)), - _ => return None, + + RTerm::CArray { .. } | + RTerm::DTypNew { .. } => return None, + + RTerm::Cst(_) => return None, } } } - } + + impl_fmt!{ RTerm(self, fmt) { let mut buf = Vec::with_capacity(250) ; @@ -988,1503 +1051,3 @@ impl<'a> PebcakFmt<'a> for RTerm { ) } } - - - - -/// Hash consed term. -pub type Term = HConsed ; - - - - -/// Top term, as they appear in clauses. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum TTerm { - /// A predicate application. - P { - /// Predicate applied. - pred: PrdIdx, - /// The arguments. - args: VarTerms, - }, - /// Just a term. - T(Term), -} -impl TTerm { - /// The false top term. - pub fn fls() -> Self { - TTerm::T( term::fls() ) - } - /// The true top term. - pub fn tru() -> Self { - TTerm::T( term::tru() ) - } - - /// Type of the top term. - /// - /// Should always be bool, except during parsing. - pub fn typ(& self) -> Typ { - match * self { - TTerm::P { .. } => typ::bool(), - TTerm::T(ref term) => term.typ(), - } - } - - /// True if the top term is a term with no variables and evaluates to true. - pub fn is_true(& self) -> bool { - self.bool() == Some(true) - } - /// True if the top term is a term with no variables and evaluates to false. - pub fn is_false(& self) -> bool { - self.bool() == Some(false) - } - /// Boolean corresponding to the top term if it's a bool constant. - pub fn bool(& self) -> Option { - match * self { - TTerm::T(ref t) => t.bool(), - _ => None, - } - } - /// Boolean corresponding to the top term if it's an integer constant. - pub fn int(& self) -> Option { - match * self { - TTerm::T(ref t) => t.int(), - _ => None, - } - } - /// The operator and the kids of a top term, if it's an operator application. - pub fn app_inspect(& self) -> Option< (Op, & Vec) > { - match * self { - TTerm::T(ref t) => t.app_inspect(), - _ => None, - } - } - /// If the top term is simply a term, returns that term. - #[inline] - pub fn term(& self) -> Option<& Term> { - if let TTerm::T(ref t) = * self { Some(t) } else { None } - } - - /// The predicate a top term is an application of, if any. - pub fn pred(& self) -> Option { - match * self { - TTerm::P { pred, .. } => Some(pred), - _ => None, - } - } - - /// The arguments of a top term if it's a predicate application. - pub fn args(& self) -> Option<& VarTerms> { - match * self { - TTerm::P { ref args, .. } => Some(args), - _ => None, - } - } - - /// Applies some treatment if the top term is a predicate application. - pub fn pred_app_fold(& mut self, init: T, f: F) -> T - where F: Fn(T, PrdIdx, & mut VarTerms) -> T { - if let TTerm::P { pred, ref mut args } = * self { - f(init, pred, args) - } else { - init - } - } - - /// Variables appearing in a top term. - pub fn vars(& self) -> VarSet { - match * self { - TTerm::P { ref args, .. } => { - let mut vars = VarSet::with_capacity(17) ; - for term in args.iter() { - vars.extend( term::vars(term) ) - } - vars - }, - TTerm::T(ref term) => term::vars(term), - } - } - - /// In-place variable substitution for top terms. - /// - /// Used for substitutions in the same clause / predicate scope. - pub fn subst>( - & mut self, map: & Map - ) -> bool { - match * self { - TTerm::T(ref mut term) => { - let (t, b) = term.subst(map) ; - * term = t ; - b - }, - TTerm::P { ref mut args, .. } => { - let mut changed = false ; - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let (t, b) = arg.subst(map) ; - nu_args.push(t) ; - changed = changed || b - } - * args = nu_args.into() ; - changed - }, - } - } - - /// Total variable substitution for top terms. - /// - /// Used for substitutions in different clause / predicate scope. - pub fn subst_total>( - & self, map: & Map - ) -> Res { - match * self { - TTerm::P { pred, ref args } => { - let mut new_args = VarMap::with_capacity( args.len() ) ; - for term in args.iter() { - if let Some((term, _)) = term.subst_total(map) { - new_args.push(term) - } else { - bail!("total substitution failed (predicate)") - } - } - Ok( TTerm::P { pred, args: new_args.into() } ) - }, - TTerm::T(ref term) => if let Some((term, _)) = term.subst_total(map) { - Ok( TTerm::T(term) ) - } else { - bail!("total substitution failed (term)") - }, - } - } - - /// Writes a top term using special functions for writing predicates and - /// variables. - pub fn write( - & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - use self::TTerm::* ; - match * self { - P { pred, ref args } => write_prd(w, pred, args), - T(ref t) => t.write(w, write_var), - } - } - - /// Writes a top term smt2 style using a special function for writing - /// predicates. - pub fn write_smt2( - & self, w: & mut W, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - self.write( - w, |w, var| var.default_write(w), write_prd - ) - } -} -impl_fmt!{ - TTerm(self, fmt) { - match * self { - TTerm::P { pred, ref args } => { - write!(fmt, "(p_{}", pred) ? ; - for arg in args.iter() { - write!(fmt, " {}", arg) ? - } - write!(fmt, ")") - }, - TTerm::T(ref t) => write!(fmt, "{}", t), - } - } -} - - -/// A *set* of top terms. -/// -/// Actually contains a set of `Term`s and a map from predicates to their -/// arguments. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct TTermSet { - /// Set of terms. - terms: TermSet, - /// Predicate applications. - preds: PrdHMap< VarTermsSet >, -} -impl TTermSet { - /// Creates a new top term set with some capacity - #[inline] - pub fn with_capacity(capa: usize) -> Self { - TTermSet { - terms: TermSet::with_capacity(capa), - preds: PrdHMap::with_capacity(capa), - } - } - /// Creates a new top term set with some capacities - #[inline] - pub fn with_capacities(term_capa: usize, pred_capa: usize) -> Self { - TTermSet { - terms: TermSet::with_capacity(term_capa), - preds: PrdHMap::with_capacity(pred_capa), - } - } - - /// Destroys the set. - #[inline] - pub fn destroy(self) -> (TermSet, PrdHMap) { - (self.terms, self.preds) - } - - /// Reserves some space. - #[inline] - pub fn reserve(& mut self, terms: usize, preds: usize) { - self.terms.reserve(terms) ; - self.preds.reserve(preds) - } - - /// Creates an empty top term set. - #[inline] - pub fn new() -> Self { Self::with_capacity(5) } - - /// Creates a top term set from a set of terms. - #[inline] - pub fn of_terms(terms: TermSet, pred_capa: usize) -> Self { - TTermSet { - terms, preds: PrdHMap::with_capacity(pred_capa), - } - } - - /// True iff the set is empty. - #[inline] - pub fn is_empty(& self) -> bool { - self.terms.is_empty() && self.preds.is_empty() - } - - /// Number of elements. - #[inline] - pub fn len(& self) -> usize { - let mut len = self.terms.len() ; - for (_, argss) in & self.preds { - len += argss.len() - } - len - } - - /// Terms. - #[inline] - pub fn terms(& self) -> & TermSet { - & self.terms - } - /// Predicate applications. - #[inline] - pub fn preds(& self) -> & PrdHMap< VarTermsSet > { - & self.preds - } - - /// Terms (mutable version). - #[inline] - pub fn terms_mut(& mut self) -> & mut TermSet { - & mut self.terms - } - /// Predicate applications (mutable version). - #[inline] - pub fn preds_mut(& mut self) -> & mut PrdHMap< VarTermsSet > { - & mut self.preds - } - - /// True if `self` is a subset of `that`. - #[inline] - pub fn is_subset_of(& self, that: & Self) -> bool { - // `terms` subset? - if ! self.terms.is_subset(& that.terms) { return false } - // All predicates in `that` also appear in `self`? - for (pred, _) in & that.preds { - if ! self.preds.contains_key(pred) { - return false - } - } - // All applications in `self` also in `that`? - for (pred, self_set) in & self.preds { - if let Some(that_set) = that.preds.get(pred) { - if ! self_set.is_subset(that_set) { return false } - } else { - return false - } - } - true - } - - /// Variable substitution. - pub fn subst>(& self, map: & Map) -> Self { - let mut terms = TermSet::with_capacity(self.terms.len()) ; - for term in self.terms() { - let (term, _) = term.subst(map) ; - terms.insert(term) ; - } - - let mut preds = PrdHMap::with_capacity( self.preds.len() ) ; - for (pred, argss) in self.preds.iter() { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss { - let args = var_to::terms::new( args.subst(map) ) ; - nu_argss.insert(args) ; - } - preds.insert(* pred, nu_argss) ; - } - - TTermSet { terms, preds } - } - - /// Inserts a predicate application. - #[inline] - pub fn insert_pred_app(& mut self, pred: PrdIdx, args: VarTerms) -> bool { - self.preds.entry(pred).or_insert_with(VarTermsSet::new).insert(args) - } - /// Inserts some predicate applications. - pub fn insert_pred_apps( - & mut self, pred: PrdIdx, argss: TArgss - ) - where - Iter: Iterator + ExactSizeIterator, - TArgss: IntoIterator { - let argss = argss.into_iter() ; - if argss.len() == 0 { return () } - self.preds.entry(pred).or_insert_with(VarTermsSet::new).extend( argss ) - } - - /// Inserts a term. - #[inline] - pub fn insert_term(& mut self, term: Term) -> bool { - self.terms.insert(term) - } - /// Inserts some terms. - #[inline] - pub fn insert_terms(& mut self, terms: Terms) - where - Iter: Iterator + ExactSizeIterator, - Terms: IntoIterator { - let terms = terms.into_iter() ; - self.terms.reserve( terms.len() ) ; - for term in terms { self.terms.insert(term) ; () } - } - - /// Inserts a top term. - pub fn insert_tterm(& mut self, tterm: TTerm) -> bool { - match tterm { - TTerm::T(term) => self.insert_term(term), - TTerm::P { pred, args } => self.insert_pred_app(pred, args) - } - } - - /// Constructor from some top terms. - pub fn of_tterms(tterms: TTs) -> Self - where - Iter: Iterator + ExactSizeIterator, - TTs: IntoIterator { - let tterms = tterms.into_iter() ; - let mut slf = Self::with_capacity( tterms.len() ) ; - for tterm in tterms { - slf.insert_tterm(tterm) ; () - } - slf - } - - /// Constructor from a single term. - pub fn of_term(term: Term) -> Self { - let mut slf = Self::new() ; - slf.insert_term(term) ; - slf - } - - /// Puts the variables appearing in the top terms in some set. - pub fn vars(& self, set: & mut VarSet) { - for term in & self.terms { - set.extend( term::vars(term) ) - } - for (_, argss) in & self.preds { - for args in argss { - for arg in args.iter() { - set.extend( term::vars(arg) ) - } - } - } - } - - /// Removes some arguments from the predicate applications. - pub fn remove_vars(& mut self, to_keep: & PrdHMap) { - remove_vars_from_pred_apps( - & mut self.preds, to_keep - ) - } - - - /// Writes all top terms with some separator. - pub fn write( - & self, w: & mut W, sep: & str, write_var: WriteVar, write_pred: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - // Don't print the separator the first time. - let mut separate = false ; - macro_rules! write_sep { - () => ( - if separate { - write!(w, "{}", sep) ? - } else { - separate = true - } - ) ; - } - - for term in & self.terms { - write_sep!() ; - term.write(w, & write_var) ? - } - - for (pred, argss) in & self.preds { - for args in argss { - write_sep!() ; - write_pred(w, * pred, args) ? - } - } - - Ok(()) - } -} -impl ::std::cmp::PartialOrd for TTermSet { - fn partial_cmp(& self, other: & Self) -> Option<::std::cmp::Ordering> { - use ::std::cmp::Ordering::* ; - let mut le = true ; - let mut ge = true ; - macro_rules! check_none { - () => ( if ! le && ! ge { return None } ) - } - - for term in & self.terms { - if ! other.terms.contains(term) { le = false ; break } - } - for term in & other.terms { - if ! self.terms.contains(term) { ge = false ; break } - } - check_none! {} - - // Part of what happens in this loop is explained below. - for (pred, argss) in & self.preds { - check_none! {} - if let Some(ass) = other.preds.get(pred) { - if ! argss.is_subset(ass) { - le = false - } - if ! ass.is_subset(argss) { - ge = false - } - } else { - le = false - } - } - - // At this point we checked all predicate applications in `self`. We won't - // touch `le` anymore. - // - // The only way `ge` can change is if it is true and `other` has some - // predicates that don't appear in `self`. That's because in the previous - // loop, whenever a predicate from `self` also appears in `other`, we - // checked whether the other set of arguments is a subset of the self set - // of arguments and set `ge` to false if it's not the case. - - if ge { - for pred in other.preds.keys() { - if ! self.preds.contains_key(pred) { - ge = false ; - break - } - } - } - - match (le, ge) { - (true, true) => Some(Equal), - (true, false) => Some(Less), - (false, true) => Some(Greater), - (false, false) => None, - } - } -} - -/// Removes some arguments from some predicate applications. -fn remove_vars_from_pred_apps( - apps: & mut PrdHMap< VarTermsSet >, to_keep: & PrdHMap -) { - for (pred, argss) in apps.iter_mut() { - let vars_to_keep = if let Some(vars) = to_keep.get(pred) { - vars - } else { - continue - } ; - let mut old_argss = VarTermsSet::with_capacity( argss.len() ) ; - ::std::mem::swap( & mut old_argss, argss ) ; - for args in old_argss { - argss.insert( args.remove( vars_to_keep ) ) ; - } - } -} - - -/// A formula composed of top terms. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TTerms { - /// True. - True, - /// False. - False, - /// Conjunction. - Conj { - quant: Option, - tterms: TTermSet, - }, - /// Disjunction. - Disj { - quant: Option, - tterms: TTermSet, - neg_preds: PrdHMap< VarTermsSet >, - }, - /// Almost a DNF: a disjunction of conjunctions of `TTerm`s. - Dnf { - disj: Vec< (Option, TTermSet) > - }, -} -impl TTerms { - // pub fn inspect(& self) { - // match * self { - // TTerms::True => println!("true"), - // TTerms::False => println!("false"), - // TTerms::Conj { ref quant, ref tterms } => println!( - // "conj, {} ({})", tterms.len(), - // if let Some(q) = quant.as_ref() { - // format!("{}", q.len()) - // } else { "none".into() } - // ), - // TTerms::Disj { ref quant, ref tterms, ref neg_preds } => println!( - // "conj, {}, {}, {}", tterms.len(), neg_preds.len(), - // if let Some(q) = quant.as_ref() { - // format!("{}", q.len()) - // } else { "none".into() } - // ), - // TTerms::Dnf { ref disj } => println!( - // "dnf, {}", disj.len() - // ), - // } - // } - - /// True. - #[inline] - pub fn tru() -> Self { TTerms::True } - /// False. - #[inline] - pub fn fls() -> Self { TTerms::False } - /// Constructor from a boolean. - #[inline] - pub fn of_bool(b: bool) -> Self { - if b { Self::tru() } else { Self::fls() } - } - - /// Attempts to transform some terms in a term. - pub fn to_term(& self) -> Option { - match * self { - TTerms::True => Some( term::tru() ), - TTerms::False => Some( term::fls() ), - - TTerms::Conj { ref quant, .. } if quant.is_some() => None, - TTerms::Conj { ref tterms, .. } if ! tterms.preds.is_empty() => None, - TTerms::Conj { ref tterms, .. } => Some( - term::and( - tterms.terms().iter().cloned().collect() - ) - ), - - TTerms::Disj { ref quant, .. } if quant.is_some() => None, - TTerms::Disj { - ref tterms, ref neg_preds, .. - } if ! tterms.preds.is_empty() || ! neg_preds.is_empty() => None, - TTerms::Disj { ref tterms, .. } => Some( - term::or( - tterms.terms().iter().cloned().collect() - ) - ), - - TTerms::Dnf { ref disj } => { - let mut disj_terms = Vec::with_capacity( disj.len() ) ; - for & (ref quant, ref conj) in disj { - if quant.is_some() - || ! conj.preds.is_empty() { return None } - disj_terms.push( - term::and( - conj.terms().iter().cloned().collect() - ) - ) - } - Some( term::or(disj_terms) ) - }, - } - } - - /// Boolean value of some top terms. - #[inline] - pub fn bool(& self) -> Option { - match * self { - TTerms::True => Some(true), - TTerms::False => Some(false), - _ => None, - } - } - - /// Removes some arguments from the predicate applications. - pub fn remove_vars(& mut self, to_keep: & PrdHMap< VarSet >) { - match * self { - TTerms::True | TTerms::False => (), - TTerms::Conj { ref mut tterms, .. } => tterms.remove_vars(to_keep), - TTerms::Disj { - ref mut tterms, ref mut neg_preds, .. - } => { - tterms.remove_vars(to_keep) ; - remove_vars_from_pred_apps(neg_preds, to_keep) - }, - TTerms::Dnf { ref mut disj } => for & mut (_, ref mut tterms) in disj { - tterms.remove_vars(to_keep) - }, - } - } - - /// Variable substitution. - pub fn subst>(& self, map: & Map) -> Self { - match * self { - TTerms::True => TTerms::True, - TTerms::False => TTerms::False, - - TTerms::Conj { ref quant, ref tterms } => { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) - } else { - true - } - } - TTerms::Conj { quant: quant.clone(), tterms: tterms.subst(map) } - }, - - TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) - } else { - true - } - } - - let mut preds = PrdHMap::with_capacity( neg_preds.len() ) ; - for (pred, argss) in neg_preds.iter() { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss { - let args = var_to::terms::new( args.subst(map) ) ; - nu_argss.insert(args) ; - } - preds.insert(* pred, nu_argss) ; - } - - TTerms::Disj { - quant: quant.clone(), - tterms: tterms.subst(map), - neg_preds: preds, - } - }, - - TTerms::Dnf { ref disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - for & (ref quant, ref tterms) in disj { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) - } else { - true - } - } - nu_disj.push( - (quant.clone(), tterms.subst(map)) - ) - } - TTerms::Dnf { disj: nu_disj } - }, - } - } - - /// Constructor for a single term. - pub fn of_term(quant: Option, term: Term) -> Self { - Self::conj( quant, TTermSet::of_term(term) ) - } - - /// Constructs a conjuction. - pub fn conj(quant: Option, tterms: TTermSet) -> Self { - TTerms::Conj { quant, tterms }.simplify() - } - - /// Constructs a disjuction. - pub fn disj( - quant: Option, tterms: TTermSet, neg_preds: PrdHMap - ) -> Self { - TTerms::Disj { quant, tterms, neg_preds }.simplify() - } - /// Constructs a disjunction from a positive application and some negated top - /// terms. - /// - /// This special format is exactly the one used by preprocessing. - pub fn disj_of_pos_neg( - quant: Option, pos: Option<(PrdIdx, VarTerms)>, neg: TTermSet - ) -> Self { - let TTermSet { terms, preds: neg_preds } = neg ; - let mut tterms = TTermSet::with_capacities(terms.len(), 1) ; - for term in terms { - tterms.insert_term( not(term) ) ; - } - if let Some((pred, args)) = pos { - tterms.insert_pred_app(pred, args) ; - } - TTerms::Disj { - quant, tterms, neg_preds - } - } - - /// Constructs a DNF. - pub fn dnf(disj: Vec< (Option, TTermSet) >) -> Self { - TTerms::Dnf{ disj }.simplify() - } - - /// Predicates appearing in the top terms. - pub fn preds(& self) -> PrdSet { - let mut res = PrdSet::new() ; - match * self { - TTerms::True | TTerms::False => (), - - TTerms::Conj { ref tterms, .. } => for pred in tterms.preds.keys() { - res.insert(* pred) ; - }, - TTerms::Disj { ref tterms, ref neg_preds, .. } => { - for pred in tterms.preds.keys() { - res.insert(* pred) ; - } - for pred in neg_preds.keys() { - res.insert(* pred) ; - } - }, - - TTerms::Dnf { ref disj } => for & (_, ref tterms) in disj { - for pred in tterms.preds.keys() { - res.insert(* pred) ; - } - }, - } - - res - } - - /// Constructs the disjunction of `self` and `conj` (a conjunction). - /// - /// Does not call `simplify` if it creates a dnf. - /// - /// # Error if - /// - /// - called on a `Disj` - pub fn or(self, conj: (Option, TTermSet)) -> Res { - match self { - TTerms::True => Ok(self), - TTerms::False => Ok( - TTerms::conj(conj.0, conj.1).simplify() - ), - TTerms::Conj { quant, tterms } => { - if tterms.is_subset_of(& conj.1) { - // conj => self? - Ok( TTerms::Conj { quant, tterms } ) - } else if conj.1.is_subset_of(& tterms) { - // self => conj? - Ok( - TTerms::Conj{ quant: conj.0, tterms: conj.1 }.simplify() - ) - } else { - Ok( - TTerms::dnf( - vec![ (quant, tterms), conj ] - ) - ) - } - }, - TTerms::Disj { .. } => bail!( - "TTerms: trying to call `or` on a disjunction" - ), - TTerms::Dnf { disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - let mut ignore_conj = false ; - for (quant, tterms) in disj { - use std::cmp::Ordering::* ; - match tterms.partial_cmp( & conj.1 ) { - // conj => tterms - // don't need `conj` - Some(Less) => ignore_conj = true, - // tterms => conj - // skip `tterms` - Some(Greater) => continue, - // tterms = conj - // skip `conj` - Some(Equal) => ignore_conj = true, - - None => (), - } - nu_disj.push( (quant, tterms) ) - } - - if ! ignore_conj { nu_disj.push(conj) } - - // Note that if we ignored a `tterms_1` because `tterms_1 => conj`, and - // then we saw a `tterms_2` s.t. `conj => tterms_2` so we ignored - // `conj` as well, there is no problem because implication is - // transitive. - - Ok( TTerms::Dnf { disj: nu_disj } ) - }, - } - } - - /// Simplifies a formula of top terms. - /// - /// # TODO - /// - /// - factor code inside this function, `Conj` and `Disj` are almost the - /// same, DRY - /// - /// # Warning - /// - /// This function recurses on a `Conj` in the `Dnf` case. At the time of - /// writing, this recursive call is guaranteed not to cause more recursive - /// calls. - /// - /// Be careful to maintain, or (ideally) improve, this invariant. - pub fn simplify(self) -> Self { - - match self { - - TTerms::True | TTerms::False => self, - - TTerms::Conj { quant, mut tterms } => { - tterms.preds.retain( - |_, argss| ! argss.is_empty() - ) ; - - let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; - // Used to inline conjunctions. - let mut swap = TermSet::new() ; - ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; - - 'inline_conjs: loop { - - 'inspect_conj_terms: for term in old_terms.drain() { - - // Is the term a conjunction? - if let Some(kids) = term.conj_inspect() { - for kid in kids { - swap.insert( kid.clone() ) ; - () - } - continue 'inspect_conj_terms - } - - // Term trivial? - match term.bool() { - Some(true) => continue 'inspect_conj_terms, - Some(false) => return TTerms::fls(), - None => (), - } - - // Do we also have its negation? - if tterms.terms.contains( & not( term.clone() ) ) { - return TTerms::fls() - } - - // Okay, move on. - tterms.terms.insert(term) ; - () - } - - // Keep going if `swap` is not empty. - if ! swap.is_empty() { - ::std::mem::swap( & mut old_terms, & mut swap ) ; - continue 'inline_conjs - } else { - break 'inline_conjs - } - - } - - // Only keep active quantified variables. - let quant = quant.and_then( - |quant| { - let mut active = VarSet::with_capacity( - quant.vars().len() * 2 - ) ; - tterms.vars(& mut active) ; - quant.filter(|var| active.contains(var)) - } - ) ; - - if tterms.is_empty() { - TTerms::tru() - } else { - TTerms::Conj { quant, tterms } - } - }, - - TTerms::Disj { - quant, mut tterms, mut neg_preds - } => { - tterms.preds.retain( - |_, argss| ! argss.is_empty() - ) ; - neg_preds.retain( - |_, argss| ! argss.is_empty() - ) ; - - // Do we have a predicate application and its negation? - for (pred, argss) in & tterms.preds { - if let Some(neg_argss) = neg_preds.get(pred) { - for args in argss { - if neg_argss.contains(args) { return TTerms::tru() } - } - } - } - - let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; - // Used to inline disjunctions. - let mut swap = TermSet::new() ; - ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; - - 'inline_disj: loop { - - 'inspect_disj_terms: for term in old_terms.drain() { - - // Is the term a disjunction? - if let Some(kids) = term.disj_inspect() { - for kid in kids { - swap.insert( kid.clone() ) ; - () - } - continue 'inspect_disj_terms - } - - // Term trivial? - match term.bool() { - Some(true) => return TTerms::tru(), - Some(false) => continue 'inspect_disj_terms, - None => (), - } - - // Do we also have its negation? - if tterms.terms.contains( & not( term.clone() ) ) { - return TTerms::tru() - } - - // Okay, move on. - tterms.terms.insert(term) ; - () - } - - // Keep going if `swap` is not empty. - if ! swap.is_empty() { - ::std::mem::swap( & mut old_terms, & mut swap ) ; - continue 'inline_disj - } else { - break 'inline_disj - } - - } - - // Only keep active quantified variables. - let quant = quant.and_then( - |quant| { - let mut active = VarSet::with_capacity( - quant.vars().len() * 2 - ) ; - tterms.vars(& mut active) ; - for (_, argss) in & neg_preds { - for args in argss { - for arg in args.iter() { - active.extend( term::vars(arg) ) - } - } - } - quant.filter(|var| active.contains(var)) - } - ) ; - - if tterms.is_empty() && neg_preds.is_empty() { - TTerms::fls() - } else { - TTerms::Disj { quant, tterms, neg_preds } - } - }, - - TTerms::Dnf { disj } => { - // We're cheating a bit here. Simplifying the disjuncts is done by - // constructing the corresponding `TTerms::Conj` and simplifying them. - // While this causes a recursive call, it's fine because it is - // guaranteed to be the only one. - // - // Unless something changes later in `Conj`'s simplification... - let mut nu_disj: Vec<(_, TTermSet)> = Vec::with_capacity( - disj.len() - ) ; - 'simplify_disjuncts: for (quant, tterms) in disj { - match ( TTerms::Conj { quant, tterms } ).simplify() { - TTerms::True => return TTerms::True, - TTerms::False => (), - TTerms::Conj { quant, tterms } => { - // Check with other disjuncts. - let mut cnt = 0 ; - while cnt < nu_disj.len() { - use std::cmp::Ordering::* ; - match tterms.partial_cmp(& nu_disj[cnt].1) { - None => cnt += 1, - // other disjunct => this disjunct - Some(Less) => { nu_disj.swap_remove(cnt) ; () }, - // other disjunct = this disjunct - Some(Equal) => continue 'simplify_disjuncts, - // this disjunct => other disjunct - Some(Greater) => continue 'simplify_disjuncts, - } - } - nu_disj.push( (quant, tterms) ) - }, - TTerms::Disj { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a disjunction, unreachable" - ), - TTerms::Dnf { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a DNF, unreachable" - ), - } - } - match nu_disj.len() { - 0 => TTerms::fls(), - 1 => if let Some((quant, tterms)) = nu_disj.pop() { - TTerms::Conj { quant, tterms } - } else { - unreachable!() - }, - _ => TTerms::Dnf { disj: nu_disj } - } - }, - } - } - - - /// Simplifies some top terms given some definitions for the predicates. - pub fn simplify_pred_apps( - self, model: ModelRef, pred_terms: & PrdMap< Option > - ) -> Self { - macro_rules! if_defined { - ($pred:ident then |$def:ident| $stuff:expr) => ( - if let Some($def) = pred_terms[* $pred].as_ref() { - $stuff - } else { - for (ref idx, ref $def) in model { - if idx == $pred { $stuff } - } - } - ) - } - - match self { - TTerms::True => TTerms::True, - TTerms::False => TTerms::False, - - TTerms::Conj { quant, mut tterms } => { - let mut to_rm = PrdSet::new() ; - - for pred in tterms.preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(true) => { to_rm.insert(* pred) ; () }, - Some(false) => return TTerms::fls(), - None => (), - } - } - } - - for pred in to_rm { - let value = tterms.preds.remove(& pred) ; - debug_assert!( value.is_some() ) - } - - TTerms::Conj { quant, tterms }.simplify() - }, - - TTerms::Disj { quant, mut tterms, mut neg_preds } => { - let mut to_rm = PrdSet::new() ; - - for pred in tterms.preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(false) => { to_rm.insert(* pred) ; () }, - Some(true) => return TTerms::tru(), - None => (), - } - } - } - - for pred in to_rm.drain() { - let value = tterms.preds.remove(& pred) ; - debug_assert!( value.is_some() ) - } - - for pred in neg_preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(true) => { to_rm.insert(* pred) ; () }, - Some(false) => return TTerms::tru(), - None => (), - } - } - } - - for pred in to_rm.drain() { - let value = neg_preds.remove(& pred) ; - debug_assert!( value.is_some() ) - } - - TTerms::Disj { quant, tterms, neg_preds }.simplify() - }, - - TTerms::Dnf { disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - - for (quant, tterms) in disj { - match ( - TTerms::Conj { quant, tterms } - ).simplify_pred_apps(model, pred_terms) { - TTerms::True => return TTerms::tru(), - TTerms::False => (), - - TTerms::Conj { quant, tterms } => nu_disj.push( - (quant, tterms) - ), - - TTerms::Disj { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a disjunction, unreachable" - ), - TTerms::Dnf { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a DNF, unreachable" - ), - } - } - - TTerms::Dnf{ disj: nu_disj }.simplify() - }, - } - } - - - /// Writes some top terms using special functions for writing predicates and - /// variables. - pub fn write( - & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - - macro_rules! write_conj { - ($quant:expr, $tterms:expr) => ({ - let close_quant = if let Some(quant) = $quant.as_ref() { - write!(w, "(") ? ; - quant.write(w, & write_var) ? ; - write!(w, " ") ? ; - true - } else { false } ; - - let close_and = if $tterms.len() > 1 { - write!(w, "(and ") ? ; - true - } else { false } ; - - $tterms.write(w, " ", & write_var, & write_prd) ? ; - - if close_and { write!(w, ")") ? } - - if close_quant { write!(w, ")") } else { Ok(()) } - }) ; - } - - match * self { - TTerms::True => return write!(w, "true"), - TTerms::False => return write!(w, "false"), - - TTerms::Conj { ref quant, ref tterms } => write_conj!(quant, tterms), - - TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { - let close_quant = if let Some(quant) = quant.as_ref() { - write!(w, "(") ? ; - quant.write(w, & write_var) ? ; - write!(w, " ") ? ; - true - } else { false } ; - - let close_or = if tterms.len() + neg_preds.len() > 1 { - write!(w, "(or ") ? ; - true - } else { false } ; - - tterms.write(w, " ", & write_var, & write_prd) ? ; - - let mut sep = ! tterms.is_empty() && ! neg_preds.is_empty() ; - - for (pred, argss) in neg_preds { - for args in argss { - if sep { - write!(w, " ") ? - } else { - sep = true - } - write!(w, "(not ") ? ; - write_prd(w, * pred, args) ? ; - write!(w, ")") ? - } - } - - if close_or { write!(w, ")") ? } - - if close_quant { write!(w, ")") } else { Ok(()) } - }, - - TTerms::Dnf { ref disj } => { - let close_or = if disj.len() > 1 { - write!(w, "(or") ? ; - true - } else { false } ; - - for & (ref quant, ref tterms) in disj { - write!(w, " ") ? ; - write_conj!(quant, tterms) ? - } - - if close_or { write!(w, ")") } else { Ok(()) } - }, - } - } - - /// Writes some top terms smt2 style using a special function for writing - /// predicates. - /// - /// Equivalent to `write` with variable default printing. - pub fn write_smt2( - & self, w: & mut W, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - self.write( - w, |w, var| var.default_write(w), write_prd - ) - } -} - -impl<'a, 'b> ::rsmt2::print::Expr2Smt< - & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdMap< ::instance::info::PrdInfo >) -> for TTerms { - fn expr_to_smt2( - & self, w: & mut Writer, info: & 'b ( - & 'a PrdSet, & 'a PrdSet, & 'a PrdMap<::instance::info::PrdInfo> - ) - ) -> SmtRes<()> { - let (true_preds, false_preds, pred_info) = * info ; - self.write_smt2( - w, |w, pred, args| { - if true_preds.contains(& pred) { - write!(w, "true") - } else if false_preds.contains(& pred) { - write!(w, "false") - } else if args.is_empty() { - write!(w, "{}", pred_info[pred]) - } else { - write!(w, "({}", pred_info[pred]) ? ; - for arg in args.iter() { - write!(w, " ") ? ; - arg.write(w, |w, var| var.default_write(w)) ? - } - write!(w, ")") - } - } - ) ? ; - Ok(()) - } -} - - - -/// Existential or universal quantifier. -/// -/// Always use the constructors to avoid falsifying the invariant. -/// -/// # Invariant -/// -/// The variable partial maps are never empty. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Quant { - /// Exists. - Exists( VarHMap ), - /// Forall. - Forall( VarHMap ), -} -impl Quant { - /// Creates an existential qualifier. - pub fn exists(map: VarHMap) -> Option { - if map.is_empty() { None } else { Some( Quant::Exists(map) ) } - } - /// Creates an existential qualifier. - pub fn forall(map: VarHMap) -> Option { - if map.is_empty() { None } else { Some( Quant::Forall(map) ) } - } - - /// Quantified variables. - pub fn vars(& self) -> & VarHMap { - match * self { - Quant::Exists(ref vars) => vars, - Quant::Forall(ref vars) => vars, - } - } - - /// Quantified variables (mutable version). - pub fn vars_mut(& mut self) -> & mut VarHMap { - match * self { - Quant::Exists(ref mut vars) => vars, - Quant::Forall(ref mut vars) => vars, - } - } - - /// Number of quantified variables. - pub fn len(& self) -> usize { - let map = match * self { - Quant::Exists(ref map) => map, - Quant::Forall(ref map) => map, - } ; - debug_assert! { ! map.is_empty() } - map.len() - } - - /// True if there are no quantified variables. - pub fn is_empty(& self) -> bool { self.len() == 0 } - - /// Filters some quantified variables. - /// - /// Keeps all quantified variables such that `f(var)`. - /// - /// Returns `None` if the mapping ends up being empty. - pub fn filter(mut self, f: F) -> Option - where F: Fn(& VarIdx) -> bool { - self.vars_mut().retain( |var, _| f(var) ) ; - if self.vars().is_empty() { None } else { Some(self) } - } - - /// Writes the quantier and its quantified variables. - pub fn write( - & self, w: & mut W, write_var: WVar - ) -> IoRes<()> - where W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { - debug_assert!( ! self.vars().is_empty() ) ; - - let qvars = match * self { - Quant::Exists(ref qvars) => { - write!(w, "exists ") ? ; - qvars - }, - Quant::Forall(ref qvars) => { - write!(w, "forall ") ? ; - qvars - }, - } ; - - write!(w, "(") ? ; - for (var, typ) in qvars { - write!(w, " (") ? ; - write_var(w, * var) ? ; - write!(w, " {})", typ) ? ; - } - write!(w, " )") - } - - /// Writes the opening part of the quantifier as a line. - /// - /// Basically `"{}( ( )\n", prefix`. - pub fn write_pref( - & self, w: & mut W, pref: & str, write_var: WVar - ) -> IoRes<()> - where - W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { - w.write_all( pref.as_bytes() ) ? ; - let map = match * self { - Quant::Exists(ref map) => { - write!(w, "(exists (") ? ; - map - }, - Quant::Forall(ref map) => { - write!(w, "(forall (") ? ; - map - }, - } ; - for (var, typ) in map { - write!(w, " (") ? ; - write_var(w, * var) ? ; - write!(w, " {})", typ) ? - } - writeln!(w, " )") - } -} - - -/// A term type-checking error. -pub enum TypError { - /// No type info, just an error message. - Msg(String), - /// Type info: - /// - /// - the type expected (if known), - /// - the type obtained, - /// - the index of the argument that caused it. - Typ { - expected: Option, - obtained: Typ, - index: usize, - } -} -impl TypError { - /// Message constructor. - pub fn msg>(s: S) -> Self { - TypError::Msg( s.into() ) - } - - /// Type info constructor. - pub fn typ( - expected: Option, obtained: Typ, index: usize - ) -> Self { - TypError::Typ { expected, obtained, index } - } -} \ No newline at end of file diff --git a/src/term/tterms.rs b/src/term/tterms.rs new file mode 100644 index 00000000..aa23da26 --- /dev/null +++ b/src/term/tterms.rs @@ -0,0 +1,1473 @@ +//! Top-term-related types. + +use common::* ; +use var_to::terms::VarTermsSet ; + +use term::* ; + +/// Top term, as they appear in clauses. +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum TTerm { + /// A predicate application. + P { + /// Predicate applied. + pred: PrdIdx, + /// The arguments. + args: VarTerms, + }, + /// Just a term. + T(Term), +} + +impl TTerm { + /// The false top term. + pub fn fls() -> Self { + TTerm::T( term::fls() ) + } + /// The true top term. + pub fn tru() -> Self { + TTerm::T( term::tru() ) + } + + /// Type of the top term. + /// + /// Should always be bool, except during parsing. + pub fn typ(& self) -> Typ { + match * self { + TTerm::P { .. } => typ::bool(), + TTerm::T(ref term) => term.typ(), + } + } + + /// True if the top term is a term with no variables and evaluates to true. + pub fn is_true(& self) -> bool { + self.bool() == Some(true) + } + /// True if the top term is a term with no variables and evaluates to false. + pub fn is_false(& self) -> bool { + self.bool() == Some(false) + } + /// Boolean corresponding to the top term if it's a bool constant. + pub fn bool(& self) -> Option { + match * self { + TTerm::T(ref t) => t.bool(), + _ => None, + } + } + /// Boolean corresponding to the top term if it's an integer constant. + pub fn int(& self) -> Option { + match * self { + TTerm::T(ref t) => t.int(), + _ => None, + } + } + /// The operator and the kids of a top term, if it's an operator application. + pub fn app_inspect(& self) -> Option< (Op, & Vec) > { + match * self { + TTerm::T(ref t) => t.app_inspect(), + _ => None, + } + } + /// If the top term is simply a term, returns that term. + #[inline] + pub fn term(& self) -> Option<& Term> { + if let TTerm::T(ref t) = * self { Some(t) } else { None } + } + + /// The predicate a top term is an application of, if any. + pub fn pred(& self) -> Option { + match * self { + TTerm::P { pred, .. } => Some(pred), + _ => None, + } + } + + /// The arguments of a top term if it's a predicate application. + pub fn args(& self) -> Option<& VarTerms> { + match * self { + TTerm::P { ref args, .. } => Some(args), + _ => None, + } + } + + /// Applies some treatment if the top term is a predicate application. + pub fn pred_app_fold(& mut self, init: T, f: F) -> T + where F: Fn(T, PrdIdx, & mut VarTerms) -> T { + if let TTerm::P { pred, ref mut args } = * self { + f(init, pred, args) + } else { + init + } + } + + /// Variables appearing in a top term. + pub fn vars(& self) -> VarSet { + match * self { + TTerm::P { ref args, .. } => { + let mut vars = VarSet::with_capacity(17) ; + for term in args.iter() { + vars.extend( term::vars(term) ) + } + vars + }, + TTerm::T(ref term) => term::vars(term), + } + } + + /// In-place variable substitution for top terms. + /// + /// Used for substitutions in the same clause / predicate scope. + pub fn subst>( + & mut self, map: & Map + ) -> bool { + match * self { + TTerm::T(ref mut term) => { + let (t, b) = term.subst(map) ; + * term = t ; + b + }, + TTerm::P { ref mut args, .. } => { + let mut changed = false ; + let mut nu_args = VarMap::with_capacity( args.len() ) ; + for arg in args.iter() { + let (t, b) = arg.subst(map) ; + nu_args.push(t) ; + changed = changed || b + } + * args = nu_args.into() ; + changed + }, + } + } + + /// Total variable substitution for top terms. + /// + /// Used for substitutions in different clause / predicate scope. + pub fn subst_total>( + & self, map: & Map + ) -> Res { + match * self { + TTerm::P { pred, ref args } => { + let mut new_args = VarMap::with_capacity( args.len() ) ; + for term in args.iter() { + if let Some((term, _)) = term.subst_total(map) { + new_args.push(term) + } else { + bail!("total substitution failed (predicate)") + } + } + Ok( TTerm::P { pred, args: new_args.into() } ) + }, + TTerm::T(ref term) => if let Some((term, _)) = term.subst_total(map) { + Ok( TTerm::T(term) ) + } else { + bail!("total substitution failed (term)") + }, + } + } + + /// Writes a top term using special functions for writing predicates and + /// variables. + pub fn write( + & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + use self::TTerm::* ; + match * self { + P { pred, ref args } => write_prd(w, pred, args), + T(ref t) => t.write(w, write_var), + } + } + + /// Writes a top term smt2 style using a special function for writing + /// predicates. + pub fn write_smt2( + & self, w: & mut W, write_prd: WritePrd + ) -> IoRes<()> + where + W: Write, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + self.write( + w, |w, var| var.default_write(w), write_prd + ) + } +} +impl_fmt!{ + TTerm(self, fmt) { + match * self { + TTerm::P { pred, ref args } => { + write!(fmt, "(p_{}", pred) ? ; + for arg in args.iter() { + write!(fmt, " {}", arg) ? + } + write!(fmt, ")") + }, + TTerm::T(ref t) => write!(fmt, "{}", t), + } + } +} + + + + + + + +/// A *set* of top terms. +/// +/// Actually contains a set of `Term`s and a map from predicates to their +/// arguments. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct TTermSet { + /// Set of terms. + terms: TermSet, + /// Predicate applications. + preds: PrdHMap< VarTermsSet >, +} +impl TTermSet { + /// Creates a new top term set with some capacity + #[inline] + pub fn with_capacity(capa: usize) -> Self { + TTermSet { + terms: TermSet::with_capacity(capa), + preds: PrdHMap::with_capacity(capa), + } + } + /// Creates a new top term set with some capacities + #[inline] + pub fn with_capacities(term_capa: usize, pred_capa: usize) -> Self { + TTermSet { + terms: TermSet::with_capacity(term_capa), + preds: PrdHMap::with_capacity(pred_capa), + } + } + + /// Destroys the set. + #[inline] + pub fn destroy(self) -> (TermSet, PrdHMap) { + (self.terms, self.preds) + } + + /// Reserves some space. + #[inline] + pub fn reserve(& mut self, terms: usize, preds: usize) { + self.terms.reserve(terms) ; + self.preds.reserve(preds) + } + + /// Creates an empty top term set. + #[inline] + pub fn new() -> Self { Self::with_capacity(5) } + + /// Creates a top term set from a set of terms. + #[inline] + pub fn of_terms(terms: TermSet, pred_capa: usize) -> Self { + TTermSet { + terms, preds: PrdHMap::with_capacity(pred_capa), + } + } + + /// True iff the set is empty. + #[inline] + pub fn is_empty(& self) -> bool { + self.terms.is_empty() && self.preds.is_empty() + } + + /// Number of elements. + #[inline] + pub fn len(& self) -> usize { + let mut len = self.terms.len() ; + for (_, argss) in & self.preds { + len += argss.len() + } + len + } + + /// Terms. + #[inline] + pub fn terms(& self) -> & TermSet { + & self.terms + } + /// Predicate applications. + #[inline] + pub fn preds(& self) -> & PrdHMap< VarTermsSet > { + & self.preds + } + + /// Terms (mutable version). + #[inline] + pub fn terms_mut(& mut self) -> & mut TermSet { + & mut self.terms + } + /// Predicate applications (mutable version). + #[inline] + pub fn preds_mut(& mut self) -> & mut PrdHMap< VarTermsSet > { + & mut self.preds + } + + /// True if `self` is a subset of `that`. + #[inline] + pub fn is_subset_of(& self, that: & Self) -> bool { + // `terms` subset? + if ! self.terms.is_subset(& that.terms) { return false } + // All predicates in `that` also appear in `self`? + for (pred, _) in & that.preds { + if ! self.preds.contains_key(pred) { + return false + } + } + // All applications in `self` also in `that`? + for (pred, self_set) in & self.preds { + if let Some(that_set) = that.preds.get(pred) { + if ! self_set.is_subset(that_set) { return false } + } else { + return false + } + } + true + } + + /// Variable substitution. + pub fn subst>(& self, map: & Map) -> Self { + let mut terms = TermSet::with_capacity(self.terms.len()) ; + for term in self.terms() { + let (term, _) = term.subst(map) ; + terms.insert(term) ; + } + + let mut preds = PrdHMap::with_capacity( self.preds.len() ) ; + for (pred, argss) in self.preds.iter() { + let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; + for args in argss { + let args = var_to::terms::new( args.subst(map) ) ; + nu_argss.insert(args) ; + } + preds.insert(* pred, nu_argss) ; + } + + TTermSet { terms, preds } + } + + /// Inserts a predicate application. + #[inline] + pub fn insert_pred_app(& mut self, pred: PrdIdx, args: VarTerms) -> bool { + self.preds.entry(pred).or_insert_with(VarTermsSet::new).insert(args) + } + /// Inserts some predicate applications. + pub fn insert_pred_apps( + & mut self, pred: PrdIdx, argss: TArgss + ) + where + Iter: Iterator + ExactSizeIterator, + TArgss: IntoIterator { + let argss = argss.into_iter() ; + if argss.len() == 0 { return () } + self.preds.entry(pred).or_insert_with(VarTermsSet::new).extend( argss ) + } + + /// Inserts a term. + #[inline] + pub fn insert_term(& mut self, term: Term) -> bool { + self.terms.insert(term) + } + /// Inserts some terms. + #[inline] + pub fn insert_terms(& mut self, terms: Terms) + where + Iter: Iterator + ExactSizeIterator, + Terms: IntoIterator { + let terms = terms.into_iter() ; + self.terms.reserve( terms.len() ) ; + for term in terms { self.terms.insert(term) ; () } + } + + /// Inserts a top term. + pub fn insert_tterm(& mut self, tterm: TTerm) -> bool { + match tterm { + TTerm::T(term) => self.insert_term(term), + TTerm::P { pred, args } => self.insert_pred_app(pred, args) + } + } + + /// Constructor from some top terms. + pub fn of_tterms(tterms: TTs) -> Self + where + Iter: Iterator + ExactSizeIterator, + TTs: IntoIterator { + let tterms = tterms.into_iter() ; + let mut slf = Self::with_capacity( tterms.len() ) ; + for tterm in tterms { + slf.insert_tterm(tterm) ; () + } + slf + } + + /// Constructor from a single term. + pub fn of_term(term: Term) -> Self { + let mut slf = Self::new() ; + slf.insert_term(term) ; + slf + } + + /// Puts the variables appearing in the top terms in some set. + pub fn vars(& self, set: & mut VarSet) { + for term in & self.terms { + set.extend( term::vars(term) ) + } + for (_, argss) in & self.preds { + for args in argss { + for arg in args.iter() { + set.extend( term::vars(arg) ) + } + } + } + } + + /// Removes some arguments from the predicate applications. + pub fn remove_vars(& mut self, to_keep: & PrdHMap) { + remove_vars_from_pred_apps( + & mut self.preds, to_keep + ) + } + + + /// Writes all top terms with some separator. + pub fn write( + & self, w: & mut W, sep: & str, write_var: WriteVar, write_pred: WritePrd + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + // Don't print the separator the first time. + let mut separate = false ; + macro_rules! write_sep { + () => ( + if separate { + write!(w, "{}", sep) ? + } else { + separate = true + } + ) ; + } + + for term in & self.terms { + write_sep!() ; + term.write(w, & write_var) ? + } + + for (pred, argss) in & self.preds { + for args in argss { + write_sep!() ; + write_pred(w, * pred, args) ? + } + } + + Ok(()) + } +} +impl ::std::cmp::PartialOrd for TTermSet { + fn partial_cmp(& self, other: & Self) -> Option<::std::cmp::Ordering> { + use ::std::cmp::Ordering::* ; + let mut le = true ; + let mut ge = true ; + macro_rules! check_none { + () => ( if ! le && ! ge { return None } ) + } + + for term in & self.terms { + if ! other.terms.contains(term) { le = false ; break } + } + for term in & other.terms { + if ! self.terms.contains(term) { ge = false ; break } + } + check_none! {} + + // Part of what happens in this loop is explained below. + for (pred, argss) in & self.preds { + check_none! {} + if let Some(ass) = other.preds.get(pred) { + if ! argss.is_subset(ass) { + le = false + } + if ! ass.is_subset(argss) { + ge = false + } + } else { + le = false + } + } + + // At this point we checked all predicate applications in `self`. We won't + // touch `le` anymore. + // + // The only way `ge` can change is if it is true and `other` has some + // predicates that don't appear in `self`. That's because in the previous + // loop, whenever a predicate from `self` also appears in `other`, we + // checked whether the other set of arguments is a subset of the self set + // of arguments and set `ge` to false if it's not the case. + + if ge { + for pred in other.preds.keys() { + if ! self.preds.contains_key(pred) { + ge = false ; + break + } + } + } + + match (le, ge) { + (true, true) => Some(Equal), + (true, false) => Some(Less), + (false, true) => Some(Greater), + (false, false) => None, + } + } +} + +/// Removes some arguments from some predicate applications. +fn remove_vars_from_pred_apps( + apps: & mut PrdHMap< VarTermsSet >, to_keep: & PrdHMap +) { + for (pred, argss) in apps.iter_mut() { + let vars_to_keep = if let Some(vars) = to_keep.get(pred) { + vars + } else { + continue + } ; + let mut old_argss = VarTermsSet::with_capacity( argss.len() ) ; + ::std::mem::swap( & mut old_argss, argss ) ; + for args in old_argss { + argss.insert( args.remove( vars_to_keep ) ) ; + } + } +} + + +/// A formula composed of top terms. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TTerms { + /// True. + True, + /// False. + False, + /// Conjunction. + Conj { + quant: Option, + tterms: TTermSet, + }, + /// Disjunction. + Disj { + quant: Option, + tterms: TTermSet, + neg_preds: PrdHMap< VarTermsSet >, + }, + /// Almost a DNF: a disjunction of conjunctions of `TTerm`s. + Dnf { + disj: Vec< (Option, TTermSet) > + }, +} +impl TTerms { + // pub fn inspect(& self) { + // match * self { + // TTerms::True => println!("true"), + // TTerms::False => println!("false"), + // TTerms::Conj { ref quant, ref tterms } => println!( + // "conj, {} ({})", tterms.len(), + // if let Some(q) = quant.as_ref() { + // format!("{}", q.len()) + // } else { "none".into() } + // ), + // TTerms::Disj { ref quant, ref tterms, ref neg_preds } => println!( + // "conj, {}, {}, {}", tterms.len(), neg_preds.len(), + // if let Some(q) = quant.as_ref() { + // format!("{}", q.len()) + // } else { "none".into() } + // ), + // TTerms::Dnf { ref disj } => println!( + // "dnf, {}", disj.len() + // ), + // } + // } + + /// True. + #[inline] + pub fn tru() -> Self { TTerms::True } + /// False. + #[inline] + pub fn fls() -> Self { TTerms::False } + /// Constructor from a boolean. + #[inline] + pub fn of_bool(b: bool) -> Self { + if b { Self::tru() } else { Self::fls() } + } + + /// Attempts to transform some terms in a term. + pub fn to_term(& self) -> Option { + match * self { + TTerms::True => Some( term::tru() ), + TTerms::False => Some( term::fls() ), + + TTerms::Conj { ref quant, .. } if quant.is_some() => None, + TTerms::Conj { ref tterms, .. } if ! tterms.preds.is_empty() => None, + TTerms::Conj { ref tterms, .. } => Some( + term::and( + tterms.terms().iter().cloned().collect() + ) + ), + + TTerms::Disj { ref quant, .. } if quant.is_some() => None, + TTerms::Disj { + ref tterms, ref neg_preds, .. + } if ! tterms.preds.is_empty() || ! neg_preds.is_empty() => None, + TTerms::Disj { ref tterms, .. } => Some( + term::or( + tterms.terms().iter().cloned().collect() + ) + ), + + TTerms::Dnf { ref disj } => { + let mut disj_terms = Vec::with_capacity( disj.len() ) ; + for & (ref quant, ref conj) in disj { + if quant.is_some() + || ! conj.preds.is_empty() { return None } + disj_terms.push( + term::and( + conj.terms().iter().cloned().collect() + ) + ) + } + Some( term::or(disj_terms) ) + }, + } + } + + /// Boolean value of some top terms. + #[inline] + pub fn bool(& self) -> Option { + match * self { + TTerms::True => Some(true), + TTerms::False => Some(false), + _ => None, + } + } + + /// Removes some arguments from the predicate applications. + pub fn remove_vars(& mut self, to_keep: & PrdHMap< VarSet >) { + match * self { + TTerms::True | TTerms::False => (), + TTerms::Conj { ref mut tterms, .. } => tterms.remove_vars(to_keep), + TTerms::Disj { + ref mut tterms, ref mut neg_preds, .. + } => { + tterms.remove_vars(to_keep) ; + remove_vars_from_pred_apps(neg_preds, to_keep) + }, + TTerms::Dnf { ref mut disj } => for & mut (_, ref mut tterms) in disj { + tterms.remove_vars(to_keep) + }, + } + } + + /// Variable substitution. + pub fn subst>(& self, map: & Map) -> Self { + match * self { + TTerms::True => TTerms::True, + TTerms::False => TTerms::False, + + TTerms::Conj { ref quant, ref tterms } => { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } + TTerms::Conj { quant: quant.clone(), tterms: tterms.subst(map) } + }, + + TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } + + let mut preds = PrdHMap::with_capacity( neg_preds.len() ) ; + for (pred, argss) in neg_preds.iter() { + let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; + for args in argss { + let args = var_to::terms::new( args.subst(map) ) ; + nu_argss.insert(args) ; + } + preds.insert(* pred, nu_argss) ; + } + + TTerms::Disj { + quant: quant.clone(), + tterms: tterms.subst(map), + neg_preds: preds, + } + }, + + TTerms::Dnf { ref disj } => { + let mut nu_disj = Vec::with_capacity( disj.len() ) ; + for & (ref quant, ref tterms) in disj { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } + nu_disj.push( + (quant.clone(), tterms.subst(map)) + ) + } + TTerms::Dnf { disj: nu_disj } + }, + } + } + + /// Constructor for a single term. + pub fn of_term(quant: Option, term: Term) -> Self { + Self::conj( quant, TTermSet::of_term(term) ) + } + + /// Constructs a conjuction. + pub fn conj(quant: Option, tterms: TTermSet) -> Self { + TTerms::Conj { quant, tterms }.simplify() + } + + /// Constructs a disjuction. + pub fn disj( + quant: Option, tterms: TTermSet, neg_preds: PrdHMap + ) -> Self { + TTerms::Disj { quant, tterms, neg_preds }.simplify() + } + /// Constructs a disjunction from a positive application and some negated top + /// terms. + /// + /// This special format is exactly the one used by preprocessing. + pub fn disj_of_pos_neg( + quant: Option, pos: Option<(PrdIdx, VarTerms)>, neg: TTermSet + ) -> Self { + let TTermSet { terms, preds: neg_preds } = neg ; + let mut tterms = TTermSet::with_capacities(terms.len(), 1) ; + for term in terms { + tterms.insert_term( not(term) ) ; + } + if let Some((pred, args)) = pos { + tterms.insert_pred_app(pred, args) ; + } + TTerms::Disj { + quant, tterms, neg_preds + } + } + + /// Constructs a DNF. + pub fn dnf(disj: Vec< (Option, TTermSet) >) -> Self { + TTerms::Dnf{ disj }.simplify() + } + + /// Predicates appearing in the top terms. + pub fn preds(& self) -> PrdSet { + let mut res = PrdSet::new() ; + match * self { + TTerms::True | TTerms::False => (), + + TTerms::Conj { ref tterms, .. } => for pred in tterms.preds.keys() { + res.insert(* pred) ; + }, + TTerms::Disj { ref tterms, ref neg_preds, .. } => { + for pred in tterms.preds.keys() { + res.insert(* pred) ; + } + for pred in neg_preds.keys() { + res.insert(* pred) ; + } + }, + + TTerms::Dnf { ref disj } => for & (_, ref tterms) in disj { + for pred in tterms.preds.keys() { + res.insert(* pred) ; + } + }, + } + + res + } + + /// Constructs the disjunction of `self` and `conj` (a conjunction). + /// + /// Does not call `simplify` if it creates a dnf. + /// + /// # Error if + /// + /// - called on a `Disj` + pub fn or(self, conj: (Option, TTermSet)) -> Res { + match self { + TTerms::True => Ok(self), + TTerms::False => Ok( + TTerms::conj(conj.0, conj.1).simplify() + ), + TTerms::Conj { quant, tterms } => { + if tterms.is_subset_of(& conj.1) { + // conj => self? + Ok( TTerms::Conj { quant, tterms } ) + } else if conj.1.is_subset_of(& tterms) { + // self => conj? + Ok( + TTerms::Conj{ quant: conj.0, tterms: conj.1 }.simplify() + ) + } else { + Ok( + TTerms::dnf( + vec![ (quant, tterms), conj ] + ) + ) + } + }, + TTerms::Disj { .. } => bail!( + "TTerms: trying to call `or` on a disjunction" + ), + TTerms::Dnf { disj } => { + let mut nu_disj = Vec::with_capacity( disj.len() ) ; + let mut ignore_conj = false ; + for (quant, tterms) in disj { + use std::cmp::Ordering::* ; + match tterms.partial_cmp( & conj.1 ) { + // conj => tterms + // don't need `conj` + Some(Less) => ignore_conj = true, + // tterms => conj + // skip `tterms` + Some(Greater) => continue, + // tterms = conj + // skip `conj` + Some(Equal) => ignore_conj = true, + + None => (), + } + nu_disj.push( (quant, tterms) ) + } + + if ! ignore_conj { nu_disj.push(conj) } + + // Note that if we ignored a `tterms_1` because `tterms_1 => conj`, and + // then we saw a `tterms_2` s.t. `conj => tterms_2` so we ignored + // `conj` as well, there is no problem because implication is + // transitive. + + Ok( TTerms::Dnf { disj: nu_disj } ) + }, + } + } + + /// Simplifies a formula of top terms. + /// + /// # TODO + /// + /// - factor code inside this function, `Conj` and `Disj` are almost the + /// same, DRY + /// + /// # Warning + /// + /// This function recurses on a `Conj` in the `Dnf` case. At the time of + /// writing, this recursive call is guaranteed not to cause more recursive + /// calls. + /// + /// Be careful to maintain, or (ideally) improve, this invariant. + pub fn simplify(self) -> Self { + + match self { + + TTerms::True | TTerms::False => self, + + TTerms::Conj { quant, mut tterms } => { + tterms.preds.retain( + |_, argss| ! argss.is_empty() + ) ; + + let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; + // Used to inline conjunctions. + let mut swap = TermSet::new() ; + ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; + + 'inline_conjs: loop { + + 'inspect_conj_terms: for term in old_terms.drain() { + + // Is the term a conjunction? + if let Some(kids) = term.conj_inspect() { + for kid in kids { + swap.insert( kid.clone() ) ; + () + } + continue 'inspect_conj_terms + } + + // Term trivial? + match term.bool() { + Some(true) => continue 'inspect_conj_terms, + Some(false) => return TTerms::fls(), + None => (), + } + + // Do we also have its negation? + if tterms.terms.contains( & not( term.clone() ) ) { + return TTerms::fls() + } + + // Okay, move on. + tterms.terms.insert(term) ; + () + } + + // Keep going if `swap` is not empty. + if ! swap.is_empty() { + ::std::mem::swap( & mut old_terms, & mut swap ) ; + continue 'inline_conjs + } else { + break 'inline_conjs + } + + } + + // Only keep active quantified variables. + let quant = quant.and_then( + |quant| { + let mut active = VarSet::with_capacity( + quant.vars().len() * 2 + ) ; + tterms.vars(& mut active) ; + quant.filter(|var| active.contains(var)) + } + ) ; + + if tterms.is_empty() { + TTerms::tru() + } else { + TTerms::Conj { quant, tterms } + } + }, + + TTerms::Disj { + quant, mut tterms, mut neg_preds + } => { + tterms.preds.retain( + |_, argss| ! argss.is_empty() + ) ; + neg_preds.retain( + |_, argss| ! argss.is_empty() + ) ; + + // Do we have a predicate application and its negation? + for (pred, argss) in & tterms.preds { + if let Some(neg_argss) = neg_preds.get(pred) { + for args in argss { + if neg_argss.contains(args) { return TTerms::tru() } + } + } + } + + let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; + // Used to inline disjunctions. + let mut swap = TermSet::new() ; + ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; + + 'inline_disj: loop { + + 'inspect_disj_terms: for term in old_terms.drain() { + + // Is the term a disjunction? + if let Some(kids) = term.disj_inspect() { + for kid in kids { + swap.insert( kid.clone() ) ; + () + } + continue 'inspect_disj_terms + } + + // Term trivial? + match term.bool() { + Some(true) => return TTerms::tru(), + Some(false) => continue 'inspect_disj_terms, + None => (), + } + + // Do we also have its negation? + if tterms.terms.contains( & not( term.clone() ) ) { + return TTerms::tru() + } + + // Okay, move on. + tterms.terms.insert(term) ; + () + } + + // Keep going if `swap` is not empty. + if ! swap.is_empty() { + ::std::mem::swap( & mut old_terms, & mut swap ) ; + continue 'inline_disj + } else { + break 'inline_disj + } + + } + + // Only keep active quantified variables. + let quant = quant.and_then( + |quant| { + let mut active = VarSet::with_capacity( + quant.vars().len() * 2 + ) ; + tterms.vars(& mut active) ; + for (_, argss) in & neg_preds { + for args in argss { + for arg in args.iter() { + active.extend( term::vars(arg) ) + } + } + } + quant.filter(|var| active.contains(var)) + } + ) ; + + if tterms.is_empty() && neg_preds.is_empty() { + TTerms::fls() + } else { + TTerms::Disj { quant, tterms, neg_preds } + } + }, + + TTerms::Dnf { disj } => { + // We're cheating a bit here. Simplifying the disjuncts is done by + // constructing the corresponding `TTerms::Conj` and simplifying them. + // While this causes a recursive call, it's fine because it is + // guaranteed to be the only one. + // + // Unless something changes later in `Conj`'s simplification... + let mut nu_disj: Vec<(_, TTermSet)> = Vec::with_capacity( + disj.len() + ) ; + 'simplify_disjuncts: for (quant, tterms) in disj { + match ( TTerms::Conj { quant, tterms } ).simplify() { + TTerms::True => return TTerms::True, + TTerms::False => (), + TTerms::Conj { quant, tterms } => { + // Check with other disjuncts. + let mut cnt = 0 ; + while cnt < nu_disj.len() { + use std::cmp::Ordering::* ; + match tterms.partial_cmp(& nu_disj[cnt].1) { + None => cnt += 1, + // other disjunct => this disjunct + Some(Less) => { nu_disj.swap_remove(cnt) ; () }, + // other disjunct = this disjunct + Some(Equal) => continue 'simplify_disjuncts, + // this disjunct => other disjunct + Some(Greater) => continue 'simplify_disjuncts, + } + } + nu_disj.push( (quant, tterms) ) + }, + TTerms::Disj { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a disjunction, unreachable" + ), + TTerms::Dnf { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a DNF, unreachable" + ), + } + } + match nu_disj.len() { + 0 => TTerms::fls(), + 1 => if let Some((quant, tterms)) = nu_disj.pop() { + TTerms::Conj { quant, tterms } + } else { + unreachable!() + }, + _ => TTerms::Dnf { disj: nu_disj } + } + }, + } + } + + + /// Simplifies some top terms given some definitions for the predicates. + pub fn simplify_pred_apps( + self, model: ModelRef, pred_terms: & PrdMap< Option > + ) -> Self { + macro_rules! if_defined { + ($pred:ident then |$def:ident| $stuff:expr) => ( + if let Some($def) = pred_terms[* $pred].as_ref() { + $stuff + } else { + for (ref idx, ref $def) in model { + if idx == $pred { $stuff } + } + } + ) + } + + match self { + TTerms::True => TTerms::True, + TTerms::False => TTerms::False, + + TTerms::Conj { quant, mut tterms } => { + let mut to_rm = PrdSet::new() ; + + for pred in tterms.preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(true) => { to_rm.insert(* pred) ; () }, + Some(false) => return TTerms::fls(), + None => (), + } + } + } + + for pred in to_rm { + let value = tterms.preds.remove(& pred) ; + debug_assert!( value.is_some() ) + } + + TTerms::Conj { quant, tterms }.simplify() + }, + + TTerms::Disj { quant, mut tterms, mut neg_preds } => { + let mut to_rm = PrdSet::new() ; + + for pred in tterms.preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(false) => { to_rm.insert(* pred) ; () }, + Some(true) => return TTerms::tru(), + None => (), + } + } + } + + for pred in to_rm.drain() { + let value = tterms.preds.remove(& pred) ; + debug_assert!( value.is_some() ) + } + + for pred in neg_preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(true) => { to_rm.insert(* pred) ; () }, + Some(false) => return TTerms::tru(), + None => (), + } + } + } + + for pred in to_rm.drain() { + let value = neg_preds.remove(& pred) ; + debug_assert!( value.is_some() ) + } + + TTerms::Disj { quant, tterms, neg_preds }.simplify() + }, + + TTerms::Dnf { disj } => { + let mut nu_disj = Vec::with_capacity( disj.len() ) ; + + for (quant, tterms) in disj { + match ( + TTerms::Conj { quant, tterms } + ).simplify_pred_apps(model, pred_terms) { + TTerms::True => return TTerms::tru(), + TTerms::False => (), + + TTerms::Conj { quant, tterms } => nu_disj.push( + (quant, tterms) + ), + + TTerms::Disj { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a disjunction, unreachable" + ), + TTerms::Dnf { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a DNF, unreachable" + ), + } + } + + TTerms::Dnf{ disj: nu_disj }.simplify() + }, + } + } + + + /// Writes some top terms using special functions for writing predicates and + /// variables. + pub fn write( + & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + + macro_rules! write_conj { + ($quant:expr, $tterms:expr) => ({ + let close_quant = if let Some(quant) = $quant.as_ref() { + write!(w, "(") ? ; + quant.write(w, & write_var) ? ; + write!(w, " ") ? ; + true + } else { false } ; + + let close_and = if $tterms.len() > 1 { + write!(w, "(and ") ? ; + true + } else { false } ; + + $tterms.write(w, " ", & write_var, & write_prd) ? ; + + if close_and { write!(w, ")") ? } + + if close_quant { write!(w, ")") } else { Ok(()) } + }) ; + } + + match * self { + TTerms::True => return write!(w, "true"), + TTerms::False => return write!(w, "false"), + + TTerms::Conj { ref quant, ref tterms } => write_conj!(quant, tterms), + + TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { + let close_quant = if let Some(quant) = quant.as_ref() { + write!(w, "(") ? ; + quant.write(w, & write_var) ? ; + write!(w, " ") ? ; + true + } else { false } ; + + let close_or = if tterms.len() + neg_preds.len() > 1 { + write!(w, "(or ") ? ; + true + } else { false } ; + + tterms.write(w, " ", & write_var, & write_prd) ? ; + + let mut sep = ! tterms.is_empty() && ! neg_preds.is_empty() ; + + for (pred, argss) in neg_preds { + for args in argss { + if sep { + write!(w, " ") ? + } else { + sep = true + } + write!(w, "(not ") ? ; + write_prd(w, * pred, args) ? ; + write!(w, ")") ? + } + } + + if close_or { write!(w, ")") ? } + + if close_quant { write!(w, ")") } else { Ok(()) } + }, + + TTerms::Dnf { ref disj } => { + let close_or = if disj.len() > 1 { + write!(w, "(or") ? ; + true + } else { false } ; + + for & (ref quant, ref tterms) in disj { + write!(w, " ") ? ; + write_conj!(quant, tterms) ? + } + + if close_or { write!(w, ")") } else { Ok(()) } + }, + } + } + + /// Writes some top terms smt2 style using a special function for writing + /// predicates. + /// + /// Equivalent to `write` with variable default printing. + pub fn write_smt2( + & self, w: & mut W, write_prd: WritePrd + ) -> IoRes<()> + where + W: Write, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + self.write( + w, |w, var| var.default_write(w), write_prd + ) + } +} + +impl<'a, 'b> ::rsmt2::print::Expr2Smt< + & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdMap< ::instance::info::PrdInfo >) +> for TTerms { + fn expr_to_smt2( + & self, w: & mut Writer, info: & 'b ( + & 'a PrdSet, & 'a PrdSet, & 'a PrdMap<::instance::info::PrdInfo> + ) + ) -> SmtRes<()> { + let (true_preds, false_preds, pred_info) = * info ; + self.write_smt2( + w, |w, pred, args| { + if true_preds.contains(& pred) { + write!(w, "true") + } else if false_preds.contains(& pred) { + write!(w, "false") + } else if args.is_empty() { + write!(w, "{}", pred_info[pred]) + } else { + write!(w, "({}", pred_info[pred]) ? ; + for arg in args.iter() { + write!(w, " ") ? ; + arg.write(w, |w, var| var.default_write(w)) ? + } + write!(w, ")") + } + } + ) ? ; + Ok(()) + } +} + + + +/// Existential or universal quantifier. +/// +/// Always use the constructors to avoid falsifying the invariant. +/// +/// # Invariant +/// +/// The variable partial maps are never empty. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Quant { + /// Exists. + Exists( VarHMap ), + /// Forall. + Forall( VarHMap ), +} +impl Quant { + /// Creates an existential qualifier. + pub fn exists(map: VarHMap) -> Option { + if map.is_empty() { None } else { Some( Quant::Exists(map) ) } + } + /// Creates an existential qualifier. + pub fn forall(map: VarHMap) -> Option { + if map.is_empty() { None } else { Some( Quant::Forall(map) ) } + } + + /// Quantified variables. + pub fn vars(& self) -> & VarHMap { + match * self { + Quant::Exists(ref vars) => vars, + Quant::Forall(ref vars) => vars, + } + } + + /// Quantified variables (mutable version). + pub fn vars_mut(& mut self) -> & mut VarHMap { + match * self { + Quant::Exists(ref mut vars) => vars, + Quant::Forall(ref mut vars) => vars, + } + } + + /// Number of quantified variables. + pub fn len(& self) -> usize { + let map = match * self { + Quant::Exists(ref map) => map, + Quant::Forall(ref map) => map, + } ; + debug_assert! { ! map.is_empty() } + map.len() + } + + /// True if there are no quantified variables. + pub fn is_empty(& self) -> bool { self.len() == 0 } + + /// Filters some quantified variables. + /// + /// Keeps all quantified variables such that `f(var)`. + /// + /// Returns `None` if the mapping ends up being empty. + pub fn filter(mut self, f: F) -> Option + where F: Fn(& VarIdx) -> bool { + self.vars_mut().retain( |var, _| f(var) ) ; + if self.vars().is_empty() { None } else { Some(self) } + } + + /// Writes the quantier and its quantified variables. + pub fn write( + & self, w: & mut W, write_var: WVar + ) -> IoRes<()> + where W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { + debug_assert!( ! self.vars().is_empty() ) ; + + let qvars = match * self { + Quant::Exists(ref qvars) => { + write!(w, "exists ") ? ; + qvars + }, + Quant::Forall(ref qvars) => { + write!(w, "forall ") ? ; + qvars + }, + } ; + + write!(w, "(") ? ; + for (var, typ) in qvars { + write!(w, " (") ? ; + write_var(w, * var) ? ; + write!(w, " {})", typ) ? ; + } + write!(w, " )") + } + + /// Writes the opening part of the quantifier as a line. + /// + /// Basically `"{}( ( )\n", prefix`. + pub fn write_pref( + & self, w: & mut W, pref: & str, write_var: WVar + ) -> IoRes<()> + where + W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { + w.write_all( pref.as_bytes() ) ? ; + let map = match * self { + Quant::Exists(ref map) => { + write!(w, "(exists (") ? ; + map + }, + Quant::Forall(ref map) => { + write!(w, "(forall (") ? ; + map + }, + } ; + for (var, typ) in map { + write!(w, " (") ? ; + write_var(w, * var) ? ; + write!(w, " {})", typ) ? + } + writeln!(w, " )") + } +} \ No newline at end of file diff --git a/src/term/typ.rs b/src/term/typ.rs index 37788142..476f2729 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -355,9 +355,13 @@ impl_fmt!{ RTyp::DTyp { ref dtyp, ref prms } => { stack.push((typs, sep, end)) ; - write!(fmt, "({}", dtyp.name) ? ; - let typs: Vec<_> = prms.iter().map(|typ| typ.get()).collect() ; - stack.push( (typs.into_iter(), " ", ")") ) ; + if prms.is_empty() { + write!(fmt, "{}", dtyp.name) ? ; + } else { + write!(fmt, "({}", dtyp.name) ? ; + let typs: Vec<_> = prms.iter().map(|typ| typ.get()).collect() ; + stack.push( (typs.into_iter(), " ", ")") ) ; + } continue 'stack }, diff --git a/src/term/zip.rs b/src/term/zip.rs new file mode 100644 index 00000000..49404a13 --- /dev/null +++ b/src/term/zip.rs @@ -0,0 +1,41 @@ +//! Types for zipping over terms. + +use common::* ; + +use std::slice::Iter ; + +/// Zip info for terms. +#[allow(dead_code)] +enum TermDer<'a, Info> { + /// Constant array. + CArray, + + /// Operator application. + App { + /// Operator. + op: Op, + /// Info already processed. + lft_args: Info, + /// Kids left to process. + rgt_args: Iter<'a, Term>, + }, + + /// Datatype constructor. + DTypNew { + /// Type of the application. + typ: Typ, + /// Name of the constructor. + name: String, + /// Kids already processed. + lft_args: Info, + /// Kids left to process. + rgt_args: Iter<'a, Term>, + } +} + + +// /// Zips over a term. +// pub fn zip( +// term: & Term, +// cst +// ) diff --git a/src/val/mod.rs b/src/val/mod.rs index 5c8bfd2d..411a718e 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -157,6 +157,17 @@ pub enum RVal { I(Int), /// Real value (actually a rational). R(Rat), + + /// Datatype constructor. + DTypNew { + /// Type of the value. + typ: Typ, + /// Constructor. + name: String, + /// Arguments. + args: Vec, + }, + /// An array is a total function. /// /// The `vals` field encodes a sequence of if-then-else's. @@ -182,6 +193,8 @@ pub enum RVal { }, } + + impl Into for RVal { fn into(self) -> Val { factory.mk(self) @@ -343,6 +356,7 @@ impl RVal { Array { ref idx_typ, ref default, .. } => typ::array( idx_typ.clone(), default.typ() ), + DTypNew{ ref typ, .. } => typ.clone(), N(ref typ) => typ.clone() } } @@ -512,6 +526,19 @@ impl RVal { } Some(res) }, + RVal::DTypNew { ref name, ref typ, ref args } => { + let mut t_args = Vec::with_capacity( args.len() ) ; + for arg in args { + if let Some(t_arg) = arg.to_term() { + t_args.push(t_arg) + } else { + return None + } + } + Some( + term::dtyp_new( typ.clone(), name.clone(), t_args ) + ) + }, } } @@ -1381,6 +1408,15 @@ impl_fmt!{ } Ok(()) }, + RVal::DTypNew { ref name, ref args, .. } => if args.is_empty() { + write!(fmt, "{}", name) + } else { + write!(fmt, "({}", name) ? ; + for arg in args { + write!(fmt, " {}", arg) ? + } + write!(fmt, ")") + }, } } } From 5601d6efde906ded884a8680896ada6e565daf42 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 2 Jul 2018 20:05:45 +0900 Subject: [PATCH 06/94] term zippin', term leaf iter --- src/term/factory.rs | 18 ++- src/term/leaf_iter.rs | 90 +++++++++++++ src/term/mod.rs | 289 ++++++++++++++++++++---------------------- src/term/zip.rs | 41 ------ src/term/zipper.rs | 266 ++++++++++++++++++++++++++++++++++++++ src/val/mod.rs | 14 +- 6 files changed, 520 insertions(+), 198 deletions(-) create mode 100644 src/term/leaf_iter.rs delete mode 100644 src/term/zip.rs create mode 100644 src/term/zipper.rs diff --git a/src/term/factory.rs b/src/term/factory.rs index e8ba889c..1f5f5144 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -116,6 +116,16 @@ pub fn bool_var>(v: V) -> Term { factory.mk( RTerm::Var(typ::bool(), v.into()) ) } +/// Creates a constant. +#[inline] +pub fn cst>(val: V) -> Term { + let val = val.into() ; + if ! val.is_known() { + panic!("trying to construct a constant term from a non-value {}", val) + } + factory.mk( RTerm::Cst( val ) ) +} + /// Creates an integer constant. #[inline(always)] pub fn int>(i: I) -> Term { @@ -203,7 +213,13 @@ pub fn and(terms: Vec) -> Term { /// The type is the type of **the indices** of the array. #[inline] pub fn cst_array(typ: Typ, default: Term) -> Term { - factory.mk( RTerm::CArray { typ, term: Box::new(default) } ) + if let Some(val) = default.val() { + factory.mk( + RTerm::Cst( val::array(typ, val) ) + ) + } else { + factory.mk( RTerm::CArray { typ, term: Box::new(default) } ) + } } /// Store operation in an array. diff --git a/src/term/leaf_iter.rs b/src/term/leaf_iter.rs new file mode 100644 index 00000000..2c1fb4f6 --- /dev/null +++ b/src/term/leaf_iter.rs @@ -0,0 +1,90 @@ +//! Iterator over all the leaves in a term. + +use std::slice::Iter ; + +use common::* ; + +/// Iterator over all the leaves in a term. +pub struct LeafIter<'a> { + /// Stack of subterms to look at. + stack: Vec< Iter<'a, Term> >, + /// Original starting term if this is the first iteration. None otherwise. + term: Option<& 'a RTerm>, +} + +impl<'a> LeafIter<'a> { + /// Constructor. + pub fn of_rterm(term: & 'a RTerm) -> Self { + LeafIter { stack: Vec::with_capacity(11), term: Some(term) } + } +} + +impl<'a> Iterator for LeafIter<'a> { + type Item = Either< (& 'a Typ, VarIdx), & 'a Val > ; + + fn next(& mut self) -> Option { + + 'find_next: loop { + + let mut current = if let Some(term) = ::std::mem::replace( + & mut self.term, None + ) { + // First iteration. + term + } else { + loop { + // Something in the stack? + if let Some(mut iter) = self.stack.last_mut() { + // Is there something in `iter`? + if let Some(term) = iter.next() { + // Use that + break term.get() + } else { + // Keep going, stack will be popped below. + } + } else { + return None + } + + // Only reachable if + // - stack is not empty, and + // there was nothing in the last element of the stack. + let iter = self.stack.pop() ; + debug_assert_eq! { + iter.map( + |mut iter| iter.next().map( + |_| "iter non-empty" + ).unwrap_or("ok") + ).unwrap_or("stack was empty") , "ok" + } + } + } ; + + use term::RTerm::* ; + + 'go_down: loop { + + let next = match * current { + Var(ref typ, var) => Either::Left( (typ, var) ), + Cst(ref val) => Either::Right(val), + + CArray { ref term, .. } => { + current = term.get() ; + continue 'go_down + }, + + App { ref args, .. } | + DTypNew { ref args, .. } => { + self.stack.push( args.iter() ) ; + continue 'find_next + }, + } ; + + return Some(next) + + } + + } + + } +} \ No newline at end of file diff --git a/src/term/mod.rs b/src/term/mod.rs index bf933827..4a8d3d69 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -66,12 +66,14 @@ mod factory ; mod tterms ; pub mod simplify ; pub mod typ ; -mod zip ; +mod zipper ; +mod leaf_iter ; pub use self::op::* ; pub use self::factory::* ; pub use self::tterms::* ; pub use self::typ::Typ ; +pub use self::leaf_iter::LeafIter ; #[cfg(test)] mod test ; @@ -212,8 +214,13 @@ impl RTerm { } } + /// Iterator over over all the leafs of a term. + pub fn leaf_iter(& self) -> LeafIter { + LeafIter::of_rterm(self) + } + /// Iterates over the subterms of a term. - pub fn iter(& self, mut f: F) { + fn iter(& self, mut f: F) { let mut stack = vec![self] ; while let Some(term) = stack.pop() { @@ -522,6 +529,55 @@ impl RTerm { } } + /// Zips over a term. + /// + /// # Type parameters + /// + /// - `Info`: information extracted by the zipping process + /// - `VarF`: will run on variables + /// - `CstF`: will run on constants + /// - `AppF`: will run on the result of zipping on operator applications + /// - `ArrF`: will run on the result of zipping on arrays + /// - `NewF`: will run on the result of zipping on datatype constructors + pub fn zip( + & self, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF + ) -> Info + where + VarF: FnMut(& Typ, VarIdx) -> Info, + CstF: FnMut(& Val) -> Info, + AppF: FnMut(& Typ, Op, Vec) -> Info, + ArrF: FnMut(& Typ, Info) -> Info, + NewF: FnMut(& Typ, & String, Vec) -> Info, { + zipper::zip(self, varf, cstf, appf, arrf, newf) + } + + + + /// Zips over a term. + /// + /// Early returns **iff** any a call to one of the input functions returns an + /// error. + /// + /// # Type parameters + /// + /// - `Info`: information extracted by the zipping process + /// - `VarF`: will run on variables + /// - `CstF`: will run on constants + /// - `AppF`: will run on the result of zipping on operator applications + /// - `ArrF`: will run on the result of zipping on arrays + /// - `NewF`: will run on the result of zipping on datatype constructors + pub fn zip_res( + & self, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF + ) -> Res + where + VarF: FnMut(& Typ, VarIdx) -> Res, + CstF: FnMut(& Val) -> Res, + AppF: FnMut(& Typ, Op, Vec) -> Res, + ArrF: FnMut(& Typ, Info) -> Res, + NewF: FnMut(& Typ, & String, Vec) -> Res, { + zipper::zip_res(self, varf, cstf, appf, arrf, newf) + } + /// Term evaluation. /// @@ -529,63 +585,32 @@ impl RTerm { /// /// - remove recursive call for constant arrays pub fn eval(& self, model: & E) -> Res { - use self::RTerm::* ; - let mut current = self ; - let mut stack = vec![] ; - - 'eval: loop { - // Go down applications. - let mut evaled = match * current { - // Leaves, going up. - Var(_, v) => if v < model.len() { - model.get(v).clone() - } else { - bail!("model is too short ({})", model.len()) - }, - Cst(ref val) => val.clone(), + self.zip_res( + // Variable evaluation. + |_, v| if v < model.len() { + Ok( model.get(v).clone() ) + } else { + bail!("model is too short ({})", model.len()) + }, - // Recursive cases. + // Constant evaluation. + |val| Ok( val.clone() ), - App { op, ref args, .. } => { - current = & args[0] ; - stack.push( (op, & args[1..], vec![]) ) ; - continue 'eval - }, - - CArray { ref typ, ref term } => { - let default = term.eval(model) ? ; - val::array(typ.clone(), default) - }, + // Operator application evaluation. + |_, op, values| op.eval(values).chain_err( + || format!("while evaluating operator `{}`", op) + ), - DTypNew { .. } => bail!( - "datatype constructor evaluation is not implemented" - ), - } ; - - // Go up. - 'go_up: loop { - if let Some( (op, to_do, mut values) ) = stack.pop() { - if to_do.is_empty() { - values.push(evaled) ; - evaled = op.eval(values).chain_err( - || format!("while evaluating operator `{}`", op) - ) ? ; - continue 'go_up - } else { - // Going down the first element of `to_do`. - current = & to_do[0] ; - values.push(evaled) ; - stack.push( (op, & to_do[1..], values) ) ; - // Go down. - continue 'eval - } - } else { - // We are at the top level, done. - return Ok(evaled) - } - } + // Constant array evaluation. + |typ, default| Ok( + val::array( typ.clone(), default ) + ), - } + // Datatype evaluation. + |typ, name, values| Ok( + val::dtyp_new( typ.clone(), name.clone(), values ) + ), + ) } /// If the term's an integer constant, returns the value. @@ -609,26 +634,18 @@ impl RTerm { /// The highest variable index appearing in the term. pub fn highest_var(& self) -> Option { - let mut to_do = vec![ self ] ; let mut max = None ; - while let Some(term) = to_do.pop() { - match * term { - RTerm::Var(_, i) => max = Some( - ::std::cmp::max( i, max.unwrap_or_else(|| 0.into()) ) - ), - RTerm::Cst(_) => (), - - RTerm::CArray { ref term, .. } => to_do.push(& * term), - - RTerm::App{ ref args, .. } => for arg in args { - to_do.push(arg) - }, - RTerm::DTypNew { ref args, .. } => for arg in args { - to_do.push(arg) - }, + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + max = Some( + ::std::cmp::max( + var_idx, max.unwrap_or_else(|| 0.into()) + ) + ) } } + max } @@ -642,27 +659,13 @@ impl RTerm { /// Return true if the term mentions at least one variable from `vars`. pub fn mentions_one_of(& self, vars: & VarSet) -> bool { - let mut to_do = vec![ self ] ; - - while let Some(term) = to_do.pop() { - match * term { - RTerm::Var(_, var) => if vars.contains(& var) { + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + if vars.contains(& var_idx) { return true - }, - RTerm::Cst(_) => (), - - RTerm::CArray { ref term, .. } => to_do.push(term), - - RTerm::App { ref args, .. } => for arg in args { - to_do.push(arg) - }, - - RTerm::DTypNew { ref args, .. } => for arg in args { - to_do.push(arg) - }, + } } } - false } @@ -692,75 +695,54 @@ impl RTerm { /// `map`. /// /// The boolean returned is true if at least on substitution occured. - /// - /// # TODO - /// - /// - remove recursive call in the array case pub fn subst_custom>( & self, map: & Map, total: bool ) -> Option<(Term, bool)> { - let mut current = & self.to_hcons() ; - // Stack for traversal. - let mut stack = vec![] ; - // Number of substitutions performed. - let mut subst_count = 0 ; - - 'go_down: loop { - - // Go down. - let mut term = match * current.get() { - RTerm::Var(ref typ, var) => if let Some(term) = map.var_get(var) { - debug_assert_eq! { typ, & term.typ() } - subst_count += 1 ; - term - } else if total { - return None - } else { - current.clone() - }, - RTerm::App { op, ref args, .. } => { - current = & args[0] ; - stack.push( - (op, & args[1..], Vec::with_capacity( args.len() )) - ) ; - continue 'go_down - }, - RTerm::CArray { ref typ, ref term } => { - if let Some((term, changed)) = term.subst_custom(map, total) { - if changed { - subst_count += 1 ; - } - cst_array(typ.clone(), term) - } else { - return None - } + let mut changed = false ; + + let res = zipper::zip_custom_res( + self, + + // Variable. + |typ, var| if let Some(term) = map.var_get(var) { + debug_assert_eq! { typ, & term.typ() } + changed = true ; + Ok(term) + } else if total { + Err(()) + } else { + Ok( + term::var( var, typ.clone() ) + ) + }, - }, - RTerm::DTypNew { .. } => panic!( - "substitution in datatype constructors is not implemented" - ), - - RTerm::Cst(_) => current.clone(), - } ; - - // Go up. - 'go_up: while let Some( - (op, args, mut new_args) - ) = stack.pop() { - new_args.push( term ) ; - - if args.is_empty() { - term = app(op, new_args) ; - continue 'go_up // Just for readability - } else { - current = & args[0] ; - stack.push( (op, & args[1..], new_args) ) ; - continue 'go_down - } - } + // Constant. + |cst| Ok( + term::cst( cst.clone() ) + ), - // Only way to get here is if the stack is empty, meaning we're done. - return Some( (term, subst_count > 0) ) + // Operator application. + |_, op, args| Ok( + term::app(op, args) + ), + + // Constant array. + |typ, default| Ok( + term::cst_array( typ.clone(), default ) + ), + + // Datatype constructor. + |typ, name, args| Ok( + term::dtyp_new( + typ.clone(), name.clone(), args + ) + ), + ) ; + + if let Ok(term) = res { + Some( (term, changed) ) + } else { + None } } @@ -1013,6 +995,7 @@ impl RTerm { return None } }, + RTerm::Var(_, v) => return Some((v, solution)), RTerm::CArray { .. } | diff --git a/src/term/zip.rs b/src/term/zip.rs deleted file mode 100644 index 49404a13..00000000 --- a/src/term/zip.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Types for zipping over terms. - -use common::* ; - -use std::slice::Iter ; - -/// Zip info for terms. -#[allow(dead_code)] -enum TermDer<'a, Info> { - /// Constant array. - CArray, - - /// Operator application. - App { - /// Operator. - op: Op, - /// Info already processed. - lft_args: Info, - /// Kids left to process. - rgt_args: Iter<'a, Term>, - }, - - /// Datatype constructor. - DTypNew { - /// Type of the application. - typ: Typ, - /// Name of the constructor. - name: String, - /// Kids already processed. - lft_args: Info, - /// Kids left to process. - rgt_args: Iter<'a, Term>, - } -} - - -// /// Zips over a term. -// pub fn zip( -// term: & Term, -// cst -// ) diff --git a/src/term/zipper.rs b/src/term/zipper.rs new file mode 100644 index 00000000..c4577e3b --- /dev/null +++ b/src/term/zipper.rs @@ -0,0 +1,266 @@ +//! Types for zipping over terms. + +use common::* ; + +use std::slice::Iter ; + +/// Zip info for terms. +enum ZipInfo<'a, Info> { + /// Constant array. + Arr { + /// Type. + typ: & 'a Typ, + }, + + /// Operator application. + App { + /// Type. + typ: & 'a Typ, + /// Operator. + op: Op, + /// Info already processed. + lft_args: Vec, + /// Kids left to process. + rgt_args: Iter<'a, Term>, + }, + + /// Datatype constructor. + New { + /// Type of the application. + typ: & 'a Typ, + /// Name of the constructor. + name: & 'a String, + /// Kids already processed. + lft_args: Vec, + /// Kids left to process. + rgt_args: Iter<'a, Term>, + } +} + + +/// Zips over a term. +/// +/// Early returns **iff** any a call to one of the input functions returns an +/// error. +/// +/// # Type parameters +/// +/// - `Info`: information extracted by the zipping process +/// - `VarF`: will run on variables +/// - `CstF`: will run on constants +/// - `AppF`: will run on the result of zipping on operator applications +/// - `ArrF`: will run on the result of zipping on arrays +/// - `NewF`: will run on the result of zipping on datatype constructors +pub fn zip_res( + term: & RTerm, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF +) -> Res +where +VarF: FnMut(& Typ, VarIdx) -> Res, +CstF: FnMut(& Val) -> Res, +AppF: FnMut(& Typ, Op, Vec) -> Res, +ArrF: FnMut(& Typ, Info) -> Res, +NewF: FnMut(& Typ, & String, Vec) -> Res, { + zip_custom_res(term, varf, cstf, appf, arrf, newf) +} + + +/// Zips over a term. +/// +/// # Type parameters +/// +/// - `Info`: information extracted by the zipping process +/// - `VarF`: will run on variables +/// - `CstF`: will run on constants +/// - `AppF`: will run on the result of zipping on operator applications +/// - `ArrF`: will run on the result of zipping on arrays +/// - `NewF`: will run on the result of zipping on datatype constructors +pub fn zip( + term: & RTerm, + mut varf: VarF, mut cstf: CstF, mut appf: AppF, + mut arrf: ArrF, mut newf: NewF +) -> Info +where +VarF: FnMut(& Typ, VarIdx) -> Info, +CstF: FnMut(& Val) -> Info, +AppF: FnMut(& Typ, Op, Vec) -> Info, +ArrF: FnMut(& Typ, Info) -> Info, +NewF: FnMut(& Typ, & String, Vec) -> Info, { + zip_custom_res::< Info, (), _, _, _, _, _ >( + term, + |t,v| Ok( varf(t, v) ), + |v| Ok( cstf(v) ), + |t, o, i| Ok( appf(t, o, i) ), + |t, i| Ok( arrf(t, i) ), + |t, s, i| Ok( newf(t, s, i) ), + ).unwrap() + //^^^^^^~~~~ this unwrap is proved safe trivially. +} + + +/// Zips over a term. +/// +/// This function returns an error **iff** one of the function provided returns +/// one. +/// +/// # Type parameters +/// +/// - `Info`: information extracted by the zipping process +/// - `E`: type of errors (for early returns) +/// - `VarF`: will run on variables +/// - `CstF`: will run on constants +/// - `AppF`: will run on the result of zipping on operator applications +/// - `ArrF`: will run on the result of zipping on arrays +/// - `NewF`: will run on the result of zipping on datatype constructors +pub fn zip_custom_res( + term: & RTerm, + mut varf: VarF, mut cstf: CstF, mut appf: AppF, + mut arrf: ArrF, mut newf: NewF +) -> Result +where +VarF: FnMut(& Typ, VarIdx) -> Result, +CstF: FnMut(& Val) -> Result, +AppF: FnMut(& Typ, Op, Vec) -> Result, +ArrF: FnMut(& Typ, Info) -> Result, +NewF: FnMut(& Typ, & String, Vec) -> Result, { + use term::RTerm ; + + // Stack of stuff to zip on. + let mut stack: Vec< ZipInfo > = Vec::with_capacity(11) ; + + // Term we're currently going into. + let mut curr = term ; + + 'go_down: loop { + + // Retrieve info for `curr` if it's a leaf. Keep going down otherwise. + let mut info = match * curr { + + // Leaves (there's one more case: nullary datatype constructors, handled + // below). + + RTerm::Var(ref typ, idx) => varf(typ, idx) ?, + + RTerm::Cst(ref val) => cstf(val) ?, + + // Not a leaf, we're going into their kids and updating the stack. + + RTerm::CArray { ref typ, ref term } => { + curr = term ; + stack.push( ZipInfo::Arr { typ } ) ; + continue 'go_down + }, + + RTerm::App { ref typ, op, ref args } => { + let lft_args = Vec::with_capacity( args.len() ) ; + let mut rgt_args = args.iter() ; + + curr = rgt_args.next().expect( + "illegal nullary operator application" + ) ; + + stack.push( + ZipInfo::App { typ, op, lft_args, rgt_args } + ) ; + + continue 'go_down + }, + + RTerm::DTypNew { ref typ, ref name, ref args } => { + let lft_args = Vec::with_capacity( args.len() ) ; + let mut rgt_args = args.iter() ; + + if let Some(term) = rgt_args.next() { + // Not a nullary constructor, go down. + curr = term ; + stack.push( + ZipInfo::New { typ, name, lft_args, rgt_args } + ) ; + + continue 'go_down + + } else { + // Nullary constructor, retrieve info. + newf(typ, name, vec![]) ? + } + }, + + } ; + + + // We have the info, time to go up. + 'go_up: loop { + + match stack.pop() { + + // If none, we're done, the current info is for the original term. + None => return Ok(info), + + // Array, current info is for the default value. + Some( + ZipInfo::Arr { typ } + ) => { + // Update info and keep going up. + info = arrf(typ, info) ? ; + continue 'go_up + }, + + // Operator application, info is for the kid between `lft_args` and + // `rgt_args`. + Some( + ZipInfo::App { typ, op, mut lft_args, mut rgt_args } + ) => { + + lft_args.push(info) ; + + // Are we done with this application? + if let Some(term) = rgt_args.next() { + // Let's go down in `term`. + curr = term ; + // Don't forget to push back the zip info in the stack. + stack.push( + ZipInfo::App { typ, op, lft_args, rgt_args } + ) ; + // Go down. + continue 'go_down + + } else { + // No more term to go down into. Update info and go up. + info = appf(typ, op, lft_args) ? ; + continue 'go_up + } + + }, + + // Datatype constructor, info is for the kid between `lft_args` and + // `rgt_args`. + Some( + ZipInfo::New { typ, name, mut lft_args, mut rgt_args } + ) => { + + lft_args.push(info) ; + + // Are we done with this constructor? + if let Some(term) = rgt_args.next() { + // Let's go down in `term`. + curr = term ; + // Don't forget to push back the zip info in the stack. + stack.push( + ZipInfo::New { typ, name, lft_args, rgt_args } + ) ; + // Go down. + continue 'go_down + + } else { + // No more term to go down into. Update info and go up. + info = newf(typ, name, lft_args) ? ; + continue 'go_up + } + + }, + + } + + } + + } +} diff --git a/src/val/mod.rs b/src/val/mod.rs index 411a718e..fa68cfdb 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -122,17 +122,25 @@ pub fn array_of_fun(idx_typ: Typ, term: & Term) -> Res { /// Creates an integer value. pub fn int>(i: I) -> Val { - factory.mk(RVal::I(i.into())) + factory.mk( + RVal::I( i.into() ) + ) } /// Creates a rational value. pub fn real>(r: R) -> Val { - factory.mk(RVal::R(r.into())) + factory.mk( + RVal::R( r.into() ) + ) } /// Creates a non-value for a type. pub fn none(typ: Typ) -> Val { - factory.mk(RVal::N(typ)) + factory.mk( RVal::N(typ) ) } +/// Creates a new datatype value. +pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Val { + factory.mk( RVal::DTypNew { typ, name, args } ) +} /// Values. From dbe73ffc7976822e803d1c3ec743cac2bc77a140 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 3 Jul 2018 19:23:24 +0900 Subject: [PATCH 07/94] datatypes: parsing, terms, to solver | model parsing missing --- src/dtyp/mod.rs | 144 +++++++++- src/parse/mod.rs | 477 ++++++++++++++++++++++++-------- src/term/factory.rs | 33 ++- src/term/{zipper.rs => fold.rs} | 120 +++++--- src/term/leaf_iter.rs | 1 + src/term/mod.rs | 261 ++++++++++++++--- src/term/typ.rs | 25 +- src/val/mod.rs | 2 +- 8 files changed, 849 insertions(+), 214 deletions(-) rename src/term/{zipper.rs => fold.rs} (62%) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index c0a8ea2f..d152da42 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -65,7 +65,18 @@ impl PartialTyp { bail!("cannot unify type {} and {}", ptyp, typ) }, - _ => unimplemented!(), + ( PartialTyp::DTyp(name, _, p_prms), RTyp::DTyp { dtyp, prms } ) + if name == & dtyp.name + && p_prms.len() == prms.len() => { + for (p_typ, typ) in p_prms.iter().zip( prms.iter() ) { + stack.push( (p_typ, typ) ) + } + }, + + _ => bail!( + "cannot unify {} with ({})", + ptyp, typ + ), } } @@ -133,6 +144,94 @@ impl PartialTyp { Ok(()) } + + /// Turns a partial type into a concrete type. + pub fn to_type( + & self, prms: & TPrmMap + ) -> Result { + enum Frame<'a> { + ArrayLft(& 'a PartialTyp), + ArrayRgt(Typ), + DTyp(DTyp, TPrmMap, ::std::slice::Iter<'a, PartialTyp>), + } + + let mut curr = self ; + let mut stack = vec![] ; + + 'go_down: loop { + + let mut typ = match curr { + + PartialTyp::Array(src, tgt) => { + curr = & ** src ; + stack.push( Frame::ArrayLft(& ** tgt) ) ; + + continue 'go_down + }, + + PartialTyp::DTyp(name, pos, prms) => { + let mut nu_prms = TPrmMap::with_capacity( prms.len() ) ; + let mut prms = prms.iter() ; + + let dtyp = if let Ok(dtyp) = get(name) { + dtyp + } else { + return Err( + ( * pos, "unknown datatype".into() ) + ) + } ; + + if let Some(partial) = prms.next() { + curr = partial ; + stack.push( + Frame::DTyp( dtyp, nu_prms, prms ) + ) ; + + continue 'go_down + } else { + typ::dtyp(dtyp, nu_prms) + } + }, + + PartialTyp::Typ(typ) => typ.clone(), + + PartialTyp::Param(idx) => prms[* idx].clone(), + + } ; + + 'go_up: loop { + + match stack.pop() { + None => return Ok(typ), + + Some( Frame::ArrayLft(tgt) ) => { + curr = tgt ; + stack.push( Frame::ArrayRgt(typ) ) ; + continue 'go_down + }, + + Some( Frame::ArrayRgt(src) ) => { + typ = typ::array(src, typ) ; + continue 'go_up + }, + + Some( Frame::DTyp(dtyp, mut prms, mut to_do) ) => { + prms.push(typ) ; + if let Some(typ_to_do) = to_do.next() { + stack.push( Frame::DTyp(dtyp, prms, to_do) ) ; + curr = typ_to_do ; + continue 'go_down + } else { + typ = typ::dtyp(dtyp, prms) ; + continue 'go_up + } + }, + } + + } + + } + } } impl_fmt! { @@ -355,14 +454,10 @@ pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { pub fn type_constructor( constructor: & str, args: & [ Term ] ) -> Res< Option > { - let dtyp = if let Ok(f) = factory.read() { - if let Some(dtyp) = f.get(constructor) { - dtyp.clone() - } else { - return Ok(None) - } + let dtyp = if let Some(dtyp) = of_constructor(constructor) { + dtyp } else { - bail!("failed to access datatype factory") + return Ok(None) } ; let params = { @@ -396,6 +491,36 @@ pub fn type_constructor( +/// Types a datatype selector application. +pub fn type_selector( + selector: & str, slc_pos: ::parse::Pos, term: & Term +) -> Result { + if let Some((dtyp, prms)) = term.typ().dtyp_inspect() { + + for args in dtyp.news.values() { + for (slc, partial_typ) in args { + if slc == selector { + + return partial_typ.to_type(prms) + + } + } + } + + } + + Err( + ( + slc_pos, format!( + "cannot apply selector `{}` to term of type {}", + conf.bad(selector), term.typ() + ) + ) + ) +} + + + @@ -489,3 +614,6 @@ impl RDTyp { Ok(()) } } +impl_fmt! { + RDTyp(self, fmt) { write!(fmt, "{}", self.name) } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 565fefc9..b55a2e70 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -197,11 +197,43 @@ impl ClauseRes { } +/// The operator of an s-expression. +#[derive(Clone, Debug, PartialEq, Eq)] +enum FrameOp { + /// An actual operator. + Op(Op), + /// A constant array constructor. + CArray(Typ, Pos), + /// A cast. + Cast, + /// A datatype constructor. + DTypNew(String, DTyp), + /// A datatype selector. + DTypSlc(String), +} +impl FrameOp { + /// String representation for a frame operator. + pub fn as_str(& self) -> String { + match self { + FrameOp::Op(op) => format!("{}", op), + FrameOp::CArray(typ, _) => format!( + "array constructor for {}", typ + ), + FrameOp::Cast => "cast operator".into(), + FrameOp::DTypNew(name, typ) => format!( + "`{}` constructor ({})", name, typ + ), + FrameOp::DTypSlc(name) => format!("`{}` selector", name), + } + } +} + + /// Term stack frame used in the parser to avoid recursion. struct TermFrame { /// Operator when going up. - op: Op, + op: FrameOp, /// Position of the operator. op_pos: Pos, /// Position of the arguments. @@ -214,7 +246,7 @@ struct TermFrame { impl TermFrame { /// Constructor. pub fn new( - op: Op, op_pos: Pos, let_count: LetCount + op: FrameOp, op_pos: Pos, let_count: LetCount ) -> Self { TermFrame { op, op_pos, let_count, @@ -223,6 +255,11 @@ impl TermFrame { } } + /// True if the frame operator is a cast operation. + pub fn is_cast(& self) -> bool { + self.op == FrameOp::Cast + } + /// Pushes an argument. pub fn push_arg(& mut self, pos: Pos, arg: Term) { debug_assert_eq! { self.args_pos.len(), self.args.len() } @@ -243,7 +280,7 @@ impl TermFrame { /// Destroys the frame. pub fn destroy(self) -> ( - Op, Pos, Vec, Vec + FrameOp, Pos, Vec, Vec ) { (self.op, self.op_pos, self.args_pos, self.args) } @@ -806,7 +843,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { match stack.pop() { - Some(CTyp::Array { pos }) => if let Some(src) = typ { + Some( + CTyp::Array { pos } + ) => if let Some(src) = typ { stack.push( CTyp::ArraySrc { pos, src } ) ; // Need to parse the domain now. continue 'go_down @@ -842,7 +881,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? }, - Some(CTyp::DTyp { name, pos, mut typs }) => if let Some(prm) = typ { + Some( + CTyp::DTyp { name, pos, mut typs } + ) => if let Some(prm) = typ { typs.push(prm) ; self.ws_cmt() ; @@ -1295,6 +1336,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok(()) } + +} + + + + + + + + + +/// Let-binding-related functions. +impl<'cxt, 's> Parser<'cxt, 's> { + /// Adds a binding to the current bindings. fn insert_bind( & mut self, var: & 's str, term: PTTerms @@ -1430,8 +1485,17 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } +} + + + + + +/// Arithmetic values. +impl<'cxt, 's> Parser<'cxt, 's> { + /// Numeral parser. - fn numeral(& mut self) -> Option { + pub fn numeral(& mut self) -> Option { let start_pos = self.pos() ; if let Some(char) = self.next() { @@ -1468,7 +1532,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Decimal parser. - fn decimal(& mut self) -> Option { + pub fn decimal(& mut self) -> Option { let start_pos = self.pos() ; macro_rules! if_not_give_up { (( $($cond:tt)* ) => $thing:expr) => ( @@ -1514,40 +1578,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { num } - /// Type checks an operator application. - fn build_app(& self, frame: TermFrame) -> Res<(Term, Pos)> { - let (op, op_pos, args_pos, args) = frame.destroy() ; - - match term::try_app(op, args) { - Ok(term) => Ok((term, op_pos)), - Err( - TypError::Typ { expected, obtained, index } - ) => if let Some(exp) = expected { - err_chain! { - self.error( - args_pos[index], format!( - "expected an expression of sort {}, found {}", exp, obtained - ) - ) - => self.error(op_pos, "in this operator application") - } - } else { - err_chain! { - self.error( - args_pos[index], format!( - "expected the expression starting here has sort {} \ - which is illegal", obtained - ) - ) - => self.error(op_pos, "in this operator application") - } - } - Err( TypError::Msg(blah) ) => bail!( - self.error(op_pos, blah) - ), - } - } - /// Real parser. /// /// Decimal or fraction. @@ -1596,6 +1626,233 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok(None) } +} + + + + + +/// Operator construction and type checking. +impl<'cxt, 's> Parser<'cxt, 's> { + + /// Type checks and builds an application. + fn build_app(& self, frame: TermFrame) -> Res<(Term, Pos)> { + let (op, op_pos, args_pos, args) = frame.destroy() ; + debug_assert_eq! { args_pos.len(), args.len() } + + match op { + FrameOp::Op(op) => self.build_op_app( + op, op_pos, & args_pos, args + ), + + FrameOp::CArray(typ, typ_pos) => self.build_carray( + & typ, typ_pos, op_pos, & args_pos, args + ), + + FrameOp::DTypNew(name, dtyp) => self.build_dtyp_new( + name, & dtyp, op_pos, & args_pos, args + ), + + FrameOp::DTypSlc(name) => self.build_dtyp_slc( + name, op_pos, & args_pos, args + ), + + _ => unimplemented!(), + } + } + + /// Type checks and builds an operator application. + fn build_op_app( + & self, op: Op, op_pos: Pos, args_pos: & [ Pos ], args: Vec + ) -> Res<(Term, Pos)> { + debug_assert_eq! { args_pos.len(), args.len() } + + match term::try_app(op, args) { + Ok(term) => Ok((term, op_pos)), + Err( + TypError::Typ { expected, obtained, index } + ) => if let Some(exp) = expected { + err_chain! { + self.error( + args_pos[index], format!( + "expected an expression of sort {}, found {}", exp, obtained + ) + ) + => self.error(op_pos, "in this operator application") + } + } else { + err_chain! { + self.error( + args_pos[index], format!( + "expected the expression starting here has sort {} \ + which is illegal", obtained + ) + ) + => self.error(op_pos, "in this operator application") + } + } + Err( TypError::Msg(blah) ) => bail!( + self.error(op_pos, blah) + ), + } + } + + /// Type checks and builds a constant array constructor. + fn build_carray( + & self, + // Type of the array. + typ: & Typ, typ_pos: Pos, + // Position of the array constructor. + new_pos: Pos, + // Arguments. There should be only one. + args_pos: & [ Pos ], mut args: Vec, + ) -> Res<(Term, Pos)> { + debug_assert_eq! { args_pos.len(), args.len() } + + // Retrieve input and output types. + let (src, tgt) = if let Some((src, tgt)) = typ.array_inspect() { + (src, tgt) + } else { + bail!( self.error(typ_pos, "expected array sort") ) + } ; + + // Retrieve the only argument. + let (arg, arg_pos) = if let Some(arg) = args.pop() { + if args.pop().is_some() { + bail!( + self.error( + new_pos, format!( + "array constructor applied to {} (> 1) elements", + args.len() + 2 + ) + ) + ) + } else { + (arg, args_pos[0]) + } + } else { + bail!( self.error(new_pos, "array constructor applied to nothing") ) + } ; + + // let tgt = if let Some(tgt) = tgt.merge( & arg.typ() ) { + // tgt + // } else { + // bail!( + // self.error( + // arg_pos, format!( + // "this term has type {}, expected {}", arg.typ(), tgt + // ) + // ) + // ) + // } ; + + let default = match arg.cast(tgt) { + Ok(None) => arg, + Ok( Some(term) ) => term, + Err(_) => bail!( + self.error( + arg_pos, format!( + "this term of type {} is not compatible with {}", + arg.typ(), tgt + ) + ) + ), + } ; + + let term = term::cst_array( src.clone(), default ) ; + + Ok( (term, new_pos) ) + } + + /// Type checks and builds a datatype constructor. + fn build_dtyp_new( + & self, + name: String, dtyp: & DTyp, + new_pos: Pos, _args_pos: & [ Pos ], args: Vec + ) -> Res<(Term, Pos)> { + // let mut buff: Vec = vec![] ; + // dtyp::write_all(& mut buff, "| ").unwrap() ; + // write!(& mut buff, "\n\n").unwrap() ; + // dtyp::write_constructor_map(& mut buff, "| ").unwrap() ; + // println!("{}", ::std::str::from_utf8(& buff).unwrap()) ; + + let typ = if let Some(typ) = dtyp::type_constructor( + & name, & args + ).chain_err( + || self.error( + new_pos, format!( + "while typing this constructor for `{}`", + conf.emph(& dtyp.name) + ) + ) + ) ? { + typ + } else { + bail!( + self.error( + new_pos, format!( + "unknown `{}` constructor for datatype `{}`", + conf.bad(& name), conf.emph(& dtyp.name) + ) + ) + ) + } ; + + Ok( + ( term::dtyp_new(typ, name, args), new_pos ) + ) + } + + /// Type checks and builds a datatype selector. + fn build_dtyp_slc( + & self, + name: String, slc_pos: Pos, _args_pos: & [ Pos ], mut args: Vec + ) -> Res<(Term, Pos)> { + debug_assert_eq! { _args_pos.len(), args.len() } + + let arg = if let Some(arg) = args.pop() { + + if args.pop().is_some() { + bail!( + self.error( + slc_pos, format!( + "illegal datatype selector application to {} (> 1) arguments", + args.len() + 2 + ) + ) + ) + } else { + arg + } + + } else { + bail!( + self.error( + slc_pos, "illegal datatype selector application to nothing" + ) + ) + } ; + + match dtyp::type_selector( & name, slc_pos, & arg ) { + Ok(typ) => Ok( + ( term::dtyp_slc(typ, name, arg), slc_pos ) + ), + + Err((pos, blah)) => bail!( self.error(pos, blah) ), + } + } + +} + + + + + + + + +impl<'cxt, 's> Parser<'cxt, 's> { + // /// Parses an operator or fails. // fn op(& mut self) -> Res { // if let Some(op) = self.op_opt() ? { @@ -1700,10 +1957,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Parses a single term. - /// - /// # TODO - /// - /// - remove the recursive call for arrays pub fn term_opt( & mut self, var_map: & VarInfos, @@ -1722,17 +1975,23 @@ impl<'cxt, 's> Parser<'cxt, 's> { // `start_pos` and clear the `term_stack`, so there's no need to do it in // the loop. let res = 'read_kids: loop { + self.ws_cmt() ; let bind_count = self.let_bindings(var_map, map, instance) ? ; self.ws_cmt() ; let mut term_pos = self.pos() ; + + let mut term = if let Some(int) = self.int() { term::int(int) + } else if let Some(real) = self.real() ? { term::real(real) + } else if let Some(b) = self.bool() { term::bool(b) + } else if let Some((pos, id)) = self.ident_opt() ? { if let Some(idx) = map.get(id) { @@ -1753,11 +2012,15 @@ impl<'cxt, 's> Parser<'cxt, 's> { break 'read_kids None } else if let Some(datatype) = dtyp::of_constructor(id) { + if let Some(constructor) = datatype.news.get(id) { + if constructor.is_empty() { - bail!( - self.error(pos, "term for datatypes isn't implemented") - ) + let (term, _) = self.build_dtyp_new( + id.into(), & datatype, pos, & [], vec![] + ) ? ; + term + } else { bail!( self.error( @@ -1769,15 +2032,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ) } + } else { bail!("inconsistent datatype map internal state") } - } else if dtyp::is_selector(id) { - bail!( - self.error( - pos, "term datatypes (selector) isn't implemented" - ) - ) + } else { bail!( self.error( @@ -1792,11 +2051,21 @@ impl<'cxt, 's> Parser<'cxt, 's> { let op_pos = self.pos() ; if let Some(op) = self.op_opt() ? { - let frame = TermFrame::new(op, op_pos, bind_count) ; + let frame = TermFrame::new( + FrameOp::Op(op), op_pos, bind_count + ) ; + self.cxt.term_stack.push(frame) ; + continue 'read_kids + + } else if self.tag_opt("as") { + let frame = TermFrame::new( + FrameOp::Cast, op_pos, bind_count + ) ; self.cxt.term_stack.push(frame) ; continue 'read_kids } else if self.tag_opt("(") { + self.ws_cmt() ; // Try to parse a constant array. if self.tag_opt("as") @@ -1804,55 +2073,15 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; let sort_pos = self.pos() ; let typ = self.sort() ? ; - let (src, tgt) = if let Some((src, tgt)) = typ.array_inspect() { - (src, tgt) - } else { - bail!( - self.error(sort_pos, "expected array sort") - ) - } ; self.ws_cmt() ; self.tag(")") ? ; - self.ws_cmt() ; - - let term_pos = self.pos() ; - let stack = Vec::with_capacity( - self.cxt.term_stack.capacity() + let frame = TermFrame::new( + FrameOp::CArray(typ, sort_pos), op_pos, bind_count ) ; - let old_stack = ::std::mem::replace( - & mut self.cxt.term_stack, stack - ) ; - - // !!!! RECURSIVE CALL !!!! - if let Some(term) = self.term_opt(var_map, map, instance) ? { - if term.typ() != * tgt { - bail!( - self.error( - term_pos, format!( - "expected expression of sort {}, got one of sort {}", - tgt, term.typ() - ) - ) - ) - } - - let empty_stack = ::std::mem::replace( - & mut self.cxt.term_stack, - old_stack - ) ; - debug_assert! { empty_stack.is_empty() } - - self.ws_cmt() ; - self.tag(")") ? ; - term::cst_array(src.clone(), term) - - } else { - bail!( - self.error_here("expected term") - ) - } + self.cxt.term_stack.push(frame) ; + continue 'read_kids } else { bail!( self.error_here("unexpected token") ) @@ -1861,27 +2090,32 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else if let Some((pos, id)) = self.ident_opt().chain_err( || "while trying to parse datatype" ) ? { - let mut trm: Option = None ; + if let Some(datatype) = dtyp::of_constructor(id) { - if let Some(_constructor) = datatype.news.get(id) { - bail!( - self.error( - pos, "term datatypes (constructor) isn't implemented" - ) - ) - } + debug_assert! { datatype.news.get(id).is_some() } + let frame = TermFrame::new( + FrameOp::DTypNew( + id.into(), datatype + ), op_pos, bind_count + ) ; + self.cxt.term_stack.push(frame) ; + continue 'read_kids + } else if dtyp::is_selector(id) { - bail!( - self.error( - pos, "term datatypes (selector) isn't implemented" - ) - ) + let frame = TermFrame::new ( + FrameOp::DTypSlc( + id.into() + ), op_pos, bind_count + ) ; + self.cxt.term_stack.push(frame) ; + continue 'read_kids + } - if let Some(trm) = trm { - trm - } else if self.cxt.term_stack.is_empty() { + + if self.cxt.term_stack.is_empty() { self.backtrack_to(pos) ; break 'read_kids None + } else { bail!( self.error( @@ -1930,7 +2164,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.close_let_bindings(bind_count) ? ; continue 'go_up + } else if frame.is_cast() { + // Cast expect a type after the term being cast. + let _sort = self.sort().chain_err( + || "in term casting, after term" + ) ? ; + + bail!( + self.error( + frame.op_pos, "casts are not supported" + ) + ) + } else { + // Keep on parsing terms. self.cxt.term_stack.push(frame) ; continue 'read_kids } diff --git a/src/term/factory.rs b/src/term/factory.rs index 1f5f5144..4691fbc2 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -27,19 +27,21 @@ lazy_static! { fn scan_vars(t: & Term) -> VarSet { let mut to_do = vec![ t ] ; let mut set = VarSet::with_capacity(11) ; + while let Some(term) = to_do.pop() { match term.get() { RTerm::Var(_, i) => { let _ = set.insert(* i) ; () }, RTerm::Cst(_) => (), - RTerm::CArray { ref term, .. } => to_do.push(& * term), - RTerm::App{ ref args, .. } => for arg in args { + RTerm::CArray { term, .. } => to_do.push(& * term), + RTerm::App{ args, .. } => for arg in args { to_do.push(arg) }, - RTerm::DTypNew { ref args, .. } => for arg in args { + RTerm::DTypNew { args, .. } => for arg in args { to_do.push(arg) }, + RTerm::DTypSlc { term, .. } => to_do.push(term), } } set.shrink_to_fit() ; @@ -218,7 +220,7 @@ pub fn cst_array(typ: Typ, default: Term) -> Term { RTerm::Cst( val::array(typ, val) ) ) } else { - factory.mk( RTerm::CArray { typ, term: Box::new(default) } ) + factory.mk( RTerm::CArray { typ, term: default } ) } } @@ -279,9 +281,32 @@ pub fn app(op: Op, args: Vec) -> Term { /// Creates a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { + if let Some((dtyp, _)) = typ.dtyp_inspect() { + if let Some(fargs) = dtyp.news.get(& name) { + if args.len() != fargs.len() { + panic!( + "constructor `{}` for `{}` expects {} arguments, found {}", + conf.emph(& name), conf.emph(& dtyp.name), + fargs.len(), args.len() + ) + } + } else { + panic!( + "datatype `{}` has no constructor named `{}`", + conf.emph(& dtyp.name), conf.bad(& name) + ) + } + } else { + panic!("ill-typed datatype constructor: {}", typ) + } factory.mk( RTerm::DTypNew { typ, name, args } ) } +/// Creates a new datatype selector. +pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { + factory.mk( RTerm::DTypSlc { typ, name, term } ) +} + /// Creates an operator application. /// /// Error if the application is ill-typed (int will be cast to real diff --git a/src/term/zipper.rs b/src/term/fold.rs similarity index 62% rename from src/term/zipper.rs rename to src/term/fold.rs index c4577e3b..fe8f65c6 100644 --- a/src/term/zipper.rs +++ b/src/term/fold.rs @@ -1,11 +1,11 @@ -//! Types for zipping over terms. +//! Types for folding over terms. use common::* ; use std::slice::Iter ; -/// Zip info for terms. -enum ZipInfo<'a, Info> { +/// Fold info for terms. +enum FoldInfo<'a, Info> { /// Constant array. Arr { /// Type. @@ -34,98 +34,118 @@ enum ZipInfo<'a, Info> { lft_args: Vec, /// Kids left to process. rgt_args: Iter<'a, Term>, - } + }, + + /// Datatype selector. + Sel { + /// Type. + typ: & 'a Typ, + /// Name. + name: & 'a String, + }, } -/// Zips over a term. +/// Folds over a term. /// /// Early returns **iff** any a call to one of the input functions returns an /// error. /// /// # Type parameters /// -/// - `Info`: information extracted by the zipping process +/// - `Info`: information extracted by the folding process /// - `VarF`: will run on variables /// - `CstF`: will run on constants -/// - `AppF`: will run on the result of zipping on operator applications -/// - `ArrF`: will run on the result of zipping on arrays -/// - `NewF`: will run on the result of zipping on datatype constructors -pub fn zip_res( - term: & RTerm, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF +/// - `AppF`: will run on the result of folding on operator applications +/// - `ArrF`: will run on the result of folding on arrays +/// - `NewF`: will run on the result of folding on datatype constructors +/// - `SlcF`: will run on the result of folding on datatype selectors +pub fn fold_res( + term: & RTerm, + varf: VarF, cstf: CstF, + appf: AppF, arrf: ArrF, + newf: NewF, slcf: SlcF, ) -> Res where VarF: FnMut(& Typ, VarIdx) -> Res, CstF: FnMut(& Val) -> Res, AppF: FnMut(& Typ, Op, Vec) -> Res, ArrF: FnMut(& Typ, Info) -> Res, -NewF: FnMut(& Typ, & String, Vec) -> Res, { - zip_custom_res(term, varf, cstf, appf, arrf, newf) +NewF: FnMut(& Typ, & String, Vec) -> Res, +SlcF: FnMut(& Typ, & String, Info) -> Res, { + fold_custom_res(term, varf, cstf, appf, arrf, newf, slcf) } -/// Zips over a term. +/// Folds over a term. /// /// # Type parameters /// -/// - `Info`: information extracted by the zipping process +/// - `Info`: information extracted by the folding process /// - `VarF`: will run on variables /// - `CstF`: will run on constants -/// - `AppF`: will run on the result of zipping on operator applications -/// - `ArrF`: will run on the result of zipping on arrays -/// - `NewF`: will run on the result of zipping on datatype constructors -pub fn zip( +/// - `AppF`: will run on the result of folding on operator applications +/// - `ArrF`: will run on the result of folding on arrays +/// - `NewF`: will run on the result of folding on datatype constructors +/// - `SlcF`: will run on the result of folding on datatype selectors +pub fn fold( term: & RTerm, - mut varf: VarF, mut cstf: CstF, mut appf: AppF, - mut arrf: ArrF, mut newf: NewF + mut varf: VarF, mut cstf: CstF, + mut appf: AppF, mut arrf: ArrF, + mut newf: NewF, mut slcf: SlcF, ) -> Info where VarF: FnMut(& Typ, VarIdx) -> Info, CstF: FnMut(& Val) -> Info, AppF: FnMut(& Typ, Op, Vec) -> Info, ArrF: FnMut(& Typ, Info) -> Info, -NewF: FnMut(& Typ, & String, Vec) -> Info, { - zip_custom_res::< Info, (), _, _, _, _, _ >( +NewF: FnMut(& Typ, & String, Vec) -> Info, +SlcF: FnMut(& Typ, & String, Info) -> Info, { + fold_custom_res::< Info, (), _, _, _, _, _, _ >( term, |t,v| Ok( varf(t, v) ), |v| Ok( cstf(v) ), |t, o, i| Ok( appf(t, o, i) ), |t, i| Ok( arrf(t, i) ), |t, s, i| Ok( newf(t, s, i) ), + |t, s, i| Ok( slcf(t, s, i) ), ).unwrap() //^^^^^^~~~~ this unwrap is proved safe trivially. } -/// Zips over a term. +/// Folds over a term. /// /// This function returns an error **iff** one of the function provided returns /// one. /// /// # Type parameters /// -/// - `Info`: information extracted by the zipping process +/// - `Info`: information extracted by the folding process /// - `E`: type of errors (for early returns) /// - `VarF`: will run on variables /// - `CstF`: will run on constants -/// - `AppF`: will run on the result of zipping on operator applications -/// - `ArrF`: will run on the result of zipping on arrays -/// - `NewF`: will run on the result of zipping on datatype constructors -pub fn zip_custom_res( +/// - `AppF`: will run on the result of folding on operator applications +/// - `ArrF`: will run on the result of folding on arrays +/// - `NewF`: will run on the result of folding on datatype constructors +/// - `SlcF`: will run on the result of folding on datatype selectors +pub fn fold_custom_res( term: & RTerm, - mut varf: VarF, mut cstf: CstF, mut appf: AppF, - mut arrf: ArrF, mut newf: NewF + mut varf: VarF, mut cstf: CstF, + mut appf: AppF, mut arrf: ArrF, + mut newf: NewF, mut slcf: SlcF, ) -> Result where VarF: FnMut(& Typ, VarIdx) -> Result, CstF: FnMut(& Val) -> Result, AppF: FnMut(& Typ, Op, Vec) -> Result, ArrF: FnMut(& Typ, Info) -> Result, -NewF: FnMut(& Typ, & String, Vec) -> Result, { +NewF: FnMut(& Typ, & String, Vec) -> Result, +SlcF: FnMut(& Typ, & String, Info) -> Result, { use term::RTerm ; // Stack of stuff to zip on. - let mut stack: Vec< ZipInfo > = Vec::with_capacity(11) ; + let mut stack: Vec< FoldInfo > = Vec::with_capacity(11) ; // Term we're currently going into. let mut curr = term ; @@ -146,7 +166,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { RTerm::CArray { ref typ, ref term } => { curr = term ; - stack.push( ZipInfo::Arr { typ } ) ; + stack.push( FoldInfo::Arr { typ } ) ; continue 'go_down }, @@ -159,7 +179,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { ) ; stack.push( - ZipInfo::App { typ, op, lft_args, rgt_args } + FoldInfo::App { typ, op, lft_args, rgt_args } ) ; continue 'go_down @@ -173,7 +193,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { // Not a nullary constructor, go down. curr = term ; stack.push( - ZipInfo::New { typ, name, lft_args, rgt_args } + FoldInfo::New { typ, name, lft_args, rgt_args } ) ; continue 'go_down @@ -184,6 +204,15 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { } }, + RTerm::DTypSlc { ref typ, ref name, ref term } => { + curr = term ; + stack.push( + FoldInfo::Sel { typ, name } + ) ; + + continue 'go_down + }, + } ; @@ -197,7 +226,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { // Array, current info is for the default value. Some( - ZipInfo::Arr { typ } + FoldInfo::Arr { typ } ) => { // Update info and keep going up. info = arrf(typ, info) ? ; @@ -207,7 +236,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { // Operator application, info is for the kid between `lft_args` and // `rgt_args`. Some( - ZipInfo::App { typ, op, mut lft_args, mut rgt_args } + FoldInfo::App { typ, op, mut lft_args, mut rgt_args } ) => { lft_args.push(info) ; @@ -218,7 +247,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { curr = term ; // Don't forget to push back the zip info in the stack. stack.push( - ZipInfo::App { typ, op, lft_args, rgt_args } + FoldInfo::App { typ, op, lft_args, rgt_args } ) ; // Go down. continue 'go_down @@ -234,7 +263,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { // Datatype constructor, info is for the kid between `lft_args` and // `rgt_args`. Some( - ZipInfo::New { typ, name, mut lft_args, mut rgt_args } + FoldInfo::New { typ, name, mut lft_args, mut rgt_args } ) => { lft_args.push(info) ; @@ -245,7 +274,7 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { curr = term ; // Don't forget to push back the zip info in the stack. stack.push( - ZipInfo::New { typ, name, lft_args, rgt_args } + FoldInfo::New { typ, name, lft_args, rgt_args } ) ; // Go down. continue 'go_down @@ -258,6 +287,15 @@ NewF: FnMut(& Typ, & String, Vec) -> Result, { }, + // Datatype selector, info is for the subterm. + Some( + FoldInfo::Sel { typ, name } + ) => { + // Update info and go up. + info = slcf(typ, name, info) ? ; + continue 'go_up + }, + } } diff --git a/src/term/leaf_iter.rs b/src/term/leaf_iter.rs index 2c1fb4f6..5c0bffc2 100644 --- a/src/term/leaf_iter.rs +++ b/src/term/leaf_iter.rs @@ -68,6 +68,7 @@ impl<'a> Iterator for LeafIter<'a> { Var(ref typ, var) => Either::Left( (typ, var) ), Cst(ref val) => Either::Right(val), + DTypSlc { ref term, .. } | CArray { ref term, .. } => { current = term.get() ; continue 'go_down diff --git a/src/term/mod.rs b/src/term/mod.rs index 4a8d3d69..1cb3c5ab 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -66,7 +66,7 @@ mod factory ; mod tterms ; pub mod simplify ; pub mod typ ; -mod zipper ; +mod fold ; mod leaf_iter ; pub use self::op::* ; @@ -100,7 +100,7 @@ pub enum RTerm { /// Type of **the indices** (not the array). typ: Typ, /// Default term of the array. - term: Box + term: Term }, /// An operator application. @@ -122,6 +122,16 @@ pub enum RTerm { /// Arguments of the constructor. args: Vec, }, + + /// A datatype selector application. + DTypSlc { + /// Type of the application. + typ: Typ, + /// Name of the selector. + name: String, + /// Argument of the selector. + term: Term, + }, } @@ -225,14 +235,18 @@ impl RTerm { while let Some(term) = stack.pop() { f(term) ; - match * term { - RTerm::App { ref args, .. } => for term in args { + match term { + RTerm::App { args, .. } => for term in args { stack.push(term) }, - RTerm::CArray { ref term, .. } => stack.push(& * term), - RTerm::DTypNew { ref args, .. } => for term in args { + + RTerm::CArray { term, .. } => stack.push( term.get() ), + RTerm::DTypSlc { term, .. } => stack.push( term.get() ), + + RTerm::DTypNew { args, .. } => for term in args { stack.push(term) }, + RTerm::Var(_, _) | RTerm::Cst(_) => (), } @@ -244,11 +258,15 @@ impl RTerm { match self { RTerm::Var(typ, _) => typ.clone(), RTerm::Cst(val) => val.typ(), + RTerm::CArray { typ, term } => typ::array( typ.clone(), term.typ() ), - RTerm::DTypNew { typ, .. } => typ.clone(), + RTerm::App { typ, .. } => typ.clone(), + + RTerm::DTypSlc { typ, .. } => typ.clone(), + RTerm::DTypNew { typ, .. } => typ.clone(), } } @@ -288,26 +306,29 @@ impl RTerm { if let Some(this_term) = to_do.pop() { stack.push( (to_do, sep, end) ) ; + write!(w, "{}", sep) ? ; match this_term { - Var(_, v) => { - write!(w, "{}", sep) ? ; - write_var(w, * v) ? - }, - Cst(val) => write!(w, "{}{}", sep, val) ?, + Var(_, v) => write_var(w, * v) ?, + Cst(val) => write!(w, "{}", val) ?, CArray { term, .. } => { - write!(w, "{}((as const {})", sep, this_term.typ()) ? ; + write!(w, "((as const {})", this_term.typ()) ? ; stack.push( (vec![term], " ", ")") ) }, App { op, args, .. } => { - write!(w, "{}({}", sep, op) ? ; + write!(w, "({}", op) ? ; stack.push( (args.iter().rev().map(|t| t.get()).collect(), " ", ")") ) }, + DTypSlc { name, term, .. } => { + write!(w, "({}", name) ? ; + stack.push( (vec![term], " ", ")") ) + }, + DTypNew { name, args, .. } => if args.is_empty() { write!(w, "{}", name) ? } else { @@ -509,6 +530,149 @@ impl RTerm { } } + /// Casts a term. + /// + /// Only legal if the term's type and the one provided are compatible. + /// + /// Returns + /// + /// - an error if the types are not compatible + /// - `None` if the cast didn't do anything + /// - the new term otherwise + pub fn cast(& self, to_typ: & Typ) -> Res< Option > { + let nu_typ = if let Some(typ) = self.typ().merge(to_typ) { + if to_typ == & typ { return Ok(None) } + typ + } else { + bail!( + "types {} and {} are incompatible", self.typ(), to_typ + ) + } ; + + enum Frame<'a> { + // Array frame. + Arr(Typ), + // Datatype constructor. + New { + typ: Typ, + name: String, + lft: Vec, + rgt: ::std::vec::IntoIter<(Typ, & 'a RTerm)>, + }, + } + + let mut stack = vec![] ; + let (mut nu_typ, mut curr) = (nu_typ, self) ; + + 'go_down: loop { + + let mut term = match curr { + RTerm::Var(_, idx) => term::var(* idx, nu_typ), + + RTerm::Cst(val) => if let Ok(val) = val.cast(& nu_typ) { + factory::cst(val) + } else { + return Ok(None) + }, + + RTerm::App { op, args, .. } => term::app( * op, args.clone() ), + + RTerm::CArray { typ, term } => { + let (src, tgt) = typ.array_inspect().unwrap() ; + stack.push( + Frame::Arr( src.clone() ) + ) ; + nu_typ = tgt.clone() ; + curr = term.get() ; + continue 'go_down + }, + + RTerm::DTypNew { typ, name, args } => { + let mut lft = vec![] ; + let mut next = None ; + let mut rgt = vec![] ; + + scoped! { + + let (_, nu_prms) = nu_typ.dtyp_inspect().unwrap() ; + let (_, prms) = typ.dtyp_inspect().unwrap() ; + debug_assert_eq! { args.len(), nu_prms.len() } + debug_assert_eq! { args.len(), prms.len() } + let mut all = nu_prms.iter().zip( + prms.iter() + ).zip( args.iter() ) ; + + while let Some(((nu, typ), arg)) = all.next() { + if nu == typ { + lft.push( arg.clone() ) + } else { + next = Some( + ( arg.get(), nu.clone() ) + ) + } + } + + for ((nu_typ, _), arg) in all { + rgt.push( (nu_typ.clone(), arg.get()) ) + } + + } + + if let Some((term, nu)) = next { + let frame = Frame::New { + typ: nu_typ, name: name.clone(), lft, rgt: rgt.into_iter() + } ; + stack.push(frame) ; + nu_typ = nu ; + curr = term ; + + continue 'go_down + } else { + term::dtyp_new(nu_typ, name.clone(), lft) + } + }, + + RTerm::DTypSlc { typ, name, term } => { + debug_assert_eq! { typ, & nu_typ } + term::dtyp_slc( + typ.clone(), name.clone(), term.clone() + ) + }, + } ; + + 'go_up: loop { + + match stack.pop() { + None => return Ok( Some(term) ), + + Some( Frame::Arr(typ) ) => { + term = term::cst_array(typ, term) ; + continue 'go_up + }, + + Some( + Frame::New { typ, name, mut lft, mut rgt } + ) => { + lft.push(term) ; + + if let Some((ty, tm)) = rgt.next() { + nu_typ = ty ; + curr = tm ; + stack.push( Frame::New { typ, name, lft, rgt } ) ; + continue 'go_down + } else { + term = term::dtyp_new(typ, name, lft) ; + continue 'go_up + } + }, + } + + } + + } + + } + /// Checks whether the term is a relation. pub fn is_relation(& self) -> bool { match * self { @@ -529,53 +693,59 @@ impl RTerm { } } - /// Zips over a term. + /// Folds over a term. /// /// # Type parameters /// - /// - `Info`: information extracted by the zipping process + /// - `Info`: information extracted by the folding process /// - `VarF`: will run on variables /// - `CstF`: will run on constants - /// - `AppF`: will run on the result of zipping on operator applications - /// - `ArrF`: will run on the result of zipping on arrays - /// - `NewF`: will run on the result of zipping on datatype constructors - pub fn zip( - & self, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF + /// - `AppF`: will run on the result of folding on operator applications + /// - `ArrF`: will run on the result of folding on arrays + /// - `NewF`: will run on the result of folding on datatype constructors + /// - `SlcF`: will run on the result of folding on datatype selectors + pub fn fold( + & self, + varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF, slcf: SlcF ) -> Info where VarF: FnMut(& Typ, VarIdx) -> Info, CstF: FnMut(& Val) -> Info, AppF: FnMut(& Typ, Op, Vec) -> Info, ArrF: FnMut(& Typ, Info) -> Info, - NewF: FnMut(& Typ, & String, Vec) -> Info, { - zipper::zip(self, varf, cstf, appf, arrf, newf) + NewF: FnMut(& Typ, & String, Vec) -> Info, + SlcF: FnMut(& Typ, & String, Info) -> Info, { + fold::fold(self, varf, cstf, appf, arrf, newf, slcf) } - /// Zips over a term. + /// Folds over a term. /// /// Early returns **iff** any a call to one of the input functions returns an /// error. /// /// # Type parameters /// - /// - `Info`: information extracted by the zipping process + /// - `Info`: information extracted by the folding process /// - `VarF`: will run on variables /// - `CstF`: will run on constants - /// - `AppF`: will run on the result of zipping on operator applications - /// - `ArrF`: will run on the result of zipping on arrays - /// - `NewF`: will run on the result of zipping on datatype constructors - pub fn zip_res( - & self, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF + /// - `AppF`: will run on the result of folding on operator applications + /// - `ArrF`: will run on the result of folding on arrays + /// - `NewF`: will run on the result of folding on datatype constructors + /// - `SlcF`: will run on the result of folding on datatype selectors + pub fn fold_res( + & self, + varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF, slcf: SlcF ) -> Res where VarF: FnMut(& Typ, VarIdx) -> Res, CstF: FnMut(& Val) -> Res, AppF: FnMut(& Typ, Op, Vec) -> Res, ArrF: FnMut(& Typ, Info) -> Res, - NewF: FnMut(& Typ, & String, Vec) -> Res, { - zipper::zip_res(self, varf, cstf, appf, arrf, newf) + NewF: FnMut(& Typ, & String, Vec) -> Res, + SlcF: FnMut(& Typ, & String, Info) -> Res, { + fold::fold_res(self, varf, cstf, appf, arrf, newf, slcf) } @@ -585,7 +755,7 @@ impl RTerm { /// /// - remove recursive call for constant arrays pub fn eval(& self, model: & E) -> Res { - self.zip_res( + self.fold_res( // Variable evaluation. |_, v| if v < model.len() { Ok( model.get(v).clone() ) @@ -606,10 +776,16 @@ impl RTerm { val::array( typ.clone(), default ) ), - // Datatype evaluation. + // Datatype construction. |typ, name, values| Ok( val::dtyp_new( typ.clone(), name.clone(), values ) ), + + // Datatype selection. + |typ, name, value| bail!( + "datatype ({}) selector evaluation is unimplemented ; on ({} {})", + typ, name, value + ), ) } @@ -700,7 +876,7 @@ impl RTerm { ) -> Option<(Term, bool)> { let mut changed = false ; - let res = zipper::zip_custom_res( + let res = fold::fold_custom_res( self, // Variable. @@ -737,6 +913,13 @@ impl RTerm { typ.clone(), name.clone(), args ) ), + + // Datatype selector. + |typ, name, term| Ok( + term::dtyp_slc( + typ.clone(), name.clone(), term + ) + ), ) ; if let Ok(term) = res { @@ -998,10 +1181,10 @@ impl RTerm { RTerm::Var(_, v) => return Some((v, solution)), - RTerm::CArray { .. } | - RTerm::DTypNew { .. } => return None, - - RTerm::Cst(_) => return None, + RTerm::Cst(_) | + RTerm::CArray { .. } | + RTerm::DTypNew { .. } | + RTerm::DTypSlc { .. } => return None, } } } diff --git a/src/term/typ.rs b/src/term/typ.rs index 476f2729..929a1c8d 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -3,6 +3,7 @@ use hashconsing::{ HashConsign, HConser, HConsed } ; use common::* ; +use dtyp::TPrmMap ; /// Type of the term factory. type Factory = RwLock< HashConsign > ; @@ -36,7 +37,7 @@ pub fn unk() -> Typ { factory.mk(RTyp::Unk) } /// Generates a datatype. -pub fn dtyp(dtyp: dtyp::DTyp, prms: dtyp::TPrmMap) -> Typ { +pub fn dtyp(dtyp: dtyp::DTyp, prms: TPrmMap) -> Typ { factory.mk( RTyp::DTyp { dtyp, prms } ) } @@ -71,6 +72,7 @@ pub enum RTyp { Real, /// Booleans. Bool, + /// Arrays. Array { /// Type of indices. @@ -78,12 +80,13 @@ pub enum RTyp { /// Type of values. tgt: Typ, }, + /// Datatype. DTyp { /// Original datatype. - dtyp: dtyp::DTyp, + dtyp: DTyp, /// Type parameters. - prms: dtyp::TPrmMap, + prms: TPrmMap, }, } impl RTyp { @@ -118,9 +121,10 @@ impl RTyp { /// Inspects an array type. pub fn array_inspect(& self) -> Option<(& Typ, & Typ)> { - match * self { - RTyp::Array { ref src, ref tgt } => Some((src, tgt)), - _ => None, + if let RTyp::Array { src, tgt } = self { + Some( (src, tgt) ) + } else { + None } } @@ -129,6 +133,15 @@ impl RTyp { RTyp::Unk == * self } + /// Inspects a datatype type. + pub fn dtyp_inspect(& self) -> Option<(& DTyp, & TPrmMap)> { + if let RTyp::DTyp { dtyp, prms } = self { + Some( (dtyp, prms) ) + } else { + None + } + } + /// True if the types are compatible. /// /// Two types are compatible if they're the same except for unknown subtypes. diff --git a/src/val/mod.rs b/src/val/mod.rs index fa68cfdb..5d60f4f2 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -370,7 +370,7 @@ impl RVal { } /// Attempts to cast a value. - pub fn cast(& self, typ: & ::term::Typ) -> Res { + pub fn cast(& self, typ: & Typ) -> Res { use num::One ; use term::typ::RTyp ; if & self.typ() == typ { From bbd079c45fba57c9b3a2e092cc6ddb47bb15545f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 4 Jul 2018 11:35:36 +0900 Subject: [PATCH 08/94] datatype model parsing --- src/common/smt.rs | 16 +++++++++++++++- src/term/factory.rs | 30 +++++++++++++++++++++++++++++- src/term/simplify.rs | 5 ++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index 7792ee84..91218315 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -666,6 +666,15 @@ impl<'a> ModelParser for FullParser { Ok( FPVal::Val( val::bool(val) ) ) + } else if let Ok( Some(term) ) = parser.term_opt( + & vec![].into(), & HashMap::new(), & Instance::new() + ) { + if let Some(val) = term.val() { + Ok( FPVal::Val(val) ) + } else { + bail!("cannot turn term into a value: {}", term) + } + } else if parser.tag_opt("(") && { parser.ws_cmt() ; parser.tag_opt("_") } && { @@ -677,7 +686,12 @@ impl<'a> ModelParser for FullParser { parser.ws_cmt() ; if let Ok((_, ident)) = parser.ident() { - Ok( FPVal::FunToArray( ident.into() ) ) + parser.ws_cmt() ; + if parser.tag_opt(")") { + Ok( FPVal::FunToArray( ident.into() ) ) + } else { + bail!("ill-formed value, missing closing paren") + } } else { bail!("expected symbol in function to array conversion `{}`", input) } diff --git a/src/term/factory.rs b/src/term/factory.rs index 4691fbc2..4ca4f8b9 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -278,6 +278,11 @@ pub fn app(op: Op, args: Vec) -> Term { normalize(op, args, typ.clone()) } +/// Creates a constant term. +pub fn val(val: Val) -> Term { + factory.mk( RTerm::Cst(val) ) +} + /// Creates a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { @@ -299,7 +304,30 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { } else { panic!("ill-typed datatype constructor: {}", typ) } - factory.mk( RTerm::DTypNew { typ, name, args } ) + + let mut vals = if args.is_empty() { + Some(vec![]) + } else { + None + } ; + + for arg in & args { + if let Some(val) = arg.val() { + vals.get_or_insert_with( + || Vec::with_capacity( args.len() ) + ).push(val) + } else { + vals = None ; + break + } + } + + if let Some(vals) = vals { + debug_assert_eq! { vals.len(), args.len() } + val( val::dtyp_new(typ, name, vals) ) + } else { + factory.mk( RTerm::DTypNew { typ, name, args } ) + } } /// Creates a new datatype selector. diff --git a/src/term/simplify.rs b/src/term/simplify.rs index bd511c53..7dbd8b56 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1704,4 +1704,7 @@ simpl_fun! { } } ; -} \ No newline at end of file +} + + + From 3562fa5e5d8fa837b702011630aec16511dc3343 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 4 Jul 2018 15:21:30 +0900 Subject: [PATCH 09/94] datatypes: full support, no synth --- src/dtyp/mod.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++-- src/term/mod.rs | 44 ++++++++++++++++++-- src/term/typ.rs | 60 +++++++++++++++++++++++----- src/val/mod.rs | 9 +++++ 4 files changed, 200 insertions(+), 17 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index d152da42..c578c60d 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -38,6 +38,56 @@ impl From for PartialTyp { } } impl PartialTyp { + /// True if the type mentions the datatype provided. + pub fn mentions_dtyp( + & self, dtyp_name: & str + ) -> bool { + let mut to_do = vec![ self ] ; + let mut typ_to_do = vec![] ; + + loop { + if to_do.is_empty() && typ_to_do.is_empty() { + return false + } + + while let Some(current) = to_do.pop() { + match current { + PartialTyp::Array(src, tgt) => { + to_do.push( & ** src ) ; + to_do.push( & ** tgt ) + }, + + PartialTyp::DTyp(name, _, _) if name == dtyp_name => return true, + + PartialTyp::DTyp(_, _, prms) => for typ in prms { + to_do.push(typ) + }, + + PartialTyp::Typ(typ) => typ_to_do.push(typ), + + PartialTyp::Param(_) => (), + } + } + + while let Some(current) = typ_to_do.pop() { + use typ::RTyp ; + + match current.get() { + RTyp::Unk | RTyp::Int | RTyp::Real | RTyp::Bool => (), + RTyp::Array { src, tgt } => { + typ_to_do.push(src) ; + typ_to_do.push(tgt) + }, + RTyp::DTyp { dtyp, .. } if dtyp.name == dtyp_name => return true, + RTyp::DTyp { prms, .. } => for typ in prms { + typ_to_do.push(typ) + }, + } + } + + } + } + /// Resolves a partial type against a type. pub fn unify ( & self, typ: & Typ, map: & mut TPrmMap @@ -314,10 +364,51 @@ lazy_static! { /// - the datatype already exists /// - one of the constructors of the datatype already exists /// - can't access the datatype map -pub fn mk(dtyp: RDTyp) -> Res { +/// - the datatype has no constructor +/// - the datatype has no constructor that don't mention itself +pub fn mk(mut dtyp: RDTyp) -> Res { let name = dtyp.name.clone() ; - let to_insert = Arc::new(dtyp) ; - let dtyp = to_insert.clone() ; + + if dtyp.news.is_empty() { + bail!("illegal datatype: no constructor") + } + + // Number of constructor that mention `dtyp`. + let mut rec_count = 0 ; + let mut default = None ; + + for (constructor, args) in & dtyp.news { + let mut recursive = false ; + for (_, ty) in args { + if ty.mentions_dtyp(& dtyp.name) { + recursive = true ; + break + } + } + + if ! recursive { + rec_count += 1 ; + let default = default.get_or_insert_with( + || (constructor.clone(), args.len()) + ) ; + if default.1 > args.len() { + default.0 = constructor.clone() ; + default.1 = args.len() + } + } + } + + if rec_count == dtyp.news.len() { + bail!("all constructors for datatype `{}` are recursive", dtyp.name) + } + + if let Some((default, _)) = default { + dtyp.default = default + } else { + bail!("could not find a default constructor for datatype `{}`", dtyp.name) + } + + let dtyp = Arc::new(dtyp) ; // Update constructor map. if let Ok(mut map) = constructor_map.write() { @@ -330,6 +421,8 @@ pub fn mk(dtyp: RDTyp) -> Res { ) } } + } else { + bail!("failed to retrieve datatype constructor map") } // Update selector set. @@ -343,7 +436,7 @@ pub fn mk(dtyp: RDTyp) -> Res { let prev = if let Ok(mut f) = factory.write() { - f.insert(name, to_insert) + f.insert( name, dtyp.clone() ) } else { bail!("failed to access datatype factory") } ; @@ -535,6 +628,8 @@ pub struct RDTyp { pub prms: TPrmMap, /// Constructors. pub news: BTreeMap, + /// Default type constructor. + pub default: String, } impl RDTyp { /// Constructor. @@ -542,6 +637,7 @@ impl RDTyp { let name = name.into() ; RDTyp { name, deps: vec![], prms, news: BTreeMap::new(), + default: "".into() } } diff --git a/src/term/mod.rs b/src/term/mod.rs index 1cb3c5ab..f4029d0e 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -782,10 +782,46 @@ impl RTerm { ), // Datatype selection. - |typ, name, value| bail!( - "datatype ({}) selector evaluation is unimplemented ; on ({} {})", - typ, name, value - ), + |typ, name, value| if ! value.is_known() { + Ok( val::none( typ.clone() ) ) + } else if let Some( + (ty, constructor, values) + ) = value.dtyp_inspect() { + if let Some((dtyp, _)) = ty.dtyp_inspect() { + + if let Some(selectors) = dtyp.news.get(constructor) { + + let mut res = None ; + for ((selector, _), value) in selectors.iter().zip( + values.iter() + ) { + if selector == name { + res = Some( value.clone() ) + } + } + + if let Some(res) = res { + Ok(res) + } else { + Ok( val::none( typ.clone() ) ) + } + + } else { + bail!( + "unknown constructor `{}` for datatype {}", + conf.bad(constructor), dtyp.name + ) + } + + } else { + bail!("inconsistent type {} for value {}", ty, value) + } + } else { + bail!( + "illegal application of constructor `{}` of `{}` to `{}`", + conf.bad(& name), typ, value + ) + } ) } diff --git a/src/term/typ.rs b/src/term/typ.rs index 929a1c8d..952c271e 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -316,15 +316,57 @@ impl RTyp { /// /// Fails if the type is unknown. pub fn default_val(& self) -> Val { - match * self { - RTyp::Real => val::real( Rat::zero() ), - RTyp::Int => val::int( Int::zero() ), - RTyp::Bool => val::bool( true ), - RTyp::Array { ref src, ref tgt } => val::array( - src.clone(), tgt.default_val() - ), - RTyp::DTyp { .. } => unimplemented!(), - RTyp::Unk => panic!("unknown type has no default value"), + let typ = factory.mk( self.clone() ) ; + let mut current = & typ ; + let mut stack = vec![] ; + + 'go_down: loop { + + let mut val = match current.get() { + RTyp::Real => val::real( Rat::zero() ), + RTyp::Int => val::int( Int::zero() ), + RTyp::Bool => val::bool( true ), + RTyp::Array { ref src, ref tgt } => val::array( + src.clone(), tgt.default_val() + ), + RTyp::DTyp { dtyp, prms } => { + let mut prms = prms.iter() ; + if let Some(next) = prms.next() { + current = next ; + stack.push( + (current, dtyp.default.clone(), vec![], prms) + ) ; + continue 'go_down + } else { + val::dtyp_new( + current.clone(), dtyp.default.clone(), vec![] + ) + } + }, + RTyp::Unk => panic!("unknown type has no default value"), + } ; + + + 'go_up: loop { + match stack.pop() { + None => return val, + + Some( + (typ, default, mut args, mut prms) + ) => { + args.push(val) ; + if let Some(prm) = prms.next() { + stack.push( (typ, default, args, prms) ) ; + current = prm ; + continue 'go_down + } else { + val = val::dtyp_new( typ.clone(), default, args ) ; + continue 'go_up + } + }, + } + } + } } diff --git a/src/val/mod.rs b/src/val/mod.rs index 5d60f4f2..90281bff 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -354,6 +354,15 @@ impl RVal { } } + /// Inspects a datatype value. + pub fn dtyp_inspect(& self) -> Option<(& Typ, & str, & [Val])> { + if let RVal::DTypNew { typ, name, args } = self { + Some((typ, name, args)) + } else { + None + } + } + /// Returns the type of the value. pub fn typ(& self) -> Typ { use self::RVal::* ; From e1c8f8819435b02967592f923b1137cba9227a50 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 4 Jul 2018 15:22:19 +0900 Subject: [PATCH 10/94] inactive test case for adts --- rsc/inactive/adt/sorted.smt2 | 108 +++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 rsc/inactive/adt/sorted.smt2 diff --git a/rsc/inactive/adt/sorted.smt2 b/rsc/inactive/adt/sorted.smt2 new file mode 100644 index 00000000..0db63d06 --- /dev/null +++ b/rsc/inactive/adt/sorted.smt2 @@ -0,0 +1,108 @@ +(set-logic HORN) + +(declare-datatypes () ( + (Lst nil (cons (head Int) (tail Lst))) +) ) + + +; let rev = +; let rec loop acc = function +; | [] -> acc +; | h :: t -> loop (h :: acc) t +; in +; loop [] + +; Post-condition. +(declare-fun + rev_pst ( Lst Lst Lst ) Bool +) +; Terminal case. +(assert + (forall ( (acc Lst) ) + (rev_pst acc nil acc) +) ) +; Recursive case. +(assert + (forall ( (acc Lst) (lst Lst) (res Lst) ) + (=> + (and + (not (= lst nil)) + (rev_pst + (cons (head lst) acc) + (tail lst) + res + ) + ) + (rev_pst acc lst res) + ) +) ) + + +; let rec sorted = function +; | nil | _ :: nil => true +; | h1 :: h2 :: t => (h1 < h2) and (sorted (h2 :: t)) +; (* STRICTLY sorted~~~~~^ *) + +; Post-condition. +(declare-fun + srt_pst ( Lst Bool ) Bool +) +; Terminal cases. +(assert + (forall ( (unused Bool) ) + (srt_pst nil true) +) ) +(assert + (forall ( (hd Int) ) + (srt_pst (cons hd nil) true) +) ) +(assert + (forall ( (lst Lst) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (not (< (head lst) (head (tail lst)))) + ) + (srt_pst lst false) + ) +) ) +; Recursive case. +(assert + (forall ( (lst Lst) (res Bool) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (< (head lst) (head (tail lst))) + (srt_pst (tail lst) res) + ) + (srt_pst lst res) + ) +) ) + + +; let main lst = +; if lst = (rev lst) +; and (sorted lst) +; and (sorted (rev lst)) +; then match lst +; | nil | _ :: nil => () +; | _ => assert false +(assert + (forall ( (lst1 Lst) (lst2 Lst) ) + (=> + (and + (rev_pst nil lst1 lst2) + (srt_pst lst1 true) + (srt_pst lst2 true) + (not (= lst1 nil)) + (not (= (tail lst1) nil)) + ) + false + ) +) ) + + +(check-sat) +(get-model) \ No newline at end of file From 39cba782ec0c03ed2a6c39f0921dd9bdc8e863be Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 4 Jul 2018 16:38:17 +0900 Subject: [PATCH 11/94] datatypes support in check mode --- rsc/inactive/adt/simple-adt-horn.smt2 | 5 ++- src/check/mod.rs | 7 +++ src/check/parse.rs | 61 ++++++++++++++++++--------- src/dtyp/mod.rs | 6 ++- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/rsc/inactive/adt/simple-adt-horn.smt2 b/rsc/inactive/adt/simple-adt-horn.smt2 index 0155f351..60dafe6d 100644 --- a/rsc/inactive/adt/simple-adt-horn.smt2 +++ b/rsc/inactive/adt/simple-adt-horn.smt2 @@ -7,7 +7,7 @@ (declare-fun I1 (Pair) Bool) (declare-fun I2 (Pair) Bool) -(assert (I1 (P 0 true))) +(assert (forall ((unused Bool)) (I1 (P 0 true)))) (assert (forall ((p Pair)) (=> (I1 p) (I2 (P (+ (left p) 1) (not (right p))))))) (assert (forall ((p Pair)) @@ -16,4 +16,5 @@ (assert (forall ((p Pair)) (=> (I1 p) (and (>= (left p) 0) (right p))))) -(check-sat) \ No newline at end of file +(check-sat) +(get-model) \ No newline at end of file diff --git a/src/check/mod.rs b/src/check/mod.rs index 9349dcc0..2b07cbb8 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -80,6 +80,8 @@ pub struct Clause { /// Data from the input file. pub struct Input { + /// Unknown stuff. + pub unknown: Vec, /// Predicate declarations. pub pred_decs: Vec, /// Function definitions. @@ -222,6 +224,11 @@ impl Data { } in & self.input.clauses { solver.reset() ? ; + for unknown in & self.input.unknown { + use std::io::Write ; + writeln!(solver, "{}", unknown) ? + } + // Define all functions. for & FunDef { ref name, ref args, ref typ, ref body diff --git a/src/check/parse.rs b/src/check/parse.rs index 6218a2cd..3b3730f4 100644 --- a/src/check/parse.rs +++ b/src/check/parse.rs @@ -8,6 +8,9 @@ use check::* ; /// Parser. #[derive(Clone)] pub struct InParser<'a> { + /// Unknown stuff. Datatype declarations, recursive function definitions and + /// such. + pub unknown: Vec, /// Predicate definitions. pub pred_defs: Vec, /// Predicate declarations. @@ -25,6 +28,7 @@ impl<'a> InParser<'a> { /// Constructor. pub fn new(s: & 'a str) -> Self { InParser { + unknown: vec![], pred_defs: vec![], pred_decs: vec![], fun_defs: vec![], clauses: vec![], chars: s.chars(), buf: vec![] @@ -327,17 +331,11 @@ impl<'a> InParser<'a> { self.char('(') ? ; self.ws_cmt() ; let mut sig = vec![] ; - loop { - if let Some(t) = self.typ_opt() ? { - sig.push(t) - } else { - break - } + while ! self.char_opt(')') { + sig.push( self.sexpr() ? ) ; self.ws_cmt() } self.ws_cmt() ; - self.char(')') ? ; - self.ws_cmt() ; self.tag("Bool") ? ; self.pred_decs.push( @@ -355,7 +353,7 @@ impl<'a> InParser<'a> { while self.char_opt('(') { let id = self.ident() ? ; self.ws_cmt() ; - let ty = self.typ() ? ; + let ty = self.sexpr() ? ; self.ws_cmt() ; self.char(')') ? ; self.ws_cmt() ; @@ -441,6 +439,29 @@ impl<'a> InParser<'a> { Ok(true) } + /// Parses anything. + fn parse_unknown(& mut self) -> Res<()> { + let mut s = "(".to_string() ; + + let mut count = 1 ; + + while let Some(char) = self.next() { + s.push(char) ; + match char { + ')' => count -= 1, + '(' => count += 1, + _ => (), + } + if count == 0 { + self.backtrack( vec![')'] ) ; + self.unknown.push(s) ; + return Ok(()) + } + } + + bail!("expected closing paren, found ") + } + /// Parses an `smt2` file. pub fn parse_input(mut self) -> Res { self.ws_cmt() ; @@ -459,16 +480,17 @@ impl<'a> InParser<'a> { || self.tag_opt("exit") { () } else { - print!("> `") ; - while let Some(next) = self.next() { - if next != '\n' { - print!("{}", next) - } else { - break - } - } - println!("`") ; - bail!("expected item") + self.parse_unknown() ? + // print!("> `") ; + // while let Some(next) = self.next() { + // if next != '\n' { + // print!("{}", next) + // } else { + // break + // } + // } + // println!("`") ; + // bail!("expected item") } self.ws_cmt() ; @@ -491,6 +513,7 @@ impl<'a> InParser<'a> { Ok( Input { + unknown: self.unknown, pred_decs: self.pred_decs, fun_defs: self.fun_defs, clauses: self.clauses, diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index c578c60d..68a9ab49 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -380,14 +380,16 @@ pub fn mk(mut dtyp: RDTyp) -> Res { for (constructor, args) in & dtyp.news { let mut recursive = false ; for (_, ty) in args { - if ty.mentions_dtyp(& dtyp.name) { + let rec = ty.mentions_dtyp(& dtyp.name) ; + if rec { recursive = true ; break } } - if ! recursive { + if recursive { rec_count += 1 ; + } else { let default = default.get_or_insert_with( || (constructor.clone(), args.len()) ) ; From 52fd7269b6b96abf141ef9da25104f97278f40d8 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 9 Jul 2018 19:02:03 +0900 Subject: [PATCH 12/94] define-funs-rec: parsing and terms, no evaluation --- rsc/inactive/adt/sorted_len.smt2 | 120 ++++++++++ src/common/config.rs | 2 + src/common/consts.rs | 11 +- src/common/smt.rs | 10 +- src/dtyp/mod.rs | 9 +- src/errors.rs | 4 + src/fun/mod.rs | 291 ++++++++++++++++-------- src/instance/info.rs | 2 +- src/instance/instance/mod.rs | 2 +- src/parse/mod.rs | 376 +++++++++++++++++++++++++++---- src/term/factory.rs | 85 +++---- src/term/fold.rs | 2 + src/term/leaf_iter.rs | 1 + src/term/mod.rs | 152 +++++++------ src/term/tterms.rs | 39 ++-- 15 files changed, 841 insertions(+), 265 deletions(-) create mode 100644 rsc/inactive/adt/sorted_len.smt2 diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 new file mode 100644 index 00000000..36fd2f01 --- /dev/null +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -0,0 +1,120 @@ +(set-logic HORN) + +(declare-datatypes () ( + (Lst nil (cons (head Int) (tail Lst))) +) ) + +(define-funs-rec + ( + (len ( (l Lst) ) Int) + ) + ( + (ite + (= l nil) + 0 + (+ 1 (len (tail l))) + ) + ) +) + +; let rev = +; let rec loop acc = function +; | [] -> acc +; | h :: t -> loop (h :: acc) t +; in +; loop [] + +; Post-condition. +(declare-fun + rev_pst ( Lst Lst Lst ) Bool +) +; Terminal case. +(assert + (forall ( (acc Lst) ) + (rev_pst acc nil acc) +) ) +; Recursive case. +(assert + (forall ( (acc Lst) (lst Lst) (res Lst) ) + (=> + (and + (not (= lst nil)) + (rev_pst + (cons (head lst) acc) + (tail lst) + res + ) + ) + (rev_pst acc lst res) + ) +) ) + + +; let rec sorted = function +; | nil | _ :: nil => true +; | h1 :: h2 :: t => (h1 < h2) and (sorted (h2 :: t)) +; (* STRICTLY sorted~~~~~^ *) + +; Post-condition. +(declare-fun + srt_pst ( Lst Bool ) Bool +) +; Terminal cases. +(assert + (forall ( (unused Bool) ) + (srt_pst nil true) +) ) +(assert + (forall ( (hd Int) ) + (srt_pst (cons hd nil) true) +) ) +(assert + (forall ( (lst Lst) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (not (< (head lst) (head (tail lst)))) + ) + (srt_pst lst false) + ) +) ) +; Recursive case. +(assert + (forall ( (lst Lst) (res Bool) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (< (head lst) (head (tail lst))) + (srt_pst (tail lst) res) + ) + (srt_pst lst res) + ) +) ) + + +; let main lst = +; if lst = (rev lst) +; and (sorted lst) +; and (sorted (rev lst)) +; then match lst +; | nil | _ :: nil => () +; | _ => assert false +(assert + (forall ( (lst1 Lst) (lst2 Lst) ) + (=> + (and + (rev_pst nil lst1 lst2) + (srt_pst lst1 true) + (srt_pst lst2 true) + (not (= lst1 nil)) + (not (= (tail lst1) nil)) + ) + false + ) +) ) + + +(check-sat) +(get-model) \ No newline at end of file diff --git a/src/common/config.rs b/src/common/config.rs index 671cbb5b..77db1fdc 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -82,6 +82,8 @@ impl SmtConf { /// Spawns a solver. /// + /// Performs the solver initialization step given by `common::smt::init`. + /// /// If logging is active, will log to `.smt2`. pub fn spawn( & self, name: & 'static str, parser: Parser, instance: I diff --git a/src/common/consts.rs b/src/common/consts.rs index af6f5235..568fe9cd 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -153,9 +153,14 @@ pub mod keywords { keys { dec_dtyp ("declare-datatypes", doc = "Datatype declaration keyword.") dec_fun ("declare-fun", doc = "Predicate declaration keyword.") - def_fun ("define-fun", doc = "Predicate definition keyword.") - def_funs ( - "define-funs-rec", doc = "Multiple predicate declaration keyword." + def_fun ("define-fun", doc = "Function definition keyword.") + def_fun_rec ( + "define-fun-rec", + doc = "Recursive function definition keyword." + ) + def_funs_rec ( + "define-funs-rec", + doc = "Multiple recursive functions definition keyword." ) assert ("assert", doc = "Assertion keyword.") diff --git a/src/common/smt.rs b/src/common/smt.rs index 91218315..91339951 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -16,9 +16,11 @@ use data::Constraint ; /// Initial setup for a solver. /// -/// Declares all the datatypes used in the instance. +/// - declares all the datatypes +/// - defines all the functions pub fn init

(solver: & mut Solver

) -> Res<()> { dtyp::write_all(solver, "") ? ; + fun::write_all(solver) ? ; Ok(()) } @@ -526,9 +528,9 @@ impl FullParser { if let Ok(Some(term)) = match val { FPVal::FunDef(ref fun) => { - let mut var_hmap: HashMap< + let mut var_hmap: BTreeMap< & str, VarIdx - > = HashMap::with_capacity( sig.len() ) ; + > = BTreeMap::new() ; for (idx, info) in var_infos.index_iter() { let prev = var_hmap.insert( @@ -667,7 +669,7 @@ impl<'a> ModelParser for FullParser { Ok( FPVal::Val( val::bool(val) ) ) } else if let Ok( Some(term) ) = parser.term_opt( - & vec![].into(), & HashMap::new(), & Instance::new() + & vec![].into(), & BTreeMap::new(), & Instance::new() ) { if let Some(val) = term.val() { Ok( FPVal::Val(val) ) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 68a9ab49..636fc2a5 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -2,14 +2,14 @@ use common::* ; -wrap_usize!{ +wrap_usize! { #[doc = "Type parameter indices."] TPrmIdx #[doc = "Total map from type parameters to something."] map: TPrmMap with iter: TPrmMapIter } -wrap_usize!{ +wrap_usize! { #[doc = "Constructor argument indices."] CArgIdx #[doc = "Total map from constructor arguments to something."] @@ -513,6 +513,9 @@ pub fn write_constructor_map( /// Writes all the datatypes. pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { let decs = get_all() ; + + if decs.is_empty() { return Ok(()) } + let mut known = HashSet::new() ; let dtyp_pref = & format!("{} ", pref) ; @@ -539,6 +542,8 @@ pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { writeln!(w, "{}) )", pref) ? } + writeln!(w) ? ; + Ok(()) } diff --git a/src/errors.rs b/src/errors.rs index c9807a7d..72f68e40 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -212,7 +212,11 @@ pub fn print_err(errs: & Error) { println!( "({} \"", conf.bad("error") ) ; + let mut list = vec![] ; for err in errs.iter() { + list.push( err ) + } + for err in list.into_iter().rev() { for line in format!("{}", err).lines() { println!(" {}", line) } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index cba413ed..04f03352 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1,131 +1,242 @@ -/*! Hash consed functions. - -Note that [`Fun`][fun] is extended by [`FunExt`][fun ext] to allow - -- retrieving the name of the function, -- defining the function in a solver. - -[fun]: type.Fun.html (Fun type) -[fun ext]: trait.FunExt.html (FunExt trait) -*/ - -use hashconsing::{ HashConsign, HConser, HConsed } ; +//! Hash consed functions. use common::* ; -/// Type of the term factory. -type Factory = RwLock< HashConsign > ; +/// A function. +pub type Fun = Arc ; +/// Type of the function factory. +type Factory = RwLock< BTreeMap > ; lazy_static! { - /// Term factory. + /// Function factory. static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa ) + BTreeMap::new() ) ; } -/// A hash consed function. -pub type Fun = HConsed ; +/// Creates a function definition. +pub fn mk(fun: RFun) -> Res { + let fun = Arc::new( fun ) ; + let prev = if let Ok(mut f) = factory.write() { + f.insert( fun.name.clone(), fun.clone() ) + } else { + bail!("failed to access function factory (write)") + } ; + if let Some(prev) = prev { + bail!("attempting to redefine function `{}`", prev.name) + } -/// Defines all functions. -pub fn define_all

(solver: & mut Solver

) -> Res<()> { - if let Ok(f) = factory.read() { - f.fold_res( - (), |_, fun| fun.define(solver).map(|_| ()) - ) + Ok(fun) +} + + + +/// Defines all the functions. +pub fn write_all(w: & mut W) -> Res<()> { + let f = if let Ok(f) = factory.read() { + f } else { - bail!("failed to lock factory") + bail!("failed to access function factory (read)") + } ; + + if f.is_empty() { return Ok(()) } + + let mut set = BTreeSet::new() ; + + let mut all = vec![] ; + + for fun in f.values() { + let do_it = set.insert(& fun.name) ; + if ! do_it { continue } + + debug_assert! { all.is_empty() } + + all.reserve( fun.deps.len() + 1 ) ; + all.push(fun) ; + for dep in & fun.deps { + if let Some(dep) = f.get(dep) { + all.push(dep) + } else { + bail!( + "function `{}` depends on unknown function `{}`", + conf.emph(& fun.name), conf.bad(& dep) + ) + } + } + + writeln!(w, "(define-funs-rec (") ? ; + + // Write all signatures. + for fun in & all { + write!(w, " (") ? ; + write!(w, "{} (", fun.name) ? ; + for info in & fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ) ? + } + writeln!(w, " ) {})", fun.typ) ? + } + + writeln!(w, ") (") ? ; + + // Write all definitions. + for fun in all.drain( 0 .. ) { + write!(w, " ") ? ; + fun.def.write( + w, |w, var| var.default_write(w) + ) ? ; + writeln!(w) ? + } + + writeln!(w, ") )") ? } + + writeln!(w) ? ; + + Ok(()) } -/// Extends [`Fun`][fun] with function declaration. -/// -/// [fun]: type.Fun.html (Fun type) -pub trait FunExt { - /// Defines itself as a function. - /// - /// Returns the name of the function. - fn define

(& self, & mut Solver

) -> Res ; - /// Name of the function. - fn name(& self) -> String ; + + +/// Retrieves the definition of a function. +pub fn get(name: & str) -> Option { + if let Ok(f) = factory.read() { + f.get(name).cloned() + } else { + panic!("failed to access function factory (read)") + } } -impl FunExt for Fun { - fn define

(& self, solver: & mut Solver

) -> Res { - use smt::SmtTerm ; - let name = self.name() ; - let sig: Vec<_> = self.sig.index_iter().map( - |(var, typ)| (var, typ.get()) - ).collect() ; - solver.define_fun( - & name, & sig, self.out.get(), & SmtTerm::new(& self.def) - ) ? ; - Ok(name) + + +/// Types and creates a function application. +pub fn type_apply( + name: String, var_info: & VarInfos, out: & Typ, args: Vec +) -> Result { + if args.len() != var_info.len() { + return Err( + TypError::Msg( + format!( + "function `{}` is applied to {} arguments, expected {}", + conf.emph(name), args.len(), var_info.len() + ) + ) + ) } - fn name(& self) -> String { - format!("hoice_reserved_fun_{}", self.uid()) + + for (arg, info) in args.iter().zip( var_info.iter() ) { + if ! arg.typ().is_compatible( & info.typ ) { + return Err( + TypError::Typ { + expected: Some( info.typ.clone() ), + obtained: arg.typ().clone(), + index: * info.idx, + } + ) + } } + + Ok( + term::fun( out.clone(), name, args ) + ) } +/// Creates a function application. +pub fn apply( + name: String, args: Vec +) -> Result { + use ::errors::TypError ; + + let def = if let Some(def) = get(& name) { def } else { + return Err( + TypError::Msg( format!("unknown function `{}`", conf.bad(name)) ) + ) + } ; -/// Creates a hash consed function. -pub fn mk>(val: F) -> Fun { - factory.mk(val.into()) + type_apply(name, & def.sig, & def.typ, args) } -/// Functions. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] + + + + + +/// Real structure for functions. +#[derive(Debug, Clone)] pub struct RFun { - /// Input signature. - sig: Sig, - /// Output type. - out: Typ, + /// Name. + pub name: String, + /// Other functions this function depends on. + pub deps: BTreeSet, + /// Signature. + /// + /// The string stored is the original name of the argument. + pub sig: VarInfos, + /// Type. + pub typ: Typ, /// Definition. - def: Term, + pub def: Term, } -impl RFun { - /// Constructor. - pub fn new(sig: Sig, out: Typ, def: Term) -> Self { - debug_assert_eq! { out, def.typ() } - RFun { sig, out, def } - } - /// Signature accessor. - pub fn sig(& self) -> (& Sig, & Typ) { - (& self.sig, & self.out) +impl PartialEq for RFun { + fn eq(& self, other: & Self) -> bool { + self.name == other.name } +} +impl Eq for RFun {} - /// Definition accessor. - pub fn def(& self) -> & Term { - & self.def +impl PartialOrd for RFun { + fn partial_cmp(& self, other: & Self) -> Option< ::std::cmp::Ordering > { + self.name.partial_cmp(& other.name) } - - /// Function evaluation. - pub fn eval(& self, model: & E) -> Res { - debug_assert_eq! { self.sig.len(), model.len() } - debug_assert! {{ - for (var, typ) in self.sig.index_iter() { - debug_assert_eq! { * typ, model.get(var).typ() } - } - true - }} - self.def.eval(model) +} +impl Ord for RFun { + fn cmp(& self, other: & Self) -> ::std::cmp::Ordering { + self.name.cmp(& other.name) } } +impl ::std::hash::Hash for RFun { + fn hash(& self, state: & mut H) { + self.name.hash(state) + } +} +impl RFun { + /// Constructor. + /// + /// The dependencies are initially empty, and the definition is set to + /// `true`. + pub fn new>( + name: S, sig: VarInfos, typ: Typ + ) -> Self { + let name = name.into() ; + RFun { name, deps: BTreeSet::new(), sig, typ, def: term::tru() } + } -impl Into for (Sig, Term) { - fn into(self) -> RFun { - let (sig, term) = self ; - RFun::new(sig, term.typ(), term) + /// Insert a dependency. + /// + /// Only inserts if `dep` is not `self.name`. + pub fn insert_dep>(& mut self, dep: S) -> bool { + let dep = dep.into() ; + if self.name == dep { + false + } else { + self.deps.insert(dep) + } } -} -impl Into for (Sig, Typ, Term) { - fn into(self) -> RFun { - let (sig, out, term) = self ; - RFun::new(sig, out, term) + /// Sets the definition of a function. + /// + /// # Panics + /// + /// - if `self.def` is not `term::tru()` + pub fn set_def(& mut self, def: Term) { + match * self.def { + RTerm::Cst(ref cst) if cst.is_true() => (), + _ => panic!("trying to set the definition of a function twice"), + } + self.def = def } } \ No newline at end of file diff --git a/src/instance/info.rs b/src/instance/info.rs index 298467da..3d1bb079 100644 --- a/src/instance/info.rs +++ b/src/instance/info.rs @@ -7,7 +7,7 @@ use common::* ; use super::Typ ; /// Variable info. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VarInfo { /// Variable's name. pub name: String, diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index d0d724e8..d7206a72 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -1260,7 +1260,7 @@ impl Instance { } else { write!( - w, "{}({} (", pref, keywords::cmd::def_funs + w, "{}({} (", pref, keywords::cmd::def_funs_rec ) ? ; for & (pred, _) in defs { write!(w, "\n{} {} ", pref, self[pred].name) ? ; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index b55a2e70..5a637ca0 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -210,6 +210,8 @@ enum FrameOp { DTypNew(String, DTyp), /// A datatype selector. DTypSlc(String), + /// A function application. + Fun(String), } impl FrameOp { /// String representation for a frame operator. @@ -224,6 +226,7 @@ impl FrameOp { "`{}` constructor ({})", name, typ ), FrameOp::DTypSlc(name) => format!("`{}` selector", name), + FrameOp::Fun(name) => format!("`{}` function", name), } } } @@ -295,7 +298,7 @@ pub struct ParserCxt { /// Memory for backtracking. mem: Vec, /// Map from predicate names to predicate indices. - pred_name_map: HashMap, + pred_name_map: BTreeMap, } impl ParserCxt { /// Constructor. @@ -303,7 +306,7 @@ impl ParserCxt { ParserCxt { term_stack: Vec::with_capacity(17), mem: Vec::with_capacity(17), - pred_name_map: HashMap::with_capacity(42), + pred_name_map: BTreeMap::new(), } } /// Generates a parser from itself. @@ -318,6 +321,7 @@ impl ParserCxt { cursor: 0, line_off, bindings: Vec::with_capacity(7), + functions: BTreeMap::new(), _profiler, } } @@ -355,7 +359,11 @@ pub struct Parser<'cxt, 's> { /// Line offset (for errors). line_off: usize, /// Stack of bindings. - bindings: Vec< HashMap<& 's str, PTTerms> >, + bindings: Vec< BTreeMap<& 's str, PTTerms> >, + /// Functions we are currently parsing. + /// + /// Only used when parsing a `define-funs-rec`. + functions: BTreeMap<& 's str, (VarInfos, Typ)>, /// Profiler. _profiler: & 'cxt Profiler, } @@ -495,6 +503,33 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } + /// Parses a word (a tag followed by an illegal ident character). + pub fn word(& mut self, word: & str) -> Res<()> { + if self.word_opt(word) { + Ok(()) + } else { + bail!( + self.error_here( + format!("expected `{}`", conf.emph(word)) + ) + ) + } + } + /// Parses a word (a tag followed by an illegal ident character). + pub fn word_opt(& mut self, word: & str) -> bool { + let pos = self.pos() ; + if self.tag_opt(word) { + if self.legal_id_char() { + self.backtrack_to(pos) ; + false + } else { + true + } + } else { + false + } + } + /// Parses a string or fails. pub fn tag(& mut self, tag: & str) -> Res<()> { if self.tag_opt(tag) { @@ -657,7 +692,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses a set-info. fn set_info(& mut self) -> Res { - if ! self.tag_opt("set-info") { + if ! self.word_opt("set-info") { return Ok(false) } self.ws_cmt() ; @@ -681,7 +716,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Set-option. fn set_option(& mut self) -> Res< Option<(& 's str, & 's str)> > { let start_pos = self.pos() ; - if ! self.tag_opt("set-option") { + if ! self.word_opt("set-option") { return Ok(None) } self.ws_cmt() ; @@ -717,7 +752,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses an echo. fn echo(& mut self) -> Res< Option<& 's str> > { - if ! self.tag_opt("echo") { + if ! self.word_opt("echo") { return Ok(None) } self.ws_cmt() ; @@ -735,12 +770,12 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses a set-logic. fn set_logic(& mut self) -> Res { - if ! self.tag_opt("set-logic") { + if ! self.word_opt("set-logic") { return Ok(false) } self.ws_cmt() ; - if ! self.tag_opt("HORN") { + if ! self.word_opt("HORN") { bail!( self.error_here("unknown logic: ") ) } Ok(true) @@ -1102,9 +1137,199 @@ impl<'cxt, 's> Parser<'cxt, 's> { + + + /// Parses some recursive function declarations. + fn define_funs_rec(& mut self, instance: & Instance) -> Res { + if ! self.word_opt(keywords::cmd::def_funs_rec) { + return Ok(false) + } + + use fun::RFun ; + + self.functions.clear() ; + + let mut funs: Vec<(RFun, Pos, _)> = vec![] ; + + self.ws_cmt() ; + self.tag("(").chain_err( + || "opening the list of function declarations" + ) ? ; + self.ws_cmt() ; + + + // Parse all declarations. + while ! self.tag_opt(")") { + + self.tag("(").chain_err( + || "opening a function declaration" + ) ? ; + self.ws_cmt() ; + + let (pos, name) = self.ident().chain_err( + || "at the start of the function declaration" + ) ? ; + self.ws_cmt() ; + + self.ws_cmt() ; + self.tag("(").chain_err( + || format!( + "opening function `{}`'s arguments", conf.emph(name) + ) + ) ? ; + self.ws_cmt() ; + + let mut args = VarInfos::new() ; + let mut arg_map = BTreeMap::new() ; + + // Parse the signature of this function. + while ! self.tag_opt(")") { + self.tag("(") ? ; + self.ws_cmt() ; + + let (pos, arg_name) = self.ident().chain_err( + || format!( + "in function `{}`'s signature (argument name)", + conf.emph(name) + ) + ) ? ; + self.ws_cmt() ; + + let sort = self.sort().chain_err( + || format!( + "for argument `{}` of function `{}`", + conf.emph(arg_name), conf.emph(name) + ) + ) ? ; + + let idx = args.next_index() ; + args.push( + VarInfo::new( arg_name.into(), sort, idx ) + ) ; + + if arg_map.insert(arg_name, idx).is_some() { + bail!( + self.error( + pos, format!( + "found two arguments named `{}` \ + in function `{}`'s declaration", + conf.bad(arg_name), conf.emph(name) + ) + ) + ) + } + + self.ws_cmt() ; + self.tag(")").chain_err( + || format!( + "closing argument `{}` of function `{}`", + conf.emph(arg_name), conf.emph(name) + ) + ) ? ; + self.ws_cmt() + } + + self.ws_cmt() ; + let typ = self.sort().chain_err( + || format!( + "sort of function `{}`", conf.emph(name) + ) + ) ? ; + + let mut fun = RFun::new(name, args, typ) ; + + // Check this is the first time we see this function and populate + // dependencies. + for (other, other_pos, _) in & mut funs { + if other.name == fun.name { + let e: Error = self.error( + pos, format!( "found two functions named `{}`", conf.bad(name) ) + ).into() ; + bail!( + e.chain_err( || self.error(* other_pos, "first appearance") ) + ) + } + + other.insert_dep( fun.name.clone() ) ; + fun.insert_dep( other.name.clone() ) ; + } + + let prev = self.functions.insert( + name, (fun.sig.clone(), fun.typ.clone()) + ) ; + debug_assert! { prev.is_none() } + + funs.push( (fun, pos, arg_map) ) ; + + self.ws_cmt() ; + self.tag(")").chain_err( + || format!("closing function `{}`'s declaration", conf.emph(name)) + ) ? ; + self.ws_cmt() + } + + self.ws_cmt() ; + self.tag("(").chain_err( + || "opening the list of function definitions" + ) ? ; + self.ws_cmt() ; + + let mut final_funs = Vec::with_capacity( funs.len() ) ; + + // Parse all definitions. + for (mut fun, pos, var_map) in funs { + + if let Some(term) = self.term_opt( + & fun.sig, & var_map, instance + ).chain_err( + || format!( + "while parsing definition (term) for function `{}`", + conf.emph(& fun.name) + ) + ).chain_err( + || self.error(pos, "declared here") + ) ? { + + // Success. + fun.set_def(term) ; + self.ws_cmt() + + } else { + let e: Error = self.error_here( + format!( + "expected definition (term) for function `{}`", + conf.emph(& fun.name) + ) + ).into() ; + bail!( + e.chain_err( + || self.error(pos, "declared here") + ) + ) + } + + let fun = fun::mk(fun).chain_err( + || self.error(pos, "while registering this function") + ) ; + + final_funs.push(fun) + } + + self.ws_cmt() ; + self.tag(")").chain_err( + || "closing the list of function definitions" + ) ? ; + + Ok(true) + } + + + + + /// Datatype declaration. fn dtyp_dec(& mut self) -> Res { - if ! self.tag_opt(keywords::cmd::dec_dtyp) { + if ! self.word_opt(keywords::cmd::dec_dtyp) { return Ok(false) } @@ -1256,7 +1481,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Predicate declaration. fn pred_dec(& mut self, instance: & mut Instance) -> Res { - if ! self.tag_opt(keywords::cmd::dec_fun) { + if ! self.word_opt(keywords::cmd::dec_fun) { return Ok(false) } @@ -1276,7 +1501,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; self.tag(")") ? ; self.ws_cmt() ; - if ! self.tag_opt("Bool") { + if ! self.word_opt("Bool") { bail!( self.error_here("expected Bool sort") ) @@ -1304,7 +1529,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses some arguments `( ( ) ... )`. fn args( & mut self, - var_map: & mut VarInfos, hash_map: & mut HashMap<& 's str, VarIdx> + var_map: & mut VarInfos, hash_map: & mut BTreeMap<& 's str, VarIdx> ) -> Res<()> { self.tag("(") ? ; @@ -1332,7 +1557,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { } self.tag(")") ? ; var_map.shrink_to_fit() ; - hash_map.shrink_to_fit() ; Ok(()) } @@ -1363,7 +1587,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Pushes a binding scopes. fn push_bind(& mut self) { - self.bindings.push( HashMap::with_capacity(17) ) + self.bindings.push( BTreeMap::new() ) } /// Pops a binding scope. fn pop_bind(& mut self) -> Res<()> { @@ -1409,7 +1633,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn let_bindings( & mut self, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance ) -> Res { let mut n = 0 ; @@ -1657,6 +1881,10 @@ impl<'cxt, 's> Parser<'cxt, 's> { name, op_pos, & args_pos, args ), + FrameOp::Fun(name) => self.build_fun_app( + name, op_pos, & args_pos, args + ), + _ => unimplemented!(), } } @@ -1690,7 +1918,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) => self.error(op_pos, "in this operator application") } - } + }, Err( TypError::Msg(blah) ) => bail!( self.error(op_pos, blah) ), @@ -1842,6 +2070,63 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } + /// Type checks and builds a datatype selector. + fn build_fun_app( + & self, + name: String, name_pos: Pos, args_pos: & [ Pos ], args: Vec + ) -> Res<(Term, Pos)> { + use errors::TypError ; + + let res = if let Some((var_infos, typ)) = self.functions.get( + & name as & str + ) { + // Function application for one of the functions we are currently parsing + // the definition of? (i.e. the function is not registered yet) + fun::type_apply(name, var_infos, typ, args) + } else { + // Function should already exist. + fun::apply(name, args) + } ; + + // Parsing a application of a function that's already defined. + + match res { + Ok(term) => Ok((term, name_pos)), + + Err( + TypError::Typ { expected, obtained, index } + ) => if let Some(exp) = expected { + err_chain! { + self.error( + args_pos[index], format!( + "expected an expression of sort {}, found {}", exp, obtained + ) + ) + => self.error(name_pos, "in this function application") + } + } else { + err_chain! { + self.error( + args_pos[index], format!( + "expected the expression starting here has sort {} \ + which is illegal", obtained + ) + ) + => self.error(name_pos, "in this function application") + } + }, + + Err( TypError::Msg(blah) ) => { + let e: Error = blah.into() ; + bail!( + e.chain_err( + || self.error(name_pos, "in this function application") + ) + ) + }, + } + } + } @@ -1960,7 +2245,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { pub fn term_opt( & mut self, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance ) -> Res< Option > { debug_assert! { self.cxt.term_stack.is_empty() } @@ -2102,7 +2387,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { continue 'read_kids } else if dtyp::is_selector(id) { - let frame = TermFrame::new ( + let frame = TermFrame::new( FrameOp::DTypSlc( id.into() ), op_pos, bind_count @@ -2110,6 +2395,14 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.cxt.term_stack.push(frame) ; continue 'read_kids + } else if self.functions.get(id).is_some() + || fun::get(id).is_some() { + let frame = TermFrame::new( + FrameOp::Fun( id.into() ), + op_pos, bind_count + ) ; + self.cxt.term_stack.push(frame) ; + continue 'read_kids } if self.cxt.term_stack.is_empty() { @@ -2201,7 +2494,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn define_fun( & mut self, instance: & mut Instance ) -> Res { - if ! self.tag_opt(keywords::cmd::def_fun) { + if ! self.word_opt(keywords::cmd::def_fun) { return Ok(false) } conf.check_timeout() ? ; @@ -2211,7 +2504,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; let mut var_info = VarInfos::new() ; - let mut map = HashMap::new() ; + let mut map = BTreeMap::new() ; self.args(& mut var_info, & mut map) ? ; self.ws_cmt() ; @@ -2257,7 +2550,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn ptterm_args( & mut self, var_map: & VarInfos, - map : & HashMap<& 's str, VarIdx>, + map : & BTreeMap<& 's str, VarIdx>, instance: & Instance ) -> Res< VarMap<(Pos, PTTerms)> > { let mut res = VarMap::with_capacity(11) ; @@ -2290,7 +2583,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { pred: PrdIdx, pred_pos: Pos, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance ) -> Res< Option > { let mut args = VarMap::with_capacity(11) ; @@ -2359,7 +2652,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn top_term( & mut self, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance, ) -> Res { if let Some(res) = self.top_term_opt(var_map, map, instance) ? { @@ -2372,7 +2665,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn top_term_opt( & mut self, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance, ) -> Res< Option< PTTerms > > { conf.check_timeout() ? ; @@ -2520,7 +2813,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn parse_ptterms( & mut self, var_map: & VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & Instance, ) -> Res { enum Frame { @@ -2732,12 +3025,12 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn forall( & mut self, instance: & mut Instance ) -> Res< Option > { - if ! self.tag_opt(keywords::forall) { + if ! self.word_opt(keywords::forall) { return Ok(None) } let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = ( - VarMap::with_capacity(11), HashMap::with_capacity(11), true, 0 + VarMap::with_capacity(11), BTreeMap::new(), true, 0 ) ; while parse_args { @@ -2792,22 +3085,22 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn nexists( & mut self, instance: & mut Instance ) -> Res< Option > { - if ! self.tag_opt(keywords::op::not_) { + if ! self.word_opt(keywords::op::not_) { return Ok(None) } self.ws_cmt() ; let outter_bind_count = self.let_bindings( - & VarMap::new(), & HashMap::new(), instance + & VarMap::new(), & BTreeMap::new(), instance ) ? ; self.ws_cmt() ; self.tag("(") ? ; self.ws_cmt() ; - self.tag(keywords::exists) ? ; + self.word(keywords::exists) ? ; let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = ( - VarMap::with_capacity(11), HashMap::with_capacity(11), true, 0 + VarMap::with_capacity(11), BTreeMap::new(), true, 0 ) ; while parse_args { @@ -2850,7 +3143,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn parse_clause( & mut self, var_map: VarInfos, - map: & HashMap<& 's str, VarIdx>, + map: & BTreeMap<& 's str, VarIdx>, instance: & mut Instance, negated: bool, ) -> Res< ClauseRes > { @@ -2945,7 +3238,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses an assert. fn assert(& mut self, instance: & mut Instance) -> Res { - if ! self.tag_opt(keywords::cmd::assert) { + if ! self.word_opt(keywords::cmd::assert) { return Ok(false) } @@ -2968,7 +3261,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { } ; let bind_count = self.let_bindings( - & VarMap::new(), & HashMap::new(), instance + & VarMap::new(), & BTreeMap::new(), instance ) ? ; let idx = if self.tag_opt("(") { @@ -3003,7 +3296,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { if tagged { self.ws_cmt() ; - self.tag(":named").chain_err( + self.word(":named").chain_err( || "unexpected tag" ) ? ; self.ws_cmt() ; @@ -3024,32 +3317,32 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Parses a check-sat. fn check_sat(& mut self) -> bool { - self.tag_opt(keywords::cmd::check_sat) + self.word_opt(keywords::cmd::check_sat) } /// Parses a get-model. fn get_model(& mut self) -> bool { - self.tag_opt(keywords::cmd::get_model) + self.word_opt(keywords::cmd::get_model) } /// Parses a get-unsat-core. fn get_unsat_core(& mut self) -> bool { - self.tag_opt(keywords::cmd::get_unsat_core) + self.word_opt(keywords::cmd::get_unsat_core) } /// Parses a get-proof. fn get_proof(& mut self) -> bool { - self.tag_opt(keywords::cmd::get_proof) + self.word_opt(keywords::cmd::get_proof) } /// Parses an exit command. fn exit(& mut self) -> bool { - self.tag_opt(keywords::cmd::exit) + self.word_opt(keywords::cmd::exit) } /// Parses an reset command. fn reset(& mut self) -> bool { - self.tag_opt(keywords::cmd::reset) + self.word_opt(keywords::cmd::reset) } /// Parses items, returns true if it found a check-sat. @@ -3085,6 +3378,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else if self.set_logic() ? || self.pred_dec(instance) ? || self.define_fun(instance) ? + || self.define_funs_rec(instance) ? || self.assert(instance) ? || self.dtyp_dec() ? { Parsed::Items diff --git a/src/term/factory.rs b/src/term/factory.rs index 4ca4f8b9..eaee3148 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -42,6 +42,9 @@ fn scan_vars(t: & Term) -> VarSet { to_do.push(arg) }, RTerm::DTypSlc { term, .. } => to_do.push(term), + RTerm::Fun { args, .. } => for arg in args { + to_do.push(arg) + }, } } set.shrink_to_fit() ; @@ -89,31 +92,31 @@ where F: FnMut(VarIdx) { } /// Creates a term. -#[inline(always)] +#[inline] pub fn term(t: RTerm) -> Term { factory.mk(t) } /// Creates a variable. -#[inline(always)] +#[inline] pub fn var>(v: V, typ: Typ) -> Term { factory.mk( RTerm::Var(typ, v.into()) ) } /// Creates an integer variable. -#[inline(always)] +#[inline] pub fn int_var>(v: V) -> Term { factory.mk( RTerm::Var(typ::int(), v.into()) ) } /// Creates a real variable. -#[inline(always)] +#[inline] pub fn real_var>(v: V) -> Term { factory.mk( RTerm::Var(typ::real(), v.into()) ) } /// Creates a boolean variable. -#[inline(always)] +#[inline] pub fn bool_var>(v: V) -> Term { factory.mk( RTerm::Var(typ::bool(), v.into()) ) } @@ -129,13 +132,13 @@ pub fn cst>(val: V) -> Term { } /// Creates an integer constant. -#[inline(always)] +#[inline] pub fn int>(i: I) -> Term { let i = i.into() ; factory.mk( RTerm::Cst( val::int(i) ) ) } /// Creates a real constant. -#[inline(always)] +#[inline] pub fn real>(r: R) -> Term { let r = r.into() ; factory.mk( RTerm::Cst( val::real(r) ) ) @@ -146,66 +149,66 @@ pub fn real_of_float(f: f64) -> Term { real( rat_of_float(f) ) } /// Creates the constant `0`. -#[inline(always)] +#[inline] pub fn int_zero() -> Term { int( Int::zero() ) } /// Creates the constant `1`. -#[inline(always)] +#[inline] pub fn int_one() -> Term { int( Int::one() ) } /// Creates the constant `0`. -#[inline(always)] +#[inline] pub fn real_zero() -> Term { real( Rat::zero() ) } /// Creates the constant `1`. -#[inline(always)] +#[inline] pub fn real_one() -> Term { real( Rat::one() ) } /// Creates a boolean. -#[inline(always)] +#[inline] pub fn bool(b: bool) -> Term { factory.mk( RTerm::Cst( val::bool(b) ) ) } /// Creates the constant `true`. -#[inline(always)] +#[inline] pub fn tru() -> Term { bool(true) } /// Creates the constant `false`. -#[inline(always)] +#[inline] pub fn fls() -> Term { bool(false) } /// If-then-else. -#[inline(always)] +#[inline] pub fn ite(c: Term, t: Term, e: Term) -> Term { app(Op::Ite, vec![c, t, e]) } /// Implication. -#[inline(always)] +#[inline] pub fn implies(lhs: Term, rhs: Term) -> Term { app(Op::Impl, vec![lhs, rhs]) } /// Negates a term. -#[inline(always)] +#[inline] pub fn not(term: Term) -> Term { app(Op::Not, vec![term]) } /// Disjunction. -#[inline(always)] +#[inline] pub fn or(terms: Vec) -> Term { app(Op::Or, terms) } /// Conjunction. -#[inline(always)] +#[inline] pub fn and(terms: Vec) -> Term { app(Op::And, terms) } @@ -235,12 +238,18 @@ pub fn select(array: Term, idx: Term) -> Term { app( Op::Select, vec![ array, idx ] ) } +/// Function application. +#[inline] +pub fn fun(typ: Typ, name: String, args: Vec) -> Term { + factory.mk( RTerm::Fun { typ, name, args } ) +} + /// Creates an operator application. /// /// Assumes the application is well-typed, modulo int to real casting. /// /// Runs [`normalize`](fn.normalize.html) and returns its result. -#[inline(always)] +#[inline] pub fn app(op: Op, args: Vec) -> Term { let typ = expect!( op.type_check(& args) => |e| @@ -341,73 +350,73 @@ pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { /// automatically). /// /// Runs [`normalize`](fn.normalize.html) and returns its result. -#[inline(always)] +#[inline] pub fn try_app(op: Op, args: Vec) -> Result { let typ = op.type_check(& args) ? ; Ok( normalize(op, args, typ) ) } /// Creates a less than or equal to. -#[inline(always)] +#[inline] pub fn le(lhs: Term, rhs: Term) -> Term { app(Op::Le, vec![lhs, rhs]) } /// Creates a less than. -#[inline(always)] +#[inline] pub fn lt(lhs: Term, rhs: Term) -> Term { app(Op::Lt, vec![lhs, rhs]) } /// Creates a greater than. -#[inline(always)] +#[inline] pub fn gt(lhs: Term, rhs: Term) -> Term { app(Op::Gt, vec![lhs, rhs]) } /// Creates a greater than or equal to. -#[inline(always)] +#[inline] pub fn ge(lhs: Term, rhs: Term) -> Term { app(Op::Ge, vec![lhs, rhs]) } /// Creates an equality. -#[inline(always)] +#[inline] pub fn eq(lhs: Term, rhs: Term) -> Term { app(Op::Eql, vec![lhs, rhs]) } /// Creates a sum. -#[inline(always)] +#[inline] pub fn add(kids: Vec) -> Term { app(Op::Add, kids) } /// Creates a sum, binary version. -#[inline(always)] +#[inline] pub fn add2(kid_1: Term, kid_2: Term) -> Term { app(Op::Add, vec![kid_1, kid_2]) } /// Creates a subtraction. -#[inline(always)] +#[inline] pub fn sub(kids: Vec) -> Term { app(Op::Sub, kids) } /// Creates a subtraction, binary version. -#[inline(always)] +#[inline] pub fn sub2(kid_1: Term, kid_2: Term) -> Term { app(Op::Sub, vec![kid_1, kid_2]) } /// Creates a unary minus. -#[inline(always)] +#[inline] pub fn u_minus(kid: Term) -> Term { app(Op::Sub, vec![kid]) } /// Creates a multiplication. -#[inline(always)] +#[inline] pub fn mul(kids: Vec) -> Term { app(Op::Mul, kids) } /// Creates a multiplication by a constant. -#[inline(always)] +#[inline] pub fn cmul(cst: V, term: Term) -> Term where V: Into { app( @@ -421,28 +430,28 @@ where V: Into { } /// Creates an integer division. -#[inline(always)] +#[inline] pub fn idiv(kids: Vec) -> Term { app(Op::IDiv, kids) } /// Creates a division. -#[inline(always)] +#[inline] pub fn div(kids: Vec) -> Term { app(Op::Div, kids) } /// Creates a modulo application. -#[inline(always)] +#[inline] pub fn modulo(a: Term, b: Term) -> Term { app(Op::Mod, vec![a, b]) } /// Creates a conversion from `Int` to `Real`. -#[inline(always)] +#[inline] pub fn to_real(int: Term) -> Term { app(Op::ToReal, vec![int]) } /// Creates a conversion from `Real` to `Int`. -#[inline(always)] +#[inline] pub fn to_int(real: Term) -> Term { app(Op::ToInt, vec![real]) } diff --git a/src/term/fold.rs b/src/term/fold.rs index fe8f65c6..fab83829 100644 --- a/src/term/fold.rs +++ b/src/term/fold.rs @@ -213,6 +213,8 @@ SlcF: FnMut(& Typ, & String, Info) -> Result, { continue 'go_down }, + RTerm::Fun { ref typ, ref name, ref args } => unimplemented!(), + } ; diff --git a/src/term/leaf_iter.rs b/src/term/leaf_iter.rs index 5c0bffc2..45b713e0 100644 --- a/src/term/leaf_iter.rs +++ b/src/term/leaf_iter.rs @@ -75,6 +75,7 @@ impl<'a> Iterator for LeafIter<'a> { }, App { ref args, .. } | + Fun { ref args, .. } | DTypNew { ref args, .. } => { self.stack.push( args.iter() ) ; continue 'find_next diff --git a/src/term/mod.rs b/src/term/mod.rs index f4029d0e..76d13ec3 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -132,6 +132,16 @@ pub enum RTerm { /// Argument of the selector. term: Term, }, + + /// A function application. + Fun { + /// Type of this term. + typ: Typ, + /// Function being applied. + name: String, + /// Arguments of the function application. + args: Vec, + } } @@ -139,89 +149,81 @@ pub enum RTerm { impl RTerm { /// The operator and the kids of a term. pub fn app_inspect(& self) -> Option< (Op, & Vec) > { - match * self { - RTerm::App { op, ref args, .. } => Some((op, args)), - _ => None, - } + if let RTerm::App { op, ref args, .. } = * self { + Some((op, args)) + } else { None } } /// Returns the kids of an ite. pub fn ite_inspect(& self) -> Option<(& Term, & Term, & Term)> { - match * self { - RTerm::App { op: Op::Ite, ref args, .. } => { - debug_assert_eq! { args.len(), 3 } - Some( (& args[0], & args[1], & args[2]) ) - }, - _ => None, - } + if let RTerm::App { op: Op::Ite, ref args, .. } = * self { + debug_assert_eq! { args.len(), 3 } + Some( (& args[0], & args[1], & args[2]) ) + } else { None } + } + + /// Inspects a function application. + pub fn fun_inspect(& self) -> Option<(& String, & Vec)> { + if let RTerm::Fun { ref name, ref args, .. } = * self { + Some((name, args)) + } else { None } } /// Returns the kid of a negation. pub fn neg_inspect(& self) -> Option<& Term> { - match * self { - RTerm::App { op: Op::Not, ref args, .. } => { - debug_assert_eq! { args.len(), 1 } - Some(& args[0]) - }, - _ => None, - } + if let RTerm::App { op: Op::Not, ref args, .. } = * self { + debug_assert_eq! { args.len(), 1 } + Some(& args[0]) + } else { None } } /// Returns the kids of conjunctions. pub fn conj_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::And, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::And, ref args, .. } = * self { + Some(args) + } else { None } } /// Returns the kids of disjunctions. pub fn disj_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::Or, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::Or, ref args, .. } = * self { + Some(args) + } else { None } } /// Returns the kids of equalities. pub fn eq_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::Eql, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::Eql, ref args, .. } = * self { + Some(args) + } else { None } } + /// Returns the kids of additions. pub fn add_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::Add, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::Add, ref args, .. } = * self { + Some(args) + } else { None } } /// Returns the kids of subtractions. pub fn sub_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::Sub, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::Sub, ref args, .. } = * self { + Some(args) + } else { None } } /// Returns the kids of multiplications. pub fn mul_inspect(& self) -> Option<& Vec> { - match * self { - RTerm::App { op: Op::Mul, ref args, .. } => Some(args), - _ => None, - } + if let RTerm::App { op: Op::Mul, ref args, .. } = * self { + Some(args) + } else { None } } /// Returns the kids of a constant multiplication. pub fn cmul_inspect(& self) -> Option<(Val, & Term)> { - match * self { - RTerm::App { op: Op::CMul, ref args, .. } => { - if args.len() == 2 { - if let Some(val) = args[0].val() { - return Some((val, & args[1])) - } + if let RTerm::App { op: Op::CMul, ref args, .. } = * self { + if args.len() == 2 { + if let Some(val) = args[0].val() { + return Some((val, & args[1])) } - panic!("illegal c_mul application: {}", self) - }, - _ => None, - } + } + panic!("illegal c_mul application: {}", self) + } else { None } } /// Iterator over over all the leafs of a term. @@ -236,6 +238,8 @@ impl RTerm { while let Some(term) = stack.pop() { f(term) ; match term { + RTerm::DTypNew { args, .. } | + RTerm::Fun { args, .. } | RTerm::App { args, .. } => for term in args { stack.push(term) }, @@ -243,9 +247,6 @@ impl RTerm { RTerm::CArray { term, .. } => stack.push( term.get() ), RTerm::DTypSlc { term, .. } => stack.push( term.get() ), - RTerm::DTypNew { args, .. } => for term in args { - stack.push(term) - }, RTerm::Var(_, _) | RTerm::Cst(_) => (), @@ -256,16 +257,17 @@ impl RTerm { /// Type of the term. pub fn typ(& self) -> Typ { match self { - RTerm::Var(typ, _) => typ.clone(), - RTerm::Cst(val) => val.typ(), RTerm::CArray { typ, term } => typ::array( typ.clone(), term.typ() ), - RTerm::App { typ, .. } => typ.clone(), + RTerm::Cst(val) => val.typ(), - RTerm::DTypSlc { typ, .. } => typ.clone(), + RTerm::Var(typ, _) | + RTerm::App { typ, .. } | + RTerm::Fun { typ, .. } | + RTerm::DTypSlc { typ, .. } | RTerm::DTypNew { typ, .. } => typ.clone(), } } @@ -309,7 +311,9 @@ impl RTerm { write!(w, "{}", sep) ? ; match this_term { + Var(_, v) => write_var(w, * v) ?, + Cst(val) => write!(w, "{}", val) ?, CArray { term, .. } => { @@ -337,9 +341,21 @@ impl RTerm { (args.iter().rev().map(|t| t.get()).collect(), " ", ")") ) }, + + Fun { name, args, .. } => if args.is_empty() { + write!(w, "{}", name) ? + } else { + write!(w, "({}", name) ? ; + stack.push( + (args.iter().rev().map(|t| t.get()).collect(), " ", ")") + ) + }, + } - } else { w.write_all( end.as_bytes() ) ? } + } else { + w.write_all( end.as_bytes() ) ? + } } Ok(()) @@ -567,6 +583,7 @@ impl RTerm { 'go_down: loop { let mut term = match curr { + RTerm::Var(_, idx) => term::var(* idx, nu_typ), RTerm::Cst(val) => if let Ok(val) = val.cast(& nu_typ) { @@ -634,10 +651,14 @@ impl RTerm { RTerm::DTypSlc { typ, name, term } => { debug_assert_eq! { typ, & nu_typ } - term::dtyp_slc( - typ.clone(), name.clone(), term.clone() - ) + term::dtyp_slc( typ.clone(), name.clone(), term.clone() ) }, + + RTerm::Fun { typ, name, args } => { + debug_assert_eq! { typ, & nu_typ } + term::fun( typ.clone(), name.clone(), args.clone() ) + }, + } ; 'go_up: loop { @@ -1141,6 +1162,7 @@ impl RTerm { loop { // println!("inverting {}", term) ; match * term { + RTerm::App { op, ref args, .. } => { let (po, symmetric) = match op { Op::Add => (Op::Sub, true), @@ -1217,10 +1239,12 @@ impl RTerm { RTerm::Var(_, v) => return Some((v, solution)), - RTerm::Cst(_) | + RTerm::Cst ( _ ) | + RTerm::Fun { .. } | RTerm::CArray { .. } | RTerm::DTypNew { .. } | RTerm::DTypSlc { .. } => return None, + } } } diff --git a/src/term/tterms.rs b/src/term/tterms.rs index aa23da26..cb1ee11a 100644 --- a/src/term/tterms.rs +++ b/src/term/tterms.rs @@ -49,45 +49,42 @@ impl TTerm { } /// Boolean corresponding to the top term if it's a bool constant. pub fn bool(& self) -> Option { - match * self { - TTerm::T(ref t) => t.bool(), - _ => None, - } + if let TTerm::T(t) = self { + t.bool() + } else { None } } /// Boolean corresponding to the top term if it's an integer constant. pub fn int(& self) -> Option { - match * self { - TTerm::T(ref t) => t.int(), - _ => None, - } + if let TTerm::T(t) = self { + t.int() + } else { None } } /// The operator and the kids of a top term, if it's an operator application. pub fn app_inspect(& self) -> Option< (Op, & Vec) > { - match * self { - TTerm::T(ref t) => t.app_inspect(), - _ => None, - } + if let TTerm::T(t) = self { + t.app_inspect() + } else { None } } /// If the top term is simply a term, returns that term. #[inline] pub fn term(& self) -> Option<& Term> { - if let TTerm::T(ref t) = * self { Some(t) } else { None } + if let TTerm::T(ref t) = * self { + Some(t) + } else { None } } /// The predicate a top term is an application of, if any. pub fn pred(& self) -> Option { - match * self { - TTerm::P { pred, .. } => Some(pred), - _ => None, - } + if let TTerm::P { pred, .. } = * self { + Some(pred) + } else { None } } /// The arguments of a top term if it's a predicate application. pub fn args(& self) -> Option<& VarTerms> { - match * self { - TTerm::P { ref args, .. } => Some(args), - _ => None, - } + if let TTerm::P { ref args, .. } = * self { + Some(args) + } else { None } } /// Applies some treatment if the top term is a predicate application. From 3696de7bb6b7fbc365324d4373a5e5d7307bff33 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 10 Jul 2018 19:36:05 +0900 Subject: [PATCH 13/94] new zipper for recursive function evaluation --- src/common/mod.rs | 20 ++++ src/term/eval.rs | 101 ++++++++++++++++ src/term/fold.rs | 80 +++++++++++-- src/term/mod.rs | 174 +++++++++++++++------------ src/term/test.rs | 3 + src/term/zip.rs | 289 +++++++++++++++++++++++++++++++++++++++++++++ src/var_to/vals.rs | 4 + 7 files changed, 585 insertions(+), 86 deletions(-) create mode 100644 src/term/eval.rs create mode 100644 src/term/zip.rs diff --git a/src/common/mod.rs b/src/common/mod.rs index 7306d143..a0f46c5e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -393,6 +393,8 @@ pub trait Evaluator { fn get(& self, var: VarIdx) -> & Val ; /// Number of variables the evaluator supports. fn len(& self) -> usize ; + /// Prints itself (for debug). + fn print(& self) ; } impl Evaluator for VarMap { #[inline] @@ -401,6 +403,14 @@ impl Evaluator for VarMap { } #[inline] fn len(& self) -> usize { VarMap::len(self) } + fn print(& self) { + println!("varmap:") ; + print!(" ") ; + for (var, val) in self.index_iter() { + print!("{} -> {}, ", var, val) + } + println!("") + } } impl Evaluator for () { #[inline] @@ -409,6 +419,7 @@ impl Evaluator for () { } #[inline] fn len(& self) -> usize { 0 } + fn print(& self) { println!("()") } } /// This implements a redirection `(map, vals)`, where a variable `var` from /// the term evaluated is evaluated to `vals[ map[var] ]`. @@ -420,6 +431,15 @@ where E: Evaluator { } #[inline] fn len(& self) -> usize { self.0.len() } + fn print(& self) { + println!("varmap<(varidx, typ)>") ; + print!(" ") ; + for (v1, (v2, _)) in self.0.index_iter() { + print!("{} -> {}", v1, v2) + } + println!("") ; + self.1.print() + } } diff --git a/src/term/eval.rs b/src/term/eval.rs new file mode 100644 index 00000000..b036bfca --- /dev/null +++ b/src/term/eval.rs @@ -0,0 +1,101 @@ +//! Term evaluation using the term zipper. + +use common::* ; +use term::zip::* ; + + +/// Term evaluation. +pub fn eval(term: & Term, model: & E) -> Res { + zip::, Val, _, _, _>( + term, + + // Variables and constants. + |zip_null| match zip_null { + ZipNullary::Cst(val) => Ok( val.clone() ), + ZipNullary::Var(_, var) => if var < model.len() { + Ok( model.get(var).clone() ) + } else { + bail!("model is too short ({} / {})", * var, model.len()) + }, + }, + + // Applications. + |op, typ, mut values| match op { + ZipOp::Op(op) => op.eval(values).chain_err( + || format!("while evaluating operator `{}`", op) + ), + + ZipOp::New(name) => Ok( + val::dtyp_new( typ.clone(), name.clone(), values ) + ), + + ZipOp::Slc(name) => if values.len() == 1 { + let value = values.pop().unwrap() ; + if ! value.is_known() { + Ok( val::none( typ.clone() ) ) + } else if let Some( + (ty, constructor, values) + ) = value.dtyp_inspect() { + if let Some((dtyp, _)) = ty.dtyp_inspect() { + + if let Some(selectors) = dtyp.news.get(constructor) { + + let mut res = None ; + for ((selector, _), value) in selectors.iter().zip( + values.iter() + ) { + if selector == name { + res = Some( value.clone() ) + } + } + + if let Some(res) = res { + Ok(res) + } else { + Ok( val::none( typ.clone() ) ) + } + + } else { + bail!( + "unknown constructor `{}` for datatype {}", + conf.bad(constructor), dtyp.name + ) + } + + } else { + bail!("inconsistent type {} for value {}", ty, value) + } + } else { + bail!( + "illegal application of constructor `{}` of `{}` to `{}`", + conf.bad(& name), typ, value + ) + } + } else { + bail!( + "expected one value for datatype selection, found {}", values.len() + ) + }, + + ZipOp::CArray => if values.len() == 1 { + let default = values.pop().unwrap() ; + Ok( val::array( typ.clone(), default ) ) + } else { + bail!( + "expected one value for constant array construction, found {}", + values.len() + ) + }, + + ZipOp::Fun(_) => unimplemented!(), + }, + + |mut frame| if let Some(nu_term) = frame.rgt_args.next() { + Ok( + ZipDo::Trm { nu_term, frame } + ) + } else { + bail!("unreachable") + } + ) +} \ No newline at end of file diff --git a/src/term/fold.rs b/src/term/fold.rs index fab83829..ed6fe992 100644 --- a/src/term/fold.rs +++ b/src/term/fold.rs @@ -5,7 +5,7 @@ use common::* ; use std::slice::Iter ; /// Fold info for terms. -enum FoldInfo<'a, Info> { +pub enum FoldInfo<'a, Info> { /// Constant array. Arr { /// Type. @@ -43,6 +43,18 @@ enum FoldInfo<'a, Info> { /// Name. name: & 'a String, }, + + /// A function application. + Fun { + /// Type. + typ: & 'a Typ, + /// Name. + name: & 'a String, + /// Arguments already processed. + lft_args: Vec, + /// Arguments left to process. + rgt_args: Iter<'a, Term>, + } } @@ -60,11 +72,12 @@ enum FoldInfo<'a, Info> { /// - `ArrF`: will run on the result of folding on arrays /// - `NewF`: will run on the result of folding on datatype constructors /// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold_res( +pub fn fold_res( term: & RTerm, varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF, slcf: SlcF, + funf: FunF, ) -> Res where VarF: FnMut(& Typ, VarIdx) -> Res, @@ -72,8 +85,9 @@ CstF: FnMut(& Val) -> Res, AppF: FnMut(& Typ, Op, Vec) -> Res, ArrF: FnMut(& Typ, Info) -> Res, NewF: FnMut(& Typ, & String, Vec) -> Res, -SlcF: FnMut(& Typ, & String, Info) -> Res, { - fold_custom_res(term, varf, cstf, appf, arrf, newf, slcf) +SlcF: FnMut(& Typ, & String, Info) -> Res, +FunF: FnMut(& Typ, & String, Vec) -> Res, { + fold_custom_res(term, varf, cstf, appf, arrf, newf, slcf, funf) } @@ -88,11 +102,12 @@ SlcF: FnMut(& Typ, & String, Info) -> Res, { /// - `ArrF`: will run on the result of folding on arrays /// - `NewF`: will run on the result of folding on datatype constructors /// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold( +pub fn fold( term: & RTerm, mut varf: VarF, mut cstf: CstF, mut appf: AppF, mut arrf: ArrF, mut newf: NewF, mut slcf: SlcF, + mut funf: FunF, ) -> Info where VarF: FnMut(& Typ, VarIdx) -> Info, @@ -100,8 +115,9 @@ CstF: FnMut(& Val) -> Info, AppF: FnMut(& Typ, Op, Vec) -> Info, ArrF: FnMut(& Typ, Info) -> Info, NewF: FnMut(& Typ, & String, Vec) -> Info, -SlcF: FnMut(& Typ, & String, Info) -> Info, { - fold_custom_res::< Info, (), _, _, _, _, _, _ >( +SlcF: FnMut(& Typ, & String, Info) -> Info, +FunF: FnMut(& Typ, & String, Vec) -> Info, { + fold_custom_res::< Info, (), _, _, _, _, _, _, _ >( term, |t,v| Ok( varf(t, v) ), |v| Ok( cstf(v) ), @@ -109,6 +125,7 @@ SlcF: FnMut(& Typ, & String, Info) -> Info, { |t, i| Ok( arrf(t, i) ), |t, s, i| Ok( newf(t, s, i) ), |t, s, i| Ok( slcf(t, s, i) ), + |t, s, i| Ok( funf(t, s, i) ), ).unwrap() //^^^^^^~~~~ this unwrap is proved safe trivially. } @@ -129,11 +146,14 @@ SlcF: FnMut(& Typ, & String, Info) -> Info, { /// - `ArrF`: will run on the result of folding on arrays /// - `NewF`: will run on the result of folding on datatype constructors /// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold_custom_res( +pub fn fold_custom_res< + Info, E, VarF, CstF, AppF, ArrF, NewF, SlcF, FunF +>( term: & RTerm, mut varf: VarF, mut cstf: CstF, mut appf: AppF, mut arrf: ArrF, mut newf: NewF, mut slcf: SlcF, + mut funf: FunF ) -> Result where VarF: FnMut(& Typ, VarIdx) -> Result, @@ -141,7 +161,8 @@ CstF: FnMut(& Val) -> Result, AppF: FnMut(& Typ, Op, Vec) -> Result, ArrF: FnMut(& Typ, Info) -> Result, NewF: FnMut(& Typ, & String, Vec) -> Result, -SlcF: FnMut(& Typ, & String, Info) -> Result, { +SlcF: FnMut(& Typ, & String, Info) -> Result, +FunF: FnMut(& Typ, & String, Vec) -> Result, { use term::RTerm ; // Stack of stuff to zip on. @@ -213,7 +234,21 @@ SlcF: FnMut(& Typ, & String, Info) -> Result, { continue 'go_down }, - RTerm::Fun { ref typ, ref name, ref args } => unimplemented!(), + RTerm::Fun { ref typ, ref name, ref args } => { + let mut rgt_args = args.iter() ; + let lft_args = Vec::with_capacity( args.len() ) ; + + if let Some(term) = rgt_args.next() { + curr = term ; + stack.push( + FoldInfo::Fun { typ, name, lft_args, rgt_args } + ) ; + + continue 'go_down + } else { + funf(typ, name, lft_args) ? + } + }, } ; @@ -298,6 +333,31 @@ SlcF: FnMut(& Typ, & String, Info) -> Result, { continue 'go_up }, + // Function application, info is for the middle term. + Some( + FoldInfo::Fun { typ, name, mut lft_args, mut rgt_args } + ) => { + lft_args.push(info) ; + + // Are we done? + if let Some(term) = rgt_args.next() { + // Going down `term`. + curr = term ; + // Push back current frame. + stack.push( + FoldInfo::New { typ, name, lft_args, rgt_args } + ) ; + // Go down. + continue 'go_down + + } else { + // No more term to go down into, update info and go up. + info = funf(typ, name, lft_args) ? ; + continue 'go_up + } + + }, + } } diff --git a/src/term/mod.rs b/src/term/mod.rs index 76d13ec3..4573a540 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -67,6 +67,8 @@ mod tterms ; pub mod simplify ; pub mod typ ; mod fold ; +mod zip ; +mod eval ; mod leaf_iter ; pub use self::op::* ; @@ -725,9 +727,13 @@ impl RTerm { /// - `ArrF`: will run on the result of folding on arrays /// - `NewF`: will run on the result of folding on datatype constructors /// - `SlcF`: will run on the result of folding on datatype selectors - pub fn fold( + /// - `SlcF`: will run on the result of folding on function applications + pub fn fold( & self, - varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF, slcf: SlcF + varf: VarF, cstf: CstF, + appf: AppF, arrf: ArrF, + newf: NewF, slcf: SlcF, + funf: FunF, ) -> Info where VarF: FnMut(& Typ, VarIdx) -> Info, @@ -735,8 +741,9 @@ impl RTerm { AppF: FnMut(& Typ, Op, Vec) -> Info, ArrF: FnMut(& Typ, Info) -> Info, NewF: FnMut(& Typ, & String, Vec) -> Info, - SlcF: FnMut(& Typ, & String, Info) -> Info, { - fold::fold(self, varf, cstf, appf, arrf, newf, slcf) + SlcF: FnMut(& Typ, & String, Info) -> Info, + FunF: FnMut(& Typ, & String, Vec) -> Info, { + fold::fold(self, varf, cstf, appf, arrf, newf, slcf, funf) } @@ -755,9 +762,12 @@ impl RTerm { /// - `ArrF`: will run on the result of folding on arrays /// - `NewF`: will run on the result of folding on datatype constructors /// - `SlcF`: will run on the result of folding on datatype selectors - pub fn fold_res( + pub fn fold_res( & self, - varf: VarF, cstf: CstF, appf: AppF, arrf: ArrF, newf: NewF, slcf: SlcF + varf: VarF, cstf: CstF, + appf: AppF, arrf: ArrF, + newf: NewF, slcf: SlcF, + funf: FunF, ) -> Res where VarF: FnMut(& Typ, VarIdx) -> Res, @@ -765,8 +775,9 @@ impl RTerm { AppF: FnMut(& Typ, Op, Vec) -> Res, ArrF: FnMut(& Typ, Info) -> Res, NewF: FnMut(& Typ, & String, Vec) -> Res, - SlcF: FnMut(& Typ, & String, Info) -> Res, { - fold::fold_res(self, varf, cstf, appf, arrf, newf, slcf) + SlcF: FnMut(& Typ, & String, Info) -> Res, + FunF: FnMut(& Typ, & String, Vec) -> Res, { + fold::fold_res(self, varf, cstf, appf, arrf, newf, slcf, funf) } @@ -776,74 +787,78 @@ impl RTerm { /// /// - remove recursive call for constant arrays pub fn eval(& self, model: & E) -> Res { - self.fold_res( - // Variable evaluation. - |_, v| if v < model.len() { - Ok( model.get(v).clone() ) - } else { - bail!("model is too short ({})", model.len()) - }, - - // Constant evaluation. - |val| Ok( val.clone() ), - - // Operator application evaluation. - |_, op, values| op.eval(values).chain_err( - || format!("while evaluating operator `{}`", op) - ), - - // Constant array evaluation. - |typ, default| Ok( - val::array( typ.clone(), default ) - ), - - // Datatype construction. - |typ, name, values| Ok( - val::dtyp_new( typ.clone(), name.clone(), values ) - ), - - // Datatype selection. - |typ, name, value| if ! value.is_known() { - Ok( val::none( typ.clone() ) ) - } else if let Some( - (ty, constructor, values) - ) = value.dtyp_inspect() { - if let Some((dtyp, _)) = ty.dtyp_inspect() { - - if let Some(selectors) = dtyp.news.get(constructor) { - - let mut res = None ; - for ((selector, _), value) in selectors.iter().zip( - values.iter() - ) { - if selector == name { - res = Some( value.clone() ) - } - } - - if let Some(res) = res { - Ok(res) - } else { - Ok( val::none( typ.clone() ) ) - } - - } else { - bail!( - "unknown constructor `{}` for datatype {}", - conf.bad(constructor), dtyp.name - ) - } - - } else { - bail!("inconsistent type {} for value {}", ty, value) - } - } else { - bail!( - "illegal application of constructor `{}` of `{}` to `{}`", - conf.bad(& name), typ, value - ) - } - ) + eval::eval( & factory::term( self.clone() ), model ) + // self.fold_res( + // // Variable evaluation. + // |_, v| if v < model.len() { + // Ok( model.get(v).clone() ) + // } else { + // bail!("model is too short ({})", model.len()) + // }, + + // // Constant evaluation. + // |val| Ok( val.clone() ), + + // // Operator application evaluation. + // |_, op, values| op.eval(values).chain_err( + // || format!("while evaluating operator `{}`", op) + // ), + + // // Constant array evaluation. + // |typ, default| Ok( + // val::array( typ.clone(), default ) + // ), + + // // Datatype construction. + // |typ, name, values| Ok( + // val::dtyp_new( typ.clone(), name.clone(), values ) + // ), + + // // Datatype selection. + // |typ, name, value| if ! value.is_known() { + // Ok( val::none( typ.clone() ) ) + // } else if let Some( + // (ty, constructor, values) + // ) = value.dtyp_inspect() { + // if let Some((dtyp, _)) = ty.dtyp_inspect() { + + // if let Some(selectors) = dtyp.news.get(constructor) { + + // let mut res = None ; + // for ((selector, _), value) in selectors.iter().zip( + // values.iter() + // ) { + // if selector == name { + // res = Some( value.clone() ) + // } + // } + + // if let Some(res) = res { + // Ok(res) + // } else { + // Ok( val::none( typ.clone() ) ) + // } + + // } else { + // bail!( + // "unknown constructor `{}` for datatype {}", + // conf.bad(constructor), dtyp.name + // ) + // } + + // } else { + // bail!("inconsistent type {} for value {}", ty, value) + // } + // } else { + // bail!( + // "illegal application of constructor `{}` of `{}` to `{}`", + // conf.bad(& name), typ, value + // ) + // }, + + // // Function application. + // |typ, name, args| unimplemented!(), + // ) } /// If the term's an integer constant, returns the value. @@ -977,6 +992,13 @@ impl RTerm { typ.clone(), name.clone(), term ) ), + + // Function application. + |typ, name, args| Ok( + term::fun( + typ.clone(), name.clone(), args + ) + ) ) ; if let Ok(term) = res { diff --git a/src/term/test.rs b/src/term/test.rs index 6bd007fe..dbeda79e 100644 --- a/src/term/test.rs +++ b/src/term/test.rs @@ -404,6 +404,9 @@ fn ite_1() { ) ) ; let model = model!( val::real( rat_of_float(1.0) ) ) ; + println!("ite_1") ; + println!("model:") ; + model.print() ; assert_eval!( real model => ite, 2.0 ) ; } diff --git a/src/term/zip.rs b/src/term/zip.rs new file mode 100644 index 00000000..54e34db3 --- /dev/null +++ b/src/term/zip.rs @@ -0,0 +1,289 @@ +//! Zipper over terms. +//! +//! # TODO +//! +//! - explain + +use std::slice::Iter ; + +use common::* ; + + + +/// The direction of the next step. +/// +/// This is essentially a command from the exterior (the caller) to the zipper +/// telling it which way it will go next. +#[derive(Clone, Debug)] +pub enum ZipDo<'a, Acc, Yield> { + /// Remember a frame and inspect a term. + Trm { + /// The new term to work on. + nu_term: & 'a Term, + /// The frame (application) inside of which we are. + frame: ZipFrame<'a, Acc>, + }, + + /// Go down. Means "skip the current application and go directly to this". + /// + /// Example: when evaluating an `(ite c t e)` application: given the value of + /// `c`, when want to go down into `t` or `e` directly, not evaluate both. + Dwn { + /// The term we're going down in. + nu_term: & 'a Term, + /// An optional substitution. + /// + /// The only case where this is useful currently is when evaluating a + /// function application. + nu_subst: Option< VarMap >, + }, + + /// Go up. Means "here is the result, skip everything else at this level". + /// + /// Example: when evaluating a `(* a0 a1 ...)` application. If `a0` evaluates + /// to `0` then we can go up directly with `0`. + Upp { + /// The result to propagate upwards. + yielded: Yield + }, +} + + + +/// The operators manipulated by the zipper. +#[derive(Clone, Debug)] +pub enum ZipOp<'a> { + /// An operator. + Op(Op), + /// A datatype constructor. + New(& 'a String), + /// A function application. + Fun(& 'a String), + /// A constant array construction. + CArray, + /// A datatype selection. + Slc(& 'a String), +} + + +// Nullary things the zipper can manipulate. +#[derive(Clone, Debug)] +pub enum ZipNullary<'a> { + /// A constant. + Cst(& 'a Val), + /// A variable. + Var(& 'a Typ, VarIdx), +} + + +/// A frame in the zipper. +/// +/// This is both what the zipper manipulates and what the user's function will +/// take as input. +#[derive(Clone, Debug)] +pub struct ZipFrame<'a, Acc> { + /// The thing being applied. + pub thing: ZipOp<'a>, + /// The type of the application. + pub typ: & 'a Typ, + /// The arguments that have already been handled. + pub lft_args: Acc, + /// The arguments that have not already been handled. + pub rgt_args: Iter<'a, Term>, +} + + + +/// Accumulator trait. +/// +/// The goal is to let the zipper know how to construct an empty accumulator. +pub trait Accumulator { + /// Creates an empty accumulator. + fn new_empty(usize) -> Self ; + /// Pushes a new element in the accumulator. + fn push(& mut self, Elem) ; +} +impl Accumulator for Vec { + fn new_empty(hint: usize) -> Self { Vec::with_capacity(hint) } + fn push(& mut self, elem: T) { self.push(elem) } +} + + + +/// Zip function. +pub fn zip( + term: & Term, mut nul_do: NulF, mut app_do: AppF, mut partial: Partial +) -> Result +where +Acc: Accumulator, Yield: Clone, + +NulF: for<'a> FnMut( + ZipNullary<'a> +) -> Result< Yield, E >, + +AppF: for<'a> FnMut( + ZipOp<'a>, & 'a Typ, Acc +) -> Result< Yield, E >, + +Partial: for<'a> FnMut( + ZipFrame<'a, Acc> +) -> Result< ZipDo<'a, Acc, Yield>, E > { + // Empty vector of terms, useful when handling unary operators. + let empty: Vec = Vec::with_capacity(0) ; + + // Current term we're going down in. + let mut term = term ; + + // Stack of `ZipFrame`. + let mut stack = Vec::with_capacity(7) ; + // The current substitution, if any. + let mut subst: Option< VarMap > = None ; + + 'inspect_term: loop { + + let mut result = match * term.get() { + + RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { + subst[var_idx].clone() + } else { + nul_do( ZipNullary::Var(typ, var_idx) ) ? + }, + + RTerm::Cst(ref cst) => nul_do( ZipNullary::Cst(cst) ) ?, + + RTerm::CArray { ref typ, term: ref nu_term } => { + let frame = ZipFrame { + thing: ZipOp::CArray, typ, + lft_args: Acc::new_empty(1), + rgt_args: empty.iter(), + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + }, + + RTerm::App { op, ref typ, ref args } => { + let mut rgt_args = args.iter() ; + let op = ZipOp::Op(op) ; + let lft_args = Acc::new_empty( args.len() ) ; + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, typ, lft_args, rgt_args, + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + + } else { + app_do(op, typ, lft_args) ? + } + }, + + RTerm::DTypNew { ref typ, ref name, ref args } => { + let mut rgt_args = args.iter() ; + let op = ZipOp::New(name) ; + let lft_args = Acc::new_empty( args.len() ) ; + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, typ, lft_args, rgt_args, + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + + } else { + app_do(op, typ, lft_args) ? + } + }, + + RTerm::DTypSlc { ref typ, ref name, term: ref nu_term } => { + let mut rgt_args = empty.iter() ; + let op = ZipOp::Slc(name) ; + let lft_args = Acc::new_empty(1) ; + + let frame = ZipFrame { + thing: op, typ, lft_args, rgt_args, + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + }, + + RTerm::Fun { ref typ, ref name, ref args } => { + let mut rgt_args = empty.iter() ; + let op = ZipOp::Fun(name) ; + let lft_args = Acc::new_empty( args.len() ) ; + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, typ, lft_args, rgt_args, + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + + } else { + app_do(op, typ, lft_args) ? + } + }, + + } ; + + 'inspect_do_res: loop { + + match stack.pop() { + + // Done, we're at top level. + None => return Ok(result), + + // Work on the next frame. + Some( + (ZipFrame { thing, typ, mut lft_args, rgt_args }, old_subst) + ) => { + subst = old_subst ; + + // Update left args. + lft_args.push( result ) ; + + if rgt_args.len() == 0 { + result = app_do(thing, typ, lft_args) ? ; + + continue 'inspect_do_res + } else { + + match partial( + ZipFrame { thing, typ, lft_args, rgt_args } + ) ? { + + ZipDo::Trm { nu_term, frame } => { + term = nu_term ; + stack.push( (frame, subst.clone()) ) ; + continue 'inspect_term + }, + + ZipDo::Dwn { nu_term, nu_subst } => { + if let Some(nu_subst) = nu_subst { + subst = Some(nu_subst) + } + term = nu_term ; + continue 'inspect_term + }, + + ZipDo::Upp { yielded } => { + result = yielded ; + continue 'inspect_do_res + }, + } + } + }, + } + } + } +} \ No newline at end of file diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index 2b4d2b7d..3d95fd87 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -72,6 +72,10 @@ impl Evaluator for RVarVals { } #[inline] fn len(& self) -> usize { VarMap::len(& self.map) } + fn print(& self) { + println!("varvals@") ; + self.map.print() ; + } } impl RVarVals { From b57efce899341613e55dcdff79c25ac400efee5e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 11 Jul 2018 13:20:44 +0900 Subject: [PATCH 14/94] lazy evaluation! --- src/term/eval.rs | 319 ++++++++++++++++++++++++++++++++++---------- src/term/factory.rs | 6 + src/term/fold.rs | 2 +- src/term/test.rs | 83 ++++++++++++ src/term/zip.rs | 6 +- 5 files changed, 343 insertions(+), 73 deletions(-) diff --git a/src/term/eval.rs b/src/term/eval.rs index b036bfca..90f3beb0 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -1,101 +1,280 @@ //! Term evaluation using the term zipper. +use std::slice::Iter ; + use common::* ; use term::zip::* ; +/// Zipper frames for term evaluation. +pub type Frame<'a> = ZipFrame< 'a, Vec > ; +/// Zipper command for term evaluation. +pub type Cmd<'a> = ZipDo< 'a, Vec, Val > ; /// Term evaluation. pub fn eval(term: & Term, model: & E) -> Res { - zip::, Val, _, _, _>( - term, - - // Variables and constants. - |zip_null| match zip_null { - ZipNullary::Cst(val) => Ok( val.clone() ), - ZipNullary::Var(_, var) => if var < model.len() { - Ok( model.get(var).clone() ) - } else { - bail!("model is too short ({} / {})", * var, model.len()) - }, + zip( + term, |zip_null| leaf(model, zip_null), total, partial + ) +} + + +fn leaf<'a, E: Evaluator>( + model: & E, zip_null: ZipNullary<'a>, +) -> Res { + match zip_null { + ZipNullary::Cst(val) => Ok( val.clone() ), + ZipNullary::Var(_, var) => if var < model.len() { + Ok( model.get(var).clone() ) + } else { + bail!("model is too short ({} / {})", * var, model.len()) }, + } +} - // Applications. - |op, typ, mut values| match op { - ZipOp::Op(op) => op.eval(values).chain_err( - || format!("while evaluating operator `{}`", op) - ), - - ZipOp::New(name) => Ok( - val::dtyp_new( typ.clone(), name.clone(), values ) - ), - - ZipOp::Slc(name) => if values.len() == 1 { - let value = values.pop().unwrap() ; - if ! value.is_known() { - Ok( val::none( typ.clone() ) ) - } else if let Some( - (ty, constructor, values) - ) = value.dtyp_inspect() { - if let Some((dtyp, _)) = ty.dtyp_inspect() { - - if let Some(selectors) = dtyp.news.get(constructor) { - - let mut res = None ; - for ((selector, _), value) in selectors.iter().zip( - values.iter() - ) { - if selector == name { - res = Some( value.clone() ) - } - } - if let Some(res) = res { - Ok(res) - } else { - Ok( val::none( typ.clone() ) ) +fn total<'a>( + op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec +) -> Res { + match op { + ZipOp::Op(op) => op.eval(values).chain_err( + || format!("while evaluating operator `{}`", op) + ), + + ZipOp::New(name) => Ok( + val::dtyp_new( typ.clone(), name.clone(), values ) + ), + + ZipOp::Slc(name) => if values.len() == 1 { + let value = values.pop().unwrap() ; + if ! value.is_known() { + Ok( val::none( typ.clone() ) ) + } else if let Some( + (ty, constructor, values) + ) = value.dtyp_inspect() { + if let Some((dtyp, _)) = ty.dtyp_inspect() { + + if let Some(selectors) = dtyp.news.get(constructor) { + + let mut res = None ; + for ((selector, _), value) in selectors.iter().zip( + values.iter() + ) { + if selector == name { + res = Some( value.clone() ) } + } + if let Some(res) = res { + Ok(res) } else { - bail!( - "unknown constructor `{}` for datatype {}", - conf.bad(constructor), dtyp.name - ) + Ok( val::none( typ.clone() ) ) } } else { - bail!("inconsistent type {} for value {}", ty, value) + bail!( + "unknown constructor `{}` for datatype {}", + conf.bad(constructor), dtyp.name + ) } + } else { - bail!( - "illegal application of constructor `{}` of `{}` to `{}`", - conf.bad(& name), typ, value - ) + bail!("inconsistent type {} for value {}", ty, value) } } else { bail!( - "expected one value for datatype selection, found {}", values.len() + "illegal application of constructor `{}` of `{}` to `{}`", + conf.bad(& name), typ, value ) - }, + } + } else { + bail!( + "expected one value for datatype selection, found {}", values.len() + ) + }, - ZipOp::CArray => if values.len() == 1 { - let default = values.pop().unwrap() ; - Ok( val::array( typ.clone(), default ) ) - } else { + ZipOp::CArray => if values.len() == 1 { + let default = values.pop().unwrap() ; + Ok( val::array( typ.clone(), default ) ) + } else { + bail!( + "expected one value for constant array construction, found {}", + values.len() + ) + }, + + ZipOp::Fun(_) => unimplemented!(), + } +} + + + + + +fn partial<'a>( + Frame { thing, typ, lft_args, mut rgt_args }: Frame<'a> +) -> Res< Cmd<'a> > { + + match thing { + ZipOp::Op(op) => partial_op(op, typ, lft_args, rgt_args), + thing @ ZipOp::New(_) | + thing @ ZipOp::Fun(_) | + thing @ ZipOp::CArray | + thing @ ZipOp::Slc(_) => { + let nu_term = rgt_args.next().expect( + "illegal call to `partial_op`: empty `rgt_args`" + ) ; + Ok( + ZipDo::Trm { + nu_term, frame: Frame { thing, typ, lft_args, rgt_args } + } + ) + }, + } + +} + + + + +fn partial_op<'a>( + op: Op, typ: & 'a Typ, mut lft_args: Vec, mut rgt_args: Iter<'a, Term> +) -> Res< ZipDo< 'a, Vec, Val > > { + // Since this is called each time a value is added to `lft_args`, we only + // need to check the last value in `lft_args`. + + macro_rules! go { + (up $e:expr) => ( + return Ok( ZipDo::Upp { yielded: $e } ) + ) ; + (down $e:expr) => ( + return Ok( ZipDo::Dwn { nu_term: $e, nu_subst: None } ) + ) ; + } + + match op { + + Op::Ite => if let Some(c) = lft_args.pop() { + if ! lft_args.is_empty() { bail!( - "expected one value for constant array construction, found {}", - values.len() + "partial `Ite` application with `lft_args` of length {}", + lft_args.len() + 1 ) - }, + } + + let (t, e) = if let (Some(t), Some(e), None) = ( + rgt_args.next(), rgt_args.next(), rgt_args.next() + ) { + (t, e) + } else { + bail!("illegal application of `Ite`") + } ; - ZipOp::Fun(_) => unimplemented!(), + match c.to_bool().chain_err( + || "during `Ite` condition evaluation" + ) ? { + + // Condition is true, go into the `then` branch. + Some(true) => go!(down t), + + // Condition is false, go into the `else` branch. + Some(false) => go!(down e), + + // Unknown condition value. Keep going, the Ite might still be + // evaluable if both branches are equal. + None => (), + } }, - |mut frame| if let Some(nu_term) = frame.rgt_args.next() { - Ok( - ZipDo::Trm { nu_term, frame } - ) - } else { - bail!("unreachable") + Op::And => if let Some(last) = lft_args.pop() { + match last.to_bool() ? { + // False, no need to evaluate the other arguments. + Some(false) => go!( up val::bool(false) ), + // True, just skip. + Some(true) => (), + // Unknown, push back and keep going. + None => lft_args.push(last), + } + }, + + Op::Or => if let Some(last) = lft_args.pop() { + println!("{}", last) ; + match last.to_bool() ? { + // True, no need to evaluate the other arguments. + Some(true) => go!( up val::bool(true) ), + // False, just skip. + Some(false) => (), + // Unknown, push back and keep going. + None => lft_args.push(last), + } + }, + + Op::Mul => if let Some(last) = lft_args.last() { + if last.is_zero() || ! last.is_known() { + go!( up last.clone() ) + } + }, + + Op::Mod | Op::Rem => if let Some(last) = lft_args.last() { + debug_assert! { lft_args.len() == 1 } + if last.is_zero() || ! last.is_known() { + go!( up last.clone() ) + } + }, + + Op::Impl => if let Some(last) = lft_args.last() { + debug_assert! { lft_args.len() == 1 } + if last.is_false() { + go!( up val::bool(true) ) + } + }, + + Op::Distinct => if let Some(last) = lft_args.last() { + if last.is_known() { + for other in lft_args.iter().take(lft_args.len() - 1) { + if last == other { + go!( up val::bool(false) ) + } + } + println!() + } + }, + + Op::Add | Op::Sub | + Op::CMul | Op::IDiv | Op::Div | + Op::Gt | Op::Ge | Op::Le | Op::Lt | + Op::Eql + => if let Some(last) = lft_args.last() { + if ! last.is_known() { + return Ok( + ZipDo::Upp { yielded: val::none( typ.clone() ) } + ) + } + }, + + Op::Store | Op::Select => (), + + Op::Not | + Op::ToInt | + Op::ToReal => bail!( + "partial application of unary operator ({}) makes no sense", op + ), + + } + + // Normal exit. + let nu_term = rgt_args.next().expect( + "illegal call to `partial_op`: empty `rgt_args`" + ) ; + Ok( + ZipDo::Trm { + nu_term, frame: Frame { + thing: ZipOp::Op(op), typ, lft_args, rgt_args + } } ) -} \ No newline at end of file +} + + + + + diff --git a/src/term/factory.rs b/src/term/factory.rs index eaee3148..a586a25a 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -383,6 +383,12 @@ pub fn eq(lhs: Term, rhs: Term) -> Term { app(Op::Eql, vec![lhs, rhs]) } +/// Creates a distinct application. +#[inline] +pub fn distinct(terms: Vec) -> Term { + app(Op::Distinct, terms) +} + /// Creates a sum. #[inline] pub fn add(kids: Vec) -> Term { diff --git a/src/term/fold.rs b/src/term/fold.rs index ed6fe992..ebfad06d 100644 --- a/src/term/fold.rs +++ b/src/term/fold.rs @@ -153,7 +153,7 @@ pub fn fold_custom_res< mut varf: VarF, mut cstf: CstF, mut appf: AppF, mut arrf: ArrF, mut newf: NewF, mut slcf: SlcF, - mut funf: FunF + mut funf: FunF, ) -> Result where VarF: FnMut(& Typ, VarIdx) -> Result, diff --git a/src/term/test.rs b/src/term/test.rs index dbeda79e..3e4c606e 100644 --- a/src/term/test.rs +++ b/src/term/test.rs @@ -410,6 +410,89 @@ fn ite_1() { assert_eval!( real model => ite, 2.0 ) ; } + + +// The lazy evaluation tests rely on the order in which the terms are created. +// This is not the case outside of these tests obviously. But here the goal is +// to have the last term being illegal, usually a variable that's not defined +// in the model. That way, if lazy evaluation does not do the right thing the +// test crashes. +// +// Unfortunately, test run in parallel and this can end up screwing up the +// order of the terms. To avoid this, the illegal variable should use a +// different index each time, ideally a high one to avoid clashes with other +// tests. + + + + +#[test] +fn lazy_1() { + let v_0 = term::real_var(0) ; + let t_1 = term::ge( v_0, term::real( rat_of_float(7.0) ) ) ; + let v_1 = term::real_var(1000) ; + let t_2 = term::ge( v_1, term::real( rat_of_float(0.0) ) ) ; + + let conj = term::and( vec![ t_1, t_2 ] ) ; + + let model = model!( val::real( rat_of_float(1.0) ) ) ; + + // This evaluation should not work: `v_1` is not defined in the model, but + // because evaluation is lazy and `t_1` is false with this model, it goes + // through and the conjunction evaluates to false. + + assert_eval!( bool not model => conj ) +} + + +#[test] +fn lazy_2() { + let v_0 = term::real_var(0) ; + let t_1 = term::ge( v_0, term::real( rat_of_float(0.0) ) ) ; + let v_1 = term::real_var(1001) ; + let t_2 = term::ge( v_1, term::real( rat_of_float(0.0) ) ) ; + + let disj = term::or( vec![ t_1, t_2 ] ) ; + + let model = model!( val::real( rat_of_float(1.0) ) ) ; + + // This evaluation should not work: `v_1` is not defined in the model, but + // because evaluation is lazy and `t_1` is false with this model, it goes + // through and the conjunction evaluates to false. + + assert_eval!( bool model => disj ) +} + + +#[test] +fn lazy_3() { + let v_0 = term::real_var(0) ; + let t_1 = term::ge( + v_0.clone(), term::real( rat_of_float(7.0) ) + ) ; + let v_1 = term::real_var(1002) ; + let ite = term::ite(t_1, v_1, v_0) ; + + let model = model!( val::real( rat_of_float(1.0) ) ) ; + + assert_eval!( real model => ite, 1.0 ) +} + + +#[test] +fn lazy_4() { + let v_0 = term::real_var(0) ; + let t_1 = term::u_minus( v_0.clone() ) ; + let v_1 = term::real_var(1003) ; + let distinct = term::distinct( vec![t_1, v_0, v_1] ) ; + + let model = model!( val::real( rat_of_float(0.0) ) ) ; + + assert_eval!( bool not model => distinct ) +} + + + // #[test] // fn models() { // let v_1 = term::bool_var(0) ; diff --git a/src/term/zip.rs b/src/term/zip.rs index 54e34db3..177ce6db 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -51,7 +51,7 @@ pub enum ZipDo<'a, Acc, Yield> { /// The operators manipulated by the zipper. -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub enum ZipOp<'a> { /// An operator. Op(Op), @@ -286,4 +286,6 @@ Partial: for<'a> FnMut( } } } -} \ No newline at end of file +} + + From f35ee050d1f7cb2a688c5e19868d0b54e9023893 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 11 Jul 2018 18:11:32 +0900 Subject: [PATCH 15/94] recursive functions, (cross-)synthesis for adts --- rsc/inactive/adt/sorted_len.smt2 | 30 ++++- src/common/profiling.rs | 2 +- src/fun/mod.rs | 161 ++++++++++++++++++++++--- src/learning/ice/quals.rs | 29 ++++- src/learning/ice/synth/adt.rs | 191 ++++++++++++++++++++++++++++++ src/learning/ice/synth/helpers.rs | 8 +- src/learning/ice/synth/int.rs | 16 +-- src/learning/ice/synth/mod.rs | 129 ++++++++++++++++++-- src/learning/ice/synth/real.rs | 12 +- src/parse/mod.rs | 25 +++- src/term/eval.rs | 82 +++++++++---- src/term/zip.rs | 92 +++++++++++--- 12 files changed, 674 insertions(+), 103 deletions(-) create mode 100644 src/learning/ice/synth/adt.rs diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index 36fd2f01..bab2cc79 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -17,6 +17,29 @@ ) ) +(define-funs-rec + ( + (all_equal ( (l Lst) ) Bool) + (all_equal_aux ( (v Int) (l Lst) ) Bool) + ) + ( + (ite + (= l nil) + true + (all_equal_aux (head l) (tail l)) + ) + (ite + (= l nil) + true + (ite + (not (= (head l) v) ) + false + (all_equal_aux v (tail l)) + ) + ) + ) +) + ; let rev = ; let rec loop acc = function ; | [] -> acc @@ -98,9 +121,7 @@ ; if lst = (rev lst) ; and (sorted lst) ; and (sorted (rev lst)) -; then match lst -; | nil | _ :: nil => () -; | _ => assert false +; then (assert (all_elements_the_same lst)) (assert (forall ( (lst1 Lst) (lst2 Lst) ) (=> @@ -108,8 +129,7 @@ (rev_pst nil lst1 lst2) (srt_pst lst1 true) (srt_pst lst2 true) - (not (= lst1 nil)) - (not (= (tail lst1) nil)) + (not (all_equal lst1)) ) false ) diff --git a/src/common/profiling.rs b/src/common/profiling.rs index c10acead..83134798 100644 --- a/src/common/profiling.rs +++ b/src/common/profiling.rs @@ -59,7 +59,7 @@ impl ProfileTree { self.fold( None, |prev, scope, time, sub_time| if let Some(last) = scope.last() { - debug_assert! { scope.is_empty() } + debug_assert! { ! scope.is_empty() } let art = match prev { Some(n) if n < scope.len() => "\\", Some(_) | None => "|", diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 04f03352..64681eec 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1,4 +1,12 @@ //! Hash consed functions. +//! +//! # TODO +//! +//! Move this in the instance to avoid the unsafe code to borrow definitions. + +use std::sync::{ + RwLockReadGuard, RwLockWriteGuard +} ; use common::* ; @@ -6,22 +14,56 @@ use common::* ; pub type Fun = Arc ; /// Type of the function factory. -type Factory = RwLock< BTreeMap > ; +/// +/// The usize indicates whether an element of the factory is being borrowed +/// **unsafely** by [`get_as_ref`](fun/fn.get_as_ref.html). If it is true, then +/// borrowing the factory mutably is unsafe. +/// +/// To avoid problems, **always** use the `factory` macro to access the +/// factory. +type Factory = RwLock< (BTreeMap, usize) > ; lazy_static! { /// Function factory. static ref factory: Factory = RwLock::new( - BTreeMap::new() + ( BTreeMap::new(), 0 ) ) ; } +/// Read version of the factory. +fn read_factory<'a>() -> RwLockReadGuard< + 'a, (BTreeMap, usize) +> { + if let Ok(res) = factory.read() { + res + } else { + panic!("failed to access function factory (read)") + } +} +/// Write version of the factory. +fn write_factory<'a>() -> RwLockWriteGuard< + 'a, (BTreeMap, usize) +> { + loop { + if let Ok(res) = factory.write() { + if res.1 != 0 { continue } + return res + } else { + panic!("failed to access function factory (write)") + } + } +} + +macro_rules! factory { + (read) => (& read_factory().0) ; + (write) => (& mut write_factory().0) ; +} + + /// Creates a function definition. pub fn mk(fun: RFun) -> Res { let fun = Arc::new( fun ) ; - let prev = if let Ok(mut f) = factory.write() { - f.insert( fun.name.clone(), fun.clone() ) - } else { - bail!("failed to access function factory (write)") - } ; + let f = factory!(write) ; + let prev = f.insert( fun.name.clone(), fun.clone() ) ; if let Some(prev) = prev { bail!("attempting to redefine function `{}`", prev.name) @@ -34,11 +76,7 @@ pub fn mk(fun: RFun) -> Res { /// Defines all the functions. pub fn write_all(w: & mut W) -> Res<()> { - let f = if let Ok(f) = factory.read() { - f - } else { - bail!("failed to access function factory (read)") - } ; + let f = factory!(read) ; if f.is_empty() { return Ok(()) } @@ -55,6 +93,7 @@ pub fn write_all(w: & mut W) -> Res<()> { all.reserve( fun.deps.len() + 1 ) ; all.push(fun) ; for dep in & fun.deps { + set.insert(dep) ; if let Some(dep) = f.get(dep) { all.push(dep) } else { @@ -98,17 +137,56 @@ pub fn write_all(w: & mut W) -> Res<()> { +/// Retrieves the definition of a function as a reference. +/// +/// This actually uses unsafe code, this kind of borrow should not be possible. +/// If something modifies the factory while the borrow is alive, then it might +/// end up pointing to arbitrary data. +/// +/// It's made safe by keeping track of how many references have been created +/// and preventing modifying the factory as long as this count is not zero. +/// This function hence works in conjunction with [`decrease_ref_count`][link]. +/// When using this function, you must keep track of how many references you +/// have created and when you are sure they're dead, call `decrease_ref_count`. +/// +/// link: fun/fn.decrease_ref_count.html +/// (decrease_ref_count function) +pub fn get_as_ref<'a>(name: & 'a str) -> Option<& 'a Fun> { + let mut pair = if let Ok(mut f) = factory.write() { + f + } else { + panic!("failed to access function factory (write)") + } ; + pair.1 += 1 ; + unsafe { + ::std::mem::transmute::, Option<& 'a Fun>>( + pair.0.get(name) + ) + } +} -/// Retrieves the definition of a function. -pub fn get(name: & str) -> Option { - if let Ok(f) = factory.read() { - f.get(name).cloned() +pub fn decrease_ref_count(count: usize) { + if count == 0 { return () } + if let Ok(mut f) = factory.write() { + if count <= f.1 { + f.1 -= count + } else { + panic!("trying to decrease ref count for function factory by too much") + } } else { - panic!("failed to access function factory (read)") + panic!("failed to access function factory (write)") } } + +/// Retrieves the definition of a function. +pub fn get(name: & str) -> Option { + let f = factory!(read) ; + f.get(name).cloned() +} + + /// Types and creates a function application. pub fn type_apply( name: String, var_info: & VarInfos, out: & Typ, args: Vec @@ -239,4 +317,53 @@ impl RFun { } self.def = def } +} + + + + + +/// Stores functions from and to some type. +#[derive(Debug, Clone)] +pub struct Functions { + /// Type these functions are for. + pub typ: Typ, + /// Functions from this type to another one. + pub from_typ: Vec, + /// Function from another type to this one. + pub to_typ: Vec, + /// Functions from this type to itself. + pub from_to_typ: Vec, +} +impl Functions { + + /// Constructor. + pub fn new(typ: Typ) -> Self { + let f = factory!(read) ; + + let mut from_typ = vec![] ; + let mut to_typ = vec![] ; + let mut from_to_typ = vec![] ; + + 'find_funs: for fun in f.values() { + let mut sig = fun.sig.iter() ; + + let ftyp = match sig.next() { + Some(info) => info.typ == typ && sig.next().is_none(), + _ => false, + } ; + + let ttyp = fun.typ == typ ; + + match (ftyp, ttyp) { + (true, true) => from_to_typ.push( fun.clone() ), + (true, false) => from_typ.push( fun.clone() ), + (false, true) => to_typ.push( fun.clone() ), + (false, false) => continue 'find_funs, + } + } + + Functions { typ, from_typ, to_typ, from_to_typ } + } + } \ No newline at end of file diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index f163be4f..8518b695 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -576,6 +576,7 @@ impl NuQuals { for (var, typ) in sig { match ** typ { + typ::RTyp::Int => { quals.insert( term::ge( term::var(var, typ.clone()), @@ -608,6 +609,7 @@ impl NuQuals { // pred_info.idx // ) ? ; }, + typ::RTyp::Real => { quals.insert( term::ge( @@ -652,6 +654,7 @@ impl NuQuals { // pred_info.idx // ) ? ; }, + typ::RTyp::Bool => { let var = term::bool_var(var) ; quals.insert( var.clone(), pred_info.idx ) ? ; @@ -668,7 +671,31 @@ impl NuQuals { ) ? ; }, - typ::RTyp::DTyp { .. } => (), + typ::RTyp::DTyp { ref dtyp, .. } => { + for (name, args) in & dtyp.news { + if args.is_empty() { + quals.insert( + term::eq( + term::var( var, typ.clone() ), + term::dtyp_new( typ.clone(), name.clone(), vec![] ) + ), + pred_info.idx + ) ? ; + } + } + let functions = fun::Functions::new( typ.clone() ) ; + for fun in functions.from_typ { + if fun.typ.is_bool() { + quals.insert( + term::fun( + fun.typ.clone(), fun.name.clone(), + vec![ term::var(var, typ.clone()) ], + ), + pred_info.idx + ) ? ; + } + } + }, typ::RTyp::Unk => bail!( "unexpected unknown type" diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs new file mode 100644 index 00000000..030c4ce8 --- /dev/null +++ b/src/learning/ice/synth/adt.rs @@ -0,0 +1,191 @@ +//! ADT qualifier synthesis. + +use common::* ; +use ::fun::Functions ; + +use super::{ TermVals, TheoSynth } ; + +#[derive(Clone, Debug)] +pub struct AdtSynth { + /// Expressivity level. + expressivity: usize, + /// Type this synthesizer handles. + typ: Typ, + /// Functions relevant for this type. + pub funs: Functions, +} +impl PartialEq for AdtSynth { + fn eq(& self, other: & Self) -> bool { + self.typ == other.typ + } +} +impl Eq for AdtSynth {} + +impl ::std::hash::Hash for AdtSynth { + fn hash(& self, hasher: & mut H) + where H: ::std::hash::Hasher { + self.typ.hash(hasher) + } +} + +impl PartialOrd for AdtSynth { + fn partial_cmp(& self, other: & Self) -> Option<::std::cmp::Ordering> { + self.typ.partial_cmp(& other.typ) + } +} +impl Ord for AdtSynth { + fn cmp(& self, other: & Self) -> ::std::cmp::Ordering { + self.typ.cmp(& other.typ) + } +} + +impl TheoSynth for AdtSynth { + fn typ(& self) -> & Typ { & self.typ } + + fn is_done(& self) -> bool { + self.expressivity > 0 + } + + fn restart(& mut self) { + self.expressivity = 0 + } + + fn increment(& mut self) { + self.expressivity += 1 + } + + fn synth( + & mut self, f: F, sample: & VarVals, others: & mut TermVals, + _profiler: & Profiler + ) -> Res + where F: FnMut(Term) -> Res { + match self.expressivity { + 0 => self.eq_synth(f, sample, others), + + _ => Ok(false), + } + } + + fn project( + & self, sample: & VarVals, typ: & Typ, map: & mut TermVals + ) -> Res<()> { + for fun in & self.funs.from_typ { + + if & fun.typ != typ { + continue + } + + for (var, val) in sample.index_iter() { + if val.is_known() + && val.typ() == self.typ { + let var = term::var( var, self.typ.clone() ) ; + let input: VarMap<_> = vec![ val.clone() ].into() ; + + let val = fun.def.eval(& input).chain_err( + || format!( + "while evaluating ({} {})", fun.name, val + ) + ) ? ; + + let term = term::fun( typ.clone(), fun.name.clone(), vec![var] ) ; + + let prev = map.insert(term, val) ; + debug_assert! { prev.is_none() } + } + } + } + + Ok(()) + } +} + + + +impl AdtSynth { + /// Constructor. + pub fn new(typ: Typ) -> Self { + let funs = Functions::new( typ.clone() ) ; + AdtSynth { expressivity: 0, typ, funs } + } + + /// True if the synthesizer can project values to int. + pub fn can_project_to_int(& self) -> bool { + for fun in & self.funs.from_typ { + if fun.typ.is_int() { return true } + } + false + } + + /// True if the synthesizer can project values to real. + pub fn can_project_to_real(& self) -> bool { + for fun in & self.funs.from_typ { + if fun.typ.is_real() { return true } + } + false + } + + /// Generates equalities between variables of some ADT. + fn eq_synth( + & self, mut f: F, sample: & VarVals, others: & mut TermVals + ) -> Res + where F: FnMut(Term) -> Res { + let mut previous: BTreeSet<(Term, Val)> = BTreeSet::new() ; + + for (var, val) in sample.index_iter() { + if val.is_known() + && val.typ() == self.typ { + let var = term::var( var, self.typ.clone() ) ; + + let mut extended = Vec::with_capacity( + self.funs.from_to_typ.len() + 1 + ) ; + + if f( + term::eq( var.clone(), term::cst( val.clone() ) ) + ) ? { + return Ok(true) + } + + let mut input: Option< VarMap > = None ; + + for fun in & self.funs.from_to_typ { + let input = input.get_or_insert_with( + || vec![ val.clone() ].into() + ) ; + + let val = fun.def.eval(input).chain_err( + || format!( + "while evaluating ({} {})", fun.name, val + ) + ) ? ; + + extended.push(( + term::fun( + self.typ.clone(), fun.name.clone(), vec![ var.clone() ] + ), val + )) + } + + extended.push(( var, val.clone() )) ; + + for (t_1, v_1) in extended { + + for (t_2, _) in previous.iter().map( + |(t,v)| (t,v) + ).chain( others.iter() ) { + if f( + term::eq( t_1.clone(), t_2.clone() ) + ) ? { + return Ok(true) + } + } + + previous.insert( (t_1, v_1) ) ; + } + } + } + + Ok(false) + } +} + diff --git a/src/learning/ice/synth/helpers.rs b/src/learning/ice/synth/helpers.rs index b404571f..a4ebad46 100644 --- a/src/learning/ice/synth/helpers.rs +++ b/src/learning/ice/synth/helpers.rs @@ -82,7 +82,7 @@ macro_rules! simple_arith_synth { apply! { $f to term } } - $previous.push( ($term, $val.clone()) ) + $previous.insert(($term, $val.clone())) ; }) ; } @@ -109,7 +109,7 @@ macro_rules! arith_synth_non_lin { let term = term::eq( lhs, rhs ) ; apply! { $f to term } } - $previous.push(($term, $val)) + $previous.insert(($term, $val)) ; } }) ; @@ -268,8 +268,10 @@ macro_rules! arith_synth_three_terms { } } } - $previous.push( ($term, $val.clone()) ) + $previous.insert(($term, $val.clone())) ; }) ; + + (@internal $f:tt($lhs:expr, $rhs:expr)) => ({ let term = term::app( Op::Ge, vec![ $lhs.clone(), $rhs.clone() ] diff --git a/src/learning/ice/synth/int.rs b/src/learning/ice/synth/int.rs index 5a61cce8..837ebf8a 100644 --- a/src/learning/ice/synth/int.rs +++ b/src/learning/ice/synth/int.rs @@ -118,9 +118,7 @@ pub fn non_lin_int_synth( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_int: Vec<(Term, Int)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { @@ -163,9 +161,7 @@ pub fn simple_int_synth( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_int: Vec<(Term, Int)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { @@ -197,9 +193,7 @@ pub fn int_synth_1( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_int: Vec<(Term, Int)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { @@ -234,9 +228,7 @@ pub fn int_synth_2( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_int: Vec<(Term, Int)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index 6a23a59e..e9bd5822 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -3,6 +3,7 @@ //! # TO DO //! //! - document the workflow +//! - factor code between the synthesizers (`eq_synth` etc.) use common::* ; @@ -10,6 +11,7 @@ use common::* ; pub mod helpers ; pub mod int ; pub mod real ; +pub mod adt ; pub type TermVals = TermMap ; @@ -46,52 +48,98 @@ pub trait TheoSynth { use self::int::IntSynth ; use self::real::RealSynth ; +use self::adt::AdtSynth ; /// Manages theory synthesizers. pub struct SynthSys { int: Option, real: Option, + adt: Vec, cross_synth: TermMap, } impl SynthSys { /// Constructor. pub fn new(sig: & Sig) -> Self { - let mut int = false ; - let mut real = false ; + let mut int = None ; + let mut real = None ; + + macro_rules! set { + (int) => ( + if int.is_none() { + int = Some( IntSynth::new() ) + } + ) ; + (real) => ( + if real.is_none() { + real = Some( RealSynth::new() ) + } + ) ; + } + + let mut adt: Vec = Vec::new() ; for typ in sig { match ** typ { - typ::RTyp::Int => int = true, - typ::RTyp::Real => real = true, + typ::RTyp::Int => set!(int), + typ::RTyp::Real => set!(real), + + typ::RTyp::DTyp { .. } => if adt.iter().all( + |adt| adt.typ() != typ + ) { + let synth = AdtSynth::new( typ.clone() ) ; + // println!("creating synth for {}", synth.typ()) ; + // println!(" from_typ:") ; + // for fun in & synth.funs.from_typ { + // println!(" - {}", fun.name) + // } + // println!(" to_typ:") ; + // for fun in & synth.funs.to_typ { + // println!(" - {}", fun.name) + // } + // println!(" from_to_typ:") ; + // for fun in & synth.funs.from_to_typ { + // println!(" - {}", fun.name) + // } + if synth.can_project_to_int() { set!(int) } + if synth.can_project_to_real() { set!(real) } + adt.push(synth) + }, + typ::RTyp::Bool | typ::RTyp::Array { .. } | - typ::RTyp::DTyp { .. } | typ::RTyp::Unk => (), } } SynthSys { - int: if int { Some( IntSynth::new() ) } else { None }, - real: if real { Some( RealSynth::new() ) } else { None }, - cross_synth: TermMap::new(), + int, real, adt, cross_synth: TermMap::new(), } } /// True if all synthesizers are done. pub fn is_done(& self) -> bool { self.int.as_ref().map(|i| i.is_done()).unwrap_or(true) && - self.real.as_ref().map(|r| r.is_done()).unwrap_or(true) + self.real.as_ref().map(|r| r.is_done()).unwrap_or(true) && + self.adt.iter().all( + |a| a.is_done() + ) } /// Increments all synthesizers. pub fn increment(& mut self) { if let Some(i) = self.int.as_mut() { i.increment() } if let Some(r) = self.real.as_mut() { r.increment() } + for a in & mut self.adt { + a.increment() + } } /// Restarts all synthesizers. pub fn restart(& mut self) { if let Some(i) = self.int.as_mut() { i.restart() } if let Some(r) = self.real.as_mut() { r.restart() } + for a in & mut self.adt { + a.restart() + } } @@ -107,6 +155,7 @@ impl SynthSys { if let Some(int_synth) = self.int.as_mut() { if ! int_synth.is_done() { self.cross_synth.clear() ; + if let Some(real_synth) = self.real.as_mut() { profile!{ |_profiler| tick "learning", "qual", "synthesis", "int project" @@ -119,6 +168,19 @@ impl SynthSys { } res ? } + for adt_synth in & mut self.adt { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "adt project" + } + let res = adt_synth.project( + sample, int_synth.typ(), & mut self.cross_synth + ) ; + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "adt project" + } + res ? + } + profile!{ |_profiler| tick "learning", "qual", "synthesis", "int" } let done = int_synth.synth( & mut f, sample, & mut self.cross_synth, _profiler @@ -131,6 +193,7 @@ impl SynthSys { if let Some(real_synth) = self.real.as_mut() { if ! real_synth.is_done() { self.cross_synth.clear() ; + if let Some(int_synth) = self.int.as_mut() { profile! ( |_profiler| wrap { @@ -140,6 +203,19 @@ impl SynthSys { } "learning", "qual", "synthesis", "real project" ) ? } + for adt_synth in & mut self.adt { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "adt project" + } + let res = adt_synth.project( + sample, real_synth.typ(), & mut self.cross_synth + ) ; + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "adt project" + } + res ? + } + profile!{ |_profiler| tick "learning", "qual", "synthesis", "real" } let done = real_synth.synth( & mut f, sample, & mut self.cross_synth, _profiler @@ -149,6 +225,41 @@ impl SynthSys { } } + for adt_synth in & mut self.adt { + if ! adt_synth.is_done() { + self.cross_synth.clear() ; + + if let Some(int_synth) = self.int.as_mut() { + profile! ( + |_profiler| wrap { + int_synth.project( + sample, adt_synth.typ(), & mut self.cross_synth + ) + } "learning", "qual", "synthesis", "real project" + ) ? + } + if let Some(real_synth) = self.real.as_mut() { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "int project" + } + let res = real_synth.project( + sample, adt_synth.typ(), & mut self.cross_synth + ) ; + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "int project" + } + res ? + } + + profile!{ |_profiler| tick "learning", "qual", "synthesis", "adt" } + let done = adt_synth.synth( + & mut f, sample, & mut self.cross_synth, _profiler + ) ; + profile!{ |_profiler| mark "learning", "qual", "synthesis", "adt" } + if done ? { return Ok(true) } + } + } + Ok(false) } } diff --git a/src/learning/ice/synth/real.rs b/src/learning/ice/synth/real.rs index 42a65894..c1c5766c 100644 --- a/src/learning/ice/synth/real.rs +++ b/src/learning/ice/synth/real.rs @@ -120,9 +120,7 @@ pub fn simple_real_synth( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_real: Vec<(Term, Rat)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { @@ -153,9 +151,7 @@ pub fn real_synth_1( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_real: Vec<(Term, Rat)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { @@ -189,9 +185,7 @@ pub fn real_synth_2( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { - let mut previous_real: Vec<(Term, Rat)> = Vec::with_capacity( - sample.len() - ) ; + let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; // Iterate over the sample. for (var_idx, val) in sample.index_iter() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 5a637ca0..21541887 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1656,9 +1656,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; let (_, id) = self.ident() ? ; self.ws_cmt() ; + + // Save term stack. + let old_stack = ::std::mem::replace( + & mut self.cxt.term_stack, vec![] + ) ; + let tterms = self.parse_ptterms( var_map, map, instance ) ? ; + + // Load term stack. + debug_assert! { self.cxt.term_stack.is_empty() } + self.cxt.term_stack = old_stack ; + self.insert_bind(id, tterms) ? ; self.ws_cmt() ; self.tag(")") ? ; @@ -2248,7 +2259,19 @@ impl<'cxt, 's> Parser<'cxt, 's> { map: & BTreeMap<& 's str, VarIdx>, instance: & Instance ) -> Res< Option > { - debug_assert! { self.cxt.term_stack.is_empty() } + if ! self.cxt.term_stack.is_empty() { + let e: Error = self.error_here("non-empty term stack").into() ; + let mut blah: String = "while parsing this:\n".into() ; + for line in self.string.lines() { + blah.push_str("| ") ; + blah.push_str(line) ; + blah.push('\n') + } + print_err( + & e.chain_err(|| blah) + ) ; + panic!("non-empty term stack during parsing") + } conf.check_timeout() ? ; let start_pos = self.pos() ; diff --git a/src/term/eval.rs b/src/term/eval.rs index 90f3beb0..08de397e 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -9,12 +9,29 @@ use term::zip::* ; pub type Frame<'a> = ZipFrame< 'a, Vec > ; /// Zipper command for term evaluation. pub type Cmd<'a> = ZipDo< 'a, Vec, Val > ; +/// Zipper command (total case) for term evaluation. +pub type CmdT<'a> = ZipDoTotal< 'a, Val > ; /// Term evaluation. pub fn eval(term: & Term, model: & E) -> Res { - zip( - term, |zip_null| leaf(model, zip_null), total, partial - ) + let mut fun_ref_count = 0 ; + let res = zip( + term, |zip_null| leaf(model, zip_null), + |op, typ, values| total(op, typ, values, & mut fun_ref_count), + partial + ) ; + fun::decrease_ref_count(fun_ref_count) ; + res +} + + +macro_rules! go { + (up $e:expr) => ( + return Ok( ZipDo::Upp { yielded: $e } ) + ) ; + (down $e:expr) => ( + return Ok( ZipDo::Dwn { nu_term: $e } ) + ) ; } @@ -33,21 +50,22 @@ fn leaf<'a, E: Evaluator>( fn total<'a>( - op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec -) -> Res { - match op { + op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec, + fun_ref_count: & mut usize +) -> Res< CmdT<'a> > { + let yielded = match op { ZipOp::Op(op) => op.eval(values).chain_err( || format!("while evaluating operator `{}`", op) - ), + ) ?, - ZipOp::New(name) => Ok( - val::dtyp_new( typ.clone(), name.clone(), values ) + ZipOp::New(name) => val::dtyp_new( + typ.clone(), name.clone(), values ), ZipOp::Slc(name) => if values.len() == 1 { let value = values.pop().unwrap() ; if ! value.is_known() { - Ok( val::none( typ.clone() ) ) + val::none( typ.clone() ) } else if let Some( (ty, constructor, values) ) = value.dtyp_inspect() { @@ -65,9 +83,9 @@ fn total<'a>( } if let Some(res) = res { - Ok(res) + res } else { - Ok( val::none( typ.clone() ) ) + val::none( typ.clone() ) } } else { @@ -94,7 +112,7 @@ fn total<'a>( ZipOp::CArray => if values.len() == 1 { let default = values.pop().unwrap() ; - Ok( val::array( typ.clone(), default ) ) + val::array( typ.clone(), default ) } else { bail!( "expected one value for constant array construction, found {}", @@ -102,8 +120,31 @@ fn total<'a>( ) }, - ZipOp::Fun(_) => unimplemented!(), - } + ZipOp::Fun(name) => { + let fun = if let Some(fun) = fun::get_as_ref(name) { + fun + } else { + bail!("cannot evaluate unknown function `{}`", conf.bad(name)) + } ; + * fun_ref_count += 1 ; + + if values.len() != fun.sig.len() { + bail!( + "illegal application of function `{}` to {} arguments (expected {})", + conf.bad(name), values.len(), fun.sig.len() + ) + } + + return Ok( + ZipDoTotal::Dwn { + nu_term: & fun.def, + nu_subst: Some( values.into() ), + } + ) + }, + } ; + + Ok( ZipDoTotal::Upp { yielded } ) } @@ -142,15 +183,6 @@ fn partial_op<'a>( // Since this is called each time a value is added to `lft_args`, we only // need to check the last value in `lft_args`. - macro_rules! go { - (up $e:expr) => ( - return Ok( ZipDo::Upp { yielded: $e } ) - ) ; - (down $e:expr) => ( - return Ok( ZipDo::Dwn { nu_term: $e, nu_subst: None } ) - ) ; - } - match op { Op::Ite => if let Some(c) = lft_args.pop() { @@ -197,7 +229,6 @@ fn partial_op<'a>( }, Op::Or => if let Some(last) = lft_args.pop() { - println!("{}", last) ; match last.to_bool() ? { // True, no need to evaluate the other arguments. Some(true) => go!( up val::bool(true) ), @@ -235,7 +266,6 @@ fn partial_op<'a>( go!( up val::bool(false) ) } } - println!() } }, diff --git a/src/term/zip.rs b/src/term/zip.rs index 177ce6db..0168146b 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -10,6 +10,11 @@ use common::* ; + + + + + /// The direction of the next step. /// /// This is essentially a command from the exterior (the caller) to the zipper @@ -31,11 +36,6 @@ pub enum ZipDo<'a, Acc, Yield> { Dwn { /// The term we're going down in. nu_term: & 'a Term, - /// An optional substitution. - /// - /// The only case where this is useful currently is when evaluating a - /// function application. - nu_subst: Option< VarMap >, }, /// Go up. Means "here is the result, skip everything else at this level". @@ -44,12 +44,38 @@ pub enum ZipDo<'a, Acc, Yield> { /// to `0` then we can go up directly with `0`. Upp { /// The result to propagate upwards. - yielded: Yield + yielded: Yield, }, } +/// The direction of the next step, total version. +/// +/// This is what the user produces when zipping up an application and all of +/// its arguments. +pub enum ZipDoTotal<'a, Yield> { + /// Going down. + /// + /// The only use case here is function application. + Dwn { + /// Term we're going down in. + nu_term: & 'a Term, + /// An optional substitution. + /// + /// The only case where this is useful currently is when evaluating a + /// function application. + nu_subst: Option< VarMap >, + }, + + /// Go up. Result. + Upp { + /// The result to propagate upwards. + yielded: Yield, + }, +} + + /// The operators manipulated by the zipper. #[derive(Clone, Copy, Debug)] pub enum ZipOp<'a> { @@ -67,7 +93,7 @@ pub enum ZipOp<'a> { // Nullary things the zipper can manipulate. -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub enum ZipNullary<'a> { /// A constant. Cst(& 'a Val), @@ -123,7 +149,7 @@ NulF: for<'a> FnMut( AppF: for<'a> FnMut( ZipOp<'a>, & 'a Typ, Acc -) -> Result< Yield, E >, +) -> Result< ZipDoTotal<'a, Yield>, E >, Partial: for<'a> FnMut( ZipFrame<'a, Acc> @@ -141,15 +167,21 @@ Partial: for<'a> FnMut( 'inspect_term: loop { - let mut result = match * term.get() { + let result = match * term.get() { RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { - subst[var_idx].clone() + ZipDoTotal::Upp { + yielded: subst[var_idx].clone(), + } } else { - nul_do( ZipNullary::Var(typ, var_idx) ) ? + ZipDoTotal::Upp { + yielded: nul_do( ZipNullary::Var(typ, var_idx) ) ?, + } }, - RTerm::Cst(ref cst) => nul_do( ZipNullary::Cst(cst) ) ?, + RTerm::Cst(ref cst) => ZipDoTotal::Upp { + yielded: nul_do( ZipNullary::Cst(cst) ) ?, + }, RTerm::CArray { ref typ, term: ref nu_term } => { let frame = ZipFrame { @@ -216,7 +248,7 @@ Partial: for<'a> FnMut( }, RTerm::Fun { ref typ, ref name, ref args } => { - let mut rgt_args = empty.iter() ; + let mut rgt_args = args.iter() ; let op = ZipOp::Fun(name) ; let lft_args = Acc::new_empty( args.len() ) ; @@ -236,6 +268,19 @@ Partial: for<'a> FnMut( } ; + let mut result = match result { + ZipDoTotal::Dwn { nu_term, nu_subst } => { + if nu_subst.is_some() { + subst = nu_subst + } + term = nu_term ; + + continue 'inspect_term + }, + + ZipDoTotal::Upp { yielded } => yielded + } ; + 'inspect_do_res: loop { match stack.pop() { @@ -253,9 +298,21 @@ Partial: for<'a> FnMut( lft_args.push( result ) ; if rgt_args.len() == 0 { - result = app_do(thing, typ, lft_args) ? ; + match app_do(thing, typ, lft_args) ? { + ZipDoTotal::Upp { yielded } => { + result = yielded ; + continue 'inspect_do_res + }, + + ZipDoTotal::Dwn { nu_term, nu_subst} => { + if nu_subst.is_some() { + subst = nu_subst + } + term = nu_term ; + continue 'inspect_term + } + } - continue 'inspect_do_res } else { match partial( @@ -268,10 +325,7 @@ Partial: for<'a> FnMut( continue 'inspect_term }, - ZipDo::Dwn { nu_term, nu_subst } => { - if let Some(nu_subst) = nu_subst { - subst = Some(nu_subst) - } + ZipDo::Dwn { nu_term } => { term = nu_term ; continue 'inspect_term }, From 93211a735eaf633605203521a1e518635aaff26f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 12 Jul 2018 20:39:15 +0900 Subject: [PATCH 16/94] datatype evaluation broken, a lot of debug info --- rsc/inactive/adt/sorted_len.smt2 | 49 +++-- src/check/parse.rs | 65 +----- src/common/consts.rs | 5 +- src/dtyp/mod.rs | 75 ++++++- src/learning/ice/mod.rs | 15 ++ src/parse/mod.rs | 353 +++++++++++++++++++++---------- src/term/eval.rs | 13 +- src/term/factory.rs | 8 +- src/term/mod.rs | 17 ++ src/term/op.rs | 68 ++++-- src/term/typ.rs | 24 ++- src/term/zip.rs | 43 +++- src/val/mod.rs | 12 ++ 13 files changed, 516 insertions(+), 231 deletions(-) diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index bab2cc79..8c9a7b75 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -1,12 +1,15 @@ (set-logic HORN) -(declare-datatypes () ( - (Lst nil (cons (head Int) (tail Lst))) +(declare-datatypes ( (Lst 1) ) ( + (par (T) ( + (nil) + (cons (head T) (tail (Lst T))) + ) ) ) ) (define-funs-rec ( - (len ( (l Lst) ) Int) + (len ( (l (Lst Int)) ) Int) ) ( (ite @@ -19,8 +22,8 @@ (define-funs-rec ( - (all_equal ( (l Lst) ) Bool) - (all_equal_aux ( (v Int) (l Lst) ) Bool) + (all_equal ( (l (Lst Int)) ) Bool) + (all_equal_aux ( (v Int) (l (Lst Int)) ) Bool) ) ( (ite @@ -47,18 +50,27 @@ ; in ; loop [] +; Pre-condition. +(declare-fun + rev_pre ((Lst Int) (Lst Int)) Bool +) ; Post-condition. (declare-fun - rev_pst ( Lst Lst Lst ) Bool + rev_pst ( (Lst Int) (Lst Int) (Lst Int) ) Bool ) + ; Terminal case. (assert - (forall ( (acc Lst) ) - (rev_pst acc nil acc) + (forall ( (acc (Lst Int)) ) + (=> + (rev_pre acc nil) + (rev_pst acc nil acc) + ) ) ) + ; Recursive case. (assert - (forall ( (acc Lst) (lst Lst) (res Lst) ) + (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) (=> (and (not (= lst nil)) @@ -80,8 +92,9 @@ ; Post-condition. (declare-fun - srt_pst ( Lst Bool ) Bool + srt_pst ( (Lst Int) Bool ) Bool ) + ; Terminal cases. (assert (forall ( (unused Bool) ) @@ -92,7 +105,7 @@ (srt_pst (cons hd nil) true) ) ) (assert - (forall ( (lst Lst) ) + (forall ( (lst (Lst Int)) ) (=> (and (not (= lst nil)) @@ -102,9 +115,10 @@ (srt_pst lst false) ) ) ) + ; Recursive case. (assert - (forall ( (lst Lst) (res Bool) ) + (forall ( (lst (Lst Int)) (res Bool) ) (=> (and (not (= lst nil)) @@ -123,9 +137,18 @@ ; and (sorted (rev lst)) ; then (assert (all_elements_the_same lst)) (assert - (forall ( (lst1 Lst) (lst2 Lst) ) + (forall ( (lst (Lst Int)) ) + (=> + true + (rev_pre nil lst) + ) + ) +) +(assert + (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) ) (=> (and + (rev_pre nil lst1) (rev_pst nil lst1 lst2) (srt_pst lst1 true) (srt_pst lst2 true) diff --git a/src/check/parse.rs b/src/check/parse.rs index 3b3730f4..fade07c7 100644 --- a/src/check/parse.rs +++ b/src/check/parse.rs @@ -286,40 +286,6 @@ impl<'a> InParser<'a> { Ok(Some((key, val))) } - /// Type or fails. - fn typ(& mut self) -> Res { - if let Some(t) = self.typ_opt() ? { - Ok(t) - } else { - bail!("expected type") - } - } - /// Type. - fn typ_opt(& mut self) -> Res> { - if self.tag_opt("Bool") { - Ok( Some( "Bool".to_string() ) ) - } else if self.tag_opt("Int") { - Ok( Some( "Int".to_string() ) ) - } else if self.tag_opt("Real") { - Ok( Some( "Real".to_string() ) ) - } else if self.tag_opt("(") { - self.ws_cmt() ; - if self.tag_opt("Array") { - self.ws_cmt() ; - let src = self.typ() ? ; - self.ws_cmt() ; - let tgt = self.typ() ? ; - self.ws_cmt() ; - self.tag(")") ? ; - Ok( Some( format!("(Array {} {})", src, tgt) ) ) - } else { - bail!("expected type") - } - } else { - Ok(None) - } - } - /// Declare-fun. fn declare_fun(& mut self) -> Res { if ! self.tag_opt("declare-fun") { @@ -473,7 +439,7 @@ impl<'a> InParser<'a> { || self.set_info() ? || self.set_option()?.is_some() || self.declare_fun() ? - || self.define_fun() ? + // || self.define_fun() ? || self.assert() ? || self.tag_opt("check-sat") || self.tag_opt("get-model") @@ -552,35 +518,6 @@ impl<'a> InParser<'a> { } - /// Define-fun. - fn define_fun(& mut self) -> Res { - if ! self.tag_opt("define-fun") { - return Ok(false) - } - self.ws_cmt() ; - let name = self.ident().chain_err( - || "while parsing symbol" - ) ? ; - self.ws_cmt() ; - let args = self.args().chain_err( - || "while parsing arguments" - ) ? ; - self.ws_cmt() ; - let typ = self.typ().chain_err( - || "while parsing return type" - ) ? ; - self.ws_cmt() ; - let body = self.sexpr().chain_err( - || "while parsing body" - ) ? ; - self.ws_cmt() ; - self.fun_defs.push( - FunDef { name, args, typ, body } - ) ; - - Ok(true) - } - /// Parses an `smt2` file. pub fn parse_output(mut self) -> Res { if conf.check_eld { diff --git a/src/common/consts.rs b/src/common/consts.rs index 568fe9cd..783d381a 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -151,7 +151,10 @@ pub mod keywords { } keys { - dec_dtyp ("declare-datatypes", doc = "Datatype declaration keyword.") + dec_dtyp ("declare-datatype", doc = "Datatype declaration keyword.") + dec_dtyps ( + "declare-datatypes", doc = "Multiple datatype declaration keyword." + ) dec_fun ("declare-fun", doc = "Predicate declaration keyword.") def_fun ("define-fun", doc = "Function definition keyword.") def_fun_rec ( diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 636fc2a5..95ea1e06 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -525,18 +525,35 @@ pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { continue } - write!(w, "{}({} (", pref, keywords::cmd::dec_dtyp) ? ; - for name in & dtyp.prms { - write!(w, " {}", name) ? + let mut all = vec![ dtyp.clone() ] ; + + for dtyp in & dtyp.deps { + let is_new = known.insert(dtyp) ; + assert! { is_new } + + if let Ok(dtyp) = get(dtyp) { + all.push(dtyp) + } else { + panic!("inconsistent datatype factory/maps state") + } + } + + writeln!(w, "{}({} (", pref, keywords::cmd::dec_dtyps) ? ; + write!(w, "{} ", pref) ? ; + + for dtyp in all.clone() { + write!(w, " ({} {})", dtyp.name, dtyp.prms.len()) ? + } + + writeln!(w, "\n{}) (", pref) ? ; + + for dtyp in all { + dtyp.write_dec(w, dtyp_pref) ? } - writeln!(w, " ) (") ? ; - dtyp.write(w, dtyp_pref) ? ; for dtyp in & dtyp.deps { let is_new = known.insert(dtyp) ; - assert!(is_new) ; - let dtyp = get(dtyp).expect("inconsistent datatype state") ; - dtyp.write(w, dtyp_pref) ? + assert!(is_new) } writeln!(w, "{}) )", pref) ? @@ -640,14 +657,52 @@ pub struct RDTyp { } impl RDTyp { /// Constructor. - pub fn new>(name: S, prms: TPrmMap) -> Self { + pub fn new>(name: S) -> Self { let name = name.into() ; RDTyp { - name, deps: vec![], prms, news: BTreeMap::new(), + name, deps: vec![], prms: TPrmMap::new(), news: BTreeMap::new(), default: "".into() } } + /// Pushes a type parameter. + pub fn push_typ_param>(& mut self, name: S) -> TPrmIdx { + let idx = self.prms.next_index() ; + self.prms.push( name.into() ) ; + idx + } + + /// Writes the declaration for a datatype. + pub fn write_dec( + & self, w: & mut W, pref: & str + ) -> ::std::io::Result<()> { + let close_par = if self.prms.is_empty() { + writeln!(w, "{}( ; {}", pref, self.name) ? ; + false + } else { + write!(w, "{}(par (", pref) ? ; + for prm in & self.prms { + write!(w, " {}", prm) ? + } + writeln!(w, " ) (") ? ; + true + } ; + + for (name, args) in & self.news { + write!(w, "{} ({}", pref, name) ? ; + for (name, typ) in args { + write!(w, " ({} ", name) ? ; + typ.write(w, & self.prms) ? ; + write!(w, ")") ? + } + writeln!(w, ")") ? + } + + writeln!( + w, "{}){}", pref, if close_par { " )" } else { "" } + ) + } + /// Writes a single datatype. pub fn write( & self, w: & mut W, pref: & str diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index 661b4aba..34b8dcaf 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -878,6 +878,11 @@ impl<'core> IceLearner<'core> { profile!{ self tick "learning", "qual", "simple gain" } let res = self.qualifiers.maximize( pred, |qual| { + if conf.ice.qual_step { + let _ = core.msg( + format!("evaluating {} (simple gain)", qual) + ) ; + } let res = data.simple_gain(qual, false) ? ; if conf.ice.qual_step { let _ = core.msg( @@ -906,6 +911,11 @@ impl<'core> IceLearner<'core> { profile!{ |self.core._profiler| tick "learning", "qual", "gain" } let res = qualifiers.maximize( pred, |qual| { + if conf.ice.qual_step { + let _ = core.msg( + format!("evaluating {} (gain)", qual) + ) ; + } let res = data.gain( pred, all_data, qual, & self_core._profiler, false ) ? ; @@ -1029,6 +1039,11 @@ impl<'core> IceLearner<'core> { // Term already known, skip. Ok(false) } else { + if conf.ice.qual_step || conf.ice.qual_synth_step { + let _ = self_core.msg( + format!("synth evaluating {}", term) + ) ; + } let gain = if simple { data.simple_gain(& term, false) ? } else { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 21541887..7245f336 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1325,144 +1325,278 @@ impl<'cxt, 's> Parser<'cxt, 's> { + /// Parses a datatype constructor. + fn dtyp_new( + & mut self, + params_map: & BTreeMap<& 's str, dtyp::TPrmIdx>, + ) -> Res< Option<(Pos, & 's str, dtyp::CArgs)> > { + if self.tag_opt("(") { + // Normal case. - /// Datatype declaration. - fn dtyp_dec(& mut self) -> Res { - if ! self.word_opt(keywords::cmd::dec_dtyp) { - return Ok(false) + let (constructor_pos, constructor_ident) = self.ident() ? ; + self.ws_cmt() ; + + let mut selectors = dtyp::CArgs::new() ; + + // Parse selectors. + while self.tag_opt("(") { + self.ws_cmt() ; + let (selector_pos, selector_ident) = self.ident() ? ; + self.ws_cmt() ; + + if selectors.iter().any( + |(id, _)| id == selector_ident + ) { + bail!( + self.error( + selector_pos, format!( + "found a selector named `{}` twice", selector_ident + ) + ) + ) + } + + let ptyp = self.nu_sort(& params_map) ? ; + selectors.push( + ( selector_ident.to_string(), ptyp ) + ) ; + + self.tag(")").chain_err( + || format!( + "closing selector `{}`", conf.emph(selector_ident) + ) + ) ? ; + self.ws_cmt() + } + + self.tag(")").chain_err( + || "closing datatype constructor" + ) ? ; + + Ok( + Some( (constructor_pos, constructor_ident, selectors) ) + ) + + } else if let Some( + (constructor_pos, constructor_ident) + ) = self.ident_opt() ? { + // Constant, paren-free constructor. This is actually not legal in + // SMT-LIB 2, but some people use it anyways. + return Ok( + Some( + ( constructor_pos, constructor_ident, dtyp::CArgs::new() ) + ) + ) + + } else { + return Ok( None ) } - let mut dtyps: Vec<(Pos, dtyp::RDTyp)> = vec![] ; + } - let mut params = dtyp::TPrmMap::new() ; - self.ws_cmt() ; - self.tag("(") ? ; + + + /// Parses a single datatype declaration. + fn dtyp_dec( + & mut self, dtyp: & mut dtyp::RDTyp, arity: Option + ) -> Res<()> { + self.tag("(").chain_err( + || "opening datatype declaration" + ) ? ; self.ws_cmt() ; let mut params_map = BTreeMap::new() ; + let param_pos = self.pos() ; - // Type parameters. - while let Some((pos, ident)) = self.ident_opt() ? { - let idx = params.next_index() ; - if let Some(prev) = params_map.insert(ident, idx) { - bail!( - self.error( - pos, format!( - "type parameters #{} and #{} have the same name `{}`", - idx, prev, ident + // Try to parse parameters. + let closing_paren = if self.word_opt("par") { + self.ws_cmt() ; + self.tag("(").chain_err( + || "opening sort parameter list" + ) ? ; + + while let Some((pos, ident)) = self.ident_opt() ? { + let idx = dtyp.push_typ_param(ident) ; + if let Some(prev) = params_map.insert(ident, idx) { + bail!( + self.error( + pos, format!( + "type parameters #{} and #{} have the same name `{}`", + idx, prev, ident + ) ) ) - ) + } + self.ws_cmt() } - params.push( ident.to_string() ) ; - self.ws_cmt() - } - self.tag(")") ? ; - self.ws_cmt() ; + if let Some(arity) = arity { + if Int::from( params_map.len() ) != arity { + bail!( + self.error( + param_pos, format!( + "expected {} parameters, found {}", arity, params_map.len() + ) + ) + ) + } + } - self.tag("(") ? ; - self.ws_cmt() ; + self.tag(")").chain_err( + || "closing sort parameter list" + ) ? ; - // Datatype declarations. - while self.tag_opt("(") { self.ws_cmt() ; - let (dtyp_pos, dtyp_ident) = self.ident() ? ; + self.tag("(").chain_err( + || "opening the list of constructor" + ) ? ; self.ws_cmt() ; - let mut dtyp = dtyp::RDTyp::new( dtyp_ident, params.clone() ) ; + true + } else { + false + } ; - // Constructors. - 'constructors: loop { - let ( - constructor_pos, constructor_ident, selectors - ) = if self.tag_opt("(") { - self.ws_cmt() ; - let (constructor_pos, constructor_ident) = self.ident() ? ; - self.ws_cmt() ; + while let Some( + (constructor_pos, constructor_ident, selectors) + ) = self.dtyp_new(& params_map) ? { + self.ws_cmt() ; - let mut selectors = dtyp::CArgs::new() ; + dtyp.add_constructor(constructor_ident, selectors).chain_err( + || self.error( + constructor_pos, "in this constructor" + ) + ) ? + } - // Selectors. - while self.tag_opt("(") { - self.ws_cmt() ; - let (selector_pos, selector_ident) = self.ident() ? ; - self.ws_cmt() ; - if selectors.iter().any( - |(id, _)| id == selector_ident - ) { - let error: Error = self.error( - selector_pos, - format!("found the selector `{}` twice", selector_ident) - ).into() ; - bail!( - error.chain_err( - || self.error( - dtyp_pos, "in this datatype declaration" - ) - ) - ) - } + if closing_paren { + self.ws_cmt() ; + self.tag(")").chain_err( + || "closing the list of constructor" + ) ? ; + } - let ptyp = self.nu_sort(& params_map) ? ; - selectors.push( - ( selector_ident.to_string(), ptyp ) - ) ; + self.ws_cmt() ; + self.tag(")").chain_err( + || "closing datatype declaration" + ) + } - self.ws_cmt() ; - self.tag(")") ? ; - self.ws_cmt() - } - self.tag(")") ? ; - self.ws_cmt() ; - (constructor_pos, constructor_ident, selectors) + /// Single datatype declaration. + fn dtyp_dec_item(& mut self) -> Res { + if ! self.word_opt(keywords::cmd::dec_dtyp) { + return Ok(false) + } - } else if let Some( - (constructor_pos, constructor_ident) - ) = self.ident_opt() ? { - self.ws_cmt() ; - (constructor_pos, constructor_ident, dtyp::CArgs::new()) + let (dtyp_pos, dtyp_ident) = self.ident().chain_err( + || "while parsing datatype declaration" + ) ? ; - } else { - break 'constructors - } ; + let mut dtyp = dtyp::RDTyp::new(dtyp_ident) ; + self.dtyp_dec(& mut dtyp, None).chain_err( + || self.error( + dtyp_pos, format!( + "while parsing the declaration for datatype `{}`", + conf.emph(& dtyp.name) + ) + ) + ) ? ; - dtyp.add_constructor(constructor_ident, selectors).chain_err( - || self.error(constructor_pos, "in this constructor") - ) ? + Ok(true) + } - } - for (_, dt) in & mut dtyps { - dt.add_dep( dtyp.name.clone() ) ; - dtyp.add_dep( dt.name.clone() ) - } + /// Multiple datatype declaration. + fn dtyp_decs_item(& mut self) -> Res { + if ! self.word_opt(keywords::cmd::dec_dtyps) { + return Ok(false) + } + self.ws_cmt() ; - dtyps.push( (dtyp_pos, dtyp) ) ; + // List of datatypes. + self.tag("(").chain_err( + || "opening the list of symbol/arity pairs" + ) ? ; + self.ws_cmt() ; - self.tag_opt(")") ; + let mut dtyps = vec![] ; + + while self.tag_opt("(") { + let (dtyp_pos, dtyp_ident) = self.ident().chain_err( + || "declaring a new datatype" + ) ? ; + self.ws_cmt() ; + + let arity = if let Some(arity) = self.numeral() { + arity + } else { + bail!( + self.error_here( + format!( + "expected arity for datatype `{}`", conf.emph(dtyp_ident) + ) + ) + ) + } ; + + dtyps.push( (dtyp_pos, dtyp::RDTyp::new(dtyp_ident), arity) ) ; + + self.tag(")").chain_err( + || format!( + "closing symbol/arity pair for `{}`", conf.emph(dtyp_ident) + ) + ) ? ; self.ws_cmt() } - self.tag(")") ? ; + self.tag(")").chain_err( + || "closing the list of symbol/arity pairs" + ) ? ; + self.ws_cmt() ; + + + + + + self.tag("(").chain_err( + || "opening the list of datatype declaration" + ) ? ; + self.ws_cmt() ; let mut final_dtyps = Vec::with_capacity( dtyps.len() ) ; - for (dtyp_pos, dtyp) in dtyps { - final_dtyps.push(( - dtyp_pos, dtyp::mk(dtyp).chain_err( - || self.error(dtyp_pos, "while parsing this datatype constructor") - ) ? - )) + for (dtyp_pos, mut dtyp, dtyp_arity) in dtyps { + self.dtyp_dec( + & mut dtyp, Some(dtyp_arity) + ).chain_err( + || format!( + "while parsing the declaration for datatype `{}`", + conf.emph(& dtyp.name) + ) + ).chain_err( + || self.error(dtyp_pos, "declared here") + ) ? ; + self.ws_cmt() ; + + let dtyp = dtyp::mk(dtyp).chain_err( + || self.error( + dtyp_pos, "while parsing the declaration for this datatype" + ) + ) ? ; + final_dtyps.push( (dtyp_pos, dtyp) ) } + self.tag(")").chain_err( + || "closing the list of datatype declaration" + ) ? ; + for (dtyp_pos, dtyp) in final_dtyps { if let Err((pos, err)) = dtyp.check() { let err: Error = self.error(pos, err).into() ; @@ -2644,19 +2778,25 @@ impl<'cxt, 's> Parser<'cxt, 's> { for ((index, exp), pos) in sig.index_iter().zip( kid_pos.into_iter() ) { let found = args[index].typ() ; if exp != & found { - err_chain! { - self.error( - pos, format!( - "expected an expression of sort {}, found {} ({})", - exp, & args[index], found + if let Some(nu) = exp.merge(& found) { + if let Some(term) = args[index].force_dtyp(nu) { + args[index] = term + } + } else { + err_chain! { + self.error( + pos, format!( + "expected an expression of sort {}, found {} ({})", + exp, & args[index], found + ) ) - ) - => self.error( - pred_pos, format!( - "in this application of {}, parameter #{}", - conf.emph(& instance[pred].name), index + => self.error( + pred_pos, format!( + "in this application of {}, parameter #{}", + conf.emph(& instance[pred].name), index + ) ) - ) + } } } } @@ -3403,7 +3543,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { || self.define_fun(instance) ? || self.define_funs_rec(instance) ? || self.assert(instance) ? - || self.dtyp_dec() ? { + || self.dtyp_dec_item() ? + || self.dtyp_decs_item() ? { Parsed::Items } else if self.check_sat() { Parsed::CheckSat diff --git a/src/term/eval.rs b/src/term/eval.rs index 08de397e..d812df33 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -54,9 +54,15 @@ fn total<'a>( fun_ref_count: & mut usize ) -> Res< CmdT<'a> > { let yielded = match op { - ZipOp::Op(op) => op.eval(values).chain_err( - || format!("while evaluating operator `{}`", op) - ) ?, + ZipOp::Op(op) => { + println!("{}", op) ; + for value in & values { + println!(" {} -- {}", value, value.typ()) + } + op.eval(values).chain_err( + || format!("while evaluating operator `{}`", op) + ) ? + }, ZipOp::New(name) => val::dtyp_new( typ.clone(), name.clone(), values @@ -121,6 +127,7 @@ fn total<'a>( }, ZipOp::Fun(name) => { + pause(& format!("fun {}", name), & Profiler::new()) ; let fun = if let Some(fun) = fun::get_as_ref(name) { fun } else { diff --git a/src/term/factory.rs b/src/term/factory.rs index a586a25a..8835e9c7 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -250,9 +250,9 @@ pub fn fun(typ: Typ, name: String, args: Vec) -> Term { /// /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] -pub fn app(op: Op, args: Vec) -> Term { +pub fn app(op: Op, mut args: Vec) -> Term { let typ = expect!( - op.type_check(& args) => |e| + op.type_check(& mut args) => |e| let res: Res<()> = Err( "Fatal internal type checking error, \ please notify the developer(s)".into() @@ -351,8 +351,8 @@ pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { /// /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] -pub fn try_app(op: Op, args: Vec) -> Result { - let typ = op.type_check(& args) ? ; +pub fn try_app(op: Op, mut args: Vec) -> Result { + let typ = op.type_check(& mut args) ? ; Ok( normalize(op, args, typ) ) } diff --git a/src/term/mod.rs b/src/term/mod.rs index 4573a540..813900a7 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -548,6 +548,20 @@ impl RTerm { } } + /// Forces the type of a datatype constructor. + pub fn force_dtyp(& self, nu_typ: Typ) -> Option { + match self { + RTerm::DTypNew { typ, name, args } => { + debug_assert! { nu_typ.is_compatible(typ) } + Some( + dtyp_new( nu_typ, name.clone(), args.clone() ) + ) + }, + RTerm::Cst(val) => val.force_dtyp(nu_typ).map(cst), + _ => None, + } + } + /// Casts a term. /// /// Only legal if the term's type and the one provided are compatible. @@ -948,11 +962,14 @@ impl RTerm { ) -> Option<(Term, bool)> { let mut changed = false ; + // println!("{}", self) ; + let res = fold::fold_custom_res( self, // Variable. |typ, var| if let Some(term) = map.var_get(var) { + // println!(" {}, {} ({})", typ, term, term.typ()) ; debug_assert_eq! { typ, & term.typ() } changed = true ; Ok(term) diff --git a/src/term/op.rs b/src/term/op.rs index 35eaa102..5a813ea4 100644 --- a/src/term/op.rs +++ b/src/term/op.rs @@ -102,10 +102,10 @@ impl Op { /// If there is an error, returns the type the spurious argument should have /// (if it is known) and the one found. pub fn type_check( - self, args: & [ Term ] + self, args: & mut [ Term ] ) -> Result { use Op::* ; - let mut args_iter = args.iter().enumerate() ; + macro_rules! err { (lft $($lft:tt)*) => ( return Err( term::TypError::typ($($lft)*) ) @@ -124,10 +124,10 @@ impl Op { macro_rules! arity_check { ( [ $min:tt, . ] => $e:expr ) => ( - if args_iter.len() < $min { + if args.len() < $min { err!(rgt "illegal application of `{}` to {} arguments (> {})", - self, args_iter.len(), $min + self, args.len(), $min ) } else { $e @@ -135,10 +135,10 @@ impl Op { ) ; ( [ $min:tt, $max:tt ] => $e:expr ) => ( - if args_iter.len() > $max { + if args.len() > $max { err!(rgt "illegal application of `{}` to {} arguments (> {})", - self, args_iter.len(), $max + self, args.len(), $max ) } else { arity_check!( [ $min, .] => $e ) @@ -147,13 +147,14 @@ impl Op { } macro_rules! all_same { - (arith) => ( + (arith) => ({ + let mut args_iter = args.iter_mut().enumerate() ; if let Some((index, fst)) = args_iter.next() { let typ = fst.typ() ; if ! typ.is_arith() { err!(lft None, typ, index) } - while let Some((index, next)) = args_iter.next() { + for (index, next) in args_iter { if typ != next.typ() { err!(lft Some(typ), next.typ(), index) } @@ -162,10 +163,11 @@ impl Op { } else { err!(nullary) } - ) ; + }) ; (bool) => ({ - while let Some((index, fst)) = args_iter.next() { + let mut args_iter = args.iter_mut().enumerate() ; + for (index, fst) in args_iter { let typ = fst.typ() ; if typ != typ::bool() { err!(lft Some(typ::bool()), typ, index) @@ -174,11 +176,30 @@ impl Op { typ::bool() }) ; - () => ({ + () => ( + all_same!( sub args args.iter_mut().enumerate() ) + ) ; + + (sub args $args:expr) => ({ + let mut args_iter = $args ; if let Some((_, fst)) = args_iter.next() { - let typ = fst.typ() ; - while let Some((index, next)) = args_iter.next() { - if typ != next.typ() { + // println!("all same") ; + let mut typ = fst.typ() ; + // println!(" {} | {}", typ, fst) ; + for (index, next) in args_iter { + let next_typ = next.typ() ; + // println!(" {} | {}", next_typ, next) ; + if let Some(merged_typ) = typ.merge( & next_typ ) { + if let Some(nu) = fst.force_dtyp( merged_typ.clone() ) { + typ = merged_typ.clone() ; + * fst = nu ; + // println!(" -> {}", fst) + } + if let Some(nu) = next.force_dtyp( merged_typ ) { + * next = nu ; + // println!(" -> {}", next) + } + } else { err!(lft Some(typ), next.typ(), index) } } @@ -189,7 +210,8 @@ impl Op { }) ; ($typ:expr) => ({ - while let Some((index, fst)) = args_iter.next() { + let mut args_iter = args.iter_mut().enumerate() ; + for (index, fst) in args_iter { if fst.typ() != $typ { err!(lft Some($typ), fst.typ(), index) } @@ -236,13 +258,17 @@ impl Op { } ), Ite => arity_check!( - [ 3, 3 ] => if let Some((index, cond)) = args_iter.next() { - if ! cond.typ().is_bool() { - err!(lft Some(typ::bool()), cond.typ(), index) + [ 3, 3 ] => { + let mut args_iter = args.iter_mut().enumerate() ; + + if let Some((index, cond)) = args_iter.next() { + if ! cond.typ().is_bool() { + err!(lft Some(typ::bool()), cond.typ(), index) + } + all_same!(sub args args_iter) + } else { + err!(nullary) } - all_same!() - } else { - err!(nullary) } ), diff --git a/src/term/typ.rs b/src/term/typ.rs index 952c271e..4ff10fd8 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -317,12 +317,12 @@ impl RTyp { /// Fails if the type is unknown. pub fn default_val(& self) -> Val { let typ = factory.mk( self.clone() ) ; - let mut current = & typ ; + let mut current = typ ; let mut stack = vec![] ; 'go_down: loop { - let mut val = match current.get() { + let mut val = match current.clone().get() { RTyp::Real => val::real( Rat::zero() ), RTyp::Int => val::int( Int::zero() ), RTyp::Bool => val::bool( true ), @@ -330,12 +330,24 @@ impl RTyp { src.clone(), tgt.default_val() ), RTyp::DTyp { dtyp, prms } => { - let mut prms = prms.iter() ; - if let Some(next) = prms.next() { - current = next ; + let mut args = vec![] ; + + for (_, arg_typ) in dtyp.news.get(& dtyp.default).expect( + "inconsistent datatype factory/map state" + ) { + let arg_typ = arg_typ.to_type(prms).unwrap_or_else( + |_| panic!("illegal type {}", current) + ) ; + args.push(arg_typ) + } + + let mut args = args.into_iter() ; + + if let Some(next) = args.next() { stack.push( - (current, dtyp.default.clone(), vec![], prms) + ( current.clone(), dtyp.default.clone(), vec![], args ) ) ; + current = next ; continue 'go_down } else { val::dtyp_new( diff --git a/src/term/zip.rs b/src/term/zip.rs index 0168146b..73245d25 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -90,6 +90,17 @@ pub enum ZipOp<'a> { /// A datatype selection. Slc(& 'a String), } +impl<'a> ::std::fmt::Display for ZipOp<'a> { + fn fmt(& self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ZipOp::Op(inner) => inner.fmt(fmt), + ZipOp::New(inner) => inner.fmt(fmt), + ZipOp::Fun(inner) => inner.fmt(fmt), + ZipOp::Slc(inner) => inner.fmt(fmt), + ZipOp::CArray => write!(fmt, "array"), + } + } +} // Nullary things the zipper can manipulate. @@ -141,7 +152,7 @@ pub fn zip( term: & Term, mut nul_do: NulF, mut app_do: AppF, mut partial: Partial ) -> Result where -Acc: Accumulator, Yield: Clone, +Acc: Accumulator, Yield: Clone + ::std::fmt::Display, NulF: for<'a> FnMut( ZipNullary<'a> @@ -157,15 +168,31 @@ Partial: for<'a> FnMut( // Empty vector of terms, useful when handling unary operators. let empty: Vec = Vec::with_capacity(0) ; + println!() ; + println!("start") ; + // Current term we're going down in. let mut term = term ; // Stack of `ZipFrame`. - let mut stack = Vec::with_capacity(7) ; + let mut stack: Vec<_> = Vec::with_capacity(7) ; // The current substitution, if any. let mut subst: Option< VarMap > = None ; + macro_rules! stack_print { + () => ({ + println!("stack {{") ; + for (ZipFrame { thing, rgt_args, .. }, _) in & stack { + println!(" {} > {}", thing, rgt_args.len()) + } + println!("}}") + }) ; + } + 'inspect_term: loop { + println!("{}", term) ; + + stack_print!() ; let result = match * term.get() { @@ -262,6 +289,7 @@ Partial: for<'a> FnMut( continue 'inspect_term } else { + println!("calling total function on {}", name) ; app_do(op, typ, lft_args) ? } }, @@ -282,6 +310,9 @@ Partial: for<'a> FnMut( } ; 'inspect_do_res: loop { + println!(" {}", result) ; + + stack_print!() ; match stack.pop() { @@ -294,10 +325,14 @@ Partial: for<'a> FnMut( ) => { subst = old_subst ; + println!(" {:?}", thing) ; + // Update left args. lft_args.push( result ) ; if rgt_args.len() == 0 { + println!(" -> 0") ; + match app_do(thing, typ, lft_args) ? { ZipDoTotal::Upp { yielded } => { result = yielded ; @@ -305,15 +340,17 @@ Partial: for<'a> FnMut( }, ZipDoTotal::Dwn { nu_term, nu_subst} => { + println!(" down (stack {})", stack.len()) ; if nu_subst.is_some() { subst = nu_subst } term = nu_term ; continue 'inspect_term - } + }, } } else { + println!(" -> {}", rgt_args.len()) ; match partial( ZipFrame { thing, typ, lft_args, rgt_args } diff --git a/src/val/mod.rs b/src/val/mod.rs index 90281bff..a2d57a75 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -378,6 +378,18 @@ impl RVal { } } + /// Forces the type of a datatype constructor. + pub fn force_dtyp(& self, nu_typ: Typ) -> Option { + if let RVal::DTypNew { typ, name, args } = self { + debug_assert! { nu_typ.is_compatible(typ) } + Some( + dtyp_new( nu_typ, name.clone(), args.clone() ) + ) + } else { + None + } + } + /// Attempts to cast a value. pub fn cast(& self, typ: & Typ) -> Res { use num::One ; From 268ece21c4c27281a9c855e952eac969a408823a Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 13 Jul 2018 01:24:18 +0900 Subject: [PATCH 17/94] bug fixes in datatype typing --- rsc/inactive/adt/sorted_len.smt2 | 17 ++++++++++++-- src/dtyp/mod.rs | 2 +- src/term/eval.rs | 5 ---- src/term/zip.rs | 34 +++++++++------------------ src/val/mod.rs | 40 +++++++++++++++++++++++++++++++- 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index 8c9a7b75..6dfe6ae4 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -73,6 +73,19 @@ (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) (=> (and + (rev_pre acc lst) + (not (= lst nil)) + ) + (rev_pre (cons (head lst) acc) (tail lst)) + ) +) ) + +; Recursive case. +(assert + (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) + (=> + (and + (rev_pre acc lst) (not (= lst nil)) (rev_pst (cons (head lst) acc) @@ -110,7 +123,7 @@ (and (not (= lst nil)) (not (= (tail lst) nil)) - (not (< (head lst) (head (tail lst)))) + (not (<= (head lst) (head (tail lst)))) ) (srt_pst lst false) ) @@ -123,7 +136,7 @@ (and (not (= lst nil)) (not (= (tail lst) nil)) - (< (head lst) (head (tail lst))) + (<= (head lst) (head (tail lst))) (srt_pst (tail lst) res) ) (srt_pst lst res) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 95ea1e06..a377f548 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -89,7 +89,7 @@ impl PartialTyp { } /// Resolves a partial type against a type. - pub fn unify ( + pub fn unify( & self, typ: & Typ, map: & mut TPrmMap ) -> Res<()> { let mut stack = vec![ (self, typ) ] ; diff --git a/src/term/eval.rs b/src/term/eval.rs index d812df33..21129627 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -55,10 +55,6 @@ fn total<'a>( ) -> Res< CmdT<'a> > { let yielded = match op { ZipOp::Op(op) => { - println!("{}", op) ; - for value in & values { - println!(" {} -- {}", value, value.typ()) - } op.eval(values).chain_err( || format!("while evaluating operator `{}`", op) ) ? @@ -127,7 +123,6 @@ fn total<'a>( }, ZipOp::Fun(name) => { - pause(& format!("fun {}", name), & Profiler::new()) ; let fun = if let Some(fun) = fun::get_as_ref(name) { fun } else { diff --git a/src/term/zip.rs b/src/term/zip.rs index 73245d25..bca6c115 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -168,9 +168,6 @@ Partial: for<'a> FnMut( // Empty vector of terms, useful when handling unary operators. let empty: Vec = Vec::with_capacity(0) ; - println!() ; - println!("start") ; - // Current term we're going down in. let mut term = term ; @@ -179,20 +176,18 @@ Partial: for<'a> FnMut( // The current substitution, if any. let mut subst: Option< VarMap > = None ; - macro_rules! stack_print { - () => ({ - println!("stack {{") ; - for (ZipFrame { thing, rgt_args, .. }, _) in & stack { - println!(" {} > {}", thing, rgt_args.len()) - } - println!("}}") - }) ; - } + // macro_rules! stack_print { + // () => ({ + // println!("stack {{") ; + // for (ZipFrame { thing, rgt_args, .. }, _) in & stack { + // println!(" {} > {}", thing, rgt_args.len()) + // } + // println!("}}") + // }) ; + // } 'inspect_term: loop { - println!("{}", term) ; - - stack_print!() ; + // stack_print!() ; let result = match * term.get() { @@ -289,7 +284,6 @@ Partial: for<'a> FnMut( continue 'inspect_term } else { - println!("calling total function on {}", name) ; app_do(op, typ, lft_args) ? } }, @@ -310,9 +304,7 @@ Partial: for<'a> FnMut( } ; 'inspect_do_res: loop { - println!(" {}", result) ; - - stack_print!() ; + // stack_print!() ; match stack.pop() { @@ -325,13 +317,11 @@ Partial: for<'a> FnMut( ) => { subst = old_subst ; - println!(" {:?}", thing) ; // Update left args. lft_args.push( result ) ; if rgt_args.len() == 0 { - println!(" -> 0") ; match app_do(thing, typ, lft_args) ? { ZipDoTotal::Upp { yielded } => { @@ -340,7 +330,6 @@ Partial: for<'a> FnMut( }, ZipDoTotal::Dwn { nu_term, nu_subst} => { - println!(" down (stack {})", stack.len()) ; if nu_subst.is_some() { subst = nu_subst } @@ -350,7 +339,6 @@ Partial: for<'a> FnMut( } } else { - println!(" -> {}", rgt_args.len()) ; match partial( ZipFrame { thing, typ, lft_args, rgt_args } diff --git a/src/val/mod.rs b/src/val/mod.rs index a2d57a75..05e4ef9b 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -138,7 +138,35 @@ pub fn none(typ: Typ) -> Val { } /// Creates a new datatype value. -pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Val { +pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { + { + let (dtyp, typ_args) = typ.dtyp_inspect().unwrap_or_else( + || panic!("illegal application of constructor {} of type {}", name, typ) + ) ; + let cargs = dtyp.news.get(& name).unwrap_or_else( + || panic!( + "unknown constructor {} for datatype {}", conf.bad(& name), dtyp.name + ) + ) ; + if args.len() != cargs.len() { + panic!( + "illegal application of constructor {} for datatype {} to {} \ + arguments, expected {}", + conf.bad(& name), dtyp.name, args.len(), cargs.len() + ) + } + + for (count, (_ , ptyp)) in cargs.iter().enumerate() { + let typ = ptyp.to_type(typ_args).unwrap_or_else( + |_| panic!("illegal datatype {}", typ) + ) ; + if let Some(nu) = args[count].typ().merge(& typ) { + if let Some(nu) = args[count].force_dtyp(nu) { + args[count] = nu + } + } + } + } factory.mk( RVal::DTypNew { typ, name, args } ) } @@ -414,6 +442,16 @@ impl RVal { // non-value of any other type... (& RVal::N(_), _) => Ok(none(typ.clone())), + ( + & RVal::DTypNew { typ: ref vtyp, ref name, ref args }, _ + ) => if let Some(typ) = vtyp.merge(typ) { + Ok( dtyp_new(typ, name.clone(), args.clone()) ) + } else { + bail!( + "Cannot cast value {} to type {}", self, typ + ) + }, + (val, typ) => bail!( "Cannot cast value {} to type {}", val, typ ), From f4b436a08eecdf9d27772c8f81e8bc76e04f9065 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 13 Jul 2018 02:22:19 +0900 Subject: [PATCH 18/94] option for restarting teacher's solver on each cex query --- rsc/inactive/adt/sorted_len.smt2 | 40 +++----------- src/common/config.rs | 19 ++++++- src/teacher/mod.rs | 93 +++++++++++++++++++------------- 3 files changed, 80 insertions(+), 72 deletions(-) diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index 6dfe6ae4..49f7deba 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -50,10 +50,6 @@ ; in ; loop [] -; Pre-condition. -(declare-fun - rev_pre ((Lst Int) (Lst Int)) Bool -) ; Post-condition. (declare-fun rev_pst ( (Lst Int) (Lst Int) (Lst Int) ) Bool @@ -62,10 +58,7 @@ ; Terminal case. (assert (forall ( (acc (Lst Int)) ) - (=> - (rev_pre acc nil) - (rev_pst acc nil acc) - ) + (rev_pst acc nil acc) ) ) ; Recursive case. @@ -73,19 +66,6 @@ (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) (=> (and - (rev_pre acc lst) - (not (= lst nil)) - ) - (rev_pre (cons (head lst) acc) (tail lst)) - ) -) ) - -; Recursive case. -(assert - (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) - (=> - (and - (rev_pre acc lst) (not (= lst nil)) (rev_pst (cons (head lst) acc) @@ -123,7 +103,7 @@ (and (not (= lst nil)) (not (= (tail lst) nil)) - (not (<= (head lst) (head (tail lst)))) + (not (< (head lst) (head (tail lst)))) ) (srt_pst lst false) ) @@ -136,7 +116,7 @@ (and (not (= lst nil)) (not (= (tail lst) nil)) - (<= (head lst) (head (tail lst))) + (< (head lst) (head (tail lst))) (srt_pst (tail lst) res) ) (srt_pst lst res) @@ -150,22 +130,14 @@ ; and (sorted (rev lst)) ; then (assert (all_elements_the_same lst)) (assert - (forall ( (lst (Lst Int)) ) - (=> - true - (rev_pre nil lst) - ) - ) -) -(assert - (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) ) + (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) (i Int) ) (=> (and - (rev_pre nil lst1) (rev_pst nil lst1 lst2) (srt_pst lst1 true) (srt_pst lst2 true) - (not (all_equal lst1)) + (not (and (= lst1 nil ) (= lst2 nil ))) + (not (and (= lst1 (cons i nil)) (= lst2 (cons i nil)))) ) false ) diff --git a/src/common/config.rs b/src/common/config.rs index 77db1fdc..8a60c3e9 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -938,6 +938,8 @@ pub struct TeacherConf { pub max_bias: bool, /// Allow partial samples. pub partial: bool, + /// Restart the teacher's solver everytime it's used. + pub restart_on_cex: bool, } impl SubConf for TeacherConf { fn need_out_dir(& self) -> bool { false } @@ -1013,6 +1015,20 @@ impl TeacherConf { "on" ).takes_value(true).number_of_values(1).display_order( order() ) + ).arg( + + Arg::with_name("restart_on_cex").long("--restart_on_cex").help( + "restart the teacher's solver for each cex query" + ).validator( + bool_validator + ).value_name( + bool_format + ).default_value( + "off" + ).takes_value(true).number_of_values(1).display_order( + order() + ).hidden(true) + ) } @@ -1023,9 +1039,10 @@ impl TeacherConf { let bias_cexs = bool_of_matches(matches, "bias_cexs") ; let max_bias = bool_of_matches(matches, "max_bias") ; let partial = bool_of_matches(matches, "partial") ; + let restart_on_cex = bool_of_matches(matches, "restart_on_cex") ; TeacherConf { - step, assistant, bias_cexs, max_bias, partial + step, assistant, bias_cexs, max_bias, partial, restart_on_cex } } } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 106e1a40..e7b0c64b 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -668,45 +668,55 @@ impl<'a> Teacher<'a> { let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ; - self.solver.push(1) ? ; - - // Retrieve true/false predicates and clauses to ignore. Define predicates. - for (pred, cand) in cands.index_iter() { - if let Some(ref term) = * cand { - let term = if let Some(other) = self.partial_model.get(& pred) { - term::and( vec![term.clone(), other.clone()] ) - } else { - term.clone() - } ; - match term.bool() { - Some(true) => { - let _ = true_preds.insert(pred) ; - clauses_to_ignore.extend( - self.instance.clauses_of(pred).1 - ) - }, - Some(false) => { - let _ = false_preds.insert(pred) ; - clauses_to_ignore.extend( - self.instance.clauses_of(pred).0 - ) - }, - None => { + macro_rules! define_preds { + () => ({ + true_preds.clear() ; + false_preds.clear() ; + + // Retrieve true/false predicates and clauses to ignore. Define predicates. + for (pred, cand) in cands.index_iter() { + if let Some(ref term) = * cand { let term = if let Some(other) = self.partial_model.get(& pred) { term::and( vec![term.clone(), other.clone()] ) } else { term.clone() } ; - let pred = & self.instance[pred] ; - let sig: Vec<_> = pred.sig.index_iter().map( - |(var, typ)| (var, typ.get()) - ).collect() ; - self.solver.define_fun( - & pred.name, & sig, typ::bool().get(), & SmtTerm::new(& term) - ) ? - }, + match term.bool() { + Some(true) => { + let _ = true_preds.insert(pred) ; + clauses_to_ignore.extend( + self.instance.clauses_of(pred).1 + ) + }, + Some(false) => { + let _ = false_preds.insert(pred) ; + clauses_to_ignore.extend( + self.instance.clauses_of(pred).0 + ) + }, + None => { + let term = if let Some(other) = self.partial_model.get(& pred) { + term::and( vec![term.clone(), other.clone()] ) + } else { + term.clone() + } ; + let pred = & self.instance[pred] ; + let sig: Vec<_> = pred.sig.index_iter().map( + |(var, typ)| (var, typ.get()) + ).collect() ; + self.solver.define_fun( + & pred.name, & sig, typ::bool().get(), & SmtTerm::new(& term) + ) ? + }, + } + } } - } + }) ; + } + + if ! conf.teacher.restart_on_cex { + self.solver.push(1) ? ; + define_preds!() } let instance = self.instance.clone() ; @@ -717,7 +727,11 @@ impl<'a> Teacher<'a> { macro_rules! run { ($clause:expr, $bias:expr) => ( if ! clauses_to_ignore.contains($clause) { - self.solver.push(1) ? ; + if conf.teacher.restart_on_cex { + define_preds!() + } else { + self.solver.push(1) ? + } let cexs = self.get_cex( * $clause, & true_preds, & false_preds, $bias, @@ -727,7 +741,11 @@ impl<'a> Teacher<'a> { || format!("while getting counterexample for clause {}", $clause) ) ? ; - self.solver.pop(1) ? ; + if conf.teacher.restart_on_cex { + smt::reset(& mut self.solver) ? + } else { + self.solver.pop(1) ? + } if ! cexs.is_empty() { // got_pos_neg_samples = got_pos_neg_samples || ( @@ -796,8 +814,9 @@ impl<'a> Teacher<'a> { // ) // } - if self.count % 100 == 0 { - smt::reset(& mut self.solver) ? + if self.count % 100 == 0 + || conf.teacher.restart_on_cex { + smt::reset(& mut self.solver) ? ; } else { self.solver.pop(1) ? } From 8312c37f05652b5ed4b0dd128430a84417954695 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 13 Jul 2018 13:57:12 +0900 Subject: [PATCH 19/94] minor evaluation improvement --- rsc/inactive/adt/sorted_len.smt2 | 21 ++++- rsc/inactive/adt/sorted_len_rev.smt2 | 130 +++++++++++++++++++++++++++ src/term/eval.rs | 23 +++++ 3 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 rsc/inactive/adt/sorted_len_rev.smt2 diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index 49f7deba..589c62c2 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -77,6 +77,18 @@ ) ) ) +; Partial spec. +(assert + (forall ( (acc (Lst Int)) (lst (Lst Int)) (res (Lst Int)) ) + (=> + (and + (rev_pst acc lst res) + (not (= (+ (len acc) (len lst)) (len res))) + ) + false + ) +) ) + ; let rec sorted = function ; | nil | _ :: nil => true @@ -93,10 +105,12 @@ (forall ( (unused Bool) ) (srt_pst nil true) ) ) + (assert (forall ( (hd Int) ) (srt_pst (cons hd nil) true) ) ) + (assert (forall ( (lst (Lst Int)) ) (=> @@ -104,8 +118,9 @@ (not (= lst nil)) (not (= (tail lst) nil)) (not (< (head lst) (head (tail lst)))) + (srt_pst lst true) ) - (srt_pst lst false) + false ) ) ) @@ -136,8 +151,7 @@ (rev_pst nil lst1 lst2) (srt_pst lst1 true) (srt_pst lst2 true) - (not (and (= lst1 nil ) (= lst2 nil ))) - (not (and (= lst1 (cons i nil)) (= lst2 (cons i nil)))) + (not (<= (len lst1) 2)) ) false ) @@ -145,4 +159,3 @@ (check-sat) -(get-model) \ No newline at end of file diff --git a/rsc/inactive/adt/sorted_len_rev.smt2 b/rsc/inactive/adt/sorted_len_rev.smt2 new file mode 100644 index 00000000..eb0c5286 --- /dev/null +++ b/rsc/inactive/adt/sorted_len_rev.smt2 @@ -0,0 +1,130 @@ +(set-logic HORN) + +(declare-datatypes ( (Lst 1) ) ( + (par (T) ( + (nil) + (cons (head T) (tail (Lst T))) + ) ) +) ) + +(define-funs-rec + ( + (len ( (l (Lst Int)) ) Int) + ) + ( + (ite + (= l nil) + 0 + (+ 1 (len (tail l))) + ) + ) +) + +(define-funs-rec + ( + (all_equal ( (l (Lst Int)) ) Bool) + (all_equal_aux ( (v Int) (l (Lst Int)) ) Bool) + ) + ( + (ite + (= l nil) + true + (all_equal_aux (head l) (tail l)) + ) + (ite + (= l nil) + true + (ite + (not (= (head l) v) ) + false + (all_equal_aux v (tail l)) + ) + ) + ) +) + +(define-funs-rec + ( + (rev ( (l (Lst Int)) ) (Lst Int)) + (rev_aux ( (acc (Lst Int)) (l (Lst Int)) ) (Lst Int)) + ) + ( + (rev_aux nil l) + (ite + (= l nil) + acc + (rev_aux (cons (head l) acc) (tail l)) + ) + ) +) + + +; let rec sorted = function +; | nil | _ :: nil => true +; | h1 :: h2 :: t => (h1 < h2) and (sorted (h2 :: t)) +; (* STRICTLY sorted~~~~~^ *) + +; Post-condition. +(declare-fun + srt_pst ( (Lst Int) Bool ) Bool +) + +; Terminal cases. +(assert + (forall ( (unused Bool) ) + (srt_pst nil true) +) ) + +(assert + (forall ( (hd Int) ) + (srt_pst (cons hd nil) true) +) ) + +(assert + (forall ( (lst (Lst Int)) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (not (< (head lst) (head (tail lst)))) + (srt_pst lst true) + ) + false + ) +) ) + +; Recursive case. +(assert + (forall ( (lst (Lst Int)) (res Bool) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (< (head lst) (head (tail lst))) + (srt_pst (tail lst) res) + ) + (srt_pst lst res) + ) +) ) + + +; let main lst = +; if lst = (rev lst) +; and (sorted lst) +; and (sorted (rev lst)) +; then (assert (all_elements_the_same lst)) +(assert + (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) (i Int) ) + (=> + (and + (= (rev lst1) lst2) + (srt_pst lst1 true) + (srt_pst lst2 true) + (not (<= (len lst1) 1)) + ) + false + ) +) ) + + +(check-sat) diff --git a/src/term/eval.rs b/src/term/eval.rs index 21129627..e6ad02d6 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -14,13 +14,36 @@ pub type CmdT<'a> = ZipDoTotal< 'a, Val > ; /// Term evaluation. pub fn eval(term: & Term, model: & E) -> Res { + if let Some(val) = term.val() { + return Ok(val) + } else if let Some(idx) = term.var_idx() { + if idx < model.len() { + return Ok( model.get(idx).clone() ) + } else { + bail!("model is too short ({} / {})", * idx, model.len()) + } + } + + let mut fun_ref_count = 0 ; + let res = zip( term, |zip_null| leaf(model, zip_null), |op, typ, values| total(op, typ, values, & mut fun_ref_count), partial ) ; fun::decrease_ref_count(fun_ref_count) ; + // if let Ok(res) = res.as_ref() { + // if model.len() > 0 + // && ! res.is_known() { + // println!("eval {}", term) ; + // for v in 0 .. model.len() { + // println!(" v_{}: {}", v, model.get( v.into() )) + // } + // println!("= {}", res) ; + // pause(" blah", & Profiler::new()) ; + // } + // } res } From 6a8ba6336f8c1f04c9a9b618d44ffa8be6c51401 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 18 Jul 2018 16:06:29 +0900 Subject: [PATCH 20/94] added tailrec len in adt test case --- rsc/inactive/adt/sorted_len_rev.smt2 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rsc/inactive/adt/sorted_len_rev.smt2 b/rsc/inactive/adt/sorted_len_rev.smt2 index eb0c5286..0f77908c 100644 --- a/rsc/inactive/adt/sorted_len_rev.smt2 +++ b/rsc/inactive/adt/sorted_len_rev.smt2 @@ -7,6 +7,21 @@ ) ) ) ) +(define-funs-rec + ( + (len_tr ( (l (Lst Int)) ) Int ) + (len_tailrec ( (acc Int) (l (Lst Int)) ) Int) + ) + ( + (len_tailrec 0 l) + (ite + (= l nil) + acc + (len_tailrec (+ 1 acc) (tail l)) + ) + ) +) + (define-funs-rec ( (len ( (l (Lst Int)) ) Int) From 86879f3131ac2d24cbe1116b5cd4d87bd159de30 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 18 Jul 2018 16:29:10 +0900 Subject: [PATCH 21/94] dependency update --- Cargo.lock | 29 ++++++++++++++++------------- src/hoice.rs | 2 +- src/parse/mod.rs | 2 +- src/term/factory.rs | 11 +++-------- src/term/typ.rs | 13 ++++--------- src/val/mod.rs | 13 ++++--------- src/var_to/terms.rs | 12 +++--------- src/var_to/vals.rs | 13 +++---------- 8 files changed, 35 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0589c99..c4acc949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ name = "backtrace-sys" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -103,8 +103,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hashconsing" -version = "0.9.10" -source = "git+https://github.com/AdrienChampion/hashconsing#45f0a4dad7bd81e569e05baf1cc65b4f53b663b1" +version = "0.10.1" +source = "git+https://github.com/AdrienChampion/hashconsing#61e5cd1ff482f84a90075fe749755f64fe2c59c2" +dependencies = [ + "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "hoice" @@ -114,12 +117,12 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashconsing 0.9.10 (git+https://github.com/AdrienChampion/hashconsing)", + "hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)", "isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)", ] @@ -135,7 +138,7 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -215,7 +218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rand" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -314,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" +"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" "checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -322,9 +325,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum hashconsing 0.9.10 (git+https://github.com/AdrienChampion/hashconsing)" = "" +"checksum hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)" = "" "checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" -"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" +"checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)" = "" "checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" @@ -334,7 +337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" -"checksum rand 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6802c0e883716383777e147b7c21323d5de7527257c8b6dc1365a7f2983e90f6" +"checksum rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "12397506224b2f93e6664ffc4f664b29be8208e5157d3d90b44f09b5fae470ea" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" diff --git a/src/hoice.rs b/src/hoice.rs index 49927dcd..6424a984 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -10,7 +10,6 @@ #![doc(test(attr(deny(warnings))))] #![allow(non_upper_case_globals)] -#[macro_use] extern crate lazy_static ; #[macro_use] extern crate mylib ; @@ -19,6 +18,7 @@ extern crate error_chain ; #[macro_use] extern crate clap ; extern crate ansi_term as ansi ; +#[macro_use] extern crate hashconsing ; extern crate rsmt2 ; extern crate num ; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 7245f336..c65c4fe6 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -35,7 +35,7 @@ pub enum Parsed { -lazy_static!{ +lazy_static! { /// Set of legal special characters in identifiers. static ref id_special_chars: HashSet<& 'static str> = { let mut set = HashSet::with_capacity(17) ; diff --git a/src/term/factory.rs b/src/term/factory.rs index 8835e9c7..84d4aa1c 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -1,18 +1,13 @@ //! Term creation functions. -use hashconsing::{ HashConsign, HConser } ; +use hashconsing::HashConsign ; use common::* ; use term::{ RTerm, Term, Op } ; -/// Type of the term factory. -type Factory = RwLock< HashConsign > ; - -lazy_static! { +new_consign! { /// Term factory. - static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa ) - ) ; + let factory = consign(conf.instance.term_capa) for RTerm ; } lazy_static! { diff --git a/src/term/typ.rs b/src/term/typ.rs index 4ff10fd8..cdcafbbf 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -1,18 +1,13 @@ //! Everything type-related. -use hashconsing::{ HashConsign, HConser, HConsed } ; +use hashconsing::{ HashConsign, HConsed } ; use common::* ; use dtyp::TPrmMap ; -/// Type of the term factory. -type Factory = RwLock< HashConsign > ; - -lazy_static! { - /// Term factory. - static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa ) - ) ; +new_consign! { + /// Type factory. + let factory = consign(conf.instance.term_capa) for RTyp ; } diff --git a/src/val/mod.rs b/src/val/mod.rs index 05e4ef9b..3ae09957 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -6,18 +6,13 @@ //! - `Val::I` from `Int`, `usize`, `isize`, `u32`, `i32`, `u64`, `i64` //! - `Val::N` from `()` -use hashconsing::{ HashConsign, HConser, HConsed } ; +use hashconsing::HConsed ; use common::* ; -/// Type of the term factory. -type Factory = RwLock< HashConsign > ; - -lazy_static! { - /// Term factory. - static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa ) - ) ; +new_consign! { + /// Value factory. + let factory = consign(conf.instance.term_capa) for RVal ; } /// A hash-consed type. diff --git a/src/var_to/terms.rs b/src/var_to/terms.rs index 0596361e..c577305f 100644 --- a/src/var_to/terms.rs +++ b/src/var_to/terms.rs @@ -1,18 +1,13 @@ /*! Hashconsed maps from variables to terms. */ -use hashconsing::HConsed ; +use hashconsing::{ HashConsign, HConsed } ; use common::* ; -/// Type of the term factory. -type Factory = RwLock< HashConsign> > ; - -lazy_static! { +new_consign! { /// Term factory. - static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa / 10 ) - ) ; + let factory = consign(conf.instance.term_capa / 10) for VarMap ; } /// Hashconsed arguments for predicate applications. @@ -24,6 +19,5 @@ pub type VarTermsMap = HConMap ; /// Creates some new arguments. pub fn new(args: VarMap) -> VarTerms { - use hashconsing::HConser ; factory.mk(args) } \ No newline at end of file diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index 3d95fd87..f43567bf 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -3,20 +3,13 @@ use std::cmp::Ordering ; -use hashconsing::{ HashConsign, HConsed, HConser } ; +use hashconsing::{ HashConsign, HConsed } ; use common::* ; - - -/// Factory for hash consed arguments. -pub type Factory = RwLock> ; - -lazy_static! { +new_consign! { /// Term factory. - static ref factory: Factory = RwLock::new( - HashConsign::with_capacity( conf.instance.term_capa ) - ) ; + let factory = consign(conf.instance.term_capa) for RVarVals ; } /// Creates hashconsed arguments. From 6b6b1d010cc9eab27720442fdd90376ee32142e2 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 18 Jul 2018 20:21:37 +0900 Subject: [PATCH 22/94] reducing cyclomatic complexity --- src/common/mod.rs | 14 +- src/instance/instance/pre_instance.rs | 2 +- src/instance/preproc/mod.rs | 188 +------- src/instance/preproc/utils.rs | 249 ++++++++++- src/parse/mod.rs | 4 +- src/split.rs | 8 +- src/teacher/assistant.rs | 67 +-- src/teacher/mod.rs | 597 ++++++++++++++++---------- src/term/fold.rs | 366 ---------------- src/term/mod.rs | 243 +++-------- src/val/mod.rs | 2 +- 11 files changed, 747 insertions(+), 993 deletions(-) delete mode 100644 src/term/fold.rs diff --git a/src/common/mod.rs b/src/common/mod.rs index a0f46c5e..a1061260 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -268,6 +268,16 @@ pub type VarInfos = VarMap<::instance::info::VarInfo> ; pub type Candidates = PrdMap< Option > ; unsafe impl Send for PrdMap {} + +/// Teaching result. +pub enum TeachRes { + /// A model. + Model(Candidates), + /// An unsat result. + Unsat( ::unsat_core::UnsatRes ), +} + + /// Quantified variables for a top term. pub type Quantfed = VarHMap ; @@ -409,7 +419,7 @@ impl Evaluator for VarMap { for (var, val) in self.index_iter() { print!("{} -> {}, ", var, val) } - println!("") + println!() } } impl Evaluator for () { @@ -437,7 +447,7 @@ where E: Evaluator { for (v1, (v2, _)) in self.0.index_iter() { print!("{} -> {}", v1, v2) } - println!("") ; + println!() ; self.1.print() } } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index b61fbb37..2df7e22b 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -2143,7 +2143,7 @@ impl ClauseSimplifier { bool_prop!() ; if ! changed { - break + break 'outter } else { changed = false ; } diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index b468da3a..dbbd0dbe 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -371,33 +371,6 @@ impl<'a> Reductor<'a> { // Starts at `1`, `0` is reserved for the fixed point. let mut count = 1 ; - // Checks if the instance is already solved. - macro_rules! check_solved { - () => ( - if self.instance.is_solved() { - if ! self.instance.is_unsat() { - profile! { - |_profiler| tick "preproc", "final simplify" - } - // Check remaining clauses, maybe some of them are unsat. - match self.instance.simplify_all() { - Ok(_) => (), - Err(e) => { - if e.is_unsat() { - self.instance.set_unsat() - } - bail!(e) - }, - } - profile! { - |_profiler| mark "preproc", "final simplify" - } - } - return Ok(()) - } - ) ; - } - // Runs and profiles a pre-processor. // // Returns `true` if the pre-processor did something. @@ -412,57 +385,12 @@ impl<'a> Reductor<'a> { ($preproc:ident) => ( run!($preproc bool) ) ; ($preproc:ident $($tail:tt)*) => ( if let Some(preproc) = self.$preproc.as_mut() { - profile! { - |_profiler| tick "preproc", preproc.name() - } - log! { @verb - "running {}", conf.emph( preproc.name() ) - } - let mut red_info = preproc.apply( & mut self.instance ) ? ; - profile! { - |_profiler| mark "preproc", preproc.name() - } - - if red_info.non_zero() { - - count += 1 ; - preproc_dump!( - self.instance => - format!("preproc_{:0>4}_{}", count, preproc.name()), - format!("Instance after running `{}`.", preproc.name()) - ) ? ; - - profile!{ - |_profiler| format!( - "{:>10} pred red", preproc.name() - ) => add red_info.preds - } - profile!{ - |_profiler| format!( - "{:>10} clause red", preproc.name() - ) => add red_info.clauses_rmed - } - profile!{ - |_profiler| format!( - "{:>10} clause add", preproc.name() - ) => add red_info.clauses_added - } - profile!{ - |_profiler| format!( - "{:>10} arg red", preproc.name() - ) => add red_info.args_rmed - } - log! { @verb - "{}: {}", conf.emph( preproc.name() ), red_info - } - conf.check_timeout() ? ; - check_solved!() ; + if let Some(red_info) = utils::run_preproc( + & mut self.instance, _profiler, preproc, & mut count + ) ? { run! { @ $($tail)* Some(red_info) } } else { - log! { @verb - "{}: did nothing", conf.emph( preproc.name() ) - } - run! { @ $($tail)* Some(red_info) } + return Ok(()) } } else { run! { @ $($tail)* None } @@ -470,54 +398,7 @@ impl<'a> Reductor<'a> { ) ; } - preproc_dump!( - self.instance => - format!("preproc_{:0>4}_original_instance", count), - "Instance before pre-processing." - ) ? ; - profile!{ - |_profiler| - "clause count original" => add self.instance.clauses().len() - } - profile!{ - |_profiler| - "nl clause count original" => add { - let mut count = 0 ; - 'clause_iter: for clause in self.instance.clauses() { - for (_, argss) in clause.lhs_preds() { - if argss.len() > 1 { - count += 1 ; - continue 'clause_iter - } - } - } - count - } - } - profile!{ - |_profiler| - "pred count original" => add { - let mut count = 0 ; - for pred in self.instance.pred_indices() { - if ! self.instance.is_known(pred) { - count += 1 - } - } - count - } - } - profile!{ - |_profiler| - "arg count original" => add { - let mut args = 0 ; - for info in self.instance.preds() { - if ! self.instance.is_known(info.idx) { - args += info.sig.len() - } - } - args - } - } + utils::register_stats(& self.instance, _profiler, count) ? ; if simplify_first { run! { simplify } ; @@ -591,57 +472,7 @@ impl<'a> Reductor<'a> { } } - preproc_dump!( - self.instance => - "preproc_0000_fixed_point", - "Instance after reaching preproc fixed-point." - ) ? ; - - profile!{ - |_profiler| - "clause count final" => add self.instance.clauses().len() - } - profile!{ - |_profiler| - "nl clause count final" => add { - let mut count = 0 ; - 'clause_iter: for clause in self.instance.clauses() { - for (_, argss) in clause.lhs_preds() { - if argss.len() > 1 { - count += 1 ; - continue 'clause_iter - } - } - } - count - } - } - - profile!{ - |_profiler| - "pred count final" => add { - let mut count = 0 ; - for pred in self.instance.pred_indices() { - if ! self.instance.is_known(pred) { - count += 1 - } - } - count - } - } - - profile!{ - |_profiler| - "arg count final" => add { - let mut args = 0 ; - for info in self.instance.preds() { - if ! self.instance.is_known(info.idx) { - args += info.sig.len() - } - } - args - } - } + utils::register_final_stats(& self.instance, _profiler) ? ; Ok(()) } @@ -2822,3 +2653,10 @@ impl RedStrat for RUnroll { } } + + + + + + + diff --git a/src/instance/preproc/utils.rs b/src/instance/preproc/utils.rs index 3a434298..6fcda191 100644 --- a/src/instance/preproc/utils.rs +++ b/src/instance/preproc/utils.rs @@ -1,6 +1,8 @@ //! Helper types and functions for preprocessing. use common::* ; + +use super::{ PreInstance, RedStrat } ; use var_to::terms::VarTermsSet ; @@ -342,7 +344,6 @@ impl ExtractionCxt { } - fn terms_of_lhs_part<'a>( & mut self, instance: & Instance, var_info: & VarInfos, (lhs_terms, lhs_preds): (& TermSet, & 'a PredApps), @@ -578,3 +579,249 @@ pub enum TExtractRes { /// Failure: need qualifiers. Failed, } + + + + + + + + + + + +/// Registers statistics of the original instance. +/// +/// Dumps the instance if asked to do so. +pub fn register_stats( + instance: & Instance, _profiler: & Profiler, count: usize +) -> Res<()> { + + preproc_dump!( + instance => + format!("preproc_{:0>4}_original_instance", count), + "Instance before pre-processing." + ) ? ; + profile!{ + |_profiler| + "clause count original" => add instance.clauses().len() + } + profile!{ + |_profiler| + "nl clause count original" => add { + let mut count = 0 ; + 'clause_iter: for clause in instance.clauses() { + for (_, argss) in clause.lhs_preds() { + if argss.len() > 1 { + count += 1 ; + continue 'clause_iter + } + } + } + count + } + } + profile!{ + |_profiler| + "pred count original" => add { + let mut count = 0 ; + for pred in instance.pred_indices() { + if ! instance.is_known(pred) { + count += 1 + } + } + count + } + } + profile!{ + |_profiler| + "arg count original" => add { + let mut args = 0 ; + for info in instance.preds() { + if ! instance.is_known(info.idx) { + args += info.sig.len() + } + } + args + } + } + + Ok(()) +} + +/// Registers some info for a preprocessor. +pub fn register_info( + instance: & Instance, _profiler: & Profiler, + preproc: & 'static str, red_info: & RedInfo, + count: usize, +) -> Res<()> { + preproc_dump!( + instance => + format!("preproc_{:0>4}_{}", count, preproc), + format!("Instance after running `{}`.", preproc) + ) ? ; + + profile!{ + |_profiler| format!( + "{:>10} pred red", preproc + ) => add red_info.preds + } + profile!{ + |_profiler| format!( + "{:>10} clause red", preproc + ) => add red_info.clauses_rmed + } + profile!{ + |_profiler| format!( + "{:>10} clause add", preproc + ) => add red_info.clauses_added + } + profile!{ + |_profiler| format!( + "{:>10} arg red", preproc + ) => add red_info.args_rmed + } + log! { @verb + "{}: {}", conf.emph( preproc ), red_info + } + Ok(()) +} + +/// Registers the final info, after preprocessing. +pub fn register_final_stats( + instance: & Instance, _profiler: & Profiler +) -> Res<()> { + preproc_dump!( + instance => + "preproc_0000_fixed_point", + "Instance after reaching preproc fixed-point." + ) ? ; + + profile!{ + |_profiler| + "clause count final" => add instance.clauses().len() + } + profile!{ + |_profiler| + "nl clause count final" => add { + let mut count = 0 ; + 'clause_iter: for clause in instance.clauses() { + for (_, argss) in clause.lhs_preds() { + if argss.len() > 1 { + count += 1 ; + continue 'clause_iter + } + } + } + count + } + } + + profile!{ + |_profiler| + "pred count final" => add { + let mut count = 0 ; + for pred in instance.pred_indices() { + if ! instance.is_known(pred) { + count += 1 + } + } + count + } + } + + profile!{ + |_profiler| + "arg count final" => add { + let mut args = 0 ; + for info in instance.preds() { + if ! instance.is_known(info.idx) { + args += info.sig.len() + } + } + args + } + } + + Ok(()) +} + + + + +/// Processes the information generated after a preprocessor run. +pub fn process_red_info( + instance: & Instance, _profiler: & Profiler, + preproc: & 'static str, count: & mut usize, red_info: & RedInfo +) -> Res<()> { + if red_info.non_zero() { + * count += 1 ; + register_info( + instance, _profiler, preproc, & red_info, * count + ) ? ; + conf.check_timeout() ? + } else { + log! { @verb "{}: did nothing", conf.emph(preproc) } + } + Ok(()) +} + + +/// Checks whether an instance is solved. +/// +/// Can return `Error::Unsat` if unsat, else returns a boolean indicating +/// whether the instance is solved. +pub fn check_solved( + instance: & mut PreInstance, _profiler: & Profiler +) -> Res { + let res = if instance.is_solved() { + if ! instance.is_unsat() { + profile! { + |_profiler| tick "preproc", "final simplify" + } + // Check remaining clauses, maybe some of them are unsat. + match instance.simplify_all() { + Ok(_) => (), + Err(ref e) if e.is_unsat() => instance.set_unsat(), + Err(e) => bail!(e), + } + profile! { + |_profiler| mark "preproc", "final simplify" + } + } + true + } else { + false + } ; + Ok(res) +} + + +/// Runs a technique, returns it's info. +/// +/// Returns `None` if the instance is solved. +pub fn run_preproc( + instance: & mut PreInstance, _profiler: & Profiler, + preproc: & mut Strat, count: & mut usize, +) -> Res< Option > { + profile! { + |_profiler| tick "preproc", preproc.name() + } + log! { @verb + "running {}", conf.emph( preproc.name() ) + } + let red_info = preproc.apply(instance) ? ; + profile! { + |_profiler| mark "preproc", preproc.name() + } + + process_red_info( + instance, _profiler, preproc.name(), count, & red_info + ) ? ; + + if check_solved(instance, _profiler) ? { + Ok(None) + } else { + Ok( Some(red_info) ) + } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index c65c4fe6..8c819acf 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1921,11 +1921,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { } let mut den: Int = 1.into() ; let ten = || consts::ten.clone() ; - while self.tag_opt("0") { den = den * ten() } + while self.tag_opt("0") { den *= ten() } let dec_start_pos = self.pos() ; if let Some(dec) = self.numeral() { for _ in * dec_start_pos .. * self.pos() { - den = den * ten() + den *= ten() } Some( Rat::new( num * den.clone() + dec, den ) ) } else if den != 1.into() { diff --git a/src/split.rs b/src/split.rs index 93f59e9c..47b7619c 100644 --- a/src/split.rs +++ b/src/split.rs @@ -112,7 +112,7 @@ pub fn work( ) ? ; match res { - Either::Left(candidates) => { + TeachRes::Model(candidates) => { log_info! { "sat\n\n" } let mut this_model = instance.model_of(candidates) ? ; // profile! { |_profiler| tick "waiting" } @@ -126,7 +126,9 @@ pub fn work( // real_instance.write_model(& model, & mut stdout()) ? }, - Either::Right(reason) => return Ok( Some( Either::Right(reason) ) ), + TeachRes::Unsat(reason) => return Ok( + Some( Either::Right(reason) ) + ), } } @@ -143,7 +145,7 @@ pub fn work( pub fn run_teacher( instance: Arc, model: & ConjCandidates, -) -> Res< Either > { +) -> Res< TeachRes > { let teacher_profiler = Profiler::new() ; let solve_res = ::teacher::start_class( instance, model, & teacher_profiler diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 9197ba23..5207bca1 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -6,6 +6,19 @@ use common::* ; use data::{ Data, Sample } ; + +/// Result of trying to force a sample positive/negative. +pub enum ForceRes { + /// Failure. + None, + /// Sample was classified as positive. + Pos { sample: Sample, clause: ClsIdx }, + /// Sample was classified as negative. + Neg { sample: Sample, clause: ClsIdx }, +} + + + /// Propagates examples, tries to break implication constraints. pub struct Assistant { /// Core, to communicate with the teacher. @@ -154,15 +167,15 @@ impl Assistant { & Sample { pred, ref args } ) = data.constraints[cstr].rhs() { match self.try_force(data, pred, args) ? { - None => (), - Some( Either::Left(pos_sample) ) => { - pos.push(pos_sample) ; + ForceRes::None => (), + ForceRes::Pos { sample, clause } => { + pos.push( (sample, clause) ) ; // Constraint is trivial, move on. trivial = true }, - Some( Either::Right(neg_sample) ) => { + ForceRes::Neg { sample, clause } => { rhs_false = true ; - neg.push(neg_sample) + neg.push( (sample, clause) ) }, } } @@ -174,13 +187,15 @@ impl Assistant { let mut lhs_trivial = true ; for sample in samples { match self.try_force(data, * pred, sample) ? { - None => { + ForceRes::None => { lhs_unknown += 1 ; lhs_trivial = false }, - Some( Either::Left(pos_sample) ) => pos.push(pos_sample), - Some( Either::Right(neg_sample) ) => { - neg.push(neg_sample) ; + ForceRes::Pos { sample, clause } => pos.push( + (sample, clause) + ), + ForceRes::Neg { sample, clause } => { + neg.push( (sample, clause) ) ; trivial = true ; // Constraint is trivial, move on. // break 'lhs @@ -210,15 +225,15 @@ impl Assistant { /// Checks if a sample can be forced to anything. /// - /// If it can't, return None. If it can, returns `Either` + /// If it can't, return None. If it can, returns /// - /// - `Left` of a sample which, when forced positive, will force the input - /// sample to be classified positive. - /// - `Right` of a sample which, when forced negative, will force the input - /// sample to be classified negative. + /// - `ForceRes::Pos` of a sample which, when forced positive, will force the + /// input sample to be classified positive. + /// - `ForceRes::Neg` of a sample which, when forced negative, will force the + /// input sample to be classified negative. pub fn try_force( & mut self, _data: & Data, pred: PrdIdx, vals: & VarVals - ) -> Res< Option< Either<(Sample, ClsIdx), (Sample, ClsIdx)> > > { + ) -> Res { self.solver.comment_args( format_args!("working on sample ({} {})", self.instance[pred], vals) ) ? ; @@ -250,11 +265,10 @@ impl Assistant { if sat { // msg! { debug self => " forcing positive" } return Ok( - Some( - Either::Left( - (Sample { pred, args: vals.clone() }, clause_idx) - ) - ) + ForceRes::Pos { + sample: Sample { pred, args: vals.clone() }, + clause: clause_idx, + } ) } } else { @@ -298,11 +312,10 @@ impl Assistant { if sat { // msg! { debug self => " forcing negative" } return Ok( - Some( - Either::Right( - (Sample { pred, args: vals.clone() }, clause_idx) - ) - ) + ForceRes::Neg { + sample: Sample { pred, args: vals.clone() }, + clause: clause_idx, + } ) } } else { @@ -312,9 +325,7 @@ impl Assistant { } - // msg! { debug self => " giving up" } - - Ok(None) + Ok(ForceRes::None) } } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index e7b0c64b..ec2101d9 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -8,6 +8,8 @@ //! [teach]: fn.teach.html //! (Teacher's teach function) +use std::time::Duration ; + use common::{ *, msg::*, @@ -19,6 +21,7 @@ use data::Data ; pub mod assistant ; use self::assistant::Assistant ; + /// Starts the teaching process. /// /// The partial model stores conjunction of top terms for some of the top @@ -27,7 +30,7 @@ pub fn start_class( instance: Arc, partial_model: & ConjCandidates, profiler: & Profiler -) -> Res< Either > { +) -> Res< TeachRes > { log! { @debug "starting the learning process" ; " launching solver kid..." @@ -45,7 +48,7 @@ pub fn start_class( please consider contacting the developer" } let core = teacher.unsat_core() ; - Ok( Either::Right(core) ) + Ok( TeachRes::Unsat(core) ) }, _ => Err(e) } @@ -57,29 +60,19 @@ pub fn start_class( } + /// Teaching to the learners. -pub fn teach(teacher: & mut Teacher) -> Res< - Either -> { +pub fn teach(teacher: & mut Teacher) -> Res { - log_debug!{ "spawning ice learner..." } + log_debug!{ "spawning ice learner(s)..." } if conf.ice.pure_synth { teacher.add_learner( ::learning::ice::Launcher, false ) ? ; } teacher.add_learner( ::learning::ice::Launcher, true ) ? ; - log_debug!{ "performing initial check..." } - let (cexs, cands) = teacher.initial_check() ? ; - if cexs.is_empty() { - log_debug!{ "solved by initial candidate..." } - return Ok( Either::Left(cands) ) - } - log_debug!{ "generating data from initial cex..." } - let nu_stuff = teacher.instance.cexs_to_data(& mut teacher.data, cexs ) ? ; - if ! nu_stuff { - bail! { "translation of initial cexs to data generated no new data" } + if let Some(res) = teacher.init() ? { + return Ok(res) } - teacher.run_assistant() ? ; // Index of the learner the teacher is currently working for. // @@ -126,7 +119,7 @@ pub fn teach(teacher: & mut Teacher) -> Res< // Unsat result, done. Either::Right(unsat) => { - return Ok(Either::Right(unsat)) + return Ok( TeachRes::Unsat(unsat) ) }, // Got a candidate. @@ -160,7 +153,7 @@ pub fn teach(teacher: & mut Teacher) -> Res< profile!{ teacher mark "cexs" } if cexs.is_empty() { - return Ok( Either::Left(candidates) ) + return Ok( TeachRes::Model(candidates) ) } profile!{ teacher tick "data" } @@ -172,6 +165,7 @@ pub fn teach(teacher: & mut Teacher) -> Res< profile!{ teacher mark "data" } teacher.run_assistant() ? ; + match res { Ok(true) => { // New data. @@ -194,7 +188,7 @@ pub fn teach(teacher: & mut Teacher) -> Res< }, Err(e) => { if e.is_unsat() { - return Ok( Either::Right(teacher.unsat_core()) ) + return Ok( TeachRes::Unsat(teacher.unsat_core()) ) } else { bail!(e) } @@ -243,6 +237,14 @@ pub struct Teacher<'a> { pub assistant: Option, /// Profiler. pub _profiler: & 'a Profiler, + + /// Predicates that are true in the current candidate. + tru_preds: PrdSet, + /// Predicates that are true in the current candidate. + fls_preds: PrdSet, + /// Clauses that are trivially verified in the current candidate. + clauses_to_ignore: ClsSet, + /// Partial candidate, only really contains stuff when in split mode. /// /// Used to further constrain the learner's candidates using previous @@ -254,6 +256,7 @@ pub struct Teacher<'a> { } impl<'a> Teacher<'a> { + /// Constructor. pub fn new( instance: Arc, profiler: & 'a Profiler, @@ -307,10 +310,37 @@ impl<'a> Teacher<'a> { solver, instance, data, from_learners, to_teacher: Some(to_teacher), learners, assistant, _profiler: profiler, partial_model, count: 0, + tru_preds: PrdSet::new(), fls_preds: PrdSet::new(), + clauses_to_ignore: ClsSet::new(), } ) } + + + /// Runs the initial check and registers the data. + pub fn init(& mut self) -> Res< Option > { + log_debug!{ "performing initial check..." } + let (cexs, cands) = self.initial_check() ? ; + if cexs.is_empty() { + log_debug!{ "solved by initial candidate..." } + return Ok( + Some( TeachRes::Model(cands) ) + ) + } + + log_debug!{ "generating data from initial cex..." } + let nu_stuff = self.instance.cexs_to_data(& mut self.data, cexs ) ? ; + if ! nu_stuff { + bail! { "translation of initial cexs to data generated no new data" } + } + self.run_assistant() ? ; + + Ok(None) + } + + + /// Runs the assistant (if any) on the current data. pub fn run_assistant(& mut self) -> Res<()> { if let Some(assistant) = self.assistant.as_mut() { @@ -326,6 +356,9 @@ impl<'a> Teacher<'a> { Ok(()) } + + + /// Finalizes the run. pub fn finalize(mut self) -> Res<()> { for set in self.data.pos.iter() { @@ -369,6 +402,9 @@ impl<'a> Teacher<'a> { Ok(()) } + + + /// Adds a new learner. pub fn add_learner(& mut self, learner: L, mine: bool) -> Res<()> where L: Learner + 'static { @@ -395,6 +431,9 @@ impl<'a> Teacher<'a> { } } + + + /// Broadcasts data to the learners. Returns `true` if there's no more /// learner left. /// @@ -421,6 +460,10 @@ impl<'a> Teacher<'a> { one_alive } + + + + /// Sends data to a specific learner. pub fn send(& self, learner: LrnIdx) -> Res { profile! { self tick "sending" } @@ -442,6 +485,81 @@ impl<'a> Teacher<'a> { Ok(alive) } + + /// Receives a message with a timeout. + fn receive_msg_tmo( + & mut self, drain: bool, timeout: Duration + ) -> Res { + macro_rules! all_dead { + () => ( unknown!("all learners are dead") ) ; + } + let msg = if ! drain { + match profile! { + self wrap { + self.from_learners.recv_timeout(timeout) + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + conf.check_timeout() ? ; + all_dead!() + }, + } + } else { + match profile! { + self wrap { + self.from_learners.recv() + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + all_dead!() + }, + } + } ; + Ok(msg) + } + + + + /// Receive a message. + fn receive_msg( + & mut self, drain: bool + ) -> Res<(Id, MsgKind)> { + macro_rules! all_dead { + () => ( unknown!("all learners are dead") ) ; + } + + if ! drain && self.learners.iter().all( + |& (ref channel, _, _)| channel.is_none() + ) { + all_dead!() + } + + profile!{ self tick "waiting" } + let Msg { id, msg } = if let Some(timeout) = conf.until_timeout() { + self.receive_msg_tmo(drain, timeout) ? + } else { + match profile! { + self wrap { + self.from_learners.recv() + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + all_dead!() + }, + } + } ; + + Ok((id, msg)) + } + + + /// Waits for some candidates. /// /// Returns `None` when there are no more kids. Otherwise, the second @@ -452,59 +570,9 @@ impl<'a> Teacher<'a> { pub fn get_candidates( & mut self, drain: bool ) -> Res< Either<(LrnIdx, Candidates), UnsatRes> > { - macro_rules! all_dead { - () => ( unknown!("all learners are dead") ) ; - } loop { - - if ! drain && self.learners.iter().all( - |& (ref channel, _, _)| channel.is_none() - ) { - all_dead!() - } - - profile!{ self tick "waiting" } - let Msg { id, msg } = if let Some(timeout) = conf.until_timeout() { - if ! drain { - match profile! { - self wrap { - self.from_learners.recv_timeout(timeout) - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - conf.check_timeout() ? ; - all_dead!() - }, - } - } else { - match profile! { - self wrap { - self.from_learners.recv() - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - all_dead!() - }, - } - } - } else { - match profile! { - self wrap { - self.from_learners.recv() - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - all_dead!() - }, - } - } ; + let (id, msg) = self.receive_msg(drain) ? ; match msg { @@ -655,68 +723,90 @@ impl<'a> Teacher<'a> { } - /// Looks for falsifiable clauses given some candidates. - pub fn get_cexs(& mut self, cands: & Candidates) -> Res< Cexs > { - use std::iter::Extend ; - self.count += 1 ; - - // These will be passed to clause printing to inline trivial predicates. - let (mut true_preds, mut false_preds) = ( PrdSet::new(), PrdSet::new() ) ; - // Clauses to ignore, because they are trivially true. (lhs is false or - // rhs is true). - let mut clauses_to_ignore = ClsSet::new() ; - - let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ; - - macro_rules! define_preds { - () => ({ - true_preds.clear() ; - false_preds.clear() ; - - // Retrieve true/false predicates and clauses to ignore. Define predicates. - for (pred, cand) in cands.index_iter() { - if let Some(ref term) = * cand { + /// Defines the predicates given some candidates. + /// + /// Only defines predicates that are neither trivially true or false. + pub fn define_preds(& mut self, cands: & Candidates) -> Res<()> { + for (pred, cand) in cands.index_iter() { + if let Some(ref term) = * cand { + let term = if let Some(other) = self.partial_model.get(& pred) { + term::and( vec![term.clone(), other.clone()] ) + } else { + term.clone() + } ; + match term.bool() { + None => { let term = if let Some(other) = self.partial_model.get(& pred) { term::and( vec![term.clone(), other.clone()] ) } else { term.clone() } ; - match term.bool() { - Some(true) => { - let _ = true_preds.insert(pred) ; - clauses_to_ignore.extend( - self.instance.clauses_of(pred).1 - ) - }, - Some(false) => { - let _ = false_preds.insert(pred) ; - clauses_to_ignore.extend( - self.instance.clauses_of(pred).0 - ) - }, - None => { - let term = if let Some(other) = self.partial_model.get(& pred) { - term::and( vec![term.clone(), other.clone()] ) - } else { - term.clone() - } ; - let pred = & self.instance[pred] ; - let sig: Vec<_> = pred.sig.index_iter().map( - |(var, typ)| (var, typ.get()) - ).collect() ; - self.solver.define_fun( - & pred.name, & sig, typ::bool().get(), & SmtTerm::new(& term) - ) ? - }, - } - } + let pred = & self.instance[pred] ; + let sig: Vec<_> = pred.sig.index_iter().map( + |(var, typ)| (var, typ.get()) + ).collect() ; + self.solver.define_fun( + & pred.name, & sig, typ::bool().get(), & SmtTerm::new(& term) + ) ? + }, + Some(_) => (), + } + } + } + Ok(()) + } + + + /// Registers predicates that are trivially true/false in a candidate. + /// + /// Returns the clauses that are trivially true given the candidate. + /// + /// Clears and then populates `self.clauses_to_ignore`, `self.tru_preds` and + /// `self.fls_preds`. + pub fn register_trivial(& mut self, cands: & Candidates) { + self.tru_preds.clear() ; + self.fls_preds.clear() ; + self.clauses_to_ignore.clear() ; + + for (pred, cand) in cands.index_iter() { + if let Some(ref term) = * cand { + let term = if let Some(other) = self.partial_model.get(& pred) { + term::and( vec![term.clone(), other.clone()] ) + } else { + term.clone() + } ; + + match term.bool() { + Some(true) => { + let _ = self.tru_preds.insert(pred) ; + self.clauses_to_ignore.extend( + self.instance.clauses_of(pred).1 + ) + }, + Some(false) => { + let _ = self.fls_preds.insert(pred) ; + self.clauses_to_ignore.extend( + self.instance.clauses_of(pred).0 + ) + }, + None => (), } - }) ; + } } + } + + + /// Looks for falsifiable clauses given some candidates. + pub fn get_cexs(& mut self, cands: & Candidates) -> Res< Cexs > { + self.count += 1 ; + + self.register_trivial(cands) ; + + let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ; if ! conf.teacher.restart_on_cex { self.solver.push(1) ? ; - define_preds!() + self.define_preds(cands) ? } let instance = self.instance.clone() ; @@ -724,48 +814,14 @@ impl<'a> Teacher<'a> { // True if we got some positive or negative samples. // let mut got_pos_neg_samples = false ; - macro_rules! run { - ($clause:expr, $bias:expr) => ( - if ! clauses_to_ignore.contains($clause) { - if conf.teacher.restart_on_cex { - define_preds!() - } else { - self.solver.push(1) ? - } - - let cexs = self.get_cex( - * $clause, & true_preds, & false_preds, $bias, - // got_pos_neg_samples && - conf.teacher.max_bias - ).chain_err( - || format!("while getting counterexample for clause {}", $clause) - ) ? ; - - if conf.teacher.restart_on_cex { - smt::reset(& mut self.solver) ? - } else { - self.solver.pop(1) ? - } - - if ! cexs.is_empty() { - // got_pos_neg_samples = got_pos_neg_samples || ( - // cexs.iter().any( - // |(_, bias)| ! bias.is_none() - // ) - // ) ; - let prev = map.insert(* $clause, cexs) ; - debug_assert_eq!(prev, None) - } - } - ) ; - } - log! { @verb "looking for counterexamples in positive clauses ({})...", instance.pos_clauses().len() } for clause in instance.pos_clauses() { - run!(clause, false) + self.get_cexs_of_clause( + cands, * clause, & mut map, false + ) ? } log! { @verb @@ -773,29 +829,37 @@ impl<'a> Teacher<'a> { instance.strict_neg_clauses().len() } for clause in instance.strict_neg_clauses() { - run!(clause, false) + self.get_cexs_of_clause( + cands, * clause, & mut map, false + ) ? } // got_pos_neg_samples = ! map.is_empty() ; - if map.is_empty() || ! conf.teacher.max_bias { + if map.is_empty() + || ! conf.teacher.max_bias { log! { @verb "looking for counterexamples in non-strict negative clauses ({})...", instance.non_strict_neg_clauses().len() } for clause in instance.non_strict_neg_clauses() { - run!(clause, true) + self.get_cexs_of_clause( + cands, * clause, & mut map, true + ) ? } } - if map.is_empty() || ! conf.teacher.max_bias { + if map.is_empty() + || ! conf.teacher.max_bias { log_verb! { "looking for counterexamples in implication clauses ({})...", instance.imp_clauses().len() } for clause in instance.imp_clauses() { - run!(clause, true) + self.get_cexs_of_clause( + cands, * clause, & mut map, true + ) ? } } @@ -824,17 +888,137 @@ impl<'a> Teacher<'a> { Ok(map) } + + + /// Retrieves counterexamples for a clause. + pub fn get_cexs_of_clause( + & mut self, cands: & Candidates, + clause: ClsIdx, map: & mut ClsHMap>, bias: bool, + ) -> Res<()> { + if ! self.clauses_to_ignore.contains(& clause) { + if conf.teacher.restart_on_cex { + self.define_preds(cands) ? + } else { + self.solver.push(1) ? + } + + let cexs = self.get_cex( + clause, bias, + // got_pos_neg_samples && + conf.teacher.max_bias + ).chain_err( + || format!("while getting counterexample for clause #{}", clause) + ) ? ; + + if conf.teacher.restart_on_cex { + smt::reset(& mut self.solver) ? + } else { + self.solver.pop(1) ? + } + + if ! cexs.is_empty() { + // got_pos_neg_samples = got_pos_neg_samples || ( + // cexs.iter().any( + // |(_, bias)| ! bias.is_none() + // ) + // ) ; + let prev = map.insert(clause, cexs) ; + debug_assert_eq!(prev, None) + } + } + + Ok(()) + } + + + + /// Retrieves a counterexample given some bias. + fn get_bias_cex(& mut self, clause: ClsIdx, bias: & Bias) -> Res { + profile! { self tick "cexs", "model" } + let model = self.solver.get_model() ? ; + let model = Parser.fix_model(model) ? ; + let cex = Cex::of_model( + self.instance[clause].vars(), model, + bias.is_none() && conf.teacher.partial + ) ? ; + profile! { self mark "cexs", "model" } + Ok(cex) + } + + + /// Check-sats given an optional bias. + fn check_sat_cex( + & mut self, clause: ClsIdx, bias: Option<(Actlit, Bias)> + ) -> Res< Option<(Cex, Bias)> > { + if let Some((actlit, bias)) = bias { + + log! { @debug + " checksat with bias {}", bias.to_string(& self.instance) + } + self.solver.comment( + & format!("checksat with bias {}", bias.to_string(& self.instance)) + ) ? ; + profile!{ self tick "cexs", "biased check-sat" } + let sat = self.solver.check_sat_act( + Some(& actlit) + ) ? ; + + if sat { + profile!{ self mark "cexs", "biased check-sat" } + let cex = self.get_bias_cex(clause, & bias) ? ; + self.solver.de_actlit(actlit) ? ; + Ok( + Some((cex, bias)) + ) + } else { + Ok(None) + } + + } else { + + log! { @debug " checksat" } + profile!{ self tick "cexs", "check-sat" } + let sat = self.solver.check_sat() ? ; + profile!{ self mark "cexs", "check-sat" } + + if sat { + let bias = if self.instance[clause].is_positive() { + Bias::Lft + } else if self.instance[clause].is_strict_neg() { + let ( + pred, args + ) = self.instance[clause].lhs_preds().iter().next().map( + |(pred, argss)| ( + * pred, argss.iter().next().unwrap().clone() + ) + ).unwrap() ; + Bias::NuRgt(pred, args) + } else { + Bias::Non + } ; + let cex = self.get_bias_cex(clause, & bias) ? ; + Ok( + Some((cex, bias)) + ) + + } else { + Ok(None) + } + + } + } + + + /// Checks if a clause is falsifiable and returns a model if it is. pub fn get_cex( - & mut self, - clause_idx: ClsIdx, true_preds: & PrdSet, false_preds: & PrdSet, - bias: bool, bias_only: bool + & mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool ) -> Res< Vec > { log! { @debug "working on clause #{}", clause_idx } // Macro to avoid borrowing `self.instance`. macro_rules! clause { - () => (& self.instance[clause_idx]) + () => ( & self.instance[clause_idx] ) ; } if conf.solver.log { @@ -850,7 +1034,9 @@ impl<'a> Teacher<'a> { profile!{ self tick "cexs", "prep" } clause!().declare(& mut self.solver) ? ; self.solver.assert_with( - clause!(), & (false, true_preds, false_preds, self.instance.preds()) + clause!(), & ( + false, & self.tru_preds, & self.fls_preds, self.instance.preds() + ) ) ? ; profile!{ self mark "cexs", "prep" } @@ -858,67 +1044,22 @@ impl<'a> Teacher<'a> { macro_rules! get_cex { - (@$sat:ident $bias:expr) => ({ // Works on the check-sat result. - conf.check_timeout() ? ; - if $sat { - log! { @3 "sat, getting cex" } - profile!{ self tick "cexs", "model" } - let model = self.solver.get_model() ? ; - let model = Parser.fix_model(model) ? ; - // for (var, _, val) in & model { - // println!("v_{} -> {}", var,) - // } - let cex = Cex::of_model( - clause!().vars(), model, ! $bias.is_none() && conf.teacher.partial - ) ? ; - profile!{ self mark "cexs", "model" } - - if ! $bias.is_none() { - profile! { self " biased checksat" => add 1 } - } else { - profile! { self "unbiased checksat" => add 1 } - } - - cexs.push((cex, $bias)) + () => ( // Normal check, no actlit. + if let Some(cex) = self.check_sat_cex( + clause_idx, None + ) ? { + cexs.push(cex) } - }) ; - - () => ({ // Normal check, no actlit. - log! { @debug " checksat" } - profile!{ self tick "cexs", "check-sat" } - let sat = self.solver.check_sat() ? ; - profile!{ self mark "cexs", "check-sat" } - get_cex!( - @sat if clause!().is_positive() { - Bias::Lft - } else if clause!().is_strict_neg() { - let (pred, args) = clause!().lhs_preds().iter().next().map( - |(pred, argss)| ( - * pred, argss.iter().next().unwrap().clone() - ) - ).unwrap() ; - Bias::NuRgt(pred, args) - } else { - Bias::Non - } - ) ; - }) ; + ) ; - ($actlit:expr ; $bias:expr) => ({ - log! { @debug - " checksat with bias {}", $bias.to_string(& self.instance) + ($actlit:expr ; $bias:expr) => ( + if let Some(cex) = self.check_sat_cex( + clause_idx, Some(($actlit, $bias)) + ) ? { + cexs.push(cex) } - self.solver.comment( - & format!("checksat with bias {}", $bias.to_string(& self.instance)) - ) ? ; - profile!{ self tick "cexs", "biased check-sat" } - let sat = self.solver.check_sat_act( - Some(& $actlit) - ) ? ; - profile!{ self mark "cexs", "biased check-sat" } - get_cex!(@sat $bias) ; - self.solver.de_actlit($actlit) ? - }) ; + ) ; + } get_cex!() ; diff --git a/src/term/fold.rs b/src/term/fold.rs deleted file mode 100644 index ebfad06d..00000000 --- a/src/term/fold.rs +++ /dev/null @@ -1,366 +0,0 @@ -//! Types for folding over terms. - -use common::* ; - -use std::slice::Iter ; - -/// Fold info for terms. -pub enum FoldInfo<'a, Info> { - /// Constant array. - Arr { - /// Type. - typ: & 'a Typ, - }, - - /// Operator application. - App { - /// Type. - typ: & 'a Typ, - /// Operator. - op: Op, - /// Info already processed. - lft_args: Vec, - /// Kids left to process. - rgt_args: Iter<'a, Term>, - }, - - /// Datatype constructor. - New { - /// Type of the application. - typ: & 'a Typ, - /// Name of the constructor. - name: & 'a String, - /// Kids already processed. - lft_args: Vec, - /// Kids left to process. - rgt_args: Iter<'a, Term>, - }, - - /// Datatype selector. - Sel { - /// Type. - typ: & 'a Typ, - /// Name. - name: & 'a String, - }, - - /// A function application. - Fun { - /// Type. - typ: & 'a Typ, - /// Name. - name: & 'a String, - /// Arguments already processed. - lft_args: Vec, - /// Arguments left to process. - rgt_args: Iter<'a, Term>, - } -} - - -/// Folds over a term. -/// -/// Early returns **iff** any a call to one of the input functions returns an -/// error. -/// -/// # Type parameters -/// -/// - `Info`: information extracted by the folding process -/// - `VarF`: will run on variables -/// - `CstF`: will run on constants -/// - `AppF`: will run on the result of folding on operator applications -/// - `ArrF`: will run on the result of folding on arrays -/// - `NewF`: will run on the result of folding on datatype constructors -/// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold_res( - term: & RTerm, - varf: VarF, cstf: CstF, - appf: AppF, arrf: ArrF, - newf: NewF, slcf: SlcF, - funf: FunF, -) -> Res -where -VarF: FnMut(& Typ, VarIdx) -> Res, -CstF: FnMut(& Val) -> Res, -AppF: FnMut(& Typ, Op, Vec) -> Res, -ArrF: FnMut(& Typ, Info) -> Res, -NewF: FnMut(& Typ, & String, Vec) -> Res, -SlcF: FnMut(& Typ, & String, Info) -> Res, -FunF: FnMut(& Typ, & String, Vec) -> Res, { - fold_custom_res(term, varf, cstf, appf, arrf, newf, slcf, funf) -} - - -/// Folds over a term. -/// -/// # Type parameters -/// -/// - `Info`: information extracted by the folding process -/// - `VarF`: will run on variables -/// - `CstF`: will run on constants -/// - `AppF`: will run on the result of folding on operator applications -/// - `ArrF`: will run on the result of folding on arrays -/// - `NewF`: will run on the result of folding on datatype constructors -/// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold( - term: & RTerm, - mut varf: VarF, mut cstf: CstF, - mut appf: AppF, mut arrf: ArrF, - mut newf: NewF, mut slcf: SlcF, - mut funf: FunF, -) -> Info -where -VarF: FnMut(& Typ, VarIdx) -> Info, -CstF: FnMut(& Val) -> Info, -AppF: FnMut(& Typ, Op, Vec) -> Info, -ArrF: FnMut(& Typ, Info) -> Info, -NewF: FnMut(& Typ, & String, Vec) -> Info, -SlcF: FnMut(& Typ, & String, Info) -> Info, -FunF: FnMut(& Typ, & String, Vec) -> Info, { - fold_custom_res::< Info, (), _, _, _, _, _, _, _ >( - term, - |t,v| Ok( varf(t, v) ), - |v| Ok( cstf(v) ), - |t, o, i| Ok( appf(t, o, i) ), - |t, i| Ok( arrf(t, i) ), - |t, s, i| Ok( newf(t, s, i) ), - |t, s, i| Ok( slcf(t, s, i) ), - |t, s, i| Ok( funf(t, s, i) ), - ).unwrap() - //^^^^^^~~~~ this unwrap is proved safe trivially. -} - - -/// Folds over a term. -/// -/// This function returns an error **iff** one of the function provided returns -/// one. -/// -/// # Type parameters -/// -/// - `Info`: information extracted by the folding process -/// - `E`: type of errors (for early returns) -/// - `VarF`: will run on variables -/// - `CstF`: will run on constants -/// - `AppF`: will run on the result of folding on operator applications -/// - `ArrF`: will run on the result of folding on arrays -/// - `NewF`: will run on the result of folding on datatype constructors -/// - `SlcF`: will run on the result of folding on datatype selectors -pub fn fold_custom_res< - Info, E, VarF, CstF, AppF, ArrF, NewF, SlcF, FunF ->( - term: & RTerm, - mut varf: VarF, mut cstf: CstF, - mut appf: AppF, mut arrf: ArrF, - mut newf: NewF, mut slcf: SlcF, - mut funf: FunF, -) -> Result -where -VarF: FnMut(& Typ, VarIdx) -> Result, -CstF: FnMut(& Val) -> Result, -AppF: FnMut(& Typ, Op, Vec) -> Result, -ArrF: FnMut(& Typ, Info) -> Result, -NewF: FnMut(& Typ, & String, Vec) -> Result, -SlcF: FnMut(& Typ, & String, Info) -> Result, -FunF: FnMut(& Typ, & String, Vec) -> Result, { - use term::RTerm ; - - // Stack of stuff to zip on. - let mut stack: Vec< FoldInfo > = Vec::with_capacity(11) ; - - // Term we're currently going into. - let mut curr = term ; - - 'go_down: loop { - - // Retrieve info for `curr` if it's a leaf. Keep going down otherwise. - let mut info = match * curr { - - // Leaves (there's one more case: nullary datatype constructors, handled - // below). - - RTerm::Var(ref typ, idx) => varf(typ, idx) ?, - - RTerm::Cst(ref val) => cstf(val) ?, - - // Not a leaf, we're going into their kids and updating the stack. - - RTerm::CArray { ref typ, ref term } => { - curr = term ; - stack.push( FoldInfo::Arr { typ } ) ; - continue 'go_down - }, - - RTerm::App { ref typ, op, ref args } => { - let lft_args = Vec::with_capacity( args.len() ) ; - let mut rgt_args = args.iter() ; - - curr = rgt_args.next().expect( - "illegal nullary operator application" - ) ; - - stack.push( - FoldInfo::App { typ, op, lft_args, rgt_args } - ) ; - - continue 'go_down - }, - - RTerm::DTypNew { ref typ, ref name, ref args } => { - let lft_args = Vec::with_capacity( args.len() ) ; - let mut rgt_args = args.iter() ; - - if let Some(term) = rgt_args.next() { - // Not a nullary constructor, go down. - curr = term ; - stack.push( - FoldInfo::New { typ, name, lft_args, rgt_args } - ) ; - - continue 'go_down - - } else { - // Nullary constructor, retrieve info. - newf(typ, name, vec![]) ? - } - }, - - RTerm::DTypSlc { ref typ, ref name, ref term } => { - curr = term ; - stack.push( - FoldInfo::Sel { typ, name } - ) ; - - continue 'go_down - }, - - RTerm::Fun { ref typ, ref name, ref args } => { - let mut rgt_args = args.iter() ; - let lft_args = Vec::with_capacity( args.len() ) ; - - if let Some(term) = rgt_args.next() { - curr = term ; - stack.push( - FoldInfo::Fun { typ, name, lft_args, rgt_args } - ) ; - - continue 'go_down - } else { - funf(typ, name, lft_args) ? - } - }, - - } ; - - - // We have the info, time to go up. - 'go_up: loop { - - match stack.pop() { - - // If none, we're done, the current info is for the original term. - None => return Ok(info), - - // Array, current info is for the default value. - Some( - FoldInfo::Arr { typ } - ) => { - // Update info and keep going up. - info = arrf(typ, info) ? ; - continue 'go_up - }, - - // Operator application, info is for the kid between `lft_args` and - // `rgt_args`. - Some( - FoldInfo::App { typ, op, mut lft_args, mut rgt_args } - ) => { - - lft_args.push(info) ; - - // Are we done with this application? - if let Some(term) = rgt_args.next() { - // Let's go down in `term`. - curr = term ; - // Don't forget to push back the zip info in the stack. - stack.push( - FoldInfo::App { typ, op, lft_args, rgt_args } - ) ; - // Go down. - continue 'go_down - - } else { - // No more term to go down into. Update info and go up. - info = appf(typ, op, lft_args) ? ; - continue 'go_up - } - - }, - - // Datatype constructor, info is for the kid between `lft_args` and - // `rgt_args`. - Some( - FoldInfo::New { typ, name, mut lft_args, mut rgt_args } - ) => { - - lft_args.push(info) ; - - // Are we done with this constructor? - if let Some(term) = rgt_args.next() { - // Let's go down in `term`. - curr = term ; - // Don't forget to push back the zip info in the stack. - stack.push( - FoldInfo::New { typ, name, lft_args, rgt_args } - ) ; - // Go down. - continue 'go_down - - } else { - // No more term to go down into. Update info and go up. - info = newf(typ, name, lft_args) ? ; - continue 'go_up - } - - }, - - // Datatype selector, info is for the subterm. - Some( - FoldInfo::Sel { typ, name } - ) => { - // Update info and go up. - info = slcf(typ, name, info) ? ; - continue 'go_up - }, - - // Function application, info is for the middle term. - Some( - FoldInfo::Fun { typ, name, mut lft_args, mut rgt_args } - ) => { - lft_args.push(info) ; - - // Are we done? - if let Some(term) = rgt_args.next() { - // Going down `term`. - curr = term ; - // Push back current frame. - stack.push( - FoldInfo::New { typ, name, lft_args, rgt_args } - ) ; - // Go down. - continue 'go_down - - } else { - // No more term to go down into, update info and go up. - info = funf(typ, name, lft_args) ? ; - continue 'go_up - } - - }, - - } - - } - - } -} diff --git a/src/term/mod.rs b/src/term/mod.rs index 813900a7..a8fc5a12 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -66,7 +66,6 @@ mod factory ; mod tterms ; pub mod simplify ; pub mod typ ; -mod fold ; mod zip ; mod eval ; mod leaf_iter ; @@ -730,70 +729,6 @@ impl RTerm { } } - /// Folds over a term. - /// - /// # Type parameters - /// - /// - `Info`: information extracted by the folding process - /// - `VarF`: will run on variables - /// - `CstF`: will run on constants - /// - `AppF`: will run on the result of folding on operator applications - /// - `ArrF`: will run on the result of folding on arrays - /// - `NewF`: will run on the result of folding on datatype constructors - /// - `SlcF`: will run on the result of folding on datatype selectors - /// - `SlcF`: will run on the result of folding on function applications - pub fn fold( - & self, - varf: VarF, cstf: CstF, - appf: AppF, arrf: ArrF, - newf: NewF, slcf: SlcF, - funf: FunF, - ) -> Info - where - VarF: FnMut(& Typ, VarIdx) -> Info, - CstF: FnMut(& Val) -> Info, - AppF: FnMut(& Typ, Op, Vec) -> Info, - ArrF: FnMut(& Typ, Info) -> Info, - NewF: FnMut(& Typ, & String, Vec) -> Info, - SlcF: FnMut(& Typ, & String, Info) -> Info, - FunF: FnMut(& Typ, & String, Vec) -> Info, { - fold::fold(self, varf, cstf, appf, arrf, newf, slcf, funf) - } - - - - /// Folds over a term. - /// - /// Early returns **iff** any a call to one of the input functions returns an - /// error. - /// - /// # Type parameters - /// - /// - `Info`: information extracted by the folding process - /// - `VarF`: will run on variables - /// - `CstF`: will run on constants - /// - `AppF`: will run on the result of folding on operator applications - /// - `ArrF`: will run on the result of folding on arrays - /// - `NewF`: will run on the result of folding on datatype constructors - /// - `SlcF`: will run on the result of folding on datatype selectors - pub fn fold_res( - & self, - varf: VarF, cstf: CstF, - appf: AppF, arrf: ArrF, - newf: NewF, slcf: SlcF, - funf: FunF, - ) -> Res - where - VarF: FnMut(& Typ, VarIdx) -> Res, - CstF: FnMut(& Val) -> Res, - AppF: FnMut(& Typ, Op, Vec) -> Res, - ArrF: FnMut(& Typ, Info) -> Res, - NewF: FnMut(& Typ, & String, Vec) -> Res, - SlcF: FnMut(& Typ, & String, Info) -> Res, - FunF: FnMut(& Typ, & String, Vec) -> Res, { - fold::fold_res(self, varf, cstf, appf, arrf, newf, slcf, funf) - } - /// Term evaluation. /// @@ -802,77 +737,6 @@ impl RTerm { /// - remove recursive call for constant arrays pub fn eval(& self, model: & E) -> Res { eval::eval( & factory::term( self.clone() ), model ) - // self.fold_res( - // // Variable evaluation. - // |_, v| if v < model.len() { - // Ok( model.get(v).clone() ) - // } else { - // bail!("model is too short ({})", model.len()) - // }, - - // // Constant evaluation. - // |val| Ok( val.clone() ), - - // // Operator application evaluation. - // |_, op, values| op.eval(values).chain_err( - // || format!("while evaluating operator `{}`", op) - // ), - - // // Constant array evaluation. - // |typ, default| Ok( - // val::array( typ.clone(), default ) - // ), - - // // Datatype construction. - // |typ, name, values| Ok( - // val::dtyp_new( typ.clone(), name.clone(), values ) - // ), - - // // Datatype selection. - // |typ, name, value| if ! value.is_known() { - // Ok( val::none( typ.clone() ) ) - // } else if let Some( - // (ty, constructor, values) - // ) = value.dtyp_inspect() { - // if let Some((dtyp, _)) = ty.dtyp_inspect() { - - // if let Some(selectors) = dtyp.news.get(constructor) { - - // let mut res = None ; - // for ((selector, _), value) in selectors.iter().zip( - // values.iter() - // ) { - // if selector == name { - // res = Some( value.clone() ) - // } - // } - - // if let Some(res) = res { - // Ok(res) - // } else { - // Ok( val::none( typ.clone() ) ) - // } - - // } else { - // bail!( - // "unknown constructor `{}` for datatype {}", - // conf.bad(constructor), dtyp.name - // ) - // } - - // } else { - // bail!("inconsistent type {} for value {}", ty, value) - // } - // } else { - // bail!( - // "illegal application of constructor `{}` of `{}` to `{}`", - // conf.bad(& name), typ, value - // ) - // }, - - // // Function application. - // |typ, name, args| unimplemented!(), - // ) } /// If the term's an integer constant, returns the value. @@ -960,62 +824,69 @@ impl RTerm { pub fn subst_custom>( & self, map: & Map, total: bool ) -> Option<(Term, bool)> { + use self::zip::* ; let mut changed = false ; - // println!("{}", self) ; - - let res = fold::fold_custom_res( - self, - - // Variable. - |typ, var| if let Some(term) = map.var_get(var) { - // println!(" {}, {} ({})", typ, term, term.typ()) ; - debug_assert_eq! { typ, & term.typ() } - changed = true ; - Ok(term) - } else if total { - Err(()) - } else { - Ok( - term::var( var, typ.clone() ) - ) + let res = zip( + & self.to_hcons(), + + |zip_null| match zip_null { + ZipNullary::Cst(val) => Ok( + cst( val.clone() ) + ), + ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { + debug_assert_eq! { typ, & term.typ() } + changed = true ; + Ok(term) + } else if total { + Err(()) + } else { + Ok( + term::var( var, typ.clone() ) + ) + } }, - // Constant. - |cst| Ok( - term::cst( cst.clone() ) - ), - - // Operator application. - |_, op, args| Ok( - term::app(op, args) - ), - - // Constant array. - |typ, default| Ok( - term::cst_array( typ.clone(), default ) - ), - - // Datatype constructor. - |typ, name, args| Ok( - term::dtyp_new( - typ.clone(), name.clone(), args - ) - ), + |zip_op, typ, mut acc| { + let yielded = match zip_op { + ZipOp::Op(op) => term::app(op, acc), + ZipOp::New(name) => term::dtyp_new( + typ.clone(), name.clone(), acc + ), + ZipOp::Slc(name) => if let Some(kid) = acc.pop() { + if ! acc.is_empty() { + panic!( + "illegal datatype selector application to {} arguments", + acc.len() + 1 + ) + } + term::dtyp_slc(typ.clone(), name.clone(), kid) + } else { + panic!("illegal datatype selector application to 0 arguments") + }, + ZipOp::CArray => if let Some(kid) = acc.pop() { + if ! acc.is_empty() { + panic!( + "illegal constant array application to {} arguments", + acc.len() + 1 + ) + } + term::cst_array(typ.clone(), kid) + } else { + panic!("illegal constant array application to 0 arguments") + }, + ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), + } ; - // Datatype selector. - |typ, name, term| Ok( - term::dtyp_slc( - typ.clone(), name.clone(), term - ) - ), + Ok( ZipDoTotal::Upp { yielded } ) + }, - // Function application. - |typ, name, args| Ok( - term::fun( - typ.clone(), name.clone(), args - ) - ) + |mut frame| { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: empty `rgt_args`" + ) ; + Ok( ZipDo::Trm { nu_term, frame } ) + }, ) ; if let Ok(term) = res { diff --git a/src/val/mod.rs b/src/val/mod.rs index 3ae09957..2dac19a1 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -859,7 +859,7 @@ impl RVal { use num::Signed ; if num.is_negative() ^ den.is_negative() && den.clone() * & res != num { - res = res - 1 + res -= 1 } Ok( val::int(res) ) } From e2028618b68ea50dc57caaab82c0eb35e015b0ae Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 19 Jul 2018 12:51:36 +0900 Subject: [PATCH 23/94] clippy lints + code refactoring --- Cargo.lock | 8 +- src/common/smt.rs | 2 +- src/teacher/cex_bias.rs | 373 +++++++++++++++++++++++++++++++++++++++ src/teacher/mod.rs | 376 ++-------------------------------------- 4 files changed, 391 insertions(+), 368 deletions(-) create mode 100644 src/teacher/cex_bias.rs diff --git a/Cargo.lock b/Cargo.lock index c4acc949..b60ae8cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -249,14 +249,14 @@ dependencies = [ [[package]] name = "rsmt2" version = "0.9.10" -source = "git+https://github.com/kino-mc/rsmt2#b4229205230c357f7365fea1fc305c52189be29a" +source = "git+https://github.com/kino-mc/rsmt2#a20f3ab117b609bf1402d037389d8a69bba53cbc" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -342,7 +342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)" = "" -"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" +"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" diff --git a/src/common/smt.rs b/src/common/smt.rs index 91339951..611e8ca1 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -616,7 +616,7 @@ impl<'a> IdentParser for FullParser { impl<'a> ModelParser for FullParser { fn parse_value( self, input: & 'a str, - _id: & FPVar, _params: & Vec<(FPVar, Typ)>, _out: & Typ + _id: & FPVar, _params: & [ (FPVar, Typ) ], _out: & Typ ) -> SmtRes { let mut cxt = ::parse::ParserCxt::new() ; let dummy_profiler = Profiler::new() ; diff --git a/src/teacher/cex_bias.rs b/src/teacher/cex_bias.rs new file mode 100644 index 00000000..a62f1e84 --- /dev/null +++ b/src/teacher/cex_bias.rs @@ -0,0 +1,373 @@ +//! Handles counterexample bias for the teacher. + +use common::{ + *, smt::DisjArgs +} ; +use data::Data ; +use var_to::{ + terms::{ VarTermsMap, VarTermsSet }, + vals::VarValsSet, +} ; + + +/// Solver. +type Slvr = Solver< smt::FullParser > ; + +/// +#[derive(Default, Debug)] +pub struct CexBias { + /// Activation literals for the predicate applications in the lhs of a + /// clause. + lhs_actlits: PrdHMap< VarTermsMap >, + /// LHS predicate applications that can't be forced positive. + lhs_non_pos: PrdHMap, +} + +impl CexBias { + /// Constructor. + pub fn new() -> Self { + CexBias { + lhs_actlits: PrdHMap::new(), + lhs_non_pos: PrdHMap::new(), + } + } + + + + + + /// Generates activation literals forcing a bias in the clause. + /// + /// Assumes all variables are already declared. + pub fn apply( + & mut self, _profiler: & Profiler, solver: & mut Slvr, + clause: ClsIdx, instance: & Instance, data: & Data, + bias_only: bool, + ) -> Res< Vec<(Actlit, Bias)> > { + macro_rules! clause { + () => ( instance[clause] ) ; + } + + self.lhs_actlits.clear() ; + self.lhs_non_pos.clear() ; + + self.get_lhs_actlits(solver, clause, instance, data) ? ; + + + let rhs_actlit = self.get_rhs_actlit(solver, clause, instance, data) ? ; + + + // Let's do this. + let mut actlits = vec![] ; + + + // If there's any positive samples at all, we can do something for the rhs + // (if any). + if ! self.lhs_actlits.is_empty() && clause!().rhs().is_some() && ( + // Skip if we're only generating biased check-sat and there are + // applications without any positive data. + ! bias_only || self.lhs_non_pos.is_empty() + ) { + self.bias_left( + _profiler, solver, & mut actlits, bias_only + ) ? + } + + + // If there's no rhs or it has negative samples we can do something for the + // lhs. + if clause!().rhs().is_none() || rhs_actlit.is_some() { + + if self.lhs_non_pos.is_empty() { + + self.exhaustive_bias_right( + _profiler, solver, & mut actlits, instance, & rhs_actlit + ) ? + + } else if ! bias_only || self.lhs_non_pos.iter().fold( + // Skip if only generating biased checksat and there's more than one + // application without positive data. + 0, |acc, (_, argss)| acc + argss.len() + ) == 1 { + + self.partial_bias_right( + _profiler, solver, & mut actlits, & rhs_actlit + ) ? + + } + + } + + + Ok(actlits) + } + + + /// Generates an actlit that forces the RHS of a clause to be a negative + /// sample. + /// + /// Returns `None` if either + /// - there's no RHS, + /// - there's no negative data for the RHS, + /// - the negative data for the RHS can't be activated. + fn get_rhs_actlit( + & mut self, solver: & mut Slvr, + clause: ClsIdx, instance: & Instance, data: & Data + ) -> Res< Option > { + log! { @4 "working on rhs" } + + let res = if let Some((pred, args)) = instance[clause].rhs() { + log! { @5 "-> {}", instance[pred] } + if data.neg[pred].is_empty() { + // No negative data... + log! { @5 " no negative data" } + None + } else { + log! { @5 " has negative data" } + // Negative data, generate an actlit for that. + solver.comment( + & format!( + "activating negative samples for {}", instance[pred] + ) + ) ? ; + + if let Some(actlit) = get_actlit(solver, args, & data.neg[pred]) ? { + Some(actlit) + } else { None } + } + } else { + log! { @5 "-> None" } + None + } ; + + Ok(res) + } + + + + /// + fn get_lhs_actlits( + & mut self, solver: & mut Slvr, + clause: ClsIdx, instance: & Instance, data: & Data + ) -> Res<()> { + macro_rules! clause { + () => (instance[clause]) ; + } + + self.lhs_actlits.clear() ; + self.lhs_non_pos.clear() ; + + log! { @4 "creating lhs actlits" } + + let mut cant_pos_argss = VarTermsSet::new() ; + + // Create actlits for lhs applications when possible. + for (pred, argss) in clause!().lhs_preds() { + let pred = * pred ; + log! { @5 "for {} ({})", instance[pred], argss.len() } + + if data.pos[pred].is_empty() { + log! { @5 " no positive data" } + self.lhs_non_pos.insert(pred, argss.clone()) ; + continue + } + + solver.comment( + & format!( + "activating positive samples for {} ({} applications)", + instance[pred], argss.len() + ) + ) ? ; + + let mut argss_map = VarTermsMap::with_capacity( argss.len() ) ; + + debug_assert! { cant_pos_argss.is_empty() } + + for args in argss { + log! { @6 "generating actlit for {}", args } + + if let Some(actlit) = get_actlit( + solver, args, & data.pos[pred] + ) ? { + let prev = argss_map.insert( args.clone(), actlit ) ; + debug_assert! { prev.is_none() } + } else { + let is_new = cant_pos_argss.insert( args.clone() ) ; + debug_assert! { is_new } + } + } + + if ! cant_pos_argss.is_empty() { + let prev = self.lhs_non_pos.insert( + pred, cant_pos_argss.drain().into() + ) ; + debug_assert! { prev.is_none() } + } + + if ! argss_map.is_empty() { + let prev = self.lhs_actlits.insert(pred, argss_map) ; + debug_assert! { prev.is_none() } + } + } + + Ok(()) + } + + /// Generates a bias-left actlit. + fn bias_left( + & mut self, _profiler: & Profiler, solver: & mut Slvr, + actlits: & mut Vec< (Actlit, Bias) >, bias_only: bool + ) -> Res<()> { + solver.comment("activating all lhs positive samples") ? ; + let actlit = solver.get_actlit() ? ; + for (_, map) in & self.lhs_actlits { + for (_, pos_actlit) in map { + solver.assert_act(& actlit, pos_actlit) ? + } + } + + let bias = if self.lhs_non_pos.is_empty() { + log! { @4 "total bias left" } + profile! { |_profiler| "bias: total left" => add 1 } + // If all lhs predicates have positive sample then this actlit yields a + // positive example for the rhs. + Bias::Lft + } else { + log! { @4 "partial bias left" } + profile! { |_profiler| "bias: partial left" => add 1 } + // Otherwise this will generate a constraint. + Bias::Non + } ; + + if ! bias.is_none() || ! bias_only { + actlits.push( (actlit, bias) ) + } else { + solver.de_actlit(actlit) ? + } + + Ok(()) + } + + + /// Generates a partial bias-right actlit. + fn partial_bias_right( + & mut self, _profiler: & Profiler, solver: & mut Slvr, + actlits: & mut Vec< (Actlit, Bias) >, + rhs_actlit: & Option + ) -> Res<()> { + // There's some lhs applications with no negative data. Activate + // everything we can. + let this_actlit = solver.get_actlit() ? ; + + // Force rhs false if any. + if let Some(rhs_actlit) = rhs_actlit.as_ref() { + solver.assert_act(& this_actlit, rhs_actlit) ? + } + + // Activate everything we can. + for (_, map) in & self.lhs_actlits { + for (_, actlit) in map { + solver.assert_act(& this_actlit, actlit) ? + } + } + + // Now the question is what kind of bias this corresponds to. + + let mut iter = self.lhs_non_pos.drain() ; + let (this_pred, argss) = iter.next().expect( + "next on non-empty iterator cannot yield none" + ) ; + + let mut argss_iter = argss.into_iter() ; + let this_args = argss_iter.next().expect( + "empty arguments for predicate are illegal in clauses" + ) ; + + // If we have a single predicate application with no negative samples + // we can generate an actlit for this one. + let bias = if iter.next().is_none() && argss_iter.next().is_none() { + log! { @4 "singular bias right" } + profile! { |_profiler| "bias: singular right" => add 1 } + Bias::NuRgt(this_pred, this_args.clone()) + } else { + // Otherwise we can just generate a negative constraint that's more + // constrained. + log! { @4 "partial bias right" } + profile! { |_profiler| "bias: partial right " => add 1 } + Bias::Non + } ; + + actlits.push( (this_actlit, bias) ) ; + + Ok(()) + } + + + + /// Generates exhaustive bias-right actlits. + fn exhaustive_bias_right( + & mut self, _profiler: & Profiler, solver: & mut Slvr, + actlits: & mut Vec< (Actlit, Bias) >, + instance: & Instance, rhs_actlit: & Option + ) -> Res<()> { + + log! { @4 "exhaustive bias right" } + // We can force all positive examples for all lhs applications but + // one in turn to try to generate negative examples. + for (this_pred, map) in & self.lhs_actlits { + for (this_args, _) in map { + + solver.comment( + & format!( + "actlit forcing everything but ({} {})", + instance[* this_pred], this_args + ) + ) ? ; + let this_actlit = solver.get_actlit() ? ; + + // Force rhs false if any. + if let Some(rhs_actlit) = rhs_actlit.as_ref() { + solver.assert_act(& this_actlit, rhs_actlit) ? + } + + // Activate everything else than this particular application + for (pred, map) in & self.lhs_actlits { + for (args, actlit) in map { + if pred == this_pred && args == this_args { + continue + } + solver.assert_act(& this_actlit, actlit) ? + } + } + + profile! { |_profiler| "bias: exhaustive right" => add 1 } + + actlits.push( + (this_actlit, Bias::NuRgt(* this_pred, this_args.clone())) + ) + } + } + + Ok(()) + } + +} + + +/// +fn get_actlit( + solver: & mut Slvr, args: & VarTerms, data: & VarValsSet +) -> Res< Option > { + let actlit = solver.get_actlit() ? ; + let disjunction = DisjArgs::new(args, data) ? ; + solver.assert_act(& actlit, & disjunction) ? ; + + if solver.check_sat_act( Some(& actlit) ) ? { + log! { @6 " sat, keeping actlit" } + Ok( Some(actlit) ) + } else { + log! { @6 " unsat, discarding actlit" } + solver.de_actlit(actlit) ? ; + Ok( None ) + } +} \ No newline at end of file diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index ec2101d9..67b9aa51 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -19,8 +19,11 @@ use unsat_core::UnsatRes ; use data::Data ; pub mod assistant ; +mod cex_bias ; use self::assistant::Assistant ; +pub use self::cex_bias::CexBias ; + /// Starts the teaching process. /// @@ -245,6 +248,10 @@ pub struct Teacher<'a> { /// Clauses that are trivially verified in the current candidate. clauses_to_ignore: ClsSet, + + /// Helper for cex bias. + bias: CexBias, + /// Partial candidate, only really contains stuff when in split mode. /// /// Used to further constrain the learner's candidates using previous @@ -312,6 +319,7 @@ impl<'a> Teacher<'a> { _profiler: profiler, partial_model, count: 0, tru_preds: PrdSet::new(), fls_preds: PrdSet::new(), clauses_to_ignore: ClsSet::new(), + bias: CexBias::new(), } ) } @@ -1072,8 +1080,11 @@ impl<'a> Teacher<'a> { log! { @3 "generating bias actlits" } let biased = profile! { self wrap { - self.nu_bias_applications(clause_idx, bias_only) - // self.bias_applications(clause_idx) + self.bias.apply( + & self._profiler, & mut self.solver, + clause_idx, & self.instance, & self.data, + bias_only + ) } "cexs", "bias generation" } ? ; log! { @3 "working on {} biased checks", biased.len() } @@ -1093,365 +1104,4 @@ impl<'a> Teacher<'a> { Ok(cexs) } - - - pub fn nu_bias_applications( - & mut self, clause_idx: ClsIdx, bias_only: bool, - ) -> Res> { - use common::smt::DisjArgs ; - use var_to::terms::{ VarTermsMap, VarTermsSet } ; - - let clause = & self.instance[clause_idx] ; - - // Maps lhs applications to activation literals for positive samples. - let mut lhs_actlits_of = PrdHMap::with_capacity( - clause.lhs_preds().len() - ) ; - // Predicates in lhs applications that don't have positive samples, or for - // which positive samples cannot be activated. - let mut lhs_preds_with_no_pos = PrdHMap::::new() ; - - log! { @4 "creating lhs actlits" } - - let mut cant_pos_argss = VarTermsSet::new() ; - - // Create actlits for lhs applications when possible. - for (pred, argss) in clause.lhs_preds() { - let pred = * pred ; - log! { @5 "for {} ({})", self.instance[pred], argss.len() } - - if self.data.pos[pred].is_empty() { - log! { @5 " no positive data" } - lhs_preds_with_no_pos.insert(pred, argss.clone()) ; - continue - } - - self.solver.comment( - & format!( - "activating positive samples for {} ({} applications)", - self.instance[pred], argss.len() - ) - ) ? ; - - let mut argss_map = VarTermsMap::with_capacity( argss.len() ) ; - - debug_assert! { cant_pos_argss.is_empty() } - - for args in argss { - log! { @6 "generating actlit for {}", args } - let actlit = self.solver.get_actlit() ? ; - let disjunction = DisjArgs::new( - args, & self.data.pos[pred] - ) ? ; - self.solver.assert_act(& actlit, & disjunction) ? ; - - if self.solver.check_sat_act( Some(& actlit) ) ? { - log! { @6 " sat, keeping" } - let prev = argss_map.insert(args.clone(), actlit) ; - debug_assert! { prev.is_none() } - } else { - log! { @6 " unsat, discarding" } - self.solver.de_actlit(actlit) ? ; - let is_new = cant_pos_argss.insert( args.clone() ) ; - debug_assert! { is_new } - } - } - - if ! cant_pos_argss.is_empty() { - let prev = lhs_preds_with_no_pos.insert( - pred, cant_pos_argss.drain().into() - ) ; - debug_assert! { prev.is_none() } - } - - if ! argss_map.is_empty() { - let prev = lhs_actlits_of.insert(pred, argss_map) ; - debug_assert! { prev.is_none() } - } - } - - log! { @4 "working on rhs" } - - // Create actlit for rhs application if any. - // - // - `Some(None)` if there's not rhs - // - `None` if there's one but it has no negative data - // - `Some(actlit)` otherwise. - // - // So `rhs_actlit.is_none()` => we can't force the rhs to be false. - let rhs_actlit = if let Some((pred, args)) = clause.rhs() { - log! { @5 "-> {}", self.instance[pred] } - if self.data.neg[pred].is_empty() { - // No negative data... - log! { @5 " no negative data" } - None - } else { - log! { @5 " has negative data" } - // Negative data, generate an actlit for that. - self.solver.comment( - & format!( - "activating negative samples for {}", self.instance[pred] - ) - ) ? ; - let actlit = self.solver.get_actlit() ? ; - let disjunction = DisjArgs::new( - args, & self.data.neg[pred] - ) ? ; - self.solver.assert_act(& actlit, & disjunction) ? ; - - if self.solver.check_sat_act( Some(& actlit) ) ? { - log! { @6 "sat, keeping" } - Some( Some(actlit) ) - } else { - log! { @6 "unsat, discarding" } - self.solver.de_actlit(actlit) ? ; - None - } - } - } else { - log! { @5 "-> None" } - Some(None) - } ; - - - // Let's do this. - let mut actlits = vec![] ; - - - // If there's any positive samples at all, we can do something for the rhs - // (if any). - if ! lhs_actlits_of.is_empty() && clause.rhs().is_some() && ( - // Skip if we're only generating biased check-sat and there are - // applications without any positive data. - ! bias_only || lhs_preds_with_no_pos.is_empty() - ) { - self.solver.comment("activating all lhs positive samples") ? ; - let actlit = self.solver.get_actlit() ? ; - for (_, map) in & lhs_actlits_of { - for (_, pos_actlit) in map { - self.solver.assert_act(& actlit, pos_actlit) ? - } - } - - let bias = if lhs_preds_with_no_pos.is_empty() { - log! { @4 "total bias left" } - profile! { self "bias: total left" => add 1 } - // If all lhs predicates have positive sample then this actlit yields a - // positive example for the rhs. - Bias::Lft - } else { - log! { @4 "partial bias left" } - profile! { self "bias: partial left" => add 1 } - // Otherwise this will generate a constraint. - Bias::Non - } ; - - if ! bias.is_none() || ! bias_only { - actlits.push( (actlit, bias) ) - } - } - - - // If there's no rhs or it has negative samples we can do something for the - // lhs. - if let Some(maybe_actlit) = rhs_actlit { - - if lhs_preds_with_no_pos.is_empty() { - log! { @4 "exhaustive bias right" } - // We can force all positive examples for all lhs applications but - // one in turn to try to generate negative examples. - for (this_pred, map) in & lhs_actlits_of { - for (this_args, _) in map { - - self.solver.comment( - & format!( - "actlit forcing application ({} {}) to be false", - self.instance[* this_pred], this_args - ) - ) ? ; - let this_actlit = self.solver.get_actlit() ? ; - - // Force rhs false if any. - if let Some(rhs_actlit) = maybe_actlit.as_ref() { - self.solver.assert_act(& this_actlit, rhs_actlit) ? - } - - // Activate everything else than this particular application - for (pred, map) in & lhs_actlits_of { - for (args, actlit) in map { - if pred == this_pred && args == this_args { - continue - } - self.solver.assert_act(& this_actlit, actlit) ? - } - } - - profile! { self "bias: exhaustive right" => add 1 } - - actlits.push( - (this_actlit, Bias::NuRgt(* this_pred, this_args.clone())) - ) - } - } - - } else if ! bias_only || lhs_preds_with_no_pos.iter().fold( - // Skip if only generating biased checksat and there's more than one - // application without positive data. - 0, |acc, (_, argss)| acc + argss.len() - ) == 1 { - - // There's some lhs applications with no negative data. Activate - // everything we can. - - let this_actlit = self.solver.get_actlit() ? ; - - // Force rhs false if any. - if let Some(rhs_actlit) = maybe_actlit.as_ref() { - self.solver.assert_act(& this_actlit, rhs_actlit) ? - } - - // Activate everything we can. - for (_, map) in & lhs_actlits_of { - for (_, actlit) in map { - self.solver.assert_act(& this_actlit, actlit) ? - } - } - - // Now the question is what kind of bias this corresponds to. - - let mut iter = lhs_preds_with_no_pos.into_iter() ; - let (this_pred, argss) = iter.next().expect( - "next on non-empty iterator cannot yield none" - ) ; - - let mut argss_iter = argss.into_iter() ; - let this_args = argss_iter.next().expect( - "empty arguments for predicate are illegal in clauses" - ) ; - - // If we have a single predicate application with no negative samples - // we can generate an actlit for this one. - let bias = if iter.next().is_none() && argss_iter.next().is_none() { - log! { @4 "singular bias right" } - profile! { self "bias: singular right" => add 1 } - Bias::NuRgt(this_pred, this_args.clone()) - } else { - // Otherwise we can just generate a negative constraint that's more - // constrained. - log! { @4 "partial bias right" } - profile! { self "bias: partial right " => add 1 } - Bias::Non - } ; - - actlits.push( (this_actlit, bias) ) - - } - - } - - - Ok(actlits) - } - - - - /// Biases the arguments of the predicates of a clause. - /// - /// **Assumes all active variables are already defined.** - /// - /// Guaranteed to return `(None, None)` if this feature is deactivated by - /// [`bias_cexs`][conf], or if the clause is positive / negative (either LHS - /// or RHS have no predicate applications). - /// - /// The first element of the pair it returns is an activation literal that - /// forces predicate applications in the LHS to be positive. That is, it - /// forces their arguments to correspond to one of the known positive - /// examples. - /// - /// - When no predicate appearing in the LHS have positive examples, returns - /// no actlit. - /// - When only some of them do, returns an actlit: some LHS applications - /// will not be constrained. - /// - /// The second element of the pair it returns is an activation literal that - /// forces the predicate application in the RHS to be negative. That is, it - /// forces its arguments to correspond to one of the known negative examples. - /// - /// - When the predicate appearing in the RHS has no negative examples, - /// returns no actlit. - /// - /// [conf]: ../common/config/struct.TeacherConf.html#structfield.bias_cexs - /// (bias_cexs configuration) - pub fn bias_applications( - & mut self, clause_idx: ClsIdx - ) -> Res> { - use common::smt::DisjArgs ; - - let clause = & self.instance[clause_idx] ; - - // Active and not a positive constraint? - if ! conf.teacher.bias_cexs - || clause.lhs_preds().is_empty() { - return Ok( vec![] ) - } - - let rhs_actlit = if let Some((rhs_pred, rhs_args)) = clause.rhs() { - if ! self.data.neg[rhs_pred].is_empty() { - self.solver.comment( - & format!("actlit for rhs bias ({})", self.instance[rhs_pred]) - ) ? ; - let actlit = self.solver.get_actlit() ? ; - - let disjunction = DisjArgs::new( - rhs_args, & self.data.neg[rhs_pred] - ) ? ; - self.solver.assert_act(& actlit, & disjunction) ? ; - - Some(actlit) - } else { - None - } - } else if clause.lhs_pred_apps_len() == 1 { - // Negative constraint, skipping. - return Ok( vec![] ) - } else { - None - } ; - - // Work on lhs pred apps that have some positive data. - let mut lhs_actlit = None ; - for (pred, argss) in clause.lhs_preds() { - let pred = * pred ; - if self.data.pos[pred].is_empty() { continue } - let actlit = if let Some(actlit) = lhs_actlit { - actlit - } else { - self.solver.comment("\nactlit for lhs bias") ? ; - self.solver.get_actlit() ? - } ; - - self.solver.comment( - & format!("\nbias for {}", self.instance[pred]) - ) ? ; - - for args in argss { - let disjunction = DisjArgs::new(args, & self.data.pos[pred]) ? ; - self.solver.assert_act(& actlit, & disjunction) ? - } - - lhs_actlit = Some(actlit) - } - - let mut res = vec![] ; - - if let Some(actlit) = rhs_actlit { - res.push((actlit, Bias::Non)) - } - - if let Some(actlit) = lhs_actlit { - res.push((actlit, Bias::Non)) - } - - Ok(res) - } } \ No newline at end of file From 1487f123ab711b8e5175489127cce95def364e71 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 19 Jul 2018 14:29:30 +0900 Subject: [PATCH 24/94] more clippy lint fixes --- src/learning/ice/synth/mod.rs | 38 +++++++- src/split.rs | 153 ++++++++++++++++++----------- src/teacher/mod.rs | 178 +++++++++++++++++++--------------- 3 files changed, 230 insertions(+), 139 deletions(-) diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index e9bd5822..aeec02c3 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -35,7 +35,9 @@ pub trait TheoSynth { /// Increments the synthesizer. fn increment(& mut self) ; /// Synthesizes qualifiers. - fn synth(& mut self, F, & VarVals, & mut TermVals, & Profiler) -> Res + fn synth( + & mut self, F, & VarVals, & mut TermVals, & Profiler + ) -> Res where F: FnMut(Term) -> Res ; /// Generates some [`TermVal`][term val]s for some other type. /// @@ -150,6 +152,19 @@ impl SynthSys { pub fn sample_synth( & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler ) -> Res + where F: FnMut(Term) -> Res { + + self.int_synth(sample, & mut f, _profiler) ? ; + self.real_synth(sample, & mut f, _profiler) ? ; + self.adt_synth(sample, & mut f, _profiler) ? ; + + Ok(false) + } + + /// Runs integer synthesis. + pub fn int_synth( + & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler + ) -> Res where F: FnMut(Term) -> Res { if let Some(int_synth) = self.int.as_mut() { @@ -190,6 +205,16 @@ impl SynthSys { } } + Ok(false) + } + + + /// Runs real synthesis. + pub fn real_synth( + & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler + ) -> Res + where F: FnMut(Term) -> Res { + if let Some(real_synth) = self.real.as_mut() { if ! real_synth.is_done() { self.cross_synth.clear() ; @@ -225,6 +250,16 @@ impl SynthSys { } } + Ok(false) + } + + + /// Runs real synthesis. + pub fn adt_synth( + & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler + ) -> Res + where F: FnMut(Term) -> Res { + for adt_synth in & mut self.adt { if ! adt_synth.is_done() { self.cross_synth.clear() ; @@ -262,4 +297,5 @@ impl SynthSys { Ok(false) } + } diff --git a/src/split.rs b/src/split.rs index 47b7619c..bc4ba70f 100644 --- a/src/split.rs +++ b/src/split.rs @@ -22,29 +22,6 @@ pub fn work( ) -> Res< Option< Either > > { let mut model = ConjCandidates::new() ; - macro_rules! model { - (add $model:expr) => ({ - for (pred, tterms) in $model { - if ! real_instance.is_known(pred) { - let conj = model.entry(pred).or_insert_with( - || vec![] - ) ; - match tterms.bool() { - Some(true) => continue, - Some(false) => conj.clear(), - None => (), - } - - if ! conj.iter().any( - |tts| tts == & tterms || tts.bool() == Some(false) - ) { - conj.push(tterms) - } - } - } - }) ; - } - let mut splitter = Splitter::new( real_instance.clone() ) ; @@ -84,51 +61,25 @@ pub fn work( MaybeModel::Model(this_model) ) => { log_info! { "sat by preproc\n\n" } - model! { add this_model } + add_submodel( + & real_instance, & mut model, this_model + ) ; continue 'split_loop }, } ; - if ! conf.infer { - if conf.split_step { - pause("to continue", _profiler) ; - } else { - log_info! { "Skipping learning..." } - } + match run_on(_profiler, instance, & model) ? { - continue 'split_loop - - } else if conf.split_step { - pause("to start solving", _profiler) ; - } else { - log_info! { "Starting learning..." } - } - - let res = profile!( - |_profiler| wrap { - run_teacher(instance.clone(), & model) - } "solving" - ) ? ; - - match res { - TeachRes::Model(candidates) => { - log_info! { "sat\n\n" } - let mut this_model = instance.model_of(candidates) ? ; - // profile! { |_profiler| tick "waiting" } - // while Arc::strong_count(& instance) != 1 {} - // profile! { |_profiler| mark "waiting" } - if let Some(instance) = Arc::get_mut(& mut instance) { - instance.simplify_pred_defs(& mut this_model) ? - } - model!(add this_model) ; - // let mut model = real_instance.extend_model(model.clone()) ? ; - // real_instance.write_model(& model, & mut stdout()) ? - }, + Some( Either::Left(this_model) ) => add_submodel( + & real_instance, & mut model, this_model + ), - TeachRes::Unsat(reason) => return Ok( + Some( Either::Right(reason) ) => return Ok( Some( Either::Right(reason) ) ), + + None => (), } } @@ -141,6 +92,82 @@ pub fn work( } + +/// Runs on a pre-processed instance. +pub fn run_on( + _profiler: & Profiler, mut instance: Arc, + model: & ConjCandidates +) -> Res< Option< Either > > { + if ! conf.infer { + if conf.split_step { + pause("to continue", _profiler) ; + } else { + log_info! { "Skipping learning..." } + } + + return Ok(None) + + } else if conf.split_step { + pause("to start solving", _profiler) ; + } else { + log_info! { "Starting learning..." } + } + + let res = profile!( + |_profiler| wrap { + run_teacher(instance.clone(), & model) + } "solving" + ) ? ; + + match res { + + TeachRes::Model(candidates) => { + log_info! { "sat\n\n" } + let mut this_model = instance.model_of(candidates) ? ; + if let Some(instance) = Arc::get_mut(& mut instance) { + instance.simplify_pred_defs(& mut this_model) ? + } + + Ok( + Some( Either::Left(this_model) ) + ) + }, + + TeachRes::Unsat(reason) => Ok( + Some( Either::Right(reason) ) + ), + + } +} + + + +/// Adds a model for a subinstance to a partial model. +pub fn add_submodel( + instance: & Arc, model: & mut ConjCandidates, submodel: Model +) { + for (pred, tterms) in submodel { + if ! instance.is_known(pred) { + let conj = model.entry(pred).or_insert_with( + || vec![] + ) ; + match tterms.bool() { + Some(true) => continue, + Some(false) => conj.clear(), + None => (), + } + + if ! conj.iter().any( + |tts| tts == & tterms || tts.bool() == Some(false) + ) { + conj.push(tterms) + } + } + } +} + + + /// Runs the teacher on an instance. pub fn run_teacher( instance: Arc, @@ -175,6 +202,7 @@ pub struct Splitter { /// Profiler. _profiler: Option, } + impl Splitter { /// Constructor. @@ -277,6 +305,13 @@ impl Splitter { } } + + + + + + + /// Retrieves the profiler. pub fn profiler(& mut self) -> Option { let mut res = None ; diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 67b9aa51..1f105f03 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -1,9 +1,4 @@ //! Checks candidates and sends data to learner(s). -//! -//! # TODO -//! -//! - clean [`teach`][teach] function, it's a mess and the way it's currently -//! written doesn't make sense //! //! [teach]: fn.teach.html //! (Teacher's teach function) @@ -128,81 +123,9 @@ pub fn teach(teacher: & mut Teacher) -> Res { // Got a candidate. Either::Left( ( idx, candidates) ) => { learner = Some(idx) ; - if_verb!{ - log! { conf.teacher.step, || @info - "\nCurrent candidates from {} learner:", - conf.emph( & teacher.learners[idx].1 ) - } - for _pred in teacher.instance.preds() { - if let Some(term) = candidates[_pred.idx].as_ref() { - log!( @info - "{}:", conf.emph(& _pred.name) ; - " {}", term - ) - } - } - log_verb! { "" } - } - - if conf.teacher.step { - pause( - "to look for counterexamples... (--step on)", - & teacher._profiler - ) ; - } - - profile!{ teacher tick "cexs" } - let cexs = teacher.get_cexs(& candidates) ? ; - profile!{ teacher mark "cexs" } - - if cexs.is_empty() { - return Ok( TeachRes::Model(candidates) ) + if let Some(res) = teacher.handle_candidates(candidates, idx) ? { + return Ok(res) } - - profile!{ teacher tick "data" } - profile!{ teacher tick "data", "registration" } - let res = teacher.instance.cexs_to_data( - & mut teacher.data, cexs - ) ; - profile!{ teacher mark "data", "registration" } - profile!{ teacher mark "data" } - - teacher.run_assistant() ? ; - - match res { - Ok(true) => { - // New data. - for ( - index, & mut (_, _, ref mut changed) - ) in teacher.learners.index_iter_mut() { - * changed = index != idx - } - }, - Ok(false) => if teacher.learners[idx].2 { - // Something has changed since the last candidate of this learner. - // The fact that the current candidate generated no new data is not - // a problem. - () - } else { - bail! { - "translation of cexs to data for {} generated no new data", - conf.emph( & teacher.learners[idx].1 ) - } - }, - Err(e) => { - if e.is_unsat() { - return Ok( TeachRes::Unsat(teacher.unsat_core()) ) - } else { - bail!(e) - } - }, - } - - profile!{ teacher tick "data" } - profile!{ teacher tick "data", "propagation" } - teacher.data.propagate() ? ; - profile!{ teacher mark "data", "propagation" } - profile!{ teacher mark "data" } }, } } @@ -568,6 +491,103 @@ impl<'a> Teacher<'a> { + /// Handles some candidates. + /// + /// - checks for counterexamples + /// - turns the cexs into learning data + /// - runs the assistant + /// - propagates the learning data + pub fn handle_candidates( + & mut self, candidates: Candidates, idx: LrnIdx + ) -> Res< Option > { + if_log!{ @1 + log! { conf.teacher.step, || @1 + "\nCurrent candidates from {} learner:", + conf.emph( & self.learners[idx].1 ) + } + for _pred in self.instance.preds() { + if let Some(term) = candidates[_pred.idx].as_ref() { + log!( @1 + "{}:", conf.emph(& _pred.name) ; + " {}", term + ) + } + } + log! { @1 "" } + } + + if conf.teacher.step { + pause( + "to look for counterexamples... (--step on)", + & self._profiler + ) ; + } + + profile!{ self tick "cexs" } + let cexs = self.get_cexs(& candidates) ? ; + profile!{ self mark "cexs" } + + if cexs.is_empty() { + return Ok( + Some( TeachRes::Model(candidates) ) + ) + } + + profile!{ self tick "data" } + profile!{ self tick "data", "registration" } + let res = self.instance.cexs_to_data( + & mut self.data, cexs + ) ; + profile!{ self mark "data", "registration" } + profile!{ self mark "data" } + + self.run_assistant() ? ; + + match res { + Ok(true) => { + // New data. + for ( + index, & mut (_, _, ref mut changed) + ) in self.learners.index_iter_mut() { + * changed = index != idx + } + }, + Ok(false) => if self.learners[idx].2 { + // Something has changed since the last candidate of this learner. + // The fact that the current candidate generated no new data is not + // a problem. + () + } else { + bail! { + "translation of cexs to data for {} generated no new data", + conf.emph( & self.learners[idx].1 ) + } + }, + Err(e) => { + if e.is_unsat() { + return Ok( + Some( + TeachRes::Unsat( self.unsat_core() ) + ) + ) + } else { + bail!(e) + } + }, + } + + profile!{ self tick "data" } + profile!{ self tick "data", "propagation" } + self.data.propagate() ? ; + profile!{ self mark "data", "propagation" } + profile!{ self mark "data" } + + Ok(None) + } + + + + /// Waits for some candidates. /// /// Returns `None` when there are no more kids. Otherwise, the second From 97c7ff372bc999cd16ebd99b4ae5a49d105658f7 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 19 Jul 2018 19:56:50 +0900 Subject: [PATCH 25/94] fixed all clippy lints + simplified preproc --- src/common/smt.rs | 2 + src/instance/preproc/arg_red.rs | 44 +- src/instance/preproc/bias_unroll.rs | 1085 ++++++++ src/instance/preproc/{graph.rs => cfg_red.rs} | 219 +- src/instance/preproc/mod.rs | 2190 +---------------- src/instance/preproc/one_lhs.rs | 233 ++ src/instance/preproc/one_rhs.rs | 195 ++ src/instance/preproc/unroll.rs | 290 +++ src/instance/preproc/utils.rs | 12 +- src/learning/ice/synth/int.rs | 2 +- src/learning/ice/synth/mod.rs | 14 +- src/learning/ice/synth/real.rs | 3 +- src/teacher/mod.rs | 48 +- 13 files changed, 2116 insertions(+), 2221 deletions(-) create mode 100644 src/instance/preproc/bias_unroll.rs rename src/instance/preproc/{graph.rs => cfg_red.rs} (85%) create mode 100644 src/instance/preproc/one_lhs.rs create mode 100644 src/instance/preproc/one_rhs.rs create mode 100644 src/instance/preproc/unroll.rs diff --git a/src/common/smt.rs b/src/common/smt.rs index 611e8ca1..a2b3b890 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -426,6 +426,7 @@ impl<'a> Expr2Smt<()> for DisjArgs<'a> { /// Type of values returned by the full parser. +#[derive(Debug)] pub enum FPVal { /// A normal value. Val(Val), @@ -435,6 +436,7 @@ pub enum FPVal { FunDef(String), } /// Type of variables returned by the full parser. +#[derive(Debug)] pub enum FPVar { /// A normal variable. Var(VarIdx), diff --git a/src/instance/preproc/arg_red.rs b/src/instance/preproc/arg_red.rs index 03fa0759..5d891828 100644 --- a/src/instance/preproc/arg_red.rs +++ b/src/instance/preproc/arg_red.rs @@ -1,10 +1,42 @@ //! Argument reduction. use common::* ; -use instance::Clause ; +use instance::{ + Clause, preproc::RedStrat, instance::PreInstance +} ; + + +/// Argument reduction. +/// +/// Applies the technique from +/// [Redundant argument filtering of logic programs][paper]. +/// +/// [paper]: https://link.springer.com/chapter/10.1007%2F3-540-62718-9_6 +/// (Redundant argument filtering of logic programs) +pub struct ArgRed { + inner: ArgReductor, +} + +impl RedStrat for ArgRed { + fn name(& self) -> & 'static str { "arg_reduce" } + + fn new(_: & Instance) -> Self { + ArgRed { + inner: ArgReductor::new(), + } + } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + let keep = self.inner.run(instance) ; + instance.rm_args(keep) + } +} + /// Argument reduction context. -pub struct ArgRed { +pub struct ArgReductor { /// Predicate arguments to keep. keep: PrdMap, /// Map from clauses to the variables appearing in their lhs. @@ -12,13 +44,13 @@ pub struct ArgRed { /// Map from clauses to the varibales appearing in their rhs. rhs_vars: ClsMap< Option<(PrdIdx, VarMap)> > } -impl Default for ArgRed { +impl Default for ArgReductor { fn default() -> Self { Self::new() } } -impl ArgRed { +impl ArgReductor { /// Constructor. pub fn new() -> Self { - ArgRed { + ArgReductor { keep: PrdMap::new(), lhs_vars: ClsMap::new(), rhs_vars: ClsMap::new(), @@ -115,7 +147,7 @@ impl ArgRed { & self, cvar: VarIdx, idx: ClsIdx ) -> bool { if * self.lhs_vars[idx].get(& cvar).expect( - "inconsistent ArgRed state" + "inconsistent ArgReductor state" ) > 1 { return true diff --git a/src/instance/preproc/bias_unroll.rs b/src/instance/preproc/bias_unroll.rs new file mode 100644 index 00000000..2caede0f --- /dev/null +++ b/src/instance/preproc/bias_unroll.rs @@ -0,0 +1,1085 @@ +//! Bias unrolling module. + + +use common::* ; +use instance::{ + instance::{ + PreInstance, Clause + }, + preproc::{ + RedStrat, utils::ExtractRes, utils::ExtractionCxt + }, +} ; + + +type NegDef = (Quantfed, TermSet) ; +type NegDefs = Vec ; +type PosDef = (Quantfed, TermSet) ; +type PosDefs = Vec ; +type IndexedArgs = Vec<(VarTerms, usize)> ; + + +/// Unrolls negative and positive constraints with a bias. +/// +/// # TODO +/// +/// - when creating new clauses for `p`, remember that `p` has new pos/neg +/// definitions ; then, only check clauses that mention new predicates (old +/// ones have already been tried) +pub struct BiasedUnroll { + /// Predicates appearing in positive clauses. + in_pos_clauses: PrdSet, + /// Predicates appearing in negative clauses. + in_neg_clauses: PrdSet, + /// Predicates not appearing in positive clauses. + not_in_pos_clauses: PrdSet, + /// Predicates not appearing in negative clauses. + not_in_neg_clauses: PrdSet, + /// Positive definitions retrieved from positive clauses. + pos_defs: PrdHMap< PosDefs >, + /// Negative definitions retrieved from negative clauses. + neg_defs: PrdHMap< NegDefs >, + /// + pos_new_preds: PrdHMap<(PrdSet, PrdSet)>, + neg_new_preds: PrdHMap<(PrdSet, PrdSet)>, + /// Maximum number of new clauses we can create by predicate. + max_new_clauses: usize, +} + +impl BiasedUnroll { + + /// Adds a positive definition for something. + fn add_pos_def_for( + & mut self, pred: PrdIdx, def: PosDef + ) { + let defs = self.pos_defs.entry(pred).or_insert_with(|| vec![]) ; + if defs.iter().all( |d| d != & def ) { + defs.push(def) + } + } + + /// Adds a negative definition for something. + fn add_neg_def_for( + & mut self, pred: PrdIdx, def: NegDef + ) { + let defs = self.neg_defs.entry(pred).or_insert_with(|| vec![]) ; + if defs.iter().all( |d| d != & def ) { + defs.push(def) + } + } + + + + /// Prints itself. + #[allow(dead_code)] + fn print(& self, instance: & Instance) { + println!("pos {{") ; + for pred in & self.in_pos_clauses { + println!(" + {}", instance[* pred]) + } + for pred in & self.not_in_pos_clauses { + println!(" - {}", instance[* pred]) + } + println!("}}") ; + println!("neg {{") ; + for pred in & self.in_neg_clauses { + println!(" + {}", instance[* pred]) + } + for pred in & self.not_in_neg_clauses { + println!(" - {}", instance[* pred]) + } + println!("}}") ; + + for (pred, defs) in & self.pos_defs { + if ! defs.is_empty() { + println!("+ {} {{", instance[* pred]) ; + for (qvars, terms) in defs { + let (pref, end) = if qvars.is_empty() { + ("", "") + } else { + print!(" (exists (") ; + for (qvar, typ) in qvars { + print!(" ({} {})", qvar.default_str(), typ) + } + println!(" )") ; + (" ", " )\n") + } ; + print!(" {}(and", pref) ; + for term in terms { + print!(" {}", term) + } + println!(")") ; + print!("{}", end) + } + println!("}}") + } + } + + for (pred, defs) in & self.neg_defs { + if ! defs.is_empty() { + println!("- {} {{", instance[* pred]) ; + for (qvars, terms) in defs { + let (pref, end) = if qvars.is_empty() { + ("", "") + } else { + print!(" (forall (") ; + for (qvar, typ) in qvars { + print!(" ({} {})", qvar.default_str(), typ) + } + println!(" )") ; + (" ", " )\n") + } ; + print!(" {}(not (and", pref) ; + for term in terms { + print!(" {}", term) + } + println!(") )") ; + print!("{}", end) + } + println!("}}") + } + } + + println!("pos_new_preds {{") ; + for (pred, (pos, neg)) in & self.pos_new_preds { + print!(" {} +{{", instance[* pred]) ; + for p in pos { + print!(" {}", instance[* p]) + } + print!(" }}, -{{") ; + for p in neg { + print!(" {}", instance[* p]) + } + println!(" }}") + } + println!("}}") ; + + println!("neg_new_preds {{") ; + for (pred, (pos, neg)) in & self.neg_new_preds { + print!(" {} +{{", instance[* pred]) ; + for p in pos { + print!(" {}", instance[* p]) + } + print!(" }}, -{{") ; + for p in neg { + print!(" {}", instance[* p]) + } + println!(" }}") + } + println!("}}") ; + println!() ; + println!() ; + println!() ; + } + + + /// Sets up the unroller by scanning the instance. + /// + /// Returns `true` if there's nothing to do. + fn setup<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + self.max_new_clauses = ::std::cmp::min( + 10, instance.clauses().len() / 20 + ) ; + + for (pred, _) in instance.preds().index_iter() { + if instance.is_known(pred) { continue } + let (lhs_clauses, rhs_clauses) = instance.clauses_of(pred) ; + + let mut in_pos = false ; + for clause in rhs_clauses { + if instance[* clause].lhs_pred_apps_len() == 0 { + in_pos = true ; + break + } + } + if in_pos { + let is_new = self.in_pos_clauses.insert(pred) ; + debug_assert! { is_new } + } else { + let is_new = self.not_in_pos_clauses.insert(pred) ; + debug_assert! { is_new } + } + + let mut in_neg = false ; + for clause in lhs_clauses { + if instance[* clause].rhs().is_none() + && instance[* clause].lhs_pred_apps_len() == 1 { + in_neg = true ; + break + } + } + if in_neg { + let is_new = self.in_neg_clauses.insert(pred) ; + debug_assert! { is_new } + } else { + let is_new = self.not_in_neg_clauses.insert(pred) ; + debug_assert! { is_new } + } + } + + let do_nothing = self.not_in_pos_clauses.is_empty( + ) && self.not_in_neg_clauses.is_empty() ; + + if ! do_nothing { + let (extractor, instance) = instance.extraction() ; + self.retrieve_all_pos_defs(instance, extractor) ? ; + self.retrieve_all_neg_defs(instance, extractor) ? ; + + let pos_neg = ( + self.in_pos_clauses.clone(), self.in_neg_clauses.clone() + ) ; + + for pred in & self.not_in_pos_clauses { + self.pos_new_preds.entry(* pred).or_insert_with( + || pos_neg.clone() + ) ; + } + + for pred in & self.not_in_neg_clauses { + self.neg_new_preds.entry(* pred).or_insert_with( + || pos_neg.clone() + ) ; + } + } + + Ok(do_nothing) + } + + + /// Retrieves a predicate positive definition from some clause. + /// + /// The clause has to be positive. + pub fn retrieve_pos_def( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt, + pred: PrdIdx, clause: & Clause, + ) -> Res<()> { + // Check what we're asked to do makes sense. + let args = if let Some((p, args)) = clause.rhs() { + if p != pred { + bail!( + "trying to retrieve_pos for {} in clause with different rhs", + instance[pred] + ) + } + args + } else { + bail!( + "trying to retrieve_pos for {} in non-positive clause (rhs)", + instance[pred] + ) + } ; + if ! clause.lhs_preds().is_empty() { + bail!( + "trying to retrieve_pos for {} in non-positive clause (lhs)", + instance[pred] + ) + } + + // println!( + // "from clause {}", clause.to_string_info(instance.preds()).unwrap() + // ) ; + + match extractor.terms_of_rhs_app( + true, instance, clause.vars(), + clause.lhs_terms(), clause.lhs_preds(), + (pred, args) + ) ? { + ExtractRes::Failed => bail!( + "term extraction failed for {}", instance[pred] + ), + ExtractRes::Trivial | + ExtractRes::SuccessTrue | + ExtractRes::SuccessFalse => bail!( + "unexpected result for term extraction for {} (false)", + instance[pred] + ), + ExtractRes::Success((qvars, tterms)) => { + let (terms, preds) = tterms.destroy() ; + debug_assert! { preds.is_empty() } + self.add_pos_def_for(pred, (qvars, terms)) + }, + } + + Ok(()) + } + + /// Retrieves all the partial positive definitions for some predicate. + fn retrieve_pos_defs( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt, + pred: PrdIdx, + ) -> Res { + log! { + @verb "retrieving positive partial definitions for {}", + conf.emph(& instance[pred].name) + } + let mut count = 0 ; + for clause in instance.clauses_of(pred).1 { + let clause = & instance[* clause] ; + if clause.lhs_preds().is_empty() { + self.retrieve_pos_def(instance, extractor, pred, clause) ? ; + count += 1 + } + } + Ok(count) + } + + /// Retrieves all partial positive definitions. + fn retrieve_all_pos_defs( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt + ) -> Res<()> { + log! { @4 "retrieve all positive definitions" } + for pred in self.in_pos_clauses.clone() { + log! { @4 "-> {}", instance[pred] } + let count = self.retrieve_pos_defs(instance, extractor, pred) ? ; + if count == 0 { + bail!("failed to retrieve positive definition for {}", instance[pred]) + } + } + Ok(()) + } + + + /// Retrieves a predicate negative definition from some clause. + /// + /// The clause has to be strictly negative. + pub fn retrieve_neg_def( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt, + pred: PrdIdx, clause: & Clause + ) -> Res<()> { + // Check what we're asked to do makes sense. + if clause.rhs().is_some() { + bail! ( + "trying to retrieve_neg for {} in non-negative clause (rhs)", + instance[pred] + ) + } + let args = { + let mut preds = clause.lhs_preds().iter() ; + let mut argss = if let Some((p, argss)) = preds.next() { + debug_assert_eq! { p, & pred } + if preds.next().is_some() { + bail!( + "trying to retrieve_neg for {} in a non-strict clause (preds)", + instance[pred] + ) + } + argss.iter() + } else { + bail!( + "trying to retrieve_neg for {} in empty clause", + instance[pred] + ) + } ; + + if let Some(args) = argss.next() { + debug_assert! { argss.next().is_none() } + args + } else { + bail!( + "trying to retrieve_neg for {} in a non-strict clause (argss)" + ) + } + } ; + + // println!( + // "from clause {}", clause.to_string_info(instance.preds()).unwrap() + // ) ; + + match extractor.terms_of_lhs_app( + true, instance, clause.vars(), + ( clause.lhs_terms(), clause.lhs_preds() ), + None, (pred, args) + ) ? { + ExtractRes::Failed => bail!( + "term extraction failed for {}", instance[pred] + ), + ExtractRes::Trivial | + ExtractRes::SuccessTrue | + ExtractRes::SuccessFalse => bail!( + "unexpected result for term extraction for {} (false)", + instance[pred] + ), + ExtractRes::Success((qvars, rhs, tterms)) => { + debug_assert! { rhs.is_none() } + let (terms, preds) = tterms.destroy() ; + debug_assert! { preds.is_empty() } + let mut neg_terms = TermSet::new() ; + for term in terms { + neg_terms.insert(term) ; + } + self.add_neg_def_for(pred, (qvars, neg_terms)) + }, + } + + Ok(()) + } + + /// Retrieves all the partial negative definitions for some predicate. + fn retrieve_neg_defs( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt, + pred: PrdIdx, + ) -> Res { + log! { + @verb "retrieving negative partial definitions for {}", + conf.emph(& instance[pred].name) + } + let mut count = 0 ; + for clause in instance.clauses_of(pred).0 { + let clause = & instance[* clause] ; + if clause.lhs_preds().len() == 1 + && clause.rhs().is_none() + && clause.lhs_preds().iter().next().map( + |(_, argss)| argss.len() == 1 + ).unwrap_or( false ) { + self.retrieve_neg_def(instance, extractor, pred, clause) ? ; + count += 1 + } + } + Ok(count) + } + + /// Retrieves all partial negative definitions. + fn retrieve_all_neg_defs( + & mut self, instance: & Instance, extractor: & mut ExtractionCxt, + ) -> Res<()> { + for pred in self.in_neg_clauses.clone() { + let count = self.retrieve_neg_defs(instance, extractor, pred) ? ; + if count == 0 { + bail!("failed to retrieve negative definition for {}", instance[pred]) + } + } + Ok(()) + } + + + + + /// Forces some terms in a clause by inserting a predicate application. + /// + /// `terms` is understood as a conjunction. + fn insert_terms( + & self, clause: & mut Clause, args: & VarTerms, + qvars: & Quantfed, terms: & TermSet, + ) -> Res<()> { + // Generate fresh variables for the clause if needed. + let qual_map = clause.fresh_vars_for(qvars) ; + + for term in terms { + if let Some((term, _)) = term.subst_total( & (args, & qual_map) ) { + clause.insert_term(term) ; + } else { + bail!("error during total substitution in `insert_terms`") + } + } + + Ok(()) + } + + + + + + /// Tries to generate some positive clauses for a predicate. + fn generate_pos_clauses_for<'a>( + & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> + ) -> Res { + let mut info = RedInfo::new() ; + + 'all_clauses: for rhs_clause in instance.clauses_of(pred).1.clone() { + let mut one_pred_is_new = false ; + + let mut estimation = 1 ; + + for (p, argss) in instance[rhs_clause].lhs_preds() { + if let Some(defs) = self.pos_defs.get(p) { + for _ in argss { + estimation *= defs.len() ; + if estimation > self.max_new_clauses { + continue 'all_clauses + } + } + } else { + continue 'all_clauses + } + + if let Some((pos, _)) = self.pos_new_preds.get(& pred) { + if pos.contains(p) { + one_pred_is_new = true + } + } + } + if ! one_pred_is_new { + continue 'all_clauses + } + + log! { @4 + "generating positive clause(s) for {} from {} ({})", + instance[pred], + instance[rhs_clause].to_string_info( instance.preds() ) ?, + estimation + } + + let mut nu_clauses = vec![] ; + + scoped! { + + let mut clause = instance[rhs_clause].clone() ; + + let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; + let mut map = Vec::with_capacity( lhs_preds.len() ) ; + + for (pred, argss) in & lhs_preds { + let mut arg_map = Vec::with_capacity( argss.len() ) ; + + if let Some(defs) = self.pos_defs.get(pred) { + for args in argss { + arg_map.push( (args, 0) ) + } + + map.push( (pred, defs, arg_map) ) + } else { + bail!("no definition for {} (positive, lhs)", instance[* pred]) + } + } + + macro_rules! map_inc { + () => ({ + let mut done = true ; + 'all_apps: for & mut (_, defs, ref mut argss) in & mut map { + for (_, ref mut index) in argss { + * index += 1 ; + if * index < defs.len() { + done = false ; + break 'all_apps + } else { + * index = 0 + } + } + } + done + }) + } + + let mut done = false ; + while ! done { + let mut clause = clause.clone() ; + + for (_, defs, argss) in & map { + for (args, index) in argss { + self.insert_terms( + & mut clause, args, & defs[* index].0, & defs[* index].1 + ) ? + } + } + + if let Some(trivial) = instance.is_this_clause_trivial( + & mut clause + ) ? { + if ! trivial { + nu_clauses.push(clause) + } + } else { + unimplemented!("unsat in biased unrolling") + } + + done = map_inc!() + } + + } + + for mut clause in nu_clauses { + clause.from_unrolling = true ; + if let Some(index) = instance.push_clause(clause) ? { + let (extractor, instance) = instance.extraction() ; + self.retrieve_pos_def( + instance, extractor, pred, & instance[index] + ) ? ; + info.clauses_added += 1 + } + } + } + + Ok(info) + } + + + + fn init_for_neg_clause_generation( + & self, pred: PrdIdx, instance: & mut PreInstance, lhs_clause: ClsIdx + ) -> Res { + let mut clause = instance[lhs_clause].clone() ; + + let mut this_pred = None ; + let rhs_pred = clause.unset_rhs() ; + let lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; + + let mut map = Vec::with_capacity( + lhs_preds.len() + ) ; + + for (p, argss) in lhs_preds { + let mut arg_map = Vec::with_capacity( argss.len() ) ; + + if let Some(defs) = self.pos_defs.get(& p) { + for args in argss { + arg_map.push( (args, 0) ) + } + + if p == pred { + this_pred = Some((defs, arg_map)) + } else { + map.push( (p, defs, arg_map) ) + } + } else if p == pred { + debug_assert_eq! { argss.len(), 1 } + let is_new = clause.insert_pred_app( + pred, argss.into_iter().next().unwrap() + ) ; + debug_assert! { is_new } + } else { + bail!("no definition for {} (negative, lhs)", instance[p]) + } + } + + if let Some((pred, args)) = rhs_pred { + if let Some(defs) = self.neg_defs.get(& pred) { + map.push( (pred, defs, vec![ (args, 0) ]) ) + } else { + bail!("no definition for {} (negative, rhs)", instance[pred]) + } + } + + Ok((clause, this_pred, map)) + } + + + + /// + fn generate_neg_clause_for( + & self, pred: PrdIdx, instance: & mut PreInstance, + lhs_clause: ClsIdx, clauses: & mut Vec + ) -> Res<()> { + + let (clause, mut this_pred, mut map) = self.init_for_neg_clause_generation( + pred, instance, lhs_clause + ) ? ; + + let mut active_lhs_pred_app = 0 ; + + macro_rules! map_inc { + () => ({ + let mut done = true ; + for & mut (_, defs, ref mut argss) in & mut map { + map_inc!(@argss done, defs, argss ; true) ; + if ! done { + break + } + } + + // println!("inc: {}", done) ; + + if done { + if let Some( + & mut (defs, ref mut argss) + ) = this_pred.as_mut() { + let argss_len = argss.len() ; + if active_lhs_pred_app < argss_len { + let mut index = 0 ; + map_inc!( + @argss done, defs, argss ; { + let iff = index != active_lhs_pred_app ; + index += 1 ; + iff + } + ) + } + + if done { + active_lhs_pred_app += 1 ; + done = active_lhs_pred_app >= argss_len + } + } + } + + done + }) ; + (@argss $done:ident, $defs:expr, $argss:expr ; $iff:expr) => ( + for (_, ref mut index) in $argss { + if $iff { + * index += 1 ; + if * index < $defs.len() { + $done = false ; + break + } else { + * index = 0 + } + } + } + ) + } + + let mut done = false ; + while ! done { + // println!("running: {}", active_lhs_pred_app) ; + + let mut clause = clause.clone() ; + if let Some((defs, argss)) = this_pred.as_ref() { + let mut current = 0 ; + + while current < argss.len() { + let (ref args, index) = argss[current] ; + if current == active_lhs_pred_app { + let is_new = clause.insert_pred_app(pred, args.clone()) ; + debug_assert! { is_new } + } else { + self.insert_terms( + & mut clause, args, & defs[index].0, & defs[index].1 + ) ? + } + current += 1 + } + + } + + for (_, defs, argss) in & map { + for (args, index) in argss { + self.insert_terms( + & mut clause, args, & defs[* index].0, & defs[* index].1 + ) ? + } + } + + // println!( + // "negative clause: {}", + // clause.to_string_info(instance.preds()).unwrap() + // ) ; + + if let Some(trivial) = instance.is_this_clause_trivial( + & mut clause + ) ? { + if ! trivial { + // println!("non-trivial...") ; + clauses.push(clause) + } else { + // println!("trivial...") + } + } else { + unsat!("in biased unrolling") + } + + done = map_inc!() + } + + Ok(()) + } + + + + /// Tries to generate a negative clause for a predicate. + fn generate_neg_clauses_for<'a>( + & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> + ) -> Res { + // self.print(instance) ; + + let mut info = RedInfo::new() ; + + 'all_clauses: for lhs_clause in instance.clauses_of(pred).0.clone() { + let mut one_pred_is_new = false ; + + let mut estimation = 1 ; + + if let Some((p, _)) = instance[lhs_clause].rhs() { + if let Some(defs) = self.neg_defs.get(& p) { + estimation *= defs.len() ; + if estimation > self.max_new_clauses { + continue 'all_clauses + } + } else { + continue 'all_clauses + } + if let Some((_, neg)) = self.neg_new_preds.get(& pred) { + if neg.contains(& p) { + one_pred_is_new = true + } + } + } + + for (p, argss) in instance[lhs_clause].lhs_preds() { + if * p == pred { + if argss.len() == 1 { + () + } else if let Some(defs) = self.pos_defs.get(p) { + estimation *= defs.len() ; + if estimation > self.max_new_clauses { + continue 'all_clauses + } + } else { + continue 'all_clauses + } + } else if let Some(defs) = self.pos_defs.get(p) { + for _ in argss { + estimation *= defs.len() ; + if estimation > self.max_new_clauses { + continue 'all_clauses + } + } + } else { + // log! { @6 "{} not in pos clauses", instance[* p] } + continue 'all_clauses + } + + if let Some((pos, _)) = self.neg_new_preds.get(& pred) { + if pos.contains(p) { + one_pred_is_new = true + } + } + } + + // log! { @6 "one pred new: {}", one_pred_is_new } + + if ! one_pred_is_new { + continue 'all_clauses + } + + log! { @4 + "generating negative clause(s) for {} from {}", + instance[pred], + instance[lhs_clause].to_string_info( instance.preds() ) ? + } + + let mut nu_clauses = vec![] ; + + self.generate_neg_clause_for( + pred, instance, lhs_clause, & mut nu_clauses + ) ? ; + + for mut clause in nu_clauses { + log! { @6 + "new clause: {}", + clause.to_string_info( instance.preds() ) ? + } + + clause.from_unrolling = true ; + if let Some(index) = instance.push_clause(clause) ? { + let (extractor, instance) = instance.extraction() ; + self.retrieve_neg_def( + instance, extractor, pred, & instance[index] + ) ? ; + info.clauses_added += 1 + } + } + } + + Ok(info) + } + + + + + fn neg_unroll( + & mut self, instance: & mut PreInstance + ) -> Res { + let mut info = RedInfo::new() ; + + for pred in self.not_in_neg_clauses.clone() { + log! { @4 + "trying to generate negative clauses for {}", instance[pred] + } + // self.print(instance) ; + + if self.neg_new_preds.get(& pred).map( + |(pos, neg)| ! pos.is_empty() || ! neg.is_empty() + ).unwrap_or(false) { + + let this_info = self.generate_neg_clauses_for(pred, instance) ? ; + + if this_info.non_zero() { + let was_there = self.not_in_neg_clauses.remove(& pred) ; + debug_assert! { was_there } + + let is_new = self.in_neg_clauses.insert(pred) ; + debug_assert! { is_new } + + let prev = self.neg_new_preds.remove(& pred) ; + debug_assert! { prev.is_some() } + + for (_, (_, neg)) in self.pos_new_preds.iter_mut().chain( + self.neg_new_preds.iter_mut() + ) { + let is_new = neg.insert(pred) ; + debug_assert! { is_new } + } + log! { @4 "-> success" } + + log! { @verb + "generated {} negative clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) + } + + info += this_info ; + + } else { + if let Some((pos, neg)) = self.neg_new_preds.get_mut(& pred) { + pos.clear() ; + neg.clear() + } else { + bail!("inconsistent BiasedUnroll state") + } + log! { @4 "-> failure" } + } + + } else { + log! { @4 "-> nothing new, skipping" } + } + } + + Ok(info) + } + + + + + fn pos_unroll( + & mut self, instance: & mut PreInstance + ) -> Res { + let mut info = RedInfo::new() ; + + for pred in self.not_in_pos_clauses.clone() { + log! { @4 + "trying to generate positive clauses for {}", instance[pred] + } + // self.print(instance) ; + + if self.pos_new_preds.get(& pred).map( + |(pos, _)| ! pos.is_empty() + ).unwrap_or(false) { + + let this_info = self.generate_pos_clauses_for(pred, instance) ? ; + + if this_info.non_zero() { + let was_there = self.not_in_pos_clauses.remove(& pred) ; + debug_assert! { was_there } + + let is_new = self.in_pos_clauses.insert(pred) ; + debug_assert! { is_new } + + let prev = self.pos_new_preds.remove(& pred) ; + debug_assert! { prev.is_some() } + + for (_, (pos, _)) in self.pos_new_preds.iter_mut().chain( + self.neg_new_preds.iter_mut() + ) { + let is_new = pos.insert(pred) ; + debug_assert! { is_new } + } + log! { @4 "-> success" } + + log! { @verb + "generated {} positive clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) + } + + info += this_info ; + + } else { + if let Some((pos, neg)) = self.pos_new_preds.get_mut(& pred) { + pos.clear() ; + neg.clear() + } else { + bail!("inconsistent BiasedUnroll state") + } + log! { @4 "-> failure" } + } + + } else { + log! { @4 "-> nothing new, skipping" } + } + } + + Ok(info) + } + + + +} + +impl RedStrat for BiasedUnroll { + fn name(& self) -> & 'static str { "biased_unroll" } + + fn new(_: & Instance) -> Self { + let ( + in_pos_clauses, in_neg_clauses, + not_in_pos_clauses, not_in_neg_clauses, + ) = ( + PrdSet::new(), PrdSet::new(), + PrdSet::new(), PrdSet::new(), + ) ; + + BiasedUnroll { + in_pos_clauses, in_neg_clauses, + not_in_pos_clauses, not_in_neg_clauses, + pos_defs: PrdHMap::new(), + neg_defs: PrdHMap::new(), + pos_new_preds: PrdHMap::new(), + neg_new_preds: PrdHMap::new(), + max_new_clauses: 0, + } + } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + let mut info = RedInfo::new() ; + + let nothing_to_do = self.setup(instance) ? ; + + if nothing_to_do { + return Ok(info) + } + + // println!("done with setup") ; + // self.print(instance) ; + // println!() ; + + let mut new_stuff = true ; + + while new_stuff { + new_stuff = false ; + + if conf.preproc.pos_unroll { + let this_info = self.pos_unroll(instance) ? ; + if this_info.non_zero() { + new_stuff = true ; + info += this_info + } + } + + if conf.preproc.neg_unroll { + let this_info = self.neg_unroll(instance) ? ; + if this_info.non_zero() { + new_stuff = true ; + info += this_info + } + } + + } + + info += instance.simplify_all() ? ; + + Ok(info) + } +} + + + + + +type NegGenInit<'a> = ( + Clause, + Option<(& 'a PosDefs, IndexedArgs)>, + Vec<(PrdIdx, & 'a PosDefs, IndexedArgs)> +) ; + + diff --git a/src/instance/preproc/graph.rs b/src/instance/preproc/cfg_red.rs similarity index 85% rename from src/instance/preproc/graph.rs rename to src/instance/preproc/cfg_red.rs index c1c692ec..873f6891 100644 --- a/src/instance/preproc/graph.rs +++ b/src/instance/preproc/cfg_red.rs @@ -4,7 +4,9 @@ use common::* ; use var_to::terms::VarTermsSet ; use instance::{ - preproc::utils, preproc::utils::ExtractionCxt, + preproc::{ + RedStrat, utils, utils::ExtractionCxt, + }, instance::PreInstance, } ; @@ -246,15 +248,8 @@ impl Graph { ) } - /// Predicates that directly depend on this predicate. - pub fn forward_dep(& self, pred: PrdIdx) -> & PrdMap { - & self.forward[pred] - } - /// Predicates this predicate directly depends on. - pub fn bakward_dep(& self, pred: PrdIdx) -> & PrdMap { - & self.bakward[pred] - } + /// Follows a forward map. Returns the predicates it encountered and how many /// times it encountered them. @@ -1065,4 +1060,210 @@ impl Graph { bail!("try to find heaviest predicate in empty weight map") } } +} + + + + + + + + +/// Detects cycles and keeps a minimal set of predicates to infer. +pub struct CfgRed { + /// Internal counter for log files. + cnt: usize, + /// Upper bound computed once at the beginning to avoid a progressive + /// blow-up. + upper_bound: usize, + /// Graph, factored to avoid reallocation. + graph: Graph, +} + + +impl CfgRed { + + /// Removes all clauses leading to some predicates and forces them in the + /// instance. + fn apply_pred_defs( + & self, instance: & mut PreInstance, pred_defs: Vec< (PrdIdx, Dnf) > + ) -> Res { + let mut info = RedInfo::new() ; + + for (pred, def) in pred_defs { + if instance.is_known(pred) { + continue + } + + conf.check_timeout() ? ; + info += instance.rm_rhs_clauses_of(pred) ? ; + + if_log! { @5 + let mut s = format!("{}(", instance[pred]) ; + let mut is_first = true ; + for (var, typ) in instance[pred].sig.index_iter() { + if ! is_first { + s.push_str(", ") + } else { + is_first = false + } + s.push_str( & var.default_str() ) ; + s.push_str( & format!(": {}", typ) ) ; + } + s.push_str(") = (or\n") ; + + for & (ref qvars, ref conj) in & def { + + let (suff, pref) = if qvars.is_empty() { + (None, " ") + } else { + s.push_str(" (exists (") ; + for (var, typ) in qvars { + s.push_str(" (") ; + s.push_str( & var.default_str() ) ; + s.push_str( & format!(" {})", typ) ) + } + s.push_str(" )\n") ; + (Some(" )"), " ") + } ; + + s.push_str(pref) ; + s.push_str("(and\n") ; + for term in conj.terms() { + s.push_str( & format!("{} {}\n", pref, term) ) + } + for (pred, argss) in conj.preds() { + for args in argss { + s.push_str( + & format!("{} ({} {})\n", pref, instance[* pred], args) + ) + } + } + s.push_str(pref) ; + s.push_str(")\n") ; + if let Some(suff) = suff { + s.push_str(suff) ; + s.push_str("\n") + } + } + s.push_str(")\n") ; + + log! { @4 "{}", s } + } + + log! { @4 " unfolding {}", instance[pred] } + + info += instance.force_dnf_left(pred, def) ? ; + + preproc_dump!( + instance => + format!("after_force_dnf_left_on_{}", pred), + "Instance after reaching preproc fixed-point." + ) ? ; + } + + Ok(info) + } + +} + + + + +impl RedStrat for CfgRed { + fn name(& self) -> & 'static str { "cfg_red" } + + fn new(instance: & Instance) -> Self { + CfgRed { + cnt: 0, + upper_bound: { + let clause_count = instance.clauses().len() ; + let adjusted = 50. * ( clause_count as f64 ).log(2.) ; + ::std::cmp::min( + clause_count, ( + adjusted + ).round() as usize + ) + }, + graph: Graph::new(instance), + } + } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + // use std::time::Instant ; + // use common::profiling::DurationExt ; + + let mut total_info = RedInfo::new() ; + + loop { + + let mut info = RedInfo::new() ; + + // let start = Instant::now() ; + self.graph.setup(instance) ; + // let setup_duration = Instant::now() - start ; + // println!("setup time: {}", setup_duration.to_str()) ; + + self.graph.check(& instance) ? ; + + // let start = Instant::now() ; + let mut to_keep = self.graph.break_cycles(instance) ? ; + // let breaking_duration = Instant::now() - start ; + // println!("breaking time: {}", breaking_duration.to_str()) ; + + self.graph.to_dot( + & instance, format!("{}_pred_dep_b4", self.cnt), & to_keep + ) ? ; + + let pred_defs = self.graph.inline( + instance, & mut to_keep, self.upper_bound + ) ? ; + + if pred_defs.is_empty() { break } + + info.preds += pred_defs.len() ; + + self.graph.check(& instance) ? ; + if_log! { @verb + log! { @verb "inlining {} predicates", pred_defs.len() } + for (pred, _) in & pred_defs { + log! { @verb " {}", instance[* pred] } + } + } + + if pred_defs.len() == instance.active_pred_count() { + let (is_sat, this_info) = instance.force_all_preds(pred_defs) ? ; + info += this_info ; + if ! is_sat { + unsat!("by preprocessing (all predicates resolved but unsat)") + } else { + total_info += info ; + break + } + } + + info += self.apply_pred_defs(instance, pred_defs) ? ; + + if conf.preproc.dump_pred_dep { + self.graph.setup(instance) ; + self.graph.check(& instance) ? ; + self.graph.to_dot( + & instance, format!("{}_pred_dep_reduced", self.cnt), & to_keep + ) ? ; + } + + self.cnt += 1 ; + + if info.non_zero() { + total_info += info + } else { + break + } + + } + + Ok(total_info) + } } \ No newline at end of file diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index dbbd0dbe..0a2b2c16 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -4,18 +4,24 @@ //! using single dispatch. That way, they can be combined however we want. use common::* ; -use instance::{ - *, preproc::utils::ExtractionCxt, -} ; +use instance::* ; pub mod utils ; -use self::utils::{ ExtractRes } ; -pub mod graph ; -pub mod arg_red ; + +mod one_rhs ; +mod one_lhs ; +mod cfg_red ; +mod arg_red ; +mod bias_unroll ; +mod unroll ; use self::{ - arg_red::ArgRed as ArgRedInner, - graph::Graph, + one_lhs::OneLhs, + one_rhs::OneRhs, + arg_red::ArgRed, + cfg_red::CfgRed, + bias_unroll:: BiasedUnroll, + unroll::RUnroll, } ; @@ -285,10 +291,6 @@ pub struct Reductor<'a> { simplify: Option, /// Optional predicate argument reduction pre-processor. arg_red: Option, - /// Optional simple one rhs pre-processor. - s_one_rhs: Option, - /// Optional simple one lhs pre-processor. - s_one_lhs: Option, /// Optional one rhs pre-processor. one_rhs: Option, /// Optional one lhs pre-processor. @@ -325,13 +327,6 @@ impl<'a> Reductor<'a> { let simplify = Some( Simplify::new(& instance) ) ; let arg_red = some_new! { ArgRed if arg_red } ; - let s_one_rhs = some_new! { - SimpleOneRhs if one_rhs and reduction - } ; - let s_one_lhs = some_new! { - SimpleOneLhs if one_lhs and reduction - } ; - let one_rhs = some_new! { OneRhs if one_rhs and one_rhs_full and reduction } ; @@ -351,7 +346,7 @@ impl<'a> Reductor<'a> { Ok( Reductor { instance, simplify, arg_red, - s_one_rhs, s_one_lhs, one_rhs, one_lhs, + one_rhs, one_lhs, cfg_red, biased_unroll, runroll, } ) @@ -419,9 +414,7 @@ impl<'a> Reductor<'a> { run! { arg_red } ; - let changed = run! { s_one_rhs } ; - - let changed = run! { s_one_lhs } || changed ; + let changed = false ; if changed { changed_since_cfg_red = true ; @@ -464,7 +457,8 @@ impl<'a> Reductor<'a> { let strict_neg_count = self.instance.strict_neg_clauses().fold( 0, |acc, _| acc + 1 ) ; - if strict_neg_count <= 1 && conf.preproc.runroll { + if strict_neg_count <= 1 + && conf.preproc.runroll { let info = run!( runroll info ) ; if info.non_zero() { run! { simplify } ; @@ -513,2150 +507,4 @@ impl RedStrat for Simplify { ) -> Res { instance.simplify_all() } -} - - -/// Calls [`Instance::arg_reduce`][arg_reduce]. -/// -/// [arg_reduce]: ../instance/struct.Instance.html#method.arg_reduce -/// (Instance's arg_reduce method) -pub struct ArgRed { - inner: ArgRedInner, -} - -impl RedStrat for ArgRed { - fn name(& self) -> & 'static str { "arg_reduce" } - - fn new(_: & Instance) -> Self { - ArgRed { - inner: ArgRedInner::new(), - } - } - - fn apply<'a>( - & mut self, instance:& mut PreInstance<'a> - ) -> Res { - let keep = self.inner.run(instance) ; - instance.rm_args(keep) - } -} - - - - - - -/// Works on predicates that appear in only one rhs. -/// -/// # Restrictions -/// -/// Unfolds a predicate `P` iff -/// -/// - it appears in exactly one clause rhs, say in clause `C` -/// - `P` does not appear in the lhs of `C` -/// - the lhs of `C` has no top term relating the variables `vars` appearing in -/// the application of `P` and the other variables `other_vars` of the clause -/// - the predicate applications in the lhs of `C` do not mention `other_vars` -/// -/// | This reduction does not run on: | | -/// |:---------------------------------------|:--------------------------| -/// | `(p ...) and ... => (p ...)` | `p` appears in lhs | -/// | `(v'' > v) and ... => (p v (v' + 1))` | `v''` and `v` are related | -/// | `(p' v'' v) and ... => (p v (v' + 1))` | `p'` mentions `v''` | -/// -/// | But it runs on: | `p v_0 v_1 =` | -/// |:-----------------------------------|:----------------------------| -/// | `(v > v' + 2) => (p v v')` | `(v_0 > v_1 + 2)` | -/// | `(v > 0) => (p 7 v )` | `(v_0 = 7) and (v_1 > 0)` | -/// | `(v > 0) => (p 7 v')` | `(v_0 = 7)` | -/// | `(v > 0) => (p v v )` | `(v_0 = v_1) and (v_0 > 0)` | -/// | `(v > 0) and (v <= 0) => (p 7 v')` | `false` (by check-sat) | -/// -pub struct SimpleOneRhs { - /// Predicates found to be equivalent to true, but not propagated yet. - true_preds: PrdSet, - /// Predicates found to be equivalent to false, but not propagated yet. - false_preds: PrdSet, - /// Predicates to propagate. - preds: PrdHMap< Vec >, -} - -impl RedStrat for SimpleOneRhs { - fn name(& self) -> & 'static str { "simple_one_rhs" } - - fn new(_: & Instance) -> Self { - SimpleOneRhs { - true_preds: PrdSet::with_capacity(7), - false_preds: PrdSet::with_capacity(7), - preds: PrdHMap::with_capacity(7), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - debug_assert!( self.true_preds.is_empty() ) ; - debug_assert!( self.false_preds.is_empty() ) ; - debug_assert!( self.preds.is_empty() ) ; - let mut red_info = RedInfo::new() ; - - for pred in instance.pred_indices() { - conf.check_timeout() ? ; - - if instance.clauses_of(pred).1.len() == 1 { - - let clause = * instance.clauses_of( - pred - ).1.iter().next().unwrap() ; - log_debug! { - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len() ; - "trying to unfold {}", instance[pred] - } - - let res = { - let (extraction, instance) = instance.extraction() ; - - if let Some((_this_pred, args)) = instance[clause].rhs() { - debug_assert_eq!( pred, _this_pred ) ; - - // Does `pred` appear in the lhs? - match instance[clause].lhs_preds().get(& pred) { - Some(apps) if ! apps.is_empty() => { - ExtractRes::SuccessFalse - }, - _ => extraction.terms_of_rhs_app( - false, instance, instance[clause].vars(), - instance[clause].lhs_terms(), instance[clause].lhs_preds(), - (pred, args) - ) ?, - } - } else { - bail!("inconsistent instance state") - } - } ; - - if res.is_failed() { continue } - - log! { @debug - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? - } - - log! { @verb - "unfolding {}", conf.emph(& instance[pred].name) - } - - use self::ExtractRes::* ; - match res { - Trivial => { - log! { @ 4 "=> trivial" } - red_info += instance.force_false(pred) ? - }, - SuccessTrue => { - log! { @ 4 "=> true" } - red_info += instance.force_true(pred) ? - }, - SuccessFalse => { - log! { @ 4 "=> false" } - red_info += instance.force_false(pred) ? - }, - Success( (qvars, tterms) ) => { - debug_assert! { qvars.is_empty() } ; - if_log! { @4 - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @4 - "=> ({} {})", instance[* pred], args - } - } - } - for term in tterms.terms() { - log! { @4 " => {}", term } - } - } - red_info += instance.force_pred_left( - pred, qvars, tterms - ) ? - }, - // Failed is caught before this match. - Failed => continue, - } - - debug_assert! { instance.is_known(pred) } - - red_info.preds += 1 - } - } - - Ok( red_info ) - } -} - - - - - - - -/// Tries to reduce predicates that appear as an antecedent in exactly one -/// clause. -/// -/// For a predicate `p`, if the clause in question is -/// -/// ```bash -/// lhs(v_1, ..., v_n) and p(v_1, ..., v_n) => rhs(v_1, ..., v_n) -/// ``` -/// -/// then `p` is reduced to -/// -/// ```bash -/// (not lhs(v_1, ..., v_n)) or rhs(v_1, ..., v_n) -/// ``` -/// -/// **iff** `p` is the only predicate application in the clause, `lhs` is sat -/// and `(not rhs)` is sat. -/// -/// If `lhs` or `(not rhs)` is unsat, then the clause is dropped and `p` is -/// reduced to `true` since it does not appear as an antecedent anywhere -/// anymore. -pub struct SimpleOneLhs { - /// Predicates found to be equivalent to true, but not propagated yet. - true_preds: PrdSet, - /// Predicates found to be equivalent to false, but not propagated yet. - false_preds: PrdSet, - /// Predicates to propagate. - preds: PrdHMap< Vec >, -} - -impl RedStrat for SimpleOneLhs { - fn name(& self) -> & 'static str { "simple_one_lhs" } - - fn new(_: & Instance) -> Self { - SimpleOneLhs { - true_preds: PrdSet::with_capacity(7), - false_preds: PrdSet::with_capacity(7), - preds: PrdHMap::with_capacity(7), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - debug_assert!( self.true_preds.is_empty() ) ; - debug_assert!( self.false_preds.is_empty() ) ; - debug_assert!( self.preds.is_empty() ) ; - let mut red_info = RedInfo::new() ; - - for pred in instance.pred_indices() { - conf.check_timeout() ? ; - - let clause_idx = { - let mut lhs_clauses = instance.clauses_of(pred).0.iter() ; - if let Some(clause) = lhs_clauses.next() { - if lhs_clauses.next().is_none() { - * clause - } else { - continue - } - } else { - continue - } - } ; - - log! { @debug - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len(), - } - - // Skip if the clause mentions this predicate more than once. - if let Some( argss ) = instance[clause_idx].lhs_preds().get(& pred) { - if argss.len() > 1 { continue } - } - - log! { @debug - "trying to unfold {}", instance[pred] - } - - let res = { - let (extraction, instance) = instance.extraction() ; - - let clause = & instance[clause_idx] ; - // log_debug!{ - // "from {}", clause.to_string_info( instance.preds() ) ? - // } - let args = if let Some(argss) = clause.lhs_preds().get(& pred) { - let mut iter = argss.iter() ; - let res = iter.next().unwrap() ; - // Guaranteed by the check before the `log_debug`. - debug_assert!( iter.next().is_none() ) ; - res - } else { - bail!("inconsistent instance state") - } ; - - // Is the rhs an application of `pred`? - match clause.rhs() { - Some((p, _)) if p == pred => { - ExtractRes::SuccessTrue - }, - _ => extraction.terms_of_lhs_app( - false, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - clause.rhs(), (pred, args) - ) ?, - } - } ; - - if res.is_failed() { continue } - - log! { @debug - "from {}", - instance.clauses()[clause_idx].to_string_info( instance.preds() ) ? - } - - // instance.forget_clause(clause_idx) ? ; - // red_info.clauses_rmed += 1 ; - - // log_verb!{ " instance:\n{}", instance.to_string_info( () ) ? } - - log! { @verb - "unfolding {}", conf.emph(& instance[pred].name) - } - use self::ExtractRes::* ; - match res { - SuccessTrue => { - log! { @4 "=> true" } - red_info += instance.force_true(pred) ? - }, - SuccessFalse => { - log! { @4 "=> false" } - red_info += instance.force_false(pred) ? - }, - Trivial => { - log! { @4 "=> trivial" } - red_info += instance.force_true(pred) ? - }, - Success((qualfed, pred_app, tterms)) => { - debug_assert! { qualfed.is_empty() } - if pred_app.is_none() && tterms.is_empty() { - log! { @4 "=> false" } - red_info += instance.force_false(pred) ? - } else { - if_log!{ @4 - log!{ @4" => (or" } - if let Some((pred, ref args)) = pred_app { - let mut s = format!("({}", instance[pred]) ; - for arg in args.iter() { - s = format!("{} {}", s, arg) - } - log! { @4 " {})", s } - } - log! { @4 - " (not" ; - " (and" - } - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @4 - " ({} {})", instance[* pred], args - } - } - } - for term in tterms.terms() { - log!{ @4 - " {}", term - } - } - } - red_info += instance.force_pred_right( - pred, qualfed, pred_app, tterms - ) ? - } - - instance.check("after unfolding") ? - }, - // Failed is caught before this match. - Failed => continue, - } - - debug_assert! { instance.is_known(pred) } - - red_info.preds += 1 - } - - Ok( red_info ) - } -} - - - - - - -/// Works on predicates that appear in only one rhs. -/// -/// Only works on predicates that need qualifiers to be reduced, complements -/// `SimpleOneRhs`. -/// -/// If a predicate `p` appears as a rhs in only in one clause -/// -/// ```bash -/// lhs(v_1, ..., v_n, v_n+1, ..., v_k) => p(v_1, ..., v_n) -/// ``` -/// -/// then it is forced to -/// -/// ```bash -/// p(v_1, ..., v_n) = exists (v_n+1, ..., v_k) . lhs(v_1, ..., v_k) -/// ``` -pub struct OneRhs { - /// Stores new variables discovered as we iterate over the lhs of clauses. - new_vars: VarSet, -} - -impl RedStrat for OneRhs { - fn name(& self) -> & 'static str { "one_rhs" } - - fn new(_: & Instance) -> Self { - OneRhs { - new_vars: VarSet::with_capacity(17) - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - debug_assert!( self.new_vars.is_empty() ) ; - let mut red_info = RedInfo::new() ; - - 'all_preds: for pred in instance.pred_indices() { - conf.check_timeout() ? ; - - if instance.clauses_of(pred).1.len() == 1 { - log! { @4 - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len(), - } - let clause = - * instance.clauses_of(pred).1.iter().next().unwrap() ; - - if instance.clauses_of(pred).0.contains(& clause) { - // || instance[clause].lhs_pred_apps_len() > 1 { - continue 'all_preds - } - - log!{ @5 - "trying to unfold {}", instance[pred] - } - - let res = { - let (extraction, instance) = instance.extraction() ; - - if let Some((_this_pred, args)) = instance[clause].rhs() { - debug_assert_eq!( pred, _this_pred ) ; - - // Does `pred` appear in the lhs? - match instance[clause].lhs_preds().get(& pred) { - Some(apps) if ! apps.is_empty() => { - ExtractRes::SuccessFalse - }, - _ => extraction.terms_of_rhs_app( - true, instance, instance[clause].vars(), - instance[clause].lhs_terms(), instance[clause].lhs_preds(), - (pred, args) - ) ?, - } - } else { - bail!("inconsistent instance state") - } - } ; - - if res.is_failed() { - log! { @debug " skipping" } - continue - } - - log! { @debug - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? - } - - log_verb!{ " unfolding {}", conf.emph(& instance[pred].name) } - use self::ExtractRes::* ; - match res { - Trivial => { - log_verb!(" => trivial") ; - red_info += instance.force_false(pred) ? - }, - SuccessTrue => { - log_verb!(" => true") ; - red_info += instance.force_true(pred) ? ; - }, - SuccessFalse => { - log_verb!(" => false") ; - red_info += instance.force_false(pred) ? ; - }, - Success( (qvars, tterms) ) => { - if_debug! { - log! { @debug - " {} quantified variables", qvars.len() - } - for (var, typ) in & qvars { - log! { @debug " - v_{}: {}", var, typ } - } - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @debug " => ({} {})", instance[* pred], args } - } - } - for term in tterms.terms() { - log! { @debug " => {}", term } - } - } - red_info += instance.force_pred_left( - pred, qvars, tterms - ) ? ; - - - instance.check("after unfolding") ? - }, - // Failed is caught before this match, and false is not possible for - // the function generating `res`. - Failed => unreachable!(), - } - - debug_assert! { instance.is_known(pred) } - - red_info.preds += 1 - } - } - - Ok( red_info ) - } -} - - - - - -/// Tries to reduce predicates that appear as an antecedent in exactly one -/// clause. -/// -/// For a predicate `p`, if the clause in question is -/// -/// ```bash -/// lhs(v_1, ..., v_n) and p(v_1, ..., v_n) => rhs(v_1, ..., v_n) -/// ``` -/// -/// then `p` is reduced to -/// -/// ```bash -/// (not lhs(v_1, ..., v_n)) or rhs(v_1, ..., v_n) -/// ``` -/// -/// **iff** `p` is the only predicate application in the clause, `lhs` is sat -/// and `(not rhs)` is sat. -/// -/// If `lhs` or `(not rhs)` is unsat, then the clause is dropped and `p` is -/// reduced to `true` since it does not appear as an antecedent anywhere -/// anymore. -pub struct OneLhs { - /// Predicates found to be equivalent to true, but not propagated yet. - true_preds: PrdSet, - /// Predicates found to be equivalent to false, but not propagated yet. - false_preds: PrdSet, - /// Predicates to propagate. - preds: PrdHMap< Vec >, -} - -impl RedStrat for OneLhs { - fn name(& self) -> & 'static str { "one_lhs" } - - fn new(_: & Instance) -> Self { - OneLhs { - true_preds: PrdSet::with_capacity(7), - false_preds: PrdSet::with_capacity(7), - preds: PrdHMap::with_capacity(7), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - debug_assert!( self.true_preds.is_empty() ) ; - debug_assert!( self.false_preds.is_empty() ) ; - debug_assert!( self.preds.is_empty() ) ; - let mut red_info = RedInfo::new() ; - - for pred in instance.pred_indices() { - conf.check_timeout() ? ; - - let clause_idx = { - let mut lhs_clauses = instance.clauses_of(pred).0.iter() ; - if let Some(clause) = lhs_clauses.next() { - if lhs_clauses.next().is_none() { - * clause - } else { - continue - } - } else { - continue - } - } ; - - log_debug! { - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len(), - } - - // Skip if the clause mentions this predicate more than once. - if let Some( argss ) = instance[clause_idx].lhs_preds().get(& pred) { - log_debug! { "skipping {}, more than one application", instance[pred] } - if argss.len() > 1 { continue } - } - - log_debug!{ - "trying to unfold {}", instance[pred] - } - - let res = { - let (extraction, instance) = instance.extraction() ; - - let clause = & instance[clause_idx] ; - // log_debug!{ - // "from {}", clause.to_string_info( instance.preds() ) ? - // } - let args = if let Some(argss) = clause.lhs_preds().get(& pred) { - let mut iter = argss.iter() ; - let res = iter.next().unwrap() ; - // Guaranteed by the check before the `log_debug`. - debug_assert!( iter.next().is_none() ) ; - res - } else { - bail!("inconsistent instance state") - } ; - - // Is the rhs an application of `pred`? - match clause.rhs() { - Some((p, _)) if p == pred => { - ExtractRes::SuccessTrue - }, - _ => extraction.terms_of_lhs_app( - true, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - clause.rhs(), (pred, args) - ) ?, - } - } ; - - if res.is_failed() { continue } - - log_debug!{ - "from {}", - instance.clauses()[clause_idx].to_string_info( instance.preds() ) ? - } - - // instance.forget_clause(clause_idx) ? ; - // red_info.clauses_rmed += 1 ; - - // log_verb!{ " instance:\n{}", instance.to_string_info( () ) ? } - - log_verb!{ " unfolding {}", conf.emph(& instance[pred].name) } - use self::ExtractRes::* ; - match res { - SuccessTrue => { - log! { @verb " => true" } - red_info += instance.force_true(pred) ? - }, - SuccessFalse => { - log! { @verb " => false" } - red_info += instance.force_false(pred) ? - }, - Trivial => { - log! { @verb " => trivial" } - red_info += instance.force_true(pred) ? - }, - Success((qvars, pred_app, tterms)) => { - if_log! { @debug - log! { @ debug "{} quantified variables", qvars.len() } - for (var, typ) in & qvars { - log! { @ debug "- v_{}: {}", var, typ } - } - log! { @ debug "=> (or" } - if let Some((pred, ref args)) = pred_app { - let mut s = format!("({}", instance[pred]) ; - for arg in args.iter() { - s = format!("{} {}", s, arg) - } - log! { @ debug " {})", s } - } - log! { @ debug " (not" } - log! { @ debug " (and" } - for (pred, args) in tterms.preds() { - let mut s = format!("({}", instance[* pred]) ; - for arg in args { - s = format!("{} {}", s, arg) - } - log! { @debug " {})", s } - } - for term in tterms.terms() { - log! { @debug " {}", term } - } - log! { @debug " )" } - log! { @debug " )" } - } - red_info += instance.force_pred_right( - pred, qvars, pred_app, tterms - ) ? ; - - instance.check("after unfolding") ? - }, - // Failed is caught before this match. - Failed => unreachable!(), - } - - debug_assert! { instance.is_known(pred) } - - red_info.preds += 1 ; - } - - Ok( red_info ) - } -} - - - -/// Detects cycles and keeps a minimal set of predicates to infer. -pub struct CfgRed { - /// Internal counter for log files. - cnt: usize, - /// Upper bound computed once at the beginning to avoid a progressive - /// blow-up. - upper_bound: usize, - /// Graph, factored to avoid reallocation. - graph: Graph, -} - -impl RedStrat for CfgRed { - fn name(& self) -> & 'static str { "cfg_red" } - - fn new(instance: & Instance) -> Self { - CfgRed { - cnt: 0, - upper_bound: { - let clause_count = instance.clauses().len() ; - let adjusted = 50. * ( clause_count as f64 ).log(2.) ; - ::std::cmp::min( - clause_count, ( - adjusted - ).round() as usize - ) - }, - graph: Graph::new(instance), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - // use std::time::Instant ; - // use common::profiling::DurationExt ; - - let mut total_info = RedInfo::new() ; - - loop { - - let mut info = RedInfo::new() ; - - // let start = Instant::now() ; - self.graph.setup(instance) ; - // let setup_duration = Instant::now() - start ; - // println!("setup time: {}", setup_duration.to_str()) ; - - self.graph.check(& instance) ? ; - - // let start = Instant::now() ; - let mut to_keep = self.graph.break_cycles(instance) ? ; - // let breaking_duration = Instant::now() - start ; - // println!("breaking time: {}", breaking_duration.to_str()) ; - - self.graph.to_dot( - & instance, format!("{}_pred_dep_b4", self.cnt), & to_keep - ) ? ; - - let pred_defs = self.graph.inline( - instance, & mut to_keep, self.upper_bound - ) ? ; - - if pred_defs.is_empty() { break } - - info.preds += pred_defs.len() ; - - self.graph.check(& instance) ? ; - if_log! { @verb - log! { @verb "inlining {} predicates", pred_defs.len() } - for (pred, _) in & pred_defs { - log! { @verb " {}", instance[* pred] } - } - } - - if pred_defs.len() == instance.active_pred_count() { - let (is_sat, this_info) = instance.force_all_preds(pred_defs) ? ; - info += this_info ; - if ! is_sat { - unsat!("by preprocessing (all predicates resolved but unsat)") - } else { - total_info += info ; - break - } - } - - // Remove all clauses leading to the predicates we just inlined. - for (pred, def) in pred_defs { - if instance.is_known(pred) { - continue - } - - conf.check_timeout() ? ; - info += instance.rm_rhs_clauses_of(pred) ? ; - - if_log! { @4 - let mut s = format!("{}(", instance[pred]) ; - let mut is_first = true ; - for (var, typ) in instance[pred].sig.index_iter() { - if ! is_first { - s.push_str(", ") - } else { - is_first = false - } - s.push_str( & var.default_str() ) ; - s.push_str(& format!(": {}", typ)) ; - } - log! { @4 "{}) = (or", s } - for & (ref qvars, ref conj) in & def { - let (suff, pref) = if qvars.is_empty() { (None, " ") } else { - let mut s = " (exists".to_string() ; - for (var, typ) in qvars { - s.push_str(" (") ; - s.push_str( & var.default_str() ) ; - s.push_str( & format!(" {})", typ) ) - } - log! { @4 "{}", s } - (Some(" )"), " ") - } ; - log! { @4 "{}(and", pref } - for term in conj.terms() { - log! { @4 "{} {}", pref, term } - } - for (pred, argss) in conj.preds() { - for args in argss { - log! { @4 "{} ({} {})", pref, instance[* pred], args } - } - } - log! { @4 "{})", pref } - if let Some(suff) = suff { - log! { @4 "{}", suff } - } - } - log! { @4 ")" } - } - - log! { @4 " unfolding {}", instance[pred] } - - info += instance.force_dnf_left(pred, def) ? ; - - preproc_dump!( - instance => - format!("after_force_dnf_left_on_{}", pred), - "Instance after reaching preproc fixed-point." - ) ? ; - } - - if conf.preproc.dump_pred_dep { - self.graph.setup(instance) ; - self.graph.check(& instance) ? ; - self.graph.to_dot( - & instance, format!("{}_pred_dep_reduced", self.cnt), & to_keep - ) ? ; - } - - self.cnt += 1 ; - - if info.non_zero() { - total_info += info - } else { - break - } - - } - - Ok(total_info) - } -} - - -/// Unrolls negative and positive constraints with a bias. -/// -/// # TODO -/// -/// - when creating new clauses for `p`, remember that `p` has new pos/neg -/// definitions ; then, only check clauses that mention new predicates (old -/// ones have already been tried) -pub struct BiasedUnroll { - /// Predicates appearing in positive clauses. - in_pos_clauses: PrdSet, - /// Predicates appearing in negative clauses. - in_neg_clauses: PrdSet, - /// Predicates not appearing in positive clauses. - not_in_pos_clauses: PrdSet, - /// Predicates not appearing in negative clauses. - not_in_neg_clauses: PrdSet, - /// Positive definitions retrieved from positive clauses. - pos_defs: PrdHMap< Vec<(Quantfed, TermSet)> >, - /// Negative definitions retrieved from negative clauses. - neg_defs: PrdHMap< Vec<(Quantfed, TermSet)> >, - /// - pos_new_preds: PrdHMap<(PrdSet, PrdSet)>, - neg_new_preds: PrdHMap<(PrdSet, PrdSet)>, - /// Maximum number of new clauses we can create by predicate. - max_new_clauses: usize, -} - -impl BiasedUnroll { - - /// Adds a positive definition for something. - fn add_pos_def_for( - & mut self, pred: PrdIdx, def: (Quantfed, TermSet) - ) { - let defs = self.pos_defs.entry(pred).or_insert_with(|| vec![]) ; - if defs.iter().all( |d| d != & def ) { - defs.push(def) - } - } - - /// Adds a negative definition for something. - fn add_neg_def_for( - & mut self, pred: PrdIdx, def: (Quantfed, TermSet) - ) { - let defs = self.neg_defs.entry(pred).or_insert_with(|| vec![]) ; - if defs.iter().all( |d| d != & def ) { - defs.push(def) - } - } - - - - /// Prints itself. - #[allow(dead_code)] - fn print(& self, instance: & Instance) { - println!("pos {{") ; - for pred in & self.in_pos_clauses { - println!(" + {}", instance[* pred]) - } - for pred in & self.not_in_pos_clauses { - println!(" - {}", instance[* pred]) - } - println!("}}") ; - println!("neg {{") ; - for pred in & self.in_neg_clauses { - println!(" + {}", instance[* pred]) - } - for pred in & self.not_in_neg_clauses { - println!(" - {}", instance[* pred]) - } - println!("}}") ; - - for (pred, defs) in & self.pos_defs { - if ! defs.is_empty() { - println!("+ {} {{", instance[* pred]) ; - for (qvars, terms) in defs { - let (pref, end) = if qvars.is_empty() { - ("", "") - } else { - print!(" (exists (") ; - for (qvar, typ) in qvars { - print!(" ({} {})", qvar.default_str(), typ) - } - println!(" )") ; - (" ", " )\n") - } ; - print!(" {}(and", pref) ; - for term in terms { - print!(" {}", term) - } - println!(")") ; - print!("{}", end) - } - println!("}}") - } - } - - for (pred, defs) in & self.neg_defs { - if ! defs.is_empty() { - println!("- {} {{", instance[* pred]) ; - for (qvars, terms) in defs { - let (pref, end) = if qvars.is_empty() { - ("", "") - } else { - print!(" (forall (") ; - for (qvar, typ) in qvars { - print!(" ({} {})", qvar.default_str(), typ) - } - println!(" )") ; - (" ", " )\n") - } ; - print!(" {}(not (and", pref) ; - for term in terms { - print!(" {}", term) - } - println!(") )") ; - print!("{}", end) - } - println!("}}") - } - } - - println!("pos_new_preds {{") ; - for (pred, (pos, neg)) in & self.pos_new_preds { - print!(" {} +{{", instance[* pred]) ; - for p in pos { - print!(" {}", instance[* p]) - } - print!(" }}, -{{") ; - for p in neg { - print!(" {}", instance[* p]) - } - println!(" }}") - } - println!("}}") ; - - println!("neg_new_preds {{") ; - for (pred, (pos, neg)) in & self.neg_new_preds { - print!(" {} +{{", instance[* pred]) ; - for p in pos { - print!(" {}", instance[* p]) - } - print!(" }}, -{{") ; - for p in neg { - print!(" {}", instance[* p]) - } - println!(" }}") - } - println!("}}") ; - println!() ; - println!() ; - println!() ; - } - - - /// Sets up the unroller by scanning the instance. - /// - /// Returns `true` if there's nothing to do. - fn setup<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - self.max_new_clauses = ::std::cmp::min( - 10, instance.clauses().len() / 20 - ) ; - - for (pred, _) in instance.preds().index_iter() { - if instance.is_known(pred) { continue } - let (lhs_clauses, rhs_clauses) = instance.clauses_of(pred) ; - - let mut in_pos = false ; - for clause in rhs_clauses { - if instance[* clause].lhs_pred_apps_len() == 0 { - in_pos = true ; - break - } - } - if in_pos { - let is_new = self.in_pos_clauses.insert(pred) ; - debug_assert! { is_new } - } else { - let is_new = self.not_in_pos_clauses.insert(pred) ; - debug_assert! { is_new } - } - - let mut in_neg = false ; - for clause in lhs_clauses { - if instance[* clause].rhs().is_none() - && instance[* clause].lhs_pred_apps_len() == 1 { - in_neg = true ; - break - } - } - if in_neg { - let is_new = self.in_neg_clauses.insert(pred) ; - debug_assert! { is_new } - } else { - let is_new = self.not_in_neg_clauses.insert(pred) ; - debug_assert! { is_new } - } - } - - let do_nothing = self.not_in_pos_clauses.is_empty( - ) && self.not_in_neg_clauses.is_empty() ; - - if ! do_nothing { - let (extractor, instance) = instance.extraction() ; - self.retrieve_all_pos_defs(instance, extractor) ? ; - self.retrieve_all_neg_defs(instance, extractor) ? ; - - let pos_neg = ( - self.in_pos_clauses.clone(), self.in_neg_clauses.clone() - ) ; - - for pred in & self.not_in_pos_clauses { - self.pos_new_preds.entry(* pred).or_insert_with( - || pos_neg.clone() - ) ; - } - - for pred in & self.not_in_neg_clauses { - self.neg_new_preds.entry(* pred).or_insert_with( - || pos_neg.clone() - ) ; - } - } - - Ok(do_nothing) - } - - - /// Retrieves a predicate positive definition from some clause. - /// - /// The clause has to be positive. - pub fn retrieve_pos_def( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, clause: & Clause, - ) -> Res<()> { - // Check what we're asked to do makes sense. - let args = if let Some((p, args)) = clause.rhs() { - if p != pred { - bail!( - "trying to retrieve_pos for {} in clause with different rhs", - instance[pred] - ) - } - args - } else { - bail!( - "trying to retrieve_pos for {} in non-positive clause (rhs)", - instance[pred] - ) - } ; - if ! clause.lhs_preds().is_empty() { - bail!( - "trying to retrieve_pos for {} in non-positive clause (lhs)", - instance[pred] - ) - } - - // println!( - // "from clause {}", clause.to_string_info(instance.preds()).unwrap() - // ) ; - - match extractor.terms_of_rhs_app( - true, instance, clause.vars(), - clause.lhs_terms(), clause.lhs_preds(), - (pred, args) - ) ? { - ExtractRes::Failed => bail!( - "term extraction failed for {}", instance[pred] - ), - ExtractRes::Trivial | - ExtractRes::SuccessTrue | - ExtractRes::SuccessFalse => bail!( - "unexpected result for term extraction for {} (false)", - instance[pred] - ), - ExtractRes::Success((qvars, tterms)) => { - let (terms, preds) = tterms.destroy() ; - debug_assert! { preds.is_empty() } - self.add_pos_def_for(pred, (qvars, terms)) - }, - } - - Ok(()) - } - - /// Retrieves all the partial positive definitions for some predicate. - fn retrieve_pos_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, - ) -> Res { - log! { - @verb "retrieving positive partial definitions for {}", - conf.emph(& instance[pred].name) - } - let mut count = 0 ; - for clause in instance.clauses_of(pred).1 { - let clause = & instance[* clause] ; - if clause.lhs_preds().is_empty() { - self.retrieve_pos_def(instance, extractor, pred, clause) ? ; - count += 1 - } - } - Ok(count) - } - - /// Retrieves all partial positive definitions. - fn retrieve_all_pos_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt - ) -> Res<()> { - log! { @4 "retrieve all positive definitions" } - for pred in self.in_pos_clauses.clone() { - log! { @4 "-> {}", instance[pred] } - let count = self.retrieve_pos_defs(instance, extractor, pred) ? ; - if count == 0 { - bail!("failed to retrieve positive definition for {}", instance[pred]) - } - } - Ok(()) - } - - - /// Retrieves a predicate negative definition from some clause. - /// - /// The clause has to be strictly negative. - pub fn retrieve_neg_def( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, clause: & Clause - ) -> Res<()> { - // Check what we're asked to do makes sense. - if clause.rhs().is_some() { - bail! ( - "trying to retrieve_neg for {} in non-negative clause (rhs)", - instance[pred] - ) - } - let args = { - let mut preds = clause.lhs_preds().iter() ; - let mut argss = if let Some((p, argss)) = preds.next() { - debug_assert_eq! { p, & pred } - if preds.next().is_some() { - bail!( - "trying to retrieve_neg for {} in a non-strict clause (preds)", - instance[pred] - ) - } - argss.iter() - } else { - bail!( - "trying to retrieve_neg for {} in empty clause", - instance[pred] - ) - } ; - - if let Some(args) = argss.next() { - debug_assert! { argss.next().is_none() } - args - } else { - bail!( - "trying to retrieve_neg for {} in a non-strict clause (argss)" - ) - } - } ; - - // println!( - // "from clause {}", clause.to_string_info(instance.preds()).unwrap() - // ) ; - - match extractor.terms_of_lhs_app( - true, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - None, (pred, args) - ) ? { - ExtractRes::Failed => bail!( - "term extraction failed for {}", instance[pred] - ), - ExtractRes::Trivial | - ExtractRes::SuccessTrue | - ExtractRes::SuccessFalse => bail!( - "unexpected result for term extraction for {} (false)", - instance[pred] - ), - ExtractRes::Success((qvars, rhs, tterms)) => { - debug_assert! { rhs.is_none() } - let (terms, preds) = tterms.destroy() ; - debug_assert! { preds.is_empty() } - let mut neg_terms = TermSet::new() ; - for term in terms { - neg_terms.insert(term) ; - } - self.add_neg_def_for(pred, (qvars, neg_terms)) - }, - } - - Ok(()) - } - - /// Retrieves all the partial negative definitions for some predicate. - fn retrieve_neg_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, - ) -> Res { - log! { - @verb "retrieving negative partial definitions for {}", - conf.emph(& instance[pred].name) - } - let mut count = 0 ; - for clause in instance.clauses_of(pred).0 { - let clause = & instance[* clause] ; - if clause.lhs_preds().len() == 1 - && clause.rhs().is_none() - && clause.lhs_preds().iter().next().map( - |(_, argss)| argss.len() == 1 - ).unwrap_or( false ) { - self.retrieve_neg_def(instance, extractor, pred, clause) ? ; - count += 1 - } - } - Ok(count) - } - - /// Retrieves all partial negative definitions. - fn retrieve_all_neg_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - ) -> Res<()> { - for pred in self.in_neg_clauses.clone() { - let count = self.retrieve_neg_defs(instance, extractor, pred) ? ; - if count == 0 { - bail!("failed to retrieve negative definition for {}", instance[pred]) - } - } - Ok(()) - } - - - - - /// Forces some terms in a clause by inserting a predicate application. - /// - /// `terms` is understood as a conjunction. - fn insert_terms( - & self, clause: & mut Clause, args: & VarTerms, - qvars: & Quantfed, terms: & TermSet, - ) -> Res<()> { - // Generate fresh variables for the clause if needed. - let qual_map = clause.fresh_vars_for(qvars) ; - - for term in terms { - if let Some((term, _)) = term.subst_total( & (args, & qual_map) ) { - clause.insert_term(term) ; - } else { - bail!("error during total substitution in `insert_terms`") - } - } - - Ok(()) - } - - - - - - /// Tries to generate some positive clauses for a predicate. - fn generate_pos_clauses_for<'a>( - & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> - ) -> Res { - let mut info = RedInfo::new() ; - - 'all_clauses: for rhs_clause in instance.clauses_of(pred).1.clone() { - let mut one_pred_is_new = false ; - - let mut estimation = 1 ; - - for (p, argss) in instance[rhs_clause].lhs_preds() { - if let Some(defs) = self.pos_defs.get(p) { - for _ in argss { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses - } - } - } else { - continue 'all_clauses - } - - if let Some((pos, _)) = self.pos_new_preds.get(& pred) { - if pos.contains(p) { - one_pred_is_new = true - } - } - } - if ! one_pred_is_new { - continue 'all_clauses - } - - log! { @4 - "generating positive clause(s) for {} from {} ({})", - instance[pred], - instance[rhs_clause].to_string_info( instance.preds() ) ?, - estimation - } - - let mut nu_clauses = vec![] ; - - scoped! { - - let mut clause = instance[rhs_clause].clone() ; - - let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; - let mut map = Vec::with_capacity( lhs_preds.len() ) ; - - for (pred, argss) in & lhs_preds { - let mut arg_map = Vec::with_capacity( argss.len() ) ; - - if let Some(defs) = self.pos_defs.get(pred) { - for args in argss { - arg_map.push( (args, 0) ) - } - - map.push( (pred, defs, arg_map) ) - } else { - bail!("no definition for {} (positive, lhs)", instance[* pred]) - } - } - - macro_rules! map_inc { - () => ({ - let mut done = true ; - 'all_apps: for & mut (_, defs, ref mut argss) in & mut map { - for (_, ref mut index) in argss { - * index += 1 ; - if * index < defs.len() { - done = false ; - break 'all_apps - } else { - * index = 0 - } - } - } - done - }) - } - - let mut done = false ; - while ! done { - let mut clause = clause.clone() ; - - for (_, defs, argss) in & map { - for (args, index) in argss { - self.insert_terms( - & mut clause, args, & defs[* index].0, & defs[* index].1 - ) ? - } - } - - if let Some(trivial) = instance.is_this_clause_trivial( - & mut clause - ) ? { - if ! trivial { - nu_clauses.push(clause) - } - } else { - unimplemented!("unsat in biased unrolling") - } - - done = map_inc!() - } - - } - - for mut clause in nu_clauses { - clause.from_unrolling = true ; - if let Some(index) = instance.push_clause(clause) ? { - let (extractor, instance) = instance.extraction() ; - self.retrieve_pos_def( - instance, extractor, pred, & instance[index] - ) ? ; - info.clauses_added += 1 - } - } - } - - Ok(info) - } - - - - - - /// Tries to generate a negative clause for a predicate. - fn generate_neg_clauses_for<'a>( - & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> - ) -> Res { - // self.print(instance) ; - - let mut info = RedInfo::new() ; - - 'all_clauses: for lhs_clause in instance.clauses_of(pred).0.clone() { - let mut one_pred_is_new = false ; - - let mut estimation = 1 ; - - if let Some((p, _)) = instance[lhs_clause].rhs() { - if let Some(defs) = self.neg_defs.get(& p) { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses - } - } else { - continue 'all_clauses - } - if let Some((_, neg)) = self.neg_new_preds.get(& pred) { - if neg.contains(& p) { - one_pred_is_new = true - } - } - } - - for (p, argss) in instance[lhs_clause].lhs_preds() { - if * p == pred { - if argss.len() == 1 { - () - } else if let Some(defs) = self.pos_defs.get(p) { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses - } - } else { - continue 'all_clauses - } - } else if let Some(defs) = self.pos_defs.get(p) { - for _ in argss { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses - } - } - } else { - // log! { @6 "{} not in pos clauses", instance[* p] } - continue 'all_clauses - } - - if let Some((pos, _)) = self.neg_new_preds.get(& pred) { - if pos.contains(p) { - one_pred_is_new = true - } - } - } - - // log! { @6 "one pred new: {}", one_pred_is_new } - - if ! one_pred_is_new { - continue 'all_clauses - } - - log! { @4 - "generating negative clause(s) for {} from {}", - instance[pred], - instance[lhs_clause].to_string_info( instance.preds() ) ? - } - - let mut nu_clauses = vec![] ; - - scoped! { - - let mut clause = instance[lhs_clause].clone() ; - - let mut this_pred = None ; - let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; - let rhs_pred = clause.unset_rhs() ; - - let mut map = Vec::with_capacity( - lhs_preds.len() - ) ; - - for (p, argss) in lhs_preds { - let mut arg_map = Vec::with_capacity( argss.len() ) ; - - if let Some(defs) = self.pos_defs.get(& p) { - for args in argss { - arg_map.push( (args, 0) ) - } - - if p == pred { - this_pred = Some((defs, arg_map)) - } else { - map.push( (p, defs, arg_map) ) - } - } else if p == pred { - debug_assert_eq! { argss.len(), 1 } - let is_new = clause.insert_pred_app( - pred, argss.into_iter().next().unwrap() - ) ; - debug_assert! { is_new } - } else { - bail!("no definition for {} (negative, lhs)", instance[p]) - } - } - - if let Some((pred, args)) = rhs_pred { - if let Some(defs) = self.neg_defs.get(& pred) { - map.push( (pred, defs, vec![ (args, 0) ]) ) - } else { - bail!("no definition for {} (negative, rhs)", instance[pred]) - } - } - - let mut active_lhs_pred_app = 0 ; - - macro_rules! map_inc { - () => ({ - let mut done = true ; - for & mut (_, defs, ref mut argss) in & mut map { - map_inc!(@argss done, defs, argss ; true) ; - if ! done { - break - } - } - - // println!("inc: {}", done) ; - - if done { - if let Some( - & mut (defs, ref mut argss) - ) = this_pred.as_mut() { - let argss_len = argss.len() ; - if active_lhs_pred_app < argss_len { - let mut index = 0 ; - map_inc!( - @argss done, defs, argss ; { - let iff = index != active_lhs_pred_app ; - index += 1 ; - iff - } - ) - } - - if done { - active_lhs_pred_app += 1 ; - done = active_lhs_pred_app >= argss_len - } - } - } - - done - }) ; - (@argss $done:ident, $defs:expr, $argss:expr ; $iff:expr) => ( - for (_, ref mut index) in $argss { - if $iff { - * index += 1 ; - if * index < $defs.len() { - $done = false ; - break - } else { - * index = 0 - } - } - } - ) - } - - let mut done = false ; - while ! done { - // println!("running: {}", active_lhs_pred_app) ; - - let mut clause = clause.clone() ; - if let Some((defs, argss)) = this_pred.as_ref() { - let mut current = 0 ; - - while current < argss.len() { - let (ref args, index) = argss[current] ; - if current == active_lhs_pred_app { - let is_new = clause.insert_pred_app(pred, args.clone()) ; - debug_assert! { is_new } - } else { - self.insert_terms( - & mut clause, args, & defs[index].0, & defs[index].1 - ) ? - } - current += 1 - } - - } - - for (_, defs, argss) in & map { - for (args, index) in argss { - self.insert_terms( - & mut clause, args, & defs[* index].0, & defs[* index].1 - ) ? - } - } - - // println!( - // "negative clause: {}", - // clause.to_string_info(instance.preds()).unwrap() - // ) ; - - if let Some(trivial) = instance.is_this_clause_trivial( - & mut clause - ) ? { - if ! trivial { - // println!("non-trivial...") ; - nu_clauses.push(clause) - } else { - // println!("trivial...") - } - } else { - unimplemented!("unsat in biased unrolling") - } - - done = map_inc!() - } - - } - - for mut clause in nu_clauses { - log! { @6 - "new clause: {}", - clause.to_string_info( instance.preds() ) ? - } - - clause.from_unrolling = true ; - if let Some(index) = instance.push_clause(clause) ? { - let (extractor, instance) = instance.extraction() ; - self.retrieve_neg_def( - instance, extractor, pred, & instance[index] - ) ? ; - info.clauses_added += 1 - } - } - } - - Ok(info) - } - -} - -impl RedStrat for BiasedUnroll { - fn name(& self) -> & 'static str { "biased_unroll" } - - fn new(_: & Instance) -> Self { - let ( - in_pos_clauses, in_neg_clauses, - not_in_pos_clauses, not_in_neg_clauses, - ) = ( - PrdSet::new(), PrdSet::new(), - PrdSet::new(), PrdSet::new(), - ) ; - - BiasedUnroll { - in_pos_clauses, in_neg_clauses, - not_in_pos_clauses, not_in_neg_clauses, - pos_defs: PrdHMap::new(), - neg_defs: PrdHMap::new(), - pos_new_preds: PrdHMap::new(), - neg_new_preds: PrdHMap::new(), - max_new_clauses: 0, - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - let mut info = RedInfo::new() ; - - let nothing_to_do = self.setup(instance) ? ; - - if nothing_to_do { - return Ok(info) - } - - // println!("done with setup") ; - // self.print(instance) ; - // println!() ; - - let mut new_stuff = true ; - - while new_stuff { - new_stuff = false ; - - if conf.preproc.pos_unroll { - - for pred in self.not_in_pos_clauses.clone() { - log! { @4 - "trying to generate positive clauses for {}", instance[pred] - } - // self.print(instance) ; - - if self.pos_new_preds.get(& pred).map( - |(pos, _)| ! pos.is_empty() - ).unwrap_or(false) { - - let this_info = self.generate_pos_clauses_for(pred, instance) ? ; - if this_info.non_zero() { - new_stuff = true ; - - let was_there = self.not_in_pos_clauses.remove(& pred) ; - debug_assert! { was_there } - - let is_new = self.in_pos_clauses.insert(pred) ; - debug_assert! { is_new } - - let prev = self.pos_new_preds.remove(& pred) ; - debug_assert! { prev.is_some() } - - for (_, (pos, _)) in self.pos_new_preds.iter_mut().chain( - self.neg_new_preds.iter_mut() - ) { - let is_new = pos.insert(pred) ; - debug_assert! { is_new } - } - log! { @4 "-> success" } - - log! { @verb - "generated {} positive clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) - } - - } else { - if let Some((pos, neg)) = self.pos_new_preds.get_mut(& pred) { - pos.clear() ; - neg.clear() - } else { - bail!("inconsistent BiasedUnroll state") - } - log! { @4 "-> failure" } - } - info += this_info ; - - } else { - log! { @4 "-> nothing new, skipping" } - } - } - - } - - if conf.preproc.neg_unroll { - - for pred in self.not_in_neg_clauses.clone() { - log! { @4 - "trying to generate negative clauses for {}", instance[pred] - } - // self.print(instance) ; - - if self.neg_new_preds.get(& pred).map( - |(pos, neg)| ! pos.is_empty() || ! neg.is_empty() - ).unwrap_or(false) { - - let this_info = self.generate_neg_clauses_for(pred, instance) ? ; - if this_info.non_zero() { - new_stuff = true ; - - let was_there = self.not_in_neg_clauses.remove(& pred) ; - debug_assert! { was_there } - - let is_new = self.in_neg_clauses.insert(pred) ; - debug_assert! { is_new } - - let prev = self.neg_new_preds.remove(& pred) ; - debug_assert! { prev.is_some() } - - for (_, (_, neg)) in self.pos_new_preds.iter_mut().chain( - self.neg_new_preds.iter_mut() - ) { - let is_new = neg.insert(pred) ; - debug_assert! { is_new } - } - log! { @4 "-> success" } - - log! { @verb - "generated {} negative clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) - } - - } else { - if let Some((pos, neg)) = self.neg_new_preds.get_mut(& pred) { - pos.clear() ; - neg.clear() - } else { - bail!("inconsistent BiasedUnroll state") - } - log! { @4 "-> failure" } - } - info += this_info ; - - } else { - log! { @4 "-> nothing new, skipping" } - } - } - - } - - } - - info += instance.simplify_all() ? ; - - Ok(info) - } -} - - - -/// Unrolls positive constraints once. -pub struct Unroll { - max_new_clauses: usize, - ignore: PrdSet, -} - -impl RedStrat for Unroll { - fn name(& self) -> & 'static str { "unroll" } - - fn new(instance: & Instance) -> Self { - Unroll { - max_new_clauses: ::std::cmp::max( - 5, instance.preds().len() + instance.clauses().len() * 5 / 100 - ), - ignore: PrdSet::new(), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - - let mut prd_map: PrdHMap< - Vec<(Option, TTermSet)> - > = PrdHMap::with_capacity(17) ; - - scoped! { - let mut insert = | - pred: PrdIdx, q: Option, ts: TTermSet - | prd_map.entry(pred).or_insert_with(Vec::new).push((q, ts)) ; - - let (extractor, instance) = instance.extraction() ; - - 'pos_clauses: for clause in instance.clauses() { - - if ! clause.lhs_preds().is_empty() { - continue 'pos_clauses - } - - conf.check_timeout() ? ; - - if let Some((pred, args)) = clause.rhs() { - if self.ignore.contains(& pred) { - continue 'pos_clauses - } - match extractor.terms_of_rhs_app( - true, instance, & clause.vars, - clause.lhs_terms(), clause.lhs_preds(), - (pred, args) - ) ? { - ExtractRes::Success((q, ts)) => insert( - pred, Quant::forall(q), ts - ), - ExtractRes::SuccessTrue => { - let mut set = TTermSet::new() ; - set.insert_term( term::tru() ) ; - insert( - pred, None, set - ) - }, - ExtractRes::Failed => continue 'pos_clauses, - ExtractRes::Trivial => bail!( - "found a trivial clause during unrolling" - ), - ExtractRes::SuccessFalse => bail!( - "found a predicate equivalent to false during unrolling" - ), - } - } - - } - } - - let mut info = RedInfo::new() ; - for (pred, terms) in prd_map { - // Anticipate blowup. - let appearances = instance.clauses_of(pred).0.len() ; - if terms.len() * appearances >= self.max_new_clauses { - let is_new = self.ignore.insert(pred) ; - debug_assert! { is_new } - log! { @verb - "not unrolling {}, {} variant(s), estimation: {} new clauses", - conf.emph(& instance[pred].name), - terms.len(), - terms.len() * appearances - } - } else { - log! { @verb - "unrolling {}, {} variant(s)", - conf.emph(& instance[pred].name), - terms.len() - } - info += instance.unroll(pred, & terms) ? - } - } - Ok(info) - } -} - - - -/// Reverse-unrolls negative constraints once. -pub struct RUnroll { - max_new_clauses: usize, - ignore: PrdSet, -} - -impl RedStrat for RUnroll { - fn name(& self) -> & 'static str { "runroll" } - - fn new(instance: & Instance) -> Self { - RUnroll { - max_new_clauses: ::std::cmp::max( - 5, instance.preds().len() + instance.clauses().len() * 5 / 100 - ), - ignore: PrdSet::new(), - } - } - - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> - ) -> Res { - - let mut prd_map: PrdHMap< - Vec<(Option, TermSet)> - > = PrdHMap::with_capacity(17) ; - - scoped! { - let mut insert = | - pred: PrdIdx, q: Option, ts: TermSet - | prd_map.entry(pred).or_insert_with(Vec::new).push((q, ts)) ; - - let (extractor, instance) = instance.extraction() ; - - 'neg_clauses: for clause in instance.clauses() { - - if clause.rhs().is_some() { continue 'neg_clauses } - - conf.check_timeout() ? ; - - let mut apps = clause.lhs_preds().iter() ; - - if let Some((pred, argss)) = apps.next() { - if self.ignore.contains(& pred) { - continue 'neg_clauses - } - - let pred = * pred ; - let mut argss = argss.iter() ; - let args = if let Some(args) = argss.next() { - args - } else { - bail!("illegal clause, predicate application leads to nothing") - } ; - - if argss.next().is_some() - || apps.next().is_some() { - continue 'neg_clauses - } - - // Negative constraint with only one pred app, reverse-unrolling. - match extractor.terms_of_lhs_app( - true, instance, & clause.vars, - ( clause.lhs_terms(), & PredApps::with_capacity(0) ), - None, (pred, args) - ) ? { - - ExtractRes::Success((q, apps, ts)) => { - debug_assert! { apps.is_none() } - let (terms, pred_apps) = ts.destroy() ; - if_debug! { - log!{ @debug - "from {}", - clause.to_string_info( - instance.preds() - ) ? - } - log! { @debug "terms {{" } - for term in & terms { - log! { @debug " {}", term } - } - log! { @debug "}}" } - log! { @debug "pred apps {{" } - for (pred, argss) in & pred_apps { - for args in argss { - let mut s = format!("({}", instance[* pred]) ; - for arg in args.iter() { - s = format!("{} {}", s, arg) - } - log! { @debug " {})", s } - } - } - log! { @debug "}}" } - } - debug_assert! { pred_apps.is_empty() } - insert( pred, Quant::exists(q), terms ) - }, - ExtractRes::SuccessFalse => { - let mut set = TermSet::new() ; - insert( pred, None, set ) - }, - ExtractRes::Failed => { - log! { @debug "extraction failed, skipping" } - continue 'neg_clauses - }, - ExtractRes::Trivial => bail!( - "found a trivial clause during unrolling" - ), - ExtractRes::SuccessTrue => bail!( - "found a predicate equivalent to true during reverse-unrolling" - ), - - } - - } - - } - } - - let mut info = RedInfo::new() ; - for (pred, terms) in prd_map { - // Anticipate blowup. - let appearances = instance.clauses_of(pred).0.len() ; - if appearances >= self.max_new_clauses { - let is_new = self.ignore.insert(pred) ; - debug_assert! { is_new } - log! { @verb - "not r_unrolling {}, {} occurence(s), \ - estimation: {} new clauses (>= {})", - conf.emph(& instance[pred].name), - appearances, - terms.len() * appearances, - self.max_new_clauses, - } - } else { - log! { @verb - "r_unrolling {}, {} variant(s)", - conf.emph(& instance[pred].name), - terms.len() - } - info += instance.reverse_unroll(pred, & terms) ? - } - } - Ok(info) - } -} - - - - - - - - +} \ No newline at end of file diff --git a/src/instance/preproc/one_lhs.rs b/src/instance/preproc/one_lhs.rs new file mode 100644 index 00000000..c6e2173e --- /dev/null +++ b/src/instance/preproc/one_lhs.rs @@ -0,0 +1,233 @@ +//! One lhs module. + + +use common::* ; +use instance::{ + instance::PreInstance, preproc::{ + RedStrat, utils::ExtractRes + }, +} ; + + + + + + +/// Tries to reduce predicates that appear as an antecedent in exactly one +/// clause. +/// +/// For a predicate `p`, if the clause in question is +/// +/// ```bash +/// lhs(v_1, ..., v_n) and p(v_1, ..., v_n) => rhs(v_1, ..., v_n) +/// ``` +/// +/// then `p` is reduced to +/// +/// ```bash +/// (not lhs(v_1, ..., v_n)) or rhs(v_1, ..., v_n) +/// ``` +/// +/// **iff** `p` is the only predicate application in the clause, `lhs` is sat +/// and `(not rhs)` is sat. +/// +/// If `lhs` or `(not rhs)` is unsat, then the clause is dropped and `p` is +/// reduced to `true` since it does not appear as an antecedent anywhere +/// anymore. +pub struct OneLhs ; + +impl OneLhs { + /// Logs an extraction result. + fn log_extraction( + & self, _instance: & Instance, + _quantfed: & Quantfed, _pred_app: & Option, _tterms: & TTermSet + ) { + if_log!{ @4 + let mut s = "(".to_string() ; + + if ! _quantfed.is_empty() { + s.push_str("exists (") ; + for (var, sort) in _quantfed { + s.push_str( + & format!(" ({} {})", var.default_str(), sort) + ) + } + s.push_str(" )\n(") + } + + s.push_str("or\n") ; + + if let Some((pred, args)) = _pred_app { + s.push_str("(") ; + s.push_str(& _instance[* pred].name) ; + for arg in args.iter() { + s.push_str( & format!(" {}", arg) ) + } + s.push_str(")") + } + + s.push_str("\n (not\n (and") ; + + for (pred, argss) in _tterms.preds() { + for args in argss { + s.push_str( + & format!(" ({} {})\n", _instance[* pred], args) + ) + } + } + for term in _tterms.terms() { + s.push_str( + & format!(" {}\n", term) + ) + } + + if ! _quantfed.is_empty() { + s.push_str(") ") + } + s.push_str(")") ; + + log!{ @4 "{}", s } + } + } + + /// Attemps to unfold a predicate that appears in exactly one LHS. + /// + /// Returns `None` if unfolding failed. + fn work_on( + & self, pred: PrdIdx, clause: ClsIdx, instance: & mut PreInstance + ) -> Res< Option > { + + let extraction_res = { + let (extraction, instance) = instance.extraction() ; + let clause = & instance[clause] ; + + let args = if let Some(argss) = clause.lhs_preds().get(& pred) { + let mut iter = argss.iter() ; + // let args = iter.next().expect( + // "illegal clause lhs_preds, argument set is empty" + // ) ; + iter.next().expect( + "illegal clause lhs_preds, argument set is empty" + ) + + // Predicate appears more than once in the LHS, aborting. + // if false && iter.next().is_some() { + // return Ok(None) + // } else { + // args + // } + } else { + bail!("inconsistent instance state") + } ; + + // Is the rhs an application of `pred`? + match clause.rhs() { + Some((p, _)) if p == pred => { + ExtractRes::SuccessTrue + }, + _ => extraction.terms_of_lhs_app( + false, instance, clause.vars(), + ( clause.lhs_terms(), clause.lhs_preds() ), + clause.rhs(), (pred, args) + ) ?, + } + } ; + + log! { @4 + "from {}", + instance.clauses()[clause].to_string_info( instance.preds() ) ? + } + log! { @2 + "unfolding {}", conf.emph(& instance[pred].name) + } + + use self::ExtractRes::* ; + let info = match extraction_res { + SuccessTrue => { + log! { @4 "=> true" } + instance.force_true(pred) ? + }, + SuccessFalse => { + log! { @4 "=> false" } + instance.force_false(pred) ? + }, + Trivial => { + log! { @4 "=> trivial" } + instance.force_true(pred) ? + }, + Success((qualfed, pred_app, tterms)) => { + if pred_app.is_none() && tterms.is_empty() { + log! { @4 "=> false" } + instance.force_false(pred) ? + } else { + self.log_extraction( + instance, & qualfed, & pred_app, & tterms + ) ; + instance.force_pred_right( + pred, qualfed, pred_app, tterms + ) ? + } + }, + // Failed is caught before this match. + Failed => return Ok(None), + } ; + + Ok( Some(info) ) + } +} + +impl RedStrat for OneLhs { + fn name(& self) -> & 'static str { "one_lhs" } + + fn new(_: & Instance) -> Self { OneLhs } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + let mut red_info = RedInfo::new() ; + + 'all_preds: for pred in instance.pred_indices() { + if instance.is_known(pred) + || instance.clauses_of(pred).0.len() > 1 { + continue 'all_preds + } + + conf.check_timeout() ? ; + + let clause = if let Some(clause) = instance.clauses_of( + pred + ).0.iter().next().cloned() { + // Appears in exactly one lhs, let's do this. + clause + } else { + log! { + @3 "{} does not appear in any lhs, forcing true", instance[pred] + } + red_info.preds += 1 ; + red_info += instance.force_true(pred) ? ; + continue 'all_preds + } ; + + log! { @3 + "looking at {} ({}, {})", + instance[pred], + instance.clauses_of(pred).0.len(), + instance.clauses_of(pred).1.len(), + } + + if let Some(info) = self.work_on( + pred, clause, instance + ) ? { + red_info.preds += 1 ; + red_info += info ; + instance.check("after unfolding (one_lhs)") ? ; + debug_assert! { instance.is_known(pred) } + } else { + log! { @4 "failed to unfold {}", instance[pred] } + } + } + + Ok( red_info ) + } +} + diff --git a/src/instance/preproc/one_rhs.rs b/src/instance/preproc/one_rhs.rs new file mode 100644 index 00000000..aa662718 --- /dev/null +++ b/src/instance/preproc/one_rhs.rs @@ -0,0 +1,195 @@ +//! One rhs module. + + +use common::* ; +use instance::{ + instance::PreInstance, preproc::{ + RedStrat, utils::ExtractRes + }, +} ; + + + + + + +/// Works on predicates that appear in only one rhs. +/// +/// # Examples +/// +/// | Clause | `p v_0 v_1 =` | +/// |:-----------------------------------|:----------------------------| +/// | `(v > v' + 2) => (p v v')` | `(v_0 > v_1 + 2)` | +/// | `(v > 0) => (p 7 v )` | `(v_0 = 7) and (v_1 > 0)` | +/// | `(v > 0) => (p 7 v')` | `(v_0 = 7)` | +/// | `(v > 0) => (p v v )` | `(v_0 = v_1) and (v_0 > 0)` | +/// | `(v > 0) and (v <= 0) => (p 7 v')` | `false` (by check-sat) | +pub struct OneRhs ; + +impl OneRhs { + /// Logs an extraction result. + fn log_extraction( + & self, _instance: & Instance, + _quantfed: & Quantfed, _tterms: & TTermSet + ) { + if_log! { @4 + let mut s = "(".to_string() ; + + if ! _quantfed.is_empty() { + s.push_str("exists") ; + for (var, sort) in _quantfed { + s.push_str( + & format!(" ({} {})", var.default_str(), sort) + ) + } + s.push_str(" )\n(") + } + + s.push_str("and\n") ; + + for (pred, argss) in _tterms.preds() { + for args in argss { + s.push_str( + & format!(" ({} {})\n", _instance[* pred], args) + ) + } + } + for term in _tterms.terms() { + s.push_str( & format!(" {}\n", term) ) + } + + if ! _quantfed.is_empty() { + s.push_str(") ") + } + s.push_str(")") ; + + log! { @4 "{}", s } + } + } + + + /// Attemps to unfold a predicate that appears in exactly one RHS. + /// + /// Returns `None` if unfolding failed. + fn work_on( + & self, pred: PrdIdx, clause: ClsIdx, instance: & mut PreInstance + ) -> Res< Option > { + + let extraction_res = { + let (extraction, instance) = instance.extraction() ; + let clause = & instance[clause] ; + + if let Some((_this_pred, args)) = clause.rhs() { + debug_assert_eq!( pred, _this_pred ) ; + + // Does `pred` appear in the lhs? + match clause.lhs_preds().get(& pred) { + Some(apps) if ! apps.is_empty() => { + ExtractRes::SuccessFalse + }, + _ => extraction.terms_of_rhs_app( + true, instance, clause.vars(), + clause.lhs_terms(), clause.lhs_preds(), + (pred, args) + ) ?, + } + } else { + bail!("inconsistent instance state") + } + } ; + + log! { @4 + "from {}", + instance.clauses()[clause].to_string_info( instance.preds() ) ? + } + log! { @2 + "unfolding {}", conf.emph(& instance[pred].name) + } + + use self::ExtractRes::* ; + let info = match extraction_res { + Trivial => { + log! { @ 4 "=> trivial" } + instance.force_false(pred) ? + }, + + SuccessTrue => { + log! { @ 4 "=> true" } + instance.force_true(pred) ? + }, + SuccessFalse => { + log! { @ 4 "=> false" } + instance.force_false(pred) ? + }, + + Success( (qvars, tterms) ) => { + self.log_extraction( + instance, & qvars, & tterms + ) ; + instance.force_pred_left( + pred, qvars, tterms + ) ? + }, + + Failed => return Ok(None), + } ; + + Ok( Some(info) ) + } +} + +impl RedStrat for OneRhs { + fn name(& self) -> & 'static str { "one_rhs" } + + fn new(_: & Instance) -> Self { OneRhs } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + let mut red_info = RedInfo::new() ; + + 'all_preds: for pred in instance.pred_indices() { + if instance.is_known(pred) + || instance.clauses_of(pred).1.len() > 1 { + continue 'all_preds + } + + conf.check_timeout() ? ; + + let clause = if let Some(clause) = instance.clauses_of( + pred + ).1.iter().next().cloned() { + // Appears in exactly on lhs, let's do this. + clause + } else { + log! { + @3 "{} does not appear in any rhs, forcing false", instance[pred] + } + red_info.preds += 1 ; + red_info += instance.force_false(pred) ? ; + continue 'all_preds + } ; + + log! { @3 + "looking at {} ({}, {})", + instance[pred], + instance.clauses_of(pred).0.len(), + instance.clauses_of(pred).1.len(), + } + + if let Some(info) = self.work_on( + pred, clause, instance + ) ? { + red_info.preds += 1 ; + red_info += info ; + instance.check("after unfolding (one_rhs)") ? ; + debug_assert! { instance.is_known(pred) } + } else { + log! { @4 "failed to unfold {}", instance[pred] } + } + + } + + Ok( red_info ) + } +} \ No newline at end of file diff --git a/src/instance/preproc/unroll.rs b/src/instance/preproc/unroll.rs new file mode 100644 index 00000000..67687e3b --- /dev/null +++ b/src/instance/preproc/unroll.rs @@ -0,0 +1,290 @@ +//! Bias unrolling module. + + +use common::* ; +use instance::{ + Clause, + instance::PreInstance, + preproc::{ + RedStrat, utils::{ + ExtractRes, ExtractionCxt + } + }, +} ; + + + + + +// /// Unrolls positive constraints once. +// pub struct Unroll { +// max_new_clauses: usize, +// ignore: PrdSet, +// } + +// impl RedStrat for Unroll { +// fn name(& self) -> & 'static str { "unroll" } + +// fn new(instance: & Instance) -> Self { +// Unroll { +// max_new_clauses: ::std::cmp::max( +// 5, instance.preds().len() + instance.clauses().len() * 5 / 100 +// ), +// ignore: PrdSet::new(), +// } +// } + +// fn apply<'a>( +// & mut self, instance: & mut PreInstance<'a> +// ) -> Res { + +// let mut prd_map: PrdHMap< +// Vec<(Option, TTermSet)> +// > = PrdHMap::with_capacity(17) ; + +// scoped! { + +// let (extractor, instance) = instance.extraction() ; + +// 'pos_clauses: for clause in instance.clauses() { + +// if ! clause.lhs_preds().is_empty() { +// continue 'pos_clauses +// } + +// conf.check_timeout() ? ; + +// if let Some((pred, args)) = clause.rhs() { +// if self.ignore.contains(& pred) { +// continue 'pos_clauses +// } +// match extractor.terms_of_rhs_app( +// true, instance, & clause.vars, +// clause.lhs_terms(), clause.lhs_preds(), +// (pred, args) +// ) ? { +// ExtractRes::Success((q, ts)) => insert( +// pred, Quant::forall(q), ts +// ), +// ExtractRes::SuccessTrue => { +// let mut set = TTermSet::new() ; +// set.insert_term( term::tru() ) ; +// insert( +// pred, None, set +// ) +// }, +// ExtractRes::Failed => continue 'pos_clauses, +// ExtractRes::Trivial => bail!( +// "found a trivial clause during unrolling" +// ), +// ExtractRes::SuccessFalse => bail!( +// "found a predicate equivalent to false during unrolling" +// ), +// } +// } + +// } +// } + +// let mut info = RedInfo::new() ; +// for (pred, terms) in prd_map { +// // Anticipate blowup. +// let appearances = instance.clauses_of(pred).0.len() ; +// if terms.len() * appearances >= self.max_new_clauses { +// let is_new = self.ignore.insert(pred) ; +// debug_assert! { is_new } +// log! { @verb +// "not unrolling {}, {} variant(s), estimation: {} new clauses", +// conf.emph(& instance[pred].name), +// terms.len(), +// terms.len() * appearances +// } +// } else { +// log! { @verb +// "unrolling {}, {} variant(s)", +// conf.emph(& instance[pred].name), +// terms.len() +// } +// info += instance.unroll(pred, & terms) ? +// } +// } +// Ok(info) +// } +// } + + + +/// Reverse-unrolls negative constraints once. +pub struct RUnroll { + max_new_clauses: usize, + ignore: PrdSet, +} + +impl RUnroll { + /// Reverse unrolls a clause. + fn runroll_clause( + & mut self, instance: & Instance, clause: & Clause, + extractor: & mut ExtractionCxt, pred_map: & mut PrdHMap< + Vec<(Option, TermSet)> + > + ) -> Res<()> { + macro_rules! insert { + ($pred:expr, $q:expr, $ts:expr) =>( + pred_map.entry($pred).or_insert_with(Vec::new).push(($q, $ts)) + ) + } + + if clause.rhs().is_some() { + return Ok(()) + } + + conf.check_timeout() ? ; + + let mut apps = clause.lhs_preds().iter() ; + + let (pred, argss) = if let Some((pred, argss)) = apps.next() { + (pred, argss) + } else { + return Ok(()) + } ; + + if self.ignore.contains(& pred) { + return Ok(()) + } + + let pred = * pred ; + let mut argss = argss.iter() ; + let args = if let Some(args) = argss.next() { + args + } else { + bail!("illegal clause, predicate application leads to nothing") + } ; + + if argss.next().is_some() + || apps.next().is_some() { + return Ok(()) + } + + // Negative constraint with only one pred app, reverse-unrolling. + match extractor.terms_of_lhs_app( + true, instance, & clause.vars, + ( clause.lhs_terms(), & PredApps::with_capacity(0) ), + None, (pred, args) + ) ? { + + ExtractRes::Success((q, apps, ts)) => { + debug_assert! { apps.is_none() } + let (terms, pred_apps) = ts.destroy() ; + if_log! { @3 + let mut s = format!( + "from {}\n", + clause.to_string_info( + instance.preds() + ) ? + ) ; + s.push_str("terms {\n") ; + + for term in & terms { + s.push_str( & format!(" {}\n", term) ) + } + s.push_str("}\npred apps {{") ; + + for (pred, argss) in & pred_apps { + for args in argss { + s.push_str( & format!(" ({}", instance[* pred]) ) ; + for arg in args.iter() { + s.push_str( & format!(" {}", arg) ) + } + s.push_str(")\n") + } + } + s.push_str(")") ; + log! { @3 "{}", s } + } + debug_assert! { pred_apps.is_empty() } + insert!( pred, Quant::exists(q), terms ) + }, + ExtractRes::SuccessFalse => { + let mut set = TermSet::new() ; + insert!( pred, None, set ) + }, + ExtractRes::Failed => log! { + @3 "extraction failed, skipping" + }, + ExtractRes::Trivial => bail!( + "found a trivial clause during unrolling" + ), + ExtractRes::SuccessTrue => bail!( + "found a predicate equivalent to true during reverse-unrolling" + ), + + } + + Ok(()) + } +} + +impl RedStrat for RUnroll { + fn name(& self) -> & 'static str { "runroll" } + + fn new(instance: & Instance) -> Self { + RUnroll { + max_new_clauses: ::std::cmp::max( + 5, instance.preds().len() + instance.clauses().len() * 5 / 100 + ), + ignore: PrdSet::new(), + } + } + + fn apply<'a>( + & mut self, instance: & mut PreInstance<'a> + ) -> Res { + + let mut prd_map: PrdHMap< + Vec<(Option, TermSet)> + > = PrdHMap::with_capacity(17) ; + + scoped! { + let (extractor, instance) = instance.extraction() ; + for clause in instance.clauses() { + self.runroll_clause( + instance, clause, extractor, & mut prd_map + ) ? + } + } + + let mut info = RedInfo::new() ; + for (pred, terms) in prd_map { + // Anticipate blowup. + let appearances = instance.clauses_of(pred).0.len() ; + if appearances >= self.max_new_clauses { + let is_new = self.ignore.insert(pred) ; + debug_assert! { is_new } + log! { @verb + "not r_unrolling {}, {} occurence(s), \ + estimation: {} new clauses (>= {})", + conf.emph(& instance[pred].name), + appearances, + terms.len() * appearances, + self.max_new_clauses, + } + } else { + log! { @verb + "r_unrolling {}, {} variant(s)", + conf.emph(& instance[pred].name), + terms.len() + } + info += instance.reverse_unroll(pred, & terms) ? + } + } + Ok(info) + } +} + + + + + + + + diff --git a/src/instance/preproc/utils.rs b/src/instance/preproc/utils.rs index 6fcda191..e2d746fa 100644 --- a/src/instance/preproc/utils.rs +++ b/src/instance/preproc/utils.rs @@ -652,7 +652,7 @@ pub fn register_stats( /// Registers some info for a preprocessor. pub fn register_info( instance: & Instance, _profiler: & Profiler, - preproc: & 'static str, red_info: & RedInfo, + preproc: & 'static str, _red_info: & RedInfo, count: usize, ) -> Res<()> { preproc_dump!( @@ -664,25 +664,25 @@ pub fn register_info( profile!{ |_profiler| format!( "{:>10} pred red", preproc - ) => add red_info.preds + ) => add _red_info.preds } profile!{ |_profiler| format!( "{:>10} clause red", preproc - ) => add red_info.clauses_rmed + ) => add _red_info.clauses_rmed } profile!{ |_profiler| format!( "{:>10} clause add", preproc - ) => add red_info.clauses_added + ) => add _red_info.clauses_added } profile!{ |_profiler| format!( "{:>10} arg red", preproc - ) => add red_info.args_rmed + ) => add _red_info.args_rmed } log! { @verb - "{}: {}", conf.emph( preproc ), red_info + "{}: {}", conf.emph( preproc ), _red_info } Ok(()) } diff --git a/src/learning/ice/synth/int.rs b/src/learning/ice/synth/int.rs index 837ebf8a..f331bdd6 100644 --- a/src/learning/ice/synth/int.rs +++ b/src/learning/ice/synth/int.rs @@ -34,7 +34,7 @@ impl TheoSynth for IntSynth { } fn restart(& mut self) { - * self = Self::new() + self.expressivity = 0 } fn increment(& mut self) { diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index aeec02c3..4f4cd423 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -154,11 +154,15 @@ impl SynthSys { ) -> Res where F: FnMut(Term) -> Res { - self.int_synth(sample, & mut f, _profiler) ? ; - self.real_synth(sample, & mut f, _profiler) ? ; - self.adt_synth(sample, & mut f, _profiler) ? ; - - Ok(false) + let done = self.int_synth( + sample, & mut f, _profiler + ) ? || self.real_synth( + sample, & mut f, _profiler + ) ? || self.adt_synth( + sample, & mut f, _profiler + ) ? ; + + Ok(done) } /// Runs integer synthesis. diff --git a/src/learning/ice/synth/real.rs b/src/learning/ice/synth/real.rs index c1c5766c..77733dfe 100644 --- a/src/learning/ice/synth/real.rs +++ b/src/learning/ice/synth/real.rs @@ -16,6 +16,7 @@ pub struct RealSynth { impl Default for RealSynth { fn default() -> Self { Self::new() } } + impl RealSynth { /// Creates a new integer synthesizer. pub fn new() -> Self { @@ -33,7 +34,7 @@ impl TheoSynth for RealSynth { } fn restart(& mut self) { - * self = Self::new() + self.expressivity = 0 } fn increment(& mut self) { diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 1f105f03..be5c2ad5 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -967,7 +967,7 @@ impl<'a> Teacher<'a> { let model = Parser.fix_model(model) ? ; let cex = Cex::of_model( self.instance[clause].vars(), model, - bias.is_none() && conf.teacher.partial + ! bias.is_none() && conf.teacher.partial ) ? ; profile! { self mark "cexs", "model" } Ok(cex) @@ -980,27 +980,29 @@ impl<'a> Teacher<'a> { ) -> Res< Option<(Cex, Bias)> > { if let Some((actlit, bias)) = bias { - log! { @debug - " checksat with bias {}", bias.to_string(& self.instance) - } - self.solver.comment( - & format!("checksat with bias {}", bias.to_string(& self.instance)) - ) ? ; - profile!{ self tick "cexs", "biased check-sat" } - let sat = self.solver.check_sat_act( - Some(& actlit) - ) ? ; - - if sat { - profile!{ self mark "cexs", "biased check-sat" } - let cex = self.get_bias_cex(clause, & bias) ? ; - self.solver.de_actlit(actlit) ? ; - Ok( - Some((cex, bias)) - ) - } else { - Ok(None) - } + log! { @debug + " checksat with bias {}", bias.to_string(& self.instance) + } + self.solver.comment( + & format!("checksat with bias {}", bias.to_string(& self.instance)) + ) ? ; + profile!{ self tick "cexs", "biased check-sat" } + let sat = self.solver.check_sat_act( + Some(& actlit) + ) ? ; + + if sat { + log! { @debug " sat, getting cex" } + profile!{ self mark "cexs", "biased check-sat" } + let cex = self.get_bias_cex(clause, & bias) ? ; + log! { @debug " {}", cex } + self.solver.de_actlit(actlit) ? ; + Ok( + Some((cex, bias)) + ) + } else { + Ok(None) + } } else { @@ -1010,6 +1012,7 @@ impl<'a> Teacher<'a> { profile!{ self mark "cexs", "check-sat" } if sat { + log! { @debug " sat, getting cex" } let bias = if self.instance[clause].is_positive() { Bias::Lft } else if self.instance[clause].is_strict_neg() { @@ -1025,6 +1028,7 @@ impl<'a> Teacher<'a> { Bias::Non } ; let cex = self.get_bias_cex(clause, & bias) ? ; + log! { @debug " {}", cex } Ok( Some((cex, bias)) ) From 1880fc084af3e6b3c8e9f51161535f50a3660dcf Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 20 Jul 2018 12:40:59 +0900 Subject: [PATCH 26/94] pass time left as timeout to solver + built-in list dtyp --- src/common/config.rs | 10 ++++- src/dtyp/mod.rs | 94 +++++++++++++++++++++++++++++++++++++++++--- src/parse/mod.rs | 9 ++++- src/teacher/mod.rs | 54 +++++++++++++------------ src/term/eval.rs | 5 ++- 5 files changed, 139 insertions(+), 33 deletions(-) diff --git a/src/common/config.rs b/src/common/config.rs index 8a60c3e9..ae17aaa2 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -89,7 +89,14 @@ impl SmtConf { & self, name: & 'static str, parser: Parser, instance: I ) -> Res< ::rsmt2::Solver > where I: AsRef { - let mut solver = ::rsmt2::Solver::new(self.conf(), parser) ? ; + let mut smt_conf = self.conf.clone() ; + if let Some(timeout) = ::common::conf.until_timeout() { + smt_conf.option( + format!( "-T:{}", timeout.as_secs() + 1 ) + ) ; + } + + let mut solver = ::rsmt2::Solver::new(smt_conf, parser) ? ; if let Some(log) = self.log_file(name, instance.as_ref()).chain_err( || format!( "While opening log file for {}", ::common::conf.emph(name) @@ -97,6 +104,7 @@ impl SmtConf { ) ? { solver.tee(log) ? } + ::common::smt::init(& mut solver) ? ; Ok(solver) } diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index a377f548..3985473e 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -21,10 +21,6 @@ wrap_usize! { pub enum PartialTyp { /// Array. Array(Box, Box), - - // /// List, - // List(PartialTyp), - /// Datatype. DTyp(String, ::parse::Pos, TPrmMap), /// Concrete type. @@ -32,11 +28,13 @@ pub enum PartialTyp { /// Type parameter. Param(TPrmIdx), } + impl From for PartialTyp { fn from(typ: Typ) -> Self { PartialTyp::Typ(typ) } } + impl PartialTyp { /// True if the type mentions the datatype provided. pub fn mentions_dtyp( @@ -346,10 +344,19 @@ lazy_static! { static ref factory: Factory = RwLock::new( BTreeMap::new() ) ; + + /// Set of reserved datatypes. + static ref reserved_dtyps: BTreeSet<& 'static str> = { + let mut set = BTreeSet::new() ; + set.insert("List") ; + set + } ; + /// Map from constructors to datatypes. static ref constructor_map: Factory = RwLock::new( BTreeMap::new() ) ; + /// Set of selectors. static ref selector_set: RwLock> = RwLock::new( BTreeSet::new() @@ -357,6 +364,15 @@ lazy_static! { } +/// Checks whether a datatype is reserved. +pub fn check_reserved(name: & str) -> Res<()> { + if reserved_dtyps.contains(name) { + bail!("attempting to redefine built-in datatype {}", conf.bad(name)) + } + Ok(()) +} + + /// Creates a datatype. /// /// Will fail if either @@ -436,7 +452,6 @@ pub fn mk(mut dtyp: RDTyp) -> Res { } } - let prev = if let Ok(mut f) = factory.write() { f.insert( name, dtyp.clone() ) } else { @@ -466,6 +481,34 @@ pub fn is_selector(selector: & str) -> bool { } +/// Lists (some of) the constructors of a datatype as an error. +pub fn constructors_as_error(dtyp: & str) -> Error { + let dtyp = match get(dtyp) { + Ok(res) => res, + Err(e) => return e, + } ; + let mut s = String::new() ; + for (count, (constructor, args)) in dtyp.news.iter().enumerate() { + if count >= 3 { + s.push_str( + & format!("and {} others...", dtyp.news.len() - 3) + ) + } else { + if count > 0 { s.push_str("\n") } + s.push_str(constructor) ; + if ! args.is_empty() { + for (selector, typ) in args { + s.push_str( + & format!(" ({} {})", selector, typ) + ) + } + } + } + } + s.into() +} + + /// Retrieves a datatype from its name. /// /// Will fail if @@ -481,6 +524,8 @@ pub fn get(dtyp: & str) -> Res { if let Some(res) = maybe_res { Ok(res) + } else if dtyp == "List" { + mk( RDTyp::list() ) } else { bail!("unknown datatype `{}`", dtyp) } @@ -520,6 +565,9 @@ pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { let dtyp_pref = & format!("{} ", pref) ; for (name, dtyp) in decs.iter() { + if reserved_dtyps.contains( name.as_str() ) { + continue + } let is_new = known.insert(name) ; if ! is_new { continue @@ -665,6 +713,42 @@ impl RDTyp { } } + /// Generates the definition for the `List` datatype. + fn list() -> Self { + use parse::Pos ; + + let mut news = BTreeMap::new() ; + + let mut prms = TPrmMap::new() ; + let param = prms.next_index() ; + prms.push( "T".into() ) ; + + let prev = news.insert( + "nil".to_string(), vec![] + ) ; + debug_assert! { prev.is_none() } + + let head = ( "head".to_string(), PartialTyp::Param(param) ) ; + let tail = ( + "tail".to_string(), PartialTyp::DTyp( + "List".into(), Pos::default(), vec![ PartialTyp::Param(param) ].into() + ) + ) ; + + let prev = news.insert( + "insert".to_string(), vec![ head, tail ] + ) ; + debug_assert! { prev.is_none() } + + let default = "nil".to_string() ; + + RDTyp { + name: "List".into(), + deps: vec![], + prms, news, default + } + } + /// Pushes a type parameter. pub fn push_typ_param>(& mut self, name: S) -> TPrmIdx { let idx = self.prms.next_index() ; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 8c819acf..b6af1854 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -170,6 +170,9 @@ pub type Cursor = usize ; PartialOrd, Ord )] pub struct Pos(usize) ; +impl Default for Pos { + fn default() -> Self { Pos(0) } +} impl ::std::ops::Deref for Pos { type Target = usize ; fn deref(& self) -> & usize { & self.0 } @@ -1585,7 +1588,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? ; self.ws_cmt() ; - let dtyp = dtyp::mk(dtyp).chain_err( + let is_okay = dtyp::check_reserved(& dtyp.name) ; + + let dtyp = is_okay.and_then( + |_| dtyp::mk(dtyp) + ).chain_err( || self.error( dtyp_pos, "while parsing the declaration for this datatype" ) diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index be5c2ad5..1a961caa 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -37,19 +37,21 @@ pub fn start_class( let res = match teach(& mut teacher) { Ok(res) => Ok(res), - Err(e) => { - match e.kind() { - ErrorKind::Unsat => { - warn! { - "legacy unsat (by error) result triggered\n\ - unsat core will not be available\n\ - please consider contacting the developer" - } - let core = teacher.unsat_core() ; - Ok( TeachRes::Unsat(core) ) - }, - _ => Err(e) - } + + Err(e) => match e.kind() { + ErrorKind::Unsat => { + warn! { + "legacy unsat (by error) result triggered\n\ + unsat core will not be available\n\ + please consider contacting the developer" + } + let core = teacher.unsat_core() ; + Ok( TeachRes::Unsat(core) ) + }, + _ => { + conf.check_timeout() ? ; + Err(e) + }, }, } ; @@ -502,7 +504,7 @@ impl<'a> Teacher<'a> { ) -> Res< Option > { if_log!{ @1 log! { conf.teacher.step, || @1 - "\nCurrent candidates from {} learner:", + "\nCurrent candidate(s) from {} learner:", conf.emph( & self.learners[idx].1 ) } for _pred in self.instance.preds() { @@ -523,9 +525,9 @@ impl<'a> Teacher<'a> { ) ; } - profile!{ self tick "cexs" } - let cexs = self.get_cexs(& candidates) ? ; - profile!{ self mark "cexs" } + let cexs = profile! { + self wrap { self.get_cexs(& candidates) } "cexs" + } ? ; if cexs.is_empty() { return Ok( @@ -962,15 +964,17 @@ impl<'a> Teacher<'a> { /// Retrieves a counterexample given some bias. fn get_bias_cex(& mut self, clause: ClsIdx, bias: & Bias) -> Res { - profile! { self tick "cexs", "model" } + profile! { + self wrap { self.get_bias_cex_inner(clause, bias) } "cexs", "model" + } + } + fn get_bias_cex_inner(& mut self, clause: ClsIdx, bias: & Bias) -> Res { let model = self.solver.get_model() ? ; let model = Parser.fix_model(model) ? ; - let cex = Cex::of_model( + Cex::of_model( self.instance[clause].vars(), model, ! bias.is_none() && conf.teacher.partial - ) ? ; - profile! { self mark "cexs", "model" } - Ok(cex) + ) } @@ -1007,9 +1011,9 @@ impl<'a> Teacher<'a> { } else { log! { @debug " checksat" } - profile!{ self tick "cexs", "check-sat" } - let sat = self.solver.check_sat() ? ; - profile!{ self mark "cexs", "check-sat" } + let sat = profile! { + self wrap { self.solver.check_sat() } "cexs", "check-sat" + } ? ; if sat { log! { @debug " sat, getting cex" } diff --git a/src/term/eval.rs b/src/term/eval.rs index e6ad02d6..2604315e 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -114,9 +114,12 @@ fn total<'a>( } } else { - bail!( + let e: Error = format!( "unknown constructor `{}` for datatype {}", conf.bad(constructor), dtyp.name + ).into() ; + bail!( + e.chain_err( || dtyp::constructors_as_error(& dtyp.name) ) ) } From 09cb69a5047f73b2102252d906ddf45ef3bdf14e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 20 Jul 2018 13:35:51 +0900 Subject: [PATCH 27/94] unsupported message for match, simplified parser slightly --- src/common/consts.rs | 2 ++ src/fun/mod.rs | 13 +++++++++ src/parse/mod.rs | 67 +++++++++++++++++++------------------------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/common/consts.rs b/src/common/consts.rs index 783d381a..fb7e0a2f 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -141,6 +141,8 @@ pub mod keywords { store_ ("store", doc = "Updater for arrays.") select_ ("select", doc = "Accessor for arrays.") + + match_ ("match", doc = "Match operator.") } } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 64681eec..b705d2d2 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -317,6 +317,19 @@ impl RFun { } self.def = def } + + /// Checks the function is legal. + pub fn check(& self) -> Res<()> { + for dep in & self.deps { + if get(dep).is_none() { + bail!( + "function `{}` depends on unknown function `{}`", + conf.emph(& self.name), conf.bad(dep) + ) + } + } + Ok(()) + } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index b6af1854..4214c4d9 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -594,7 +594,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.error( ident_start_pos, format!( - "illegal usage of keyword `{}`", + "illegal use of keyword `{}`", conf.bad(id) ) ) @@ -1277,8 +1277,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? ; self.ws_cmt() ; - let mut final_funs = Vec::with_capacity( funs.len() ) ; - // Parse all definitions. for (mut fun, pos, var_map) in funs { @@ -1311,11 +1309,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) } - let fun = fun::mk(fun).chain_err( + fun::mk(fun).chain_err( || self.error(pos, "while registering this function") - ) ; - - final_funs.push(fun) + ) ? ; } self.ws_cmt() ; @@ -2301,64 +2297,59 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Tries to parse an operator. fn op_opt(& mut self) -> Res< Option > { - macro_rules! none_if_ident_char_else { - ($e:expr) => ( - if self.legal_id_char() { - None - } else { Some($e) } - ) - } let start_pos = self.pos() ; let res = match self.next() { - Some("a") => if self.tag_opt("nd") { - none_if_ident_char_else!(Op::And) + Some("a") => if self.word_opt("nd") { + Some(Op::And) } else { None }, - Some("o") => if self.tag_opt("r") { - none_if_ident_char_else!(Op::Or) + Some("o") => if self.word_opt("r") { + Some(Op::Or) } else { None }, - Some("n") => if self.tag_opt("ot") { - none_if_ident_char_else!(Op::Not) + Some("n") => if self.word_opt("ot") { + Some(Op::Not) } else { None }, - Some("i") => if self.tag_opt("te") { - none_if_ident_char_else!(Op::Ite) + Some("i") => if self.word_opt("te") { + Some(Op::Ite) } else { None }, - Some("m") => if self.tag_opt("od") { - none_if_ident_char_else!(Op::Mod) + Some("m") => if self.word_opt("od") { + Some(Op::Mod) + } else if self.word_opt("atch") { + bail!("unsupported `{}` operator", conf.bad("match")) } else { None }, - Some("r") => if self.tag_opt("em") { - none_if_ident_char_else!(Op::Rem) + Some("r") => if self.word_opt("em") { + Some(Op::Rem) } else { None }, - Some("d") => if self.tag_opt("iv") { - none_if_ident_char_else!(Op::IDiv) - } else if self.tag_opt("istinct") { - none_if_ident_char_else!(Op::Distinct) + Some("d") => if self.word_opt("iv") { + Some(Op::IDiv) + } else if self.word_opt("istinct") { + Some(Op::Distinct) } else { None }, - Some("t") => if self.tag_opt("o_int") { - none_if_ident_char_else!(Op::ToInt) - } else if self.tag_opt("o_real") { - none_if_ident_char_else!(Op::ToReal) + Some("t") => if self.word_opt("o_int") { + Some(Op::ToInt) + } else if self.word_opt("o_real") { + Some(Op::ToReal) } else { None }, - Some("s") => if self.tag_opt("tore") { - none_if_ident_char_else!(Op::Store) - } else if self.tag_opt("elect") { - none_if_ident_char_else!(Op::Select) + Some("s") => if self.word_opt("tore") { + Some(Op::Store) + } else if self.word_opt("elect") { + Some(Op::Select) } else { None }, From 74d1ad68a18d6b8b7676695cfba16dfdca03605a Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 6 Aug 2018 19:03:51 +0900 Subject: [PATCH 28/94] support for side-clauses (hint-clauses) --- Cargo.lock | 31 +++++++---- Cargo.toml | 2 +- rsc/inactive/adt/sorted_len.smt2 | 5 ++ rsc/inactive/adt/sorted_len_rev.smt2 | 34 ++++++------ src/common/config.rs | 34 +++++++++++- src/common/smt.rs | 80 ++++++++++++++++++++++++--- src/errors.rs | 9 +-- src/hoice.rs | 4 +- src/instance/instance/clause.rs | 2 + src/instance/instance/mod.rs | 72 +++++++++++++++++++++++- src/instance/instance/pre_instance.rs | 28 +++++++++- src/instance/preproc/arg_red.rs | 4 +- src/instance/preproc/bias_unroll.rs | 16 +++--- src/instance/preproc/cfg_red.rs | 8 +-- src/instance/preproc/mod.rs | 8 +-- src/instance/preproc/one_lhs.rs | 4 +- src/instance/preproc/one_rhs.rs | 4 +- src/instance/preproc/unroll.rs | 4 +- src/learning/ice/mod.rs | 2 +- src/teacher/mod.rs | 4 +- src/term/mod.rs | 37 +++++++++++++ src/term/zip.rs | 6 +- 22 files changed, 322 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b60ae8cb..b165d270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -58,7 +58,7 @@ version = "2.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -104,9 +104,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hashconsing" version = "0.10.1" -source = "git+https://github.com/AdrienChampion/hashconsing#61e5cd1ff482f84a90075fe749755f64fe2c59c2" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -117,9 +117,9 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)", + "hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -138,8 +138,11 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "libc" @@ -249,7 +252,7 @@ dependencies = [ [[package]] name = "rsmt2" version = "0.9.10" -source = "git+https://github.com/kino-mc/rsmt2#a20f3ab117b609bf1402d037389d8a69bba53cbc" +source = "git+https://github.com/kino-mc/rsmt2#efcb77ca6cba0002adcb07d8347533fdabc3cc5a" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -292,6 +295,11 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.5" @@ -313,7 +321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" @@ -325,9 +333,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)" = "" +"checksum hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c241e038569e815af4c1153387e239dcfe30ae8adf77b39a08f4ea0a829b0c" "checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" -"checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3" +"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)" = "" "checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" @@ -348,6 +356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index a1d6edb0..47560d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ bench = [ ] [dependencies] lazy_static = "*" clap = "*" -hashconsing = { git = "https://github.com/AdrienChampion/hashconsing" } +hashconsing = "*" error-chain = "*" ansi_term = "*" rsmt2 = { git = "https://github.com/kino-mc/rsmt2" } diff --git a/rsc/inactive/adt/sorted_len.smt2 b/rsc/inactive/adt/sorted_len.smt2 index 589c62c2..71605fe6 100644 --- a/rsc/inactive/adt/sorted_len.smt2 +++ b/rsc/inactive/adt/sorted_len.smt2 @@ -20,6 +20,11 @@ ) ) +(assert (forall + ( (l (Lst Int)) ) + (>= (len l) 0) +)) + (define-funs-rec ( (all_equal ( (l (Lst Int)) ) Bool) diff --git a/rsc/inactive/adt/sorted_len_rev.smt2 b/rsc/inactive/adt/sorted_len_rev.smt2 index 0f77908c..2160eb6a 100644 --- a/rsc/inactive/adt/sorted_len_rev.smt2 +++ b/rsc/inactive/adt/sorted_len_rev.smt2 @@ -7,21 +7,6 @@ ) ) ) ) -(define-funs-rec - ( - (len_tr ( (l (Lst Int)) ) Int ) - (len_tailrec ( (acc Int) (l (Lst Int)) ) Int) - ) - ( - (len_tailrec 0 l) - (ite - (= l nil) - acc - (len_tailrec (+ 1 acc) (tail l)) - ) - ) -) - (define-funs-rec ( (len ( (l (Lst Int)) ) Int) @@ -35,6 +20,11 @@ ) ) +(assert (forall + ( (l (Lst Int)) ) + (>= (len l) 0) +)) + (define-funs-rec ( (all_equal ( (l (Lst Int)) ) Bool) @@ -95,6 +85,18 @@ (srt_pst (cons hd nil) true) ) ) +(assert + (forall ( (lst (Lst Int)) ) + (=> + (and + (not (= lst nil)) + (not (= (tail lst) nil)) + (not (< (head lst) (head (tail lst)))) + ) + (srt_pst lst false) + ) +) ) + (assert (forall ( (lst (Lst Int)) ) (=> @@ -129,7 +131,7 @@ ; and (sorted (rev lst)) ; then (assert (all_elements_the_same lst)) (assert - (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) (i Int) ) + (forall ( (lst1 (Lst Int)) (lst2 (Lst Int)) ) (=> (and (= (rev lst1) lst2) diff --git a/src/common/config.rs b/src/common/config.rs index ae17aaa2..322a54fd 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -85,8 +85,8 @@ impl SmtConf { /// Performs the solver initialization step given by `common::smt::init`. /// /// If logging is active, will log to `.smt2`. - pub fn spawn( - & self, name: & 'static str, parser: Parser, instance: I + fn internal_spawn( + & self, name: & 'static str, parser: Parser, instance: I, preproc: bool ) -> Res< ::rsmt2::Solver > where I: AsRef { let mut smt_conf = self.conf.clone() ; @@ -105,10 +105,38 @@ impl SmtConf { solver.tee(log) ? } - ::common::smt::init(& mut solver) ? ; + if preproc { + ::smt::preproc_init(& mut solver) ? + } else { + ::smt::init(& mut solver, instance) ? + } Ok(solver) } + /// Spawns a solver. + /// + /// Performs the solver initialization step given by `common::smt::init`. + /// + /// If logging is active, will log to `.smt2`. + pub fn spawn( + & self, name: & 'static str, parser: Parser, instance: I + ) -> Res< ::rsmt2::Solver > + where I: AsRef { + self.internal_spawn(name, parser, instance, false) + } + + /// Spawns a preprocessing solver. + /// + /// Performs the solver initialization step given by `common::smt::init`. + /// + /// If logging is active, will log to `.smt2`. + pub fn preproc_spawn( + & self, name: & 'static str, parser: Parser, instance: I + ) -> Res< ::rsmt2::Solver > + where I: AsRef { + self.internal_spawn(name, parser, instance, true) + } + /// Smt log dir, if any. fn log_dir(& self, instance: & Instance) -> Res< Option > { if self.log { diff --git a/src/common/smt.rs b/src/common/smt.rs index a2b3b890..e134b01a 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -12,13 +12,29 @@ use common::{ var_to::vals::{ VarValsMap, VarValsSet } } ; use data::Constraint ; +use instance::Clause ; /// Initial setup for a solver. /// /// - declares all the datatypes /// - defines all the functions -pub fn init

(solver: & mut Solver

) -> Res<()> { +/// - asserts all the side-clauses if `preproc` is false +pub fn init( + solver: & mut Solver

, instance: I +) -> Res<()> +where I: AsRef { + dtyp::write_all(solver, "") ? ; + fun::write_all(solver) ? ; + instance.as_ref().assert_side_clauses(solver) +} + +/// Initial setup for a preprocessing solver. +/// +/// - declares all the datatypes +/// - defines all the functions +/// - asserts all the side-clauses if `preproc` is false +pub fn preproc_init

( solver: & mut Solver

) -> Res<()> { dtyp::write_all(solver, "") ? ; fun::write_all(solver) ? ; Ok(()) @@ -27,11 +43,23 @@ pub fn init

(solver: & mut Solver

) -> Res<()> { /// Resets a smt solver. /// -/// Use this and not `solver.reset()`. This declares all the datatypes used in -/// the instance. -pub fn reset

(solver: & mut Solver

) -> Res<()> { +/// Use this and not `solver.reset()`. This declares all the +/// datatypes/functions used in the instance. +pub fn reset( + solver: & mut Solver

, instance: I +) -> Res<()> +where I: AsRef { solver.reset() ? ; - init(solver) + init(solver, instance) +} + +/// Resets a smt preprocessing solver. +/// +/// Use this and not `solver.reset()`. This declares all the +/// datatypes/functions used in the instance. +pub fn preproc_reset

( solver: & mut Solver

) -> Res<()> { + solver.reset() ? ; + preproc_init(solver) } @@ -59,6 +87,32 @@ impl<'a> Expr2Smt<()> for SmtTerm<'a> { } + +/// Smt-prints a clause that has no predicate application. +pub struct SmtSideClause<'a> { + /// The clause. + pub clause: & 'a Clause, +} +impl<'a> SmtSideClause<'a> { + /// Constructor. + pub fn new(clause: & 'a Clause) -> Self { + SmtSideClause { clause } + } +} +impl<'a> Expr2Smt<()> for SmtSideClause<'a> { + fn expr_to_smt2( + & self, w: & mut Writer, _: () + ) -> SmtRes<()> { + self.clause.write( + w, |_, _, _| panic!( + "illegal side clause: found predicate application(s)" + ) + ) ? ; + Ok(()) + } +} + + /// SMT-prints a collection of terms as a conjunction with default var writer. pub struct SmtConj { /// Conjunction. @@ -478,7 +532,7 @@ impl FullParser { while ! model.is_empty() { let model_len = model.len() ; - while let Some((var, sig, typ, val)) = model.pop() { + while let Some((var, sig, mut typ, val)) = model.pop() { match var { FPVar::Var(var) => match val { @@ -528,7 +582,7 @@ impl FullParser { } } - if let Ok(Some(term)) = match val { + if let Ok( Some(mut term) ) = match val { FPVal::FunDef(ref fun) => { let mut var_hmap: BTreeMap< & str, VarIdx @@ -544,7 +598,7 @@ impl FullParser { let mut parser = context.parser(fun, 0, & dummy_profiler) ; parser.term_opt( - & var_infos, & var_hmap, & instance + & var_infos, & var_hmap, & instance ) }, @@ -556,6 +610,16 @@ impl FullParser { name, fun ), } { + if let Some(new_typ) = term.typ().merge(& typ) { + if typ != new_typ { + typ = new_typ.clone() + } + if term.typ() != new_typ { + if let Some(nu_term) = term.force_dtyp(new_typ) { + term = nu_term + } + } + } debug_assert_eq! { term.typ(), typ } let prev = instance.add_define_fun( name.clone(), var_infos, diff --git a/src/errors.rs b/src/errors.rs index 72f68e40..bdfbd405 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -176,20 +176,21 @@ impl Error { /// Returns the clause explaining an unsat result if any. pub fn unsat_cause(& self) -> Option { - match * self.kind() { - ErrorKind::UnsatFrom(clause) => Some(clause), + match self.kind() { + ErrorKind::UnsatFrom(clause) => Some(* clause), _ => None, } } - /// True if the kind of the error is [`ErrorKind::Timeout`][timeout]. + /// True if the kind of the error is a timeout. /// /// [timeout]: enum.ErrorKind.html#variant.Timeout /// (ErrorKind's Timeout variant) pub fn is_timeout(& self) -> bool { - match * self.kind() { + match self.kind() { ErrorKind::Timeout => true, + ErrorKind::SmtError(smt_err) => smt_err.is_timeout(), _ => false, } } diff --git a/src/hoice.rs b/src/hoice.rs index 6424a984..c3fcfc41 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -176,7 +176,7 @@ pub fn read_and_work( } { Ok(()) => (), Err(e) => if e.is_timeout() { - println!("unknown") ; + println!("timeout") ; print_stats("top", profiler) ; ::std::process::exit(0) } else if e.is_unknown() { @@ -246,7 +246,7 @@ pub fn read_and_work( None }, Err(ref e) if e.is_timeout() => { - println!("unknown") ; + println!("timeout") ; print_stats("top", profiler) ; ::std::process::exit(0) }, diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 07d34e9d..5c27532d 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -867,12 +867,14 @@ impl Clause { write!(w, "))") } } + impl ::std::ops::Index for Clause { type Output = VarInfo ; fn index(& self, index: VarIdx) -> & VarInfo { & self.vars[index] } } + impl<'a, 'b> ::rsmt2::print::Expr2Smt< & 'b (bool, & 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) > for Clause { diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index d7206a72..197bab59 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -44,6 +44,17 @@ pub struct Instance { /// Populated by the `finalize` function. sorted_pred_terms: Vec, + /// Side-clauses. + /// + /// A side clause + /// - does not mention any predicate + /// - mentions a user-defined function + /// + /// It will be asserted when the instance is asked to initialize a solver. A + /// side-clause is viewed as additional information provided by the user, a + /// kind of lemma for the actual clauses. + side_clauses: Vec, + /// Clauses. clauses: ClsMap, /// Maps predicates to the clauses where they appear in the lhs and rhs @@ -118,6 +129,7 @@ impl Instance { pred_terms: PrdMap::with_capacity(pred_capa), sorted_pred_terms: Vec::with_capacity(pred_capa), + side_clauses: Vec::with_capacity(7), clauses: ClsMap::with_capacity(clause_capa), // clusters: CtrMap::with_capacity( clause_capa / 3 ), pred_to_clauses: PrdMap::with_capacity(pred_capa), @@ -155,6 +167,7 @@ impl Instance { pred_terms: self.pred_terms.clone(), sorted_pred_terms: Vec::with_capacity( self.preds.len() ), + side_clauses: self.side_clauses.clone(), clauses: self.clauses.clone(), pred_to_clauses: self.pred_to_clauses.clone(), is_unsat: false, @@ -827,6 +840,38 @@ impl Instance { Ok(()) } + /// Asserts all the side-clauses in a solver. + pub fn assert_side_clauses

( + & self, solver: & mut Solver

+ ) -> Res<()> { + for side_clause in & self.side_clauses { + side_clause.write( + solver, |_, _, _| panic!( + "illegal side-clause: found predicate application(s)" + ) + ) ? ; + writeln!(solver) ? ; + } + Ok(()) + } + + /// Registers a clause as a side-clause. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_side_clause(& mut self, clause: Clause) -> Res<()> { + if clause.rhs().is_some() { + bail!("cannot convert to side-clause: predicate application in rhs") + } + if ! clause.lhs_preds().is_empty() { + bail!("cannot convert to side-clause: predicate application(s) in lhs") + } + + self.side_clauses.push(clause) ; + + Ok(()) + } + /// Pushes a clause. /// /// Returns its index, if it was added. @@ -836,6 +881,13 @@ impl Instance { return Ok(None) } } + + if clause.lhs_preds().is_empty() + && clause.rhs().is_none() { + self.add_side_clause(clause) ? ; + return Ok(None) + } + let idx = self.clauses.next_index() ; let is_new = self.push_clause_unchecked(clause) ; // self.check("after `push_clause`") ? ; @@ -1104,13 +1156,31 @@ impl Instance { writeln!(w, "(set-logic HORN)") ? ; writeln!(w) ? ; - writeln!(w, "; Datatypes:") ? ; + writeln!(w, "; Datatypes") ? ; dtyp::write_all(w, "") ? ; dtyp::write_constructor_map(w, "; ") ? ; writeln!(w) ? ; + writeln!(w, "; Functions") ? ; + fun::write_all(w) ? ; + + writeln!(w) ? ; + + writeln!(w, "; Side-clauses") ? ; + for side_clause in & self.side_clauses { + side_clause.write( + w, |_, _, _| panic!( + "illegal side-clause: found predicate application(s)" + ) + ) ? ; + writeln!(w) ? ; + } + + writeln!(w) ? ; + writeln!(w) ? ; + for (pred_idx, pred) in self.preds.index_iter() { if self.pred_terms[pred_idx].is_none() { write!( diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index 2df7e22b..e894bedc 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -31,7 +31,7 @@ pub struct PreInstance<'a> { impl<'a> PreInstance<'a> { /// Constructor. pub fn new(instance: & 'a mut Instance) -> Res { - let solver = conf.solver.spawn("preproc", (), &* instance) ? ; + let solver = conf.solver.preproc_spawn("preproc", (), &* instance) ? ; let simplifier = ClauseSimplifier::new() ; let clauses_to_simplify = Vec::with_capacity(7) ; @@ -1851,6 +1851,30 @@ impl<'a> PreInstance<'a> { } } + self.solver.comment("checking side clauses") ? ; + + for clause in & self.instance.side_clauses { + self.solver.push(1) ? ; + for info in clause.vars() { + if info.active { + self.solver.declare_const( + & info.idx.default_str(), info.typ.get() + ) ? + } + } + self.solver.assert_with( + clause, & (false, & set, & set, & self.instance.preds) + ) ? ; + + let sat = self.solver.check_sat() ? ; + self.solver.pop(1) ? ; + if sat { + return Ok(false) + } + } + + self.solver.comment("checking clauses") ? ; + for clause in & self.instance.clauses { self.solver.push(1) ? ; for info in clause.vars() { @@ -1871,7 +1895,7 @@ impl<'a> PreInstance<'a> { } } - smt::reset(& mut self.solver) ? ; + smt::preproc_reset(& mut self.solver) ? ; Ok(true) } diff --git a/src/instance/preproc/arg_red.rs b/src/instance/preproc/arg_red.rs index 5d891828..3a17936c 100644 --- a/src/instance/preproc/arg_red.rs +++ b/src/instance/preproc/arg_red.rs @@ -26,8 +26,8 @@ impl RedStrat for ArgRed { } } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { let keep = self.inner.run(instance) ; instance.rm_args(keep) diff --git a/src/instance/preproc/bias_unroll.rs b/src/instance/preproc/bias_unroll.rs index 2caede0f..042bca86 100644 --- a/src/instance/preproc/bias_unroll.rs +++ b/src/instance/preproc/bias_unroll.rs @@ -176,8 +176,8 @@ impl BiasedUnroll { /// Sets up the unroller by scanning the instance. /// /// Returns `true` if there's nothing to do. - fn setup<'a>( - & mut self, instance: & mut PreInstance<'a> + fn setup( + & mut self, instance: & mut PreInstance ) -> Res { self.max_new_clauses = ::std::cmp::min( 10, instance.clauses().len() / 20 @@ -482,8 +482,8 @@ impl BiasedUnroll { /// Tries to generate some positive clauses for a predicate. - fn generate_pos_clauses_for<'a>( - & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> + fn generate_pos_clauses_for( + & mut self, pred: PrdIdx, instance: & mut PreInstance ) -> Res { let mut info = RedInfo::new() ; @@ -778,8 +778,8 @@ impl BiasedUnroll { /// Tries to generate a negative clause for a predicate. - fn generate_neg_clauses_for<'a>( - & mut self, pred: PrdIdx, instance: & mut PreInstance<'a> + fn generate_neg_clauses_for( + & mut self, pred: PrdIdx, instance: & mut PreInstance ) -> Res { // self.print(instance) ; @@ -1028,8 +1028,8 @@ impl RedStrat for BiasedUnroll { } } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { let mut info = RedInfo::new() ; diff --git a/src/instance/preproc/cfg_red.rs b/src/instance/preproc/cfg_red.rs index 873f6891..b451c7d7 100644 --- a/src/instance/preproc/cfg_red.rs +++ b/src/instance/preproc/cfg_red.rs @@ -737,8 +737,8 @@ impl Graph { /// Constructs all the predicates not in `keep` by inlining the constraints. /// /// Returns a disjunction of conjunctions. - pub fn inline<'a>( - & mut self, instance: & mut PreInstance<'a>, + pub fn inline( + & mut self, instance: & mut PreInstance, keep: & mut PrdSet, mut upper_bound: usize, ) -> Res< Vec< (PrdIdx, Dnf) > > { let (extractor, instance) = instance.extraction() ; @@ -1189,8 +1189,8 @@ impl RedStrat for CfgRed { } } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { // use std::time::Instant ; // use common::profiling::DurationExt ; diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index 0a2b2c16..9eb313ba 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -488,8 +488,8 @@ pub trait RedStrat { /// Applies the reduction strategy. Returns the number of predicates reduced /// and the number of clauses forgotten. - fn apply<'a>( - & mut self, & mut PreInstance<'a> + fn apply( + & mut self, & mut PreInstance ) -> Res ; } @@ -502,8 +502,8 @@ impl RedStrat for Simplify { fn new(_: & Instance) -> Self { Simplify } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { instance.simplify_all() } diff --git a/src/instance/preproc/one_lhs.rs b/src/instance/preproc/one_lhs.rs index c6e2173e..373f8c96 100644 --- a/src/instance/preproc/one_lhs.rs +++ b/src/instance/preproc/one_lhs.rs @@ -181,8 +181,8 @@ impl RedStrat for OneLhs { fn new(_: & Instance) -> Self { OneLhs } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { let mut red_info = RedInfo::new() ; diff --git a/src/instance/preproc/one_rhs.rs b/src/instance/preproc/one_rhs.rs index aa662718..c0240c6a 100644 --- a/src/instance/preproc/one_rhs.rs +++ b/src/instance/preproc/one_rhs.rs @@ -143,8 +143,8 @@ impl RedStrat for OneRhs { fn new(_: & Instance) -> Self { OneRhs } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { let mut red_info = RedInfo::new() ; diff --git a/src/instance/preproc/unroll.rs b/src/instance/preproc/unroll.rs index 67687e3b..59058a19 100644 --- a/src/instance/preproc/unroll.rs +++ b/src/instance/preproc/unroll.rs @@ -236,8 +236,8 @@ impl RedStrat for RUnroll { } } - fn apply<'a>( - & mut self, instance: & mut PreInstance<'a> + fn apply( + & mut self, instance: & mut PreInstance ) -> Res { let mut prd_map: PrdHMap< diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index 34b8dcaf..201539b0 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -192,7 +192,7 @@ impl<'core> IceLearner<'core> { Ok(data) => { self.count += 1 ; if self.count % 50 == 0 { - smt::reset(& mut self.solver) ? + smt::reset(& mut self.solver, & self.instance) ? } profile! { self "learn steps" => add 1 } if let Some(candidates) = profile!( diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 1a961caa..dc1486e4 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -910,7 +910,7 @@ impl<'a> Teacher<'a> { if self.count % 100 == 0 || conf.teacher.restart_on_cex { - smt::reset(& mut self.solver) ? ; + smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.pop(1) ? } @@ -941,7 +941,7 @@ impl<'a> Teacher<'a> { ) ? ; if conf.teacher.restart_on_cex { - smt::reset(& mut self.solver) ? + smt::reset(& mut self.solver, & self.instance) ? } else { self.solver.pop(1) ? } diff --git a/src/term/mod.rs b/src/term/mod.rs index a8fc5a12..f068372f 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -815,6 +815,43 @@ impl RTerm { + + /// Returns true if the term mentions a function or an ADT. + pub fn has_fun_app_or_adt(& self) -> bool { + use self::zip::* ; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + & self.to_hcons(), + + |_| Ok(()), + + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) | + ZipOp::New(_) | + ZipOp::Slc(_) => Err(()), + _ => Ok( ZipDoTotal::Upp { yielded: () } ), + }, + + |frame| match frame { + ZipFrame { thing: ZipOp::Fun(_), .. } | + ZipFrame { thing: ZipOp::New(_), .. } | + ZipFrame { thing: ZipOp::Slc(_), .. } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: empty `rgt_args`" + ) ; + Ok( ZipDo::Trm { nu_term, frame } ) + }, + } + ) ; + + res.is_err() + } + + + /// Variable substitution. /// /// The `total` flag causes substitution to fail if a variable that's not in diff --git a/src/term/zip.rs b/src/term/zip.rs index bca6c115..62aaa51d 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -144,6 +144,10 @@ impl Accumulator for Vec { fn new_empty(hint: usize) -> Self { Vec::with_capacity(hint) } fn push(& mut self, elem: T) { self.push(elem) } } +impl Accumulator<()> for () { + fn new_empty(_: usize) -> Self {} + fn push(& mut self, _: ()) {} +} @@ -152,7 +156,7 @@ pub fn zip( term: & Term, mut nul_do: NulF, mut app_do: AppF, mut partial: Partial ) -> Result where -Acc: Accumulator, Yield: Clone + ::std::fmt::Display, +Acc: Accumulator, Yield: Clone, NulF: for<'a> FnMut( ZipNullary<'a> From d31792aa6964f18eee53e90c11b62795ee5ca76f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 7 Aug 2018 12:48:11 +0900 Subject: [PATCH 29/94] fix in value printing --- src/dtyp/mod.rs | 2 +- src/parse/mod.rs | 6 +-- src/teacher/assistant.rs | 1 + src/term/op.rs | 4 +- src/term/typ.rs | 29 +++++++++++ src/val/mod.rs | 105 +++++++++++++++++++++++++++------------ 6 files changed, 111 insertions(+), 36 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 3985473e..6e6b73a7 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -301,7 +301,7 @@ impl_fmt! { PartialTyp::Typ(typ) => write!(fmt, "{}{}", typ, post) ?, - PartialTyp::Param(idx) => write!(fmt, "'{}", idx) ?, + PartialTyp::Param(idx) => write!(fmt, "'{}{}", idx, post) ?, PartialTyp::DTyp(dtyp, _, typs) => { if ! typs.is_empty() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 4214c4d9..afe8d581 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -105,7 +105,7 @@ pub trait ItemRead { impl ItemRead for T { fn read_item(& mut self, buf: & mut String) -> Res { let mut line_count = 0 ; - let mut start = buf.len() ; + let mut search_start = buf.len() ; let mut char_override: Option = None ; let mut opn_parens = 0 ; let mut cls_parens = 0 ; @@ -124,7 +124,7 @@ impl ItemRead for T { 'read_lines: while self.read_line( buf ) ? != 0 { line_count += 1 ; debug_assert!( opn_parens >= cls_parens ) ; - let mut chars = buf[start..].chars() ; + let mut chars = buf[search_start ..].chars() ; if let Some(char) = char_override { char_override = search_char(char, & mut chars) @@ -153,7 +153,7 @@ impl ItemRead for T { break 'read_lines } - start = buf.len() + search_start = buf.len() } Ok(line_count) diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 5207bca1..45bb362d 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -254,6 +254,7 @@ impl Assistant { self.solver.assert( & ConjWrap::new( clause.lhs_terms() ) ) ? ; + self.solver.assert( & ArgValEq::new(args, vals) ) ? ; let sat = profile! { self wrap { diff --git a/src/term/op.rs b/src/term/op.rs index 5a813ea4..d161485e 100644 --- a/src/term/op.rs +++ b/src/term/op.rs @@ -190,14 +190,16 @@ impl Op { let next_typ = next.typ() ; // println!(" {} | {}", next_typ, next) ; if let Some(merged_typ) = typ.merge( & next_typ ) { + // println!(" nu_typ: {}", merged_typ) ; if let Some(nu) = fst.force_dtyp( merged_typ.clone() ) { typ = merged_typ.clone() ; * fst = nu ; // println!(" -> {}", fst) } if let Some(nu) = next.force_dtyp( merged_typ ) { + // println!(" -> {} {}", nu, nu.typ()) ; * next = nu ; - // println!(" -> {}", next) + // println!(" {} {}", next, next.typ()) } } else { err!(lft Some(typ), next.typ(), index) diff --git a/src/term/typ.rs b/src/term/typ.rs index cdcafbbf..bf39f1ed 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -114,6 +114,14 @@ impl RTyp { } } + /// True if the type is a datatype. + pub fn is_dtyp(& self) -> bool { + match * self { + RTyp::DTyp { .. } => true, + _ => false, + } + } + /// Inspects an array type. pub fn array_inspect(& self) -> Option<(& Typ, & Typ)> { if let RTyp::Array { src, tgt } = self { @@ -128,6 +136,27 @@ impl RTyp { RTyp::Unk == * self } + /// True if the type mentions an unknown type. + pub fn has_unk(& self) -> bool { + let mut stack = vec![ self ] ; + while let Some(curr) = stack.pop() { + match curr { + RTyp::Unk => return true, + RTyp::Array { src, tgt } => { + stack.push( src.get() ) ; + stack.push( tgt.get() ) + }, + RTyp::DTyp { prms, .. } => for typ in prms { + stack.push( typ.get() ) + }, + RTyp::Int | + RTyp::Real | + RTyp::Bool => (), + } + } + false + } + /// Inspects a datatype type. pub fn dtyp_inspect(& self) -> Option<(& DTyp, & TPrmMap)> { if let RTyp::DTyp { dtyp, prms } = self { diff --git a/src/val/mod.rs b/src/val/mod.rs index 2dac19a1..1b028f19 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -21,12 +21,19 @@ pub type Val = HConsed ; /// Creates a value. pub fn mk>(val: V) -> Val { - factory.mk(val.into()) + let val = val.into() ; + // if val.typ().has_unk() { + // panic!( + // "trying to create a value with a (partially) unknown type: {} ({})", + // val, val.typ() + // ) + // } + factory.mk(val) } /// Creates a boolean value. pub fn bool(b: bool) -> Val { - factory.mk(RVal::B(b)) + mk( RVal::B(b) ) } /// Creates an array with a default value. @@ -41,7 +48,7 @@ pub fn array>( } else { default } ; - factory.mk( + mk( RVal::Array { idx_typ, default, vals: Vec::new() } ) } @@ -117,19 +124,19 @@ pub fn array_of_fun(idx_typ: Typ, term: & Term) -> Res { /// Creates an integer value. pub fn int>(i: I) -> Val { - factory.mk( + mk( RVal::I( i.into() ) ) } /// Creates a rational value. pub fn real>(r: R) -> Val { - factory.mk( + mk( RVal::R( r.into() ) ) } /// Creates a non-value for a type. pub fn none(typ: Typ) -> Val { - factory.mk( RVal::N(typ) ) + mk( RVal::N(typ) ) } /// Creates a new datatype value. @@ -162,7 +169,7 @@ pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { } } } - factory.mk( RVal::DTypNew { typ, name, args } ) + mk( RVal::DTypNew { typ, name, args } ) } @@ -1448,6 +1455,16 @@ impl RVal { idx, idx.typ(), self, self.typ() ) } + + /// True if the value is composite (array or ADT). + pub fn is_composite(& self) -> bool { + match self { + RVal::Array { .. } | + RVal::DTypNew { .. } => true, + RVal::I(_) | RVal::R(_) | RVal::B(_) => false, + RVal::N(ref t) => t.is_dtyp() || t.is_array(), + } + } } @@ -1455,31 +1472,57 @@ impl RVal { impl_fmt!{ RVal(self, fmt) { - match self { - RVal::N(ref t) => write!(fmt, "_[{}]", t), - RVal::I(ref i) => int_to_smt!(fmt, i), - RVal::R(ref r) => rat_to_smt!(fmt, r), - RVal::B(b) => write!(fmt, "{}", b), - RVal::Array { ref default, ref vals, .. } => { - for _ in vals { - write!(fmt, "(store ") ? - } - write!(fmt, "((as const {}) {})", self.typ(), default) ? ; - for (cond, val) in vals.iter().rev() { - write!(fmt, " {} {})", cond, val) ? - } - Ok(()) - }, - RVal::DTypNew { ref name, ref args, .. } => if args.is_empty() { - write!(fmt, "{}", name) - } else { - write!(fmt, "({}", name) ? ; - for arg in args { - write!(fmt, " {}", arg) ? - } - write!(fmt, ")") - }, + + let mut stack = vec![ Either::Left( (false, self) ) ] ; + + while let Some(curr) = stack.pop() { + + match curr { + Either::Right(()) => write!(fmt, ")") ?, + + Either::Left( (sep, curr) ) => { + if sep { + write!(fmt, " ") ? + } + + match curr { + RVal::N(ref t) => write!(fmt, "_[{}]", t) ?, + RVal::I(ref i) => int_to_smt!(fmt, i) ?, + RVal::R(ref r) => rat_to_smt!(fmt, r) ?, + RVal::B(b) => write!(fmt, "{}", b) ?, + + RVal::DTypNew { + ref name, ref args, ref typ + } => if args.is_empty() { + write!(fmt, "(as {} {})", name, typ) ? + } else { + write!(fmt, "({}", name) ? ; + stack.push( Either::Right(()) ) ; + // Reversing since we're popping from the stack. + for arg in args.iter().rev() { + stack.push( Either::Left( (true, arg) ) ) + } + }, + + RVal::Array { ref default, ref vals, .. } => { + for _ in vals { + write!(fmt, "(store ") ? + } + write!(fmt, "((as const {}) {})", self.typ(), default) ? ; + // Not reversing the list, we want to print in reverse order. + for (index, val) in vals.iter() { + stack.push( Either::Right(()) ) ; + stack.push( Either::Left( (true, val) ) ) ; + stack.push( Either::Left( (true, index) ) ) ; + } + }, + } + }, + } + } + + Ok(()) } } From 54cb4349e61929bc331ea8086d13d9c276f304c5 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 9 Aug 2018 20:14:07 +0900 Subject: [PATCH 30/94] more adt stuff: quantified checks (buggy) --- src/check/mod.rs | 5 ++ src/common/smt.rs | 52 +++++++++++++++++ src/dtyp/mod.rs | 4 +- src/fun/mod.rs | 72 +++++++++++++++++++++++ src/instance/instance/clause.rs | 26 +++++++-- src/instance/instance/mod.rs | 24 +++++++- src/instance/instance/pre_instance.rs | 14 ++++- src/parse/mod.rs | 82 ++++++++++++++++++++++++--- src/teacher/mod.rs | 49 +++++++++++++++- src/term/eval.rs | 27 ++++++--- src/term/factory.rs | 82 ++++++++++++++++++++++++++- src/term/mod.rs | 5 +- src/term/op.rs | 2 + src/term/typ.rs | 21 ++++++- src/term/zip.rs | 9 +++ src/val/mod.rs | 6 +- 16 files changed, 442 insertions(+), 38 deletions(-) diff --git a/src/check/mod.rs b/src/check/mod.rs index 2b07cbb8..0158ab1e 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -143,6 +143,7 @@ impl Output { pub fn check_consistency(& mut self, input: & Input) -> Res<()> { log_info!{ "checking predicate signature consistency..." } let mut map = HashMap::with_capacity( self.pred_defs.len() ) ; + log! { @4 "checking for duplicate definitions" } for & PredDef { ref pred, ref args, .. } in & self.pred_defs { let prev = map.insert(pred.clone(), args.clone()) ; if prev.is_some() { @@ -151,6 +152,7 @@ impl Output { ) } } + log! { @4 "checking signatures" } for & PredDec { ref pred, ref sig } in & input.pred_decs { if let Some(args) = map.get(pred) { @@ -187,6 +189,7 @@ impl Output { ) } } + log! { @4 "done" } Ok(()) } } @@ -321,6 +324,8 @@ impl Data { pub fn do_it(input_file: & str, output_file: & str) -> Res<()> { let data = Data::of_files(input_file, output_file) ? ; + log! { @4 "spawning solver" } + let mut solver = conf.solver.spawn( "check", Parser, & ::instance::Instance::new() ) ? ; diff --git a/src/common/smt.rs b/src/common/smt.rs index e134b01a..6530b6dd 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -113,6 +113,58 @@ impl<'a> Expr2Smt<()> for SmtSideClause<'a> { } +/// Smt-prints a clause with its quantifiers, under an actlit. +pub struct SmtQClause<'a> { + /// The clause. + pub clause: & 'a Clause, +} +impl<'a> SmtQClause<'a> { + /// Constructor. + pub fn new(clause: & 'a Clause) -> Self { + SmtQClause { clause } + } +} +impl<'a, 'b> Expr2Smt< + & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) +> for SmtQClause<'a> { + fn expr_to_smt2( + & self, w: & mut Writer, + info: & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) + ) -> SmtRes<()> { + let ( + ref true_preds, ref false_preds, ref prd_info + ) = * info ; + + self.clause.naked_write( + w, |w, prd, args| { + if true_preds.contains(& prd) { + write!(w, "true") + } else if false_preds.contains(& prd) { + write!(w, "false") + } else { + if ! args.is_empty() { + write!(w, "(") ? + } + write!(w, "{}", prd_info[prd].name) ? ; + for arg in args.iter() { + write!(w, " ") ? ; + arg.write( + w, |w, var| write!(w, "{}", self.clause[var]) + ) ? + } + if ! args.is_empty() { + write!(w, ")") ? + } + Ok(()) + } + } + ) ? ; + + Ok(()) + } +} + + /// SMT-prints a collection of terms as a conjunction with default var writer. pub struct SmtConj { /// Conjunction. diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 6e6b73a7..65897737 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -36,6 +36,7 @@ impl From for PartialTyp { } impl PartialTyp { + /// True if the type mentions the datatype provided. pub fn mentions_dtyp( & self, dtyp_name: & str @@ -132,8 +133,6 @@ impl PartialTyp { } - - fn write( & self, w: & mut W, prms: & TPrmMap ) -> ::std::io::Result<()> { @@ -280,6 +279,7 @@ impl PartialTyp { } } + } impl_fmt! { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index b705d2d2..22d709b2 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -29,6 +29,69 @@ lazy_static! { ) ; } + +lazy_static! { + /// Stores function declarations before obtaining the actual definition. + static ref fun_decs: RwLock< BTreeMap > = RwLock::new( + BTreeMap::new() + ) ; +} + +/// Registers a function declaration. +pub fn register_dec(fun: RFun) -> Res<()> { + if let Ok(mut decs) = fun_decs.write() { + let prev = decs.insert( fun.name.clone(), fun ) ; + if let Some(prev) = prev { + bail!("the function {} is declared twice", conf.bad(& prev.name)) + } + } else { + bail!("unable to access function declarations") + } + Ok(()) +} + +/// Retrieves a function declaration. +pub fn retrieve_dec(fun: & str) -> Res { + if let Ok(mut decs) = fun_decs.write() { + if let Some(dec) = decs.remove(fun) { + Ok(dec) + } else { + bail!("unable to retrieve function declaration for {}", conf.bad(fun)) + } + } else { + bail!("unable to access function declarations") + } +} + + + +/// Accesses the declaration of a function. +pub fn dec_do(fun: & str, mut f: F) -> Res +where F: for<'a> FnMut(& 'a RFun) -> Res { + if let Ok(defs) = factory.read() { + if let Some(def) = defs.0.get(fun) { + return f(def) + } + } else { + bail!("unable to retrieve function factory") + } + + if let Ok(defs) = fun_decs.read() { + if let Some(def) = defs.get(fun) { + f(def) + } else { + bail!( + "trying to retrieve declaration for unknown function {}", + conf.bad(fun) + ) + } + } else { + bail!("unable to retrieve function declarations") + } +} + + + /// Read version of the factory. fn read_factory<'a>() -> RwLockReadGuard< 'a, (BTreeMap, usize) @@ -59,6 +122,15 @@ macro_rules! factory { } +/// Iterates over all function definitions. +pub fn iter(mut f: F) -> Res<()> +where F: FnMut(& Fun) -> Res<()> { + let defs = read_factory() ; + for def in defs.0.values() { f(def) ? } + Ok(()) +} + + /// Creates a function definition. pub fn mk(fun: RFun) -> Res { let fun = Arc::new( fun ) ; diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 5c27532d..4a5eb024 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -790,12 +790,23 @@ impl Clause { Ok(()) } - /// Writes a clause given a special function to write predicates. + /// Writes a clause given a special function to write predicates. pub fn write( & self, w: & mut W, write_prd: WritePrd ) -> IoRes<()> where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - write!(w, "({} ({}\n (", keywords::cmd::assert, keywords::forall) ? ; + write!(w, "({} ", keywords::cmd::assert) ? ; + self.naked_write(w, write_prd) ? ; + writeln!(w, ")") ? ; + Ok(()) + } + + /// Writes a clause without the `assert` around it. + pub fn naked_write( + & self, w: & mut W, write_prd: WritePrd + ) -> IoRes<()> + where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + write!(w, "({}\n (", keywords::forall) ? ; let mut inactive = 0 ; for var in & self.vars { @@ -864,7 +875,7 @@ impl Clause { if let Some(suff) = suff { writeln!(w, "{}", suff) ? } - write!(w, "))") + write!(w, ")") } } @@ -932,12 +943,17 @@ impl<'a, 'b> ::rsmt2::print::Expr2Smt< } else if false_preds.contains(& prd) { write!(writer, "false") ? } else { - write!(writer, "({}", prd_info[prd].name) ? ; + if ! args.is_empty() { + write!(writer, "(") ? + } + write!(writer, "{}", prd_info[prd].name) ? ; for arg in args.iter() { write!(writer, " ") ? ; arg.write(writer, |w, var| var.default_write(w)) ? } - write!(writer, ")") ? + if ! args.is_empty() { + write!(writer, ")") ? + } } } else { write!(writer, "false") ? diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 197bab59..eb7925ea 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -840,6 +840,25 @@ impl Instance { Ok(()) } + /// Mutable accessor for side clauses. + pub fn side_clauses_retain( + & mut self, mut keep: Keep + ) -> Res + where Keep: FnMut(& mut Clause) -> Res { + let mut info = RedInfo::new() ; + let mut cnt = 0 ; + while cnt < self.side_clauses.len() { + if ! keep(& mut self.side_clauses[cnt]) ? { + info.clauses_rmed += 1 ; + self.side_clauses.swap_remove(cnt) ; + () + } else { + cnt += 1 + } + } + Ok(info) + } + /// Asserts all the side-clauses in a solver. pub fn assert_side_clauses

( & self, solver: & mut Solver

@@ -883,7 +902,10 @@ impl Instance { } if clause.lhs_preds().is_empty() - && clause.rhs().is_none() { + && clause.rhs().is_none() + && clause.lhs_terms().iter().any( + |term| term.has_fun_app_or_adt() + ) { self.add_side_clause(clause) ? ; return Ok(None) } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index e894bedc..771313cf 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -31,7 +31,7 @@ pub struct PreInstance<'a> { impl<'a> PreInstance<'a> { /// Constructor. pub fn new(instance: & 'a mut Instance) -> Res { - let solver = conf.solver.preproc_spawn("preproc", (), &* instance) ? ; + let solver = conf.solver.spawn("preproc", (), &* instance) ? ; let simplifier = ClauseSimplifier::new() ; let clauses_to_simplify = Vec::with_capacity(7) ; @@ -251,6 +251,18 @@ impl<'a> PreInstance<'a> { info += self.force_trivial() ? ; + // Check side-clauses. + let instance = & mut self.instance ; + let solver = & mut self.solver ; + info += instance.side_clauses_retain( + |clause| { + match solver.is_clause_trivial(clause) ? { + None => bail!( ErrorKind::Unsat ), + Some(is_trivial) => Ok(is_trivial), + } + } + ) ? ; + Ok(info) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index afe8d581..c39e9ed6 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -794,10 +794,22 @@ impl<'cxt, 's> Parser<'cxt, 's> { } + /// Tries to parse a sort. + pub fn sort_opt(& mut self) -> Res> { + let start_pos = self.pos() ; + let res = self.internal_sort_opt() ; + if let Ok( Some(typ) ) = & res { + typ.check().chain_err( + || self.error(start_pos, "") + ) ? + } + res + } + /// Tries to parse a sort. - pub fn sort_opt(& mut self) -> Res> { + pub fn internal_sort_opt(& mut self) -> Res> { // Compound type under construction. // // The position is always that of the opening paren of the type. @@ -878,6 +890,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { } 'go_up: loop { + if let Some(typ) = & typ { + typ.check().chain_err( + || self.error(start_pos, "while parsing this sort") + ) ? + } match stack.pop() { @@ -975,7 +992,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { } - /// Tries to parse a sort. pub fn nu_sort_opt( & mut self, type_params: & BTreeMap<& 's str, dtyp::TPrmIdx> @@ -1063,6 +1079,17 @@ impl<'cxt, 's> Parser<'cxt, 's> { } 'go_up: loop { + // if let Some(typ) = & typ { + // if let Err((_, err)) = typ.check() { + // let err: Error = err.into() ; + + // bail!( + // err.chain_err( + // || self.error(start_pos, "while parsing this sort") + // ) + // ) + // } + // } match stack.pop() { @@ -1240,6 +1267,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? ; let mut fun = RFun::new(name, args, typ) ; + fun::register_dec( fun.clone() ) ? ; // Check this is the first time we see this function and populate // dependencies. @@ -1309,6 +1337,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) } + let _ = fun::retrieve_dec(& fun.name) ? ; + fun::mk(fun).chain_err( || self.error(pos, "while registering this function") ) ? ; @@ -2614,15 +2644,51 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else if frame.is_cast() { // Cast expect a type after the term being cast. - let _sort = self.sort().chain_err( - || "in term casting, after term" + let sort = self.sort().chain_err( + || "expected sort" + ).chain_err( + || self.error(frame.op_pos, "in this cast") ) ? ; - bail!( - self.error( - frame.op_pos, "casts are not supported" + self.ws_cmt() ; + self.tag(")") ? ; + + self.ws_cmt() ; + self.close_let_bindings( frame.let_count() ) ? ; + + if frame.args.len() != 1 { + bail!( + self.error( + frame.op_pos, format!( + "ill-formed cast: expected one term, found {}", + frame.args.len() + ) + ) ) - ) + } + + let (sub_term, pos) = ( + frame.args.pop().unwrap(), + frame.args_pos.pop().unwrap() + ) ; + + if let Some(typ) = sub_term.typ().merge( & sort ) { + if let Some(nu_term) = sub_term.force_dtyp(typ) { + term = nu_term + } else { + term = sub_term + } + } else { + bail!( + self.error( + pos, format!( + "cannot cast `{}` to `{}`", sub_term.typ(), sort + ) + ) + ) + } + + continue 'go_up } else { // Keep on parsing terms. diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index dc1486e4..9c4f0194 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -185,6 +185,9 @@ pub struct Teacher<'a> { /// Map from /// Number of guesses. count: usize, + + /// True if some recursive functions are defined. + using_rec_funs: bool, } impl<'a> Teacher<'a> { @@ -237,6 +240,12 @@ impl<'a> Teacher<'a> { None } ; + let mut using_rec_funs = false ; + + fun::iter( + |_| { using_rec_funs = true ; Ok(()) } + ) ? ; + Ok( Teacher { solver, instance, data, from_learners, @@ -244,7 +253,7 @@ impl<'a> Teacher<'a> { _profiler: profiler, partial_model, count: 0, tru_preds: PrdSet::new(), fls_preds: PrdSet::new(), clauses_to_ignore: ClsSet::new(), - bias: CexBias::new(), + bias: CexBias::new(), using_rec_funs, } ) } @@ -1050,8 +1059,17 @@ impl<'a> Teacher<'a> { pub fn get_cex( & mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool ) -> Res< Vec > { + let mut cexs = vec![] ; + log! { @debug "working on clause #{}", clause_idx } + if self.using_rec_funs { + let falsifiable = self.quantified_checksat(clause_idx) ? ; + if ! falsifiable { + return Ok(cexs) + } + } + // Macro to avoid borrowing `self.instance`. macro_rules! clause { () => ( & self.instance[clause_idx] ) ; @@ -1076,8 +1094,6 @@ impl<'a> Teacher<'a> { ) ? ; profile!{ self mark "cexs", "prep" } - let mut cexs = vec![] ; - macro_rules! get_cex { () => ( // Normal check, no actlit. @@ -1132,4 +1148,31 @@ impl<'a> Teacher<'a> { Ok(cexs) } + + /// Checks a clause using qualifiers. + /// + /// Returns `true` if the clause is falsifiable with the current candidates. + /// + /// This is used when manipulating recursive ADTs and functions. + pub fn quantified_checksat( + & mut self, clause: ClsIdx + ) -> Res { + let actlit = self.solver.get_actlit() ? ; + let wrapped = smt::SmtQClause::new( & self.instance[clause] ) ; + + self.solver.assert_act_with( + & actlit, & wrapped, & ( + & self.tru_preds, & self.fls_preds, self.instance.preds() + ) + ) ? ; + + let falsifiable = ! self.solver.check_sat_act( + Some(& actlit) + ) ? ; + + self.solver.de_actlit(actlit) ? ; + + Ok(falsifiable) + } + } \ No newline at end of file diff --git a/src/term/eval.rs b/src/term/eval.rs index 2604315e..77ea5924 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -27,11 +27,13 @@ pub fn eval(term: & Term, model: & E) -> Res { let mut fun_ref_count = 0 ; + let res = zip( term, |zip_null| leaf(model, zip_null), |op, typ, values| total(op, typ, values, & mut fun_ref_count), partial ) ; + fun::decrease_ref_count(fun_ref_count) ; // if let Ok(res) = res.as_ref() { // if model.len() > 0 @@ -190,7 +192,7 @@ fn partial<'a>( thing @ ZipOp::CArray | thing @ ZipOp::Slc(_) => { let nu_term = rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args`" + "illegal call to `partial_op`: empty `rgt_args` (eval::partial)" ) ; Ok( ZipDo::Trm { @@ -320,16 +322,23 @@ fn partial_op<'a>( } // Normal exit. - let nu_term = rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args`" - ) ; - Ok( - ZipDo::Trm { - nu_term, frame: Frame { - thing: ZipOp::Op(op), typ, lft_args, rgt_args + if let Some(nu_term) = rgt_args.next() { + Ok( + ZipDo::Trm { + nu_term, frame: Frame { + thing: ZipOp::Op(op), typ, lft_args, rgt_args + } } + ) + } else { + log!(@4 "{}", op) ; + for arg in & lft_args { + log!(@4 " {}", arg) } - ) + panic!( + "illegal call to `partial_op`: empty `rgt_args` (partial_op)" + ) + } } diff --git a/src/term/factory.rs b/src/term/factory.rs index 84d4aa1c..9e9bdbec 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -234,8 +234,32 @@ pub fn select(array: Term, idx: Term) -> Term { } /// Function application. -#[inline] -pub fn fun(typ: Typ, name: String, args: Vec) -> Term { +/// +/// # Panics +/// +/// - if the function does not exist +/// - if the type does not make sense +/// - if the arguments are illegal +#[inline] +pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { + if let Err(e) = fun::dec_do( + & name, |fun| { + debug_assert_eq! { typ, fun.typ } + if args.len() != fun.sig.len() { + panic!("illegal application of function {}", conf.bad(& name)) + } + for (info, arg) in fun.sig.iter().zip( args.iter_mut() ) { + if let Some(nu_arg) = arg.force_dtyp( info.typ.clone() ) { + * arg = nu_arg + } + } + Ok(()) + } + ) { + print_err(& e) ; + panic!("illegal function application") + } + factory.mk( RTerm::Fun { typ, name, args } ) } @@ -288,9 +312,47 @@ pub fn val(val: Val) -> Term { } +/// Tries to create a constant datatype constructor. +fn cst_dtyp_new( + typ: Typ, name: String, args: Vec +) -> Either)> { + if args.is_empty() { + return Either::Left( + val::dtyp_new( typ, name, vec![] ) + ) + } + + let mut nu_args = None ; + + for arg in & args { + if let Some(val) = arg.val() { + nu_args.get_or_insert_with( + || Vec::with_capacity( args.len() ) + ).push(val) + } else { + nu_args = None ; + break + } + } + + if let Some(args) = nu_args { + Either::Left( val::dtyp_new(typ, name, args) ) + } else { + Either::Right( (typ, name, args) ) + } +} + + /// Creates a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { - if let Some((dtyp, _)) = typ.dtyp_inspect() { + let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { + Either::Left(val) => { + return cst(val) + }, + Either::Right(stuff) => stuff, + } ; + + if let Some((dtyp, prms)) = typ.dtyp_inspect() { if let Some(fargs) = dtyp.news.get(& name) { if args.len() != fargs.len() { panic!( @@ -299,6 +361,17 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { fargs.len(), args.len() ) } + + for (arg, param) in args.iter_mut().zip( fargs.iter() ) { + let typ = param.1.to_type(prms).unwrap_or_else( + |_| panic!("ill-formed datatype constructor: {}", typ) + ) ; + if let Some(typ) = typ.merge( & arg.typ() ) { + if let Some(nu_arg) = arg.force_dtyp(typ) { + * arg = nu_arg + } + } + } } else { panic!( "datatype `{}` has no constructor named `{}`", @@ -330,6 +403,9 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { debug_assert_eq! { vals.len(), args.len() } val( val::dtyp_new(typ, name, vals) ) } else { + if args.is_empty() { + panic!("aaaaaa") + } factory.mk( RTerm::DTypNew { typ, name, args } ) } } diff --git a/src/term/mod.rs b/src/term/mod.rs index f068372f..dad7d363 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -840,7 +840,8 @@ impl RTerm { ZipFrame { thing: ZipOp::Slc(_), .. } => Err(()), mut frame => { let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args`" + "illegal call to `partial_op`: + empty `rgt_args` (has_fun_app_or_adt)" ) ; Ok( ZipDo::Trm { nu_term, frame } ) }, @@ -920,7 +921,7 @@ impl RTerm { |mut frame| { let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args`" + "illegal call to `partial_op`: empty `rgt_args` (subst_custom)" ) ; Ok( ZipDo::Trm { nu_term, frame } ) }, diff --git a/src/term/op.rs b/src/term/op.rs index d161485e..f598f468 100644 --- a/src/term/op.rs +++ b/src/term/op.rs @@ -195,11 +195,13 @@ impl Op { typ = merged_typ.clone() ; * fst = nu ; // println!(" -> {}", fst) + // println!("{}: {}", fst, fst.typ()) ; } if let Some(nu) = next.force_dtyp( merged_typ ) { // println!(" -> {} {}", nu, nu.typ()) ; * next = nu ; // println!(" {} {}", next, next.typ()) + // println!("{}: {}", next, next.typ()) ; } } else { err!(lft Some(typ), next.typ(), index) diff --git a/src/term/typ.rs b/src/term/typ.rs index bf39f1ed..524baee2 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -166,6 +166,21 @@ impl RTyp { } } + /// Checks a type is legal. + pub fn check(& self) -> Res<()> { + match self { + RTyp::DTyp { dtyp, prms } => if dtyp.prms.len() != prms.len() { + bail!( + "datatype {} expects {} parameters, found {}", + conf.emph(& dtyp.name), dtyp.prms.len(), prms.len() + ) + }, + RTyp::Unk | RTyp::Array { .. } | + RTyp::Int | RTyp::Real | RTyp::Bool => (), + } + Ok(()) + } + /// True if the types are compatible. /// /// Two types are compatible if they're the same except for unknown subtypes. @@ -263,9 +278,7 @@ impl RTyp { ( RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, - ) => if dtyp_1.name != dtyp_2.name { - return None - } else { + ) => if dtyp_1.name == dtyp_2.name && prms_1.len() == prms_2.len() { debug_assert_eq! { prms_1.len(), prms_2.len() } let mut prms = prms_1.iter().zip( prms_2.iter() ) ; @@ -284,6 +297,8 @@ impl RTyp { } else { lft.clone() } + } else { + return None }, (RTyp::Int, _) | diff --git a/src/term/zip.rs b/src/term/zip.rs index 62aaa51d..2dfb997d 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -4,6 +4,7 @@ //! //! - explain +use std::fmt ; use std::slice::Iter ; use common::* ; @@ -111,6 +112,14 @@ pub enum ZipNullary<'a> { /// A variable. Var(& 'a Typ, VarIdx), } +impl<'a> fmt::Display for ZipNullary<'a> { + fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result { + match self { + ZipNullary::Cst(val) => write!(fmt, "{}", val), + ZipNullary::Var(typ, var) => write!(fmt, "v_{}<{}>", var ,typ), + } + } +} /// A frame in the zipper. diff --git a/src/val/mod.rs b/src/val/mod.rs index 1b028f19..fc6ef4f1 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -1494,7 +1494,11 @@ impl_fmt!{ RVal::DTypNew { ref name, ref args, ref typ } => if args.is_empty() { - write!(fmt, "(as {} {})", name, typ) ? + if typ.has_unk() { + write!(fmt, "{}", name) ? + } else { + write!(fmt, "(as {} {})", name, typ) ? + } } else { write!(fmt, "({}", name) ? ; stack.push( Either::Right(()) ) ; From 7099a25ec83111c34761fb73c6a4d16744cacb23 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 10 Aug 2018 17:35:58 +0900 Subject: [PATCH 31/94] tweaks and fixes --- src/common/config.rs | 2 +- src/common/smt.rs | 6 +++++- src/learning/ice/synth/adt.rs | 2 ++ src/teacher/cex_bias.rs | 4 ++++ src/teacher/mod.rs | 21 +++++++++++++++------ 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/common/config.rs b/src/common/config.rs index 322a54fd..ad47ce10 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -1021,7 +1021,7 @@ impl TeacherConf { ).value_name( bool_format ).default_value( - "off" + "on" ).takes_value(true).number_of_values(1).display_order( order() ) ).arg( diff --git a/src/common/smt.rs b/src/common/smt.rs index 6530b6dd..61c19ffa 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -113,7 +113,7 @@ impl<'a> Expr2Smt<()> for SmtSideClause<'a> { } -/// Smt-prints a clause with its quantifiers, under an actlit. +/// Smt-prints a clause with its quantifiers, negated. pub struct SmtQClause<'a> { /// The clause. pub clause: & 'a Clause, @@ -135,6 +135,8 @@ impl<'a, 'b> Expr2Smt< ref true_preds, ref false_preds, ref prd_info ) = * info ; + write!(w, "(not ") ? ; + self.clause.naked_write( w, |w, prd, args| { if true_preds.contains(& prd) { @@ -160,6 +162,8 @@ impl<'a, 'b> Expr2Smt< } ) ? ; + write!(w, ")") ? ; + Ok(()) } } diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs index 030c4ce8..8d5054cb 100644 --- a/src/learning/ice/synth/adt.rs +++ b/src/learning/ice/synth/adt.rs @@ -69,7 +69,9 @@ impl TheoSynth for AdtSynth { fn project( & self, sample: & VarVals, typ: & Typ, map: & mut TermVals ) -> Res<()> { + // println!("projecting {} to {}", self.typ, typ) ; for fun in & self.funs.from_typ { + // println!("> {} (-> {})", fun.name, fun.typ) ; if & fun.typ != typ { continue diff --git a/src/teacher/cex_bias.rs b/src/teacher/cex_bias.rs index a62f1e84..f67ee784 100644 --- a/src/teacher/cex_bias.rs +++ b/src/teacher/cex_bias.rs @@ -44,6 +44,10 @@ impl CexBias { clause: ClsIdx, instance: & Instance, data: & Data, bias_only: bool, ) -> Res< Vec<(Actlit, Bias)> > { + if ! conf.teacher.bias_cexs { + return Ok( vec![] ) + } + macro_rules! clause { () => ( instance[clause] ) ; } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 9c4f0194..c5e3fd00 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -188,6 +188,8 @@ pub struct Teacher<'a> { /// True if some recursive functions are defined. using_rec_funs: bool, + /// Forces to restart the solver after each check. + restart_on_cex: bool, } impl<'a> Teacher<'a> { @@ -246,6 +248,12 @@ impl<'a> Teacher<'a> { |_| { using_rec_funs = true ; Ok(()) } ) ? ; + let restart_on_cex = + conf.teacher.restart_on_cex || + ! dtyp::get_all().is_empty() || + using_rec_funs + ; + Ok( Teacher { solver, instance, data, from_learners, @@ -253,7 +261,8 @@ impl<'a> Teacher<'a> { _profiler: profiler, partial_model, count: 0, tru_preds: PrdSet::new(), fls_preds: PrdSet::new(), clauses_to_ignore: ClsSet::new(), - bias: CexBias::new(), using_rec_funs, + bias: CexBias::new(), + using_rec_funs, restart_on_cex } ) } @@ -843,7 +852,7 @@ impl<'a> Teacher<'a> { let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ; - if ! conf.teacher.restart_on_cex { + if ! self.restart_on_cex { self.solver.push(1) ? ; self.define_preds(cands) ? } @@ -918,7 +927,7 @@ impl<'a> Teacher<'a> { // } if self.count % 100 == 0 - || conf.teacher.restart_on_cex { + || self.restart_on_cex { smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.pop(1) ? @@ -935,7 +944,7 @@ impl<'a> Teacher<'a> { clause: ClsIdx, map: & mut ClsHMap>, bias: bool, ) -> Res<()> { if ! self.clauses_to_ignore.contains(& clause) { - if conf.teacher.restart_on_cex { + if self.restart_on_cex { self.define_preds(cands) ? } else { self.solver.push(1) ? @@ -949,7 +958,7 @@ impl<'a> Teacher<'a> { || format!("while getting counterexample for clause #{}", clause) ) ? ; - if conf.teacher.restart_on_cex { + if self.restart_on_cex { smt::reset(& mut self.solver, & self.instance) ? } else { self.solver.pop(1) ? @@ -1166,7 +1175,7 @@ impl<'a> Teacher<'a> { ) ) ? ; - let falsifiable = ! self.solver.check_sat_act( + let falsifiable = self.solver.check_sat_act( Some(& actlit) ) ? ; From ae98043fbdb4ae99f9c9924e7602999448c6d040 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 16 Aug 2018 19:28:45 +0900 Subject: [PATCH 32/94] partially broken build --- Cargo.lock | 36 +-- src/common/consts.rs | 6 +- src/common/smt.rs | 124 +++++++--- src/dtyp/mod.rs | 26 +- src/instance/instance/clause.rs | 290 ++++++++++++++++++----- src/instance/instance/pre_instance.rs | 40 +++- src/parse/mod.rs | 86 ++++++- src/teacher/mod.rs | 54 +++-- src/term/eval.rs | 39 ++- src/term/factory.rs | 33 ++- src/term/leaf_iter.rs | 7 +- src/term/mod.rs | 108 ++++++++- src/term/simplify.rs | 5 +- src/term/zip.rs | 20 ++ src/unsat_core/mod.rs | 2 + src/unsat_core/sample_graph.rs | 326 +++++++++++++------------- src/val/mod.rs | 9 +- 17 files changed, 877 insertions(+), 334 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b165d270..612b14fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -21,20 +21,20 @@ name = "backtrace" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -49,7 +49,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -122,7 +122,7 @@ dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)", ] @@ -131,7 +131,7 @@ name = "isatty" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.42" +version = "0.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -221,12 +221,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rand" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -252,7 +252,7 @@ dependencies = [ [[package]] name = "rsmt2" version = "0.9.10" -source = "git+https://github.com/kino-mc/rsmt2#efcb77ca6cba0002adcb07d8347533fdabc3cc5a" +source = "git+https://github.com/kino-mc/rsmt2#78f86f9b3c8c051fdf1c85f6f00269294202f443" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -272,7 +272,7 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -323,10 +323,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" +"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" -"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" @@ -336,7 +336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c241e038569e815af4c1153387e239dcfe30ae8adf77b39a08f4ea0a829b0c" "checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" -"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)" = "" "checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" "checksum num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e" @@ -345,7 +345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" -"checksum rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "12397506224b2f93e6664ffc4f664b29be8208e5157d3d90b44f09b5fae470ea" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" diff --git a/src/common/consts.rs b/src/common/consts.rs index fb7e0a2f..3aa6bb88 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -137,12 +137,16 @@ pub mod keywords { to_int_ ("to_int", doc = "Conversion from `Real` to `Int`.") to_real_ ("to_real", doc = "Conversion from `Int` to `Real`.") - as_ ("as", doc = "As.") + as_ ("as", doc = "Cast operator.") + is_ ("is", doc = "Datatype tester.") + const_ ("const", doc = "Constant cast.") store_ ("store", doc = "Updater for arrays.") select_ ("select", doc = "Accessor for arrays.") match_ ("match", doc = "Match operator.") + + lambda_ ("_", doc = "Lambda abstraction.") } } diff --git a/src/common/smt.rs b/src/common/smt.rs index 61c19ffa..bf48dc4e 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -135,7 +135,7 @@ impl<'a, 'b> Expr2Smt< ref true_preds, ref false_preds, ref prd_info ) = * info ; - write!(w, "(not ") ? ; + writeln!(w, "(not") ? ; self.clause.naked_write( w, |w, prd, args| { @@ -159,10 +159,10 @@ impl<'a, 'b> Expr2Smt< } Ok(()) } - } + }, 6 ) ? ; - write!(w, ")") ? ; + write!(w, " )") ? ; Ok(()) } @@ -170,40 +170,76 @@ impl<'a, 'b> Expr2Smt< /// SMT-prints a collection of terms as a conjunction with default var writer. -pub struct SmtConj { +pub struct SmtConj<'a, Trms> { /// Conjunction. terms: Trms, + /// True if the terms have function applications. + has_fun_apps: bool, + /// Variable informations. + infos: & 'a VarInfos } -impl<'a, Trms> SmtConj +impl<'a, 'b, Trms> SmtConj<'b, Trms> where Trms: Iterator + ExactSizeIterator + Clone { /// Constructor. - pub fn new(terms: IntoIter) -> Self + pub fn new(terms: IntoIter, infos: & 'b VarInfos) -> Self where IntoIter: IntoIterator { - SmtConj { terms: terms.into_iter() } + let terms = terms.into_iter() ; + let mut has_fun_apps = false ; + for term in terms.clone() { + if term.has_fun_apps() { + has_fun_apps = true ; + break + } + } + SmtConj { terms, has_fun_apps, infos } } /// Checks if this conjunction is unsatisfiable. pub fn is_unsat( - & self, solver: & mut Solver, vars: & VarInfos + & self, solver: & mut Solver, actlit: Option<& Actlit> ) -> Res { if self.terms.len() == 0 { return Ok(false) } solver.push(1) ? ; - for var in vars { - if var.active { - solver.declare_const(& var.idx, var.typ.get()) ? + if ! self.has_fun_apps { + for var in self.infos { + if var.active { + solver.declare_const(& var.idx, var.typ.get()) ? + } } } solver.assert( self ) ? ; - let sat = solver.check_sat() ? ; + let sat = solver.check_sat_act(actlit) ? ; solver.pop(1) ? ; Ok(! sat) } } -impl<'a, Trms> Expr2Smt<()> for SmtConj + +impl<'a, 'b, Trms> Expr2Smt<()> for SmtConj<'b, Trms> where Trms: Iterator + ExactSizeIterator + Clone { fn expr_to_smt2( & self, w: & mut Writer, _: () ) -> SmtRes<()> { + let suffix = if self.has_fun_apps { + write!(w, "(exists (") ? ; + let mut inactive = 0 ; + for var in self.infos { + if var.active { + write!(w, " (") ? ; + var.idx.default_write(w) ? ; + write!(w, " {})", var.typ) ? + } else { + inactive += 1 + } + } + if inactive == self.infos.len() { + write!(w, " (unused Bool)") ? + } + write!(w, " ) ") ? ; + ")" + } else { + "" + } ; + if self.terms.len() == 0 { write!(w, "true") ? } else { @@ -216,6 +252,7 @@ where Trms: Iterator + ExactSizeIterator + Clone { } write!(w, ")") ? } + write!(w, "{}", suffix) ? ; Ok(()) } } @@ -861,38 +898,53 @@ impl ClauseTrivialExt for Solver { } } - let conj = SmtConj::new( lhs.iter() ) ; + let mut actlit = None ; - if clause.rhs().is_none() && clause.lhs_preds().is_empty() { + let res = { - // Either it is trivial, or falsifiable regardless of the predicates. - if conj.is_unsat( - self, clause.vars() - ) ? { - Ok( Some(true) ) - } else { - Ok(None) - } + let conj = SmtConj::new( lhs.iter(), & clause.vars ) ; - } else { + if clause.rhs().is_none() && clause.lhs_preds().is_empty() { + if conj.has_fun_apps { + actlit = Some( self.get_actlit() ? ) + } - if let Some((pred, args)) = clause.rhs() { - if clause.lhs_preds().get(& pred).map( - |set| set.contains(args) - ).unwrap_or(false) { - return Ok( Some(true) ) + // Either it is trivial, or falsifiable regardless of the predicates. + if conj.is_unsat( self, actlit.as_ref() ) ? { + Ok( Some(true) ) + } else { + Ok(None) } - } - if lhs.is_empty() { - Ok( Some(false) ) } else { - clause.lhs_terms_checked() ; - conj.is_unsat( - self, clause.vars() - ).map(Some) + + if let Some((pred, args)) = clause.rhs() { + if clause.lhs_preds().get(& pred).map( + |set| set.contains(args) + ).unwrap_or(false) { + return Ok( Some(true) ) + } + } + + if lhs.is_empty() { + Ok( Some(false) ) + } else { + + if conj.has_fun_apps { + actlit = Some( self.get_actlit() ? ) + } + conj.is_unsat( self, actlit.as_ref() ).map(Some) + } + } + } ; + if let Some(actlit) = actlit { + self.de_actlit(actlit) ? } + + clause.lhs_terms_checked() ; + + res } } diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 65897737..026740d6 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -36,7 +36,6 @@ impl From for PartialTyp { } impl PartialTyp { - /// True if the type mentions the datatype provided. pub fn mentions_dtyp( & self, dtyp_name: & str @@ -133,6 +132,8 @@ impl PartialTyp { } + + fn write( & self, w: & mut W, prms: & TPrmMap ) -> ::std::io::Result<()> { @@ -279,7 +280,6 @@ impl PartialTyp { } } - } impl_fmt! { @@ -686,6 +686,28 @@ pub fn type_selector( +/// Types a datatype tester application. +pub fn type_tester( + tester: & str, tst_pos: ::parse::Pos, term: & Term +) -> Result<(), (::parse::Pos, String)> { + if let Some((dtyp, _)) = term.typ().dtyp_inspect() { + if dtyp.news.contains_key(tester) { + return Ok(()) + } + } + + Err( + ( + tst_pos, format!( + "cannot apply tester `{}` to term of type {}, no such constructor", + conf.bad(tester), term.typ() + ) + ) + ) +} + + + diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 4a5eb024..f696ab60 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -18,6 +18,7 @@ pub fn new( vars, lhs_terms, lhs_preds, rhs, terms_changed: true, preds_changed: true, from_unrolling: false, info, from, + has_fun_apps: false, } ; for tterm in lhs { clause.lhs_insert(tterm) ; } clause @@ -57,6 +58,74 @@ pub struct Clause { /// Index of the original clause this comes from. from: ClsIdx, + /// True if the clause contains function applications. + has_fun_apps: bool, +} + +/// Updates the `has_fun_apps` flag. +/// +/// Doesn't do anything if `has_fun_apps` is already false. +macro_rules! update_has_fun_apps { + ($slf:expr) => ( + if $slf.has_fun_apps { + $slf.has_fun_apps = false ; + update_has_fun_apps!(@ $slf, (terms, lhs preds, rhs)) + } + ) ; + ($slf:expr, $tail:tt) => ( + if $slf.has_fun_apps { + $slf.has_fun_apps = false ; + update_has_fun_apps!(@ $slf, $tail) + } + ) ; + + (@ $slf:expr, (, $($tail:tt)*)) => ( + update_has_fun_apps!($slf, ($($tail)*)) + ) ; + + (@ $slf:expr, (terms $($tail:tt)*)) => ({ + if ! $slf.has_fun_apps { + for term in & $slf.lhs_terms { + if term.has_fun_apps() { + $slf.has_fun_apps = true ; + break + } + } + } + update_has_fun_apps!($slf, ($($tail)*)) + }) ; + + (@ $slf:expr, (lhs preds $($tail:tt)*)) => ({ + if ! $slf.has_fun_apps { + for argss in $slf.lhs_preds.values() { + for args in argss { + for arg in args.iter() { + if arg.has_fun_apps() { + $slf.has_fun_apps = true ; + break + } + } + } + } + } + update_has_fun_apps!($slf, ($($tail)*)) + }) ; + + (@ $slf:expr, (rhs $($tail:tt)*)) => ({ + if ! $slf.has_fun_apps { + if let Some((_, args)) = $slf.rhs.as_ref() { + for arg in args.iter() { + if arg.has_fun_apps() { + $slf.has_fun_apps = true ; + break + } + } + } + } + update_has_fun_apps!($slf, ($($tail)*)) + }) ; + + (@ $slf:expr, ()) => (()) ; } @@ -77,6 +146,7 @@ impl Clause { self.shrink_vars() } + /// Inserts a top term in the lhs. /// /// Returns true if it was not there (`is_new`). @@ -98,6 +168,8 @@ impl Clause { } } + + /// Removes all predicate application of `pred` in the LHS. /// /// Returns true if the predicate application was there. @@ -107,6 +179,7 @@ impl Clause { ) -> Option< VarTermsSet > { let res = self.lhs_preds.remove(& pred) ; if res.is_some() { + update_has_fun_apps!(self) ; self.preds_changed = true ; if self.lhs_preds.is_empty() && self.rhs.is_none() { @@ -123,6 +196,15 @@ impl Clause { pub fn insert_pred_app( & mut self, pred: PrdIdx, args: VarTerms ) -> bool { + if ! self.has_fun_apps { + for arg in args.iter() { + if arg.has_fun_apps() { + self.has_fun_apps = true ; + break + } + } + } + let is_new = self.lhs_preds.insert_pred_app(pred, args) ; self.preds_changed = self.preds_changed || is_new ; is_new @@ -132,6 +214,9 @@ impl Clause { pub fn insert_term( & mut self, term: Term ) -> bool { + if ! self.has_fun_apps && term.has_fun_apps() { + self.has_fun_apps = true + } let is_new = Self::lhs_insert_term(& mut self.lhs_terms, term) ; self.terms_changed = self.terms_changed || is_new ; is_new @@ -140,6 +225,7 @@ impl Clause { /// Removes a term from the LHS. pub fn rm_term(& mut self, term: & Term) -> bool { let was_there = self.lhs_terms.remove(term) ; + update_has_fun_apps!(self) ; self.terms_changed = self.terms_changed || was_there ; was_there } @@ -151,6 +237,9 @@ impl Clause { ) -> ::std::collections::hash_map::Drain { self.terms_changed = self.terms_changed || self.rhs.is_none() ; self.preds_changed = true ; + + update_has_fun_apps!(self, (terms, rhs)) ; + self.lhs_preds.drain() } @@ -159,13 +248,19 @@ impl Clause { pub fn lhs_map_args_of( & mut self, pred: PrdIdx, mut f: F ) where F: FnMut(& VarTerms) -> VarTerms { - if let Some(argss) = self.lhs_preds.get_mut(& pred) { + let changed = if let Some(argss) = self.lhs_preds.get_mut(& pred) { self.preds_changed = true ; let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; for args in argss.iter() { nu_argss.insert( f(args) ) ; } - ::std::mem::swap( & mut nu_argss, argss ) + ::std::mem::swap( & mut nu_argss, argss ) ; + true + } else { + false + } ; + if changed { + update_has_fun_apps!(self) } } @@ -173,11 +268,19 @@ impl Clause { #[inline] pub fn rhs_map_args(& mut self, mut f: F) where F: FnMut(PrdIdx, & VarTerms) -> (PrdIdx, VarTerms) { - if let Some(& mut (ref mut pred, ref mut args)) = self.rhs.as_mut() { + let changed = if let Some( + & mut (ref mut pred, ref mut args) + ) = self.rhs.as_mut() { self.preds_changed = true ; let (nu_pred, mut nu_args) = f(* pred, args) ; * args = nu_args ; - * pred = nu_pred + * pred = nu_pred ; + true + } else { + false + } ; + if changed { + update_has_fun_apps!(self) } } @@ -192,17 +295,24 @@ impl Clause { old_rhs.is_some() && self.lhs_preds.is_empty() ) ; self.preds_changed = self.preds_changed || old_rhs.is_some() ; + if old_rhs.is_some() { + update_has_fun_apps!(self) + } old_rhs } /// Forces the RHS of a clause. #[inline] + #[cfg_attr( + feature = "cargo-clippy", allow(block_in_if_condition_stmt) + )] pub fn set_rhs(& mut self, pred: PrdIdx, args: VarTerms) -> Res<()> { - let mut vars = VarSet::new() ; - for arg in args.iter() { - term::map_vars(arg, |v| { vars.insert(v) ; () }) - } debug_assert! {{ + let mut vars = VarSet::new() ; + + for arg in args.iter() { + term::map_vars(arg, |v| { vars.insert(v) ; () }) + } for var in vars { if var >= self.vars.len() { err_chain! { @@ -224,8 +334,10 @@ impl Clause { } true }} + self.rhs = Some((pred, args)) ; self.preds_changed = true ; + update_has_fun_apps!(self) ; Ok(()) } @@ -251,7 +363,7 @@ impl Clause { self.preds_changed || was_there ) ; - Clause { + let mut clause = Clause { vars: self.vars.clone(), lhs_terms: self.lhs_terms.clone(), lhs_preds, rhs: self.rhs.clone(), @@ -259,7 +371,12 @@ impl Clause { from_unrolling: self.from_unrolling, info, from: self.from, - } + has_fun_apps: self.has_fun_apps, + } ; + + update_has_fun_apps!(clause) ; + + clause } /// Clones a clause but changes the rhs. @@ -292,7 +409,7 @@ impl Clause { ), } ; - Clause { + let mut clause = Clause { vars: self.vars.clone(), lhs_terms, lhs_preds: self.lhs_preds.clone(), @@ -300,12 +417,21 @@ impl Clause { terms_changed, preds_changed, from_unrolling: self.from_unrolling, info, - from: self.from - } + from: self.from, + has_fun_apps: true, + } ; + + update_has_fun_apps!(clause) ; + + clause } /// Removes all redundant terms from `lhs_terms`. + /// + /// # TODO + /// + /// - can be optimized by using `retain` (probably) fn prune(& mut self) { use std::cmp::Ordering::* ; use term::simplify::SimplRes::* ; @@ -313,6 +439,7 @@ impl Clause { let mut to_add = TermSet::new() ; let mut prune_things = true ; + let mut pruned = false ; while prune_things { prune_things = false ; @@ -345,6 +472,8 @@ impl Clause { } } + pruned = pruned || ! to_rm.is_empty() || ! to_add.is_empty() ; + self.terms_changed = self.terms_changed || ! to_rm.is_empty() || ! to_add.is_empty() ; @@ -359,6 +488,10 @@ impl Clause { } } + + if pruned { + update_has_fun_apps!(self) + } } @@ -407,6 +540,7 @@ impl Clause { ) ; if changed { + update_has_fun_apps!(self) ; self.terms_changed = true ; self.preds_changed = true ; self.prune() @@ -795,18 +929,29 @@ impl Clause { & self, w: & mut W, write_prd: WritePrd ) -> IoRes<()> where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - write!(w, "({} ", keywords::cmd::assert) ? ; - self.naked_write(w, write_prd) ? ; + writeln!(w, "({} ", keywords::cmd::assert) ? ; + self.internal_naked_write(w, write_prd, true, 2) ? ; writeln!(w, ")") ? ; Ok(()) } /// Writes a clause without the `assert` around it. pub fn naked_write( - & self, w: & mut W, write_prd: WritePrd + & self, w: & mut W, write_prd: WritePrd, indent: usize ) -> IoRes<()> where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - write!(w, "({}\n (", keywords::forall) ? ; + self.internal_naked_write(w, write_prd, false, indent) + } + + /// Writes a clause without the `assert` around it. + fn internal_naked_write( + & self, w: & mut W, write_prd: WritePrd, info: bool, indent: usize + ) -> IoRes<()> + where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + write!( + w, "{nil: >indent$}({}\n{nil: >indent$} (", + keywords::forall, nil="", indent=indent + ) ? ; let mut inactive = 0 ; for var in & self.vars { @@ -819,64 +964,99 @@ impl Clause { if inactive == self.vars.len() { write!(w, " (unused Bool)") ? } + writeln!(w, " )") ? ; + + if info { + writeln!( + w, "{nil: >indent$} \ + ; {} inactive variable(s)\n{nil: >indent$} \ + ; unroll: {}\n{nil: >indent$} \ + ; terms_changed: {}\n{nil: >indent$} \ + ; preds_changed: {}\n{nil: >indent$} \ + ; created by `{}`\ + ", + inactive, self.from_unrolling, + self.terms_changed, self.preds_changed, self.info, + nil="", indent=indent + ) ? + } - write!(w, " )") ? ; - writeln!( - w, "\n \ - ; {} inactive variable(s)\n \ - ; unroll: {}\n \ - ; terms_changed: {}\n \ - ; preds_changed: {}\n \ - ; created by `{}`\ - ", - inactive, self.from_unrolling, - self.terms_changed, self.preds_changed, self.info - ) ? ; - writeln!(w, " ; from: #{}", self.from) ? ; + self.internal_qf_write(w, write_prd, indent + 2) ? ; - let lhs_len = self.lhs_len() ; + writeln!(w, "{nil: >indent$})", nil="", indent=indent) ? ; - let (pref, suff) = if lhs_len != 0 { - write!(w, " (=>") ? ; - let (pref, suff) = if lhs_len > 1 { - write!(w, "\n (and") ? ; - (" ", Some(" )")) - } else { - (" ", None) - } ; + Ok(()) + } + + + /// Writes a clause without the quantifiers around it. + fn internal_qf_write( + & self, w: & mut W, write_prd: WritePrd, indent: usize + ) -> IoRes<()> + where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + write!( + w, "{nil: >indent$}(=>\n{nil: >indent$} (and\n{nil: >indent$} ", + nil="", indent=indent + ) ? ; + if self.lhs_terms.is_empty() { + write!(w, " true") ? + } else { for term in & self.lhs_terms { - write!(w, "\n{}", pref) ? ; - term.write(w, |w, var| w.write_all( self.vars[var].as_bytes() )) ? + write!(w, " ") ? ; + term.write( + w, + |w, var| w.write_all( self.vars[var].as_bytes() ) + ) ? } + } + + write!(w, "\n{nil: >indent$} ", nil="", indent=indent) ? ; + + if self.lhs_preds.is_empty() { + write!(w, " true") ? + } else { for (pred, argss) in & self.lhs_preds { for args in argss { - write!(w, "\n{}", pref) ? ; + write!(w, " ") ? ; write_prd(w, * pred, args) ? } } + } - writeln!(w) ? ; - if let Some(suff) = suff { - writeln!(w, "{}", suff) ? - } - (" ", Some(" )")) - } else { - (" ", None) - } ; + write!( + w, "\n{nil: >indent$} )\n{nil: >indent$} ", nil="", indent=indent + ) ? ; - write!(w, "{}", pref) ? ; if let Some((pred, ref args)) = self.rhs { write_prd(w, pred, args) ? } else { write!(w, "false") ? } - writeln!(w) ? ; - if let Some(suff) = suff { - writeln!(w, "{}", suff) ? + writeln!( + w, "\n{nil: >indent$})", nil="", indent=indent + ) ? ; + + Ok(()) + } + + + + /// Asserts a clause. + pub fn assert( + & self, solver: Solver

, write_pred: WritePrd, need_model: bool + ) -> Res<()> + where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + + if ! need_model && self.has_fun_apps { + + } else { + } - write!(w, ")") + + Ok(()) } + } impl ::std::ops::Index for Clause { diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index 771313cf..c384d358 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -10,6 +10,29 @@ use instance::{ } ; + +/// Performs a checksat. +macro_rules! check_sat { + ($pre_instance:expr) => ({ + let actlit = if $pre_instance.use_actlits { + Some( $pre_instance.solver.get_actlit() ? ) + } else { + None + } ; + + let sat = $pre_instance.solver.check_sat_act( actlit.as_ref() ) ? ; + + if let Some(actlit) = actlit { + $pre_instance.solver.de_actlit(actlit) ? + } + + sat + }) ; +} + + + + /// Wraps an instance for pre-processing. pub struct PreInstance<'a> { /// The instance wrapped. @@ -27,6 +50,9 @@ pub struct PreInstance<'a> { /// Term extraction context. extraction: ExtractionCxt, + + /// Use actlits in checksats. + use_actlits: bool, } impl<'a> PreInstance<'a> { /// Constructor. @@ -36,12 +62,19 @@ impl<'a> PreInstance<'a> { let simplifier = ClauseSimplifier::new() ; let clauses_to_simplify = Vec::with_capacity(7) ; + let mut use_actlits = false ; + + fun::iter( + |_| { use_actlits = true ; Ok(()) } + ) ? ; + Ok( PreInstance { instance, solver, simplifier, clauses_to_simplify, vars: VarSet::new(), extraction: ExtractionCxt::new(), + use_actlits, } ) } @@ -253,7 +286,9 @@ impl<'a> PreInstance<'a> { // Check side-clauses. let instance = & mut self.instance ; + let use_actlits = self.use_actlits ; let solver = & mut self.solver ; + info += instance.side_clauses_retain( |clause| { match solver.is_clause_trivial(clause) ? { @@ -1878,7 +1913,8 @@ impl<'a> PreInstance<'a> { clause, & (false, & set, & set, & self.instance.preds) ) ? ; - let sat = self.solver.check_sat() ? ; + let sat = check_sat!(self) ; + self.solver.pop(1) ? ; if sat { return Ok(false) @@ -1900,7 +1936,7 @@ impl<'a> PreInstance<'a> { clause, & (false, & set, & set, & self.instance.preds) ) ? ; - let sat = self.solver.check_sat() ? ; + let sat = check_sat!(self) ; self.solver.pop(1) ? ; if sat { return Ok(false) diff --git a/src/parse/mod.rs b/src/parse/mod.rs index c39e9ed6..f1d0af78 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -213,6 +213,8 @@ enum FrameOp { DTypNew(String, DTyp), /// A datatype selector. DTypSlc(String), + /// A datatype tester. + DTypTst(String), /// A function application. Fun(String), } @@ -229,6 +231,7 @@ impl FrameOp { "`{}` constructor ({})", name, typ ), FrameOp::DTypSlc(name) => format!("`{}` selector", name), + FrameOp::DTypTst(name) => format!("`{}` tester", name), FrameOp::Fun(name) => format!("`{}` function", name), } } @@ -1445,6 +1448,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.tag("(").chain_err( || "opening sort parameter list" ) ? ; + self.ws_cmt() ; while let Some((pos, ident)) = self.ident_opt() ? { let idx = dtyp.push_typ_param(ident) ; @@ -2063,7 +2067,13 @@ impl<'cxt, 's> Parser<'cxt, 's> { name, op_pos, & args_pos, args ), - _ => unimplemented!(), + FrameOp::DTypTst(name) => self.build_dtyp_tst( + name, op_pos, & args_pos, args + ), + + op => bail!( + "unsupported operator {}", conf.bad( op.as_str() ) + ), } } @@ -2222,8 +2232,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { bail!( self.error( slc_pos, format!( - "illegal datatype selector application to {} (> 1) arguments", - args.len() + 2 + "illegal application of datatype selector {} \ + to {} (> 1) arguments", + conf.bad(name), args.len() + 2 ) ) ) @@ -2234,7 +2245,10 @@ impl<'cxt, 's> Parser<'cxt, 's> { } else { bail!( self.error( - slc_pos, "illegal datatype selector application to nothing" + slc_pos, format!( + "illegal application of datatype selector {} to nothing", + conf.bad(name) + ) ) ) } ; @@ -2248,6 +2262,49 @@ impl<'cxt, 's> Parser<'cxt, 's> { } } + /// Type checks and builds a datatype tester. + fn build_dtyp_tst( + & self, + name: String, tst_pos: Pos, _args_pos: & [ Pos ], mut args: Vec + ) -> Res<(Term, Pos)> { + debug_assert_eq! { _args_pos.len(), args.len() } + + let arg = if let Some(arg) = args.pop() { + + if args.pop().is_some() { + bail!( + self.error( + tst_pos, format!( + "illegal application of datatype tester {} \ + to {} (> 1) arguments", + conf.bad(name), args.len() + 2 + ) + ) + ) + } else { + arg + } + + } else { + bail!( + self.error( + tst_pos, format!( + "illegal application of datatype tester {} to nothing", + conf.bad(name) + ) + ) + ) + } ; + + match dtyp::type_tester( & name, tst_pos, & arg ) { + Ok(()) => Ok( + ( term::dtyp_tst(name, arg), tst_pos ) + ), + + Err((pos, blah)) => bail!( self.error(pos, blah) ), + } + } + /// Type checks and builds a datatype selector. fn build_fun_app( & self, @@ -2527,7 +2584,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.cxt.term_stack.push(frame) ; continue 'read_kids - } else if self.tag_opt("as") { + } else if self.tag_opt(keywords::op::as_) { let frame = TermFrame::new( FrameOp::Cast, op_pos, bind_count ) ; @@ -2538,8 +2595,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; // Try to parse a constant array. - if self.tag_opt("as") - && { self.ws_cmt() ; self.tag_opt("const") } { + if self.tag_opt(keywords::op::as_) { + self.ws_cmt() ; + self.tag(keywords::op::const_) ? ; self.ws_cmt() ; let sort_pos = self.pos() ; let typ = self.sort() ? ; @@ -2553,6 +2611,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.cxt.term_stack.push(frame) ; continue 'read_kids + } else if self.tag_opt(keywords::op::lambda_) { + self.ws_cmt() ; + self.tag(keywords::op::is_) ? ; + self.ws_cmt() ; + let (op_pos, ident) = self.ident() ? ; + self.ws_cmt() ; + self.tag(")") ? ; + + let frame = TermFrame::new( + FrameOp::DTypTst( ident.into() ), op_pos, bind_count + ) ; + self.cxt.term_stack.push(frame) ; + continue 'read_kids + } else { bail!( self.error_here("unexpected token") ) } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index c5e3fd00..723da56e 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -951,7 +951,7 @@ impl<'a> Teacher<'a> { } let cexs = self.get_cex( - clause, bias, + cands, clause, bias, // got_pos_neg_samples && conf.teacher.max_bias ).chain_err( @@ -998,7 +998,8 @@ impl<'a> Teacher<'a> { /// Check-sats given an optional bias. fn check_sat_cex( - & mut self, clause: ClsIdx, bias: Option<(Actlit, Bias)> + & mut self, clause: ClsIdx, main_actlit: & Actlit, + bias: Option<(Actlit, Bias)> ) -> Res< Option<(Cex, Bias)> > { if let Some((actlit, bias)) = bias { @@ -1009,9 +1010,11 @@ impl<'a> Teacher<'a> { & format!("checksat with bias {}", bias.to_string(& self.instance)) ) ? ; profile!{ self tick "cexs", "biased check-sat" } - let sat = self.solver.check_sat_act( - Some(& actlit) - ) ? ; + let sat = { + self.solver.check_sat_act( + vec![ main_actlit, & actlit ] + ) ? + } ; if sat { log! { @debug " sat, getting cex" } @@ -1030,7 +1033,11 @@ impl<'a> Teacher<'a> { log! { @debug " checksat" } let sat = profile! { - self wrap { self.solver.check_sat() } "cexs", "check-sat" + self wrap { + self.solver.check_sat_act( + Some(main_actlit) + ) + } "cexs", "check-sat" } ? ; if sat { @@ -1066,14 +1073,15 @@ impl<'a> Teacher<'a> { /// Checks if a clause is falsifiable and returns a model if it is. pub fn get_cex( - & mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool + & mut self, cands: & Candidates, + clause_idx: ClsIdx, bias: bool, bias_only: bool ) -> Res< Vec > { let mut cexs = vec![] ; log! { @debug "working on clause #{}", clause_idx } if self.using_rec_funs { - let falsifiable = self.quantified_checksat(clause_idx) ? ; + let falsifiable = self.quantified_checksat(cands, clause_idx) ? ; if ! falsifiable { return Ok(cexs) } @@ -1096,8 +1104,9 @@ impl<'a> Teacher<'a> { profile!{ self tick "cexs", "prep" } clause!().declare(& mut self.solver) ? ; - self.solver.assert_with( - clause!(), & ( + let main_actlit = self.solver.get_actlit() ? ; + self.solver.assert_act_with( + & main_actlit, clause!(), & ( false, & self.tru_preds, & self.fls_preds, self.instance.preds() ) ) ? ; @@ -1107,7 +1116,7 @@ impl<'a> Teacher<'a> { () => ( // Normal check, no actlit. if let Some(cex) = self.check_sat_cex( - clause_idx, None + clause_idx, & main_actlit, None ) ? { cexs.push(cex) } @@ -1115,7 +1124,7 @@ impl<'a> Teacher<'a> { ($actlit:expr ; $bias:expr) => ( if let Some(cex) = self.check_sat_cex( - clause_idx, Some(($actlit, $bias)) + clause_idx, & main_actlit, Some(($actlit, $bias)) ) ? { cexs.push(cex) } @@ -1164,16 +1173,19 @@ impl<'a> Teacher<'a> { /// /// This is used when manipulating recursive ADTs and functions. pub fn quantified_checksat( - & mut self, clause: ClsIdx + & mut self, cands: & Candidates, clause: ClsIdx ) -> Res { let actlit = self.solver.get_actlit() ? ; - let wrapped = smt::SmtQClause::new( & self.instance[clause] ) ; - self.solver.assert_act_with( - & actlit, & wrapped, & ( - & self.tru_preds, & self.fls_preds, self.instance.preds() - ) - ) ? ; + { + let wrapped = smt::SmtQClause::new( & self.instance[clause] ) ; + + self.solver.assert_act_with( + & actlit, & wrapped, & ( + & self.tru_preds, & self.fls_preds, self.instance.preds() + ) + ) ? + } let falsifiable = self.solver.check_sat_act( Some(& actlit) @@ -1181,6 +1193,10 @@ impl<'a> Teacher<'a> { self.solver.de_actlit(actlit) ? ; + smt::reset(& mut self.solver, & self.instance) ? ; + + self.define_preds(cands) ? ; + Ok(falsifiable) } diff --git a/src/term/eval.rs b/src/term/eval.rs index 77ea5924..dc5e2040 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -14,6 +14,7 @@ pub type CmdT<'a> = ZipDoTotal< 'a, Val > ; /// Term evaluation. pub fn eval(term: & Term, model: & E) -> Res { + println!("evaluating {}", term) ; if let Some(val) = term.val() { return Ok(val) } else if let Some(idx) = term.var_idx() { @@ -63,6 +64,7 @@ macro_rules! go { fn leaf<'a, E: Evaluator>( model: & E, zip_null: ZipNullary<'a>, ) -> Res { + println!("leaf: {}", zip_null) ; match zip_null { ZipNullary::Cst(val) => Ok( val.clone() ), ZipNullary::Var(_, var) => if var < model.len() { @@ -117,7 +119,7 @@ fn total<'a>( } else { let e: Error = format!( - "unknown constructor `{}` for datatype {}", + "unknown selector `{}` for datatype {}", conf.bad(constructor), dtyp.name ).into() ; bail!( @@ -130,7 +132,7 @@ fn total<'a>( } } else { bail!( - "illegal application of constructor `{}` of `{}` to `{}`", + "illegal application of selector `{}` of `{}` to `{}`", conf.bad(& name), typ, value ) } @@ -140,6 +142,26 @@ fn total<'a>( ) }, + ZipOp::Tst(name) => if values.len() == 1 { + let value = values.pop().unwrap() ; + if ! value.is_known() { + val::none( typ.clone() ) + } else if let Some( + (_, constructor, _) + ) = value.dtyp_inspect() { + val::bool( constructor == name ) + } else { + bail!( + "illegal application of tester `{}` to {}: {}", + conf.bad(& name), value, value.typ() + ) + } + } else { + bail!( + "expected one value for datatype selection, found {}", values.len() + ) + }, + ZipOp::CArray => if values.len() == 1 { let default = values.pop().unwrap() ; val::array( typ.clone(), default ) @@ -165,6 +187,12 @@ fn total<'a>( ) } + println!("fun | {}", name) ; + for value in & values { + println!(" | {}", value) + } + println!("def | {}", fun.def) ; + return Ok( ZipDoTotal::Dwn { nu_term: & fun.def, @@ -190,7 +218,8 @@ fn partial<'a>( thing @ ZipOp::New(_) | thing @ ZipOp::Fun(_) | thing @ ZipOp::CArray | - thing @ ZipOp::Slc(_) => { + thing @ ZipOp::Slc(_) | + thing @ ZipOp::Tst(_) => { let nu_term = rgt_args.next().expect( "illegal call to `partial_op`: empty `rgt_args` (eval::partial)" ) ; @@ -331,9 +360,9 @@ fn partial_op<'a>( } ) } else { - log!(@4 "{}", op) ; + println!("{}", op) ; for arg in & lft_args { - log!(@4 " {}", arg) + println!(" {}", arg) } panic!( "illegal call to `partial_op`: empty `rgt_args` (partial_op)" diff --git a/src/term/factory.rs b/src/term/factory.rs index 9e9bdbec..c6393cea 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -29,17 +29,16 @@ fn scan_vars(t: & Term) -> VarSet { let _ = set.insert(* i) ; () }, RTerm::Cst(_) => (), - RTerm::CArray { term, .. } => to_do.push(& * term), - RTerm::App{ args, .. } => for arg in args { - to_do.push(arg) - }, + + RTerm::App { args, .. } | + RTerm::Fun { args, .. } | RTerm::DTypNew { args, .. } => for arg in args { to_do.push(arg) }, - RTerm::DTypSlc { term, .. } => to_do.push(term), - RTerm::Fun { args, .. } => for arg in args { - to_do.push(arg) - }, + + RTerm::CArray { term, .. } | + RTerm::DTypSlc { term, .. } | + RTerm::DTypTst { term, .. } => to_do.push(term), } } set.shrink_to_fit() ; @@ -270,6 +269,7 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn app(op: Op, mut args: Vec) -> Term { + println!("{} ({})", op, args.len()) ; let typ = expect!( op.type_check(& mut args) => |e| let res: Res<()> = Err( @@ -403,18 +403,29 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { debug_assert_eq! { vals.len(), args.len() } val( val::dtyp_new(typ, name, vals) ) } else { - if args.is_empty() { - panic!("aaaaaa") - } + debug_assert!( ! args.is_empty() ) ; factory.mk( RTerm::DTypNew { typ, name, args } ) } } /// Creates a new datatype selector. +/// +/// # TODO +/// +/// - treat constants better pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { factory.mk( RTerm::DTypSlc { typ, name, term } ) } +/// Creates a new datatype tester. +/// +/// # TODO +/// +/// - treat constants better +pub fn dtyp_tst(name: String, term: Term) -> Term { + factory.mk( RTerm::DTypTst { typ: typ::bool(), name, term } ) +} + /// Creates an operator application. /// /// Error if the application is ill-typed (int will be cast to real diff --git a/src/term/leaf_iter.rs b/src/term/leaf_iter.rs index 45b713e0..38686db4 100644 --- a/src/term/leaf_iter.rs +++ b/src/term/leaf_iter.rs @@ -69,13 +69,14 @@ impl<'a> Iterator for LeafIter<'a> { Cst(ref val) => Either::Right(val), DTypSlc { ref term, .. } | - CArray { ref term, .. } => { + DTypTst { ref term, .. } | + CArray { ref term, .. } => { current = term.get() ; continue 'go_down }, - App { ref args, .. } | - Fun { ref args, .. } | + App { ref args, .. } | + Fun { ref args, .. } | DTypNew { ref args, .. } => { self.stack.push( args.iter() ) ; continue 'find_next diff --git a/src/term/mod.rs b/src/term/mod.rs index dad7d363..40a86839 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -134,6 +134,16 @@ pub enum RTerm { term: Term, }, + /// A datatype tester application. + DTypTst { + /// Type of the term (always bool). + typ: Typ, + /// Name of the tester. + name: String, + /// Argument of the selector. + term: Term, + }, + /// A function application. Fun { /// Type of this term. @@ -227,6 +237,15 @@ impl RTerm { } else { None } } + /// Returns the kids of a datatype tester. + pub fn dtyp_tst_inspect(& self) -> Option<(& str, & Term)> { + if let RTerm::DTypTst { name, term, .. } = self { + Some((name, term)) + } else { + None + } + } + /// Iterator over over all the leafs of a term. pub fn leaf_iter(& self) -> LeafIter { LeafIter::of_rterm(self) @@ -245,8 +264,9 @@ impl RTerm { stack.push(term) }, - RTerm::CArray { term, .. } => stack.push( term.get() ), - RTerm::DTypSlc { term, .. } => stack.push( term.get() ), + RTerm::CArray { term, .. } | + RTerm::DTypSlc { term, .. } | + RTerm::DTypTst { term, .. } => stack.push( term.get() ), RTerm::Var(_, _) | @@ -269,6 +289,7 @@ impl RTerm { RTerm::App { typ, .. } | RTerm::Fun { typ, .. } | RTerm::DTypSlc { typ, .. } | + RTerm::DTypTst { typ, .. } | RTerm::DTypNew { typ, .. } => typ.clone(), } } @@ -318,7 +339,10 @@ impl RTerm { Cst(val) => write!(w, "{}", val) ?, CArray { term, .. } => { - write!(w, "((as const {})", this_term.typ()) ? ; + write!( + w, "(({} {} {})", + keywords::op::as_, keywords::op::const_, this_term.typ() + ) ? ; stack.push( (vec![term], " ", ")") ) }, @@ -334,6 +358,14 @@ impl RTerm { stack.push( (vec![term], " ", ")") ) }, + DTypTst { name, term, .. } => { + write!( + w, "(({} {} {})", + keywords::op::lambda_, keywords::op::is_, name + ) ? ; + stack.push( (vec![term], " ", ")") ) + }, + DTypNew { name, args, .. } => if args.is_empty() { write!(w, "{}", name) ? } else { @@ -669,6 +701,11 @@ impl RTerm { term::dtyp_slc( typ.clone(), name.clone(), term.clone() ) }, + RTerm::DTypTst { name, term, typ } => { + debug_assert_eq! { typ, & nu_typ } + term::dtyp_tst( name.clone(), term.clone() ) + }, + RTerm::Fun { typ, name, args } => { debug_assert_eq! { typ, & nu_typ } term::fun( typ.clone(), name.clone(), args.clone() ) @@ -816,6 +853,42 @@ impl RTerm { + /// Returns true if the term mentions a function. + pub fn has_fun_apps(& self) -> bool { + use self::zip::* ; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + & self.to_hcons(), + + |_| Ok(()), + + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) | + ZipOp::New(_) | + ZipOp::Slc(_) => Err(()), + _ => Ok( ZipDoTotal::Upp { yielded: () } ), + }, + + |frame| match frame { + ZipFrame { thing: ZipOp::Fun(_), .. } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: + empty `rgt_args` (has_fun_app_or_adt)" + ) ; + Ok( ZipDo::Trm { nu_term, frame } ) + }, + } + ) ; + + res.is_err() + } + + + + /// Returns true if the term mentions a function or an ADT. pub fn has_fun_app_or_adt(& self) -> bool { use self::zip::* ; @@ -891,17 +964,37 @@ impl RTerm { ZipOp::New(name) => term::dtyp_new( typ.clone(), name.clone(), acc ), + ZipOp::Slc(name) => if let Some(kid) = acc.pop() { if ! acc.is_empty() { panic!( - "illegal datatype selector application to {} arguments", - acc.len() + 1 + "illegal application of datatype selector {} to {} arguments", + conf.bad(name), acc.len() + 1 ) } term::dtyp_slc(typ.clone(), name.clone(), kid) } else { - panic!("illegal datatype selector application to 0 arguments") + panic!( + "illegal application of datatype selector {} to 0 arguments", + conf.bad(name) + ) }, + + ZipOp::Tst(name) => if let Some(kid) = acc.pop() { + if ! acc.is_empty() { + panic!( + "illegal application of datatype tester {} to {} arguments", + conf.bad(name), acc.len() + 1 + ) + } + term::dtyp_slc(typ.clone(), name.clone(), kid) + } else { + panic!( + "illegal application of datatype tester {} to 0 arguments", + conf.bad(name) + ) + }, + ZipOp::CArray => if let Some(kid) = acc.pop() { if ! acc.is_empty() { panic!( @@ -1191,7 +1284,8 @@ impl RTerm { RTerm::Fun { .. } | RTerm::CArray { .. } | RTerm::DTypNew { .. } | - RTerm::DTypSlc { .. } => return None, + RTerm::DTypSlc { .. } | + RTerm::DTypTst { .. } => return None, } } diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 7dbd8b56..1fcda7db 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -768,7 +768,8 @@ simpl_fun! { Some( // "else" term NormRes::Term( args.pop().unwrap() ) ) - } else if args[1].typ().is_bool() { + } else if args[1].typ().is_bool() + && args[0].dtyp_tst_inspect().is_none() { let (e, t, c) = ( args.pop().unwrap(), args.pop().unwrap(), args.pop().unwrap() ) ; @@ -1499,7 +1500,7 @@ simpl_fun! { Op::Ite => if args.len() != 3 { panic!("illegal ite application: {}", term) - } else { + } else if args[0].dtyp_tst_inspect().is_none() { let (c, t, e) = ( args[0].clone(), args[1].clone(), diff --git a/src/term/zip.rs b/src/term/zip.rs index 2dfb997d..804d3ef7 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -90,6 +90,8 @@ pub enum ZipOp<'a> { CArray, /// A datatype selection. Slc(& 'a String), + /// A datatype tester. + Tst(& 'a String), } impl<'a> ::std::fmt::Display for ZipOp<'a> { fn fmt(& self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { @@ -98,6 +100,7 @@ impl<'a> ::std::fmt::Display for ZipOp<'a> { ZipOp::New(inner) => inner.fmt(fmt), ZipOp::Fun(inner) => inner.fmt(fmt), ZipOp::Slc(inner) => inner.fmt(fmt), + ZipOp::Tst(inner) => inner.fmt(fmt), ZipOp::CArray => write!(fmt, "array"), } } @@ -202,6 +205,9 @@ Partial: for<'a> FnMut( 'inspect_term: loop { // stack_print!() ; + println!() ; + println!("zip | {}", term) ; + let result = match * term.get() { RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { @@ -282,6 +288,20 @@ Partial: for<'a> FnMut( continue 'inspect_term }, + RTerm::DTypTst { ref typ, ref name, term: ref nu_term } => { + let mut rgt_args = empty.iter() ; + let op = ZipOp::Tst(name) ; + let lft_args = Acc::new_empty(1) ; + + let frame = ZipFrame { + thing: op, typ, lft_args, rgt_args, + } ; + stack.push( (frame, subst.clone()) ) ; + term = nu_term ; + + continue 'inspect_term + }, + RTerm::Fun { ref typ, ref name, ref args } => { let mut rgt_args = args.iter() ; let op = ZipOp::Fun(name) ; diff --git a/src/unsat_core/mod.rs b/src/unsat_core/mod.rs index 3058f554..ec2929fd 100644 --- a/src/unsat_core/mod.rs +++ b/src/unsat_core/mod.rs @@ -1,4 +1,6 @@ //! Unsat core extraction. +//! +//! Currently inactive. use common::* ; diff --git a/src/unsat_core/sample_graph.rs b/src/unsat_core/sample_graph.rs index cc535fc0..146d9ccf 100644 --- a/src/unsat_core/sample_graph.rs +++ b/src/unsat_core/sample_graph.rs @@ -1,11 +1,14 @@ //! Sample dependency tracking. +#![allow(dead_code)] + use std::borrow::Borrow ; use common::{ *, - smt::FullParser as Parser, - var_to::vals::{ VarValsMap, VarValsSet }, + // smt::FullParser as Parser, + // var_to::vals::{ VarValsMap, VarValsSet }, + var_to::vals::VarValsMap, } ; use unsat_core::* ; @@ -808,189 +811,186 @@ impl SampleGraph { - /// Traces the origin of a sample. - /// - /// # TODO - /// - /// - should not be public - fn trace<'a>( - & 'a self, - pred: PrdIdx, args: & VarVals, - polarity: Polarity, - known: & KnownSamples, - solver: & mut Solver, - instance: & Instance, - ) -> Res { - - // Samples for which we already have an explanation for. - let mut explained = PrdHMap::::new() ; - - // Checks whether a sample is explained or inserts a sample in explained - // samples. - macro_rules! explained { - // Checks whether a sample is already explained. - (contains $pred:expr, $args:expr) => ( - explained.get(& $pred).map( - |set| set.contains(& $args) - ).unwrap_or(false) - ) ; - - // Adds a sample as explained. - (insert $pred:expr, $args:expr) => ({ - let is_new = explained.entry($pred).or_insert_with( - VarValsSet::new - ).insert($args) ; - debug_assert! { is_new } - }) ; - } - - // Result: full trace of explanation. - let mut res = vec![] ; - - // Stores the samples we need to explain. - let mut to_explain = vec![ (polarity, pred, args.clone()) ] ; - - // Explain all samples. - 'all_samples: while let Some( - (polarity, pred, args) - ) = to_explain.pop() { - - // Already explained? - if explained!(contains pred, & args) { - continue 'all_samples - } - - let (rhs, (clause, lhs)) = if let Some(origin) = known.get( - polarity, pred, & args - ) { - origin - } else { - bail!("unable to explain why sample ({} {}) is positive", pred, args) - } ; - - // Take care of the antecedents. - for (pred, argss) in lhs.iter() { - for (_, args) in argss { - to_explain.push( - (Polarity::pos(), * pred, args.clone()) - ) - } - } - - let mut map = VarHMap::new() ; - - { - use common::smt::{ SmtConj, EqConj } ; - - solver.comment( - & format!("Working on clause #{}", clause) - ) ? ; - let clause = & instance[clause] ; - - solver.push(1) ? ; + // /// Traces the origin of a sample. + // fn trace<'a>( + // & 'a self, + // pred: PrdIdx, args: & VarVals, + // polarity: Polarity, + // known: & KnownSamples, + // solver: & mut Solver, + // instance: & Instance, + // ) -> Res { + + // // Samples for which we already have an explanation for. + // let mut explained = PrdHMap::::new() ; + + // // Checks whether a sample is explained or inserts a sample in explained + // // samples. + // macro_rules! explained { + // // Checks whether a sample is already explained. + // (contains $pred:expr, $args:expr) => ( + // explained.get(& $pred).map( + // |set| set.contains(& $args) + // ).unwrap_or(false) + // ) ; + + // // Adds a sample as explained. + // (insert $pred:expr, $args:expr) => ({ + // let is_new = explained.entry($pred).or_insert_with( + // VarValsSet::new + // ).insert($args) ; + // debug_assert! { is_new } + // }) ; + // } + + // // Result: full trace of explanation. + // let mut res = vec![] ; + + // // Stores the samples we need to explain. + // let mut to_explain = vec![ (polarity, pred, args.clone()) ] ; + + // // Explain all samples. + // 'all_samples: while let Some( + // (polarity, pred, args) + // ) = to_explain.pop() { + + // // Already explained? + // if explained!(contains pred, & args) { + // continue 'all_samples + // } + + // let (rhs, (clause, lhs)) = if let Some(origin) = known.get( + // polarity, pred, & args + // ) { + // origin + // } else { + // bail!("unable to explain why sample ({} {}) is positive", pred, args) + // } ; - clause.declare(solver) ? ; + // // Take care of the antecedents. + // for (pred, argss) in lhs.iter() { + // for (_, args) in argss { + // to_explain.push( + // (Polarity::pos(), * pred, args.clone()) + // ) + // } + // } - let conj = SmtConj::new(clause.lhs_terms()) ; + // let mut map = VarHMap::new() ; - solver.assert(& conj) ? ; + // { + // use common::smt::{ SmtConj, EqConj } ; - debug_assert_eq! { clause.lhs_preds().len(), lhs.len() } + // solver.comment( + // & format!("Working on clause #{}", clause) + // ) ? ; + // let clause = & instance[clause] ; - debug_assert! { - lhs.iter().all( - |(pred, argss)| clause.lhs_preds().iter().any( - |(p, a)| p == pred && argss.len() == a.len() && argss.iter().all( - |(args, _)| a.iter().any( - |a| a == args - ) - ) - ) - ) - } + // solver.push(1) ? ; - for argss in lhs.values() { - for (fargs, sample) in argss { - let eq_conj = EqConj::new(fargs, sample) ; - solver.assert(& eq_conj) ? - } - } + // clause.declare(solver) ? ; - if let Some((pred, ref fargs, ref args)) = rhs { - debug_assert! { - if let Some((p, fa)) = clause.rhs() { - pred == p && fa == fargs - } else { - false - } - } - let eq_conj = EqConj::new(fargs, args) ; - solver.assert(& eq_conj) ? - } + // let conj = SmtConj::new(clause.lhs_terms()) ; - if ! solver.check_sat() ? { - bail!("error retrieving unsat core, trace is not feasible") - } + // solver.assert(& conj) ? ; - let model = solver.get_model() ? ; - let model = Parser.fix_model(model) ? ; + // debug_assert_eq! { clause.lhs_preds().len(), lhs.len() } - solver.pop(1) ? ; + // debug_assert! { + // lhs.iter().all( + // |(pred, argss)| clause.lhs_preds().iter().any( + // |(p, a)| p == pred && argss.len() == a.len() && argss.iter().all( + // |(args, _)| a.iter().any( + // |a| a == args + // ) + // ) + // ) + // ) + // } + + // for argss in lhs.values() { + // for (fargs, sample) in argss { + // let eq_conj = EqConj::new(fargs, sample) ; + // solver.assert(& eq_conj) ? + // } + // } + + // if let Some((pred, ref fargs, ref args)) = rhs { + // debug_assert! { + // if let Some((p, fa)) = clause.rhs() { + // pred == p && fa == fargs + // } else { + // false + // } + // } + // let eq_conj = EqConj::new(fargs, args) ; + // solver.assert(& eq_conj) ? + // } + + // if ! solver.check_sat() ? { + // bail!("error retrieving unsat core, trace is not feasible") + // } + + // let model = solver.get_model() ? ; + // let model = Parser.fix_model(model) ? ; - for (var, _, val) in model { - let prev = map.insert(var, val) ; - debug_assert_eq! { prev, None } - } + // solver.pop(1) ? ; + + // for (var, _, val) in model { + // let prev = map.insert(var, val) ; + // debug_assert_eq! { prev, None } + // } - } + // } - // Append to result. - res.push( - TraceFrame::new( - clause, map, polarity, pred, args.clone(), rhs, lhs - ) - ) ; + // // Append to result. + // res.push( + // TraceFrame::new( + // clause, map, polarity, pred, args.clone(), rhs, lhs + // ) + // ) ; - // Remember we explained this sample. - explained! { insert pred, args } + // // Remember we explained this sample. + // explained! { insert pred, args } - } + // } - res.reverse() ; + // res.reverse() ; - Ok( Trace::new(res) ) - } + // Ok( Trace::new(res) ) + // } /// Extracts a proof for unsat. - pub fn get_proof(& mut self, instance: & Instance) -> Res { - let ( - pred, pos, neg, known - ) = if let Some(contradiction) = self.find_contradiction() { - contradiction - } else { - bail!("could not retrieve unsat result") - } ; - - let mut solver = conf.solver.spawn( - "core_extraction", Parser, & instance - ) ? ; - let pos_trace = self.trace( - pred, & pos, Polarity::pos(), & known, & mut solver, - instance - ) ? ; - let neg_trace = self.trace( - pred, & neg, Polarity::neg(), & known, & mut solver, - instance - ) ? ; - - Ok( - UnsatProof { - pred, pos, neg, pos_trace, neg_trace - } - ) + pub fn get_proof(& mut self, _instance: & Instance) -> Res { + bail!("unimplemented") ; + // let ( + // pred, pos, neg, known + // ) = if let Some(contradiction) = self.find_contradiction() { + // contradiction + // } else { + // bail!("could not retrieve unsat result") + // } ; + + // let mut solver = conf.solver.spawn( + // "core_extraction", Parser, & instance + // ) ? ; + // let pos_trace = self.trace( + // pred, & pos, Polarity::pos(), & known, & mut solver, + // instance + // ) ? ; + // let neg_trace = self.trace( + // pred, & neg, Polarity::neg(), & known, & mut solver, + // instance + // ) ? ; + + // Ok( + // UnsatProof { + // pred, pos, neg, pos_trace, neg_trace + // } + // ) } diff --git a/src/val/mod.rs b/src/val/mod.rs index fc6ef4f1..0f92daf8 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -1497,7 +1497,7 @@ impl_fmt!{ if typ.has_unk() { write!(fmt, "{}", name) ? } else { - write!(fmt, "(as {} {})", name, typ) ? + write!(fmt, "({} {} {})", keywords::op::as_, name, typ) ? } } else { write!(fmt, "({}", name) ? ; @@ -1510,9 +1510,12 @@ impl_fmt!{ RVal::Array { ref default, ref vals, .. } => { for _ in vals { - write!(fmt, "(store ") ? + write!(fmt, "({} ", keywords::op::store_) ? } - write!(fmt, "((as const {}) {})", self.typ(), default) ? ; + write!( + fmt, "(({} {} {}) {})", + keywords::op::as_, keywords::op::const_, self.typ(), default + ) ? ; // Not reversing the list, we want to print in reverse order. for (index, val) in vals.iter() { stack.push( Either::Right(()) ) ; From 2d8bd7f5daba51440421c60aa73babd6e2965ebf Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 16 Aug 2018 19:50:23 +0900 Subject: [PATCH 33/94] debug info --- src/term/eval.rs | 6 ++++++ src/term/zip.rs | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/term/eval.rs b/src/term/eval.rs index dc5e2040..98be44be 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -80,6 +80,11 @@ fn total<'a>( op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec, fun_ref_count: & mut usize ) -> Res< CmdT<'a> > { + println!() ; + println!("ttl | {}", op) ; + for value in & values { + println!(" | {}", value) + } let yielded = match op { ZipOp::Op(op) => { op.eval(values).chain_err( @@ -145,6 +150,7 @@ fn total<'a>( ZipOp::Tst(name) => if values.len() == 1 { let value = values.pop().unwrap() ; if ! value.is_known() { + println!("none") ; val::none( typ.clone() ) } else if let Some( (_, constructor, _) diff --git a/src/term/zip.rs b/src/term/zip.rs index 804d3ef7..6b0ff908 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -96,11 +96,11 @@ pub enum ZipOp<'a> { impl<'a> ::std::fmt::Display for ZipOp<'a> { fn fmt(& self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { match self { - ZipOp::Op(inner) => inner.fmt(fmt), - ZipOp::New(inner) => inner.fmt(fmt), - ZipOp::Fun(inner) => inner.fmt(fmt), - ZipOp::Slc(inner) => inner.fmt(fmt), - ZipOp::Tst(inner) => inner.fmt(fmt), + ZipOp::Op(inner) => write!(fmt, "Op({})", inner), + ZipOp::New(inner) => write!(fmt, "New({})", inner), + ZipOp::Fun(inner) => write!(fmt, "Fun({})", inner), + ZipOp::Slc(inner) => write!(fmt, "Slc({})", inner), + ZipOp::Tst(inner) => write!(fmt, "Tst({})", inner), ZipOp::CArray => write!(fmt, "array"), } } @@ -211,10 +211,12 @@ Partial: for<'a> FnMut( let result = match * term.get() { RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { + println!(" | subst") ; ZipDoTotal::Upp { yielded: subst[var_idx].clone(), } } else { + println!(" | not subst") ; ZipDoTotal::Upp { yielded: nul_do( ZipNullary::Var(typ, var_idx) ) ?, } @@ -342,13 +344,17 @@ Partial: for<'a> FnMut( match stack.pop() { // Done, we're at top level. - None => return Ok(result), + None => { + println!("stack empty") ; + return Ok(result) + }, // Work on the next frame. Some( (ZipFrame { thing, typ, mut lft_args, rgt_args }, old_subst) ) => { subst = old_subst ; + println!("stack: {} ({})", thing, typ) ; // Update left args. From de6c587bfae40184edbdbd3e190becd99aae6176 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 16 Aug 2018 21:00:38 +0900 Subject: [PATCH 34/94] ite partial evaluation fix --- src/term/eval.rs | 58 +++++++++++++++++---------------------------- src/term/factory.rs | 1 - src/term/zip.rs | 12 +--------- 3 files changed, 23 insertions(+), 48 deletions(-) diff --git a/src/term/eval.rs b/src/term/eval.rs index 98be44be..a090c359 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -14,7 +14,6 @@ pub type CmdT<'a> = ZipDoTotal< 'a, Val > ; /// Term evaluation. pub fn eval(term: & Term, model: & E) -> Res { - println!("evaluating {}", term) ; if let Some(val) = term.val() { return Ok(val) } else if let Some(idx) = term.var_idx() { @@ -64,7 +63,6 @@ macro_rules! go { fn leaf<'a, E: Evaluator>( model: & E, zip_null: ZipNullary<'a>, ) -> Res { - println!("leaf: {}", zip_null) ; match zip_null { ZipNullary::Cst(val) => Ok( val.clone() ), ZipNullary::Var(_, var) => if var < model.len() { @@ -80,11 +78,6 @@ fn total<'a>( op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec, fun_ref_count: & mut usize ) -> Res< CmdT<'a> > { - println!() ; - println!("ttl | {}", op) ; - for value in & values { - println!(" | {}", value) - } let yielded = match op { ZipOp::Op(op) => { op.eval(values).chain_err( @@ -150,7 +143,6 @@ fn total<'a>( ZipOp::Tst(name) => if values.len() == 1 { let value = values.pop().unwrap() ; if ! value.is_known() { - println!("none") ; val::none( typ.clone() ) } else if let Some( (_, constructor, _) @@ -193,12 +185,6 @@ fn total<'a>( ) } - println!("fun | {}", name) ; - for value in & values { - println!(" | {}", value) - } - println!("def | {}", fun.def) ; - return Ok( ZipDoTotal::Dwn { nu_term: & fun.def, @@ -250,34 +236,34 @@ fn partial_op<'a>( match op { - Op::Ite => if let Some(c) = lft_args.pop() { - if ! lft_args.is_empty() { - bail!( - "partial `Ite` application with `lft_args` of length {}", - lft_args.len() + 1 - ) - } + Op::Ite => if lft_args.len() == 1 { + let cond = lft_args.pop().expect("pop failed on vector of length 1") ; - let (t, e) = if let (Some(t), Some(e), None) = ( - rgt_args.next(), rgt_args.next(), rgt_args.next() - ) { - (t, e) - } else { - bail!("illegal application of `Ite`") - } ; - - match c.to_bool().chain_err( + match cond.to_bool().chain_err( || "during `Ite` condition evaluation" ) ? { + Some(cond) => if let (Some(t), Some(e), None) = ( + rgt_args.next(), rgt_args.next(), rgt_args.next() + ) { + if cond { + go!(down t) + } else { + go!(down e) + } + } else { + bail!("illegal application of `Ite`") + }, - // Condition is true, go into the `then` branch. - Some(true) => go!(down t), + None if ! cond.is_known() => if let (Some(t), Some(e), None) = ( + rgt_args.next(), rgt_args.next(), rgt_args.next() + ) { + debug_assert_eq!( t.typ(), e.typ() ) ; - // Condition is false, go into the `else` branch. - Some(false) => go!(down e), + go!(up val::none(t.typ())) + } else { + bail!("illegal application of `Ite`") + }, - // Unknown condition value. Keep going, the Ite might still be - // evaluable if both branches are equal. None => (), } }, diff --git a/src/term/factory.rs b/src/term/factory.rs index c6393cea..31fb2201 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -269,7 +269,6 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn app(op: Op, mut args: Vec) -> Term { - println!("{} ({})", op, args.len()) ; let typ = expect!( op.type_check(& mut args) => |e| let res: Res<()> = Err( diff --git a/src/term/zip.rs b/src/term/zip.rs index 6b0ff908..d2a7c79f 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -205,18 +205,13 @@ Partial: for<'a> FnMut( 'inspect_term: loop { // stack_print!() ; - println!() ; - println!("zip | {}", term) ; - let result = match * term.get() { RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { - println!(" | subst") ; ZipDoTotal::Upp { yielded: subst[var_idx].clone(), } } else { - println!(" | not subst") ; ZipDoTotal::Upp { yielded: nul_do( ZipNullary::Var(typ, var_idx) ) ?, } @@ -344,18 +339,13 @@ Partial: for<'a> FnMut( match stack.pop() { // Done, we're at top level. - None => { - println!("stack empty") ; - return Ok(result) - }, + None => return Ok(result), // Work on the next frame. Some( (ZipFrame { thing, typ, mut lft_args, rgt_args }, old_subst) ) => { subst = old_subst ; - println!("stack: {} ({})", thing, typ) ; - // Update left args. lft_args.push( result ) ; From 166be195bad5e4236b4f3d102d747cd244e4f799 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 17 Aug 2018 12:53:57 +0900 Subject: [PATCH 35/94] fixes and tweaks --- src/instance/instance/clause.rs | 20 +++++++-------- src/instance/instance/pre_instance.rs | 1 - src/learning/ice/quals.rs | 18 ++++++------- src/teacher/mod.rs | 37 ++++++++++++--------------- src/term/mod.rs | 3 +-- 5 files changed, 35 insertions(+), 44 deletions(-) diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index f696ab60..1be6cf49 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -1042,20 +1042,20 @@ impl Clause { - /// Asserts a clause. - pub fn assert( - & self, solver: Solver

, write_pred: WritePrd, need_model: bool - ) -> Res<()> - where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + // /// Asserts a clause. + // pub fn assert( + // & self, solver: Solver

, write_pred: WritePrd, need_model: bool + // ) -> Res<()> + // where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - if ! need_model && self.has_fun_apps { + // if ! need_model && self.has_fun_apps { - } else { + // } else { - } + // } - Ok(()) - } + // Ok(()) + // } } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index c384d358..c196a879 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -286,7 +286,6 @@ impl<'a> PreInstance<'a> { // Check side-clauses. let instance = & mut self.instance ; - let use_actlits = self.use_actlits ; let solver = & mut self.solver ; info += instance.side_clauses_retain( diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 8518b695..380440c4 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -672,16 +672,14 @@ impl NuQuals { }, typ::RTyp::DTyp { ref dtyp, .. } => { - for (name, args) in & dtyp.news { - if args.is_empty() { - quals.insert( - term::eq( - term::var( var, typ.clone() ), - term::dtyp_new( typ.clone(), name.clone(), vec![] ) - ), - pred_info.idx - ) ? ; - } + for name in dtyp.news.keys() { + quals.insert( + term::dtyp_tst( + name.clone(), + term::var( var, typ.clone() ) + ), + pred_info.idx + ) ? ; } let functions = fun::Functions::new( typ.clone() ) ; for fun in functions.from_typ { diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 723da56e..507076e3 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -998,8 +998,7 @@ impl<'a> Teacher<'a> { /// Check-sats given an optional bias. fn check_sat_cex( - & mut self, clause: ClsIdx, main_actlit: & Actlit, - bias: Option<(Actlit, Bias)> + & mut self, clause: ClsIdx, bias: Option<(Actlit, Bias)> ) -> Res< Option<(Cex, Bias)> > { if let Some((actlit, bias)) = bias { @@ -1011,9 +1010,7 @@ impl<'a> Teacher<'a> { ) ? ; profile!{ self tick "cexs", "biased check-sat" } let sat = { - self.solver.check_sat_act( - vec![ main_actlit, & actlit ] - ) ? + self.solver.check_sat_act( Some(& actlit) ) ? } ; if sat { @@ -1034,9 +1031,7 @@ impl<'a> Teacher<'a> { log! { @debug " checksat" } let sat = profile! { self wrap { - self.solver.check_sat_act( - Some(main_actlit) - ) + self.solver.check_sat() } "cexs", "check-sat" } ? ; @@ -1073,19 +1068,19 @@ impl<'a> Teacher<'a> { /// Checks if a clause is falsifiable and returns a model if it is. pub fn get_cex( - & mut self, cands: & Candidates, + & mut self, _cands: & Candidates, clause_idx: ClsIdx, bias: bool, bias_only: bool ) -> Res< Vec > { let mut cexs = vec![] ; log! { @debug "working on clause #{}", clause_idx } - if self.using_rec_funs { - let falsifiable = self.quantified_checksat(cands, clause_idx) ? ; - if ! falsifiable { - return Ok(cexs) - } - } + // if self.using_rec_funs { + // let falsifiable = self.quantified_checksat(cands, clause_idx) ? ; + // if ! falsifiable { + // return Ok(cexs) + // } + // } // Macro to avoid borrowing `self.instance`. macro_rules! clause { @@ -1104,9 +1099,8 @@ impl<'a> Teacher<'a> { profile!{ self tick "cexs", "prep" } clause!().declare(& mut self.solver) ? ; - let main_actlit = self.solver.get_actlit() ? ; - self.solver.assert_act_with( - & main_actlit, clause!(), & ( + self.solver.assert_with( + clause!(), & ( false, & self.tru_preds, & self.fls_preds, self.instance.preds() ) ) ? ; @@ -1116,7 +1110,7 @@ impl<'a> Teacher<'a> { () => ( // Normal check, no actlit. if let Some(cex) = self.check_sat_cex( - clause_idx, & main_actlit, None + clause_idx, None ) ? { cexs.push(cex) } @@ -1124,7 +1118,7 @@ impl<'a> Teacher<'a> { ($actlit:expr ; $bias:expr) => ( if let Some(cex) = self.check_sat_cex( - clause_idx, & main_actlit, Some(($actlit, $bias)) + clause_idx, Some(($actlit, $bias)) ) ? { cexs.push(cex) } @@ -1138,7 +1132,8 @@ impl<'a> Teacher<'a> { let unbiased_cex = cexs.pop() ; // Don't try bias examples if instance is unsat. - if unbiased_cex.is_some() { + if unbiased_cex.is_some() + && ! self.using_rec_funs { log! { @3 "generating bias actlits" } let biased = profile! { self wrap { diff --git a/src/term/mod.rs b/src/term/mod.rs index 40a86839..62714dab 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -360,8 +360,7 @@ impl RTerm { DTypTst { name, term, .. } => { write!( - w, "(({} {} {})", - keywords::op::lambda_, keywords::op::is_, name + w, "({}-{}", keywords::op::is_, name ) ? ; stack.push( (vec![term], " ", ")") ) }, From 063789edd4bf8f56922c97cff04074635746f5c8 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 23 Aug 2018 12:07:57 +0900 Subject: [PATCH 36/94] improved int synth --- src/common/smt.rs | 62 +----- src/instance/instance/clause.rs | 307 ++++++++++-------------------- src/instance/instance/mod.rs | 26 ++- src/learning/ice/synth/helpers.rs | 72 ++++++- src/learning/ice/synth/int.rs | 65 +++---- src/learning/ice/synth/mod.rs | 23 +-- src/teacher/mod.rs | 55 +----- 7 files changed, 222 insertions(+), 388 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index bf48dc4e..ea09f7a4 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -103,72 +103,16 @@ impl<'a> Expr2Smt<()> for SmtSideClause<'a> { fn expr_to_smt2( & self, w: & mut Writer, _: () ) -> SmtRes<()> { - self.clause.write( - w, |_, _, _| panic!( + self.clause.forall_write( + w, |w, var_info| var_info.idx.default_write(w), |_, _, _| panic!( "illegal side clause: found predicate application(s)" - ) + ), 2 ) ? ; Ok(()) } } -/// Smt-prints a clause with its quantifiers, negated. -pub struct SmtQClause<'a> { - /// The clause. - pub clause: & 'a Clause, -} -impl<'a> SmtQClause<'a> { - /// Constructor. - pub fn new(clause: & 'a Clause) -> Self { - SmtQClause { clause } - } -} -impl<'a, 'b> Expr2Smt< - & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) -> for SmtQClause<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, - info: & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) - ) -> SmtRes<()> { - let ( - ref true_preds, ref false_preds, ref prd_info - ) = * info ; - - writeln!(w, "(not") ? ; - - self.clause.naked_write( - w, |w, prd, args| { - if true_preds.contains(& prd) { - write!(w, "true") - } else if false_preds.contains(& prd) { - write!(w, "false") - } else { - if ! args.is_empty() { - write!(w, "(") ? - } - write!(w, "{}", prd_info[prd].name) ? ; - for arg in args.iter() { - write!(w, " ") ? ; - arg.write( - w, |w, var| write!(w, "{}", self.clause[var]) - ) ? - } - if ! args.is_empty() { - write!(w, ")") ? - } - Ok(()) - } - }, 6 - ) ? ; - - write!(w, " )") ? ; - - Ok(()) - } -} - - /// SMT-prints a collection of terms as a conjunction with default var writer. pub struct SmtConj<'a, Trms> { /// Conjunction. diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 1be6cf49..4354e60f 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -18,7 +18,6 @@ pub fn new( vars, lhs_terms, lhs_preds, rhs, terms_changed: true, preds_changed: true, from_unrolling: false, info, from, - has_fun_apps: false, } ; for tterm in lhs { clause.lhs_insert(tterm) ; } clause @@ -58,74 +57,6 @@ pub struct Clause { /// Index of the original clause this comes from. from: ClsIdx, - /// True if the clause contains function applications. - has_fun_apps: bool, -} - -/// Updates the `has_fun_apps` flag. -/// -/// Doesn't do anything if `has_fun_apps` is already false. -macro_rules! update_has_fun_apps { - ($slf:expr) => ( - if $slf.has_fun_apps { - $slf.has_fun_apps = false ; - update_has_fun_apps!(@ $slf, (terms, lhs preds, rhs)) - } - ) ; - ($slf:expr, $tail:tt) => ( - if $slf.has_fun_apps { - $slf.has_fun_apps = false ; - update_has_fun_apps!(@ $slf, $tail) - } - ) ; - - (@ $slf:expr, (, $($tail:tt)*)) => ( - update_has_fun_apps!($slf, ($($tail)*)) - ) ; - - (@ $slf:expr, (terms $($tail:tt)*)) => ({ - if ! $slf.has_fun_apps { - for term in & $slf.lhs_terms { - if term.has_fun_apps() { - $slf.has_fun_apps = true ; - break - } - } - } - update_has_fun_apps!($slf, ($($tail)*)) - }) ; - - (@ $slf:expr, (lhs preds $($tail:tt)*)) => ({ - if ! $slf.has_fun_apps { - for argss in $slf.lhs_preds.values() { - for args in argss { - for arg in args.iter() { - if arg.has_fun_apps() { - $slf.has_fun_apps = true ; - break - } - } - } - } - } - update_has_fun_apps!($slf, ($($tail)*)) - }) ; - - (@ $slf:expr, (rhs $($tail:tt)*)) => ({ - if ! $slf.has_fun_apps { - if let Some((_, args)) = $slf.rhs.as_ref() { - for arg in args.iter() { - if arg.has_fun_apps() { - $slf.has_fun_apps = true ; - break - } - } - } - } - update_has_fun_apps!($slf, ($($tail)*)) - }) ; - - (@ $slf:expr, ()) => (()) ; } @@ -179,7 +110,6 @@ impl Clause { ) -> Option< VarTermsSet > { let res = self.lhs_preds.remove(& pred) ; if res.is_some() { - update_has_fun_apps!(self) ; self.preds_changed = true ; if self.lhs_preds.is_empty() && self.rhs.is_none() { @@ -196,15 +126,6 @@ impl Clause { pub fn insert_pred_app( & mut self, pred: PrdIdx, args: VarTerms ) -> bool { - if ! self.has_fun_apps { - for arg in args.iter() { - if arg.has_fun_apps() { - self.has_fun_apps = true ; - break - } - } - } - let is_new = self.lhs_preds.insert_pred_app(pred, args) ; self.preds_changed = self.preds_changed || is_new ; is_new @@ -214,9 +135,6 @@ impl Clause { pub fn insert_term( & mut self, term: Term ) -> bool { - if ! self.has_fun_apps && term.has_fun_apps() { - self.has_fun_apps = true - } let is_new = Self::lhs_insert_term(& mut self.lhs_terms, term) ; self.terms_changed = self.terms_changed || is_new ; is_new @@ -225,7 +143,6 @@ impl Clause { /// Removes a term from the LHS. pub fn rm_term(& mut self, term: & Term) -> bool { let was_there = self.lhs_terms.remove(term) ; - update_has_fun_apps!(self) ; self.terms_changed = self.terms_changed || was_there ; was_there } @@ -238,8 +155,6 @@ impl Clause { self.terms_changed = self.terms_changed || self.rhs.is_none() ; self.preds_changed = true ; - update_has_fun_apps!(self, (terms, rhs)) ; - self.lhs_preds.drain() } @@ -248,19 +163,13 @@ impl Clause { pub fn lhs_map_args_of( & mut self, pred: PrdIdx, mut f: F ) where F: FnMut(& VarTerms) -> VarTerms { - let changed = if let Some(argss) = self.lhs_preds.get_mut(& pred) { + if let Some(argss) = self.lhs_preds.get_mut(& pred) { self.preds_changed = true ; let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; for args in argss.iter() { nu_argss.insert( f(args) ) ; } ::std::mem::swap( & mut nu_argss, argss ) ; - true - } else { - false - } ; - if changed { - update_has_fun_apps!(self) } } @@ -268,19 +177,13 @@ impl Clause { #[inline] pub fn rhs_map_args(& mut self, mut f: F) where F: FnMut(PrdIdx, & VarTerms) -> (PrdIdx, VarTerms) { - let changed = if let Some( + if let Some( & mut (ref mut pred, ref mut args) ) = self.rhs.as_mut() { self.preds_changed = true ; let (nu_pred, mut nu_args) = f(* pred, args) ; * args = nu_args ; - * pred = nu_pred ; - true - } else { - false - } ; - if changed { - update_has_fun_apps!(self) + * pred = nu_pred } } @@ -295,9 +198,6 @@ impl Clause { old_rhs.is_some() && self.lhs_preds.is_empty() ) ; self.preds_changed = self.preds_changed || old_rhs.is_some() ; - if old_rhs.is_some() { - update_has_fun_apps!(self) - } old_rhs } @@ -337,7 +237,6 @@ impl Clause { self.rhs = Some((pred, args)) ; self.preds_changed = true ; - update_has_fun_apps!(self) ; Ok(()) } @@ -363,7 +262,7 @@ impl Clause { self.preds_changed || was_there ) ; - let mut clause = Clause { + Clause { vars: self.vars.clone(), lhs_terms: self.lhs_terms.clone(), lhs_preds, rhs: self.rhs.clone(), @@ -371,12 +270,7 @@ impl Clause { from_unrolling: self.from_unrolling, info, from: self.from, - has_fun_apps: self.has_fun_apps, - } ; - - update_has_fun_apps!(clause) ; - - clause + } } /// Clones a clause but changes the rhs. @@ -409,7 +303,7 @@ impl Clause { ), } ; - let mut clause = Clause { + Clause { vars: self.vars.clone(), lhs_terms, lhs_preds: self.lhs_preds.clone(), @@ -418,33 +312,64 @@ impl Clause { from_unrolling: self.from_unrolling, info, from: self.from, - has_fun_apps: true, - } ; - - update_has_fun_apps!(clause) ; - - clause + } } /// Removes all redundant terms from `lhs_terms`. - /// - /// # TODO - /// - /// - can be optimized by using `retain` (probably) fn prune(& mut self) { use std::cmp::Ordering::* ; use term::simplify::SimplRes::* ; - let mut to_rm = TermSet::new() ; - let mut to_add = TermSet::new() ; + + let mut to_rmv: Option = Option::None ; + let mut to_add: Option = Option::None ; + + let new_set = || TermSet::new() ; + + macro_rules! mem { + (apply) => ({ + if let Some(to_rmv) = to_rmv.as_mut() { + for term in to_rmv.drain() { + let was_there = self.lhs_terms.remove(& term) ; + debug_assert! { was_there } + } + } + let mut added_stuff = false ; + if let Some(to_add) = to_add.as_mut() { + for term in to_add.drain() { + let is_new = self.lhs_terms.insert(term) ; + added_stuff = added_stuff || is_new + } + } + added_stuff + }) ; + + (check empty) => ({ + debug_assert!( mem!(rmv empty) ) ; + debug_assert!( mem!(add empty) ) ; + }) ; + + (rmv empty) => ( mem!( @empty to_rmv ) ) ; + (add empty) => ( mem!( @empty to_add ) ) ; + (@ empty $coll:expr) => ( + to_add.as_ref().map( + |to_add| to_add.is_empty() + ).unwrap_or(true) + ) ; + + (rmv $term:expr) => ( mem!(@ to_rmv, $term) ) ; + (add $term:expr) => ( mem!(@ to_add, $term) ) ; + (@ $coll:expr, $term:expr) => ( + $coll.get_or_insert_with(new_set).insert($term) + ) ; + } let mut prune_things = true ; let mut pruned = false ; while prune_things { prune_things = false ; - debug_assert! { to_rm.is_empty() } - debug_assert! { to_add.is_empty() } + mem! { check empty } scoped! { let mut terms = self.lhs_terms.iter() ; @@ -453,45 +378,34 @@ impl Clause { match t.conj_simpl(term) { // `t` is more generic, `term` is redundant, keep `t`. Cmp(Equal) | Cmp(Greater) => { - to_rm.insert( term.clone() ) ; + mem!( rmv term.clone() ) ; }, // `term` is more generic, discard `t`. Cmp(Less) => { - to_rm.insert( t.clone() ) ; + mem!( rmv t.clone() ) ; }, // No relation. None => (), + // The conjunction of the two terms yields a new one. Yields(nu_term) => { - to_rm.insert( t.clone() ) ; - to_rm.insert( term.clone() ) ; - to_add.insert(nu_term) ; - () + mem!( rmv t.clone() ) ; + mem!( rmv term.clone() ) ; + mem!( add nu_term ) ; }, } } } } - pruned = pruned || ! to_rm.is_empty() || ! to_add.is_empty() ; + pruned = pruned || ! mem!(rmv empty) || ! mem!(add empty) ; self.terms_changed = self.terms_changed - || ! to_rm.is_empty() || ! to_add.is_empty() ; - - for to_rm in to_rm.drain() { - let was_there = self.lhs_terms.remove(& to_rm) ; - debug_assert! { was_there } - } + || ! mem!(rmv empty) || ! mem!(add empty) ; - for to_add in to_add.drain() { - let is_new = self.lhs_terms.insert(to_add) ; - prune_things = prune_things || is_new - } + let added_stuff = mem!(apply) ; + prune_things = prune_things || added_stuff } - - if pruned { - update_has_fun_apps!(self) - } } @@ -540,7 +454,6 @@ impl Clause { ) ; if changed { - update_has_fun_apps!(self) ; self.terms_changed = true ; self.preds_changed = true ; self.prune() @@ -925,29 +838,44 @@ impl Clause { } /// Writes a clause given a special function to write predicates. - pub fn write( - & self, w: & mut W, write_prd: WritePrd + pub fn write( + & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd, info: bool ) -> IoRes<()> - where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + where + W: Write, + WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + if info { + writeln!( + w, "\ + ; {} inactive variable(s)\n \ + ; unroll: {}\n \ + ; terms_changed: {}\n \ + ; preds_changed: {}\n \ + ; created by `{}`\ + ", + self.vars.iter().fold( + 0, |acc, var| acc + if var.active { 1 } else { 0 } + ), self.from_unrolling, + self.terms_changed, self.preds_changed, self.info, + ) ? + } + writeln!(w, "({} ", keywords::cmd::assert) ? ; - self.internal_naked_write(w, write_prd, true, 2) ? ; + self.forall_write(w, write_var, write_prd, 2) ? ; writeln!(w, ")") ? ; Ok(()) } - /// Writes a clause without the `assert` around it. - pub fn naked_write( - & self, w: & mut W, write_prd: WritePrd, indent: usize + /// Writes the body of a clause with its quantifier. + pub fn forall_write( + & self, w: & mut W, + write_var: WriteVar, write_prd: WritePrd, indent: usize ) -> IoRes<()> - where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - self.internal_naked_write(w, write_prd, false, indent) - } - - /// Writes a clause without the `assert` around it. - fn internal_naked_write( - & self, w: & mut W, write_prd: WritePrd, info: bool, indent: usize - ) -> IoRes<()> - where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + where + W: Write, + WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { write!( w, "{nil: >indent$}({}\n{nil: >indent$} (", keywords::forall, nil="", indent=indent @@ -956,7 +884,9 @@ impl Clause { let mut inactive = 0 ; for var in & self.vars { if var.active { - write!(w, " ({} {})", var.name, var.typ) ? + write!(w, " (") ? ; + write_var(w, var) ? ; + write!(w, " {})", var.typ) ? } else { inactive += 1 ; } @@ -966,22 +896,7 @@ impl Clause { } writeln!(w, " )") ? ; - if info { - writeln!( - w, "{nil: >indent$} \ - ; {} inactive variable(s)\n{nil: >indent$} \ - ; unroll: {}\n{nil: >indent$} \ - ; terms_changed: {}\n{nil: >indent$} \ - ; preds_changed: {}\n{nil: >indent$} \ - ; created by `{}`\ - ", - inactive, self.from_unrolling, - self.terms_changed, self.preds_changed, self.info, - nil="", indent=indent - ) ? - } - - self.internal_qf_write(w, write_prd, indent + 2) ? ; + self.qf_write(w, write_var, write_prd, indent + 2) ? ; writeln!(w, "{nil: >indent$})", nil="", indent=indent) ? ; @@ -990,10 +905,14 @@ impl Clause { /// Writes a clause without the quantifiers around it. - fn internal_qf_write( - & self, w: & mut W, write_prd: WritePrd, indent: usize + pub fn qf_write( + & self, w: & mut W, + write_var: WriteVar, write_prd: WritePrd, indent: usize ) -> IoRes<()> - where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + where + W: Write, + WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, + WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { write!( w, "{nil: >indent$}(=>\n{nil: >indent$} (and\n{nil: >indent$} ", nil="", indent=indent @@ -1005,8 +924,7 @@ impl Clause { for term in & self.lhs_terms { write!(w, " ") ? ; term.write( - w, - |w, var| w.write_all( self.vars[var].as_bytes() ) + w, |w, var| write_var( w, & self.vars[var] ) ) ? } } @@ -1040,23 +958,6 @@ impl Clause { Ok(()) } - - - // /// Asserts a clause. - // pub fn assert( - // & self, solver: Solver

, write_pred: WritePrd, need_model: bool - // ) -> Res<()> - // where W: Write, WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - - // if ! need_model && self.has_fun_apps { - - // } else { - - // } - - // Ok(()) - // } - } impl ::std::ops::Index for Clause { diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index eb7925ea..cf020ffb 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -864,12 +864,8 @@ impl Instance { & self, solver: & mut Solver

) -> Res<()> { for side_clause in & self.side_clauses { - side_clause.write( - solver, |_, _, _| panic!( - "illegal side-clause: found predicate application(s)" - ) - ) ? ; - writeln!(solver) ? ; + let side_clause = smt::SmtSideClause::new(side_clause) ; + solver.assert(& side_clause) ? } Ok(()) } @@ -1193,9 +1189,9 @@ impl Instance { writeln!(w, "; Side-clauses") ? ; for side_clause in & self.side_clauses { side_clause.write( - w, |_, _, _| panic!( + w, |w, var_info| write!(w, "{}", var_info.name), |_, _, _| panic!( "illegal side-clause: found predicate application(s)" - ) + ), true ) ? ; writeln!(w) ? ; } @@ -1235,21 +1231,21 @@ impl Instance { writeln!(w) ? ; clause.write( - w, |w, p, args| { + w, |w, var_info| write!(w, "{}", var_info.name), |w, p, args| { if ! args.is_empty() { write!(w, "(") ? } w.write_all( self[p].name.as_bytes() ) ? ; for arg in args.iter() { write!(w, " ") ? ; - arg.write(w, |w, var| w.write_all( clause.vars[var].as_bytes() )) ? + arg.write(w, |w, var| write!(w, "{}", clause.vars[var])) ? } if ! args.is_empty() { write!(w, ")") } else { Ok(()) } - } + }, true ) ? ; writeln!(w) ? ; writeln!(w) ? @@ -1734,15 +1730,17 @@ impl<'a> PebcakFmt<'a> for Clause { & self, w: & mut W, prds: & 'a PrdInfos ) -> IoRes<()> { self.write( - w, |w, prd, args| { + w, |w, var_info| write!(w, "{}", var_info.name), |w, prd, args| { write!(w, "(") ? ; w.write_all( prds[prd].as_bytes() ) ? ; for arg in args.iter() { write!(w, " ") ? ; - arg.write(w, |w, var| w.write_all( self.vars[var].as_bytes() )) ? + arg.write( + w, |w, var| write!(w, "{}", self.vars[var]) + ) ? } write!(w, ")") - } + }, false ) } } diff --git a/src/learning/ice/synth/helpers.rs b/src/learning/ice/synth/helpers.rs index a4ebad46..f4de5553 100644 --- a/src/learning/ice/synth/helpers.rs +++ b/src/learning/ice/synth/helpers.rs @@ -337,15 +337,13 @@ where F: FnMut(Term) -> Res { // Iterate over the sample. for (var_idx, val) in sample.index_iter() { if val.typ() == * typ && val.is_known() { - let var = term::var(var_idx, typ::int()) ; + let var = term::var( var_idx, typ.clone() ) ; - let done = ::learning::ice::synth::helpers::sum_diff_synth( + let done = sum_diff_synth( & ( var.clone(), val.clone() ), & previous, len, & mut f ) ? ; - if done { - return Ok(true) - } + if done { return Ok(true) } previous.push((var, val.clone())) } @@ -406,6 +404,70 @@ where F: FnMut(Term) -> Res { } } +#[test] +fn sum_diff() { + let term = & ( + term::var( 0, typ::int() ), val::int(0) + ) ; + + let others = & [ + (term::var( 1, typ::int() ), val::int(1)), + (term::var( 2, typ::int() ), val::int(2)), + (term::var( 3, typ::int() ), val::int(3)), + ] ; + + let expected = vec![ + "(>= (+ v_0 v_1 v_2) 3)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_2)) (- 3))", + "(= (+ v_0 v_1 v_2 (- 3)) 0)", + "(>= (+ v_1 v_2 (* (- 1) v_0)) 3)", + "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_2)) (- 3))", + "(= (+ v_1 v_2 (* (- 1) v_0) (- 3)) 0)", + "(>= (+ v_0 v_2 (* (- 1) v_1)) 1)", + "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_2)) (- 1))", + "(= (+ v_0 v_2 (- 1) (* (- 1) v_1)) 0)", + "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_1)) 1)", + "(>= (+ v_0 v_1 (* (- 1) v_2)) (- 1))", + "(= (+ v_2 (- 1) (* (- 1) v_0) (* (- 1) v_1)) 0)", + + "(>= (+ v_0 v_1 v_3) 4)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_3)) (- 4))", + "(= (+ v_0 v_1 v_3 (- 4)) 0)", + "(>= (+ v_1 v_3 (* (- 1) v_0)) 4)", + "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_3)) (- 4))", + "(= (+ v_1 v_3 (* (- 1) v_0) (- 4)) 0)", + "(>= (+ v_0 v_3 (* (- 1) v_1)) 2)", + "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_3)) (- 2))", + "(= (+ v_0 v_3 (* (- 1) v_1) (- 2)) 0)", + "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_1)) 2)", + "(>= (+ v_0 v_1 (* (- 1) v_3)) (- 2))", + "(= (+ v_3 (* (- 1) v_0) (* (- 1) v_1) (- 2)) 0)", + + "(>= (+ v_0 v_2 v_3) 5)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_2) (* (- 1) v_3)) (- 5))", + "(= (+ v_0 v_2 v_3 (- 5)) 0)", + "(>= (+ v_2 v_3 (* (- 1) v_0)) 5)", + "(>= (+ v_0 (* (- 1) v_2) (* (- 1) v_3)) (- 5))", + "(= (+ v_2 v_3 (* (- 1) v_0) (- 5)) 0)", + "(>= (+ v_0 v_3 (* (- 1) v_2)) 1)", + "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_3)) (- 1))", + "(= (+ v_0 v_3 (- 1) (* (- 1) v_2)) 0)", + "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_2)) 1)", + "(>= (+ v_0 v_2 (* (- 1) v_3)) (- 1))", + "(= (+ v_3 (- 1) (* (- 1) v_0) (* (- 1) v_2)) 0)", + ] ; + let mut cnt = 0 ; + + sum_diff_synth( + term, others, 3, |term| { + println!("{}", term) ; + assert_eq! { & format!("{}", term), & expected[cnt] } ; + cnt += 1 ; + Ok(false) + }, + ).unwrap() ; +} + /// Arith sum/diff synth. fn iter_sum_diff_synth( diff --git a/src/learning/ice/synth/int.rs b/src/learning/ice/synth/int.rs index f331bdd6..6df009d3 100644 --- a/src/learning/ice/synth/int.rs +++ b/src/learning/ice/synth/int.rs @@ -2,7 +2,7 @@ use common::* ; -// use super::helpers::n_term_arith_synth ; +use super::helpers::n_term_arith_synth ; use super::{ TermVals, TheoSynth } ; @@ -12,6 +12,8 @@ pub struct IntSynth { expressivity: usize, /// The int type. typ: Typ, + /// True if the synth is done. + done: bool, } impl Default for IntSynth { fn default() -> Self { Self::new() } @@ -23,6 +25,7 @@ impl IntSynth { IntSynth { expressivity: 0, typ: typ::int(), + done: false, } } } @@ -30,11 +33,12 @@ impl TheoSynth for IntSynth { fn typ(& self) -> & Typ { & self.typ } fn is_done(& self) -> bool { - self.expressivity > 2 + self.done } fn restart(& mut self) { - self.expressivity = 0 + self.done = false ; + self.expressivity = 0 ; } fn increment(& mut self) { @@ -42,54 +46,41 @@ impl TheoSynth for IntSynth { } fn synth( - & mut self, f: F, sample: & VarVals, others: & mut TermVals, + & mut self, mut f: F, sample: & VarVals, others: & mut TermVals, _profiler: & Profiler ) -> Res where F: FnMut(Term) -> Res { + self.done = false ; match self.expressivity { 0 => profile!( |_profiler| wrap { - simple_int_synth(sample, others, f) + let done = n_term_arith_synth( + sample, others, & self.typ, 1, & mut f + ) ? ; + if ! done { + n_term_arith_synth(sample, others, & self.typ, 2, f) + } else { + Ok(false) + } } "learning", "qual", "synthesis", "int", "level 0" ), + 1 => profile!( |_profiler| wrap { - int_synth_1(sample, others, f) + non_lin_int_synth(sample, others, f) } "learning", "qual", "synthesis", "int", "level 1" ), - 2 => profile!( + + n if n < sample.len() => profile!( |_profiler| wrap { - int_synth_2(sample, others, f) - } "learning", "qual", "synthesis", "int", "level 2" + n_term_arith_synth(sample, others, & self.typ, n + 1, f) + } "learning", "qual", "synthesis", "int", "level 4" ), - _ => Ok(false), - - // 0 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 1, f) - // } "learning", "qual", "synthesis", "int", "level 0" - // ), - // 1 => profile!( - // |_profiler| wrap { - // non_lin_int_synth(sample, others, f) - // } "learning", "qual", "synthesis", "int", "level 1" - // ), - // 2 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 3, f) - // } "learning", "qual", "synthesis", "int", "level 2" - // ), - // 3 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 4, f) - // } "learning", "qual", "synthesis", "int", "level 3" - // ), - // 4 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 4, f) - // } "learning", "qual", "synthesis", "int", "level 4" - // ), - // _ => Ok(false), + + _ => { + self.done = true ; + Ok(false) + }, } } diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index 4f4cd423..1b260eb5 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -22,8 +22,10 @@ pub type TermVals = TermMap ; /// theories. These pairs are the result of projecting/casting/... an argument /// of a different theory to this one. /// -/// It is iterable. Each version generates qualifiers more complex than the -/// previous one, making synthesis more expressive with each call to `next`. +/// A synthesizer generates more and more complex candidate qualifiers with +/// each call to [`increment`][increment]. +/// +/// [increment]: #tymethod.increment (increment method) pub trait TheoSynth { /// Type of values supported by this synthesizer. fn typ(& self) -> & Typ ; @@ -88,19 +90,6 @@ impl SynthSys { |adt| adt.typ() != typ ) { let synth = AdtSynth::new( typ.clone() ) ; - // println!("creating synth for {}", synth.typ()) ; - // println!(" from_typ:") ; - // for fun in & synth.funs.from_typ { - // println!(" - {}", fun.name) - // } - // println!(" to_typ:") ; - // for fun in & synth.funs.to_typ { - // println!(" - {}", fun.name) - // } - // println!(" from_to_typ:") ; - // for fun in & synth.funs.from_to_typ { - // println!(" - {}", fun.name) - // } if synth.can_project_to_int() { set!(int) } if synth.can_project_to_real() { set!(real) } adt.push(synth) @@ -229,7 +218,7 @@ impl SynthSys { int_synth.project( sample, real_synth.typ(), & mut self.cross_synth ) - } "learning", "qual", "synthesis", "real project" + } "learning", "qual", "synthesis", "int project" ) ? } for adt_synth in & mut self.adt { @@ -258,7 +247,7 @@ impl SynthSys { } - /// Runs real synthesis. + /// Runs adt synthesis. pub fn adt_synth( & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler ) -> Res diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 507076e3..f286588a 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -950,11 +950,7 @@ impl<'a> Teacher<'a> { self.solver.push(1) ? } - let cexs = self.get_cex( - cands, clause, bias, - // got_pos_neg_samples && - conf.teacher.max_bias - ).chain_err( + let cexs = self.get_cex(clause, bias, conf.teacher.max_bias).chain_err( || format!("while getting counterexample for clause #{}", clause) ) ? ; @@ -965,11 +961,6 @@ impl<'a> Teacher<'a> { } if ! cexs.is_empty() { - // got_pos_neg_samples = got_pos_neg_samples || ( - // cexs.iter().any( - // |(_, bias)| ! bias.is_none() - // ) - // ) ; let prev = map.insert(clause, cexs) ; debug_assert_eq!(prev, None) } @@ -1068,20 +1059,12 @@ impl<'a> Teacher<'a> { /// Checks if a clause is falsifiable and returns a model if it is. pub fn get_cex( - & mut self, _cands: & Candidates, - clause_idx: ClsIdx, bias: bool, bias_only: bool + & mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool ) -> Res< Vec > { let mut cexs = vec![] ; log! { @debug "working on clause #{}", clause_idx } - // if self.using_rec_funs { - // let falsifiable = self.quantified_checksat(cands, clause_idx) ? ; - // if ! falsifiable { - // return Ok(cexs) - // } - // } - // Macro to avoid borrowing `self.instance`. macro_rules! clause { () => ( & self.instance[clause_idx] ) ; @@ -1161,38 +1144,4 @@ impl<'a> Teacher<'a> { Ok(cexs) } - - /// Checks a clause using qualifiers. - /// - /// Returns `true` if the clause is falsifiable with the current candidates. - /// - /// This is used when manipulating recursive ADTs and functions. - pub fn quantified_checksat( - & mut self, cands: & Candidates, clause: ClsIdx - ) -> Res { - let actlit = self.solver.get_actlit() ? ; - - { - let wrapped = smt::SmtQClause::new( & self.instance[clause] ) ; - - self.solver.assert_act_with( - & actlit, & wrapped, & ( - & self.tru_preds, & self.fls_preds, self.instance.preds() - ) - ) ? - } - - let falsifiable = self.solver.check_sat_act( - Some(& actlit) - ) ? ; - - self.solver.de_actlit(actlit) ? ; - - smt::reset(& mut self.solver, & self.instance) ? ; - - self.define_preds(cands) ? ; - - Ok(falsifiable) - } - } \ No newline at end of file From b856ed23ff4c5ad44c93f5dc0ea98ec0472b7537 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 23 Aug 2018 19:53:17 +0900 Subject: [PATCH 37/94] trying to improve real synth --- src/data/mod.rs | 68 ++++++++++++++-- src/learning/ice/data.rs | 62 ++++++++++++++- src/learning/ice/mod.rs | 19 +++-- src/learning/ice/quals.rs | 82 +++++++++++++++----- src/learning/ice/synth/int.rs | 121 ++--------------------------- src/learning/ice/synth/real.rs | 137 ++++++++------------------------- src/teacher/mod.rs | 7 +- src/val/mod.rs | 2 +- 8 files changed, 239 insertions(+), 259 deletions(-) diff --git a/src/data/mod.rs b/src/data/mod.rs index 572f084e..b3e2ba84 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -32,6 +32,11 @@ pub struct Data { /// Constraints. pub constraints: CstrMap, + /// Positive samples with a single non-partial sample. + pos_single: PrdMap< VarValsSet >, + /// Negative samples with a single non-partial sample. + neg_single: PrdMap< VarValsSet >, + /// Map from samples to constraints. map: PrdMap< VarValsMap >, @@ -57,6 +62,9 @@ impl Clone for Data { constraints: self.constraints.clone(), map: self.map.clone(), + pos_single: self.pos_single.clone(), + neg_single: self.neg_single.clone(), + staged: self.staged.clone(), // Empty anyway. cstr_info: self.cstr_info.clone(), // graph: None, @@ -86,16 +94,22 @@ impl Data { let pred_count = instance.preds().len() ; let ( - mut map, mut pos, mut neg + mut map, mut pos, mut neg, + mut pos_single, mut neg_single, ) = ( PrdMap::with_capacity(pred_count), PrdMap::with_capacity(pred_count), - PrdMap::with_capacity(pred_count) + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), ) ; + for _ in instance.preds() { map.push( VarValsMap::with_capacity(103) ) ; pos.push( VarValsSet::with_capacity(103) ) ; neg.push( VarValsSet::with_capacity(103) ) ; + pos_single.push( VarValsSet::with_capacity(13) ) ; + neg_single.push( VarValsSet::with_capacity(13) ) ; } // let track_samples = instance.track_samples() ; @@ -103,7 +117,7 @@ impl Data { Data { instance, pos, neg, constraints, map, staged: Staged::with_capacity(pred_count), - cstr_info: CstrInfo::new(), + cstr_info: CstrInfo::new(), pos_single, neg_single, // graph: if track_samples { // Some( SampleGraph::new() ) // } else { @@ -533,6 +547,16 @@ impl Data { (pred, mut argss, pos) ) = self.staged.pop() { + macro_rules! single_target_set { + () => ( + if pos { + & mut self.pos_single[pred] + } else { + & mut self.neg_single[pred] + } + ) ; + } + macro_rules! target_set { () => ( if pos { @@ -556,7 +580,18 @@ impl Data { debug_assert! { rmed == 0 } false } else { - let is_new = target_set!().insert(s.clone()) ; + if s.len() > 1 { + let count = s.iter().fold( + 0, |acc, val| if ! val.is_known() { acc + 1 } else { acc } + ) ; + if count + 1 == s.len() { + let _ = single_target_set!().insert( s.clone() ) ; + () + } + } + + let is_new = target_set!().insert( s.clone() ) ; + debug_assert! { is_new } true } @@ -995,11 +1030,17 @@ impl Data { let unc_set = & self.map[pred] ; let pos_set = & self.pos[pred] ; let neg_set = & self.neg[pred] ; - let (mut pos, mut neg, mut unc) = ( + let pos_single_set = & self.pos_single[pred] ; + let neg_single_set = & self.neg_single[pred] ; + + let (mut pos, mut neg, mut unc, mut pos_single, mut neg_single) = ( Vec::with_capacity( pos_set.len() ), Vec::with_capacity( neg_set.len() ), - Vec::with_capacity( unc_set.len() ) + Vec::with_capacity( unc_set.len() ), + Vec::with_capacity( pos_single_set.len() ), + Vec::with_capacity( neg_single_set.len() ), ) ; + for sample in pos_set.iter() { pos.push( sample.clone() ) } @@ -1011,8 +1052,20 @@ impl Data { unc.push( sample.clone() ) } } + + for sample in pos_single_set { + if pos.contains(sample) { + pos_single.push( sample.clone() ) + } + } + for sample in neg_single_set { + if neg.contains(sample) { + neg_single.push( sample.clone() ) + } + } + profile! { self mark "data of" } - CData::new(pos, neg, unc) + CData::new(pos, neg, unc, pos_single, neg_single) } @@ -1459,6 +1512,7 @@ impl Staged { let is_new = set.insert(args) ; // We checked `args` is not subsumed already, so it's necessarily new. debug_assert! { is_new } + true } diff --git a/src/learning/ice/data.rs b/src/learning/ice/data.rs index 2463f445..a712404b 100644 --- a/src/learning/ice/data.rs +++ b/src/learning/ice/data.rs @@ -17,16 +17,23 @@ pub struct CData { unc: Vec, /// Total number of samples. len: f64, + /// Positive samples with a single known value. + pos_single: Vec, + /// Negative samples with a single known value. + neg_single: Vec, } impl CData { /// Constructor. #[inline] - pub fn new(pos: Vec, neg: Vec, unc: Vec) -> Self { + pub fn new( + pos: Vec, neg: Vec, unc: Vec, + pos_single: Vec, neg_single: Vec, + ) -> Self { let len = ( pos.len() + neg.len() + unc.len() ) as f64 ; CData { - pos, neg, unc, len, + pos, neg, unc, len, pos_single, neg_single } } @@ -44,6 +51,16 @@ impl CData { (self.pos, self.neg, self.unc) } + /// Pops a single sample. + pub fn pop_single_sample(& mut self) -> Option { + let res = self.pos_single.pop() ; + if res.is_some() { + res + } else { + self.neg_single.pop() + } + } + /// Adds a positive sample. #[inline] pub fn add_pos(& mut self, pos: VarVals) { @@ -378,14 +395,48 @@ impl CData { Vec::with_capacity( self.pos.len() ), Vec::with_capacity( self.neg.len() ), Vec::with_capacity( self.unc.len() ), + Vec::with_capacity( self.pos_single.len() ), + Vec::with_capacity( self.neg_single.len() ), ), CData::new( Vec::with_capacity( self.pos.len() ), Vec::with_capacity( self.neg.len() ), Vec::with_capacity( self.unc.len() ), + Vec::with_capacity( self.pos_single.len() ), + Vec::with_capacity( self.neg_single.len() ), ) ) ; + for pos_single in self.pos_single { + if let Some(value) = qual.bool_eval( pos_single.get() ).expect( + "During qualifier evaluation" + ) { + if value { + q.pos_single.push(pos_single) + } else { + nq.pos_single.push(pos_single) + } + } else { + q.pos_single.push( pos_single.clone() ) ; + nq.pos_single.push(pos_single) + } + } + + for neg_single in self.neg_single { + if let Some(value) = qual.bool_eval( neg_single.get() ).expect( + "During qualifier evaluation" + ) { + if value { + q.neg_single.push(neg_single) + } else { + nq.neg_single.push(neg_single) + } + } else { + q.neg_single.push( neg_single.clone() ) ; + nq.neg_single.push(neg_single) + } + } + for pos in self.pos { if let Some(value) = qual.bool_eval( pos.get() ).expect( "During qualifier evaluation" @@ -400,6 +451,7 @@ impl CData { nq.add_pos( pos ) } } + for neg in self.neg { if let Some(value) = qual.bool_eval( neg.get() ).expect( "During qualifier evaluation" @@ -414,6 +466,7 @@ impl CData { nq.add_neg( neg ) } } + for unc in self.unc { if let Some(value) = qual.bool_eval( unc.get() ).expect( "During qualifier evaluation" @@ -432,9 +485,14 @@ impl CData { q.pos.shrink_to_fit() ; q.neg.shrink_to_fit() ; q.unc.shrink_to_fit() ; + q.pos_single.shrink_to_fit() ; + q.neg_single.shrink_to_fit() ; + nq.pos.shrink_to_fit() ; nq.neg.shrink_to_fit() ; nq.unc.shrink_to_fit() ; + nq.pos_single.shrink_to_fit() ; + nq.neg_single.shrink_to_fit() ; (q, nq) } diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index 201539b0..8d365b64 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -29,7 +29,6 @@ impl Launcher { pub fn launch( core: & MsgCore, instance: Arc, data: Data, mine: bool ) -> Res<()> { - let mut learner = IceLearner::new( & core, instance, data, mine ).chain_err( @@ -40,6 +39,7 @@ impl Launcher { res } } + impl Learner for Launcher { fn run( & self, core: MsgCore, @@ -587,8 +587,6 @@ impl<'core> IceLearner<'core> { self.unfinished.push( (vec![], data) ) ; - // let mut branch = Vec::with_capacity(17) ; - 'learning: while let Some( (mut branch, data) ) = self.choose_branch(pred) { @@ -766,7 +764,7 @@ impl<'core> IceLearner<'core> { /// /// The `simple` flag forces to use simple, unclassified-agnostic gain. pub fn get_qualifier( - & mut self, pred: PrdIdx, data: CData, simple: bool + & mut self, pred: PrdIdx, mut data: CData, simple: bool ) -> Res< Option< (Term, CData, CData) > > { let simple = data.unc().is_empty() || ( ! data.pos().is_empty() && ! data.neg().is_empty() && ( @@ -792,7 +790,7 @@ impl<'core> IceLearner<'core> { msg! { self => s } } - let mut best_qual = self.get_best_qual(simple, pred, & data) ? ; + let mut best_qual = self.get_best_qual(simple, pred, & mut data) ? ; if let Some((qual, gain)) = best_qual { best_qual = if gain >= self.gain_pivot && gain > 0.0 { @@ -869,15 +867,18 @@ impl<'core> IceLearner<'core> { /// Gets the best qualifier available for some data. pub fn get_best_qual( - & mut self, simple_gain: bool, pred: PrdIdx, data: & CData + & mut self, simple_gain: bool, pred: PrdIdx, data: & mut CData ) -> Res< Option<(Term, f64)> > { let core = & self.core ; + let bias = data.pop_single_sample() ; + // Run simple if in simple mode. if simple_gain { + profile!{ self tick "learning", "qual", "simple gain" } let res = self.qualifiers.maximize( - pred, |qual| { + pred, bias, |qual| { if conf.ice.qual_step { let _ = core.msg( format!("evaluating {} (simple gain)", qual) @@ -902,6 +903,7 @@ impl<'core> IceLearner<'core> { ) ; profile!{ self mark "learning", "qual", "simple gain" } res + } else { let qualifiers = & mut self.qualifiers ; @@ -910,7 +912,7 @@ impl<'core> IceLearner<'core> { profile!{ |self.core._profiler| tick "learning", "qual", "gain" } let res = qualifiers.maximize( - pred, |qual| { + pred, bias, |qual| { if conf.ice.qual_step { let _ = core.msg( format!("evaluating {} (gain)", qual) @@ -937,6 +939,7 @@ impl<'core> IceLearner<'core> { ) ; profile!{ |self.core._profiler| mark "learning", "qual", "gain" } res + } } diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 380440c4..c7a47208 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -402,12 +402,25 @@ fn apply_mappings( quals: & mut NuQuals, clause: & Clause, build_conj: bool, maps: Vec<(PrdIdx, VarHMap, TermSet)>, clause_count: usize, ) -> Res<()> { + let term_count = clause.lhs_terms().len() ; + // Stores the subterms of `lhs_terms`. let mut subterms = Vec::with_capacity(7) ; // Stores all (sub-)terms. - let mut all_terms = TermSet::with_capacity( - clause.lhs_terms().len() - ) ; + let mut all_terms = if term_count <= 100 { + Some( + TermSet::with_capacity( clause.lhs_terms().len() ) + ) + } else { None } ; + + macro_rules! all_terms { + ( $fun:ident( $($args:tt)* ) ) => ( + if let Some(all_terms) = all_terms.as_mut() { + all_terms.$fun($($args)*) ; + } + ) ; + } + // Stores all top terms. let mut conj = TermSet::with_capacity( clause.lhs_terms().len() @@ -415,13 +428,13 @@ fn apply_mappings( // Look for atoms and try to apply the mappings. for (pred, map, app_quals) in maps { - all_terms.clear() ; + all_terms!( clear() ) ; conj.clear() ; for term in clause.lhs_terms() { if let Some( (term, true) ) = term.subst_total(& map) { - all_terms.insert( term.clone() ) ; + all_terms!( insert( term.clone() ) ) ; conj.insert( term.clone() ) ; let term = if let Some(term) = term.rm_neg() { term @@ -435,7 +448,7 @@ fn apply_mappings( while let Some(subterm) = subterms.pop() { if let Some( (qual, true) ) = subterm.subst_total(& map) { - all_terms.insert(qual) ; + all_terms!( insert(qual) ) ; } match subterm.app_inspect() { @@ -460,11 +473,11 @@ fn apply_mappings( // } // } // } else if let Some( (qual, true) ) = subterm.subst_total(& map) { - // all_terms.insert(qual) ; + // all_terms!().insert(qual) ; // }, _ => if let Some( (qual, true) ) = subterm.subst_total(& map) { - all_terms.insert(qual) ; + all_terms!( insert(qual) ) ; } } } @@ -487,14 +500,18 @@ fn apply_mappings( } } - let mut all_terms = all_terms.iter() ; + if let Some(all_terms) = all_terms.as_ref() { + let mut all_terms = all_terms.iter() ; - while let Some(term) = all_terms.next() { - for other in all_terms.clone() { - qual_of_terms( - |qual| { quals.insert(qual, pred) ? ; Ok(()) }, - term, other, clause_count - ) ? + if all_terms.len() <= 100 { + while let Some(term) = all_terms.next() { + for other in all_terms.clone() { + qual_of_terms( + |qual| { quals.insert(qual, pred) ? ; Ok(()) }, + term, other, clause_count + ) ? + } + } } } @@ -774,16 +791,39 @@ impl NuQuals { /// fashion, if any. Early-returns if the criterion is `>=` to the gain pivot /// defined in the configuration at some point. pub fn maximize( - & mut self, pred: PrdIdx, mut crit: Crit + & mut self, pred: PrdIdx, bias: Option, mut crit: Crit ) -> Res< Option<(Term, f64)> > where Crit: FnMut( & Term ) -> Res< Option > { use rand::Rng ; + let var_bias = if let Some(sample) = bias { + let mut set = VarSet::new() ; + for (var, val) in sample.index_iter() { + if val.is_known() { + set.insert(var) ; + } + } + if set.is_empty() { + bail!("empty bias sample in gain maximization") + } + Some(set) + } else { + None + } ; + let mut best = None ; let rng = & mut self.rng ; - let mut quals: Vec<_> = self.quals[pred].iter().map( - |(_, terms)| terms + let mut quals: Vec<_> = self.quals[pred].iter().filter_map( + | (count, terms) | if let Some(var_bias) = var_bias.as_ref() { + if var_bias.len() == ** count { + Some(terms) + } else { + None + } + } else { + Some(terms) + } ).collect() ; if conf.ice.rand_quals { @@ -800,6 +840,12 @@ impl NuQuals { // for terms in terms { for term in terms { + if let Some(var_bias) = var_bias.as_ref() { + if var_bias != & term::vars(term) { + continue + } + } + if let Some(value) = crit(term) ? { best = if value > 0.9999 { return Ok( Some((term.clone(), value)) ) diff --git a/src/learning/ice/synth/int.rs b/src/learning/ice/synth/int.rs index 6df009d3..096368f3 100644 --- a/src/learning/ice/synth/int.rs +++ b/src/learning/ice/synth/int.rs @@ -1,9 +1,10 @@ //! Qualifier synthesis in the theory of integers. use common::* ; - -use super::helpers::n_term_arith_synth ; -use super::{ TermVals, TheoSynth } ; +use super::{ + helpers::n_term_arith_synth, + TermVals, TheoSynth, +} ; /// Integer qualifier synthesizer. @@ -60,7 +61,7 @@ impl TheoSynth for IntSynth { if ! done { n_term_arith_synth(sample, others, & self.typ, 2, f) } else { - Ok(false) + Ok(true) } } "learning", "qual", "synthesis", "int", "level 0" ), @@ -74,7 +75,7 @@ impl TheoSynth for IntSynth { n if n < sample.len() => profile!( |_profiler| wrap { n_term_arith_synth(sample, others, & self.typ, n + 1, f) - } "learning", "qual", "synthesis", "int", "level 4" + } "learning", "qual", "synthesis", "int", "level n > 1" ), _ => { @@ -139,113 +140,3 @@ where F: FnMut(Term) -> Res { Ok(false) } - -/// Lowest level of int synthesis. -/// -/// All `v*` are variables. Synthesizes qualifiers of the form -/// -/// - `v = n`, `v <= n`, `v >= n`, -/// - `v_1 = v_2`, `v_1 = - v_2`, -/// - `v_1 + v_2 >= n`, `v_1 + v_2 <= n`, -/// - `v_1 - v_2 >= n`, `v_1 - v_2 <= n`, -pub fn simple_int_synth( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::I(ref i) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - simple_arith_synth! { previous_int, f, int | var = ( i.clone() ) } - } - } - - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::I(ref val) = val.get() { - simple_arith_synth! { previous_int, f, int | term = val.clone() } - } else { - bail!( - "int synthesis expects projected integers (2), \ - got {} for {}", val, term - ) - } - } - - Ok(false) -} - - - -/// Level 1 for int synthesis. -pub fn int_synth_1( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::I(ref i) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - arith_synth_non_lin! { - previous_int, f, int | var = ( i.clone() ) - } - } - } - - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::I(ref val) = val.get() { - arith_synth_non_lin! { - previous_int, f, int | term = val.clone() - } - } else { - bail!( - "int synthesis expects projected integers (3), \ - got {} for {}", val, term - ) - } - } - - Ok(false) -} - - -/// Level 2 for int synthesis. -pub fn int_synth_2( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::I(ref i) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - arith_synth_three_terms! { - previous_int, f, int | var = ( i.clone() ) - } - } - } - - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::I(ref val) = val.get() { - arith_synth_three_terms! { - previous_int, f, int | term = val.clone() - } - } else { - bail!( - "int synthesis expects projected integers (4), \ - got {} for {}", val, term - ) - } - } - - Ok(false) -} - - diff --git a/src/learning/ice/synth/real.rs b/src/learning/ice/synth/real.rs index 77733dfe..6a20af36 100644 --- a/src/learning/ice/synth/real.rs +++ b/src/learning/ice/synth/real.rs @@ -1,9 +1,10 @@ //! Qualifier synthesis in the theory of reals. use common::* ; - -// use super::helpers::n_term_arith_synth ; -use super::{ TermVals, TheoSynth } ; +use super::{ + helpers::n_term_arith_synth, + TermVals, TheoSynth, +} ; /// Real qualifier synthesizer. @@ -12,6 +13,8 @@ pub struct RealSynth { expressivity: usize, /// The real type. typ: Typ, + /// True if the synth is done. + done: bool, } impl Default for RealSynth { fn default() -> Self { Self::new() } @@ -23,6 +26,7 @@ impl RealSynth { RealSynth { expressivity: 0, typ: typ::real(), + done: false, } } } @@ -30,10 +34,11 @@ impl TheoSynth for RealSynth { fn typ(& self) -> & Typ { & self.typ } fn is_done(& self) -> bool { - self.expressivity > 2 + self.done } fn restart(& mut self) { + self.done = false ; self.expressivity = 0 } @@ -42,49 +47,41 @@ impl TheoSynth for RealSynth { } fn synth( - & mut self, f: F, sample: & VarVals, others: & mut TermVals, + & mut self, mut f: F, sample: & VarVals, others: & mut TermVals, _profiler: & Profiler ) -> Res where F: FnMut(Term) -> Res { + self.done = false ; match self.expressivity { 0 => profile!( |_profiler| wrap { - simple_real_synth(sample, others, f) + let done = n_term_arith_synth( + sample, others, & self.typ, 1, & mut f + ) ? ; + if ! done { + n_term_arith_synth(sample, others, & self.typ, 2, f) + } else { + Ok(true) + } } "learning", "qual", "synthesis", "real", "level 0" ), + 1 => profile!( |_profiler| wrap { - real_synth_1(sample, others, f) + non_lin_real_synth(sample, others, f) } "learning", "qual", "synthesis", "real", "level 1" ), - 2 => profile!( + + n if n < 3 => profile!( |_profiler| wrap { - real_synth_2(sample, others, f) - } "learning", "qual", "synthesis", "real", "level 2" + n_term_arith_synth(sample, others, & self.typ, n + 1, f) + } "learning", "qual", "synthesis", "real", "level n > 1" ), - _ => Ok(false), - - // 0 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 1, f) - // } "learning", "qual", "synthesis", "real", "level 0" - // ), - // 1 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 1, f) - // } "learning", "qual", "synthesis", "real", "level 1" - // ), - // 2 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 2, f) - // } "learning", "qual", "synthesis", "real", "level 2" - // ), - // 3 => profile!( - // |_profiler| wrap { - // n_term_arith_synth(sample, others, & self.typ, 3, f) - // } "learning", "qual", "synthesis", "real", "level 3" - // ), - // _ => Ok(false), + + _ => { + self.done = true ; + Ok(false) + }, } } @@ -109,46 +106,8 @@ impl TheoSynth for RealSynth { -/// Lowest level of real synthesis. -/// -/// All `v_*` are variables. Synthesizes qualifiers of the form -/// -/// - `v = n`, `v <= n`, `v >= n`, -/// - `v_1 = v_2`, `v_1 = - v_2`, -/// - `v_1 + v_2 >= n`, `v_1 + v_2 <= n`, -/// - `v_1 - v_2 >= n`, `v_1 - v_2 <= n`, -pub fn simple_real_synth( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::R(ref r) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - simple_arith_synth! { previous_real, f, real | var = ( r.clone() ) } - } - } - - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::R(ref r) = val.get() { - simple_arith_synth! { previous_real, f, real | term = r.clone() } - } else { - bail!( - "real synthesis expects projected reals, got {} for {}", val, term - ) - } - } - - Ok(false) -} - - - /// Level 1 for real synthesis. -pub fn real_synth_1( +pub fn non_lin_real_synth( sample: & VarVals, others: & mut TermVals, mut f: F ) -> Res where F: FnMut(Term) -> Res { @@ -179,37 +138,3 @@ where F: FnMut(Term) -> Res { Ok(false) } - - -/// Level 2 for real synthesis. -pub fn real_synth_2( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::R(ref r) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - arith_synth_three_terms! { - previous_real, f, real | var = ( r.clone() ) - } - } - } - - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::R(ref r) = val.get() { - arith_synth_three_terms! { - previous_real, f, real | term = r.clone() - } - } else { - bail!( - "real synthesis expects projected reals, got {} for {}", val, term - ) - } - } - - Ok(false) -} diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index f286588a..9eb9ff01 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -49,8 +49,11 @@ pub fn start_class( Ok( TeachRes::Unsat(core) ) }, _ => { - conf.check_timeout() ? ; - Err(e) + if let Err(tmo) = conf.check_timeout() { + Err(tmo) + } else { + Err(e) + } }, }, } ; diff --git a/src/val/mod.rs b/src/val/mod.rs index 0f92daf8..36a3c50e 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -1486,7 +1486,7 @@ impl_fmt!{ } match curr { - RVal::N(ref t) => write!(fmt, "_[{}]", t) ?, + RVal::N(_) => write!(fmt, "_") ?, RVal::I(ref i) => int_to_smt!(fmt, i) ?, RVal::R(ref r) => rat_to_smt!(fmt, r) ?, RVal::B(b) => write!(fmt, "{}", b) ?, From 18660075acc947fc4d3e6542dbb8bd5881623f1c Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 24 Aug 2018 18:47:19 +0900 Subject: [PATCH 38/94] strengthening by strict negative clauses --- src/common/config.rs | 18 ++- src/data/mod.rs | 24 ++-- src/instance/instance/mod.rs | 34 +++++- src/instance/instance/pre_instance.rs | 20 +++- src/instance/preproc/mod.rs | 12 +- src/instance/preproc/strict_neg_clauses.rs | 129 +++++++++++++++++++++ src/teacher/mod.rs | 69 ++++++++--- 7 files changed, 261 insertions(+), 45 deletions(-) create mode 100644 src/instance/preproc/strict_neg_clauses.rs diff --git a/src/common/config.rs b/src/common/config.rs index ad47ce10..cd22553f 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -336,6 +336,9 @@ pub struct PreprocConf { /// Allows clause sorting when splitting. pub split_sort: bool, + + /// Strengthening by strict clauses. + pub strict_neg: bool, } impl SubConf for PreprocConf { fn need_out_dir(& self) -> bool { @@ -602,6 +605,18 @@ impl PreprocConf { true ).number_of_values(1).display_order( order() ) + ).arg( + + Arg::with_name("strict_neg").long("--strict_neg").help( + "(de)activates strengthening by strict negative clauses" + ).validator( + bool_validator + ).value_name( + bool_format + ).default_value("on").hidden(true).takes_value( + true + ).number_of_values(1).display_order( order() ) + ) } @@ -623,12 +638,13 @@ impl PreprocConf { let neg_unroll = bool_of_matches(matches, "neg_unroll") ; let split_strengthen = bool_of_matches(matches, "split_strengthen") ; let split_sort = bool_of_matches(matches, "split_sort") ; + let strict_neg = bool_of_matches(matches, "strict_neg") ; PreprocConf { dump, dump_pred_dep, active, reduction, one_rhs, one_rhs_full, one_lhs, one_lhs_full, cfg_red, arg_red, prune_terms, runroll, pos_unroll, neg_unroll, - split_strengthen, split_sort + split_strengthen, split_sort, strict_neg, } } } diff --git a/src/data/mod.rs b/src/data/mod.rs index b3e2ba84..b78883cf 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -295,10 +295,9 @@ impl Data { /// Does not propagate. pub fn add_raw_pos( & mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals - ) -> () { + ) -> bool { let args = var_to::vals::new(args) ; - self.add_pos(clause, pred, args.clone()) ; - () + self.add_pos( clause, pred, args.clone() ) } /// Adds a negative example. @@ -308,10 +307,9 @@ impl Data { /// Does not propagate. pub fn add_raw_neg( & mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals - ) -> () { + ) -> bool { let args = var_to::vals::new(args) ; - self.add_neg(clause, pred, args.clone()) ; - () + self.add_neg( clause, pred, args.clone() ) } @@ -748,14 +746,14 @@ impl Data { pub fn add_data( & mut self, clause: ClsIdx, mut lhs: Vec< (PrdIdx, RVarVals) >, rhs: Option<(PrdIdx, RVarVals)>, - ) -> Res<()> { + ) -> Res { let rhs = match rhs { Some((pred, sample)) => if lhs.is_empty() { // Positive sample. - self.add_raw_pos(clause, pred, sample) ; - return Ok(()) + let new = self.add_raw_pos(clause, pred, sample) ; + return Ok(new) } else { // Constraint. Some((pred, sample)) @@ -767,8 +765,8 @@ impl Data { "failed pop on vector of length 1" ) ; debug_assert_eq! { lhs.len(), 0 } - self.add_raw_neg(clause, pred, sample) ; - return Ok(()) + let new = self.add_raw_neg(clause, pred, sample) ; + return Ok(new) } else { // Constraint. None @@ -784,9 +782,7 @@ impl Data { ) } - self.add_cstr(clause, lhs, rhs) ? ; - - Ok(()) + self.add_cstr(clause, lhs, rhs) } diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index cf020ffb..2876edd8 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -44,6 +44,9 @@ pub struct Instance { /// Populated by the `finalize` function. sorted_pred_terms: Vec, + /// Strengthener for predicates. + pred_str: PrdMap< Option >, + /// Side-clauses. /// /// A side clause @@ -128,6 +131,7 @@ impl Instance { old_var_maps: PrdMap::with_capacity(pred_capa), pred_terms: PrdMap::with_capacity(pred_capa), sorted_pred_terms: Vec::with_capacity(pred_capa), + pred_str: PrdMap::with_capacity(pred_capa), side_clauses: Vec::with_capacity(7), clauses: ClsMap::with_capacity(clause_capa), @@ -166,6 +170,7 @@ impl Instance { old_var_maps: PrdMap::with_capacity(self.old_preds.len()), pred_terms: self.pred_terms.clone(), sorted_pred_terms: Vec::with_capacity( self.preds.len() ), + pred_str: vec! [ None ; self.old_preds.len() ].into(), side_clauses: self.side_clauses.clone(), clauses: self.clauses.clone(), @@ -711,6 +716,7 @@ impl Instance { name, idx, sig } ) ; self.pred_terms.push(None) ; + self.pred_str.push(None) ; self.pred_to_clauses.push( ( ClsSet::with_capacity(17), ClsSet::with_capacity(17) ) @@ -718,6 +724,21 @@ impl Instance { idx } + + /// Sets the strengthener for a predicate. + pub fn set_str(& mut self, pred: PrdIdx, term: Term) -> Option { + ::std::mem::replace( + & mut self.pred_str[pred], + Some(term) + ) + } + + /// Retrieves the strengthener for a predicate if any. + pub fn get_str(& self, pred: PrdIdx) -> Option<& Term> { + self.pred_str[pred].as_ref() + } + + /// Removes and returns the indices of the clauses `pred` appears in the lhs /// of from `self.pred_to_clauses`. fn unlink_pred_lhs(& mut self, pred: PrdIdx, lhs: & mut LHS) @@ -1574,7 +1595,7 @@ impl Instance { /// Returns `true` if some new data was generated. pub fn clause_cex_to_data( & self, data: & mut Data, clause_idx: ClsIdx, cex: BCex - ) -> Res<()> { + ) -> Res { let (mut cex, bias) = cex ; if_log! { @6 @@ -1651,19 +1672,22 @@ impl Instance { pub fn cexs_to_data( & self, data: & mut Data, cexs: Cexs ) -> Res { - let metrics = data.metrics() ; + let mut changed = false ; for (clause_idx, cexs) in cexs { log! { @5 "adding cexs for #{}", clause_idx } for cex in cexs { - self.clause_cex_to_data(data, clause_idx, cex) ? + let new_stuff = self.clause_cex_to_data( + data, clause_idx, cex + ) ? ; + changed = changed || new_stuff } } - data.propagate() ? ; + let (pos, neg) = data.propagate() ? ; - Ok( metrics != data.metrics() ) + Ok( changed || pos > 0 || neg > 0 ) } } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index c196a879..87914097 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -257,12 +257,20 @@ impl<'a> PreInstance<'a> { /// Strict negative clauses. - pub fn strict_neg_clauses(& self) -> impl Iterator { - self.clauses.iter().filter( - |clause| clause.rhs().is_none() && clause.lhs_preds().len() == 1 && ( - clause.lhs_preds().iter().next().map( - |(_, apps)| apps.len() == 1 - ).unwrap_or(false) + pub fn strict_neg_clauses(& mut self) -> ( + & mut ExtractionCxt, & Instance, impl Iterator + ) { + let extraction = & mut self.extraction ; + let instance = & self.instance ; + let clauses = & instance.clauses ; + ( + extraction, instance, clauses.index_iter().filter( + |(_, clause)| clause.rhs().is_none() + && clause.lhs_preds().len() == 1 && ( + clause.lhs_preds().iter().next().map( + |(_, apps)| apps.len() == 1 + ).unwrap_or(false) + ) ) ) } diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index 9eb313ba..70f206cf 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -14,6 +14,7 @@ mod cfg_red ; mod arg_red ; mod bias_unroll ; mod unroll ; +mod strict_neg_clauses ; use self::{ one_lhs::OneLhs, @@ -22,6 +23,7 @@ use self::{ cfg_red::CfgRed, bias_unroll:: BiasedUnroll, unroll::RUnroll, + strict_neg_clauses::StrictNeg, } ; @@ -301,6 +303,8 @@ pub struct Reductor<'a> { biased_unroll: Option, /// Optional reverse unroller. runroll: Option, + /// Optional strengthener by strict negative clauses. + strict_neg: Option, } impl<'a> Reductor<'a> { /// Constructor. @@ -342,12 +346,16 @@ impl<'a> Reductor<'a> { let runroll = some_new! { RUnroll if neg_unroll } ; + let strict_neg = some_new! { + StrictNeg if strict_neg + } ; Ok( Reductor { instance, simplify, arg_red, one_rhs, one_lhs, cfg_red, biased_unroll, runroll, + strict_neg, } ) } @@ -454,7 +462,7 @@ impl<'a> Reductor<'a> { run! { simplify } ; } - let strict_neg_count = self.instance.strict_neg_clauses().fold( + let strict_neg_count = self.instance.strict_neg_clauses().2.fold( 0, |acc, _| acc + 1 ) ; if strict_neg_count <= 1 @@ -466,6 +474,8 @@ impl<'a> Reductor<'a> { } } + run! { strict_neg } ; + utils::register_final_stats(& self.instance, _profiler) ? ; Ok(()) diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs new file mode 100644 index 00000000..d1632c65 --- /dev/null +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -0,0 +1,129 @@ +//! Strict negative clause. + +use common::* ; +use instance::{ + instance::PreInstance, preproc::{ + RedStrat, utils::ExtractRes + }, +} ; + +pub struct StrictNeg ; + +impl RedStrat for StrictNeg { + fn name(& self) -> & 'static str { "one_rhs" } + + fn new(_: & Instance) -> Self { StrictNeg } + + fn apply( + & mut self, instance: & mut PreInstance + ) -> Res { + let mut info = RedInfo::new() ; + + let mut partial_defs = PrdHMap::new() ; + let mut clauses_to_rm = Vec::new() ; + + macro_rules! pdef { + ($pred:expr => set false) => ({ + partial_defs.remove( & $pred ) ; + partial_defs.insert($pred, None) ; + () + }) ; + + ($pred:expr => add $term:expr) => ({ + if let Some(vec) = partial_defs.entry($pred).or_insert_with( + || Some( Vec::new() ) + ).as_mut() { + vec.push($term) + } + }) ; + } + + scoped! { + let ( + extractor, instance, strict_clauses + ) = instance.strict_neg_clauses() ; + + for (clause_idx, clause) in strict_clauses { + + let (pred, args) = if let Some( + (pred, argss) + ) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (* pred, args) + } else { + bail!("inconsistent instance state") + } + } else { + bail!("inconsistent instance state") + } ; + + match extractor.terms_of_lhs_app( + false, instance, clause.vars(), + ( clause.lhs_terms(), clause.lhs_preds() ), + clause.rhs(), (pred, args) + ) ? { + ExtractRes::Trivial | + ExtractRes::SuccessTrue => clauses_to_rm.push( clause_idx ), + + ExtractRes::SuccessFalse => pdef!(pred => set false), + + ExtractRes::Success( (qvars, pred_app, tterms) ) => + if qvars.is_empty() { + if pred_app.is_some() + || ! tterms.preds().is_empty() { + bail!("inconsistent instance state") + } + + let terms = tterms.terms() ; + + if terms.is_empty() { + pdef!(pred => set false) + } else { + let term = term::or( + terms.iter().map( + |term| term::not( term.clone() ) + ).collect() + ) ; + pdef!(pred => add term) + } + }, + + ExtractRes::Failed => (), + } + + } + } + + if ! clauses_to_rm.is_empty() { + info.clauses_rmed += clauses_to_rm.len() ; + instance.forget_clauses(& mut clauses_to_rm) ? + } + + for (pred, terms_opt) in partial_defs { + + if let Some(mut terms) = terms_opt { + if let Some(term) = instance.get_str(pred) { + terms.push( term.clone() ) + } + let conj = term::and( terms ) ; + match conj.bool() { + Some(true) => (), + Some(false) => { + info.preds += 1 ; + info += instance.force_false(pred) ? + }, + None => { + instance.set_str(pred, conj) ; + }, + } + } else { + info.preds += 1 ; + info += instance.force_false(pred) ? + } + + } + + + Ok(info) + } +} \ No newline at end of file diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 9eb9ff01..9994d6dd 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -270,6 +270,44 @@ impl<'a> Teacher<'a> { ) } + /// Model from some candidates. + fn model_of_candidates( + & self, mut cands: Candidates + ) -> Candidates { + for (pred, cand) in cands.index_iter_mut() { + if let Some(cand) = cand.as_mut() { + if let Some(other) = self.instance.get_str(pred) { + * cand = term::and( + vec![ cand.clone(), other.clone() ] + ) + } + } + } + cands + } + + + /// Completes some candidates with partial-model and partial-defs. + fn complete_candidates( + & self, mut cands: Candidates + ) -> Candidates { + for (pred, cand) in cands.index_iter_mut() { + if let Some(cand) = cand.as_mut() { + let mut others = None ; + if let Some(other) = self.instance.get_str(pred) { + others.get_or_insert_with(Vec::new).push( other.clone() ) + } + if let Some(other) = self.partial_model.get(& pred) { + others.get_or_insert_with(Vec::new).push( other.clone() ) + } + if let Some(mut others) = others { + others.push( cand.clone() ) ; + * cand = term::and(others) + } + } + } + cands + } /// Runs the initial check and registers the data. @@ -279,7 +317,9 @@ impl<'a> Teacher<'a> { if cexs.is_empty() { log_debug!{ "solved by initial candidate..." } return Ok( - Some( TeachRes::Model(cands) ) + Some( + TeachRes::Model( self.model_of_candidates(cands) ) + ) ) } @@ -552,7 +592,9 @@ impl<'a> Teacher<'a> { if cexs.is_empty() { return Ok( - Some( TeachRes::Model(candidates) ) + Some( + TeachRes::Model( self.model_of_candidates(candidates) ) + ) ) } @@ -630,7 +672,11 @@ impl<'a> Teacher<'a> { MsgKind::Cands(cands) => { profile!{ self "candidates" => add 1 } if let Id::Learner(idx) = id { - return Ok( Either::Left( (idx, cands) ) ) + return Ok( + Either::Left( + ( idx, self.complete_candidates(cands) ) + ) + ) } else { bail!("received candidates from {}", id) } @@ -759,6 +805,8 @@ impl<'a> Teacher<'a> { } } + let cands = self.complete_candidates(cands) ; + if_verb! { log_verb! { " initial candidates:" } for (pred, cand) in cands.index_iter() { @@ -780,18 +828,8 @@ impl<'a> Teacher<'a> { pub fn define_preds(& mut self, cands: & Candidates) -> Res<()> { for (pred, cand) in cands.index_iter() { if let Some(ref term) = * cand { - let term = if let Some(other) = self.partial_model.get(& pred) { - term::and( vec![term.clone(), other.clone()] ) - } else { - term.clone() - } ; match term.bool() { None => { - let term = if let Some(other) = self.partial_model.get(& pred) { - term::and( vec![term.clone(), other.clone()] ) - } else { - term.clone() - } ; let pred = & self.instance[pred] ; let sig: Vec<_> = pred.sig.index_iter().map( |(var, typ)| (var, typ.get()) @@ -821,11 +859,6 @@ impl<'a> Teacher<'a> { for (pred, cand) in cands.index_iter() { if let Some(ref term) = * cand { - let term = if let Some(other) = self.partial_model.get(& pred) { - term::and( vec![term.clone(), other.clone()] ) - } else { - term.clone() - } ; match term.bool() { Some(true) => { From 6a5cb187278bbc6cb84dfbf2d119e21be618341b Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 28 Aug 2018 12:03:22 +0900 Subject: [PATCH 39/94] improvements in parsing and checking --- src/check/mod.rs | 186 ++++++++++++++++++++++++++--------------------- src/parse/mod.rs | 27 +++++-- 2 files changed, 125 insertions(+), 88 deletions(-) diff --git a/src/check/mod.rs b/src/check/mod.rs index 0158ab1e..7ff2649c 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -215,106 +215,126 @@ impl Data { Self::new(input, output) } - /// Checks the output data works with the input data using an SMT solver. - pub fn check(& self, solver: & mut Solver) -> Res<()> { - - let mut okay = true ; - let mut count = 0 ; + /// Checks a single clause. + pub fn check_clause( + & self, solver: & mut Solver, + Clause { args, body }: & Clause, count: usize, + ) -> Res { + solver.reset() ? ; + + for unknown in & self.input.unknown { + use std::io::Write ; + writeln!(solver, "{}", unknown) ? + } - // Check all clauses one by one. - for & Clause { - ref args, ref body // ref lets, ref lhs, ref rhs - } in & self.input.clauses { - solver.reset() ? ; - - for unknown in & self.input.unknown { - use std::io::Write ; - writeln!(solver, "{}", unknown) ? - } + // Define all functions. + for & FunDef { + ref name, ref args, ref typ, ref body + } in & self.input.fun_defs { + solver.define_fun( + name, args, typ, body + ) ? + } - // Define all functions. - for & FunDef { - ref name, ref args, ref typ, ref body - } in & self.input.fun_defs { + // Define all predicates. + for & PredDef { + ref pred, ref args, ref body + } in & self.output.pred_defs { + if let Some(body) = body.as_ref() { solver.define_fun( - name, args, typ, body + pred, args, & "Bool".to_string(), body + ) ? + } else { + solver.declare_fun( + pred, + & args.iter().map( + |& (_, ref typ)| typ.clone() + ).collect::>(), + & "Bool".to_string() ) ? } + } - // Define all predicates. - for & PredDef { - ref pred, ref args, ref body - } in & self.output.pred_defs { - if let Some(body) = body.as_ref() { - solver.define_fun( - pred, args, & "Bool".to_string(), body - ) ? - } else { - solver.declare_fun( - pred, - & args.iter().map( - |& (_, ref typ)| typ.clone() - ).collect::>(), - & "Bool".to_string() - ) ? - } - } + // Declare arguments. + for & (ref ident, ref typ) in args { + solver.declare_const(ident, typ) ? + } - // Declare arguments. - for & (ref ident, ref typ) in args { - solver.declare_const(ident, typ) ? + solver.assert( & format!("(not {})", body) ) ? ; + + let res = solver.check_sat_or_unk() ? ; + + if let Some(true) = res { + let exprs: Vec<_> = args.iter().map( + |& (ref id, _)| id.clone() + ).collect() ; + let model = solver.get_values(& exprs) ? ; + println!() ; + println!("({} \"", conf.bad("error")) ; + println!(" clause {} is falsifiable with {{", count) ; + // print!( " ") ; + // for & (ref id, ref ty) in args { + // print!(" ({} {})", id, ty) + // } + // println!() ; + // println!(" (=>") ; + // println!(" (and") ; + // for lhs in lhs { + // println!(" {}", lhs) + // } + // println!(" ) {}", rhs) ; + // println!(" )") ; + // println!(" is falsifiable with {{") ; + for (ident, value) in model { + println!(" {}: {},", ident, value) } + println!(" }}") ; + println!("\")") ; + println!() ; + log_info!("clause {} is fine", count) ; + Ok(false) + } else if let Some(false) = res { + Ok(true) + } else { + bail!( + "clause {}'s check resulted in unknown", count + ) + } + } - solver.assert( & format!("(not {})", body) ) ? ; - - let res = solver.check_sat_or_unk() ? ; - - if let Some(true) = res { - okay = false ; - let exprs: Vec<_> = args.iter().map( - |& (ref id, _)| id.clone() - ).collect() ; - let model = solver.get_values(& exprs) ? ; - println!() ; - println!("({} \"", conf.bad("error")) ; - println!(" clause {} is falsifiable with {{", count) ; - // print!( " ") ; - // for & (ref id, ref ty) in args { - // print!(" ({} {})", id, ty) - // } - // println!() ; - // println!(" (=>") ; - // println!(" (and") ; - // for lhs in lhs { - // println!(" {}", lhs) - // } - // println!(" ) {}", rhs) ; - // println!(" )") ; - // println!(" is falsifiable with {{") ; - for (ident, value) in model { - println!(" {}: {},", ident, value) - } - println!(" }}") ; - println!("\")") ; - println!() - } else if let Some(false) = res { - log_info!("clause {} is fine", count) - } else { - warn!( - "clause {}'s check resulted in unknown, assuming it's fine", count - ) - } + /// Checks the output data works with the input data using an SMT solver. + pub fn check(& self, solver: & mut Solver) -> Res<()> { + + let mut okay = true ; + let mut err = false ; - count += 1 ; + // Check all clauses one by one. + for (count, clause) in self.input.clauses.iter().enumerate() { + match self.check_clause(solver, clause, count) { + Ok(ok) => okay = okay && ok, + Err(e) => { + err = true ; + let e = e.chain_err( + || format!( + "while checking clause {}", count + ) + ) ; + print_err(& e) + }, + } } if ! okay { bail!( "predicates do not verify all the clauses of the input file" ) + } else if err { + bail!( + "at least one error while checking the clauses" + ) + } else { + Ok(()) } - - Ok(()) } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index f1d0af78..00537ac0 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2667,6 +2667,9 @@ impl<'cxt, 's> Parser<'cxt, 's> { break 'read_kids None } else { + for fun in self.functions.keys() { + println!("- {}", fun) + } bail!( self.error( pos, format!( @@ -2787,6 +2790,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { fn define_fun( & mut self, instance: & mut Instance ) -> Res { + use fun::RFun ; + if ! self.word_opt(keywords::cmd::def_fun) { return Ok(false) } @@ -2827,12 +2832,24 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) ? } - let prev = instance.add_define_fun(name, var_info, body) ; + if let Some(term) = body.to_term() ? { + let mut fun = RFun::new(name, var_info, out_sort) ; + fun.set_def(term) ; + let _ = fun::mk(fun).chain_err( + || self.error(name_pos, "while registering this function") + ) ? ; + () + + } else { + + let prev = instance.add_define_fun(name, var_info, body) ; + + if prev.is_some() { + bail!( + self.error(name_pos, format!("redefinition of {}", conf.emph(name))) + ) + } - if prev.is_some() { - bail!( - self.error(name_pos, format!("redefinition of {}", conf.emph(name))) - ) } Ok(true) From 85f6683d55e8efeab46fe7ea69b99d5bb59dd7d8 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 28 Aug 2018 15:03:16 +0900 Subject: [PATCH 40/94] improved term simplifications for datatypes --- src/dtyp/mod.rs | 19 ++++ src/learning/ice/synth/adt.rs | 3 - src/term/factory.rs | 107 +++----------------- src/term/mod.rs | 9 ++ src/term/simplify.rs | 182 ++++++++++++++++++++++++++++++++++ src/val/mod.rs | 97 +++++++++++------- 6 files changed, 286 insertions(+), 131 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 026740d6..7eb1942c 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -877,6 +877,25 @@ impl RDTyp { Ok(()) } + + /// Returns a recursive constructor. + /// + /// Only returns something if + /// + /// - there are only two constructors + /// - one of them is recursive + pub fn rec_constructor(& self) -> Option<& str> { + if self.news.len() == 2 { + for (new, args) in & self.news { + for (_, ptyp) in args { + if ptyp.mentions_dtyp(& self.name) { + return Some(new) + } + } + } + } + None + } } impl_fmt! { RDTyp(self, fmt) { write!(fmt, "{}", self.name) } diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs index 8d5054cb..a64a1c56 100644 --- a/src/learning/ice/synth/adt.rs +++ b/src/learning/ice/synth/adt.rs @@ -69,10 +69,7 @@ impl TheoSynth for AdtSynth { fn project( & self, sample: & VarVals, typ: & Typ, map: & mut TermVals ) -> Res<()> { - // println!("projecting {} to {}", self.typ, typ) ; for fun in & self.funs.from_typ { - // println!("> {} (-> {})", fun.name, fun.typ) ; - if & fun.typ != typ { continue } diff --git a/src/term/factory.rs b/src/term/factory.rs index 31fb2201..8e511d83 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -311,100 +311,10 @@ pub fn val(val: Val) -> Term { } -/// Tries to create a constant datatype constructor. -fn cst_dtyp_new( - typ: Typ, name: String, args: Vec -) -> Either)> { - if args.is_empty() { - return Either::Left( - val::dtyp_new( typ, name, vec![] ) - ) - } - - let mut nu_args = None ; - - for arg in & args { - if let Some(val) = arg.val() { - nu_args.get_or_insert_with( - || Vec::with_capacity( args.len() ) - ).push(val) - } else { - nu_args = None ; - break - } - } - - if let Some(args) = nu_args { - Either::Left( val::dtyp_new(typ, name, args) ) - } else { - Either::Right( (typ, name, args) ) - } -} - - /// Creates a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { - let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { - Either::Left(val) => { - return cst(val) - }, - Either::Right(stuff) => stuff, - } ; - - if let Some((dtyp, prms)) = typ.dtyp_inspect() { - if let Some(fargs) = dtyp.news.get(& name) { - if args.len() != fargs.len() { - panic!( - "constructor `{}` for `{}` expects {} arguments, found {}", - conf.emph(& name), conf.emph(& dtyp.name), - fargs.len(), args.len() - ) - } - - for (arg, param) in args.iter_mut().zip( fargs.iter() ) { - let typ = param.1.to_type(prms).unwrap_or_else( - |_| panic!("ill-formed datatype constructor: {}", typ) - ) ; - if let Some(typ) = typ.merge( & arg.typ() ) { - if let Some(nu_arg) = arg.force_dtyp(typ) { - * arg = nu_arg - } - } - } - } else { - panic!( - "datatype `{}` has no constructor named `{}`", - conf.emph(& dtyp.name), conf.bad(& name) - ) - } - } else { - panic!("ill-typed datatype constructor: {}", typ) - } - - let mut vals = if args.is_empty() { - Some(vec![]) - } else { - None - } ; - - for arg in & args { - if let Some(val) = arg.val() { - vals.get_or_insert_with( - || Vec::with_capacity( args.len() ) - ).push(val) - } else { - vals = None ; - break - } - } - - if let Some(vals) = vals { - debug_assert_eq! { vals.len(), args.len() } - val( val::dtyp_new(typ, name, vals) ) - } else { - debug_assert!( ! args.is_empty() ) ; - factory.mk( RTerm::DTypNew { typ, name, args } ) - } + let rterm = term::simplify::dtyp_new(typ, name, args) ; + factory.mk(rterm) } /// Creates a new datatype selector. @@ -413,7 +323,10 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { /// /// - treat constants better pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { - factory.mk( RTerm::DTypSlc { typ, name, term } ) + match term::simplify::dtyp_slc(typ, name, term) { + Either::Left(rterm) => factory.mk(rterm), + Either::Right(term) => term, + } } /// Creates a new datatype tester. @@ -422,7 +335,13 @@ pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { /// /// - treat constants better pub fn dtyp_tst(name: String, term: Term) -> Term { - factory.mk( RTerm::DTypTst { typ: typ::bool(), name, term } ) + let (rterm, positive) = term::simplify::dtyp_tst(name, term) ; + let res = factory.mk( rterm ) ; + if ! positive { + not(res) + } else { + res + } } /// Creates an operator application. diff --git a/src/term/mod.rs b/src/term/mod.rs index 62714dab..ae7c41e6 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -246,6 +246,15 @@ impl RTerm { } } + /// Returns the kids of a datatype constructor. + pub fn dtyp_new_inspect(& self) -> Option<(& Typ, & str, & [ Term ])> { + if let RTerm::DTypNew { typ, name, args } = self { + Some((typ, name, args)) + } else { + None + } + } + /// Iterator over over all the leafs of a term. pub fn leaf_iter(& self) -> LeafIter { LeafIter::of_rterm(self) diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 1fcda7db..771ab583 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1709,3 +1709,185 @@ simpl_fun! { +/// Tries to create a constant datatype constructor. +fn cst_dtyp_new( + typ: Typ, name: String, args: Vec +) -> Either)> { + if args.is_empty() { + return Either::Left( + val::dtyp_new( typ, name, vec![] ) + ) + } + + let mut nu_args = None ; + + for arg in & args { + if let Some(val) = arg.val() { + nu_args.get_or_insert_with( + || Vec::with_capacity( args.len() ) + ).push(val) + } else { + nu_args = None ; + break + } + } + + if let Some(args) = nu_args { + Either::Left( val::dtyp_new(typ, name, args) ) + } else { + Either::Right( (typ, name, args) ) + } +} + + +/// Simplifies a datatype constructor. +pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> RTerm { + let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { + Either::Left(val) => { + return RTerm::Cst(val) + }, + Either::Right(stuff) => stuff, + } ; + + if let Some((dtyp, prms)) = typ.dtyp_inspect() { + if let Some(fargs) = dtyp.news.get(& name) { + if args.len() != fargs.len() { + panic!( + "constructor `{}` for `{}` expects {} arguments, found {}", + conf.emph(& name), conf.emph(& dtyp.name), + fargs.len(), args.len() + ) + } + + for (arg, param) in args.iter_mut().zip( fargs.iter() ) { + let typ = param.1.to_type(prms).unwrap_or_else( + |_| panic!("ill-formed datatype constructor: {}", typ) + ) ; + if let Some(typ) = typ.merge( & arg.typ() ) { + if let Some(nu_arg) = arg.force_dtyp(typ) { + * arg = nu_arg + } + } + } + } else { + panic!( + "datatype `{}` has no constructor named `{}`", + conf.emph(& dtyp.name), conf.bad(& name) + ) + } + } else { + panic!("ill-typed datatype constructor: {}", typ) + } + + let mut vals = if args.is_empty() { + Some(vec![]) + } else { + None + } ; + + for arg in & args { + if let Some(val) = arg.val() { + vals.get_or_insert_with( + || Vec::with_capacity( args.len() ) + ).push(val) + } else { + vals = None ; + break + } + } + + if let Some(vals) = vals { + debug_assert_eq! { vals.len(), args.len() } + RTerm::Cst( val::dtyp_new(typ, name, vals) ) + } else { + debug_assert!( ! args.is_empty() ) ; + RTerm::DTypNew { typ, name, args } + } +} + + + +/// Simplifies a datatype selector. +pub fn dtyp_slc(typ: Typ, field: String, term: Term) -> Either { + if let Some(val) = term.val() { + if let Some(res) = val.dtyp_slc(& field) { + return Either::Left( RTerm::Cst(res) ) + } + } + + if let Some((typ, constructor, args)) = term.dtyp_new_inspect() { + if let Some((dtyp, _)) = typ.dtyp_inspect() { + if let Some(params) = dtyp.news.get(constructor) { + debug_assert_eq! { args.len(), params.len() } + for ((fld, _), arg) in params.iter().zip( args.iter() ) { + if fld == & field { + return Either::Right( arg.clone() ) + } + } + } else { + panic!("inconsistent internal datatype term") + } + } else { + panic!("inconsistent application of datatype selector") + } + } + + Either::Left( + RTerm::DTypSlc { typ, name: field, term } + ) +} + + +/// Simplifies a datatype tester. +/// +/// The boolean flag returned indicates the polarity of the result. That is, if +/// it is `false` then the term should be negated. +pub fn dtyp_tst(constructor: String, term: Term) -> (RTerm, bool) { + if let Some(val) = term.val() { + if let val::RVal::DTypNew { name, .. } = val.get() { + return ( + RTerm::Cst( val::bool(& constructor == name) ), + true + ) + } else { + panic!("illegal datatype tester (ill-typed)") + } + } else if let Some((_, name, _)) = term.dtyp_new_inspect() { + return ( + RTerm::Cst( val::bool(constructor == name) ), + true + ) + } + + // The following tries to find a constructor that's more complex than the + // current one. The reason is that so far, it seems to work better that way. + if let Some(dtyp) = dtyp::of_constructor(& constructor) { + if let Some(args) = dtyp.news.get(& constructor) { + + if args.is_empty() { + if let Some(constructor) = dtyp.rec_constructor() { + return ( + RTerm::DTypTst { + typ: typ::bool(), name: constructor.into(), term + }, + false + ) + } + } + + } else { + panic!("inconsistent maps for datatypes") + } + } else { + panic!( + "trying to construct a tester for unknown constructor {}", constructor + ) + } + + ( + RTerm::DTypTst { + typ: typ::bool(), name: constructor, term + }, + true + ) +} diff --git a/src/val/mod.rs b/src/val/mod.rs index 36a3c50e..ccc70335 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -1252,40 +1252,39 @@ impl RVal { -/** Operations over arrays. - -# Examples - -``` -use hoice::term::typ ; -use hoice::val::* ; - -let first_array = array( typ::int(), int(0) ) ; -# println!("{}", first_array) ; - -assert_eq! { first_array.select( int(7) ), int(0) } -// Following works because `first_array` is constant. -assert_eq! { first_array.select( none(typ::int()) ), int(0) } - -let array = first_array.store(int(7), int(0)) ; -# println!("{}", array) ; -assert_eq! { array, first_array } - -let array = first_array.store(int(7), int(1)) ; -# println!("{}", array) ; - -# println!("array[{}] = {}", 7, 1) ; -assert_eq! { array.select( int(7) ), int(1) } -# println!("array[{}] = {}", 5, 0) ; -assert_eq! { array.select( int(5) ), int(0) } -# println!("array[{}] = {}", 0, 0) ; -assert_eq! { array.select( int(0) ), int(0) } -# println!("array[_] = {}", 1) ; -// Select on `none` does not work anymore, array is not constant. -assert_eq! { array.select( none(typ::int()) ), none(typ::int()) } -``` -*/ -impl RVal { +/// Operations over arrays. +/// +/// # Examples +/// +/// ``` +/// use hoice::term::typ ; +/// use hoice::val::* ; +/// +/// let first_array = array( typ::int(), int(0) ) ; +/// # println!("{}", first_array) ; +/// +/// assert_eq! { first_array.select( int(7) ), int(0) } +/// // Following works because `first_array` is constant. +/// assert_eq! { first_array.select( none(typ::int()) ), int(0) } +/// +/// let array = first_array.store(int(7), int(0)) ; +/// # println!("{}", array) ; +/// assert_eq! { array, first_array } +/// +/// let array = first_array.store(int(7), int(1)) ; +/// # println!("{}", array) ; +/// +/// # println!("array[{}] = {}", 7, 1) ; +/// assert_eq! { array.select( int(7) ), int(1) } +/// # println!("array[{}] = {}", 5, 0) ; +/// assert_eq! { array.select( int(5) ), int(0) } +/// # println!("array[{}] = {}", 0, 0) ; +/// assert_eq! { array.select( int(0) ), int(0) } +/// # println!("array[_] = {}", 1) ; +/// // Select on `none` does not work anymore, array is not constant. +/// assert_eq! { array.select( none(typ::int()) ), none(typ::int()) } +/// ``` +impl RVal { /// Store over arrays, creates a `RVal`. /// /// Does not actually create a `Val`. @@ -1470,6 +1469,36 @@ impl RVal { +/// Operation over datatype values. +impl RVal { + /// Datatype selector. + pub fn dtyp_slc(& self, field: S) -> Option + where S: AsRef { + let field = field.as_ref() ; + if let Some((val_typ, constructor, args)) = self.dtyp_inspect() { + if let Some((dtyp, _)) = val_typ.dtyp_inspect() { + if let Some(params) = dtyp.news.get(constructor) { + debug_assert_eq! { params.len(), args.len() } + for ((name, _), arg) in params.iter().zip( args.iter() ) { + if name == field { + return Some( arg.clone() ) + } + } + } + } else { + panic!("inconsistent internal datatype term") + } + } else { + panic!("inconsistent internal datatype selector term") + } + + None + } +} + + + + impl_fmt!{ RVal(self, fmt) { From c0b7b71925c27db447305888a13579d8ee9fa42b Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 28 Aug 2018 18:11:44 +0900 Subject: [PATCH 41/94] improved qual mining for ADTs a bit --- src/common/mod.rs | 5 +++++ src/learning/ice/quals.rs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/common/mod.rs b/src/common/mod.rs index a1061260..1bb88a94 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -238,6 +238,11 @@ pub type TermSet = HConSet ; /// A map from terms to stuff. pub type TermMap = HConMap ; +/// A set of types. +pub type TypSet = HConSet ; +/// A map from terms to stuff. +pub type TypMap = HConMap ; + /// A signature. pub type Sig = VarMap ; diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index c7a47208..b0dee7ec 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -582,6 +582,8 @@ impl NuQuals { rng: Rng::from_seed( [ 42 ; 16 ] ), } ; + let mut prev: TypMap = TypMap::new() ; + if mine { 'all_preds: for pred_info in instance.preds() { @@ -589,8 +591,24 @@ impl NuQuals { if instance.is_known(pred_info.idx) { continue 'all_preds } let mut sig = pred_info.sig.index_iter() ; + prev.clear() ; for (var, typ) in sig { + if let Some(vars) = prev.get(typ) { + for v in vars { + quals.insert( + term::eq( + term::var(* v, typ.clone()), + term::var(var, typ.clone()), + ), + pred_info.idx + ) ? ; + } + } + + scoped! { + prev.entry( typ.clone() ).or_insert_with(VarSet::new).insert(var) ; + } match ** typ { From d3f608d3beeace90a14fe6da7f21601d3b7321d6 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 30 Aug 2018 21:14:43 +0900 Subject: [PATCH 42/94] major ADT improvements, predicate-to-function reconstruction --- src/check/mod.rs | 2 +- src/common/config.rs | 18 +- src/common/smt.rs | 59 +- src/dtyp/mod.rs | 12 + src/errors.rs | 12 +- src/fun/mod.rs | 235 ++++- src/hoice.rs | 14 +- src/instance/info.rs | 9 +- src/instance/instance/clause.rs | 10 +- src/instance/instance/mod.rs | 43 +- src/instance/instance/pre_instance.rs | 96 ++- src/instance/preproc/fun_preds.rs | 950 +++++++++++++++++++++ src/instance/preproc/mod.rs | 16 +- src/instance/preproc/strict_neg_clauses.rs | 3 +- src/learning/ice/quals.rs | 28 + src/learning/ice/synth/adt.rs | 110 ++- src/learning/ice/synth/helpers.rs | 30 +- src/parse/mod.rs | 71 +- src/teacher/assistant.rs | 18 +- src/teacher/mod.rs | 29 +- src/term/mod.rs | 12 +- src/term/simplify.rs | 45 + src/term/typ.rs | 11 + 23 files changed, 1695 insertions(+), 138 deletions(-) create mode 100644 src/instance/preproc/fun_preds.rs diff --git a/src/check/mod.rs b/src/check/mod.rs index 7ff2649c..e644373a 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -291,9 +291,9 @@ impl Data { println!(" }}") ; println!("\")") ; println!() ; - log_info!("clause {} is fine", count) ; Ok(false) } else if let Some(false) = res { + log_info!("clause {} is fine", count) ; Ok(true) } else { bail!( diff --git a/src/common/config.rs b/src/common/config.rs index cd22553f..b1b16aae 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -339,6 +339,9 @@ pub struct PreprocConf { /// Strengthening by strict clauses. pub strict_neg: bool, + + /// Activates predicates to function reduction + pub fun_preds: bool, } impl SubConf for PreprocConf { fn need_out_dir(& self) -> bool { @@ -617,6 +620,18 @@ impl PreprocConf { true ).number_of_values(1).display_order( order() ) + ).arg( + + Arg::with_name("fun_preds").long("--fun_preds").help( + "(de)activates predicate-to-function reduction" + ).validator( + bool_validator + ).value_name( + bool_format + ).default_value("on").hidden(true).takes_value( + true + ).number_of_values(1).display_order( order() ) + ) } @@ -639,12 +654,13 @@ impl PreprocConf { let split_strengthen = bool_of_matches(matches, "split_strengthen") ; let split_sort = bool_of_matches(matches, "split_sort") ; let strict_neg = bool_of_matches(matches, "strict_neg") ; + let fun_preds = bool_of_matches(matches, "fun_preds") ; PreprocConf { dump, dump_pred_dep, active, reduction, one_rhs, one_rhs_full, one_lhs, one_lhs_full, cfg_red, arg_red, prune_terms, runroll, pos_unroll, neg_unroll, - split_strengthen, split_sort, strict_neg, + split_strengthen, split_sort, strict_neg, fun_preds, } } } diff --git a/src/common/smt.rs b/src/common/smt.rs index ea09f7a4..70d4cf9b 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -25,7 +25,7 @@ pub fn init( ) -> Res<()> where I: AsRef { dtyp::write_all(solver, "") ? ; - fun::write_all(solver) ? ; + fun::write_all(solver, "", true) ? ; instance.as_ref().assert_side_clauses(solver) } @@ -36,7 +36,7 @@ where I: AsRef { /// - asserts all the side-clauses if `preproc` is false pub fn preproc_init

( solver: & mut Solver

) -> Res<()> { dtyp::write_all(solver, "") ? ; - fun::write_all(solver) ? ; + fun::write_all(solver, "", true) ? ; Ok(()) } @@ -139,11 +139,10 @@ where Trms: Iterator + ExactSizeIterator + Clone { } /// Checks if this conjunction is unsatisfiable. - pub fn is_unsat( + fn is_unsat( & self, solver: & mut Solver, actlit: Option<& Actlit> ) -> Res { if self.terms.len() == 0 { return Ok(false) } - solver.push(1) ? ; if ! self.has_fun_apps { for var in self.infos { if var.active { @@ -153,7 +152,6 @@ where Trms: Iterator + ExactSizeIterator + Clone { } solver.assert( self ) ? ; let sat = solver.check_sat_act(actlit) ? ; - solver.pop(1) ? ; Ok(! sat) } } @@ -202,6 +200,49 @@ where Trms: Iterator + ExactSizeIterator + Clone { } +/// SMT-prints a collection of terms as a conjunction with default var writer. +pub struct TermConj { + /// Conjunction. + terms: Trms, +} +impl<'a, Trms> TermConj +where Trms: Iterator + ExactSizeIterator + Clone { + /// Constructor. + pub fn new(terms: IntoIter) -> Self + where IntoIter: IntoIterator { + let terms = terms.into_iter() ; + TermConj { terms } + } +} + +impl<'a, Trms> Expr2Smt for TermConj +where Trms: Iterator + ExactSizeIterator + Clone { + fn expr_to_smt2( + & self, w: & mut Writer, pos: bool + ) -> SmtRes<()> { + if ! pos { + write!(w, "(not ") ? + } + if self.terms.len() == 0 { + write!(w, "true") ? + } else { + write!(w, "(and") ? ; + for term in self.terms.clone() { + write!(w, " ") ? ; + term.write( + w, |w, var| var.default_write(w) + ) ? ; + } + write!(w, ")") ? + } + if ! pos { + write!(w, ")") ? + } + Ok(()) + } +} + + /// SMT-prints an implication `/\ (set \ term) => term`. pub struct SmtImpl<'a> { @@ -849,16 +890,19 @@ impl ClauseTrivialExt for Solver { let conj = SmtConj::new( lhs.iter(), & clause.vars ) ; if clause.rhs().is_none() && clause.lhs_preds().is_empty() { + if conj.has_fun_apps { actlit = Some( self.get_actlit() ? ) } // Either it is trivial, or falsifiable regardless of the predicates. - if conj.is_unsat( self, actlit.as_ref() ) ? { + let res = if conj.is_unsat( self, actlit.as_ref() ) ? { Ok( Some(true) ) } else { Ok(None) - } + } ; + + res } else { @@ -877,6 +921,7 @@ impl ClauseTrivialExt for Solver { if conj.has_fun_apps { actlit = Some( self.get_actlit() ? ) } + conj.is_unsat( self, actlit.as_ref() ).map(Some) } diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 7eb1942c..0df8404f 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -896,6 +896,18 @@ impl RDTyp { } None } + + /// Returns the selectors of a constructor. + pub fn selectors_of(& self, constructor: & str) -> Res<& CArgs> { + if let Some(selectors) = self.news.get(constructor) { + Ok(selectors) + } else { + bail!( + "unknown constructor {} for dtyp {}, no selectors", + conf.bad(constructor), conf.emph(& self.name) + ) + } + } } impl_fmt! { RDTyp(self, fmt) { write!(fmt, "{}", self.name) } diff --git a/src/errors.rs b/src/errors.rs index bdfbd405..1966a37c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -163,6 +163,16 @@ impl Error { } } + /// True if the kind of the error is [`ErrorKind::SmtError::Unknown`]. + pub fn is_smt_unknown(& self) -> bool { + match * self.kind() { + ErrorKind::SmtError( + ::rsmt2::errors::ErrorKind::Unknown + ) => true, + _ => false, + } + } + /// True if the kind of the error is [`ErrorKind::Unknown`][unknown]. /// /// [unknown]: enum.ErrorKind.html#variant.Unknown @@ -170,7 +180,7 @@ impl Error { pub fn is_unknown(& self) -> bool { match * self.kind() { ErrorKind::Unknown => true, - _ => false, + _ => self.is_smt_unknown(), } } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 22d709b2..60bb2288 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -145,64 +145,196 @@ pub fn mk(fun: RFun) -> Res { } +/// Orders all functions by dependencies. +/// +/// Assumes there's no circular dependencies between the `mentions` fields. +/// Meaning the semantics of `deps` and `mentions` are respected. +pub fn ordered() -> Res< Vec< Vec > > { + let mut all: Vec<_> = read_factory().0.values().cloned().collect() ; + + let mut groups = vec![] ; + + while let Some(fun) = all.pop() { + let mut group = vec![ fun ] ; + if ! group[0].deps.is_empty() { + all.retain( + |fun| if group[0].deps.contains(& fun.name) { + group.push( fun.clone() ) ; + false + } else { + true + } + ) ; + } + groups.push(group) + } -/// Defines all the functions. -pub fn write_all(w: & mut W) -> Res<()> { - let f = factory!(read) ; + groups.sort_by( + |g_1, g_2| { + use std::cmp::Ordering::* ; + for f_1 in g_1 { + for f_2 in g_2 { + if f_2.mentions.contains( & f_1.name ) { + return Less + } else if f_1.mentions.contains( & f_2.name ) { + return Greater + } + } + } + Equal + } + ) ; + + // println!() ; + // println!("groups:") ; + // for group in & groups { + // print!(" ") ; + // for fun in group { + // print!(" {},", fun.name) + // } + // println!() + // } + + Ok(groups) +} - if f.is_empty() { return Ok(()) } - let mut set = BTreeSet::new() ; +/// Defines a function, and all functions related to it. +pub fn write(w: & mut W, fun: & RFun, pref: & str) -> Res<()> +where W: Write { + let f = factory!(read) ; let mut all = vec![] ; - for fun in f.values() { - let do_it = set.insert(& fun.name) ; - if ! do_it { continue } + all.reserve( fun.deps.len() + 1 ) ; + all.push(fun) ; + for dep in & fun.deps { + if let Some(dep) = f.get(dep) { + all.push(dep) + } else { + bail!( + "function `{}` depends on unknown function `{}`", + conf.emph(& fun.name), conf.bad(& dep) + ) + } + } - debug_assert! { all.is_empty() } + writeln!(w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec) ? ; - all.reserve( fun.deps.len() + 1 ) ; - all.push(fun) ; - for dep in & fun.deps { - set.insert(dep) ; - if let Some(dep) = f.get(dep) { - all.push(dep) - } else { - bail!( - "function `{}` depends on unknown function `{}`", - conf.emph(& fun.name), conf.bad(& dep) - ) - } + // Write all signatures. + for fun in & all { + write!(w, "{} (", pref) ? ; + write!(w, "{} (", fun.name) ? ; + for info in & fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ) ? } + writeln!(w, " ) {})", fun.typ) ? + } + + writeln!(w, "{}) (", pref) ? ; + + // Write all definitions. + for fun in all.drain( 0 .. ) { + write!(w, "{} ", pref) ? ; + fun.def.write( + w, |w, var| var.default_write(w) + ) ? ; + writeln!(w) ? + } + + writeln!(w, "{}) )", pref) ? ; + + Ok(()) +} - writeln!(w, "(define-funs-rec (") ? ; - // Write all signatures. - for fun in & all { - write!(w, " (") ? ; - write!(w, "{} (", fun.name) ? ; +/// Defines all the functions. +pub fn write_all( + w: & mut W, pref: & str, invariants: bool +) -> Res<()> { + for mut group in ordered() ? { + + if group.len() == 1 { + let fun = & group[0] ; + + let def_key = if fun.mentions.contains(& fun.name) { + consts::keywords::cmd::def_fun_rec + } else { + consts::keywords::cmd::def_fun + } ; + + writeln!( + w, "{}({} {}", pref, def_key, fun.name + ) ? ; + write!(w, "{} (", pref) ? ; for info in & fun.sig { write!(w, " ({} {})", info.idx.default_str(), info.typ) ? } - writeln!(w, " ) {})", fun.typ) ? - } + writeln!(w, " ) {}", fun.typ) ? ; - writeln!(w, ") (") ? ; + write!(w, "{} ", pref) ? ; - // Write all definitions. - for fun in all.drain( 0 .. ) { - write!(w, " ") ? ; fun.def.write( w, |w, var| var.default_write(w) ) ? ; - writeln!(w) ? + writeln!(w, "\n{})", pref) ? + + } else if group.len() > 1 { + writeln!( + w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec + ) ? ; + + // Write all signatures. + for fun in & group { + write!(w, "{} (", pref) ? ; + write!(w, "{} (", fun.name) ? ; + for info in & fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ) ? + } + writeln!(w, " ) {})", fun.typ) ? + } + + writeln!(w, "{}) (", pref) ? ; + + // Write all definitions. + for fun in & group { + write!(w, "{} ", pref) ? ; + fun.def.write( + w, |w, var| var.default_write(w) + ) ? ; + writeln!(w) ? + } + + writeln!(w, "{}) )", pref) ? } - writeln!(w, ") )") ? - } + writeln!(w) ? ; + + if invariants { + let mut one_or_more = false ; + for fun in group { + for inv in & fun.invariants { + one_or_more = true ; + writeln!(w, "{}(assert", pref) ? ; + writeln!(w, "{} (forall", pref) ? ; + write!(w, "{} (", pref) ? ; + for info in & fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ) ? + } + writeln!(w, " )") ? ; + write!(w, "{} ", pref) ? ; + inv.write( + w, |w, var| var.default_write(w) + ) ? ; + writeln!(w, "\n{}) )", pref) ? + } + } - writeln!(w) ? ; + if one_or_more { + writeln!(w) ? + } + } + } Ok(()) } @@ -319,6 +451,8 @@ pub struct RFun { pub name: String, /// Other functions this function depends on. pub deps: BTreeSet, + /// Functions mentioned in the body of the function. + pub mentions: BTreeSet, /// Signature. /// /// The string stored is the original name of the argument. @@ -327,6 +461,10 @@ pub struct RFun { pub typ: Typ, /// Definition. pub def: Term, + /// The index of the predicate this function was created for. + pub synthetic: Option, + /// Invariants of the function. + pub invariants: TermSet, } impl PartialEq for RFun { @@ -362,7 +500,11 @@ impl RFun { name: S, sig: VarInfos, typ: Typ ) -> Self { let name = name.into() ; - RFun { name, deps: BTreeSet::new(), sig, typ, def: term::tru() } + RFun { + name, deps: BTreeSet::new(), mentions: BTreeSet::new(), + sig, typ, def: term::tru(), synthetic: None, + invariants: TermSet::new(), + } } /// Insert a dependency. @@ -377,12 +519,25 @@ impl RFun { } } + /// Flips the flag indicating that the function was created internally for a + /// predicate. + pub fn set_synthetic(& mut self, pred: PrdIdx) { + self.synthetic = Some(pred) + } + /// Sets the definition of a function. /// /// # Panics /// /// - if `self.def` is not `term::tru()` pub fn set_def(& mut self, def: Term) { + def.iter( + |trm| if let Some((name, _)) = trm.fun_inspect() { + if ! self.deps.contains(name) { + self.mentions.insert( name.to_string() ) ; + } + } + ) ; match * self.def { RTerm::Cst(ref cst) if cst.is_true() => (), _ => panic!("trying to set the definition of a function twice"), @@ -402,6 +557,12 @@ impl RFun { } Ok(()) } + + /// Writes itself and all its dependencies. + pub fn write(& self, w: & mut W, pref: & str) -> Res<()> + where W: Write { + write(w, self, pref) + } } diff --git a/src/hoice.rs b/src/hoice.rs index c3fcfc41..257c807f 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -250,13 +250,21 @@ pub fn read_and_work( print_stats("top", profiler) ; ::std::process::exit(0) }, + Err(ref e) if e.is_smt_unknown() => { + println!( + "; underlying SMT solver reported unknown, giving up..." + ) ; + println!("unknown") ; + None + }, Err(ref e) if e.is_unknown() => { println!("unknown") ; None - // print_stats("top", profiler) ; - // ::std::process::exit(0) }, - Err(e) => bail!(e), + Err(e) => { + println!("bail, {}", e.description()) ; + bail!(e) + }, } } ; diff --git a/src/instance/info.rs b/src/instance/info.rs index 3d1bb079..16e18794 100644 --- a/src/instance/info.rs +++ b/src/instance/info.rs @@ -1,6 +1,6 @@ //! Types to store information about variables and predicates. -use rsmt2::print::Sym2Smt ; +use rsmt2::print::{ Sym2Smt, Sort2Smt } ; use common::* ; @@ -36,6 +36,13 @@ impl Sym2Smt<()> for VarInfo { Ok(()) } } +impl Sort2Smt for VarInfo { + fn sort_to_smt2( + & self, w: & mut Writer + ) -> SmtRes<()> where Writer: Write { + self.typ.get().sort_to_smt2(w) + } +} impl_fmt!{ VarInfo(self, fmt) { fmt.write_str(& self.name) diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 4354e60f..d100e4f0 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -848,11 +848,11 @@ impl Clause { if info { writeln!( w, "\ - ; {} inactive variable(s)\n \ - ; unroll: {}\n \ - ; terms_changed: {}\n \ - ; preds_changed: {}\n \ - ; created by `{}`\ + ; {} inactive variable(s)\n\ + ; unroll: {}\n\ + ; terms_changed: {}\n\ + ; preds_changed: {}\n\ + ; created by `{}`\ ", self.vars.iter().fold( 0, |acc, var| acc + if var.active { 1 } else { 0 } diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 2876edd8..e6f7d6bb 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -47,6 +47,13 @@ pub struct Instance { /// Strengthener for predicates. pred_str: PrdMap< Option >, + /// Companion function for predicates. + /// + /// A companion function is a function that was created internally + /// specifically for a predicate. Meaning it needs to be given to the user + /// when printing the model. + companion_funs: PrdHMap< Vec >, + /// Side-clauses. /// /// A side clause @@ -132,6 +139,7 @@ impl Instance { pred_terms: PrdMap::with_capacity(pred_capa), sorted_pred_terms: Vec::with_capacity(pred_capa), pred_str: PrdMap::with_capacity(pred_capa), + companion_funs: PrdHMap::new(), side_clauses: Vec::with_capacity(7), clauses: ClsMap::with_capacity(clause_capa), @@ -171,6 +179,7 @@ impl Instance { pred_terms: self.pred_terms.clone(), sorted_pred_terms: Vec::with_capacity( self.preds.len() ), pred_str: vec! [ None ; self.old_preds.len() ].into(), + companion_funs: self.companion_funs.clone(), side_clauses: self.side_clauses.clone(), clauses: self.clauses.clone(), @@ -738,6 +747,18 @@ impl Instance { self.pred_str[pred].as_ref() } + /// Adds a companion function for a predicate. + pub fn add_companion_fun(& mut self, pred: PrdIdx, fun: Fun) { + self.companion_funs.entry(pred).or_insert_with( + Vec::new + ).push(fun) + } + + /// Retrieves the companion functions for a predicate. + pub fn get_companion_funs(& self, pred: PrdIdx) -> Option< & Vec > { + self.companion_funs.get(& pred) + } + /// Removes and returns the indices of the clauses `pred` appears in the lhs /// of from `self.pred_to_clauses`. @@ -862,6 +883,8 @@ impl Instance { } /// Mutable accessor for side clauses. + /// + /// Does not expose function invariants. pub fn side_clauses_retain( & mut self, mut keep: Keep ) -> Res @@ -908,6 +931,22 @@ impl Instance { Ok(()) } + /// Registers a new side clause: forces the term to be true. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_new_side_clause( + & mut self, + vars: VarInfos, term: Term, info: & 'static str + ) -> Res<()> { + let clause = clause::new( + vars, vec![ TTerm::T( term::not(term) ) ], None, + info, self.side_clauses.len().into() + ) ; + + self.add_side_clause(clause) + } + /// Pushes a clause. /// /// Returns its index, if it was added. @@ -1203,7 +1242,7 @@ impl Instance { writeln!(w) ? ; writeln!(w, "; Functions") ? ; - fun::write_all(w) ? ; + fun::write_all(w, "", true) ? ; writeln!(w) ? ; @@ -1351,6 +1390,8 @@ impl Instance { & self, w: & mut W, pref: & str, model: ConjModelRef ) -> Res<()> { + fun::write_all(w, pref, false) ? ; + for defs in model { if defs.is_empty() { diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index 87914097..f4640147 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -68,6 +68,10 @@ impl<'a> PreInstance<'a> { |_| { use_actlits = true ; Ok(()) } ) ? ; + if dtyp::get_all().iter().next().is_some() { + use_actlits = true + } + Ok( PreInstance { instance, solver, simplifier, @@ -79,6 +83,11 @@ impl<'a> PreInstance<'a> { ) } + /// Resets the solver. + pub fn reset_solver(& mut self) -> Res<()> { + smt::preproc_reset(& mut self.solver) + } + /// Accessor for the solver. pub fn solver(& mut self) -> & mut Solver<()> { & mut self.solver @@ -277,6 +286,27 @@ impl<'a> PreInstance<'a> { + /// Non-strict negative clauses. + pub fn non_strict_neg_clauses(& mut self) -> ( + & mut ExtractionCxt, & Instance, impl Iterator + ) { + let extraction = & mut self.extraction ; + let instance = & self.instance ; + let clauses = & instance.clauses ; + ( + extraction, instance, clauses.index_iter().filter( + |(_, clause)| clause.rhs().is_none() + && ( + clause.lhs_preds().len() > 1 || clause.lhs_preds().iter().any( + |(_, apps)| apps.len() > 1 + ) + ) + ) + ) + } + + + /// Simplifies all the clauses. pub fn simplify_all(& mut self) -> Res { let mut info = RedInfo::new() ; // self.force_trivial() ? ; @@ -292,18 +322,33 @@ impl<'a> PreInstance<'a> { info += self.force_trivial() ? ; + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } + // Check side-clauses. - let instance = & mut self.instance ; - let solver = & mut self.solver ; - - info += instance.side_clauses_retain( - |clause| { - match solver.is_clause_trivial(clause) ? { - None => bail!( ErrorKind::Unsat ), - Some(is_trivial) => Ok(is_trivial), + scoped! { + let instance = & mut self.instance ; + let solver = & mut self.solver ; + + log! { @4 "checking side clauses" } + + info += instance.side_clauses_retain( + |clause| { + solver.push(1) ? ; + let res = match solver.is_clause_trivial(clause) ? { + None => bail!( ErrorKind::Unsat ), + Some(is_trivial) => Ok(is_trivial), + } ; + solver.pop(1) ? ; + res } - } - ) ? ; + ) ? ; + } + + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } Ok(info) } @@ -649,9 +694,21 @@ impl<'a> PreInstance<'a> { /// - the rhs is a predicate application contained in the lhs. #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] fn is_clause_trivial(& mut self, clause_idx: ClsIdx) -> Res { - if let Some(res) = self.solver.is_clause_trivial( + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } else { + self.solver.push(1) ? ; + } + let res = self.solver.is_clause_trivial( & mut self.instance[clause_idx] - ) ? { + ) ; + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } else { + self.solver.pop(1) ? ; + } + + if let Some(res) = res ? { Ok(res) } else { log_debug!{ @@ -677,7 +734,18 @@ impl<'a> PreInstance<'a> { pub fn is_this_clause_trivial( & mut self, clause: & mut Clause ) -> Res< Option > { - self.solver.is_clause_trivial(clause) + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } else { + self.solver.push(1) ? ; + } + let res = self.solver.is_clause_trivial(clause) ; + if self.use_actlits { + smt::reset(& mut self.solver, & self.instance) ? ; + } else { + self.solver.pop(1) ? ; + } + res } @@ -1950,7 +2018,7 @@ impl<'a> PreInstance<'a> { } } - smt::preproc_reset(& mut self.solver) ? ; + self.reset_solver() ? ; Ok(true) } diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs new file mode 100644 index 00000000..c82fe9fe --- /dev/null +++ b/src/instance/preproc/fun_preds.rs @@ -0,0 +1,950 @@ +//! Predicate-to-function reduction. + +use common::* ; +use instance::{ + instance::PreInstance, + info::VarInfo, + preproc::RedStrat, +} ; +use fun::RFun ; + +pub struct FunPreds ; + + +impl FunPreds { + /// Finalizes a definition for a predicate. + /// + /// Returns the final definition and some invariants. + pub fn finalize_definition( + instance: & mut PreInstance, + name: & str, sig: & VarInfos, typ: & Typ, + mut definitions: Vec<(TermSet, Term)> + ) -> Res< Option<(Term, Option)> > { + instance.reset_solver() ? ; + + let invs = get_invariants(instance, name, sig, typ, & mut definitions) ? ; + + if_log! { @3 + if let Some(invs) = invs.as_ref() { + log! { @3 "found {} invariant(s)", invs.len() } + for inv in invs { + log! { @3 " {}", inv } + } + } else { + log! { @3 "none found" } + } + } + + log! { @3 "checking conditions are mutually exclusive" } + let solver = instance.solver() ; + + for info in sig { + solver.declare_const(& info.idx, info.typ.get()) ? + } + + solver.declare_fun(name, sig, typ.get()) ? ; + + let mut actlits = Vec::with_capacity( definitions.len() ) ; + + for (cube, _) in & definitions { + use smt::TermConj ; + let conj = TermConj::new( cube.iter() ) ; + let actlit = solver.get_actlit() ? ; + solver.assert_act_with( & actlit, & conj, true ) ? ; + actlits.push(actlit) + } + + scoped! { + let mut actlits_iter = actlits.iter() ; + + while let Some(actlit) = actlits_iter.next() { + for other in actlits_iter.clone() { + let not_exclusive = solver.check_sat_act( + vec![ actlit, other ] + ) ? ; + if not_exclusive { + return Ok(None) + } + } + } + } + + for actlit in actlits { + solver.de_actlit(actlit) ? + } + + log! { @3 "all branches are exclusive, checking they're exhaustive" } + + for (cube, _) in & definitions { + use smt::TermConj ; + let conj = TermConj::new( cube.iter() ) ; + solver.assert_with( & conj, false ) ? ; + } + + let not_exhaustive = solver.check_sat() ? ; + + if not_exhaustive { + // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; + log! { @3 "branches are not exhaustive, aborting" } + return Ok(None) + } else { + log! { @3 "branches are exhaustive, building definition" } + } + + let mut def = None ; + + for (cube, value) in definitions.into_iter().rev() { + let nu_def = if let Some(def) = def { + term::ite( + term::and( cube.into_iter().collect() ), + value, + def + ) + } else { + value + } ; + def = Some(nu_def) + } + + if let Some(def) = def { + Ok( Some( (def, invs) ) ) + } else { + bail!("empty definitions during finalization in fun_preds") + } + } + + + /// Reduces a predicate to a function. + pub fn reduce_pred( + instance: & mut PreInstance, pred: PrdIdx + ) -> Res< Option > { + let mut info = RedInfo::new() ; + // Clauses to remove. + let mut to_rm = vec![] ; + + log!(@2 "working on {}", conf.emph(& instance[pred].name)) ; + + debug_assert! { to_rm.is_empty() } + + let mut var_infos = VarInfos::new() ; + for typ in & instance[pred].sig[ 0 .. instance[pred].sig.len() - 1 ] { + let idx = var_infos.next_index() ; + let var_info = VarInfo::new( + idx.default_str(), typ.clone(), idx + ) ; + var_infos.push(var_info) + } + let last: VarIdx = ( instance[pred].sig.len() - 1 ).into() ; + + let pred_fun_name = format!("{}_hoice_reserved_fun", instance[pred]) ; + let pred_fun_typ = instance[pred].sig[last].clone() ; + + let mut rfun = RFun::new( + pred_fun_name.clone(), var_infos, pred_fun_typ.clone() + ) ; + rfun.set_synthetic(pred) ; + + macro_rules! abort { + ($($stuff:tt)*) => ({ + let _ = fun::retrieve_dec(& pred_fun_name) ; + return Ok(None) + }) ; + } + + fun::register_dec(rfun) ? ; + + let mut definitions = vec![] ; + + for clause in instance.clauses_of(pred).1 { + to_rm.push(* clause) ; + + if_log! { @3 + log!(@3 "working on clause #{}", clause) ; + log!(@4 "lhs terms:") ; + for term in instance[* clause].lhs_terms() { + log!(@4 " {}", term) + } + log!(@4 "lhs preds:") ; + for (pred, argss) in instance[* clause].lhs_preds() { + for args in argss { + log!(@4 " ({} {})", instance[* pred], args) + } + } + } + let clause = * clause ; + let (_, rhs_args) = instance[clause].rhs().unwrap() ; + log!(@4 "rhs args:\n ({} {})", instance[pred], rhs_args) ; + + let ( + mut cube, mut subst + ) = if let Some((cube, subst)) = args_invert(rhs_args) ? { + (cube, subst) + } else { + log!(@3 "failed to invert rhs arguments") ; + abort!() + } ; + + if_log! { @4 + log!(@4 "substitution:") ; + for (var, term) in subst.iter() { + log!(@4 " v_{} -> {}", var, term) + } + log!(@4 "cube:") ; + for term in cube.iter() { + log!(@4 " {}", term) + } + } + + let mut lhs_argss: Vec<_> = instance[ + clause + ].lhs_preds().get(& pred).map( + |argss| argss.iter().collect() + ).unwrap_or_else( Vec::new ) ; + + let mut nu_args = Vec::with_capacity( + instance[pred].sig.len() - 1 + ) ; + + while ! lhs_argss.is_empty() { + let prev_len = lhs_argss.len() ; + let mut failed: Res<_> = Ok(false) ; + + lhs_argss.retain( + |args| { + for arg in & args[ 0 .. args.len() - 1 ] { + if let Some((arg, _)) = arg.subst_total( & subst ) { + nu_args.push(arg) + } else { + nu_args.clear() ; + return true + } + } + + let last: VarIdx = (args.len() - 1).into() ; + let nu_args = ::std::mem::replace( + & mut nu_args, Vec::with_capacity( + instance[pred].sig.len() - 1 + ) + ) ; + + let fun_app = term::fun( + pred_fun_typ.clone(), pred_fun_name.clone(), nu_args + ) ; + + let okay = map_invert( + & args[ last ], fun_app, & mut subst, & mut cube + ) ; + + match okay { + // Success, don't retain. + Ok(true) => false, + // Failure, retain. + Ok(false) => { + log!(@3 + "could not invert last argument ({}) of ({} {})", + last, instance[pred], args + ) ; + if let Ok(failed) = failed.as_mut() { + * failed = true + } + true + }, + // Error, do whatever. + err => { + failed = err.chain_err( + || format!("while inverting {}", & args[last]) + ) ; + true + }, + } + } + ) ; + + if failed ? { + log!(@3 "failed") ; + abort!() + } else if lhs_argss.len() == prev_len { + // not making progress. + log!(@3 "not making progress on lhs preds") ; + abort!() + } + } + + if_log! { @4 + log!(@4 "subst after lhs preds:") ; + for (var, term) in & subst { + log!(@4 " v_{} -> {}", var, term) + } + } + + for term in instance[clause].lhs_terms() { + if let Some((term, _)) = term.subst_total( & subst ) { + cube.insert(term) ; + } else { + log!(@3 "total substitution on term {} failed", term) ; + abort!() + } + } + + if_log! { @4 + log!(@4 "cube:") ; + for term in & cube { + log!(@4 " {}", term) + } + } + + let res = if let Some((res, _)) = rhs_args[last].subst_total(& subst) { + res + } else { + log!(@3 "failed to retrieve value, aborting") ; + abort!() + } ; + + log!(@4 "value: {}", res) ; + + definitions.push( (cube, res) ) + } + + definitions.sort_by( + |(c_1, _), (c_2, _)| c_1.len().cmp( & c_2.len() ) + ) ; + + log!(@3 "done working on {}", instance[pred]) ; + if_log! { @4 + for (cube, res) in & definitions { + log!(@4 "when {{") ; + for term in cube { + log!(@4 " {}", term) + } + log!(@4 "}} -> {}", res) + } + } + + // let mut def = pred_fun_typ.default_term() ; + + // for (cube, res) in definitions.into_iter().rev() { + // let cond = term::and( + // cube.into_iter().collect() + // ) ; + // def = term::ite( cond, res, def ) + // } + + let mut dec = fun::retrieve_dec(& pred_fun_name) ? ; + + let (def, invs) = if let Some(def) = FunPreds::finalize_definition( + instance, & pred_fun_name, & dec.sig, & pred_fun_typ, definitions + ) ? { + def + } else { + log!(@3 "failed to finalize definition, aborting") ; + abort!() + } ; + + if let Some(invs) = invs { + for inv in invs { + dec.invariants.insert(inv) ; + } + } + + instance.reset_solver() ? ; + + log!(@3 "definition:\n {}", def) ; + + log!(@4 "registering...") ; + dec.set_def( def ) ; + + let fun = fun::mk(dec).chain_err( + || format!( + "while creating internal function for predicate {}", + conf.bad(& instance[pred].name) + ) + ) ? ; + + instance.add_companion_fun( pred, fun.clone() ) ; + + // Restarting solver so that the function is declared. + instance.reset_solver() ? ; + + info.clauses_rmed += to_rm.len() ; + instance.forget_clauses(& mut to_rm) ? ; + + let mut args = Vec::with_capacity( instance[pred].sig.len() - 1 ) ; + for (var, typ) in instance[pred].sig.index_iter().take( + instance[pred].sig.len() - 1 + ) { + args.push( term::var(var, typ.clone()) ) + } + let fun_app = term::fun( + fun.typ.clone(), fun.name.clone(), args + ) ; + + let def = term::eq( + term::var( last, fun.typ.clone() ), + fun_app + ) ; + + info.preds += 1 ; + let mut tterm_set = TTermSet::new() ; + tterm_set.insert_term(def) ; + info += instance.force_dnf_left( + pred, vec![ ( Quantfed::new(), tterm_set ) ] + ) ? ; + + Ok( Some(info) ) + } +} + +impl RedStrat for FunPreds { + fn name(& self) -> & 'static str { "fun_preds" } + + fn new(_: & Instance) -> Self { FunPreds } + + fn apply( + & mut self, instance: & mut PreInstance + ) -> Res { + let mut info = RedInfo::new() ; + + let mut new_stuff = true ; + + while new_stuff { + new_stuff = false ; + + if instance.active_pred_count() <= 1 { + return Ok(info) + } + + let mut to_inline: Vec<_> = instance.preds().iter().filter_map( |info| { + let inline = { + let pred = info.idx ; + + // Predicate is still unknown. + ! instance.is_known(pred) + + // When `pred` appears in the rhs of a clause, the lhs only mentions + // `pred`. + && instance.clauses_of(pred).1.iter().all( + |clause| instance[* clause].lhs_preds().iter().all( + |(p, _)| * p == pred + ) + ) + + // `pred` appears more than once in a clause where it's not alone. + // && instance.clauses_of(pred).0.iter().any( + // |clause| instance[* clause].lhs_preds().get( + // & pred + // ).unwrap().len() > 1 + // && ( + // instance[* clause].lhs_preds().len() > 1 + // || instance[* clause].rhs().map( + // |(p, _)| pred != * p + // ).unwrap_or(false) + // ) + // ) + + // `pred` has only one dtyp argument (ignoring the last argument) + && info.sig.len() > 1 + && info.sig[ 0 .. info.sig.len() - 1 ].iter().fold( + 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } + ) <= 1 + + } ; + + if inline { + Some(info.idx) + } else { + None + } + } ).collect() ; + + to_inline.sort_by( + |p_1, p_2| { + let adt_count_1 = instance[* p_1].sig.iter().fold( + 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } + ) ; + let adt_count_2 = instance[* p_2].sig.iter().fold( + 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } + ) ; + + adt_count_1.cmp(& adt_count_2).reverse() + } + ) ; + + if to_inline.is_empty() { + return Ok(info) + } + + while let Some(pred) = to_inline.pop() { + let res = FunPreds::reduce_pred(instance, pred) ? ; + // pause("to resume fun_preds", & Profiler::new()) ; + if let Some(red_info) = res { + new_stuff = true ; + info += red_info ; + break + } + } + + } + + Ok(info) + } +} + + + + + +/// Builds a cube and a substitution corresponding to inverting some arguments. +pub fn args_invert( + args: & VarTerms +) -> Res< Option<(TermSet, VarHMap)> > { + let (mut cube, mut subst) = ( + TermSet::new(), VarHMap::new() + ) ; + + debug_assert! { args.len() >= 1 } + + let mut postponed = Vec::new() ; + let mut current: Vec<_> = args.index_iter().take( + args.len() - 1 + ).map( + |(var, term)| ( term::var(var, term.typ()), term ) + ).collect() ; + + let mut did_something ; + + while ! current.is_empty() { + debug_assert! { postponed.is_empty() } + + did_something = false ; + + for (var, term) in current.drain( 0 .. ) { + log! { @4 "attempting to invert {} | {}", var, term } + if_log! { @5 + log! { @5 "subst:" } + for (var, term) in & subst { + log! { @5 " {}: {}", var.default_str(), term } + } + log! { @5 "cube:" } + for term in & cube { + log! { @5 " {}", term } + } + } + + let worked = map_invert( + term, var.clone(), & mut subst, & mut cube + ) ? ; + + if worked { + log! { @4 "success :)" } + did_something = true ; + } else { + log! { @4 "failed :(" } + postponed.push((var, term)) + } + } + + // Making progress? + if ! did_something { + return Ok(None) + } else { + ::std::mem::swap( & mut postponed, & mut current ) + } + } + + Ok( Some((cube, subst)) ) +} + + + + + + +/// Inverts a term to complete a substitution and a cube. +/// +/// Returns false if the invertion failed. +pub fn map_invert( + to_invert: & Term, + term: Term, subst: & mut VarHMap, cube: & mut TermSet +) -> Res { + debug_assert_eq! { to_invert.typ(), term.typ() } + + let mut nu_cube = vec![] ; + let mut nu_subst = VarHMap::::new() ; + + let mut stack = vec![ ( term, to_invert.get() ) ] ; + + while let Some( (term, to_invert) ) = stack.pop() { + + match to_invert { + + RTerm::DTypNew { typ, name, args } => { + let selectors = typ.selectors_of(name) ? ; + debug_assert_eq! { args.len(), selectors.len() } + + // `term` must be a specific variant: `name` + nu_cube.push( + term::dtyp_tst( name.clone(), term.clone() ) + ) ; + + for (arg, (slc, _)) in args.iter().zip( selectors.iter() ) { + stack.push(( + term::dtyp_slc( arg.typ(), slc.clone(), term.clone() ), + arg + )) + } + + }, + + RTerm::Cst(val) => { + nu_cube.push( term::eq( term, term::val( val.clone() ) ) ) ; + continue + }, + + RTerm::Var(_, var) => { + if let Some(t) = subst.get(var) { + nu_cube.push( + term::eq( term, t.clone() ) + ) ; + continue + } else if let Some(t) = nu_subst.get(var) { + nu_cube.push( + term::eq( term, t.clone() ) + ) ; + continue + } + + nu_subst.insert(* var, term) ; + }, + + // Constant array. + RTerm::CArray { term: inner, typ } => { + stack.push(( + // Array is constant, select any value. + term::select( term, typ.default_term() ), + inner.get() + )) + }, + + RTerm::App { typ, op, args } => { + + + + match op { + + Op::CMul => if args[0].val().is_some() { + let nu_term = if typ.is_int() { + // Current term modulo `val` is zero. + nu_cube.push( + term::eq( + term::modulo( term.clone(), args[0].clone() ), + term::int(0) + ) + ) ; + term::idiv( vec![term, args[0].clone()] ) + } else if typ.is_real() { + term::div( vec![term, args[0].clone()] ) + } else { + bail!("unexpected multiplication over type {}", typ) + } ; + stack.push( + ( nu_term, args[1].get() ) + ) ; + continue + } else { + bail!("illegal CMul term {}", to_invert) + }, + + Op::Add => { + let mut subtraction = vec![ term ] ; + let mut not_substed = None ; + + for arg in args { + if let Some((term, _)) = arg.subst_total( + & (& * subst, & nu_subst) + ) { + subtraction.push(term) + } else if not_substed.is_some() { + return Ok(false) + } else { + not_substed = Some( arg.get() ) + } + } + + let nu_term = term::sub(subtraction) ; + + if let Some(nu_to_invert) = not_substed { + stack.push( (nu_term, nu_to_invert) ) + } else { + nu_cube.push( + term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + } + ) + ) ; + } + continue + }, + + Op::Sub => { + let mut sub = None ; + let mut add = vec![ term ] ; + let mut not_substed = None ; + + let mut first = true ; + for arg in args { + if let Some((term, _)) = arg.subst_total( + & (& * subst, & nu_subst) + ) { + if first { + debug_assert! { sub.is_none() } + sub = Some(term) + } else { + add.push(term) + } + } else if not_substed.is_some() { + return Ok(false) + } else { + // Careful of the polarity here vvvvv + not_substed = Some( (arg.get(), first) ) + } + + first = false + } + + let nu_term = term::add(add) ; + let nu_term = if let Some(sub) = sub { + term::sub( vec![ nu_term, sub ] ) + } else { + nu_term + } ; + + if let Some((nu_to_invert, positive)) = not_substed { + stack.push( + ( + if positive { nu_term } else { term::u_minus(nu_term) }, + nu_to_invert + ) + ) + } else { + nu_cube.push( + term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + } + ) + ) ; + } + + continue + }, + + _ => (), + + } + + let mut nu_args = Vec::with_capacity( args.len() ) ; + for arg in args { + if let Some((arg, _)) = arg.subst_total( + & (& * subst, & nu_subst) + ) { + nu_args.push(arg) + } else { + return Ok(false) + } + } + + nu_cube.push( + term::eq( + term, + term::app(* op, nu_args) + ) + ) + }, + + RTerm::DTypSlc { typ, name, term: inner } => + if let Some((inner, _)) = inner.subst_total( + & (& * subst, & nu_subst) + ) { + nu_cube.push( + term::eq( + term, term::dtyp_slc( typ.clone(), name.clone(), inner ) + ) + ) + } else { + return Ok(false) + }, + + RTerm::DTypTst { name, term: inner, .. } => + if let Some((inner, _)) = inner.subst_total( + & (& * subst, & nu_subst) + ) { + nu_cube.push( + term::eq( + term, term::dtyp_tst( name.clone(), inner ) + ) + ) + } else { + return Ok(false) + }, + + RTerm::Fun { typ, name, args } => { + let mut nu_args = Vec::with_capacity( args.len() ) ; + for arg in args { + if let Some((arg, _)) = arg.subst_total( + & (& * subst, & nu_subst) + ) { + nu_args.push(arg) + } else { + return Ok(false) + } + } + + nu_cube.push( + term::eq( + term, term::fun( typ.clone(), name.clone(), nu_args ) + ) + ) + } + + } + + } + + for term in nu_cube { + cube.insert( term ) ; + } + for (var, term) in nu_subst { + let prev = subst.insert(var, term) ; + debug_assert! { prev.is_none() } + } + + Ok(true) +} + + +/// Extracts invariants from a function pre-definition. +/// +/// Assumes the solver has nothing declared / asserted. +fn get_invariants( + instance: & mut PreInstance, + name: & str, sig: & VarInfos, typ: & Typ, + definitions: & mut Vec< (TermSet, Term) >, +) -> Res< Option > { + let mut candidates = TermMap::new() ; + + log! { @3 "looking for invariants..." } + + for (idx, (cube, _)) in definitions.iter_mut().enumerate() { + cube.retain( + |term| { + let mut applications = None ; + + term.iter( + |sub| if let Some((fun, _)) = sub.fun_inspect() { + if fun == name { + applications.get_or_insert_with( + TermSet::new + ).insert( sub.to_hcons() ) ; + } + } + ) ; + + if let Some(applications) = applications { + candidates.entry( term.clone() ).or_insert_with( + || (Vec::new(), applications) + ).0.push(idx) ; + false + } else { + true + } + } + ) + } + + if candidates.is_empty() { + return Ok(None) + } + + let mut invariants = TermSet::new() ; + + scoped! { + let solver = instance.solver() ; + + for info in sig { + solver.declare_const(& info.idx, info) ? + } + + solver.declare_fun( name, sig, typ.get() ) ? ; + + use smt::{ SmtTerm, TermConj} ; + + for (candidate, (cubes, apps)) in candidates { + log! { @4 "checking candidate: {}", candidate } + let mut invariant = true ; + + for (cube, value) in definitions.iter() { + if_log! { @5 + log! { @5 "cube:" } + for term in cube { + log! { @5 " {}", term } + } + } + let actlit = solver.get_actlit() ? ; + for term in cube { + solver.assert_act( & actlit, & SmtTerm::new(term) ) ? + } + for app in apps.iter() { + let term = term::eq( app.clone(), value.clone() ) ; + solver.assert_act( & actlit, & SmtTerm::new(& term) ) ? + } + solver.assert_act_with( + & actlit, & TermConj::new( Some(& candidate) ), false + ) ? ; + + let sat = solver.check_sat_act( Some(& actlit) ) ? ; + + if sat { + invariant = false ; + break + } + } + + if invariant { + log! { @4 "invariant :)" } + let is_new = invariants.insert(candidate) ; + debug_assert! { is_new } + } else { + log! { @4 "not invariant :(" } + for cube in cubes { + definitions[cube].0.insert( candidate.clone() ) ; + } + } + + } + + } + + instance.reset_solver() ? ; + + let res = if invariants.is_empty() { + None + } else { + Some(invariants) + } ; + + Ok(res) +} + + + diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index 70f206cf..c03f7c07 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -15,6 +15,7 @@ mod arg_red ; mod bias_unroll ; mod unroll ; mod strict_neg_clauses ; +mod fun_preds ; use self::{ one_lhs::OneLhs, @@ -24,6 +25,7 @@ use self::{ bias_unroll:: BiasedUnroll, unroll::RUnroll, strict_neg_clauses::StrictNeg, + fun_preds:: FunPreds, } ; @@ -305,6 +307,8 @@ pub struct Reductor<'a> { runroll: Option, /// Optional strengthener by strict negative clauses. strict_neg: Option, + /// Optional predicate-to-function reduction. + fun_preds: Option, } impl<'a> Reductor<'a> { /// Constructor. @@ -349,13 +353,16 @@ impl<'a> Reductor<'a> { let strict_neg = some_new! { StrictNeg if strict_neg } ; + let fun_preds = some_new! { + FunPreds if fun_preds + } ; Ok( Reductor { instance, simplify, arg_red, one_rhs, one_lhs, cfg_red, biased_unroll, runroll, - strict_neg, + strict_neg, fun_preds, } ) } @@ -439,6 +446,13 @@ impl<'a> Reductor<'a> { if self.instance.is_solved() { break } + let changed = run! { fun_preds } ; + + if changed { + changed_since_cfg_red = true ; + continue + } + if changed_since_cfg_red { let changed = run! { cfg_red } ; diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index d1632c65..8283dec2 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -10,7 +10,7 @@ use instance::{ pub struct StrictNeg ; impl RedStrat for StrictNeg { - fn name(& self) -> & 'static str { "one_rhs" } + fn name(& self) -> & 'static str { "strict_neg" } fn new(_: & Instance) -> Self { StrictNeg } @@ -44,6 +44,7 @@ impl RedStrat for StrictNeg { ) = instance.strict_neg_clauses() ; for (clause_idx, clause) in strict_clauses { + log! { @3 "working on clause #{}", clause_idx } let (pred, args) = if let Some( (pred, argss) diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index b0dee7ec..8880fc5b 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -590,6 +590,34 @@ impl NuQuals { if instance.is_known(pred_info.idx) { continue 'all_preds } + // Add companion functions if any. + // fun::iter( + // |fun| { + // if fun.synthetic == Some(pred_info.idx) { + // let mut args = Vec::with_capacity( pred_info.sig.len() - 1 ) ; + // for (var, typ) in pred_info.sig.index_iter().take( + // pred_info.sig.len() - 1 + // ) { + // args.push( term::var(var, typ.clone()) ) + // } + // let fun_app = term::fun( + // fun.typ.clone(), fun.name.clone(), args + // ) ; + // let last: VarIdx = ( + // pred_info.sig.len() - 1 + // ).into() ; + // quals.insert( + // term::eq( + // term::var( last, fun.typ.clone() ), + // fun_app + // ), + // pred_info.idx + // ) ? ; + // } + // Ok(()) + // } + // ) ? ; + let mut sig = pred_info.sig.index_iter() ; prev.clear() ; diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs index a64a1c56..b823e4fb 100644 --- a/src/learning/ice/synth/adt.rs +++ b/src/learning/ice/synth/adt.rs @@ -39,6 +39,92 @@ impl Ord for AdtSynth { } } + +impl AdtSynth { + /// Bails by saying the internal is inconsistent. + #[inline] + fn typ_problem(& self) -> Error { + "inconsistent type for ADT synth".into() + } + /// Retrieves the content of an option or bails. + #[inline] + fn get_opt(& self, opt: Option) -> Res { + if let Some(t) = opt { + Ok(t) + } else { + bail!( self.typ_problem() ) + } + } + + /// Projects a single value. + fn project_val( + & self, typ: & Typ, var: VarIdx, val: & Val, map: & mut TermVals + ) -> Res<()> { + if ! val.is_known() + || val.typ() != self.typ { + return Ok(()) + } + + let var = term::var( var, self.typ.clone() ) ; + + // Apply unary functions from `self.typ` to `typ`. + for fun in & self.funs.from_typ { + if & fun.typ != typ { + continue + } + + let input: VarMap<_> = vec![ val.clone() ].into() ; + + let val = fun.def.eval(& input).chain_err( + || format!( + "while evaluating ({} {})", fun.name, val + ) + ) ? ; + + let term = term::fun( + typ.clone(), fun.name.clone(), vec![ var.clone() ] + ) ; + + let prev = map.insert(term, val) ; + debug_assert! { prev.is_none() } + } + + // Retrieve this value's constructor. + let (val_typ, val_cons, val_args) = self.get_opt( + val.dtyp_inspect() + ) ? ; + debug_assert_eq! { val_typ, & self.typ } + + // Apply selectors from the variant of `val` to `typ`. + let (val_dtyp, _) = self.get_opt( + val_typ.dtyp_inspect() + ) ? ; + + let selectors = self.get_opt( + val_dtyp.news.get(val_cons) + ) ? ; + + debug_assert_eq! { selectors.len(), val_args.len() } + + for ((slc, _), val_arg) in selectors.iter().zip( val_args.iter() ) { + if & val_arg.typ() == typ { + let term = term::dtyp_slc( + typ.clone(), slc.clone(), var.clone() + ) ; + let prev = map.insert( term, val_arg.clone() ) ; + debug_assert! { prev.is_none() } + } + } + + Ok(()) + } + +} + + + + + impl TheoSynth for AdtSynth { fn typ(& self) -> & Typ { & self.typ } @@ -69,29 +155,9 @@ impl TheoSynth for AdtSynth { fn project( & self, sample: & VarVals, typ: & Typ, map: & mut TermVals ) -> Res<()> { - for fun in & self.funs.from_typ { - if & fun.typ != typ { - continue - } - - for (var, val) in sample.index_iter() { - if val.is_known() - && val.typ() == self.typ { - let var = term::var( var, self.typ.clone() ) ; - let input: VarMap<_> = vec![ val.clone() ].into() ; - - let val = fun.def.eval(& input).chain_err( - || format!( - "while evaluating ({} {})", fun.name, val - ) - ) ? ; - - let term = term::fun( typ.clone(), fun.name.clone(), vec![var] ) ; - let prev = map.insert(term, val) ; - debug_assert! { prev.is_none() } - } - } + for (var, val) in sample.index_iter() { + self.project_val(typ, var, val, map) ? } Ok(()) diff --git a/src/learning/ice/synth/helpers.rs b/src/learning/ice/synth/helpers.rs index f4de5553..2a6ed314 100644 --- a/src/learning/ice/synth/helpers.rs +++ b/src/learning/ice/synth/helpers.rs @@ -417,44 +417,44 @@ fn sum_diff() { ] ; let expected = vec![ + "(= (+ v_0 v_1 v_2 (- 3)) 0)", "(>= (+ v_0 v_1 v_2) 3)", "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_2)) (- 3))", - "(= (+ v_0 v_1 v_2 (- 3)) 0)", + "(= (+ v_1 v_2 (- 3) (* (- 1) v_0)) 0)", "(>= (+ v_1 v_2 (* (- 1) v_0)) 3)", "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_2)) (- 3))", - "(= (+ v_1 v_2 (* (- 1) v_0) (- 3)) 0)", + "(= (+ v_0 v_2 (- 1) (* (- 1) v_1)) 0)", "(>= (+ v_0 v_2 (* (- 1) v_1)) 1)", "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_2)) (- 1))", - "(= (+ v_0 v_2 (- 1) (* (- 1) v_1)) 0)", + "(= (+ v_2 (- 1) (* (- 1) v_0) (* (- 1) v_1)) 0)", "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_1)) 1)", "(>= (+ v_0 v_1 (* (- 1) v_2)) (- 1))", - "(= (+ v_2 (- 1) (* (- 1) v_0) (* (- 1) v_1)) 0)", + "(= (+ v_0 v_1 v_3 (- 4)) 0)", "(>= (+ v_0 v_1 v_3) 4)", "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_3)) (- 4))", - "(= (+ v_0 v_1 v_3 (- 4)) 0)", + "(= (+ v_1 v_3 (* (- 1) v_0) (- 4)) 0)", "(>= (+ v_1 v_3 (* (- 1) v_0)) 4)", "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_3)) (- 4))", - "(= (+ v_1 v_3 (* (- 1) v_0) (- 4)) 0)", + "(= (+ v_0 v_3 (* (- 1) v_1) (- 2)) 0)", "(>= (+ v_0 v_3 (* (- 1) v_1)) 2)", "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_3)) (- 2))", - "(= (+ v_0 v_3 (* (- 1) v_1) (- 2)) 0)", + "(= (+ v_3 (* (- 1) v_0) (* (- 1) v_1) (- 2)) 0)", "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_1)) 2)", "(>= (+ v_0 v_1 (* (- 1) v_3)) (- 2))", - "(= (+ v_3 (* (- 1) v_0) (* (- 1) v_1) (- 2)) 0)", + "(= (+ v_0 v_2 v_3 (- 5)) 0)", "(>= (+ v_0 v_2 v_3) 5)", "(>= (+ (* (- 1) v_0) (* (- 1) v_2) (* (- 1) v_3)) (- 5))", - "(= (+ v_0 v_2 v_3 (- 5)) 0)", + "(= (+ v_2 v_3 (* (- 1) v_0) (- 5)) 0)", "(>= (+ v_2 v_3 (* (- 1) v_0)) 5)", "(>= (+ v_0 (* (- 1) v_2) (* (- 1) v_3)) (- 5))", - "(= (+ v_2 v_3 (* (- 1) v_0) (- 5)) 0)", + "(= (+ v_0 v_3 (- 1) (* (- 1) v_2)) 0)", "(>= (+ v_0 v_3 (* (- 1) v_2)) 1)", "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_3)) (- 1))", - "(= (+ v_0 v_3 (- 1) (* (- 1) v_2)) 0)", + "(= (+ v_3 (- 1) (* (- 1) v_0) (* (- 1) v_2)) 0)", "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_2)) 1)", "(>= (+ v_0 v_2 (* (- 1) v_3)) (- 1))", - "(= (+ v_3 (- 1) (* (- 1) v_0) (* (- 1) v_2)) 0)", ] ; let mut cnt = 0 ; @@ -523,21 +523,21 @@ where F : FnMut(Term) -> Res { let sum = term::add(sum) ; let done = f( - term::ge( sum.clone(), val.clone() ) + term::eq(sum.clone(), val.clone()) ) ? ; if done { return Ok(true) } let done = f( - term::le( sum.clone(), val.clone() ) + term::ge( sum.clone(), val.clone() ) ) ? ; if done { return Ok(true) } let done = f( - term::eq(sum, val) + term::le( sum, val ) ) ? ; if done { return Ok(true) diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 00537ac0..ec1342b6 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -202,7 +202,7 @@ impl ClauseRes { /// The operator of an s-expression. #[derive(Clone, Debug, PartialEq, Eq)] -enum FrameOp { +pub enum FrameOp { /// An actual operator. Op(Op), /// A constant array constructor. @@ -240,7 +240,7 @@ impl FrameOp { /// Term stack frame used in the parser to avoid recursion. -struct TermFrame { +pub struct TermFrame { /// Operator when going up. op: FrameOp, /// Position of the operator. @@ -315,6 +315,12 @@ impl ParserCxt { pred_name_map: BTreeMap::new(), } } + + /// Term stack accessor. + pub fn term_stack(& self) -> & Vec { + & self.term_stack + } + /// Generates a parser from itself. pub fn parser<'cxt, 's>( & 'cxt mut self, string: & 's str, line_off: usize, @@ -342,7 +348,7 @@ impl ParserCxt { /// Wraps an integer, represents a number of let-bindings parsed. #[must_use] #[derive(Clone, Copy)] -struct LetCount { +pub struct LetCount { n: usize } impl LetCount { @@ -376,6 +382,10 @@ pub struct Parser<'cxt, 's> { impl<'cxt, 's> Parser<'cxt, 's> { + /// Context accessor. + pub fn cxt(& self) -> & ParserCxt { + & * self.cxt + } /// Returns the text that hasn't been parsed yet. @@ -2477,6 +2487,26 @@ impl<'cxt, 's> Parser<'cxt, 's> { var_map: & VarInfos, map: & BTreeMap<& 's str, VarIdx>, instance: & Instance + ) -> Res< Option > { + let start_pos = self.pos() ; + let res = self.inner_term_opt(var_map, map, instance) ; + + if res.as_ref().map( |res| res.is_none() ).unwrap_or(true) { + self.cxt.term_stack.clear() ; + self.backtrack_to(start_pos) ; + } else { + debug_assert! { self.cxt.term_stack.is_empty() } + } + + res + } + + /// Parses a single term. + fn inner_term_opt( + & mut self, + var_map: & VarInfos, + map: & BTreeMap<& 's str, VarIdx>, + instance: & Instance ) -> Res< Option > { if ! self.cxt.term_stack.is_empty() { let e: Error = self.error_here("non-empty term stack").into() ; @@ -2486,13 +2516,16 @@ impl<'cxt, 's> Parser<'cxt, 's> { blah.push_str(line) ; blah.push('\n') } + let mut blah_2: String = "term stack:\n".into() ; + for frame in & self.cxt.term_stack { + blah_2 += & format!(" {:?}", frame.op) + } print_err( - & e.chain_err(|| blah) + & e.chain_err(|| blah).chain_err(|| blah_2) ) ; panic!("non-empty term stack during parsing") } conf.check_timeout() ? ; - let start_pos = self.pos() ; // The correct (non-error) way to exit this loop is // @@ -2615,7 +2648,28 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt() ; self.tag(keywords::op::is_) ? ; self.ws_cmt() ; - let (op_pos, ident) = self.ident() ? ; + + let (op_pos, ident) = if let Some(res) = self.ident_opt() ? { + res + } else if self.tag_opt("(") { + self.ws_cmt() ; + let res = self.ident() ? ; + self.ws_cmt() ; + self.tag("(") ? ; + self.ws_cmt() ; + while self.sort_opt()?.is_some() { + self.ws_cmt() + } + self.tag(")") ? ; + self.ws_cmt() ; + self.sort() ? ; + self.ws_cmt() ; + self.tag(")") ? ; + res + } else { + bail!( self.error_here("unexpected token") ) + } ; + self.ws_cmt() ; self.tag(")") ? ; @@ -2777,11 +2831,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { break 'read_kids Some(term) } ; - if res.is_none() { - self.cxt.term_stack.clear() ; - self.backtrack_to(start_pos) ; - } - Ok(res) } diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 45bb362d..04038450 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -33,6 +33,8 @@ pub struct Assistant { neg: PrdHMap< ClsSet >, /// Profiler. _profiler: Profiler, + /// True if we're using ADTs. + using_adts: bool, } impl Assistant { @@ -52,6 +54,8 @@ impl Assistant { let mut pos_clauses = ClsSet::new() ; let mut neg_clauses = ClsSet::new() ; + let using_adts = dtyp::get_all().iter().next().is_some() ; + macro_rules! add_clauses { ($pred:expr) => ({ if ! pos_clauses.is_empty() { @@ -96,7 +100,7 @@ impl Assistant { Ok( Assistant { // core, - solver, instance, pos, neg, _profiler + solver, instance, pos, neg, _profiler, using_adts } ) } @@ -249,7 +253,12 @@ impl Assistant { debug_assert_eq! { pred, p } debug_assert! { clause.lhs_preds().is_empty() } - self.solver.push(1) ? ; + if self.using_adts { + smt::reset(& mut self.solver, & self.instance) ? + } else { + self.solver.push(1) ? + } + clause.declare(& mut self.solver) ? ; self.solver.assert( & ConjWrap::new( clause.lhs_terms() ) @@ -261,7 +270,10 @@ impl Assistant { self.solver.check_sat() ? } "smt" } ; - self.solver.pop(1) ? ; + + if ! self.using_adts { + self.solver.pop(1) ? + } if sat { // msg! { debug self => " forcing positive" } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 9994d6dd..bf9f013d 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -986,9 +986,7 @@ impl<'a> Teacher<'a> { self.solver.push(1) ? } - let cexs = self.get_cex(clause, bias, conf.teacher.max_bias).chain_err( - || format!("while getting counterexample for clause #{}", clause) - ) ? ; + let cexs = self.get_cex(clause, bias, conf.teacher.max_bias) ? ; if self.restart_on_cex { smt::reset(& mut self.solver, & self.instance) ? @@ -1056,13 +1054,26 @@ impl<'a> Teacher<'a> { } else { log! { @debug " checksat" } - let sat = profile! { + let (sat, actlit) = profile! { self wrap { - self.solver.check_sat() + + if self.using_rec_funs { + self.solver.get_actlit().and_then( + |actlit| { + let sat = self.solver.check_sat_act( Some(& actlit) ) ? ; + Ok( (sat, Some(actlit)) ) + } + ) + } else { + self.solver.check_sat().map( + |sat| (sat, None) + ) + } + } "cexs", "check-sat" } ? ; - if sat { + let res = if sat { log! { @debug " sat, getting cex" } let bias = if self.instance[clause].is_positive() { Bias::Lft @@ -1086,8 +1097,14 @@ impl<'a> Teacher<'a> { } else { Ok(None) + } ; + + if let Some(actlit) = actlit { + self.solver.de_actlit(actlit) ? } + res + } } diff --git a/src/term/mod.rs b/src/term/mod.rs index ae7c41e6..cb723fb0 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -260,8 +260,8 @@ impl RTerm { LeafIter::of_rterm(self) } - /// Iterates over the subterms of a term. - fn iter(& self, mut f: F) { + /// Iterates over all the subterms of a term, including itself. + pub fn iter(& self, mut f: F) { let mut stack = vec![self] ; while let Some(term) = stack.pop() { @@ -859,8 +859,6 @@ impl RTerm { } - - /// Returns true if the term mentions a function. pub fn has_fun_apps(& self) -> bool { use self::zip::* ; @@ -873,9 +871,7 @@ impl RTerm { |_| Ok(()), |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(_) | - ZipOp::New(_) | - ZipOp::Slc(_) => Err(()), + ZipOp::Fun(_) => Err(()), _ => Ok( ZipDoTotal::Upp { yielded: () } ), }, @@ -995,7 +991,7 @@ impl RTerm { conf.bad(name), acc.len() + 1 ) } - term::dtyp_slc(typ.clone(), name.clone(), kid) + term::dtyp_tst(name.clone(), kid) } else { panic!( "illegal application of datatype tester {} to 0 arguments", diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 771ab583..3ccb8788 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -720,6 +720,30 @@ simpl_fun! { } } + // } else if let Some(val) = args[0].val() { + // if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { + // if dtyp_args.is_empty() { + // return Some( + // NormRes::Term( + // term::dtyp_tst( + // constructor.into(), args[1].clone() + // ) + // ) + // ) + // } + // } + // } else if let Some(val) = args[1].val() { + // if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { + // if dtyp_args.is_empty() { + // return Some( + // NormRes::Term( + // term::dtyp_tst( + // constructor.into(), args[0].clone() + // ) + // ) + // ) + // } + // } } } else { @@ -791,6 +815,27 @@ simpl_fun! { ] ) ) + } else if let Some(eq_args) = args[0].eq_inspect().cloned() { + + if let Some(val) = eq_args[0].val() { + if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { + if dtyp_args.is_empty() { + args[0] = term::dtyp_tst( + constructor.into(), eq_args[1].clone() + ) + } + } + } else if let Some(val) = eq_args[1].val() { + if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { + if dtyp_args.is_empty() { + args[0] = term::dtyp_tst( + constructor.into(), eq_args[0].clone() + ) + } + } + } + + None } else { None } diff --git a/src/term/typ.rs b/src/term/typ.rs index 524baee2..b456cd20 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -157,6 +157,17 @@ impl RTyp { false } + /// Returns the selectors of a constructor. + /// + /// Only legal on a datatype. + pub fn selectors_of(& self, constructor: & str) -> Res<& dtyp::CArgs> { + if let Some((dtyp, _)) = self.dtyp_inspect() { + dtyp.selectors_of(constructor) + } else { + bail!("cannot retrieve dtyp selectors on non-dtyp sort {}", self) + } + } + /// Inspects a datatype type. pub fn dtyp_inspect(& self) -> Option<(& DTyp, & TPrmMap)> { if let RTyp::DTyp { dtyp, prms } = self { From 627277fec58cb04341db9f03bf8702151e274e35 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 31 Aug 2018 09:15:30 +0900 Subject: [PATCH 43/94] assistant fix --- src/teacher/assistant.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 04038450..e8d7d9f5 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -271,7 +271,9 @@ impl Assistant { } "smt" } ; - if ! self.using_adts { + if self.using_adts { + smt::reset(& mut self.solver, & self.instance) ? + } else { self.solver.pop(1) ? } @@ -309,7 +311,12 @@ impl Assistant { } } ; - self.solver.push(1) ? ; + if self.using_adts { + smt::reset(& mut self.solver, & self.instance) ? + } else { + self.solver.push(1) ? + } + clause.declare(& mut self.solver) ? ; self.solver.assert( & ConjWrap::new( clause.lhs_terms() ) @@ -320,7 +327,12 @@ impl Assistant { self.solver.check_sat() ? } "smt" } ; - self.solver.pop(1) ? ; + + if self.using_adts { + smt::reset(& mut self.solver, & self.instance) ? + } else { + self.solver.pop(1) ? + } if sat { // msg! { debug self => " forcing negative" } From e32dad8a389f426abd7b891dc8bc9531aa848ac3 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 3 Sep 2018 16:37:22 +0900 Subject: [PATCH 44/94] minor bug fixes and general improvements --- Cargo.lock | 16 +- src/check/mod.rs | 14 +- src/dtyp/mod.rs | 10 ++ src/fun/mod.rs | 52 ++---- src/instance/instance/clause.rs | 3 +- src/instance/instance/pre_instance.rs | 48 +++--- src/instance/preproc/fun_preds.rs | 240 +++++++++++++++++--------- src/instance/preproc/mod.rs | 6 +- 8 files changed, 231 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 612b14fb..bb8c267e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,18 +33,18 @@ name = "backtrace-sys" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -59,7 +59,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -71,7 +71,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -92,7 +92,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -324,8 +324,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" diff --git a/src/check/mod.rs b/src/check/mod.rs index e644373a..7c2d8498 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -219,7 +219,7 @@ impl Data { pub fn check_clause( & self, solver: & mut Solver, Clause { args, body }: & Clause, count: usize, - ) -> Res { + ) -> Res< Option > { solver.reset() ? ; for unknown in & self.input.unknown { @@ -291,14 +291,13 @@ impl Data { println!(" }}") ; println!("\")") ; println!() ; - Ok(false) + Ok( Some(false) ) } else if let Some(false) = res { log_info!("clause {} is fine", count) ; - Ok(true) + Ok( Some(true) ) } else { - bail!( - "clause {}'s check resulted in unknown", count - ) + log_info!("got unknown on clause {}, assuming it's okay", count) ; + Ok(None) } } @@ -311,7 +310,8 @@ impl Data { // Check all clauses one by one. for (count, clause) in self.input.clauses.iter().enumerate() { match self.check_clause(solver, clause, count) { - Ok(ok) => okay = okay && ok, + Ok( Some(ok) ) => okay = okay && ok, + Ok(None) => (), Err(e) => { err = true ; let e = e.chain_err( diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 0df8404f..7ffb61ba 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -364,6 +364,16 @@ lazy_static! { } +/// True if there is at least one datatype declared. +pub fn one_or_more() -> Res { + if let Ok(f) = factory.read() { + Ok( f.len() > 0 ) + } else { + bail!("could not access dtyp factory") + } +} + + /// Checks whether a datatype is reserved. pub fn check_reserved(name: & str) -> Res<()> { if reserved_dtyps.contains(name) { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 60bb2288..c6227d8b 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -145,10 +145,7 @@ pub fn mk(fun: RFun) -> Res { } -/// Orders all functions by dependencies. -/// -/// Assumes there's no circular dependencies between the `mentions` fields. -/// Meaning the semantics of `deps` and `mentions` are respected. +/// Groups all functions by dependencies. pub fn ordered() -> Res< Vec< Vec > > { let mut all: Vec<_> = read_factory().0.values().cloned().collect() ; @@ -169,32 +166,6 @@ pub fn ordered() -> Res< Vec< Vec > > { groups.push(group) } - groups.sort_by( - |g_1, g_2| { - use std::cmp::Ordering::* ; - for f_1 in g_1 { - for f_2 in g_2 { - if f_2.mentions.contains( & f_1.name ) { - return Less - } else if f_1.mentions.contains( & f_2.name ) { - return Greater - } - } - } - Equal - } - ) ; - - // println!() ; - // println!("groups:") ; - // for group in & groups { - // print!(" ") ; - // for fun in group { - // print!(" {},", fun.name) - // } - // println!() - // } - Ok(groups) } @@ -257,7 +228,7 @@ pub fn write_all( if group.len() == 1 { let fun = & group[0] ; - let def_key = if fun.mentions.contains(& fun.name) { + let def_key = if fun.recursive { consts::keywords::cmd::def_fun_rec } else { consts::keywords::cmd::def_fun @@ -451,8 +422,6 @@ pub struct RFun { pub name: String, /// Other functions this function depends on. pub deps: BTreeSet, - /// Functions mentioned in the body of the function. - pub mentions: BTreeSet, /// Signature. /// /// The string stored is the original name of the argument. @@ -465,6 +434,8 @@ pub struct RFun { pub synthetic: Option, /// Invariants of the function. pub invariants: TermSet, + /// True if the function is recursive. + recursive: bool, } impl PartialEq for RFun { @@ -501,9 +472,9 @@ impl RFun { ) -> Self { let name = name.into() ; RFun { - name, deps: BTreeSet::new(), mentions: BTreeSet::new(), + name, deps: BTreeSet::new(), sig, typ, def: term::tru(), synthetic: None, - invariants: TermSet::new(), + invariants: TermSet::new(), recursive: false, } } @@ -525,6 +496,11 @@ impl RFun { self.synthetic = Some(pred) } + /// True if the function is recursive. + pub fn is_recursive(& self) -> bool { + self.recursive + } + /// Sets the definition of a function. /// /// # Panics @@ -533,8 +509,10 @@ impl RFun { pub fn set_def(& mut self, def: Term) { def.iter( |trm| if let Some((name, _)) = trm.fun_inspect() { - if ! self.deps.contains(name) { - self.mentions.insert( name.to_string() ) ; + if name == & self.name { + self.recursive = true + } else { + self.deps.insert( name.to_string() ) ; } } ) ; diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index d100e4f0..7f20fb00 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -607,7 +607,6 @@ impl Clause { // `term` is more generic, discard. Cmp(Less) => { rmed_stuff = true ; - // println!(" removing {}", t) ; false }, // No relation, keep `t`. @@ -623,8 +622,8 @@ impl Clause { } ) } + // If we removed stuff, it means the term should not be redundant. - assert! { ! rmed_stuff || ! redundant } if ! redundant { let is_new = set.insert(term) ; debug_assert! { is_new } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index f4640147..e574d5a5 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -14,19 +14,21 @@ use instance::{ /// Performs a checksat. macro_rules! check_sat { ($pre_instance:expr) => ({ - let actlit = if $pre_instance.use_actlits { - Some( $pre_instance.solver.get_actlit() ? ) - } else { - None - } ; - - let sat = $pre_instance.solver.check_sat_act( actlit.as_ref() ) ? ; - - if let Some(actlit) = actlit { - $pre_instance.solver.de_actlit(actlit) ? - } + // let actlit = if $pre_instance.reset_solver { + // Some( $pre_instance.solver.get_actlit() ? ) + // } else { + // None + // } ; + + // let sat = + $pre_instance.solver.check_sat() ? + // ; + + // if let Some(actlit) = actlit { + // $pre_instance.solver.de_actlit(actlit) ? + // } - sat + // sat }) ; } @@ -52,7 +54,7 @@ pub struct PreInstance<'a> { extraction: ExtractionCxt, /// Use actlits in checksats. - use_actlits: bool, + reset_solver: bool, } impl<'a> PreInstance<'a> { /// Constructor. @@ -62,14 +64,14 @@ impl<'a> PreInstance<'a> { let simplifier = ClauseSimplifier::new() ; let clauses_to_simplify = Vec::with_capacity(7) ; - let mut use_actlits = false ; + let mut reset_solver = false ; fun::iter( - |_| { use_actlits = true ; Ok(()) } + |_| { reset_solver = true ; Ok(()) } ) ? ; if dtyp::get_all().iter().next().is_some() { - use_actlits = true + reset_solver = true } Ok( @@ -78,7 +80,7 @@ impl<'a> PreInstance<'a> { clauses_to_simplify, vars: VarSet::new(), extraction: ExtractionCxt::new(), - use_actlits, + reset_solver, } ) } @@ -322,7 +324,7 @@ impl<'a> PreInstance<'a> { info += self.force_trivial() ? ; - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } @@ -346,7 +348,7 @@ impl<'a> PreInstance<'a> { ) ? ; } - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } @@ -694,7 +696,7 @@ impl<'a> PreInstance<'a> { /// - the rhs is a predicate application contained in the lhs. #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] fn is_clause_trivial(& mut self, clause_idx: ClsIdx) -> Res { - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.push(1) ? ; @@ -702,7 +704,7 @@ impl<'a> PreInstance<'a> { let res = self.solver.is_clause_trivial( & mut self.instance[clause_idx] ) ; - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.pop(1) ? ; @@ -734,13 +736,13 @@ impl<'a> PreInstance<'a> { pub fn is_this_clause_trivial( & mut self, clause: & mut Clause ) -> Res< Option > { - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.push(1) ? ; } let res = self.solver.is_clause_trivial(clause) ; - if self.use_actlits { + if self.reset_solver { smt::reset(& mut self.solver, & self.instance) ? ; } else { self.solver.pop(1) ? ; diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index c82fe9fe..ba894d2a 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -1,4 +1,4 @@ -//! Predicate-to-function reduction. + //! Predicate-to-function reduction. use common::* ; use instance::{ @@ -11,6 +11,7 @@ use fun::RFun ; pub struct FunPreds ; + impl FunPreds { /// Finalizes a definition for a predicate. /// @@ -18,7 +19,8 @@ impl FunPreds { pub fn finalize_definition( instance: & mut PreInstance, name: & str, sig: & VarInfos, typ: & Typ, - mut definitions: Vec<(TermSet, Term)> + mut definitions: Vec<(TermSet, Term, bool)>, + check: bool ) -> Res< Option<(Term, Option)> > { instance.reset_solver() ? ; @@ -35,65 +37,69 @@ impl FunPreds { } } - log! { @3 "checking conditions are mutually exclusive" } - let solver = instance.solver() ; + if check { - for info in sig { - solver.declare_const(& info.idx, info.typ.get()) ? - } + log! { @3 "checking conditions are mutually exclusive" } + let solver = instance.solver() ; - solver.declare_fun(name, sig, typ.get()) ? ; + for info in sig { + solver.declare_const(& info.idx, info.typ.get()) ? + } - let mut actlits = Vec::with_capacity( definitions.len() ) ; + solver.declare_fun(name, sig, typ.get()) ? ; - for (cube, _) in & definitions { - use smt::TermConj ; - let conj = TermConj::new( cube.iter() ) ; - let actlit = solver.get_actlit() ? ; - solver.assert_act_with( & actlit, & conj, true ) ? ; - actlits.push(actlit) - } + let mut actlits = Vec::with_capacity( definitions.len() ) ; - scoped! { - let mut actlits_iter = actlits.iter() ; + for (cube, _, _) in & definitions { + use smt::TermConj ; + let conj = TermConj::new( cube.iter() ) ; + let actlit = solver.get_actlit() ? ; + solver.assert_act_with( & actlit, & conj, true ) ? ; + actlits.push(actlit) + } - while let Some(actlit) = actlits_iter.next() { - for other in actlits_iter.clone() { - let not_exclusive = solver.check_sat_act( - vec![ actlit, other ] - ) ? ; - if not_exclusive { - return Ok(None) + scoped! { + let mut actlits_iter = actlits.iter() ; + + while let Some(actlit) = actlits_iter.next() { + for other in actlits_iter.clone() { + let not_exclusive = solver.check_sat_act( + vec![ actlit, other ] + ) ? ; + if not_exclusive { + return Ok(None) + } } } } - } - for actlit in actlits { - solver.de_actlit(actlit) ? - } + for actlit in actlits { + solver.de_actlit(actlit) ? + } - log! { @3 "all branches are exclusive, checking they're exhaustive" } + log! { @3 "all branches are exclusive, checking they're exhaustive" } - for (cube, _) in & definitions { - use smt::TermConj ; - let conj = TermConj::new( cube.iter() ) ; - solver.assert_with( & conj, false ) ? ; - } + for (cube, _, _) in & definitions { + use smt::TermConj ; + let conj = TermConj::new( cube.iter() ) ; + solver.assert_with( & conj, false ) ? ; + } - let not_exhaustive = solver.check_sat() ? ; + let not_exhaustive = solver.check_sat() ? ; + + if not_exhaustive { + // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; + log! { @3 "branches are not exhaustive, aborting" } + return Ok(None) + } else { + log! { @3 "branches are exhaustive, building definition" } + } - if not_exhaustive { - // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; - log! { @3 "branches are not exhaustive, aborting" } - return Ok(None) - } else { - log! { @3 "branches are exhaustive, building definition" } } let mut def = None ; - for (cube, value) in definitions.into_iter().rev() { + for (cube, value, _) in definitions.into_iter().rev() { let nu_def = if let Some(def) = def { term::ite( term::and( cube.into_iter().collect() ), @@ -116,7 +122,7 @@ impl FunPreds { /// Reduces a predicate to a function. pub fn reduce_pred( - instance: & mut PreInstance, pred: PrdIdx + instance: & mut PreInstance, pred: PrdIdx, use_all_args: bool ) -> Res< Option > { let mut info = RedInfo::new() ; // Clauses to remove. @@ -127,17 +133,37 @@ impl FunPreds { debug_assert! { to_rm.is_empty() } let mut var_infos = VarInfos::new() ; - for typ in & instance[pred].sig[ 0 .. instance[pred].sig.len() - 1 ] { + + let args_len = if use_all_args { + instance[pred].sig.len() + } else { + instance[pred].sig.len() - 1 + } ; + + for typ in & instance[pred].sig[ 0 .. args_len ] { let idx = var_infos.next_index() ; let var_info = VarInfo::new( idx.default_str(), typ.clone(), idx ) ; var_infos.push(var_info) } - let last: VarIdx = ( instance[pred].sig.len() - 1 ).into() ; + let last: Option = if use_all_args { + None + } else { + Some( ( instance[pred].sig.len() - 1 ).into() ) + } ; - let pred_fun_name = format!("{}_hoice_reserved_fun", instance[pred]) ; - let pred_fun_typ = instance[pred].sig[last].clone() ; + let pred_fun_name = make_fun_name(& instance[pred].name).chain_err( + || format!( + "while creating function for predicate {}", + conf.emph(& instance[pred].name) + ) + ) ? ; + let pred_fun_typ = if let Some(last) = last.as_ref() { + instance[pred].sig[* last].clone() + } else { + typ::bool() + } ; let mut rfun = RFun::new( pred_fun_name.clone(), var_infos, pred_fun_typ.clone() @@ -177,7 +203,7 @@ impl FunPreds { let ( mut cube, mut subst - ) = if let Some((cube, subst)) = args_invert(rhs_args) ? { + ) = if let Some((cube, subst)) = args_invert(rhs_args, args_len) ? { (cube, subst) } else { log!(@3 "failed to invert rhs arguments") ; @@ -195,6 +221,8 @@ impl FunPreds { } } + let recursive = instance[clause].lhs_preds().contains_key(& pred) ; + let mut lhs_argss: Vec<_> = instance[ clause ].lhs_preds().get(& pred).map( @@ -202,16 +230,18 @@ impl FunPreds { ).unwrap_or_else( Vec::new ) ; let mut nu_args = Vec::with_capacity( - instance[pred].sig.len() - 1 + args_len ) ; + let mut value = None ; + while ! lhs_argss.is_empty() { let prev_len = lhs_argss.len() ; let mut failed: Res<_> = Ok(false) ; lhs_argss.retain( |args| { - for arg in & args[ 0 .. args.len() - 1 ] { + for arg in & args[ 0 .. args_len ] { if let Some((arg, _)) = arg.subst_total( & subst ) { nu_args.push(arg) } else { @@ -219,8 +249,6 @@ impl FunPreds { return true } } - - let last: VarIdx = (args.len() - 1).into() ; let nu_args = ::std::mem::replace( & mut nu_args, Vec::with_capacity( instance[pred].sig.len() - 1 @@ -231,9 +259,16 @@ impl FunPreds { pred_fun_typ.clone(), pred_fun_name.clone(), nu_args ) ; - let okay = map_invert( - & args[ last ], fun_app, & mut subst, & mut cube - ) ; + let okay = if let Some(last) = last.as_ref() { + let last = * last ; + + map_invert( + & args[ last ], fun_app, & mut subst, & mut cube + ) + } else { + value.get_or_insert_with(Vec::new).push(fun_app) ; + Ok(true) + } ; match okay { // Success, don't retain. @@ -241,7 +276,7 @@ impl FunPreds { // Failure, retain. Ok(false) => { log!(@3 - "could not invert last argument ({}) of ({} {})", + "could not invert last argument ({:?}) of ({} {})", last, instance[pred], args ) ; if let Ok(failed) = failed.as_mut() { @@ -252,7 +287,7 @@ impl FunPreds { // Error, do whatever. err => { failed = err.chain_err( - || format!("while inverting {}", & args[last]) + || format!("while inverting ({} {})", instance[pred], args) ) ; true }, @@ -293,30 +328,53 @@ impl FunPreds { } } - let res = if let Some((res, _)) = rhs_args[last].subst_total(& subst) { - res + let res = if let Some(last) = last.as_ref() { + if let Some((res, _)) = rhs_args[* last].subst_total(& subst) { + res + } else { + log!(@3 "failed to retrieve value, aborting") ; + abort!() + } + } else if let Some(conj) = value { + term::and(conj) } else { - log!(@3 "failed to retrieve value, aborting") ; - abort!() + term::tru() } ; log!(@4 "value: {}", res) ; - definitions.push( (cube, res) ) + definitions.push( (cube, res, recursive) ) } definitions.sort_by( - |(c_1, _), (c_2, _)| c_1.len().cmp( & c_2.len() ) + |(c_1, _, rec_1), (c_2, _, rec_2)| { + use std::cmp::Ordering::* ; + if * rec_1 && ! * rec_2 { + Less + } else if * rec_2 && ! * rec_1 { + Greater + } else { + c_1.len().cmp( & c_2.len() ) + } + } ) ; + if use_all_args { + let mut tru = TermSet::new() ; + tru.insert( term::tru() ) ; + definitions.push( + (tru, term::fls(), false) + ) + } + log!(@3 "done working on {}", instance[pred]) ; if_log! { @4 - for (cube, res) in & definitions { + for (cube, res, recursive) in & definitions { log!(@4 "when {{") ; for term in cube { log!(@4 " {}", term) } - log!(@4 "}} -> {}", res) + log!(@4 "}} -{}> {}", if * recursive { "rec-" } else { "" }, res) } } @@ -332,7 +390,8 @@ impl FunPreds { let mut dec = fun::retrieve_dec(& pred_fun_name) ? ; let (def, invs) = if let Some(def) = FunPreds::finalize_definition( - instance, & pred_fun_name, & dec.sig, & pred_fun_typ, definitions + instance, & pred_fun_name, & dec.sig, & pred_fun_typ, definitions, + ! use_all_args ) ? { def } else { @@ -368,9 +427,9 @@ impl FunPreds { info.clauses_rmed += to_rm.len() ; instance.forget_clauses(& mut to_rm) ? ; - let mut args = Vec::with_capacity( instance[pred].sig.len() - 1 ) ; + let mut args = Vec::with_capacity( args_len ) ; for (var, typ) in instance[pred].sig.index_iter().take( - instance[pred].sig.len() - 1 + args_len ) { args.push( term::var(var, typ.clone()) ) } @@ -378,10 +437,14 @@ impl FunPreds { fun.typ.clone(), fun.name.clone(), args ) ; - let def = term::eq( - term::var( last, fun.typ.clone() ), + let def = if let Some(last) = last.as_ref() { + term::eq( + term::var( * last, fun.typ.clone() ), + fun_app + ) + } else { fun_app - ) ; + } ; info.preds += 1 ; let mut tterm_set = TTermSet::new() ; @@ -474,12 +537,21 @@ impl RedStrat for FunPreds { } while let Some(pred) = to_inline.pop() { - let res = FunPreds::reduce_pred(instance, pred) ? ; + let res = FunPreds::reduce_pred(instance, pred, false) ? ; // pause("to resume fun_preds", & Profiler::new()) ; if let Some(red_info) = res { new_stuff = true ; info += red_info ; break + } else { + // let res = FunPreds::reduce_pred(instance, pred, true) ? ; + // // pause("to resume fun_preds", & Profiler::new()) ; + // if let Some(red_info) = res { + // new_stuff = true ; + // info += red_info ; + // break + // } + () } } @@ -495,7 +567,7 @@ impl RedStrat for FunPreds { /// Builds a cube and a substitution corresponding to inverting some arguments. pub fn args_invert( - args: & VarTerms + args: & VarTerms, args_len: usize ) -> Res< Option<(TermSet, VarHMap)> > { let (mut cube, mut subst) = ( TermSet::new(), VarHMap::new() @@ -505,7 +577,7 @@ pub fn args_invert( let mut postponed = Vec::new() ; let mut current: Vec<_> = args.index_iter().take( - args.len() - 1 + args_len ).map( |(var, term)| ( term::var(var, term.typ()), term ) ).collect() ; @@ -839,13 +911,13 @@ pub fn map_invert( fn get_invariants( instance: & mut PreInstance, name: & str, sig: & VarInfos, typ: & Typ, - definitions: & mut Vec< (TermSet, Term) >, + definitions: & mut Vec< (TermSet, Term, bool) >, ) -> Res< Option > { let mut candidates = TermMap::new() ; log! { @3 "looking for invariants..." } - for (idx, (cube, _)) in definitions.iter_mut().enumerate() { + for (idx, (cube, _, _)) in definitions.iter_mut().enumerate() { cube.retain( |term| { let mut applications = None ; @@ -893,7 +965,7 @@ fn get_invariants( log! { @4 "checking candidate: {}", candidate } let mut invariant = true ; - for (cube, value) in definitions.iter() { + for (cube, value, _) in definitions.iter() { if_log! { @5 log! { @5 "cube:" } for term in cube { @@ -947,4 +1019,14 @@ fn get_invariants( } - +fn make_fun_name(other_name: & str) -> Res { + let split: Vec<_> = other_name.split('|').collect() ; + let str = match split.len() { + 1 => format!("{}_hoice_reserved_fun", other_name), + 3 if split[0] == "" && split[2] == "" && split[1] != "" => { + format!("|{}_hoice_reserved_fun|", split[1]) + }, + _ => bail!("illegal symbol `{}`", other_name), + } ; + Ok(str) +} diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index c03f7c07..691aa5ee 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -353,8 +353,10 @@ impl<'a> Reductor<'a> { let strict_neg = some_new! { StrictNeg if strict_neg } ; - let fun_preds = some_new! { - FunPreds if fun_preds + let fun_preds = if ! dtyp::one_or_more() ? { + None + } else { + some_new! { FunPreds if fun_preds } } ; Ok( From e198b15eef2b35ac467f88e2c8b1fedff6dd5891 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 5 Sep 2018 20:39:15 +0900 Subject: [PATCH 45/94] rustfmt --- Cargo.lock | 8 +- Cargo.toml | 2 +- src/bin/main.rs | 38 +- src/check/mod.rs | 652 +- src/check/parse.rs | 1057 ++-- src/common/config.rs | 2934 +++++---- src/common/consts.rs | 193 +- src/common/macros.rs | 251 +- src/common/mod.rs | 1074 ++-- src/common/msg.rs | 693 +-- src/common/profiling.rs | 763 ++- src/common/smt.rs | 1397 ++--- src/common/wrappers.rs | 185 +- src/data/constraint.rs | 699 ++- src/data/info.rs | 93 +- src/data/mod.rs | 2612 ++++---- src/data/sample.rs | 85 +- src/dtyp/mod.rs | 1281 ++-- src/errors.rs | 204 +- src/fun/mod.rs | 831 ++- src/hoice.rs | 529 +- src/instance/info.rs | 100 +- src/instance/instance/clause.rs | 1855 +++--- src/instance/instance/mod.rs | 3467 +++++------ src/instance/instance/pre_instance.rs | 3822 ++++++------ src/instance/mod.rs | 10 +- src/instance/preproc/arg_red.rs | 376 +- src/instance/preproc/bias_unroll.rs | 1820 +++--- src/instance/preproc/cfg_red.rs | 2248 +++---- src/instance/preproc/fun_preds.rs | 1618 +++-- src/instance/preproc/mod.rs | 758 ++- src/instance/preproc/one_lhs.rs | 378 +- src/instance/preproc/one_rhs.rs | 318 +- src/instance/preproc/strict_neg_clauses.rs | 222 +- src/instance/preproc/unroll.rs | 310 +- src/instance/preproc/utils.rs | 1375 ++--- src/learning/ice/data.rs | 1153 ++-- src/learning/ice/mod.rs | 2122 +++---- src/learning/ice/quals.rs | 1369 ++--- src/learning/ice/synth/adt.rs | 363 +- src/learning/ice/synth/helpers.rs | 858 ++- src/learning/ice/synth/int.rs | 181 +- src/learning/ice/synth/mod.rs | 444 +- src/learning/ice/synth/real.rs | 179 +- src/learning/mod.rs | 2 +- src/parse/mod.rs | 6422 +++++++++----------- src/parse/ptterms.rs | 845 ++- src/split.rs | 604 +- src/teacher/assistant.rs | 795 ++- src/teacher/cex_bias.rs | 605 +- src/teacher/mod.rs | 2023 +++--- src/term/eval.rs | 632 +- src/term/factory.rs | 553 +- src/term/leaf_iter.rs | 145 +- src/term/mod.rs | 2302 +++---- src/term/op.rs | 920 ++- src/term/simplify.rs | 1110 ++-- src/term/test.rs | 494 +- src/term/tterms.rs | 2623 ++++---- src/term/typ.rs | 756 +-- src/term/zip.rs | 671 +- src/unsat_core/mod.rs | 187 +- src/unsat_core/sample_graph.rs | 1546 +++-- src/val/mod.rs | 2401 ++++---- src/var_to/mod.rs | 8 +- src/var_to/terms.rs | 14 +- src/var_to/vals.rs | 440 +- 67 files changed, 32467 insertions(+), 34558 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb8c267e..514bcf9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,7 +123,7 @@ dependencies = [ "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)", + "rsmt2 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -251,8 +251,8 @@ dependencies = [ [[package]] name = "rsmt2" -version = "0.9.10" -source = "git+https://github.com/kino-mc/rsmt2#78f86f9b3c8c051fdf1c85f6f00269294202f443" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -349,7 +349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum rsmt2 0.9.10 (git+https://github.com/kino-mc/rsmt2)" = "" +"checksum rsmt2 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "5949744877d925e91941c0a6100aa4b58c08d9b8f1570060fa75cd735427b993" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" diff --git a/Cargo.toml b/Cargo.toml index 47560d1c..5a1960ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ clap = "*" hashconsing = "*" error-chain = "*" ansi_term = "*" -rsmt2 = { git = "https://github.com/kino-mc/rsmt2" } +rsmt2 = "^0.9.11" num = "*" mylib = { git = "https://github.com/AdrienChampion/mylib" } either = "*" diff --git a/src/bin/main.rs b/src/bin/main.rs index 0ea10276..5dd6f337 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,25 +1,25 @@ //! Entry point for the binary. -extern crate hoice ; +extern crate hoice; -use hoice::common::* ; +use hoice::common::*; fn main() { - // Work and report error if any. - if let Err(errs) = ::hoice::work() { - let errs = match * errs.kind() { - ErrorKind::Z3SpawnError => format!( - "could not spawn z3 using command `{}`\n\ - make sure the z3 binary has that name and is in your path,\n\ - or specify a different z3 command with option `{}`", - conf.emph( & conf.solver.conf().get_cmd() ), - conf.emph( "--z3" ) - ).into(), - _ => errs - } ; - print_err(& errs) ; - ::std::process::exit(2) - } else { - ::std::process::exit(0) - } + // Work and report error if any. + if let Err(errs) = ::hoice::work() { + let errs = match *errs.kind() { + ErrorKind::Z3SpawnError => format!( + "could not spawn z3 using command `{}`\n\ + make sure the z3 binary has that name and is in your path,\n\ + or specify a different z3 command with option `{}`", + conf.emph(&conf.solver.conf().get_cmd()), + conf.emph("--z3") + ).into(), + _ => errs, + }; + print_err(&errs); + ::std::process::exit(2) + } else { + ::std::process::exit(0) + } } diff --git a/src/check/mod.rs b/src/check/mod.rs index 7c2d8498..94c4fd1a 100644 --- a/src/check/mod.rs +++ b/src/check/mod.rs @@ -11,428 +11,402 @@ //! [hoice]: https://github.com/hopv/hoice (hoice github repository) //! [smt]: http://smtlib.cs.uiowa.edu/ (SMT-LIB website) -use errors::* ; -use common::{ conf, ColorExt, Solver, HashMap, Read } ; +use common::{conf, ColorExt, HashMap, Read, Solver}; +use errors::*; -pub mod parse ; +pub mod parse; -use self::parse::InParser ; -use self::smt::* ; +use self::parse::InParser; +use self::smt::*; /// A predicate is just a string. -pub type Pred = String ; +pub type Pred = String; /// A type is just a string. -pub type Typ = String ; +pub type Typ = String; /// A signature. -pub type Sig = Vec ; - +pub type Sig = Vec; /// A function definition. #[derive(Clone)] pub struct FunDef { - /// Name. - pub name: String, - /// Arguments. - pub args: Args, - /// Return type. - pub typ: Typ, - /// Body. - pub body: Term, + /// Name. + pub name: String, + /// Arguments. + pub args: Args, + /// Return type. + pub typ: Typ, + /// Body. + pub body: Term, } /// A predicate declaration. #[derive(Clone)] pub struct PredDec { - /// Predicate. - pub pred: Pred, - /// Signature. - pub sig: Sig, + /// Predicate. + pub pred: Pred, + /// Signature. + pub sig: Sig, } /// An ident is just a string. -pub type Ident = String ; +pub type Ident = String; /// A value is a string. -pub type Value = String ; +pub type Value = String; /// A list of arguments. -pub type Args = Vec<(Ident, Typ)> ; +pub type Args = Vec<(Ident, Typ)>; /// A term is just a string. -pub type Term = String ; +pub type Term = String; /// A predicate definition. #[derive(Clone)] pub struct PredDef { - /// Predicate. - pub pred: Pred, - /// Arguments. - pub args: Args, - /// Body. - pub body: Option, + /// Predicate. + pub pred: Pred, + /// Arguments. + pub args: Args, + /// Body. + pub body: Option, } /// A clause. #[derive(Clone)] pub struct Clause { - /// Arguments. - pub args: Args, - /// Body. - pub body: Term, + /// Arguments. + pub args: Args, + /// Body. + pub body: Term, } /// Data from the input file. pub struct Input { - /// Unknown stuff. - pub unknown: Vec, - /// Predicate declarations. - pub pred_decs: Vec, - /// Function definitions. - pub fun_defs: Vec, - /// Clauses. - pub clauses: Vec, + /// Unknown stuff. + pub unknown: Vec, + /// Predicate declarations. + pub pred_decs: Vec, + /// Function definitions. + pub fun_defs: Vec, + /// Clauses. + pub clauses: Vec, } impl Input { - /// Loads some input data from a file. - pub fn of_file>(file: P) -> Res { - use std::fs::OpenOptions ; - let file = file.as_ref() ; - log_info!{ - "loading horn clause file {}...", conf.emph(file.to_string_lossy()) + /// Loads some input data from a file. + pub fn of_file>(file: P) -> Res { + use std::fs::OpenOptions; + let file = file.as_ref(); + log_info!{ + "loading horn clause file {}...", conf.emph(file.to_string_lossy()) + } + let mut buff = String::new(); + OpenOptions::new() + .read(true) + .open(file) + .chain_err(|| format!("while opening file {}", conf.emph(file.to_string_lossy())))? + .read_to_string(&mut buff) + .chain_err(|| format!("while reading file {}", conf.emph(file.to_string_lossy())))?; + Self::of_str(&buff) + } + /// Loads some input data from a string. + pub fn of_str(data: &str) -> Res { + InParser::new(data).parse_input() } - let mut buff = String::new() ; - OpenOptions::new().read(true).open(file).chain_err( - || format!( - "while opening file {}", conf.emph(file.to_string_lossy()) - ) - )?.read_to_string(& mut buff).chain_err( - || format!( - "while reading file {}", conf.emph(file.to_string_lossy()) - ) - )? ; - Self::of_str(& buff) - } - /// Loads some input data from a string. - pub fn of_str(data: & str) -> Res { - InParser::new(data).parse_input() - } } /// Output of a `hoice` run. pub struct Output { - /// Predicate definitions. - pub pred_defs: Vec, + /// Predicate definitions. + pub pred_defs: Vec, } impl Output { - /// Loads some input data from a file. - pub fn of_file(file: & str) -> Res { - use std::fs::OpenOptions ; - log_info!{ "loading hoice output file {}...", conf.emph(file) } - let mut buff = String::new() ; - OpenOptions::new().read(true).open(file).chain_err( - || format!( "while opening file {}", conf.emph(file) ) - )?.read_to_string(& mut buff).chain_err( - || format!( "while reading file {}", conf.emph(file) ) - )? ; - Self::of_str(& buff) - } - /// Loads some input data from a string. - pub fn of_str(data: & str) -> Res { - InParser::new(data).parse_output() - } - - /// Checks the signature of the predicates match the declarations of an input - /// `smt2` file. Also checks that all predicates are defined *once*. - pub fn check_consistency(& mut self, input: & Input) -> Res<()> { - log_info!{ "checking predicate signature consistency..." } - let mut map = HashMap::with_capacity( self.pred_defs.len() ) ; - log! { @4 "checking for duplicate definitions" } - for & PredDef { ref pred, ref args, .. } in & self.pred_defs { - let prev = map.insert(pred.clone(), args.clone()) ; - if prev.is_some() { - bail!( - "predicate {} is defined twice in hoice's output", conf.emph(pred) - ) - } + /// Loads some input data from a file. + pub fn of_file(file: &str) -> Res { + use std::fs::OpenOptions; + log_info!{ "loading hoice output file {}...", conf.emph(file) } + let mut buff = String::new(); + OpenOptions::new() + .read(true) + .open(file) + .chain_err(|| format!("while opening file {}", conf.emph(file)))? + .read_to_string(&mut buff) + .chain_err(|| format!("while reading file {}", conf.emph(file)))?; + Self::of_str(&buff) + } + /// Loads some input data from a string. + pub fn of_str(data: &str) -> Res { + InParser::new(data).parse_output() } - log! { @4 "checking signatures" } - for & PredDec { ref pred, ref sig } in & input.pred_decs { - if let Some(args) = map.get(pred) { - - if sig.len() != args.len() { - bail!( - "arguments of predicate {}'s definition \ - does not match its signature", conf.emph(pred) - ) - } - for (count, (ty_1, & (ref arg, ref ty_2))) in sig.iter().zip( - args.iter() - ).enumerate() { - if ty_1 != ty_2 { - bail!( - "type of argument {} ({}) in predicate {}'s definition ({}) \ - match that of the input file ({})", - count, arg, conf.emph(pred), ty_2, ty_1 - ) - } + /// Checks the signature of the predicates match the declarations of an input + /// `smt2` file. Also checks that all predicates are defined *once*. + pub fn check_consistency(&mut self, input: &Input) -> Res<()> { + log_info!{ "checking predicate signature consistency..." } + let mut map = HashMap::with_capacity(self.pred_defs.len()); + log! { @4 "checking for duplicate definitions" } + for &PredDef { + ref pred, ref args, .. + } in &self.pred_defs + { + let prev = map.insert(pred.clone(), args.clone()); + if prev.is_some() { + bail!( + "predicate {} is defined twice in hoice's output", + conf.emph(pred) + ) + } } - - } else { - warn!( - "predicate {} is not defined in hoice's output", conf.emph(pred) - ) ; - let mut args = vec![] ; - - for (cnt, ty) in sig.iter().enumerate() { - args.push( (format!("v_{}", cnt), ty.clone()) ) + log! { @4 "checking signatures" } + for &PredDec { ref pred, ref sig } in &input.pred_decs { + if let Some(args) = map.get(pred) { + if sig.len() != args.len() { + bail!( + "arguments of predicate {}'s definition \ + does not match its signature", + conf.emph(pred) + ) + } + + let iter = sig.iter().zip(args.iter()).enumerate(); + for (count, (ty_1, &(ref arg, ref ty_2))) in iter { + if ty_1 != ty_2 { + bail!( + "type of argument {} ({}) in predicate {}'s definition ({}) \ + match that of the input file ({})", + count, + arg, + conf.emph(pred), + ty_2, + ty_1 + ) + } + } + } else { + warn!( + "predicate {} is not defined in hoice's output", + conf.emph(pred) + ); + let mut args = vec![]; + + for (cnt, ty) in sig.iter().enumerate() { + args.push((format!("v_{}", cnt), ty.clone())) + } + self.pred_defs.push(PredDef { + pred: pred.clone(), + args, + body: None, + }) + } } - self.pred_defs.push( - PredDef { pred: pred.clone(), args, body: None } - ) - } + log! { @4 "done" } + Ok(()) } - log! { @4 "done" } - Ok(()) - } } - /// Aggregates the input and output data. pub struct Data { - /// Input data. - pub input: Input, - /// Output data. - pub output: Output, + /// Input data. + pub input: Input, + /// Output data. + pub output: Output, } impl Data { - /// Direct contructor. - pub fn new(input: Input, mut output: Output) -> Res { - output.check_consistency(& input) ? ; - Ok( Data { input, output } ) - } - /// Reads two files for input and output data. - pub fn of_files(input_file: & str, output_file: & str) -> Res { - let input = Input::of_file(input_file) ? ; - let output = Output::of_file(output_file) ? ; - Self::new(input, output) - } - - /// Checks a single clause. - pub fn check_clause( - & self, solver: & mut Solver, - Clause { args, body }: & Clause, count: usize, - ) -> Res< Option > { - solver.reset() ? ; - - for unknown in & self.input.unknown { - use std::io::Write ; - writeln!(solver, "{}", unknown) ? + /// Direct contructor. + pub fn new(input: Input, mut output: Output) -> Res { + output.check_consistency(&input)?; + Ok(Data { input, output }) } - - // Define all functions. - for & FunDef { - ref name, ref args, ref typ, ref body - } in & self.input.fun_defs { - solver.define_fun( - name, args, typ, body - ) ? + /// Reads two files for input and output data. + pub fn of_files(input_file: &str, output_file: &str) -> Res { + let input = Input::of_file(input_file)?; + let output = Output::of_file(output_file)?; + Self::new(input, output) } - // Define all predicates. - for & PredDef { - ref pred, ref args, ref body - } in & self.output.pred_defs { - if let Some(body) = body.as_ref() { - solver.define_fun( - pred, args, & "Bool".to_string(), body - ) ? - } else { - solver.declare_fun( - pred, - & args.iter().map( - |& (_, ref typ)| typ.clone() - ).collect::>(), - & "Bool".to_string() - ) ? - } - } + /// Checks a single clause. + pub fn check_clause( + &self, + solver: &mut Solver, + Clause { args, body }: &Clause, + count: usize, + ) -> Res> { + solver.reset()?; + + for unknown in &self.input.unknown { + use std::io::Write; + writeln!(solver, "{}", unknown)? + } - // Declare arguments. - for & (ref ident, ref typ) in args { - solver.declare_const(ident, typ) ? - } + // Define all functions. + for &FunDef { + ref name, + ref args, + ref typ, + ref body, + } in &self.input.fun_defs + { + solver.define_fun(name, args, typ, body)? + } - solver.assert( & format!("(not {})", body) ) ? ; - - let res = solver.check_sat_or_unk() ? ; - - if let Some(true) = res { - let exprs: Vec<_> = args.iter().map( - |& (ref id, _)| id.clone() - ).collect() ; - let model = solver.get_values(& exprs) ? ; - println!() ; - println!("({} \"", conf.bad("error")) ; - println!(" clause {} is falsifiable with {{", count) ; - // print!( " ") ; - // for & (ref id, ref ty) in args { - // print!(" ({} {})", id, ty) - // } - // println!() ; - // println!(" (=>") ; - // println!(" (and") ; - // for lhs in lhs { - // println!(" {}", lhs) - // } - // println!(" ) {}", rhs) ; - // println!(" )") ; - // println!(" is falsifiable with {{") ; - for (ident, value) in model { - println!(" {}: {},", ident, value) - } - println!(" }}") ; - println!("\")") ; - println!() ; - Ok( Some(false) ) - } else if let Some(false) = res { - log_info!("clause {} is fine", count) ; - Ok( Some(true) ) - } else { - log_info!("got unknown on clause {}, assuming it's okay", count) ; - Ok(None) - } - } - - /// Checks the output data works with the input data using an SMT solver. - pub fn check(& self, solver: & mut Solver) -> Res<()> { - - let mut okay = true ; - let mut err = false ; - - // Check all clauses one by one. - for (count, clause) in self.input.clauses.iter().enumerate() { - match self.check_clause(solver, clause, count) { - Ok( Some(ok) ) => okay = okay && ok, - Ok(None) => (), - Err(e) => { - err = true ; - let e = e.chain_err( - || format!( - "while checking clause {}", count - ) - ) ; - print_err(& e) - }, - } - } + // Define all predicates. + for &PredDef { + ref pred, + ref args, + ref body, + } in &self.output.pred_defs + { + if let Some(body) = body.as_ref() { + solver.define_fun(pred, args, &"Bool".to_string(), body)? + } else { + solver.declare_fun( + pred, + &args + .iter() + .map(|&(_, ref typ)| typ.clone()) + .collect::>(), + &"Bool".to_string(), + )? + } + } + + // Declare arguments. + for &(ref ident, ref typ) in args { + solver.declare_const(ident, typ)? + } - if ! okay { - bail!( - "predicates do not verify all the clauses of the input file" - ) - } else if err { - bail!( - "at least one error while checking the clauses" - ) - } else { - Ok(()) + solver.assert(&format!("(not {})", body))?; + + let res = solver.check_sat_or_unk()?; + + if let Some(true) = res { + let exprs: Vec<_> = args.iter().map(|&(ref id, _)| id.clone()).collect(); + let model = solver.get_values(&exprs)?; + println!(); + println!("({} \"", conf.bad("error")); + println!(" clause {} is falsifiable with {{", count); + // print!( " ") ; + // for & (ref id, ref ty) in args { + // print!(" ({} {})", id, ty) + // } + // println!() ; + // println!(" (=>") ; + // println!(" (and") ; + // for lhs in lhs { + // println!(" {}", lhs) + // } + // println!(" ) {}", rhs) ; + // println!(" )") ; + // println!(" is falsifiable with {{") ; + for (ident, value) in model { + println!(" {}: {},", ident, value) + } + println!(" }}"); + println!("\")"); + println!(); + Ok(Some(false)) + } else if let Some(false) = res { + log_info!("clause {} is fine", count); + Ok(Some(true)) + } else { + log_info!("got unknown on clause {}, assuming it's okay", count); + Ok(None) + } } - } -} + /// Checks the output data works with the input data using an SMT solver. + pub fn check(&self, solver: &mut Solver) -> Res<()> { + let mut okay = true; + let mut err = false; + + // Check all clauses one by one. + for (count, clause) in self.input.clauses.iter().enumerate() { + match self.check_clause(solver, clause, count) { + Ok(Some(ok)) => okay = okay && ok, + Ok(None) => (), + Err(e) => { + err = true; + let e = e.chain_err(|| format!("while checking clause {}", count)); + print_err(&e) + } + } + } + if !okay { + bail!("predicates do not verify all the clauses of the input file") + } else if err { + bail!("at least one error while checking the clauses") + } else { + Ok(()) + } + } +} /// Checks a `hoice` run from two files. -pub fn do_it(input_file: & str, output_file: & str) -> Res<()> { - let data = Data::of_files(input_file, output_file) ? ; +pub fn do_it(input_file: &str, output_file: &str) -> Res<()> { + let data = Data::of_files(input_file, output_file)?; - log! { @4 "spawning solver" } + log! { @4 "spawning solver" } - let mut solver = conf.solver.spawn( - "check", Parser, & ::instance::Instance::new() - ) ? ; + let mut solver = conf + .solver + .spawn("check", Parser, &::instance::Instance::new())?; - let res = data.check(& mut solver) ; - if res.is_ok() { - println!("(safe)") - } + let res = data.check(&mut solver); + if res.is_ok() { + println!("(safe)") + } - let end_res = solver.kill().chain_err( - || "While killing solver" - ) ; + let end_res = solver.kill().chain_err(|| "While killing solver"); - res.and_then(|_| end_res) + res.and_then(|_| end_res) } - - - /// Checks a `hoice` run, script from a file, model from a string. /// /// This is currently only used for testing purposes. -pub fn do_it_from_str>( - input_file: P, model: & str -) -> Res<()> { - println!("model:") ; - println!("{}", model) ; - let data = Data::new( - Input::of_file(input_file).chain_err( - || "while loading input file" - ) ?, - Output::of_str(model).chain_err( - || "while loading model" - ) ? - ) ? ; - - let mut solver = conf.solver.spawn( - "check", Parser, & ::instance::Instance::new() - ) ? ; - let res = data.check(& mut solver) ; - let end_res = solver.kill().chain_err( - || "While killing solver" - ) ; - res.and_then(|_| end_res) +pub fn do_it_from_str>(input_file: P, model: &str) -> Res<()> { + println!("model:"); + println!("{}", model); + let data = Data::new( + Input::of_file(input_file).chain_err(|| "while loading input file")?, + Output::of_str(model).chain_err(|| "while loading model")?, + )?; + + let mut solver = conf + .solver + .spawn("check", Parser, &::instance::Instance::new())?; + let res = data.check(&mut solver); + let end_res = solver.kill().chain_err(|| "While killing solver"); + res.and_then(|_| end_res) } - - - - - - - - - mod smt { - use rsmt2::parse::{ IdentParser, ValueParser, ExprParser } ; - use rsmt2::SmtRes ; - - use check::{ Ident, Value, Term } ; + use rsmt2::parse::{ExprParser, IdentParser, ValueParser}; + use rsmt2::SmtRes; + use check::{Ident, Term, Value}; -/// Parser for the output of the SMT solver. -/// -/// Parses idents and values as strings. -#[derive(Clone, Copy)] -pub struct Parser ; - + /// Parser for the output of the SMT solver. + /// + /// Parses idents and values as strings. + #[derive(Clone, Copy)] + pub struct Parser; - impl<'a> IdentParser for Parser { - fn parse_ident(self, input: & 'a str) -> SmtRes< Ident > { - Ok( input.into() ) - } - fn parse_type(self, _: & 'a str) -> SmtRes<()> { - Ok(()) + impl<'a> IdentParser for Parser { + fn parse_ident(self, input: &'a str) -> SmtRes { + Ok(input.into()) + } + fn parse_type(self, _: &'a str) -> SmtRes<()> { + Ok(()) + } } - } - impl<'a> ExprParser for Parser { - fn parse_expr(self, input: & 'a str, _: ()) -> SmtRes { - Ok( input.into() ) + impl<'a> ExprParser for Parser { + fn parse_expr(self, input: &'a str, _: ()) -> SmtRes { + Ok(input.into()) + } } - } - impl<'a> ValueParser for Parser { - fn parse_value(self, input: & 'a str) -> SmtRes { - Ok( input.into() ) + impl<'a> ValueParser for Parser { + fn parse_value(self, input: &'a str) -> SmtRes { + Ok(input.into()) + } } - } -} \ No newline at end of file +} diff --git a/src/check/parse.rs b/src/check/parse.rs index fade07c7..4835d147 100644 --- a/src/check/parse.rs +++ b/src/check/parse.rs @@ -1,441 +1,448 @@ //! Parsers used by the checker. -use check::* ; - - - +use check::*; /// Parser. #[derive(Clone)] pub struct InParser<'a> { - /// Unknown stuff. Datatype declarations, recursive function definitions and - /// such. - pub unknown: Vec, - /// Predicate definitions. - pub pred_defs: Vec, - /// Predicate declarations. - pub pred_decs: Vec, - /// Function definitions. - pub fun_defs: Vec, - /// Clauses. - pub clauses: Vec, - /// Characters. - chars: ::std::str::Chars<'a>, - /// Buffer storing characters pushed back. - buf: Vec, + /// Unknown stuff. Datatype declarations, recursive function definitions and + /// such. + pub unknown: Vec, + /// Predicate definitions. + pub pred_defs: Vec, + /// Predicate declarations. + pub pred_decs: Vec, + /// Function definitions. + pub fun_defs: Vec, + /// Clauses. + pub clauses: Vec, + /// Characters. + chars: ::std::str::Chars<'a>, + /// Buffer storing characters pushed back. + buf: Vec, } impl<'a> InParser<'a> { - /// Constructor. - pub fn new(s: & 'a str) -> Self { - InParser { - unknown: vec![], - pred_defs: vec![], pred_decs: vec![], fun_defs: vec![], - clauses: vec![], - chars: s.chars(), buf: vec![] - } - } - - /// Swaps input characters. - pub fn swap(& mut self, s: & 'a str) { - self.chars = s.chars() ; - assert!( self.buf.is_empty() ) - } - - /// True if there is a next character. - fn has_next(& mut self) -> bool { - if ! self.buf.is_empty() { - true - } else if let Some(c) = self.next() { - self.buf.push(c) ; - true - } else { - false - } - } - - /// Next character. - fn next(& mut self) -> Option { - if let Some(c) = self.buf.pop() { - Some(c) - } else { - self.chars.next() - } - } - - /// Pushes back a character. - fn txen(& mut self, c: char) { - self.buf.push(c) - } - - /// Backtracks some characters. - fn backtrack(& mut self, mut mem: Vec) { - use std::iter::Extend ; - mem.reverse() ; - self.buf.extend(mem) - } - - /// Parses a tag or fails. - fn tag(& mut self, tag: & str) -> Res<()> { - if ! self.tag_opt(tag) { - bail!("expected tag `{}`", conf.emph(tag)) - } else { - Ok(()) + /// Constructor. + pub fn new(s: &'a str) -> Self { + InParser { + unknown: vec![], + pred_defs: vec![], + pred_decs: vec![], + fun_defs: vec![], + clauses: vec![], + chars: s.chars(), + buf: vec![], + } } - } - /// Tries to parse a tag. - fn tag_opt(& mut self, tag: & str) -> bool { - let mut mem = vec![] ; - for c in tag.chars() { - if let Some(next) = self.next() { - mem.push(next) ; - if c != next { - self.backtrack(mem) ; - return false - } - } else { - self.backtrack(mem) ; - return false - } + + /// Swaps input characters. + pub fn swap(&mut self, s: &'a str) { + self.chars = s.chars(); + assert!(self.buf.is_empty()) } - true - } - - /// Parses a character or fails. - fn char(& mut self, c: char) -> Res<()> { - if ! self.char_opt(c) { - bail!( - "expected character `{}`, got `{}`", - conf.emph( & c.to_string() ), - conf.sad( - if let Some(c) = self.next() { - format!("{}", c) - } else { - "".into() - } - ) - ) - } else { - Ok(()) + + /// True if there is a next character. + fn has_next(&mut self) -> bool { + if !self.buf.is_empty() { + true + } else if let Some(c) = self.next() { + self.buf.push(c); + true + } else { + false + } } - } - /// Tries to parse a character. - fn char_opt(& mut self, c: char) -> bool { - if let Some(next) = self.next() { - if next == c { - true - } else { - self.txen(next) ; - false - } - } else { - false + + /// Next character. + fn next(&mut self) -> Option { + if let Some(c) = self.buf.pop() { + Some(c) + } else { + self.chars.next() + } } - } - - /// Parses everything it can until (and excluding) some character. - fn not_char(& mut self, c: char) -> String { - let mut s = String::new() ; - while let Some(next) = self.next() { - if next == c { self.txen(next) ; break } else { - s.push(next) - } + + /// Pushes back a character. + fn txen(&mut self, c: char) { + self.buf.push(c) } - s - } - - /// Parses an sexpression. - fn sexpr(& mut self) -> Res { - if self.tag_opt("true") { - return Ok("true".into()) - } else if self.tag_opt("false") { - return Ok("false".into()) - } else if let Some(id) = self.ident_opt() ? { - return Ok(id) + + /// Backtracks some characters. + fn backtrack(&mut self, mut mem: Vec) { + use std::iter::Extend; + mem.reverse(); + self.buf.extend(mem) } - let mut s = String::new() ; - let mut cnt = 0 ; - while let Some(next) = self.next() { - s.push(next) ; - if next == '(' { - cnt += 1 ; - } else if next == ')' { - cnt -= 1 ; - if cnt == 0 { break } - } + + /// Parses a tag or fails. + fn tag(&mut self, tag: &str) -> Res<()> { + if !self.tag_opt(tag) { + bail!("expected tag `{}`", conf.emph(tag)) + } else { + Ok(()) + } } - if cnt != 0 { - bail!("found eof while parsing sexpr") + /// Tries to parse a tag. + fn tag_opt(&mut self, tag: &str) -> bool { + let mut mem = vec![]; + for c in tag.chars() { + if let Some(next) = self.next() { + mem.push(next); + if c != next { + self.backtrack(mem); + return false; + } + } else { + self.backtrack(mem); + return false; + } + } + true } - Ok(s) - } - - /// Reads whitespaces and comments. - fn ws_cmt(& mut self) { - 'ws: while let Some(next) = self.next() { - if ! next.is_whitespace() { - if next == ';' { - 'cmt: while let Some(next) = self.next() { - if next == '\n' { break 'cmt } - } + + /// Parses a character or fails. + fn char(&mut self, c: char) -> Res<()> { + if !self.char_opt(c) { + bail!( + "expected character `{}`, got `{}`", + conf.emph(&c.to_string()), + conf.sad(if let Some(c) = self.next() { + format!("{}", c) + } else { + "".into() + }) + ) } else { - self.txen(next) ; - break 'ws + Ok(()) } - } - } - } - - /// Unquoted identifier char. - fn ident_char(c: char) -> bool { - if c.is_alphanumeric() { true } else { - match c { - '~' | '!' | '@' | '$' | '%' | '^' | '&' | '*' | ':' | - '_' | '-' | '+' | '=' | '<' | '>' | '.' | '?' | '/' => true, - _ => false, - } } - } - - /// Identifier or fails. - fn ident(& mut self) -> Res { - if let Some(id) = self.ident_opt() ? { - Ok(id) - } else { - bail!("expected identifier") + /// Tries to parse a character. + fn char_opt(&mut self, c: char) -> bool { + if let Some(next) = self.next() { + if next == c { + true + } else { + self.txen(next); + false + } + } else { + false + } } - } - /// Identifier. - fn ident_opt(& mut self) -> Res< Option > { - if let Some(next) = self.next() { - let id = if next == '|' { - let id = self.not_char('|') ; - self.char('|') ? ; - id - } else if Self::ident_char(next) && ! next.is_numeric() { - let mut id = String::new() ; - id.push(next) ; + + /// Parses everything it can until (and excluding) some character. + fn not_char(&mut self, c: char) -> String { + let mut s = String::new(); while let Some(next) = self.next() { - if Self::ident_char(next) { id.push(next) } else { - self.txen(next) ; - break - } - } - id - } else { - self.txen(next) ; - return Ok(None) - } ; - Ok( Some( format!("|{}|", id) ) ) - } else { - Ok(None) + if next == c { + self.txen(next); + break; + } else { + s.push(next) + } + } + s } - } - /// Set-logic. - fn set_logic(& mut self) -> Res { - if ! self.tag_opt("set-logic") { - return Ok(false) - } - self.ws_cmt() ; - self.tag("HORN") ? ; - Ok(true) - } - - /// Set-info. - fn set_info(& mut self) -> Res { - if ! self.tag_opt("set-info") { - return Ok(false) - } - self.ws_cmt() ; - self.char(':') ? ; - self.ident() ? ; - self.ws_cmt() ; - if self.char_opt('|') { - self.not_char('|') ; - self.char('|') ? - } else if self.char_opt('"') { - let _blah = self.not_char('"') ; - self.char('"') ? - } else { - let _blah = self.not_char(')') ; + /// Parses an sexpression. + fn sexpr(&mut self) -> Res { + if self.tag_opt("true") { + return Ok("true".into()); + } else if self.tag_opt("false") { + return Ok("false".into()); + } else if let Some(id) = self.ident_opt()? { + return Ok(id); + } + let mut s = String::new(); + let mut cnt = 0; + while let Some(next) = self.next() { + s.push(next); + if next == '(' { + cnt += 1; + } else if next == ')' { + cnt -= 1; + if cnt == 0 { + break; + } + } + } + if cnt != 0 { + bail!("found eof while parsing sexpr") + } + Ok(s) + } + + /// Reads whitespaces and comments. + fn ws_cmt(&mut self) { + 'ws: while let Some(next) = self.next() { + if !next.is_whitespace() { + if next == ';' { + 'cmt: while let Some(next) = self.next() { + if next == '\n' { + break 'cmt; + } + } + } else { + self.txen(next); + break 'ws; + } + } + } } - Ok(true) - } - /// Set-option. - fn set_option(& mut self) -> Res< Option<(String, String)> > { - if ! self.tag_opt("set-option") { - return Ok(None) + /// Unquoted identifier char. + fn ident_char(c: char) -> bool { + if c.is_alphanumeric() { + true + } else { + match c { + '~' | '!' | '@' | '$' | '%' | '^' | '&' | '*' | ':' | '_' | '-' | '+' | '=' + | '<' | '>' | '.' | '?' | '/' => true, + _ => false, + } + } } - self.ws_cmt() ; - self.char(':') ? ; - let key = self.ident() ? ; - self.ws_cmt() ; - let val = if self.char_opt('|') { - let res = self.not_char('|') ; - self.char('|') ? ; - res - } else if self.char_opt('"') { - let res = self.not_char('"') ; - self.char('"') ? ; - res - } else { - let res = self.not_char(')') ; - res.trim().into() - } ; - Ok(Some((key, val))) - } - - /// Declare-fun. - fn declare_fun(& mut self) -> Res { - if ! self.tag_opt("declare-fun") { - return Ok(false) + + /// Identifier or fails. + fn ident(&mut self) -> Res { + if let Some(id) = self.ident_opt()? { + Ok(id) + } else { + bail!("expected identifier") + } } - self.ws_cmt() ; - let pred = self.ident() ? ; - self.ws_cmt() ; - self.char('(') ? ; - self.ws_cmt() ; - let mut sig = vec![] ; - while ! self.char_opt(')') { - sig.push( self.sexpr() ? ) ; - self.ws_cmt() + /// Identifier. + fn ident_opt(&mut self) -> Res> { + if let Some(next) = self.next() { + let id = if next == '|' { + let id = self.not_char('|'); + self.char('|')?; + id + } else if Self::ident_char(next) && !next.is_numeric() { + let mut id = String::new(); + id.push(next); + while let Some(next) = self.next() { + if Self::ident_char(next) { + id.push(next) + } else { + self.txen(next); + break; + } + } + id + } else { + self.txen(next); + return Ok(None); + }; + Ok(Some(format!("|{}|", id))) + } else { + Ok(None) + } } - self.ws_cmt() ; - self.tag("Bool") ? ; - - self.pred_decs.push( - PredDec { pred, sig } - ) ; - - Ok(true) - } - - /// Arguments. - fn args(& mut self) -> Res { - self.char('(') ? ; - self.ws_cmt() ; - let mut args = vec![] ; - while self.char_opt('(') { - let id = self.ident() ? ; - self.ws_cmt() ; - let ty = self.sexpr() ? ; - self.ws_cmt() ; - self.char(')') ? ; - self.ws_cmt() ; - args.push( (id, ty) ) + + /// Set-logic. + fn set_logic(&mut self) -> Res { + if !self.tag_opt("set-logic") { + return Ok(false); + } + self.ws_cmt(); + self.tag("HORN")?; + Ok(true) } - self.char(')') ? ; - Ok(args) - } - - /// Assert. - fn assert(& mut self) -> Res { - if ! self.tag_opt("assert") { - return Ok(false) + + /// Set-info. + fn set_info(&mut self) -> Res { + if !self.tag_opt("set-info") { + return Ok(false); + } + self.ws_cmt(); + self.char(':')?; + self.ident()?; + self.ws_cmt(); + if self.char_opt('|') { + self.not_char('|'); + self.char('|')? + } else if self.char_opt('"') { + let _blah = self.not_char('"'); + self.char('"')? + } else { + let _blah = self.not_char(')'); + } + Ok(true) } - self.ws_cmt() ; - self.char('(') ? ; - - let negated = if self.tag_opt("not") { - self.ws_cmt() ; - self.char('(') ? ; - true - } else { - false - } ; - - let mut cnt = 1 ; - - let (args, body) = if self.tag_opt("forall") { - if negated { - bail!("negated forall in assertion") - } - self.ws_cmt() ; - use std::iter::Extend ; - let mut args = vec![] ; - loop { - let these_args = self.args().chain_err( - || "while parsing arguments" - ) ? ; - args.extend(these_args) ; - self.ws_cmt() ; - if self.char_opt('(') { - self.ws_cmt() ; - if self.tag_opt("forall") { - cnt += 1 ; - self.ws_cmt() - } else { - self.txen('(') ; - break - } + + /// Set-option. + fn set_option(&mut self) -> Res> { + if !self.tag_opt("set-option") { + return Ok(None); + } + self.ws_cmt(); + self.char(':')?; + let key = self.ident()?; + self.ws_cmt(); + let val = if self.char_opt('|') { + let res = self.not_char('|'); + self.char('|')?; + res + } else if self.char_opt('"') { + let res = self.not_char('"'); + self.char('"')?; + res } else { - break - } - } - self.ws_cmt() ; - let body = self.sexpr().chain_err(|| "while parsing body") ? ; - (args, body) - } else if self.tag_opt("exists") { - self.ws_cmt() ; - let args = self.args().chain_err(|| "while parsing arguments") ? ; - self.ws_cmt() ; - let body = self.sexpr().chain_err(|| "while parsing body") ? ; - (args, body) - } else { - bail!("expected forall or exists") - } ; - self.ws_cmt() ; - - let body = if negated { format!("(not {})", body) } else { body } ; - - while cnt > 0 { - self.char(')').chain_err(|| "closing qualifier") ? ; - self.ws_cmt() ; - cnt -= 1 + let res = self.not_char(')'); + res.trim().into() + }; + Ok(Some((key, val))) } - if negated { - self.char(')').chain_err(|| "closing negation") ? ; + + /// Declare-fun. + fn declare_fun(&mut self) -> Res { + if !self.tag_opt("declare-fun") { + return Ok(false); + } + self.ws_cmt(); + let pred = self.ident()?; + self.ws_cmt(); + self.char('(')?; + self.ws_cmt(); + let mut sig = vec![]; + while !self.char_opt(')') { + sig.push(self.sexpr()?); + self.ws_cmt() + } + self.ws_cmt(); + self.tag("Bool")?; + + self.pred_decs.push(PredDec { pred, sig }); + + Ok(true) + } + + /// Arguments. + fn args(&mut self) -> Res { + self.char('(')?; + self.ws_cmt(); + let mut args = vec![]; + while self.char_opt('(') { + let id = self.ident()?; + self.ws_cmt(); + let ty = self.sexpr()?; + self.ws_cmt(); + self.char(')')?; + self.ws_cmt(); + args.push((id, ty)) + } + self.char(')')?; + Ok(args) } - self.clauses.push( - Clause { args, body } - ) ; - - Ok(true) - } - - /// Parses anything. - fn parse_unknown(& mut self) -> Res<()> { - let mut s = "(".to_string() ; - - let mut count = 1 ; - - while let Some(char) = self.next() { - s.push(char) ; - match char { - ')' => count -= 1, - '(' => count += 1, - _ => (), - } - if count == 0 { - self.backtrack( vec![')'] ) ; - self.unknown.push(s) ; - return Ok(()) - } + /// Assert. + fn assert(&mut self) -> Res { + if !self.tag_opt("assert") { + return Ok(false); + } + self.ws_cmt(); + self.char('(')?; + + let negated = if self.tag_opt("not") { + self.ws_cmt(); + self.char('(')?; + true + } else { + false + }; + + let mut cnt = 1; + + let (args, body) = if self.tag_opt("forall") { + if negated { + bail!("negated forall in assertion") + } + self.ws_cmt(); + use std::iter::Extend; + let mut args = vec![]; + loop { + let these_args = self.args().chain_err(|| "while parsing arguments")?; + args.extend(these_args); + self.ws_cmt(); + if self.char_opt('(') { + self.ws_cmt(); + if self.tag_opt("forall") { + cnt += 1; + self.ws_cmt() + } else { + self.txen('('); + break; + } + } else { + break; + } + } + self.ws_cmt(); + let body = self.sexpr().chain_err(|| "while parsing body")?; + (args, body) + } else if self.tag_opt("exists") { + self.ws_cmt(); + let args = self.args().chain_err(|| "while parsing arguments")?; + self.ws_cmt(); + let body = self.sexpr().chain_err(|| "while parsing body")?; + (args, body) + } else { + bail!("expected forall or exists") + }; + self.ws_cmt(); + + let body = if negated { + format!("(not {})", body) + } else { + body + }; + + while cnt > 0 { + self.char(')').chain_err(|| "closing qualifier")?; + self.ws_cmt(); + cnt -= 1 + } + if negated { + self.char(')').chain_err(|| "closing negation")?; + } + + self.clauses.push(Clause { args, body }); + + Ok(true) } - bail!("expected closing paren, found ") - } + /// Parses anything. + fn parse_unknown(&mut self) -> Res<()> { + let mut s = "(".to_string(); + + let mut count = 1; + + while let Some(char) = self.next() { + s.push(char); + match char { + ')' => count -= 1, + '(' => count += 1, + _ => (), + } + if count == 0 { + self.backtrack(vec![')']); + self.unknown.push(s); + return Ok(()); + } + } + + bail!("expected closing paren, found ") + } - /// Parses an `smt2` file. - pub fn parse_input(mut self) -> Res { - self.ws_cmt() ; + /// Parses an `smt2` file. + pub fn parse_input(mut self) -> Res { + self.ws_cmt(); - while self.char_opt('(') { - self.ws_cmt() ; + while self.char_opt('(') { + self.ws_cmt(); - if self.set_logic() ? + if self.set_logic() ? || self.set_info() ? || self.set_option()?.is_some() || self.declare_fun() ? @@ -443,145 +450,135 @@ impl<'a> InParser<'a> { || self.assert() ? || self.tag_opt("check-sat") || self.tag_opt("get-model") - || self.tag_opt("exit") { - () - } else { - self.parse_unknown() ? - // print!("> `") ; - // while let Some(next) = self.next() { - // if next != '\n' { - // print!("{}", next) - // } else { - // break - // } - // } - // println!("`") ; - // bail!("expected item") - } - - self.ws_cmt() ; - self.char(')').chain_err(|| "closing item") ? ; - self.ws_cmt() - } + || self.tag_opt("exit") + { + () + } else { + self.parse_unknown()? + // print!("> `") ; + // while let Some(next) = self.next() { + // if next != '\n' { + // print!("{}", next) + // } else { + // break + // } + // } + // println!("`") ; + // bail!("expected item") + } + + self.ws_cmt(); + self.char(')').chain_err(|| "closing item")?; + self.ws_cmt() + } - if self.has_next() { - print!("> `") ; - while let Some(next) = self.next() { - if next != '\n' { - print!("{}", next) - } else { - break + if self.has_next() { + print!("> `"); + while let Some(next) = self.next() { + if next != '\n' { + print!("{}", next) + } else { + break; + } + } + println!("`"); + bail!("could not parse the whole input file") } - } - println!("`") ; - bail!("could not parse the whole input file") - } - Ok( - Input { - unknown: self.unknown, - pred_decs: self.pred_decs, - fun_defs: self.fun_defs, - clauses: self.clauses, - } - ) - } - - - /// Define-fun. - fn define_pred(& mut self) -> Res { - if ! self.tag_opt("define-fun") { - return Ok(false) - } - self.ws_cmt() ; - let pred = self.ident().chain_err( - || "while parsing predicate identifier" - ) ? ; - self.ws_cmt() ; - let args = self.args().chain_err( - || "while parsing arguments" - ) ? ; - self.ws_cmt() ; - self.tag("Bool") ? ; - self.ws_cmt() ; - let body = Some( - self.sexpr().chain_err( - || "while parsing body" - ) ? - ) ; - self.ws_cmt() ; - self.pred_defs.push( - PredDef { pred, args, body } - ) ; - - Ok(true) - } - - - /// Parses an `smt2` file. - pub fn parse_output(mut self) -> Res { - if conf.check_eld { - self.ws_cmt() ; - self.tag_opt("Warning: ignoring get-model") ; - } - self.ws_cmt() ; - if self.tag_opt("sat") { - self.ws_cmt() ; + Ok(Input { + unknown: self.unknown, + pred_decs: self.pred_decs, + fun_defs: self.fun_defs, + clauses: self.clauses, + }) } - let error = "expected `(model (define-fun ...))`" ; + /// Define-fun. + fn define_pred(&mut self) -> Res { + if !self.tag_opt("define-fun") { + return Ok(false); + } + self.ws_cmt(); + let pred = self + .ident() + .chain_err(|| "while parsing predicate identifier")?; + self.ws_cmt(); + let args = self.args().chain_err(|| "while parsing arguments")?; + self.ws_cmt(); + self.tag("Bool")?; + self.ws_cmt(); + let body = Some(self.sexpr().chain_err(|| "while parsing body")?); + self.ws_cmt(); + self.pred_defs.push(PredDef { pred, args, body }); + + Ok(true) + } + + /// Parses an `smt2` file. + pub fn parse_output(mut self) -> Res { + if conf.check_eld { + self.ws_cmt(); + self.tag_opt("Warning: ignoring get-model"); + } + self.ws_cmt(); + if self.tag_opt("sat") { + self.ws_cmt(); + } - if ! conf.check_eld { - self.char('(').chain_err(|| error) ? ; - self.ws_cmt() ; - self.tag("model").chain_err(|| error) ? ; - self.ws_cmt() - } + let error = "expected `(model (define-fun ...))`"; - while self.char_opt('(') { - self.ws_cmt() ; + if !conf.check_eld { + self.char('(').chain_err(|| error)?; + self.ws_cmt(); + self.tag("model").chain_err(|| error)?; + self.ws_cmt() + } - if self.define_pred().chain_err( - || "while parsing a define-fun" - ) ? { - () - } else { - print!("> `") ; - while let Some(next) = self.next() { - if next != '\n' { - print!("{}", next) - } else { - break - } - } - println!("`") ; - bail!("expected define-fun") - } - - self.ws_cmt() ; - self.char(')').chain_err(|| "closing define-fun") ? ; - self.ws_cmt() - } - if ! conf.check_eld { - self.char(')').chain_err(|| "closing model") ? ; - self.ws_cmt() ; - } + while self.char_opt('(') { + self.ws_cmt(); + + if self + .define_pred() + .chain_err(|| "while parsing a define-fun")? + { + () + } else { + print!("> `"); + while let Some(next) = self.next() { + if next != '\n' { + print!("{}", next) + } else { + break; + } + } + println!("`"); + bail!("expected define-fun") + } + + self.ws_cmt(); + self.char(')').chain_err(|| "closing define-fun")?; + self.ws_cmt() + } + if !conf.check_eld { + self.char(')').chain_err(|| "closing model")?; + self.ws_cmt(); + } - if self.has_next() { - print!("> `") ; - while let Some(next) = self.next() { - if next != '\n' { - print!("{}", next) - } else { - break + if self.has_next() { + print!("> `"); + while let Some(next) = self.next() { + if next != '\n' { + print!("{}", next) + } else { + break; + } + } + println!("`"); + bail!("could not parse the whole output file") } - } - println!("`") ; - bail!("could not parse the whole output file") - } - Ok( - Output { pred_defs: self.pred_defs } - ) - } -} \ No newline at end of file + Ok(Output { + pred_defs: self.pred_defs, + }) + } +} diff --git a/src/common/config.rs b/src/common/config.rs index b1b16aae..8b491cdc 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -1,1632 +1,1574 @@ //! Hoice's global configuration. -use std::path::PathBuf ; +use std::path::PathBuf; -use rsmt2::SmtConf as SolverConf ; +use rsmt2::SmtConf as SolverConf; -use clap::Arg ; -use ansi::{ Colour, Style } ; +use ansi::{Colour, Style}; +use clap::Arg; -use errors::* ; -use common::mk_dir ; -use instance::Instance ; +use common::mk_dir; +use errors::*; +use instance::Instance; /// Clap `App` with static lifetimes. -pub type App = ::clap::App<'static, 'static> ; +pub type App = ::clap::App<'static, 'static>; /// Clap `ArgMatches` with static lifetime. -pub type Matches = ::clap::ArgMatches<'static> ; - - - +pub type Matches = ::clap::ArgMatches<'static>; /// Functions all sub-configurations must have. pub trait SubConf { - /// True if the options of the subconf need the output directory. - fn need_out_dir(& self) -> bool ; + /// True if the options of the subconf need the output directory. + fn need_out_dir(&self) -> bool; } - - - /// Instance and factory configuration. /// /// Currently, these options are static. They cannot be changed through clap. pub struct InstanceConf { - /// Initial capacity of the term factory. - pub term_capa: usize, - /// Initial capacity of the clause vector. - pub clause_capa: usize, - /// Initial capacity of the predicate vector. - pub pred_capa: usize, + /// Initial capacity of the term factory. + pub term_capa: usize, + /// Initial capacity of the clause vector. + pub clause_capa: usize, + /// Initial capacity of the predicate vector. + pub pred_capa: usize, } impl SubConf for InstanceConf { - fn need_out_dir(& self) -> bool { false } + fn need_out_dir(&self) -> bool { + false + } } impl InstanceConf { - /// Adds clap options to a clap App. - pub fn add_args(app: App, _: usize) -> App { - app - } - - /// Creates itself from some matches. - pub fn new(_: & Matches) -> Self { - InstanceConf { - term_capa: 3_000, - clause_capa: 42, - pred_capa: 42, + /// Adds clap options to a clap App. + pub fn add_args(app: App, _: usize) -> App { + app } - } -} - - - - - + /// Creates itself from some matches. + pub fn new(_: &Matches) -> Self { + InstanceConf { + term_capa: 3_000, + clause_capa: 42, + pred_capa: 42, + } + } +} /// Solver configuration. pub struct SmtConf { - /// Actual solver configuration. - conf: SolverConf, - /// Smt logging flag. - pub log: bool, + /// Actual solver configuration. + conf: SolverConf, + /// Smt logging flag. + pub log: bool, } impl SubConf for SmtConf { - fn need_out_dir(& self) -> bool { self.log } + fn need_out_dir(&self) -> bool { + self.log + } } impl SmtConf { - /// Actual, `rsmt2` solver configuration. - pub fn conf(& self) -> SolverConf { - self.conf.clone() - } - - /// Spawns a solver. - /// - /// Performs the solver initialization step given by `common::smt::init`. - /// - /// If logging is active, will log to `.smt2`. - fn internal_spawn( - & self, name: & 'static str, parser: Parser, instance: I, preproc: bool - ) -> Res< ::rsmt2::Solver > - where I: AsRef { - let mut smt_conf = self.conf.clone() ; - if let Some(timeout) = ::common::conf.until_timeout() { - smt_conf.option( - format!( "-T:{}", timeout.as_secs() + 1 ) - ) ; + /// Actual, `rsmt2` solver configuration. + pub fn conf(&self) -> SolverConf { + self.conf.clone() } - let mut solver = ::rsmt2::Solver::new(smt_conf, parser) ? ; - if let Some(log) = self.log_file(name, instance.as_ref()).chain_err( - || format!( - "While opening log file for {}", ::common::conf.emph(name) - ) - ) ? { - solver.tee(log) ? + /// Spawns a solver. + /// + /// Performs the solver initialization step given by `common::smt::init`. + /// + /// If logging is active, will log to `.smt2`. + fn internal_spawn( + &self, + name: &'static str, + parser: Parser, + instance: I, + preproc: bool, + ) -> Res<::rsmt2::Solver> + where + I: AsRef, + { + let mut smt_conf = self.conf.clone(); + if let Some(timeout) = ::common::conf.until_timeout() { + smt_conf.option(format!("-T:{}", timeout.as_secs() + 1)); + } + + let mut solver = ::rsmt2::Solver::new(smt_conf, parser)?; + if let Some(log) = self + .log_file(name, instance.as_ref()) + .chain_err(|| format!("While opening log file for {}", ::common::conf.emph(name)))? + { + solver.tee(log)? + } + + if preproc { + ::smt::preproc_init(&mut solver)? + } else { + ::smt::init(&mut solver, instance)? + } + Ok(solver) } - if preproc { - ::smt::preproc_init(& mut solver) ? - } else { - ::smt::init(& mut solver, instance) ? - } - Ok(solver) - } - - /// Spawns a solver. - /// - /// Performs the solver initialization step given by `common::smt::init`. - /// - /// If logging is active, will log to `.smt2`. - pub fn spawn( - & self, name: & 'static str, parser: Parser, instance: I - ) -> Res< ::rsmt2::Solver > - where I: AsRef { - self.internal_spawn(name, parser, instance, false) - } - - /// Spawns a preprocessing solver. - /// - /// Performs the solver initialization step given by `common::smt::init`. - /// - /// If logging is active, will log to `.smt2`. - pub fn preproc_spawn( - & self, name: & 'static str, parser: Parser, instance: I - ) -> Res< ::rsmt2::Solver > - where I: AsRef { - self.internal_spawn(name, parser, instance, true) - } - - /// Smt log dir, if any. - fn log_dir(& self, instance: & Instance) -> Res< Option > { - if self.log { - let mut path = ::common::conf.out_dir(instance) ; - path.push("solvers") ; - mk_dir(& path) ? ; - Ok(Some(path)) - } else { - Ok(None) + /// Spawns a solver. + /// + /// Performs the solver initialization step given by `common::smt::init`. + /// + /// If logging is active, will log to `.smt2`. + pub fn spawn( + &self, + name: &'static str, + parser: Parser, + instance: I, + ) -> Res<::rsmt2::Solver> + where + I: AsRef, + { + self.internal_spawn(name, parser, instance, false) } - } - - /// Smt log file, if any. - fn log_file>( - & self, name: S, instance: & Instance - ) -> Res< Option<::std::fs::File> > { - use std::fs::OpenOptions ; - if let Some(mut path) = self.log_dir(instance) ? { - path.push(name.as_ref()) ; - path.set_extension("smt2") ; - let file = OpenOptions::new().write(true).truncate(true).create( - true - ).open(& path).chain_err( - || format!("while creating smt log file {}", path.to_string_lossy()) - ) ? ; - Ok( Some(file) ) - } else { - Ok(None) + + /// Spawns a preprocessing solver. + /// + /// Performs the solver initialization step given by `common::smt::init`. + /// + /// If logging is active, will log to `.smt2`. + pub fn preproc_spawn( + &self, + name: &'static str, + parser: Parser, + instance: I, + ) -> Res<::rsmt2::Solver> + where + I: AsRef, + { + self.internal_spawn(name, parser, instance, true) } - } - - /// Adds clap options to a clap `App`. - pub fn add_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.arg( - - Arg::with_name("z3_cmd").long("--z3").help( - "sets the command used to call z3" - ).default_value( - "z3" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("smt_log").long("--smt_log").help( - "(de)activates smt logging to the output directory" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ) - } - - /// Creates itself from some matches. - pub fn new(matches: & Matches) -> Self { - let z3_cmd = matches.value_of("z3_cmd").expect( - "unreachable(out_dir): default is provided" - ).to_string() ; - let mut conf = SolverConf::z3() ; - conf.cmd( z3_cmd ) ; - conf.models() ; - - let log = bool_of_matches(matches, "smt_log") ; - - SmtConf { conf, log } - } -} + /// Smt log dir, if any. + fn log_dir(&self, instance: &Instance) -> Res> { + if self.log { + let mut path = ::common::conf.out_dir(instance); + path.push("solvers"); + mk_dir(&path)?; + Ok(Some(path)) + } else { + Ok(None) + } + } + /// Smt log file, if any. + fn log_file>( + &self, + name: S, + instance: &Instance, + ) -> Res> { + use std::fs::OpenOptions; + if let Some(mut path) = self.log_dir(instance)? { + path.push(name.as_ref()); + path.set_extension("smt2"); + let file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(&path) + .chain_err(|| format!("while creating smt log file {}", path.to_string_lossy()))?; + Ok(Some(file)) + } else { + Ok(None) + } + } + /// Adds clap options to a clap `App`. + pub fn add_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.arg( + Arg::with_name("z3_cmd") + .long("--z3") + .help("sets the command used to call z3") + .default_value("z3") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("smt_log") + .long("--smt_log") + .help("(de)activates smt logging to the output directory") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ) + } + /// Creates itself from some matches. + pub fn new(matches: &Matches) -> Self { + let z3_cmd = matches + .value_of("z3_cmd") + .expect("unreachable(out_dir): default is provided") + .to_string(); + let mut conf = SolverConf::z3(); + conf.cmd(z3_cmd); + conf.models(); + let log = bool_of_matches(matches, "smt_log"); + SmtConf { conf, log } + } +} /// Pre-processing configuration. pub struct PreprocConf { - /// (De)activates the whole preprocessing. - pub active: bool, - - /// Dump all steps of the preprocessing as smt2 systems. - pub dump: bool, - /// Dump predicate dependency graphs. - /// - /// The predicate dependency graphs are constructed by [`CfgRed`][cfg red]. - /// For each of its runs, it will log the dependencies before and after - /// reduction. - /// - /// [cfg red]: ../../instance/preproc/struct.CfgRed.html - /// (CfgRed reduction strategy) - pub dump_pred_dep: bool, - - /// Horn reduction flag. - /// - /// (De)activates [`SimpleOneLhs`][slhs], [`SimpleOneRhs`][srhs], - /// [`OneLhs`][lhs], and [`OneRhs`][rhs]. If true, then these strategies are - /// controlled separately by the flags below. - /// - /// [slhs]: ../../instance/preproc/struct.SimpleOneLhs.html - /// (SimpleOneLhs reduction strategy) - /// [srhs]: ../../instance/preproc/struct.SimpleOneRhs.html - /// (SimpleOneRhs reduction strategy) - /// [lhs]: ../../instance/preproc/struct.OneLhs.html - /// (OneLhs reduction strategy) - /// [rhs]: ../../instance/preproc/struct.OneRhs.html - /// (OneRhs reduction strategy) - pub reduction: bool, - - /// Allows right-hand side Horn reduction. - /// - /// Deactivates [`OneRhs`][rhs] and [`SimpleOneRhs`][srhs] if false. - /// - /// [rhs]: ../../instance/preproc/struct.OneRhs.html - /// (OneRhs reduction strategy) - /// [srhs]: ../../instance/preproc/struct.SimpleOneRhs.html - /// (SimpleOneRhs reduction strategy) - pub one_rhs: bool, - /// Allows full right-hand side Horn reduction. - /// - /// Deactivates [`OneRhs`][rhs] if false. - /// - /// [rhs]: ../../instance/preproc/struct.OneRhs.html - /// (OneRhs reduction strategy) - pub one_rhs_full: bool, - /// Allows left-hand side Horn reduction. - /// - /// Deactivates [`OneLhs`][lhs] and [`SimpleOneLhs`][slhs] if false. - /// - /// [lhs]: ../../instance/preproc/struct.OneLhs.html - /// (OneLhs reduction strategy) - /// [slhs]: ../../instance/preproc/struct.SimpleOneLhs.html - /// (SimpleOneLhs reduction strategy) - pub one_lhs: bool, - /// Allows full left-hand side Horn reduction. - /// - /// Deactivates [`OneLhs`][lhs] if false. - /// - /// [lhs]: ../../instance/preproc/struct.OneLhs.html - /// (OneLhs reduction strategy) - pub one_lhs_full: bool, - - /// Allows cfg reduction. - /// - /// (De)activates [`CfgRed`][cfg red]. - /// - /// [cfg red]: ../../instance/preproc/struct.CfgRed.html - /// (CfgRed reduction strategy) - pub cfg_red: bool, - - /// Allows argument reduction. - /// - /// (De)activates [`ArgRed`][arg red]. - /// - /// [arg red]: ../../instance/preproc/struct.ArgRed.html - /// (ArgRed reduction strategy) - pub arg_red: bool, - - /// Reverse unrolling. - pub runroll: bool, - - /// Allows positive clever unrolling. - /// - /// (De)activates [`BiasedUnroll`][unroll] (positive version). - /// - /// [unroll]: ../../instance/preproc/struct.BiasedUnroll.html - /// (BiasedUnroll strategy) - pub pos_unroll: bool, - - /// Allows negative clever unrolling. - /// - /// (De)activates [`BiasedUnroll`][unroll] (negative version). - /// - /// [unroll]: ../../instance/preproc/struct.BiasedUnroll.html - /// (BiasedUnroll strategy) - pub neg_unroll: bool, - - /// Allows clause term pruning. - /// - /// This is part of the [`Simplify`][simpl] strategy as well as the - /// simplification that run on the clauses modified by all reduction - /// strategies. This can be expensive as it relies on SMT queries for - /// pruning. - /// - /// [simpl]: ../../instance/preproc/struct.Simplify.html - /// (Simplify reduction strategy) - pub prune_terms: bool, - - /// Allows strengthening when splitting. - pub split_strengthen: bool, - - /// Allows clause sorting when splitting. - pub split_sort: bool, - - /// Strengthening by strict clauses. - pub strict_neg: bool, - - /// Activates predicates to function reduction - pub fun_preds: bool, + /// (De)activates the whole preprocessing. + pub active: bool, + + /// Dump all steps of the preprocessing as smt2 systems. + pub dump: bool, + /// Dump predicate dependency graphs. + /// + /// The predicate dependency graphs are constructed by [`CfgRed`][cfg red]. + /// For each of its runs, it will log the dependencies before and after + /// reduction. + /// + /// [cfg red]: ../../instance/preproc/struct.CfgRed.html + /// (CfgRed reduction strategy) + pub dump_pred_dep: bool, + + /// Horn reduction flag. + /// + /// (De)activates [`SimpleOneLhs`][slhs], [`SimpleOneRhs`][srhs], + /// [`OneLhs`][lhs], and [`OneRhs`][rhs]. If true, then these strategies are + /// controlled separately by the flags below. + /// + /// [slhs]: ../../instance/preproc/struct.SimpleOneLhs.html + /// (SimpleOneLhs reduction strategy) + /// [srhs]: ../../instance/preproc/struct.SimpleOneRhs.html + /// (SimpleOneRhs reduction strategy) + /// [lhs]: ../../instance/preproc/struct.OneLhs.html + /// (OneLhs reduction strategy) + /// [rhs]: ../../instance/preproc/struct.OneRhs.html + /// (OneRhs reduction strategy) + pub reduction: bool, + + /// Allows right-hand side Horn reduction. + /// + /// Deactivates [`OneRhs`][rhs] and [`SimpleOneRhs`][srhs] if false. + /// + /// [rhs]: ../../instance/preproc/struct.OneRhs.html + /// (OneRhs reduction strategy) + /// [srhs]: ../../instance/preproc/struct.SimpleOneRhs.html + /// (SimpleOneRhs reduction strategy) + pub one_rhs: bool, + /// Allows full right-hand side Horn reduction. + /// + /// Deactivates [`OneRhs`][rhs] if false. + /// + /// [rhs]: ../../instance/preproc/struct.OneRhs.html + /// (OneRhs reduction strategy) + pub one_rhs_full: bool, + /// Allows left-hand side Horn reduction. + /// + /// Deactivates [`OneLhs`][lhs] and [`SimpleOneLhs`][slhs] if false. + /// + /// [lhs]: ../../instance/preproc/struct.OneLhs.html + /// (OneLhs reduction strategy) + /// [slhs]: ../../instance/preproc/struct.SimpleOneLhs.html + /// (SimpleOneLhs reduction strategy) + pub one_lhs: bool, + /// Allows full left-hand side Horn reduction. + /// + /// Deactivates [`OneLhs`][lhs] if false. + /// + /// [lhs]: ../../instance/preproc/struct.OneLhs.html + /// (OneLhs reduction strategy) + pub one_lhs_full: bool, + + /// Allows cfg reduction. + /// + /// (De)activates [`CfgRed`][cfg red]. + /// + /// [cfg red]: ../../instance/preproc/struct.CfgRed.html + /// (CfgRed reduction strategy) + pub cfg_red: bool, + + /// Allows argument reduction. + /// + /// (De)activates [`ArgRed`][arg red]. + /// + /// [arg red]: ../../instance/preproc/struct.ArgRed.html + /// (ArgRed reduction strategy) + pub arg_red: bool, + + /// Reverse unrolling. + pub runroll: bool, + + /// Allows positive clever unrolling. + /// + /// (De)activates [`BiasedUnroll`][unroll] (positive version). + /// + /// [unroll]: ../../instance/preproc/struct.BiasedUnroll.html + /// (BiasedUnroll strategy) + pub pos_unroll: bool, + + /// Allows negative clever unrolling. + /// + /// (De)activates [`BiasedUnroll`][unroll] (negative version). + /// + /// [unroll]: ../../instance/preproc/struct.BiasedUnroll.html + /// (BiasedUnroll strategy) + pub neg_unroll: bool, + + /// Allows clause term pruning. + /// + /// This is part of the [`Simplify`][simpl] strategy as well as the + /// simplification that run on the clauses modified by all reduction + /// strategies. This can be expensive as it relies on SMT queries for + /// pruning. + /// + /// [simpl]: ../../instance/preproc/struct.Simplify.html + /// (Simplify reduction strategy) + pub prune_terms: bool, + + /// Allows strengthening when splitting. + pub split_strengthen: bool, + + /// Allows clause sorting when splitting. + pub split_sort: bool, + + /// Strengthening by strict clauses. + pub strict_neg: bool, + + /// Activates predicates to function reduction + pub fun_preds: bool, } impl SubConf for PreprocConf { - fn need_out_dir(& self) -> bool { - self.dump && self.active || self.dump_pred_dep - } + fn need_out_dir(&self) -> bool { + self.dump && self.active || self.dump_pred_dep + } } impl PreprocConf { - /// Instance dump dir. - fn log_dir(& self, sub: Path, instance: & Instance) -> Res - where Path: AsRef<::std::path::Path> { - let mut path = ::common::conf.out_dir(instance) ; - path.push("preproc") ; - path.push(sub) ; - mk_dir(& path) ? ; - Ok(path) - } - - /// Instance dump file. - pub fn instance_log_file>( - & self, name: S, instance: & Instance - ) -> Res< Option<::std::fs::File> > { - use std::fs::OpenOptions ; - if self.dump && self.active { - let mut path = self.log_dir("instances", instance) ? ; - path.push(name.as_ref()) ; - path.set_extension("smt2") ; - let file = OpenOptions::new().write(true).truncate(true).create( - true - ).open(& path).chain_err( - || format!( - "while creating instance dump file {}", path.to_string_lossy() - ) - ) ? ; - Ok(Some(file)) - } else { - Ok(None) + /// Instance dump dir. + fn log_dir(&self, sub: Path, instance: &Instance) -> Res + where + Path: AsRef<::std::path::Path>, + { + let mut path = ::common::conf.out_dir(instance); + path.push("preproc"); + path.push(sub); + mk_dir(&path)?; + Ok(path) } - } - - /// Predicate dependency file. - pub fn pred_dep_file>( - & self, name: S, instance: & Instance - ) -> Res< - Option< (::std::fs::File, ::std::path::PathBuf) > - > { - use std::fs::OpenOptions ; - if self.dump_pred_dep { - let mut path = self.log_dir("pred_dep", instance) ? ; - path.push(name.as_ref()) ; - path.set_extension("gv") ; - let file = OpenOptions::new().write(true).truncate(true).create( - true - ).open(& path).chain_err( - || format!( - "while creating predicade dependency graph file {}", - path.to_string_lossy() - ) - ) ? ; - Ok(Some((file, path))) - } else { - Ok(None) - } - } - - /// Adds clap options to a clap `App`. - pub fn add_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.arg( - - Arg::with_name("preproc").long("--preproc").help( - "(de)activates pre-processing" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("dump_preproc").long("--dump_preproc").help( - "(de)activates instance dumping during preprocessing" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("prune_terms").long("--prune_terms").help( - "(de)activates clause term pruning when simplifying clauses" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).hidden( - true - ).display_order( order() ) - - ).arg( - - Arg::with_name("reduction").long("--reduction").help( - "(de)activates all Horn reduction" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("one_rhs").long("--one_rhs").help( - "(de)activates one rhs reduction" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("one_rhs_full").long("--one_rhs_full").help( - "(de)activates full one rhs reduction (might introduce quantifiers)" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("one_lhs").long("--one_lhs").help( - "(de)activates reduction of predicate \ - appearing in exactly one clause lhs" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("one_lhs_full").long("--one_lhs_full").help( - "(de)activates full one lhs reduction (might introduce quantifiers)" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("arg_red").long("--arg_red").help( - "(de)activates argument reduction" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("cfg_red").long("--cfg_red").help( - "(de)activates control flow graph reduction" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("dump_pred_dep").long("--dump_pred_dep").help( - "(de)activates predicate dependency dumps (cfg_red)" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("runroll").long("--runroll").help( - "(de)activates reverse unrolling" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("off").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("pos_unroll").long("--pos_unroll").help( - "(de)activates positive unrolling" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("neg_unroll").long("--neg_unroll").help( - "(de)activates negative unrolling" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value(true).hidden( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("split_strengthen").long("--split_strengthen").help( - "(de)activates strengthening when splitting is active" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("split_sort").long("--split_sort").help( - "(de)activates clause sorting when splitting is active" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("strict_neg").long("--strict_neg").help( - "(de)activates strengthening by strict negative clauses" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").hidden(true).takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("fun_preds").long("--fun_preds").help( - "(de)activates predicate-to-function reduction" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").hidden(true).takes_value( - true - ).number_of_values(1).display_order( order() ) - - ) - } - - /// Creates itself from some matches. - pub fn new(matches: & Matches) -> Self { - let active = bool_of_matches(matches, "preproc") ; - let reduction = bool_of_matches(matches, "reduction") ; - let arg_red = bool_of_matches(matches, "arg_red") ; - let one_rhs = bool_of_matches(matches, "one_rhs") ; - let one_rhs_full = bool_of_matches(matches, "one_rhs_full") ; - let one_lhs = bool_of_matches(matches, "one_lhs") ; - let one_lhs_full = bool_of_matches(matches, "one_lhs_full") ; - let cfg_red = bool_of_matches(matches, "cfg_red") ; - let dump = bool_of_matches(matches, "dump_preproc") ; - let dump_pred_dep = bool_of_matches(matches, "dump_pred_dep") ; - let prune_terms = bool_of_matches(matches, "prune_terms") ; - let runroll = bool_of_matches(matches, "runroll") ; - let pos_unroll = bool_of_matches(matches, "pos_unroll") ; - let neg_unroll = bool_of_matches(matches, "neg_unroll") ; - let split_strengthen = bool_of_matches(matches, "split_strengthen") ; - let split_sort = bool_of_matches(matches, "split_sort") ; - let strict_neg = bool_of_matches(matches, "strict_neg") ; - let fun_preds = bool_of_matches(matches, "fun_preds") ; - - PreprocConf { - dump, dump_pred_dep, active, - reduction, one_rhs, one_rhs_full, one_lhs, one_lhs_full, cfg_red, - arg_red, prune_terms, runroll, pos_unroll, neg_unroll, - split_strengthen, split_sort, strict_neg, fun_preds, - } - } -} - - + /// Instance dump file. + pub fn instance_log_file>( + &self, + name: S, + instance: &Instance, + ) -> Res> { + use std::fs::OpenOptions; + if self.dump && self.active { + let mut path = self.log_dir("instances", instance)?; + path.push(name.as_ref()); + path.set_extension("smt2"); + let file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(&path) + .chain_err(|| { + format!( + "while creating instance dump file {}", + path.to_string_lossy() + ) + })?; + Ok(Some(file)) + } else { + Ok(None) + } + } + /// Predicate dependency file. + pub fn pred_dep_file>( + &self, + name: S, + instance: &Instance, + ) -> Res> { + use std::fs::OpenOptions; + if self.dump_pred_dep { + let mut path = self.log_dir("pred_dep", instance)?; + path.push(name.as_ref()); + path.set_extension("gv"); + let file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(&path) + .chain_err(|| { + format!( + "while creating predicade dependency graph file {}", + path.to_string_lossy() + ) + })?; + Ok(Some((file, path))) + } else { + Ok(None) + } + } + /// Adds clap options to a clap `App`. + pub fn add_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.arg( + Arg::with_name("preproc") + .long("--preproc") + .help("(de)activates pre-processing") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("dump_preproc") + .long("--dump_preproc") + .help("(de)activates instance dumping during preprocessing") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("prune_terms") + .long("--prune_terms") + .help("(de)activates clause term pruning when simplifying clauses") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("reduction") + .long("--reduction") + .help("(de)activates all Horn reduction") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("one_rhs") + .long("--one_rhs") + .help("(de)activates one rhs reduction") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("one_rhs_full") + .long("--one_rhs_full") + .help("(de)activates full one rhs reduction (might introduce quantifiers)") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("one_lhs") + .long("--one_lhs") + .help( + "(de)activates reduction of predicate \ + appearing in exactly one clause lhs", + ).validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("one_lhs_full") + .long("--one_lhs_full") + .help("(de)activates full one lhs reduction (might introduce quantifiers)") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("arg_red") + .long("--arg_red") + .help("(de)activates argument reduction") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("cfg_red") + .long("--cfg_red") + .help("(de)activates control flow graph reduction") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("dump_pred_dep") + .long("--dump_pred_dep") + .help("(de)activates predicate dependency dumps (cfg_red)") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("runroll") + .long("--runroll") + .help("(de)activates reverse unrolling") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("pos_unroll") + .long("--pos_unroll") + .help("(de)activates positive unrolling") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("neg_unroll") + .long("--neg_unroll") + .help("(de)activates negative unrolling") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .hidden(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("split_strengthen") + .long("--split_strengthen") + .help("(de)activates strengthening when splitting is active") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("split_sort") + .long("--split_sort") + .help("(de)activates clause sorting when splitting is active") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("strict_neg") + .long("--strict_neg") + .help("(de)activates strengthening by strict negative clauses") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .hidden(true) + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("fun_preds") + .long("--fun_preds") + .help("(de)activates predicate-to-function reduction") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .hidden(true) + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ) + } + /// Creates itself from some matches. + pub fn new(matches: &Matches) -> Self { + let active = bool_of_matches(matches, "preproc"); + let reduction = bool_of_matches(matches, "reduction"); + let arg_red = bool_of_matches(matches, "arg_red"); + let one_rhs = bool_of_matches(matches, "one_rhs"); + let one_rhs_full = bool_of_matches(matches, "one_rhs_full"); + let one_lhs = bool_of_matches(matches, "one_lhs"); + let one_lhs_full = bool_of_matches(matches, "one_lhs_full"); + let cfg_red = bool_of_matches(matches, "cfg_red"); + let dump = bool_of_matches(matches, "dump_preproc"); + let dump_pred_dep = bool_of_matches(matches, "dump_pred_dep"); + let prune_terms = bool_of_matches(matches, "prune_terms"); + let runroll = bool_of_matches(matches, "runroll"); + let pos_unroll = bool_of_matches(matches, "pos_unroll"); + let neg_unroll = bool_of_matches(matches, "neg_unroll"); + let split_strengthen = bool_of_matches(matches, "split_strengthen"); + let split_sort = bool_of_matches(matches, "split_sort"); + let strict_neg = bool_of_matches(matches, "strict_neg"); + let fun_preds = bool_of_matches(matches, "fun_preds"); + + PreprocConf { + dump, + dump_pred_dep, + active, + reduction, + one_rhs, + one_rhs_full, + one_lhs, + one_lhs_full, + cfg_red, + arg_red, + prune_terms, + runroll, + pos_unroll, + neg_unroll, + split_strengthen, + split_sort, + strict_neg, + fun_preds, + } + } +} /// Ice learner configuration. pub struct IceConf { - /// Ignore unclassified data when computing entropy. - pub simple_gain_ratio: f64, - /// Sort predicates. - pub sort_preds: f64, - /// Randomize qualifiers. - pub rand_quals: bool, - /// Generate complete transformations for qualifiers. - pub complete: bool, - /// Biases qualifier selection based on the predicates the qualifier was - /// created for. - pub qual_bias: bool, - /// Activates qualifier printing. - pub qual_print: bool, - /// Gain above which a qualifier is considered acceptable for splitting data. - pub gain_pivot: f64, - /// - pub gain_pivot_inc: f64, - /// - pub gain_pivot_mod: usize, - /// Same as `gain_pivot` but for qualifier synthesis. - pub gain_pivot_synth: Option, - /// Run a learner that does not mine the instance. - pub pure_synth: bool, - /// Mine conjunction of terms. - pub mine_conjs: bool, - /// Add synthesized qualifiers. - pub add_synth: bool, - /// Lockstep for qualifiers. - pub qual_step: bool, - /// Lockstep for synthesized qualifiers only. - pub qual_synth_step: bool, + /// Ignore unclassified data when computing entropy. + pub simple_gain_ratio: f64, + /// Sort predicates. + pub sort_preds: f64, + /// Randomize qualifiers. + pub rand_quals: bool, + /// Generate complete transformations for qualifiers. + pub complete: bool, + /// Biases qualifier selection based on the predicates the qualifier was + /// created for. + pub qual_bias: bool, + /// Activates qualifier printing. + pub qual_print: bool, + /// Gain above which a qualifier is considered acceptable for splitting data. + pub gain_pivot: f64, + /// + pub gain_pivot_inc: f64, + /// + pub gain_pivot_mod: usize, + /// Same as `gain_pivot` but for qualifier synthesis. + pub gain_pivot_synth: Option, + /// Run a learner that does not mine the instance. + pub pure_synth: bool, + /// Mine conjunction of terms. + pub mine_conjs: bool, + /// Add synthesized qualifiers. + pub add_synth: bool, + /// Lockstep for qualifiers. + pub qual_step: bool, + /// Lockstep for synthesized qualifiers only. + pub qual_synth_step: bool, } impl SubConf for IceConf { - fn need_out_dir(& self) -> bool { false } + fn need_out_dir(&self) -> bool { + false + } } impl IceConf { - /// Adds clap options to a clap App. - pub fn add_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.arg( - - Arg::with_name("simple_gain_ratio").long("--simple_gain_ratio").help( - "percent of times simple gain will be used" - ).validator( - int_validator - ).value_name( - "int" - ).default_value( - "20" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("sort_preds").long("--sort_preds").help( - "predicate sorting before learning probability" - ).validator( - int_validator - ).value_name( - "int" - ).default_value( - "40" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ).arg( - - Arg::with_name("rand_quals").long("--rand_quals").help( - "randomize the qualifiers before gain computation" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ).arg( - - Arg::with_name("complete").long("--qual_complete").help( - "generate complete transformations for qualifiers" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("no").takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("qual_bias").long("--qual_bias").help( - "predicate-based bias for qualifier section" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("qual_print").long("--qual_print").help( - "(de)activates qualifier printing" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("no").takes_value( - true - ).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("gain_pivot").long("--gain_pivot").help( - "first qualifier with a gain higher than this value will be used\n\ - (between 0 and 100)" - ).validator( - int_validator - ).value_name( - "int" - ).default_value("5").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("gain_pivot_synth").long("--gain_pivot_synth").help( - "same as `--gain_pivot` but for qualifier synthesis \ - (inactive if > 100)" - ).validator( - int_validator - ).value_name( - "int" - ).default_value("1").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("gain_pivot_inc").long("--gain_pivot_inc").help( - "undocumented" - ).validator( - int_validator - ).value_name( - "int" - ).default_value("0").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("gain_pivot_mod").long("--gain_pivot_mod").help( - "undocumented" - ).validator( - int_validator - ).value_name( - "int" - ).default_value("100000").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("pure_synth").long("--pure_synth").help( - "if true, runs another pure-synthesis learner" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("no").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("add_synth").long("--add_synth").help( - "add synthesized qualifiers as normal qualifiers" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("mine_conjs").long("--mine_conjs").help( - "mine conjunctions of atoms from clauses" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("on").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("qual_step").long("--qual_step").help( - "lockstep qualifier selection" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("off").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ).arg( - - Arg::with_name("qual_synth_step").long("--qual_synth_step").help( - "lockstep qualifier selection (synthesis only)" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value("off").takes_value( - true - ).number_of_values(1).hidden(true).display_order( order() ) - - ) - } - - /// Creates itself from some matches. - pub fn new(matches: & Matches) -> Self { - let simple_gain_ratio = { - let mut value = int_of_matches( - matches, "simple_gain_ratio" - ) as f64 / 100.0 ; - if value < 0.0 { - 0.0 - } else if 1.0 < value { - 1.0 - } else { - value - } - } ; - let sort_preds = { - let mut value = int_of_matches( - matches, "sort_preds" - ) as f64 / 100. ; - if value < 0.0 { - 0.0 - } else if 1.0 < value { - 1.0 - } else { - value - } - } ; - - let rand_quals = bool_of_matches(matches, "rand_quals") ; - - let complete = bool_of_matches(matches, "complete") ; - let qual_bias = bool_of_matches(matches, "qual_bias") ; - let qual_print = bool_of_matches(matches, "qual_print") ; - let gain_pivot = { - let mut value = int_of_matches( - matches, "gain_pivot" - ) as f64 / 100.0 ; - if value < 0.0 { - 0.0 - } else if 1.0 < value { - 1.0 - } else { - value - } - } ; - let gain_pivot_inc = { - let mut value = int_of_matches( - matches, "gain_pivot_inc" - ) as f64 / 100.0 ; - if value < 0.0 { - 0.0 - } else if 1.0 < value { - 1.0 - } else { - value - } - } ; - let gain_pivot_synth = { - let mut value = int_of_matches( - matches, "gain_pivot_synth" - ) as f64 / 100.0 ; - if value < 0.0 { - Some(0.0) - } else if 1.0 < value { - None - } else { - Some(value) - } - } ; - let gain_pivot_mod = int_of_matches(matches, "gain_pivot_mod") ; - let add_synth = bool_of_matches(matches, "add_synth") ; - let pure_synth = bool_of_matches(matches, "pure_synth") ; - let mine_conjs = bool_of_matches(matches, "mine_conjs") ; - let qual_step = bool_of_matches(matches, "qual_step") ; - let qual_synth_step = bool_of_matches(matches, "qual_synth_step") ; - - IceConf { - simple_gain_ratio, sort_preds, rand_quals, complete, - qual_bias, qual_print, - gain_pivot, gain_pivot_inc, gain_pivot_mod, gain_pivot_synth, - pure_synth, mine_conjs, add_synth, qual_step, qual_synth_step + /// Adds clap options to a clap App. + pub fn add_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.arg( + Arg::with_name("simple_gain_ratio") + .long("--simple_gain_ratio") + .help("percent of times simple gain will be used") + .validator(int_validator) + .value_name("int") + .default_value("20") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("sort_preds") + .long("--sort_preds") + .help("predicate sorting before learning probability") + .validator(int_validator) + .value_name("int") + .default_value("40") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ).arg( + Arg::with_name("rand_quals") + .long("--rand_quals") + .help("randomize the qualifiers before gain computation") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ).arg( + Arg::with_name("complete") + .long("--qual_complete") + .help("generate complete transformations for qualifiers") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("qual_bias") + .long("--qual_bias") + .help("predicate-based bias for qualifier section") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("qual_print") + .long("--qual_print") + .help("(de)activates qualifier printing") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("gain_pivot") + .long("--gain_pivot") + .help( + "first qualifier with a gain higher than this value will be used\n\ + (between 0 and 100)", + ).validator(int_validator) + .value_name("int") + .default_value("5") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("gain_pivot_synth") + .long("--gain_pivot_synth") + .help( + "same as `--gain_pivot` but for qualifier synthesis \ + (inactive if > 100)", + ).validator(int_validator) + .value_name("int") + .default_value("1") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("gain_pivot_inc") + .long("--gain_pivot_inc") + .help("undocumented") + .validator(int_validator) + .value_name("int") + .default_value("0") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("gain_pivot_mod") + .long("--gain_pivot_mod") + .help("undocumented") + .validator(int_validator) + .value_name("int") + .default_value("100000") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("pure_synth") + .long("--pure_synth") + .help("if true, runs another pure-synthesis learner") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("add_synth") + .long("--add_synth") + .help("add synthesized qualifiers as normal qualifiers") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("mine_conjs") + .long("--mine_conjs") + .help("mine conjunctions of atoms from clauses") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("qual_step") + .long("--qual_step") + .help("lockstep qualifier selection") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ).arg( + Arg::with_name("qual_synth_step") + .long("--qual_synth_step") + .help("lockstep qualifier selection (synthesis only)") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .hidden(true) + .display_order(order()), + ) } - } -} - - - - + /// Creates itself from some matches. + pub fn new(matches: &Matches) -> Self { + let simple_gain_ratio = { + let mut value = int_of_matches(matches, "simple_gain_ratio") as f64 / 100.0; + if value < 0.0 { + 0.0 + } else if 1.0 < value { + 1.0 + } else { + value + } + }; + let sort_preds = { + let mut value = int_of_matches(matches, "sort_preds") as f64 / 100.; + if value < 0.0 { + 0.0 + } else if 1.0 < value { + 1.0 + } else { + value + } + }; + + let rand_quals = bool_of_matches(matches, "rand_quals"); + + let complete = bool_of_matches(matches, "complete"); + let qual_bias = bool_of_matches(matches, "qual_bias"); + let qual_print = bool_of_matches(matches, "qual_print"); + let gain_pivot = { + let mut value = int_of_matches(matches, "gain_pivot") as f64 / 100.0; + if value < 0.0 { + 0.0 + } else if 1.0 < value { + 1.0 + } else { + value + } + }; + let gain_pivot_inc = { + let mut value = int_of_matches(matches, "gain_pivot_inc") as f64 / 100.0; + if value < 0.0 { + 0.0 + } else if 1.0 < value { + 1.0 + } else { + value + } + }; + let gain_pivot_synth = { + let mut value = int_of_matches(matches, "gain_pivot_synth") as f64 / 100.0; + if value < 0.0 { + Some(0.0) + } else if 1.0 < value { + None + } else { + Some(value) + } + }; + let gain_pivot_mod = int_of_matches(matches, "gain_pivot_mod"); + let add_synth = bool_of_matches(matches, "add_synth"); + let pure_synth = bool_of_matches(matches, "pure_synth"); + let mine_conjs = bool_of_matches(matches, "mine_conjs"); + let qual_step = bool_of_matches(matches, "qual_step"); + let qual_synth_step = bool_of_matches(matches, "qual_synth_step"); + + IceConf { + simple_gain_ratio, + sort_preds, + rand_quals, + complete, + qual_bias, + qual_print, + gain_pivot, + gain_pivot_inc, + gain_pivot_mod, + gain_pivot_synth, + pure_synth, + mine_conjs, + add_synth, + qual_step, + qual_synth_step, + } + } +} /// Teacher configuration. pub struct TeacherConf { - /// Stop before sending data to learner(s). - pub step: bool, - /// Run the assistant to break implication constraints. - pub assistant: bool, - /// Try to find implication constraints related to existing samples first. - pub bias_cexs: bool, - /// Maximize bias: remove all constraints when there are pos/neg cexs. - pub max_bias: bool, - /// Allow partial samples. - pub partial: bool, - /// Restart the teacher's solver everytime it's used. - pub restart_on_cex: bool, + /// Stop before sending data to learner(s). + pub step: bool, + /// Run the assistant to break implication constraints. + pub assistant: bool, + /// Try to find implication constraints related to existing samples first. + pub bias_cexs: bool, + /// Maximize bias: remove all constraints when there are pos/neg cexs. + pub max_bias: bool, + /// Allow partial samples. + pub partial: bool, + /// Restart the teacher's solver everytime it's used. + pub restart_on_cex: bool, } impl SubConf for TeacherConf { - fn need_out_dir(& self) -> bool { false } + fn need_out_dir(&self) -> bool { + false + } } impl TeacherConf { - /// Adds clap options to a clap App. - pub fn add_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.arg( - - Arg::with_name("teach_step").long("--teach_step").help( - "forces the teacher to progress incrementally" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("assistant").long("--assistant").help( - "(de)activate breaking implication constraints" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("bias_cexs").long("--bias_cexs").help( - "(de)activate biased implication constraints" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("max_bias").long("--max_bias").help( - "(de)activate constraint pruning when there's at least one \ - pos/neg sample" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "off" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ).arg( - - Arg::with_name("partial").long("--partial").help( - "(de)activate partial samples" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("restart_on_cex").long("--restart_on_cex").help( - "restart the teacher's solver for each cex query" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "off" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ) - } - - /// Creates itself from some matches. - pub fn new(matches: & Matches) -> Self { - let step = bool_of_matches(matches, "teach_step") ; - let assistant = bool_of_matches(matches, "assistant") ; - let bias_cexs = bool_of_matches(matches, "bias_cexs") ; - let max_bias = bool_of_matches(matches, "max_bias") ; - let partial = bool_of_matches(matches, "partial") ; - let restart_on_cex = bool_of_matches(matches, "restart_on_cex") ; - - TeacherConf { - step, assistant, bias_cexs, max_bias, partial, restart_on_cex + /// Adds clap options to a clap App. + pub fn add_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.arg( + Arg::with_name("teach_step") + .long("--teach_step") + .help("forces the teacher to progress incrementally") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("assistant") + .long("--assistant") + .help("(de)activate breaking implication constraints") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("bias_cexs") + .long("--bias_cexs") + .help("(de)activate biased implication constraints") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("max_bias") + .long("--max_bias") + .help( + "(de)activate constraint pruning when there's at least one \ + pos/neg sample", + ).validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ).arg( + Arg::with_name("partial") + .long("--partial") + .help("(de)activate partial samples") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("restart_on_cex") + .long("--restart_on_cex") + .help("restart the teacher's solver for each cex query") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ) } - } -} + /// Creates itself from some matches. + pub fn new(matches: &Matches) -> Self { + let step = bool_of_matches(matches, "teach_step"); + let assistant = bool_of_matches(matches, "assistant"); + let bias_cexs = bool_of_matches(matches, "bias_cexs"); + let max_bias = bool_of_matches(matches, "max_bias"); + let partial = bool_of_matches(matches, "partial"); + let restart_on_cex = bool_of_matches(matches, "restart_on_cex"); + + TeacherConf { + step, + assistant, + bias_cexs, + max_bias, + partial, + restart_on_cex, + } + } +} - -use std::time::{ Instant, Duration } ; - +use std::time::{Duration, Instant}; /// Global configuration. pub struct Config { - file: Option, - /// Verbosity. - pub verb: usize, - /// Statistics flag. - pub stats: bool, - /// Inference flag. - pub infer: bool, - /// Reason on each negative clause separately. - pub split: bool, - /// Pause between negative clauses when in split mode. - pub split_step: bool, - /// Instant at which we'll timeout. - timeout: Option, - /// Output directory. - out_dir: String, - /// Styles, for coloring. - styles: Styles, - - /// Result check file. - check: Option, - /// Eldarica result checking flag. - pub check_eld: bool, - /// If true, SMT-check all simplifications. - pub check_simpl: bool, - /// Level of term simplification. - pub term_simpl: usize, - - /// Instance and factory configuration. - pub instance: InstanceConf, - /// Pre-processing configuration. - pub preproc: PreprocConf, - /// Solver configuration. - pub solver: SmtConf, - /// Ice configuration. - pub ice: IceConf, - /// Teacher configuration. - pub teacher: TeacherConf, + file: Option, + /// Verbosity. + pub verb: usize, + /// Statistics flag. + pub stats: bool, + /// Inference flag. + pub infer: bool, + /// Reason on each negative clause separately. + pub split: bool, + /// Pause between negative clauses when in split mode. + pub split_step: bool, + /// Instant at which we'll timeout. + timeout: Option, + /// Output directory. + out_dir: String, + /// Styles, for coloring. + styles: Styles, + + /// Result check file. + check: Option, + /// Eldarica result checking flag. + pub check_eld: bool, + /// If true, SMT-check all simplifications. + pub check_simpl: bool, + /// Level of term simplification. + pub term_simpl: usize, + + /// Instance and factory configuration. + pub instance: InstanceConf, + /// Pre-processing configuration. + pub preproc: PreprocConf, + /// Solver configuration. + pub solver: SmtConf, + /// Ice configuration. + pub ice: IceConf, + /// Teacher configuration. + pub teacher: TeacherConf, } impl ColorExt for Config { - fn styles(& self) -> & Styles { & self.styles } + fn styles(&self) -> &Styles { + &self.styles + } } impl Config { - /// Output directory as a `PathBuf`. - #[inline] - pub fn out_dir(& self, instance: & Instance) -> PathBuf { - let mut path = PathBuf::from(& self.out_dir) ; - if let Some(clause) = instance.split() { - path.push( format!("split_on_clause_{}", clause) ) + /// Output directory as a `PathBuf`. + #[inline] + pub fn out_dir(&self, instance: &Instance) -> PathBuf { + let mut path = PathBuf::from(&self.out_dir); + if let Some(clause) = instance.split() { + path.push(format!("split_on_clause_{}", clause)) + } + path } - path - } - - /// Input file. - #[inline] - pub fn in_file(& self) -> Option<& String> { - self.file.as_ref() - } - /// Result to check file. - #[inline] - pub fn check_file(& self) -> Option<& String> { - self.check.as_ref() - } - - /// Checks if we're out of time. - #[inline] - pub fn check_timeout(& self) -> Res<()> { - if let Some(max) = self.timeout.as_ref() { - if Instant::now() > * max { - bail!( ErrorKind::Timeout ) - } + + /// Input file. + #[inline] + pub fn in_file(&self) -> Option<&String> { + self.file.as_ref() } - Ok(()) - } - /// Time until timeout. - #[inline] - pub fn until_timeout(& self) -> Option { - if let Some(timeout) = self.timeout.as_ref() { - let now = Instant::now() ; - if now > * timeout { - Some( Duration::new(0,0) ) - } else { - Some( * timeout - now ) - } - } else { - None + /// Result to check file. + #[inline] + pub fn check_file(&self) -> Option<&String> { + self.check.as_ref() } - } - - /// Parses command-line arguments and generates the configuration. - pub fn clap() -> Self { - let mut app = App::new( crate_name!() ) ; - app = Self::add_args(app, 0) ; - app = PreprocConf::add_args(app, 100) ; - app = InstanceConf::add_args(app, 200) ; - app = SmtConf::add_args(app, 300) ; - app = IceConf::add_args(app, 400) ; - app = TeacherConf::add_args(app, 500) ; - app = Self::add_check_args(app, 600) ; - - let matches = app.get_matches() ; - - // Input file. - let file = matches.value_of("input file").map(|s| s.to_string()) ; - - // Verbosity - let mut verb = 0 ; - for _ in 0..matches.occurrences_of("verb") { - verb += 1 + + /// Checks if we're out of time. + #[inline] + pub fn check_timeout(&self) -> Res<()> { + if let Some(max) = self.timeout.as_ref() { + if Instant::now() > *max { + bail!(ErrorKind::Timeout) + } + } + Ok(()) } - for _ in 0..matches.occurrences_of("quiet") { - if verb > 0 { - verb -= 1 - } + /// Time until timeout. + #[inline] + pub fn until_timeout(&self) -> Option { + if let Some(timeout) = self.timeout.as_ref() { + let now = Instant::now(); + if now > *timeout { + Some(Duration::new(0, 0)) + } else { + Some(*timeout - now) + } + } else { + None + } } - // Colors. - let color = ::isatty::stdout_isatty() && bool_of_matches( - & matches, "color" - ) ; - let styles = Styles::new(color) ; - - // Output directory. - let out_dir = matches.value_of("out_dir").expect( - "unreachable(out_dir): default is provided" - ).to_string() ; - - // Profiling. - let stats = bool_of_matches(& matches, "stats") ; - - // Inference flag. - let infer = bool_of_matches(& matches, "infer") ; - - // Inference flag. - let split_step = bool_of_matches(& matches, "split_step") ; - - // Timeout. - let timeout = match int_of_matches(& matches, "timeout") { - 0 => None, - n => Some( - Instant::now() + Duration::new(n as u64, 0) - ), - } ; - - let split = bool_of_matches(& matches, "split") ; - - // Result checking. - let check = matches.value_of("check").map( - |s| s.to_string() - ) ; - let check_eld = bool_of_matches(& matches, "check_eld") ; - let check_simpl = bool_of_matches(& matches, "check_simpl") ; - - // Timeout. - let term_simpl = int_of_matches(& matches, "term_simpl") ; - - let instance = InstanceConf::new(& matches) ; - let preproc = PreprocConf::new(& matches) ; - let solver = SmtConf::new(& matches) ; - let ice = IceConf::new(& matches) ; - let teacher = TeacherConf::new(& matches) ; - - Config { - file, verb, stats, infer, split, split_step, - timeout, out_dir, styles, - check, check_eld, check_simpl, term_simpl, - instance, preproc, solver, ice, teacher, + /// Parses command-line arguments and generates the configuration. + pub fn clap() -> Self { + let mut app = App::new(crate_name!()); + app = Self::add_args(app, 0); + app = PreprocConf::add_args(app, 100); + app = InstanceConf::add_args(app, 200); + app = SmtConf::add_args(app, 300); + app = IceConf::add_args(app, 400); + app = TeacherConf::add_args(app, 500); + app = Self::add_check_args(app, 600); + + let matches = app.get_matches(); + + // Input file. + let file = matches.value_of("input file").map(|s| s.to_string()); + + // Verbosity + let mut verb = 0; + for _ in 0..matches.occurrences_of("verb") { + verb += 1 + } + for _ in 0..matches.occurrences_of("quiet") { + if verb > 0 { + verb -= 1 + } + } + + // Colors. + let color = ::isatty::stdout_isatty() && bool_of_matches(&matches, "color"); + let styles = Styles::new(color); + + // Output directory. + let out_dir = matches + .value_of("out_dir") + .expect("unreachable(out_dir): default is provided") + .to_string(); + + // Profiling. + let stats = bool_of_matches(&matches, "stats"); + + // Inference flag. + let infer = bool_of_matches(&matches, "infer"); + + // Inference flag. + let split_step = bool_of_matches(&matches, "split_step"); + + // Timeout. + let timeout = match int_of_matches(&matches, "timeout") { + 0 => None, + n => Some(Instant::now() + Duration::new(n as u64, 0)), + }; + + let split = bool_of_matches(&matches, "split"); + + // Result checking. + let check = matches.value_of("check").map(|s| s.to_string()); + let check_eld = bool_of_matches(&matches, "check_eld"); + let check_simpl = bool_of_matches(&matches, "check_simpl"); + + // Timeout. + let term_simpl = int_of_matches(&matches, "term_simpl"); + + let instance = InstanceConf::new(&matches); + let preproc = PreprocConf::new(&matches); + let solver = SmtConf::new(&matches); + let ice = IceConf::new(&matches); + let teacher = TeacherConf::new(&matches); + + Config { + file, + verb, + stats, + infer, + split, + split_step, + timeout, + out_dir, + styles, + check, + check_eld, + check_simpl, + term_simpl, + instance, + preproc, + solver, + ice, + teacher, + } } - } - - /// Adds clap options to a clap App. - pub fn add_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.author( crate_authors!() ).version( - * ::common::version - ).about( - "ICE engine for systems described as Horn Clauses." - ).arg( - - Arg::with_name("input file").help( - "sets the input file to use" - ).index(1).display_order( order() ) - - ).arg( - - Arg::with_name("verb").short("-v").help( - "increases verbosity" - ).takes_value(false).multiple(true).display_order( order() ) - - ).arg( - - Arg::with_name("quiet").short("-q").help( - "decreases verbosity" - ).takes_value(false).multiple(true).display_order( order() ) - - ).arg( - - Arg::with_name("color").long("--color").short("-c").help( - "(de)activates coloring (off if output is not a tty)" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("out_dir").long("--out_dir").short("-o").help( - "sets the output directory" - ).value_name( - "DIR" - ).default_value( - "hoice_out" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("stats").long("--stats").short("-s").help( - "reports some statistics at the end of the run" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("infer").long("--infer").short("-i").help( - "if `off`, ignore `check-sat` and `get-model` queries" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "on" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("timeout").long("--timeout").short("-t").help( - "sets a timeout in seconds, `0` for none" - ).validator( - int_validator - ).value_name( - "int" - ).default_value( - "0" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("split").long("--split").help( - "reason on each negative clause separately" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "off" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("split_step").long("--split_step").help( - "pause between negative clauses in split mode" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "off" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("term_simpl").long("--term_simpl").help( - "level of term simplification between 0 and 3" - ).validator( - int_validator - ).value_name( - "int" - ).default_value( - "1" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ).arg( - - Arg::with_name("check_simpl").long("--check_simpl").help( - "if true, check all simplifications" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( - order() - ).hidden(true) - - ) - } - - /// Add args related to result checking. - pub fn add_check_args(app: App, mut order: usize) -> App { - let mut order = || { - order += 1 ; - order - } ; - - app.arg( - - Arg::with_name("check").long("--check").help( - "checks a model for the input system (does not run inference)" - ).value_name( - "FILE" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ).arg( - - Arg::with_name("check_eld").long("--check_eld").help( - "if `check` is active, expect eldarica-style result" - ).validator( - bool_validator - ).value_name( - bool_format - ).default_value( - "no" - ).takes_value(true).number_of_values(1).display_order( order() ) - - ) - } -} - - - - - - - + /// Adds clap options to a clap App. + pub fn add_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.author(crate_authors!()) + .version(*::common::version) + .about("ICE engine for systems described as Horn Clauses.") + .arg( + Arg::with_name("input file") + .help("sets the input file to use") + .index(1) + .display_order(order()), + ).arg( + Arg::with_name("verb") + .short("-v") + .help("increases verbosity") + .takes_value(false) + .multiple(true) + .display_order(order()), + ).arg( + Arg::with_name("quiet") + .short("-q") + .help("decreases verbosity") + .takes_value(false) + .multiple(true) + .display_order(order()), + ).arg( + Arg::with_name("color") + .long("--color") + .short("-c") + .help("(de)activates coloring (off if output is not a tty)") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("out_dir") + .long("--out_dir") + .short("-o") + .help("sets the output directory") + .value_name("DIR") + .default_value("hoice_out") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("stats") + .long("--stats") + .short("-s") + .help("reports some statistics at the end of the run") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("infer") + .long("--infer") + .short("-i") + .help("if `off`, ignore `check-sat` and `get-model` queries") + .validator(bool_validator) + .value_name(bool_format) + .default_value("on") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("timeout") + .long("--timeout") + .short("-t") + .help("sets a timeout in seconds, `0` for none") + .validator(int_validator) + .value_name("int") + .default_value("0") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("split") + .long("--split") + .help("reason on each negative clause separately") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("split_step") + .long("--split_step") + .help("pause between negative clauses in split mode") + .validator(bool_validator) + .value_name(bool_format) + .default_value("off") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("term_simpl") + .long("--term_simpl") + .help("level of term simplification between 0 and 3") + .validator(int_validator) + .value_name("int") + .default_value("1") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ).arg( + Arg::with_name("check_simpl") + .long("--check_simpl") + .help("if true, check all simplifications") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()) + .hidden(true), + ) + } + /// Add args related to result checking. + pub fn add_check_args(app: App, mut order: usize) -> App { + let mut order = || { + order += 1; + order + }; + + app.arg( + Arg::with_name("check") + .long("--check") + .help("checks a model for the input system (does not run inference)") + .value_name("FILE") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ).arg( + Arg::with_name("check_eld") + .long("--check_eld") + .help("if `check` is active, expect eldarica-style result") + .validator(bool_validator) + .value_name(bool_format) + .default_value("no") + .takes_value(true) + .number_of_values(1) + .display_order(order()), + ) + } +} /// Contains some styles for coloring. #[derive(Debug, Clone)] pub struct Styles { - /// Emphasis style. - emph: Style, - /// Happy style. - hap: Style, - /// Sad style. - sad: Style, - /// Bad style. - bad: Style, + /// Emphasis style. + emph: Style, + /// Happy style. + hap: Style, + /// Sad style. + sad: Style, + /// Bad style. + bad: Style, } impl Default for Styles { - fn default() -> Self { Styles::new(true) } + fn default() -> Self { + Styles::new(true) + } } impl ColorExt for Styles { - fn styles(& self) -> & Styles { self } + fn styles(&self) -> &Styles { + self + } } impl Styles { - /// Creates some styles. - pub fn new(colored: bool) -> Self { - Styles { - emph: if colored { - Style::new().bold() - } else { Style::new() }, - hap: if colored { - Colour::Green.normal().bold() - } else { Style::new() }, - sad: if colored { - Colour::Yellow.normal().bold() - } else { Style::new() }, - bad: if colored { - Colour::Red.normal().bold() - } else { Style::new() }, + /// Creates some styles. + pub fn new(colored: bool) -> Self { + Styles { + emph: if colored { + Style::new().bold() + } else { + Style::new() + }, + hap: if colored { + Colour::Green.normal().bold() + } else { + Style::new() + }, + sad: if colored { + Colour::Yellow.normal().bold() + } else { + Style::new() + }, + bad: if colored { + Colour::Red.normal().bold() + } else { + Style::new() + }, + } } - } } - - - - - /// Can color things. pub trait ColorExt { - /// The styles in the colorizer: emph, happy, sad, and bad. - fn styles(& self) -> & Styles ; - /// String emphasis. - #[inline] - fn emph>(& self, s: S) -> String { - format!("{}", self.styles().emph.paint(s.as_ref())) - } - /// Happy string. - #[inline] - fn happy>(& self, s: S) -> String { - format!("{}", self.styles().hap.paint(s.as_ref())) - } - /// Sad string. - #[inline] - fn sad>(& self, s: S) -> String { - format!("{}", self.styles().sad.paint(s.as_ref())) - } - /// Bad string. - #[inline] - fn bad>(& self, s: S) -> String { - format!("{}", self.styles().bad.paint(s.as_ref())) - } + /// The styles in the colorizer: emph, happy, sad, and bad. + fn styles(&self) -> &Styles; + /// String emphasis. + #[inline] + fn emph>(&self, s: S) -> String { + format!("{}", self.styles().emph.paint(s.as_ref())) + } + /// Happy string. + #[inline] + fn happy>(&self, s: S) -> String { + format!("{}", self.styles().hap.paint(s.as_ref())) + } + /// Sad string. + #[inline] + fn sad>(&self, s: S) -> String { + format!("{}", self.styles().sad.paint(s.as_ref())) + } + /// Bad string. + #[inline] + fn bad>(&self, s: S) -> String { + format!("{}", self.styles().bad.paint(s.as_ref())) + } } - - - - /// Format for booleans. -pub static bool_format: & str = "on/true|no/off/false" ; +pub static bool_format: &str = "on/true|no/off/false"; /// Boolean of a string. -pub fn bool_of_str(s: & str) -> Option { - match & s as & str { - "on" | "true" => Some(true), - "no" | "off" | "false" => Some(false), - _ => None, - } +pub fn bool_of_str(s: &str) -> Option { + match &s as &str { + "on" | "true" => Some(true), + "no" | "off" | "false" => Some(false), + _ => None, + } } /// Boolean of some matches. /// /// Assumes a default is provided and the input has been validated with /// `bool_validator`. -pub fn bool_of_matches(matches: & Matches, key: & str) -> bool { - matches.value_of(key).and_then( - |s| bool_of_str(& s) - ).expect( - "failed to retrieve boolean argument" - ) +pub fn bool_of_matches(matches: &Matches, key: &str) -> bool { + matches + .value_of(key) + .and_then(|s| bool_of_str(&s)) + .expect("failed to retrieve boolean argument") } /// Integer of some matches. /// /// Assumes a default is provided and the input has been validated with /// `bool_validator`. -pub fn int_of_matches(matches: & Matches, key: & str) -> usize { - use std::str::FromStr ; - matches.value_of(key).map( - |s| usize::from_str(& s) - ).expect( - "failed to retrieve integer argument" - ).expect( - "failed to retrieve integer argument" - ) +pub fn int_of_matches(matches: &Matches, key: &str) -> usize { + use std::str::FromStr; + matches + .value_of(key) + .map(|s| usize::from_str(&s)) + .expect("failed to retrieve integer argument") + .expect("failed to retrieve integer argument") } /// Validates integer input. #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] pub fn int_validator(s: String) -> Result<(), String> { - use std::str::FromStr ; - match usize::from_str(& s) { - Ok(_) => Ok(()), - Err(_) => Err( - format!("expected an integer, got `{}`", s) - ), - } + use std::str::FromStr; + match usize::from_str(&s) { + Ok(_) => Ok(()), + Err(_) => Err(format!("expected an integer, got `{}`", s)), + } } /// Validates integer input between some bounds. #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] -pub fn bounded_int_validator( - s: String, lo: usize, hi: usize -) -> Result<(), String> { - use std::str::FromStr ; - match usize::from_str(& s) { - Ok(val) => if lo <= val && val <= hi { - Ok(()) - } else { - Err( - format!( - "expected a value between {} and {}, got `{}`", lo , hi, val - ) - ) - }, - Err(_) => Err( - format!("expected an integer, got `{}`", s) - ), - } +pub fn bounded_int_validator(s: String, lo: usize, hi: usize) -> Result<(), String> { + use std::str::FromStr; + match usize::from_str(&s) { + Ok(val) => if lo <= val && val <= hi { + Ok(()) + } else { + Err(format!( + "expected a value between {} and {}, got `{}`", + lo, hi, val + )) + }, + Err(_) => Err(format!("expected an integer, got `{}`", s)), + } } /// Validates boolean input. #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] pub fn bool_validator(s: String) -> Result<(), String> { - if bool_of_str(& s).is_some() { - Ok(()) - } else { - Err( - format!("expected `on/true` or `off/false`, got `{}`", s) - ) - } + if bool_of_str(&s).is_some() { + Ok(()) + } else { + Err(format!("expected `on/true` or `off/false`, got `{}`", s)) + } } diff --git a/src/common/consts.rs b/src/common/consts.rs index 3aa6bb88..be31d0e9 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -8,7 +8,6 @@ lazy_static! { pub static ref ten: ::common::Int = 10.into() ; } - /// Use this macro to declare keywords. /// /// Declares everything and creates a function testing if a string is a @@ -62,129 +61,123 @@ macro_rules! keys { ) ; } - - /// Values used in hoice. pub mod values { - /// Default values. - pub mod default { - /// Generate unsat cores? - pub const unsat_cores: bool = false ; - /// Generate proofs? - pub const proofs: bool = false ; - /// Print success? - pub const print_success: bool = false ; - } + /// Default values. + pub mod default { + /// Generate unsat cores? + pub const unsat_cores: bool = false; + /// Generate proofs? + pub const proofs: bool = false; + /// Print success? + pub const print_success: bool = false; + } } /// Error messages. pub mod errors { - /// Unsat core asked but not active. - pub const no_unsat_cores: & str = "\ + /// Unsat core asked but not active. + pub const no_unsat_cores: &str = "\ unsat core production is not active:\n\ consider adding `(set-option :produce-unsat-core true)`\n\ at the start of your script - " ; + "; } - - - /// Language keywords. pub mod keywords { - keys! { + keys! { - funs { - is_keyword () - } - - keys { - forall ("forall", doc = "Universal quantifier.") - exists ("exists", doc = "Existential quantifier") - let_ ("let", doc = "Let-binding keyword") - } - - doc = "Operator-related keywords." - mod op { funs { is_keyword () } keys { - distinct_ ("distinct", doc = "Distinct.") - - eq_ ("=", doc = "Equal.") - not_ ("not", doc = "Not.") - and_ ("and", doc = "And.") - or_ ("or", doc = "Or.") - impl_ ("=>", doc = "Implication.") - ite_ ("ite", doc = "If-then-else.") - - gt_ (">", doc = "Greater than.") - ge_ (">=", doc = "Greater than or equal to.") - lt_ ("<", doc = "Less than.") - le_ ("<=", doc = "Less than or equal to.") - - add_ ("+", doc = "Addition.") - sub_ ("-", doc = "Subtraction.") - mul_ ("*", doc = "Multiplication.") - div_ ("/", doc = "Division.") - idiv_ ("div", doc = "Integer division.") - mod_ ("mod", doc = "Modulo.") - rem_ ("rem", doc = "Remainder.") - - to_int_ ("to_int", doc = "Conversion from `Real` to `Int`.") - to_real_ ("to_real", doc = "Conversion from `Int` to `Real`.") - - as_ ("as", doc = "Cast operator.") - is_ ("is", doc = "Datatype tester.") - const_ ("const", doc = "Constant cast.") - - store_ ("store", doc = "Updater for arrays.") - select_ ("select", doc = "Accessor for arrays.") - - match_ ("match", doc = "Match operator.") - - lambda_ ("_", doc = "Lambda abstraction.") + forall ("forall", doc = "Universal quantifier.") + exists ("exists", doc = "Existential quantifier") + let_ ("let", doc = "Let-binding keyword") } - } - doc = "Command-related keywords." - mod cmd { - funs { - is_keyword () + doc = "Operator-related keywords." + mod op { + funs { + is_keyword () + } + + keys { + distinct_ ("distinct", doc = "Distinct.") + + eq_ ("=", doc = "Equal.") + not_ ("not", doc = "Not.") + and_ ("and", doc = "And.") + or_ ("or", doc = "Or.") + impl_ ("=>", doc = "Implication.") + ite_ ("ite", doc = "If-then-else.") + + gt_ (">", doc = "Greater than.") + ge_ (">=", doc = "Greater than or equal to.") + lt_ ("<", doc = "Less than.") + le_ ("<=", doc = "Less than or equal to.") + + add_ ("+", doc = "Addition.") + sub_ ("-", doc = "Subtraction.") + mul_ ("*", doc = "Multiplication.") + div_ ("/", doc = "Division.") + idiv_ ("div", doc = "Integer division.") + mod_ ("mod", doc = "Modulo.") + rem_ ("rem", doc = "Remainder.") + + to_int_ ("to_int", doc = "Conversion from `Real` to `Int`.") + to_real_ ("to_real", doc = "Conversion from `Int` to `Real`.") + + as_ ("as", doc = "Cast operator.") + is_ ("is", doc = "Datatype tester.") + const_ ("const", doc = "Constant cast.") + + store_ ("store", doc = "Updater for arrays.") + select_ ("select", doc = "Accessor for arrays.") + + match_ ("match", doc = "Match operator.") + + lambda_ ("_", doc = "Lambda abstraction.") + } } - keys { - dec_dtyp ("declare-datatype", doc = "Datatype declaration keyword.") - dec_dtyps ( - "declare-datatypes", doc = "Multiple datatype declaration keyword." - ) - dec_fun ("declare-fun", doc = "Predicate declaration keyword.") - def_fun ("define-fun", doc = "Function definition keyword.") - def_fun_rec ( - "define-fun-rec", - doc = "Recursive function definition keyword." - ) - def_funs_rec ( - "define-funs-rec", - doc = "Multiple recursive functions definition keyword." - ) - - assert ("assert", doc = "Assertion keyword.") - - check_sat ("check-sat", doc = "Check-sat keyword.") - get_model ("get-model", doc = "Get-model keyword.") - get_unsat_core ("get-unsat-core", doc = "Get-unsat-core keyword.") - get_proof ("get-proof", doc = "Get-proof keyword.") - - reset ("reset", doc = "Reset keyword.") - exit ("exit", doc = "Exit keyword.") + doc = "Command-related keywords." + mod cmd { + funs { + is_keyword () + } + + keys { + dec_dtyp ("declare-datatype", doc = "Datatype declaration keyword.") + dec_dtyps ( + "declare-datatypes", doc = "Multiple datatype declaration keyword." + ) + dec_fun ("declare-fun", doc = "Predicate declaration keyword.") + def_fun ("define-fun", doc = "Function definition keyword.") + def_fun_rec ( + "define-fun-rec", + doc = "Recursive function definition keyword." + ) + def_funs_rec ( + "define-funs-rec", + doc = "Multiple recursive functions definition keyword." + ) + + assert ("assert", doc = "Assertion keyword.") + + check_sat ("check-sat", doc = "Check-sat keyword.") + get_model ("get-model", doc = "Get-model keyword.") + get_unsat_core ("get-unsat-core", doc = "Get-unsat-core keyword.") + get_proof ("get-proof", doc = "Get-proof keyword.") + + reset ("reset", doc = "Reset keyword.") + exit ("exit", doc = "Exit keyword.") + } } - } - - } + } -} \ No newline at end of file +} diff --git a/src/common/macros.rs b/src/common/macros.rs index da384019..ed2f88fd 100644 --- a/src/common/macros.rs +++ b/src/common/macros.rs @@ -1,6 +1,5 @@ //! Macros. - /// If the input is an error, prints it SMT-LIB-style and panics. #[macro_export] macro_rules! expect { @@ -39,7 +38,6 @@ macro_rules! fail_with { }) ; } - /// Bails with unsat. /// /// Logs unsat (`@info`) and the input message if any (`@debug`). @@ -52,7 +50,6 @@ macro_rules! unsat { }) ; } - /// Bails with unknown. #[macro_export] macro_rules! unknown { @@ -63,8 +60,6 @@ macro_rules! unknown { }) ; } - - /// Wraps stuff in a block, usually to please borrow-checking. #[macro_export] macro_rules! scoped { @@ -85,7 +80,6 @@ macro_rules! err_chain { }) } - /// Guards something by a log level, inactive in bench mode. #[cfg(not(feature = "bench"))] #[macro_export] @@ -107,7 +101,6 @@ macro_rules! if_log { (@$flag:tt $($stuff:tt)*) => (()) ; } - /** Logging macro, inactive in bench mode. | Log level | active when... | prefix (`_` are spaces) | @@ -179,12 +172,17 @@ macro_rules! log { } #[cfg(feature = "bench")] macro_rules! log { - (|pref_of| $($stuff:tt)*) => ("") ; - (|cond_of| $($stuff:tt)*) => (false) ; - ($($stuff:tt)*) => (()) ; + (|pref_of| $($stuff:tt)*) => { + "" + }; + (|cond_of| $($stuff:tt)*) => { + false + }; + ($($stuff:tt)*) => { + () + }; } - /// Same as `log! { @verb ... }`. #[macro_export] macro_rules! log_verb { @@ -208,8 +206,6 @@ macro_rules! log_debug { ) ; } - - /// Prints a warning SMT-LIB-style. /// /// **Active in bench mode.** @@ -227,10 +223,9 @@ macro_rules! warn { }) ; } - /// Does something if not in bench mode. #[macro_export] -#[cfg(not (feature = "bench") )] +#[cfg(not(feature = "bench"))] macro_rules! if_not_bench { ( then { $($then:tt)* } else { $($else:tt)* } ) => ( $($then)* @@ -250,26 +245,23 @@ macro_rules! if_not_bench { ($($stuff:tt)*) => (()) ; } - /// Same as `if_log! { @verb ... }`. #[macro_export] macro_rules! if_verb { ($($stuff:tt)*) => ( if_log! { @verb $($stuff)* } ) ; } - /// Same as `if_log! { @debug ... }` . #[macro_export] macro_rules! if_debug { ($($stuff:tt)*) => ( if_log! { @debug $($stuff)* } ) ; } - /// Profiling macro, inactive in bench mode. /// /// If passed `self`, assumes `self` has a `_profiler` field. #[macro_export] -#[cfg( not(feature = "bench") )] +#[cfg(not(feature = "bench"))] macro_rules! profile { ( | $stuff:ident $(. $prof:ident)* | wrap $b:block $( $scope:expr ),+ $(,)* @@ -310,18 +302,23 @@ macro_rules! profile { } #[cfg(feature = "bench")] macro_rules! profile { - ( | $stuff:ident $(. $prof:ident)* | + ( | $stuff:ident $(. $prof:ident)* | wrap $b:block $( $scope:expr ),+ $(,)* - ) => ($b) ; - ( $slf:ident + ) => { + $b + }; + ( $slf:ident wrap $b:block $( $scope:expr ),+ $(,)* - ) => ($b) ; - ( $($stuff:tt)* ) => ( () ) ; + ) => { + $b + }; + ( $($stuff:tt)* ) => { + () + }; } - /// Messaging macro, compiled to nothing in bench mode. -#[cfg( not(feature = "bench") )] +#[cfg(not(feature = "bench"))] #[macro_export] macro_rules! msg { ( @$flag:tt $slf:expr => $($tt:tt)* ) => ( @@ -348,12 +345,13 @@ macro_rules! msg { ) ; } #[macro_export] -#[cfg( feature = "bench" )] +#[cfg(feature = "bench")] macro_rules! msg { - ( $($tt:tt)* ) => (()) ; + ( $($tt:tt)* ) => { + () + }; } - /// Yields the value if the type (int or bool) matches, otherwise /// `return`s `Ok(Val::N)`. /// @@ -382,85 +380,84 @@ macro_rules! msg { /// assert_eq!{ bool( none.clone() ), none } /// ``` macro_rules! try_val { - (int $e:expr) => ( - if let Some(i) = $e.to_int() ? { i } else { - return Ok( $crate::val::none($crate::term::typ::int()) ) - } - ) ; - (real $e:expr) => ( - if let Some(r) = $e.to_real() ? { r } else { - return Ok( $crate::val::none($crate::term::typ::real()) ) - } - ) ; - (bool $e:expr) => ( - if let Some(b) = $e.to_bool() ? { b } else { - return Ok( $crate::val::none($crate::term::typ::bool()) ) - } - ) ; + (int $e:expr) => { + if let Some(i) = $e.to_int()? { + i + } else { + return Ok($crate::val::none($crate::term::typ::int())); + } + }; + (real $e:expr) => { + if let Some(r) = $e.to_real()? { + r + } else { + return Ok($crate::val::none($crate::term::typ::real())); + } + }; + (bool $e:expr) => { + if let Some(b) = $e.to_bool()? { + b + } else { + return Ok($crate::val::none($crate::term::typ::bool())); + } + }; } - /// Dumps an instance if the `PreprocConf` flag says so. macro_rules! preproc_dump { - ($instance:expr => $file:expr, $blah:expr) => ( - if let Some(mut file) = conf.preproc.instance_log_file( - $file, & $instance - ) ? { - $instance.dump_as_smt2(& mut file, $blah) - } else { Ok(()) } - ) ; + ($instance:expr => $file:expr, $blah:expr) => { + if let Some(mut file) = conf.preproc.instance_log_file($file, &$instance)? { + $instance.dump_as_smt2(&mut file, $blah) + } else { + Ok(()) + } + }; } - - /// `Int` writer. #[macro_export] macro_rules! int_to_smt { - ($writer:expr, $i:expr) => ( - if $i.is_negative() { - write!($writer, "(- {})", - $i) - } else { - write!($writer, "{}", $i) - } - ) + ($writer:expr, $i:expr) => { + if $i.is_negative() { + write!($writer, "(- {})", -$i) + } else { + write!($writer, "{}", $i) + } + }; } /// `Rat` writer. #[macro_export] macro_rules! rat_to_smt { - ($writer:expr, $r:expr) => ({ - let (num, den) = ( $r.numer(), $r.denom() ) ; - debug_assert!( ! den.is_negative() ) ; - if num.is_zero() { - write!($writer, "0.0") - } else if ! num.is_negative() { - if den.is_one() { - write!($writer, "{}.0", num) - } else { - write!($writer, "(/ {} {})", num, den) - } - } else { - if den.is_one() { - write!($writer, "(- {}.0)", - num) - } else { - write!($writer, "(- (/ {} {}))", - num, den) - } - } - }) + ($writer:expr, $r:expr) => {{ + let (num, den) = ($r.numer(), $r.denom()); + debug_assert!(!den.is_negative()); + if num.is_zero() { + write!($writer, "0.0") + } else if !num.is_negative() { + if den.is_one() { + write!($writer, "{}.0", num) + } else { + write!($writer, "(/ {} {})", num, den) + } + } else { + if den.is_one() { + write!($writer, "(- {}.0)", -num) + } else { + write!($writer, "(- (/ {} {}))", -num, den) + } + } + }}; } - - - - /// Test macros #[cfg(test)] #[macro_use] mod test { - /// Turns a sequence of values into a `Cex` (`VarMap`). - #[macro_export] - macro_rules! model { + /// Turns a sequence of values into a `Cex` (`VarMap`). + #[macro_export] + macro_rules! model { ( $($values:expr),* ) => ( $crate::common::VarMap::of( vec![ $( $values ),* ] @@ -468,43 +465,45 @@ mod test { ) ; } - /// Checks that the result of an evaluation yields the correct value. - /// - /// Prints information before the check. - #[macro_export] - macro_rules! assert_eval { - ( int $model:expr => $expr:expr, $value:expr ) => ({ - let res = $expr.eval(& $model).unwrap().to_int().unwrap().unwrap() ; - println!( - "{} evaluated with {} is {}, expecting {}", $expr, $model, res, $value - ) ; - assert_eq!( res, $value.into() ) - }) ; - - ( real $model:expr => $expr:expr, $value:expr ) => ({ - let res = $expr.eval(& $model).unwrap().to_real().unwrap().unwrap() ; - println!( - "{} evaluated with {} is {}, expecting {}", $expr, $model, res, $value - ) ; - assert_eq!( res, rat_of_float($value) ) - }) ; - - ( bool not $model:expr => $expr:expr ) => ({ - let res = $expr.eval(& $model).unwrap().to_bool().unwrap().unwrap() ; - println!( - "{} evaluated with {} is {}, expecting false", $expr, $model, res - ) ; - assert!( ! res ) - }) ; - - ( bool $model:expr => $expr:expr ) => ({ - let res = $expr.eval(& $model).unwrap().to_bool().unwrap().unwrap() ; - println!( - "{} evaluated with {} is {}, expecting true", $expr, $model, res - ) ; - assert!( res ) - }) ; - } + /// Checks that the result of an evaluation yields the correct value. + /// + /// Prints information before the check. + #[macro_export] + macro_rules! assert_eval { + ( int $model:expr => $expr:expr, $value:expr ) => {{ + let res = $expr.eval(&$model).unwrap().to_int().unwrap().unwrap(); + println!( + "{} evaluated with {} is {}, expecting {}", + $expr, $model, res, $value + ); + assert_eq!(res, $value.into()) + }}; + + ( real $model:expr => $expr:expr, $value:expr ) => {{ + let res = $expr.eval(&$model).unwrap().to_real().unwrap().unwrap(); + println!( + "{} evaluated with {} is {}, expecting {}", + $expr, $model, res, $value + ); + assert_eq!(res, rat_of_float($value)) + }}; + + ( bool not $model:expr => $expr:expr ) => {{ + let res = $expr.eval(&$model).unwrap().to_bool().unwrap().unwrap(); + println!( + "{} evaluated with {} is {}, expecting false", + $expr, $model, res + ); + assert!(!res) + }}; + + ( bool $model:expr => $expr:expr ) => {{ + let res = $expr.eval(&$model).unwrap().to_bool().unwrap().unwrap(); + println!( + "{} evaluated with {} is {}, expecting true", + $expr, $model, res + ); + assert!(res) + }}; + } } - - diff --git a/src/common/mod.rs b/src/common/mod.rs index 1bb88a94..555ad50f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,68 +1,62 @@ //! Base types and functions. -pub use std::io::{ Read, Write } ; -pub use std::io::Result as IoRes ; -pub use std::sync::{ Arc, RwLock } ; -pub use std::sync::mpsc::{ Receiver, Sender } ; -pub use std::collections::{ BTreeMap, BTreeSet } ; +pub use std::{ + collections::{BTreeMap, BTreeSet}, + io::{ + Result as IoRes, {Read, Write}, + }, + sync::{ + mpsc::{Receiver, Sender}, + Arc, RwLock, + }, +}; -pub use rand::prng::XorShiftRng as Rng ; +pub use rand::prng::XorShiftRng as Rng; -pub use mylib::common::hash::* ; +pub use mylib::common::hash::*; -pub use hashconsing::{ HashConsign, HashConsed } ; -pub use hashconsing::coll::* ; +pub use hashconsing::{coll::*, HashConsed, HashConsign}; -pub use rsmt2::{ SmtRes, Solver } ; -pub use rsmt2::actlit::Actlit ; +pub use rsmt2::{actlit::Actlit, print::Expr2Smt, SmtRes, Solver}; -pub use num::{ Zero, One, Signed } ; +pub use num::{One, Signed, Zero}; -pub use either::Either ; +pub use either::Either; -pub use errors::* ; -pub use term ; -pub use term::{ - RTerm, Term, TTerm, - TTermSet, TTerms, - Op, Typ, Quant, - typ, -} ; +pub use errors::*; +pub use term; +pub use term::{typ, Op, Quant, RTerm, TTerm, TTermSet, TTerms, Term, Typ}; -pub use dtyp ; -pub use dtyp::DTyp ; +pub use dtyp; +pub use dtyp::DTyp; -pub use val ; -pub use val::Val ; +pub use val; +pub use val::Val; -pub use fun ; -pub use fun::Fun ; +pub use fun; +pub use fun::Fun; -pub use var_to ; -pub use var_to::{ - VarVals, VarTerms, - vals::SubsumeExt, -} ; +pub use var_to; +pub use var_to::{vals::SubsumeExt, VarTerms, VarVals}; -pub use instance::Instance ; -pub use common::consts::keywords ; +pub use common::consts::keywords; +pub use instance::Instance; -mod wrappers ; +mod wrappers; #[macro_use] -pub mod macros ; -pub mod config ; +pub mod macros; +pub mod config; #[macro_use] -pub mod msg ; -pub mod consts ; -pub mod profiling ; -pub mod smt ; - -pub use self::config::* ; -pub use self::profiling::{ Profiler, CanPrint } ; -pub use self::wrappers::* ; +pub mod msg; +pub mod consts; +pub mod profiling; +pub mod smt; +pub use self::config::*; +pub use self::profiling::{CanPrint, Profiler}; +pub use self::wrappers::*; lazy_static!{ /// Configuration from clap. @@ -72,68 +66,64 @@ lazy_static!{ pub static ref version: & 'static str = & version_string ; } - - - // |===| Helpers. /// Stdout. -pub use ::std::io::stdout ; +pub use std::io::stdout; /// Prints the stats if asked. Does nothing in bench mode. #[cfg(feature = "bench")] -pub fn print_stats(_: & 'static str, _: Profiler) {} +pub fn print_stats(_: &'static str, _: Profiler) {} /// Prints the stats if asked. Does nothing in bench mode. -#[cfg( not(feature = "bench") )] -pub fn print_stats(name: & str, profiler: Profiler) { - if conf.stats { - let others = profiler.drain_others() ; - println!() ; - profiler.print( name, "", & [ "data" ] ) ; - println!() ; - for (name, other) in others { - print_stats(& name, other) +#[cfg(not(feature = "bench"))] +pub fn print_stats(name: &str, profiler: Profiler) { + if conf.stats { + let others = profiler.drain_others(); + println!(); + profiler.print(name, "", &["data"]); + println!(); + for (name, other) in others { + print_stats(&name, other) + } } - } } /// Lock corrupted error. pub fn corrupted_err(_: T) -> Error { - "[bug] lock on learning data is corrupted...".into() + "[bug] lock on learning data is corrupted...".into() } /// Notifies the user and reads a line from stdin. -pub fn pause(s: & str, _profiler: & Profiler) { - let mut dummy = String::new() ; - println!() ; - println!( "; {} {}...", conf.emph("press return"), s ) ; - let _ = profile!( +pub fn pause(s: &str, _profiler: &Profiler) { + let mut dummy = String::new(); + println!(); + println!("; {} {}...", conf.emph("press return"), s); + let _ = profile!( |_profiler| wrap { ::std::io::stdin().read_line(& mut dummy) } "waiting for user input" - ) ; + ); } /// Notifies the user through a message and reads a line from stdin. -pub fn pause_msg(core: & msg::MsgCore, s: & str) { - let mut dummy = String::new() ; - let _ = core.msg( - format!( "; {} {}...", conf.emph("press return"), s ) - ) ; - let _ = ::std::io::stdin().read_line(& mut dummy) ; +pub fn pause_msg(core: &msg::MsgCore, s: &str) { + let mut dummy = String::new(); + let _ = core.msg(format!("; {} {}...", conf.emph("press return"), s)); + let _ = ::std::io::stdin().read_line(&mut dummy); } /// Identity function. -pub fn identity(t: T) -> T { t } +pub fn identity(t: T) -> T { + t +} /// Creates a directory if it doesn't exist. pub fn mk_dir>(path: P) -> Res<()> { - use std::fs::DirBuilder ; - DirBuilder::new().recursive(true).create(path) ? ; - Ok(()) + use std::fs::DirBuilder; + DirBuilder::new().recursive(true).create(path)?; + Ok(()) } - /// Compares two data metrics. /// /// Takes the amount of classified and unknown data from two data collections @@ -172,192 +162,172 @@ pub fn mk_dir>(path: P) -> Res<()> { /// assert_eq! { cmp(15, 70, 20, 70), Less } /// ``` pub fn cmp_data_metrics( - classified_1: usize, - unknown_1: usize, - classified_2: usize, - unknown_2: usize, + classified_1: usize, + unknown_1: usize, + classified_2: usize, + unknown_2: usize, ) -> ::std::cmp::Ordering { - use std::cmp::Ordering::* ; + use std::cmp::Ordering::*; - match (unknown_1 == 0, unknown_2 == 0) { - (true, false) => Greater, - (false, true) => Less, + match (unknown_1 == 0, unknown_2 == 0) { + (true, false) => Greater, + (false, true) => Less, - (true, true) => classified_1.cmp(& classified_2), + (true, true) => classified_1.cmp(&classified_2), - (false, false) => match (classified_1 == 0, classified_2 == 0) { + (false, false) => match (classified_1 == 0, classified_2 == 0) { + (true, false) => Less, + (false, true) => Greater, - (true, false) => Less, - (false, true) => Greater, + (true, true) => unknown_1.cmp(&unknown_2), - (true, true) => unknown_1.cmp(& unknown_2), + (false, false) => match (classified_1.cmp(&classified_2), unknown_1.cmp(&unknown_2)) { + (Greater, Greater) => (classified_1 - classified_2).cmp(&(unknown_1 - unknown_2)), + (Greater, _) | (Equal, Less) => Greater, - (false, false) => match ( - classified_1.cmp(& classified_2), unknown_1.cmp(& unknown_2) - ) { + (Less, Less) => (classified_2 - classified_1) + .cmp(&(unknown_2 - unknown_1)) + .reverse(), + (Less, _) | (Equal, Greater) => Less, - (Greater, Greater) => (classified_1 - classified_2).cmp( - & (unknown_1 - unknown_2) - ), - (Greater, _) | - (Equal, Less) => Greater, - - (Less, Less) => (classified_2 - classified_1).cmp( - & (unknown_2 - unknown_1) - ).reverse(), - (Less, _) | - (Equal, Greater) => Less, - - (Equal, Equal) => Equal, - - }, - - }, - } + (Equal, Equal) => Equal, + }, + }, + } } - - - - // |===| Type and traits aliases. /// Integers. -pub type Int = ::num::BigInt ; +pub type Int = ::num::BigInt; /// Rationals. -pub type Rat = ::num::BigRational ; +pub type Rat = ::num::BigRational; /// Converts a float into a rational. pub fn rat_of_float(f: f64) -> Rat { - Rat::from_float(f).expect( - "can't construct a rational from NaN" - ) + Rat::from_float(f).expect("can't construct a rational from NaN") } /// A set of terms. -pub type TermSet = HConSet ; +pub type TermSet = HConSet; /// A map from terms to stuff. -pub type TermMap = HConMap ; +pub type TermMap = HConMap; /// A set of types. -pub type TypSet = HConSet ; +pub type TypSet = HConSet; /// A map from terms to stuff. -pub type TypMap = HConMap ; +pub type TypMap = HConMap; /// A signature. -pub type Sig = VarMap ; +pub type Sig = VarMap; /// A predicate application. -pub type PredApp = (PrdIdx, VarTerms) ; +pub type PredApp = (PrdIdx, VarTerms); /// Some predicate applications. -pub type PredApps = PrdHMap< var_to::terms::VarTermsSet > ; +pub type PredApps = PrdHMap; /// Predicate application alias type extension. pub trait PredAppsExt { - /// Insert a predicate application. Returns true if the application is new. - fn insert_pred_app(& mut self, PrdIdx, VarTerms) -> bool ; + /// Insert a predicate application. Returns true if the application is new. + fn insert_pred_app(&mut self, PrdIdx, VarTerms) -> bool; } impl PredAppsExt for PredApps { - fn insert_pred_app(& mut self, pred: PrdIdx, args: VarTerms) -> bool { - self.entry(pred).or_insert_with( - || var_to::terms::VarTermsSet::with_capacity(4) - ).insert(args) - } + fn insert_pred_app(&mut self, pred: PrdIdx, args: VarTerms) -> bool { + self.entry(pred) + .or_insert_with(|| var_to::terms::VarTermsSet::with_capacity(4)) + .insert(args) + } } /// Predicate informations. -pub type PrdInfos = PrdMap<::instance::info::PrdInfo> ; +pub type PrdInfos = PrdMap<::instance::info::PrdInfo>; /// Variable informations. -pub type VarInfos = VarMap<::instance::info::VarInfo> ; +pub type VarInfos = VarMap<::instance::info::VarInfo>; /// Maps predicates to optional terms. -pub type Candidates = PrdMap< Option > ; +pub type Candidates = PrdMap>; unsafe impl Send for PrdMap {} - /// Teaching result. pub enum TeachRes { - /// A model. - Model(Candidates), - /// An unsat result. - Unsat( ::unsat_core::UnsatRes ), + /// A model. + Model(Candidates), + /// An unsat result. + Unsat(::unsat_core::UnsatRes), } - /// Quantified variables for a top term. -pub type Quantfed = VarHMap ; +pub type Quantfed = VarHMap; /// Dnf definition for a predicate. -pub type Dnf = Vec< (Quantfed, TTermSet) > ; +pub type Dnf = Vec<(Quantfed, TTermSet)>; /// Dnf definition for a predicate, reference version. -pub type DnfRef<'a> = & 'a [ (Quantfed, TTermSet) ] ; +pub type DnfRef<'a> = &'a [(Quantfed, TTermSet)]; /// Associates predicates to some quantified variables and some top terms. -pub type Model = Vec< (PrdIdx, TTerms) > ; +pub type Model = Vec<(PrdIdx, TTerms)>; /// Reference version of a model. -pub type ModelRef<'a> = & 'a [ (PrdIdx, TTerms) ] ; +pub type ModelRef<'a> = &'a [(PrdIdx, TTerms)]; /// A conjunction of candidates. -pub type ConjCandidates = PrdHMap< Vec > ; +pub type ConjCandidates = PrdHMap>; /// A model where definitions are conjunctions. -pub type ConjModel = Vec< Vec<(PrdIdx, Vec)> > ; +pub type ConjModel = Vec)>>; /// A model of conjunctions, reference version. -pub type ConjModelRef<'a> = & 'a [ Vec<(PrdIdx, Vec)> ] ; +pub type ConjModelRef<'a> = &'a [Vec<(PrdIdx, Vec)>]; /// A model or unsat. #[derive(Clone, PartialEq, Eq)] pub enum MaybeModel { - /// Unsat result. - Unsat, - /// A model. - Model(M), + /// Unsat result. + Unsat, + /// A model. + Model(M), } impl MaybeModel { - /// True if unsat. - pub fn is_unsat(& self) -> bool { - * self == MaybeModel::Unsat - } - /// Turns itself to an option. - pub fn into_option(self) -> Option { - if let MaybeModel::Model(model) = self { - Some(model) - } else { - None + /// True if unsat. + pub fn is_unsat(&self) -> bool { + *self == MaybeModel::Unsat + } + /// Turns itself to an option. + pub fn into_option(self) -> Option { + if let MaybeModel::Model(model) = self { + Some(model) + } else { + None + } } - } } /// Indicates a bias in a counterexample for some clause. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Bias { - /// Left bias: the whole LHS of the clause should be considered positive. - Lft, - /// Right bias: the RHS should be considered negative. - Rgt, - /// Right bias: the RHS should be considered negative, and the whole LHS - /// should be considered positive **except** for the predicate application - /// mentioned. - NuRgt(PrdIdx, VarTerms), - /// No bias. - Non, + /// Left bias: the whole LHS of the clause should be considered positive. + Lft, + /// Right bias: the RHS should be considered negative. + Rgt, + /// Right bias: the RHS should be considered negative, and the whole LHS + /// should be considered positive **except** for the predicate application + /// mentioned. + NuRgt(PrdIdx, VarTerms), + /// No bias. + Non, } impl Bias { - /// True if `Non`. - pub fn is_none(& self) -> bool { - * self == Bias::Non - } + /// True if `Non`. + pub fn is_none(&self) -> bool { + *self == Bias::Non + } - /// Pretty string representation. - pub fn to_string(& self, instance: & Instance) -> String { - match * self { - Bias::NuRgt(pred, ref args) => format!( - "right({} {})", instance[pred], args - ), - Bias::Lft => "left".into(), - Bias::Rgt => "right".into(), - Bias::Non => "none".into(), + /// Pretty string representation. + pub fn to_string(&self, instance: &Instance) -> String { + match *self { + Bias::NuRgt(pred, ref args) => format!("right({} {})", instance[pred], args), + Bias::Lft => "left".into(), + Bias::Rgt => "right".into(), + Bias::Non => "none".into(), + } } - } } impl_fmt! { Bias(self, fmt) { @@ -371,195 +341,207 @@ impl_fmt! { } /// Alias type for a counterexample for a clause. -pub type Cex = var_to::vals::RVarVals ; +pub type Cex = var_to::vals::RVarVals; /// Alias type for a counterexample for a clause. -pub type BCex = ( Cex, Bias ) ; +pub type BCex = (Cex, Bias); /// Alias type for a counterexample for a sequence of clauses. -pub type Cexs = ClsHMap< Vec > ; +pub type Cexs = ClsHMap>; /// Signature trait, for polymorphic term insertion. #[cfg_attr(feature = "cargo-clippy", allow(len_without_is_empty))] pub trait Signature { - /// Type of a variable. - fn get(& self, VarIdx) -> Typ ; - /// Length of the signature. - fn len(& self) -> usize ; -} -impl Signature for VarMap< - ::instance::info::VarInfo -> { - fn len(& self) -> usize { VarMap::len(self) } - fn get(& self, var: VarIdx) -> Typ { - self[var].typ.clone() - } + /// Type of a variable. + fn get(&self, VarIdx) -> Typ; + /// Length of the signature. + fn len(&self) -> usize; +} +impl Signature for VarMap<::instance::info::VarInfo> { + fn len(&self) -> usize { + VarMap::len(self) + } + fn get(&self, var: VarIdx) -> Typ { + self[var].typ.clone() + } } impl Signature for VarMap { - fn len(& self) -> usize { VarMap::len(self) } - fn get(& self, var: VarIdx) -> Typ { - self[var].clone() - } + fn len(&self) -> usize { + VarMap::len(self) + } + fn get(&self, var: VarIdx) -> Typ { + self[var].clone() + } } - /// Implemented by types lending themselves to evaluation. #[cfg_attr(feature = "cargo-clippy", allow(len_without_is_empty))] pub trait Evaluator { - /// Retrieves the value associated with a variable. - fn get(& self, var: VarIdx) -> & Val ; - /// Number of variables the evaluator supports. - fn len(& self) -> usize ; - /// Prints itself (for debug). - fn print(& self) ; + /// Retrieves the value associated with a variable. + fn get(&self, var: VarIdx) -> &Val; + /// Number of variables the evaluator supports. + fn len(&self) -> usize; + /// Prints itself (for debug). + fn print(&self); } impl Evaluator for VarMap { - #[inline] - fn get(& self, var: VarIdx) -> & Val { - & self[var] - } - #[inline] - fn len(& self) -> usize { VarMap::len(self) } - fn print(& self) { - println!("varmap:") ; - print!(" ") ; - for (var, val) in self.index_iter() { - print!("{} -> {}, ", var, val) - } - println!() - } + #[inline] + fn get(&self, var: VarIdx) -> &Val { + &self[var] + } + #[inline] + fn len(&self) -> usize { + VarMap::len(self) + } + fn print(&self) { + println!("varmap:"); + print!(" "); + for (var, val) in self.index_iter() { + print!("{} -> {}, ", var, val) + } + println!() + } } impl Evaluator for () { - #[inline] - fn get(& self, _: VarIdx) -> & Val { - panic!("trying actual evaluation with unit") - } - #[inline] - fn len(& self) -> usize { 0 } - fn print(& self) { println!("()") } + #[inline] + fn get(&self, _: VarIdx) -> &Val { + panic!("trying actual evaluation with unit") + } + #[inline] + fn len(&self) -> usize { + 0 + } + fn print(&self) { + println!("()") + } } /// This implements a redirection `(map, vals)`, where a variable `var` from /// the term evaluated is evaluated to `vals[ map[var] ]`. -impl<'a, E> Evaluator for (& 'a VarMap<(VarIdx, Typ)>, & 'a E) -where E: Evaluator { - #[inline] - fn get(& self, var: VarIdx) -> & Val { - self.1.get( self.0[var].0 ) - } - #[inline] - fn len(& self) -> usize { self.0.len() } - fn print(& self) { - println!("varmap<(varidx, typ)>") ; - print!(" ") ; - for (v1, (v2, _)) in self.0.index_iter() { - print!("{} -> {}", v1, v2) - } - println!() ; - self.1.print() - } +impl<'a, E> Evaluator for (&'a VarMap<(VarIdx, Typ)>, &'a E) +where + E: Evaluator, +{ + #[inline] + fn get(&self, var: VarIdx) -> &Val { + self.1.get(self.0[var].0) + } + #[inline] + fn len(&self) -> usize { + self.0.len() + } + fn print(&self) { + println!("varmap<(varidx, typ)>"); + print!(" "); + for (v1, (v2, _)) in self.0.index_iter() { + print!("{} -> {}", v1, v2) + } + println!(); + self.1.print() + } } - - - - - - - - /// Something that can be evaluated to a boolean. pub trait CanBEvaled: ::std::fmt::Display { - /// Evaluates self. - fn evaluate(& self, & E) -> Res< Option > - where E: Evaluator ; + /// Evaluates self. + fn evaluate(&self, &E) -> Res> + where + E: Evaluator; } impl CanBEvaled for Term { - fn evaluate(& self, args: & E) -> Res< Option > - where E: Evaluator { - self.bool_eval(args) - } + fn evaluate(&self, args: &E) -> Res> + where + E: Evaluator, + { + self.bool_eval(args) + } } - /// Information returned by /// [`RedStrat`](../instance/preproc/trait.RedStrat.html)s and /// [`SolverRedStrat`](../instance/preproc/trait.SolverRedStrat.html)s. #[must_use] #[derive(Debug, Default)] pub struct RedInfo { - /// Number of predicates eliminated. - pub preds: usize, - /// Number of clauses removed. - pub clauses_rmed: usize, - /// Number of clauses created. - pub clauses_added: usize, - /// Number of arguments removed. - pub args_rmed: usize, + /// Number of predicates eliminated. + pub preds: usize, + /// Number of clauses removed. + pub clauses_rmed: usize, + /// Number of clauses created. + pub clauses_added: usize, + /// Number of arguments removed. + pub args_rmed: usize, } impl RedInfo { - /// Basic constructor. - pub fn new() -> Self { - RedInfo { - preds: 0, clauses_rmed: 0, clauses_added: 0, args_rmed: 0 + /// Basic constructor. + pub fn new() -> Self { + RedInfo { + preds: 0, + clauses_rmed: 0, + clauses_added: 0, + args_rmed: 0, + } + } + /// Constructor from the number of predicates eliminated. + pub fn of_preds(preds: usize) -> Self { + let mut slf = Self::new(); + slf.preds += preds; + slf + } + /// Constructor from the number of clauses removed. + pub fn of_clauses_rmed(clauses_rmed: usize) -> Self { + let mut slf = Self::new(); + slf.clauses_rmed += clauses_rmed; + slf + } + /// Constructor from the number of clauses added. + pub fn of_clauses_added(clauses_added: usize) -> Self { + let mut slf = Self::new(); + slf.clauses_added += clauses_added; + slf + } + /// True if one or more fields are non-zero. + pub fn non_zero(&self) -> bool { + self.preds > 0 || self.clauses_rmed > 0 || self.clauses_added > 0 || self.args_rmed > 0 } - } - /// Constructor from the number of predicates eliminated. - pub fn of_preds(preds: usize) -> Self { - let mut slf = Self::new() ; - slf.preds += preds ; - slf - } - /// Constructor from the number of clauses removed. - pub fn of_clauses_rmed(clauses_rmed: usize) -> Self { - let mut slf = Self::new() ; - slf.clauses_rmed += clauses_rmed ; - slf - } - /// Constructor from the number of clauses added. - pub fn of_clauses_added(clauses_added: usize) -> Self { - let mut slf = Self::new() ; - slf.clauses_added += clauses_added ; - slf - } - /// True if one or more fields are non-zero. - pub fn non_zero(& self) -> bool { - self.preds > 0 - || self.clauses_rmed > 0 - || self.clauses_added > 0 - || self.args_rmed > 0 - } - /// True if `clause_added > clause_rmed`. - pub fn added_clauses(& self) -> bool { - self.clauses_added > self.clauses_rmed - } - /// Clauses added minus clauses removed. - /// - /// Zero if clauses removed greater than clauses added. - pub fn clause_diff(& self) -> usize { - if self.clauses_added > self.clauses_rmed { - self.clauses_added - self.clauses_rmed - } else { - 0 + /// True if `clause_added > clause_rmed`. + pub fn added_clauses(&self) -> bool { + self.clauses_added > self.clauses_rmed + } + /// Clauses added minus clauses removed. + /// + /// Zero if clauses removed greater than clauses added. + pub fn clause_diff(&self) -> usize { + if self.clauses_added > self.clauses_rmed { + self.clauses_added - self.clauses_rmed + } else { + 0 + } } - } } impl From<(usize, usize, usize)> for RedInfo { - fn from( - (preds, clauses_rmed, clauses_added): (usize, usize, usize) - ) -> RedInfo { - RedInfo { preds, clauses_rmed, clauses_added, args_rmed: 0 } - } + fn from((preds, clauses_rmed, clauses_added): (usize, usize, usize)) -> RedInfo { + RedInfo { + preds, + clauses_rmed, + clauses_added, + args_rmed: 0, + } + } } impl ::std::ops::AddAssign for RedInfo { - fn add_assign( - & mut self, RedInfo { - preds, clauses_rmed, clauses_added, args_rmed - }: Self - ) { - self.preds += preds ; - self.clauses_rmed += clauses_rmed ; - self.clauses_added += clauses_added ; - self.args_rmed += args_rmed - } + fn add_assign( + &mut self, + RedInfo { + preds, + clauses_rmed, + clauses_added, + args_rmed, + }: Self, + ) { + self.preds += preds; + self.clauses_rmed += clauses_rmed; + self.clauses_added += clauses_added; + self.args_rmed += args_rmed + } } impl_fmt!{ RedInfo(self, fmt) { @@ -571,141 +553,123 @@ impl_fmt!{ } } - - - - - // |===| Helper traits. - /// Provides user-friendly formatting: `pebcak_fmt`. pub trait PebcakFmt<'a> { - /// Info needed. - type Info ; - /// User-friendly formatting. - fn pebcak_io_fmt( - & self, & mut Writer, Self::Info - ) -> IoRes<()> ; - /// Error specific to the implementor. - fn pebcak_err(& self) -> ErrorKind ; - /// User-friendly formatting. - fn pebcak_fmt( - & self, w: & mut Writer, i: Self::Info - ) -> Res<()> { - self.pebcak_io_fmt(w, i).chain_err( - || self.pebcak_err() - ) - } - /// Formatted string. - fn string_do(& self, i: Self::Info, f: F) -> Res - where F: FnOnce(& str) -> T { - let mut v = vec![] ; - self.pebcak_fmt(& mut v, i) ? ; - ::std::str::from_utf8(& v).chain_err( - || self.pebcak_err() - ).map( - |s| f(s) - ) - } - /// Formatted string. - fn to_string_info(& self, i: Self::Info) -> Res { - self.string_do(i, |s| s.to_string()) - } + /// Info needed. + type Info; + /// User-friendly formatting. + fn pebcak_io_fmt(&self, &mut Writer, Self::Info) -> IoRes<()>; + /// Error specific to the implementor. + fn pebcak_err(&self) -> ErrorKind; + /// User-friendly formatting. + fn pebcak_fmt(&self, w: &mut Writer, i: Self::Info) -> Res<()> { + self.pebcak_io_fmt(w, i).chain_err(|| self.pebcak_err()) + } + /// Formatted string. + fn string_do(&self, i: Self::Info, f: F) -> Res + where + F: FnOnce(&str) -> T, + { + let mut v = vec![]; + self.pebcak_fmt(&mut v, i)?; + ::std::str::from_utf8(&v) + .chain_err(|| self.pebcak_err()) + .map(|s| f(s)) + } + /// Formatted string. + fn to_string_info(&self, i: Self::Info) -> Res { + self.string_do(i, |s| s.to_string()) + } } - /// Indexed by `VarIdx`. pub trait VarIndexed { - /// Gets the value associated with a variable. - fn var_get(& self, var: VarIdx) -> Option ; + /// Gets the value associated with a variable. + fn var_get(&self, var: VarIdx) -> Option; } impl VarIndexed for VarMap { - fn var_get(& self, var: VarIdx) -> Option { - if var < self.len() { - Some( self[var].clone() ) - } else { - None + fn var_get(&self, var: VarIdx) -> Option { + if var < self.len() { + Some(self[var].clone()) + } else { + None + } } - } } impl VarIndexed for VarTerms { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - if var < self.len() { - Some( self[var].clone() ) - } else { - None + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + if var < self.len() { + Some(self[var].clone()) + } else { + None + } } - } } impl VarIndexed for VarHMap { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - self.get(& var).cloned() - } + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + self.get(&var).cloned() + } } impl VarIndexed for VarMap<(VarIdx, Typ)> { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - if var < self.len() { - Some( term::var( self[var].0, self[var].1.clone() ) ) - } else { - None + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + if var < self.len() { + Some(term::var(self[var].0, self[var].1.clone())) + } else { + None + } } - } } impl VarIndexed for VarHMap<(VarIdx, Typ)> { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - self.get(& var).map( - |& (v, ref t)| term::var(v, t.clone()) - ) - } + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + self.get(&var).map(|&(v, ref t)| term::var(v, t.clone())) + } } impl VarIndexed for VarMap<::parse::PTTerms> { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - if self.len() < * var { - None - } else if let Ok(res) = self[var].to_term() { - res - } else { - None + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + if self.len() < *var { + None + } else if let Ok(res) = self[var].to_term() { + res + } else { + None + } } - } } impl VarIndexed for (T, U) -where T: VarIndexed, U: VarIndexed { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - if let Some(res) = self.0.var_get(var) { - debug_assert!( self.1.var_get(var).is_none() ) ; - Some(res) - } else if let Some(res) = self.1.var_get(var) { - debug_assert!( self.0.var_get(var).is_none() ) ; - Some(res) - } else { - None +where + T: VarIndexed, + U: VarIndexed, +{ + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + if let Some(res) = self.0.var_get(var) { + debug_assert!(self.1.var_get(var).is_none()); + Some(res) + } else if let Some(res) = self.1.var_get(var) { + debug_assert!(self.0.var_get(var).is_none()); + Some(res) + } else { + None + } } - } } -impl<'a, Elem, T> VarIndexed for & 'a T -where T: VarIndexed { - #[inline] - fn var_get(& self, var: VarIdx) -> Option { - (* self).var_get(var) - } +impl<'a, Elem, T> VarIndexed for &'a T +where + T: VarIndexed, +{ + #[inline] + fn var_get(&self, var: VarIdx) -> Option { + (*self).var_get(var) + } } - - - - - - - - /// Luby series. /// /// # Examples @@ -732,65 +696,62 @@ where T: VarIndexed { /// ``` #[derive(Default)] pub struct Luby { - /// Current max power of two. - max_pow: usize, - /// Current power of two, current values is `2^pow`. - pow: usize + /// Current max power of two. + max_pow: usize, + /// Current power of two, current values is `2^pow`. + pow: usize, } impl Luby { - /// Constructor. - pub fn new() -> Self { - Luby { max_pow: 0, pow: 0 } - } - /// Next value in the series. - pub fn next_value(& mut self) -> Int { - if self.pow > self.max_pow { - self.pow = 0 ; - self.max_pow += 1 - } - let mut res: Int = 2.into() ; - res = ::num::pow::pow(res, self.pow) ; - self.pow += 1 ; - res - } + /// Constructor. + pub fn new() -> Self { + Luby { max_pow: 0, pow: 0 } + } + /// Next value in the series. + pub fn next_value(&mut self) -> Int { + if self.pow > self.max_pow { + self.pow = 0; + self.max_pow += 1 + } + let mut res: Int = 2.into(); + res = ::num::pow::pow(res, self.pow); + self.pow += 1; + res + } } /// Counts up to the current value of the Luby series, outputs true and moves /// on to the next value when it reaches it. #[derive(Default)] pub struct LubyCount { - /// Luby series. - luby: Luby, - /// Current max value. - max: Int, - /// Counter. - count: Int, + /// Luby series. + luby: Luby, + /// Current max value. + max: Int, + /// Counter. + count: Int, } impl LubyCount { - /// Constructor. - pub fn new() -> Self { - let mut luby = Luby::new() ; - let max = luby.next_value() ; - let count = 0.into() ; - LubyCount { luby, max, count } - } + /// Constructor. + pub fn new() -> Self { + let mut luby = Luby::new(); + let max = luby.next_value(); + let count = 0.into(); + LubyCount { luby, max, count } + } - /// Increments the counter, returns true when it reaches the current luby - /// value. - pub fn inc(& mut self) -> bool { - self.count = & self.count + 1 ; - let ping = self.count >= self.max ; - if ping { - self.max = self.luby.next_value() ; - self.count = 0.into() + /// Increments the counter, returns true when it reaches the current luby + /// value. + pub fn inc(&mut self) -> bool { + self.count = &self.count + 1; + let ping = self.count >= self.max; + if ping { + self.max = self.luby.next_value(); + self.count = 0.into() + } + ping } - ping - } } - - - /// Iterator over all the combinations of some length of a collection of /// something. /// @@ -818,83 +779,76 @@ impl LubyCount { /// - `self.next.capacity() == self.len()` /// - There's `self.len - 1` elements in `self.tail` pub struct CombinationIter { - current: Option< Vec< (Iter::Item, Iter) > >, - len: usize, - next: Vec, - head: Iter::Item, - tail: Iter, + current: Option>, + len: usize, + next: Vec, + head: Iter::Item, + tail: Iter, } impl CombinationIter -where Iter: Iterator + ExactSizeIterator + Clone, Iter::Item: Clone { - - /// Constructor. - /// - /// Fails if `coll.next().is_none()`, or if `len == 0`. - pub fn new(mut coll: Iter, len: usize) -> Res { - if len == 0 { - bail!("trying to create all combinations of length 0, illegal") - } - let (head, tail) = if let Some(first) = coll.next() { - (first, coll) - } else { - bail!("trying to create all combinations of an empty collection") - } ; - - Ok( - CombinationIter { - current: Some( - vec![ (head.clone(), tail.clone()) ; len ] - ), - len, - next: Vec::with_capacity(len), - head, tail - } - ) - } - - /// Returns the next combination if any. - pub fn next_combination(& mut self) -> Option< & Vec > { - let mut res = None ; - - if let Some(mut current) = ::std::mem::replace( - & mut self.current, None - ) { - self.next.clear() ; - - // Construct result, easy part. - for (item, _) in & current { - self.next.push( item.clone() ) - } - // Remember we have a next. - res = Some(& self.next) ; - - // Tricky part. - // - // Remove from `current` the pairs that don't have a next element, until - // we find one that does (starting from the end). - 'find_next: while let Some((_, mut curr)) = current.pop() { - if let Some(next) = curr.next() { - // Found an element with a next. - current.push( (next, curr) ) ; - // Now we restart all elements after this one (the elements we - // removed). - while current.len() < self.len { - current.push( - // Starting again from the beginning for this element. - ( self.head.clone(), self.tail.clone() ) - ) - } - // Done, update current and break out. - self.current = Some(current) ; - break 'find_next +where + Iter: Iterator + ExactSizeIterator + Clone, + Iter::Item: Clone, +{ + /// Constructor. + /// + /// Fails if `coll.next().is_none()`, or if `len == 0`. + pub fn new(mut coll: Iter, len: usize) -> Res { + if len == 0 { + bail!("trying to create all combinations of length 0, illegal") } - } + let (head, tail) = if let Some(first) = coll.next() { + (first, coll) + } else { + bail!("trying to create all combinations of an empty collection") + }; + + Ok(CombinationIter { + current: Some(vec![(head.clone(), tail.clone()); len]), + len, + next: Vec::with_capacity(len), + head, + tail, + }) } - res - } + /// Returns the next combination if any. + pub fn next_combination(&mut self) -> Option<&Vec> { + let mut res = None; + + if let Some(mut current) = ::std::mem::replace(&mut self.current, None) { + self.next.clear(); + + // Construct result, easy part. + for (item, _) in ¤t { + self.next.push(item.clone()) + } + // Remember we have a next. + res = Some(&self.next); + + // Tricky part. + // + // Remove from `current` the pairs that don't have a next element, until + // we find one that does (starting from the end). + 'find_next: while let Some((_, mut curr)) = current.pop() { + if let Some(next) = curr.next() { + // Found an element with a next. + current.push((next, curr)); + // Now we restart all elements after this one (the elements we + // removed). + while current.len() < self.len { + current.push( + // Starting again from the beginning for this element. + (self.head.clone(), self.tail.clone()), + ) + } + // Done, update current and break out. + self.current = Some(current); + break 'find_next; + } + } + } + res + } } - - - diff --git a/src/common/msg.rs b/src/common/msg.rs index 825ba769..180eeb1b 100644 --- a/src/common/msg.rs +++ b/src/common/msg.rs @@ -1,45 +1,39 @@ //! Messages used in the framework. -use std::sync::mpsc::channel ; -use std::cell::RefCell ; +use std::cell::RefCell; +use std::sync::mpsc::channel; -use common::{ - *, - profiling::Profiler, -} ; - -use data::Data ; +use common::{profiling::Profiler, *}; +use data::Data; /// Sender / receiver pair alias type. -pub type Channel = (Sender, Receiver) ; - - +pub type Channel = (Sender, Receiver); /// Type of processes the teacher can spawn, and how to id them. #[derive(Clone, Copy)] pub enum Id { - /// Learners. - Learner(LrnIdx), - /// Assistant. - Assistant, + /// Learners. + Learner(LrnIdx), + /// Assistant. + Assistant, } impl Id { - /// True if the id is that of a learner. - pub fn is_learner(& self) -> bool { - match * self { - Id::Learner(_) => true, - _ => false, + /// True if the id is that of a learner. + pub fn is_learner(&self) -> bool { + match *self { + Id::Learner(_) => true, + _ => false, + } } - } - /// True if the id is the assistant. - pub fn is_assistant(& self) -> bool { - match * self { - Id::Assistant => true, - _ => false, + /// True if the id is the assistant. + pub fn is_assistant(&self) -> bool { + match *self { + Id::Assistant => true, + _ => false, + } } - } } impl_fmt! { Id(self, fmt) { @@ -50,440 +44,419 @@ impl_fmt! { } } - - /// Kind of messages the teacher can receive. pub enum MsgKind { - /// Some candidates, from learners. - Cands(Candidates), - /// Some samples from the assistant. - Samples(Box), - /// A message. - Msg(String), - /// An error. - Err(Error), - /// Unsat result. - Unsat, - /// Statistics. - Stats(Profiler), + /// Some candidates, from learners. + Cands(Candidates), + /// Some samples from the assistant. + Samples(Box), + /// A message. + Msg(String), + /// An error. + Err(Error), + /// Unsat result. + Unsat, + /// Statistics. + Stats(Profiler), } impl MsgKind { - /// True if the message is a candidates message. - pub fn is_candidates(& self) -> bool { - match * self { - MsgKind::Cands(_) => true, - _ => false, + /// True if the message is a candidates message. + pub fn is_candidates(&self) -> bool { + match *self { + MsgKind::Cands(_) => true, + _ => false, + } } - } - /// True if the message is a samples message. - pub fn is_samples(& self) -> bool { - match * self { - MsgKind::Samples(_) => true, - _ => false, + /// True if the message is a samples message. + pub fn is_samples(&self) -> bool { + match *self { + MsgKind::Samples(_) => true, + _ => false, + } } - } } impl From for MsgKind { - fn from(cands: Candidates) -> MsgKind { - MsgKind::Cands(cands) - } + fn from(cands: Candidates) -> MsgKind { + MsgKind::Cands(cands) + } } impl From for MsgKind { - fn from(data: Data) -> MsgKind { - MsgKind::Samples( Box::new(data) ) - } + fn from(data: Data) -> MsgKind { + MsgKind::Samples(Box::new(data)) + } } impl From for MsgKind { - fn from(msg: String) -> MsgKind { - MsgKind::Msg(msg) - } + fn from(msg: String) -> MsgKind { + MsgKind::Msg(msg) + } } impl From for MsgKind { - fn from(err: Error) -> MsgKind { - MsgKind::Err(err) - } + fn from(err: Error) -> MsgKind { + MsgKind::Err(err) + } } impl From for MsgKind { - fn from(profiler: Profiler) -> MsgKind { - MsgKind::Stats(profiler) - } + fn from(profiler: Profiler) -> MsgKind { + MsgKind::Stats(profiler) + } } - /// Messages the teacher can receive. pub struct Msg { - /// Id of the sender. - pub id: Id, - /// Actual message. - pub msg: MsgKind, + /// Id of the sender. + pub id: Id, + /// Actual message. + pub msg: MsgKind, } impl Msg { - /// Creates a message. - pub fn new(id: Id, msg: M) -> Self - where M: Into { - let msg = msg.into() ; - debug_assert! { ! msg.is_candidates() || id.is_learner() } - debug_assert! { ! msg.is_samples() || id.is_assistant() } - - Msg { id, msg } - } + /// Creates a message. + pub fn new(id: Id, msg: M) -> Self + where + M: Into, + { + let msg = msg.into(); + debug_assert! { ! msg.is_candidates() || id.is_learner() } + debug_assert! { ! msg.is_samples() || id.is_assistant() } + + Msg { id, msg } + } - /// Creates a candidates message. - pub fn cands(id: Id, cands: Candidates) -> Self { - debug_assert! { id.is_learner() } - Msg { id, msg: MsgKind::Cands(cands) } - } - /// Creates a samples message. - pub fn samples(id: Id, samples: Data) -> Self { - debug_assert! { id.is_assistant() } - Msg { id, msg: MsgKind::Samples( Box::new(samples) ) } - } + /// Creates a candidates message. + pub fn cands(id: Id, cands: Candidates) -> Self { + debug_assert! { id.is_learner() } + Msg { + id, + msg: MsgKind::Cands(cands), + } + } + /// Creates a samples message. + pub fn samples(id: Id, samples: Data) -> Self { + debug_assert! { id.is_assistant() } + Msg { + id, + msg: MsgKind::Samples(Box::new(samples)), + } + } - /// Channel to the teacher. - pub fn channel() -> Channel { channel() } + /// Channel to the teacher. + pub fn channel() -> Channel { + channel() + } } - /// Data sent by the teacher. pub enum FromTeacher { - /// Exit message. - Exit, - /// Learning data. - Data(Box), + /// Exit message. + Exit, + /// Learning data. + Data(Box), } impl FromTeacher { - /// Channel from the teacher. - pub fn channel() -> Channel { channel() } + /// Channel from the teacher. + pub fn channel() -> Channel { + channel() + } } - /// Bails saying `"disconnected from teacher"`. macro_rules! deco { - () => ( bail!("disconnected from teacher") ) ; + () => { + bail!("disconnected from teacher") + }; } - /// Communication core. /// /// Used by sub-processes to communicate with the teacher. pub struct MsgCore { - /// Identifier of the process. - id: Id, - /// Message channel to the teacher. - sender: Sender, - /// Receives stuff from the teacher. - recver: Receiver, - /// Profiler. - pub _profiler: Profiler, - /// Some profilers whoever is above the core can use. - _subs: RefCell< HashMap<& 'static str, Profiler> >, + /// Identifier of the process. + id: Id, + /// Message channel to the teacher. + sender: Sender, + /// Receives stuff from the teacher. + recver: Receiver, + /// Profiler. + pub _profiler: Profiler, + /// Some profilers whoever is above the core can use. + _subs: RefCell>, } impl MsgCore { - - /// Creates a core for a learner. - pub fn new_learner( - id: LrnIdx, sender: Sender, recver: Receiver - ) -> Self { - MsgCore { - id: Id::Learner(id), sender, recver, _profiler: Profiler::new(), - _subs: RefCell::new( HashMap::new() ), + /// Creates a core for a learner. + pub fn new_learner(id: LrnIdx, sender: Sender, recver: Receiver) -> Self { + MsgCore { + id: Id::Learner(id), + sender, + recver, + _profiler: Profiler::new(), + _subs: RefCell::new(HashMap::new()), + } } - } - /// Creates a core for the assistant. - pub fn new_assistant( - sender: Sender, recver: Receiver - ) -> Self { - MsgCore { - id: Id::Assistant, sender, recver, _profiler: Profiler::new(), - _subs: RefCell::new( HashMap::new() ), + /// Creates a core for the assistant. + pub fn new_assistant(sender: Sender, recver: Receiver) -> Self { + MsgCore { + id: Id::Assistant, + sender, + recver, + _profiler: Profiler::new(), + _subs: RefCell::new(HashMap::new()), + } } - } - /// Merges a profiler with the subprofiler `name`. - pub fn merge_prof(& self, name: & 'static str, prof: Profiler) { - self._subs.borrow_mut().entry(name).or_insert_with( - Profiler::new - ).merge(prof) - } - - /// Merges a profiler with the subprofiler `name`. - pub fn merge_set_prof(& self, name: & 'static str, prof: Profiler) { - self._subs.borrow_mut().entry(name).or_insert_with( - Profiler::new - ).merge_set(prof) - } + /// Merges a profiler with the subprofiler `name`. + pub fn merge_prof(&self, name: &'static str, prof: Profiler) { + self._subs + .borrow_mut() + .entry(name) + .or_insert_with(Profiler::new) + .merge(prof) + } - /// Sends some candidates. - pub fn send_candidates( - & self, candidates: Candidates - ) -> Res<()> { - if self.sender.send( - Msg::cands(self.id, candidates) - ).is_ok() { - Ok(()) - } else { - deco!() + /// Merges a profiler with the subprofiler `name`. + pub fn merge_set_prof(&self, name: &'static str, prof: Profiler) { + self._subs + .borrow_mut() + .entry(name) + .or_insert_with(Profiler::new) + .merge_set(prof) } - } - /// Sends some samples. - pub fn send_samples( - & self, samples: Data - ) -> Res<()> { - if self.sender.send( - Msg::samples(self.id, samples) - ).is_ok() { - Ok(()) - } else { - deco!() + /// Sends some candidates. + pub fn send_candidates(&self, candidates: Candidates) -> Res<()> { + if self.sender.send(Msg::cands(self.id, candidates)).is_ok() { + Ok(()) + } else { + deco!() + } } - } - /// Sends statistics. - pub fn stats(self) -> Res<()> { - for (name, prof) in self._subs.into_inner() { - self._profiler.add_sub(name, prof) + /// Sends some samples. + pub fn send_samples(&self, samples: Data) -> Res<()> { + if self.sender.send(Msg::samples(self.id, samples)).is_ok() { + Ok(()) + } else { + deco!() + } } - if self.sender.send( - Msg::new(self.id, self._profiler) - ).is_ok() { - Ok(()) - } else { - deco!() + + /// Sends statistics. + pub fn stats(self) -> Res<()> { + for (name, prof) in self._subs.into_inner() { + self._profiler.add_sub(name, prof) + } + if self.sender.send(Msg::new(self.id, self._profiler)).is_ok() { + Ok(()) + } else { + deco!() + } } - } - /// Sends a string message. - #[cfg( not(feature = "bench") )] - pub fn msg>(& self, s: S) -> Res<()> { - if self.sender.send( - Msg::new(self.id, s.into()) - ).is_ok() { - Ok(()) - } else { - deco!() + /// Sends a string message. + #[cfg(not(feature = "bench"))] + pub fn msg>(&self, s: S) -> Res<()> { + if self.sender.send(Msg::new(self.id, s.into())).is_ok() { + Ok(()) + } else { + deco!() + } } - } - #[cfg(feature = "bench")] - #[inline] - pub fn msg(& self, _: S) -> Res<()> { Ok(()) } - - /// Sends an error message and then the stats. - /// - /// Equivalent to `self.unsat()` if the error is `Unsat`. - pub fn err(self, err: Error) -> () { - if err.is_unsat() { - self.unsat() - } else if err.is_exit() { - self.exit() - } else { - let _ = self.sender.send( Msg::new(self.id, err) ) ; - self.exit() + #[cfg(feature = "bench")] + #[inline] + pub fn msg(&self, _: S) -> Res<()> { + Ok(()) } - } - /// Sends an unsat message and exits. - pub fn unsat(self) -> () { - if self.sender.send( - Msg::new(self.id, MsgKind::Unsat) - ).is_ok() { - self.exit() + /// Sends an error message and then the stats. + /// + /// Equivalent to `self.unsat()` if the error is `Unsat`. + pub fn err(self, err: Error) -> () { + if err.is_unsat() { + self.unsat() + } else if err.is_exit() { + self.exit() + } else { + let _ = self.sender.send(Msg::new(self.id, err)); + self.exit() + } } - } - - /// Exits, sends statistics. - pub fn exit(self) -> () { - let _ = self.stats() ; - () - } + /// Sends an unsat message and exits. + pub fn unsat(self) -> () { + if self.sender.send(Msg::new(self.id, MsgKind::Unsat)).is_ok() { + self.exit() + } + } - /// Exit if we have received an exit message. - #[inline] - pub fn check_exit(& self) -> Res<()> { - use std::sync::mpsc::TryRecvError::* ; - match self.recver.try_recv() { - Ok(FromTeacher::Exit) => bail!(ErrorKind::Exit), - Ok(_) => bail!( - "received data while checking for exit, logic error" - ), - Err(Empty) => Ok(()), - Err(Disconnected) => deco!(), + /// Exits, sends statistics. + pub fn exit(self) -> () { + let _ = self.stats(); + () } - } - /// Receives some data from the teacher. - pub fn recv(& self) -> Res { - match self.recver.recv() { - Ok(FromTeacher::Exit) => bail!(ErrorKind::Exit), - Ok( FromTeacher::Data(data) ) => Ok(* data), - Err(_) => deco!(), + /// Exit if we have received an exit message. + #[inline] + pub fn check_exit(&self) -> Res<()> { + use std::sync::mpsc::TryRecvError::*; + match self.recver.try_recv() { + Ok(FromTeacher::Exit) => bail!(ErrorKind::Exit), + Ok(_) => bail!("received data while checking for exit, logic error"), + Err(Empty) => Ok(()), + Err(Disconnected) => deco!(), + } } - } + /// Receives some data from the teacher. + pub fn recv(&self) -> Res { + match self.recver.recv() { + Ok(FromTeacher::Exit) => bail!(ErrorKind::Exit), + Ok(FromTeacher::Data(data)) => Ok(*data), + Err(_) => deco!(), + } + } } - - - - - - - /// A learner can be launched when given a core and an instance, and has a /// description. pub trait Learner: Sync + Send { - /// Launches the learner. - /// - /// The boolean flag `mine` specifies whether the learner should mine the - /// instance, typically for qualifiers. - fn run( - & self, MsgCore, Arc, Data, mine: bool - ) ; - /// Short description of the learner. - fn description(& self, mine: bool) -> String ; + /// Launches the learner. + /// + /// The boolean flag `mine` specifies whether the learner should mine the + /// instance, typically for qualifiers. + fn run(&self, MsgCore, Arc, Data, mine: bool); + /// Short description of the learner. + fn description(&self, mine: bool) -> String; } - - - /// Messages from assistant. pub enum FromAssistant { - /// Positive and negative samples. - Samples(Box), - /// Message. - Msg(String), - /// Error. - Err(Error), - /// Statistics. - Stats(Profiler), - /// Unsat. - Unsat, + /// Positive and negative samples. + Samples(Box), + /// Message. + Msg(String), + /// Error. + Err(Error), + /// Statistics. + Stats(Profiler), + /// Unsat. + Unsat, } unsafe impl Send for FromAssistant {} /// Messages to the assistant. pub enum ToAssistant { - /// Implication constraints. - Samples(Box), - /// Exit message. - Exit, + /// Implication constraints. + Samples(Box), + /// Exit message. + Exit, } /// Channel from a teacher to an assistant. -pub fn new_to_assistant() -> Channel { channel() } +pub fn new_to_assistant() -> Channel { + channel() +} /// Channel from the assistants to the teacher. -pub fn from_assistants() -> Channel { channel() } +pub fn from_assistants() -> Channel { + channel() +} /// A communication core for assistants. pub struct AssistantCore { - /// Sends stuff to the teacher. - sender: Sender, - /// Receives stuff from the teacher. - recver: Receiver, - /// Profiler. - pub _profiler: Profiler, -} -impl AssistantCore { - /// Constructor. - pub fn new( + /// Sends stuff to the teacher. sender: Sender, + /// Receives stuff from the teacher. recver: Receiver, - ) -> Self { - AssistantCore { - sender, recver, _profiler: Profiler::new() + /// Profiler. + pub _profiler: Profiler, +} +impl AssistantCore { + /// Constructor. + pub fn new(sender: Sender, recver: Receiver) -> Self { + AssistantCore { + sender, + recver, + _profiler: Profiler::new(), + } } - } - /// Sends some samples to the teacher. Returns `false` iff sending fails, - /// **meaning the teacher is disconnected**. - pub fn send_samples(& self, samples: Data) -> bool { - self.sender.send( - FromAssistant::Samples( Box::new(samples) ) - ).is_ok() - } + /// Sends some samples to the teacher. Returns `false` iff sending fails, + /// **meaning the teacher is disconnected**. + pub fn send_samples(&self, samples: Data) -> bool { + self.sender + .send(FromAssistant::Samples(Box::new(samples))) + .is_ok() + } - /// Sends statistics. - #[cfg( not(feature = "bench") )] - pub fn stats(self) -> bool { - if conf.stats { - self.sender.send( - FromAssistant::Stats(self._profiler) - ).is_ok() - } else { - true + /// Sends statistics. + #[cfg(not(feature = "bench"))] + pub fn stats(self) -> bool { + if conf.stats { + self.sender + .send(FromAssistant::Stats(self._profiler)) + .is_ok() + } else { + true + } + } + #[cfg(feature = "bench")] + pub fn stats(self) -> bool { + true } - } - #[cfg(feature = "bench")] - pub fn stats(self) -> bool { - true - } - /// Sends a message to the teacher. Returns `false` iff sending fails, - /// **meaning the teacher is disconnected**. - pub fn msg>(& self, s: S) -> bool { - self.sender.send( - FromAssistant::Msg(s.into()) - ).is_ok() - } - /// Sends an error to the teacher. Returns `false` iff sending fails, - /// **meaning the teacher is disconnected**. - pub fn err(& self, err: Error) -> bool { - if let ErrorKind::Unsat = * err.kind() { - return self.unsat() - } - self.sender.send( - FromAssistant::Err(err) - ).is_ok() - } + /// Sends a message to the teacher. Returns `false` iff sending fails, + /// **meaning the teacher is disconnected**. + pub fn msg>(&self, s: S) -> bool { + self.sender.send(FromAssistant::Msg(s.into())).is_ok() + } + /// Sends an error to the teacher. Returns `false` iff sending fails, + /// **meaning the teacher is disconnected**. + pub fn err(&self, err: Error) -> bool { + if let ErrorKind::Unsat = *err.kind() { + return self.unsat(); + } + self.sender.send(FromAssistant::Err(err)).is_ok() + } - /// Sends an unsat result to the teacher. Returns `false` iff sending fails, - /// **meaning the teacher is disconnected**. - pub fn unsat(& self) -> bool { - self.sender.send( - FromAssistant::Unsat - ).is_ok() - } + /// Sends an unsat result to the teacher. Returns `false` iff sending fails, + /// **meaning the teacher is disconnected**. + pub fn unsat(&self) -> bool { + self.sender.send(FromAssistant::Unsat).is_ok() + } - /// Exits, destroys itself. - pub fn exit(self) -> () { - self.stats() ; - () - } + /// Exits, destroys itself. + pub fn exit(self) -> () { + self.stats(); + () + } - /// Exit if we have received an exit message. - #[inline] - pub fn check_exit(& self) -> Res<()> { - use std::sync::mpsc::TryRecvError::* ; - match self.recver.try_recv() { - Ok(ToAssistant::Exit) => bail!( ErrorKind::Exit ), - Ok( ToAssistant::Samples(_) ) => bail!( - "received data while checking for exit, logic error" - ), - Err(Empty) => Ok(()), - Err(Disconnected) => bail!( - "disconnected from teacher..." - ), + /// Exit if we have received an exit message. + #[inline] + pub fn check_exit(&self) -> Res<()> { + use std::sync::mpsc::TryRecvError::*; + match self.recver.try_recv() { + Ok(ToAssistant::Exit) => bail!(ErrorKind::Exit), + Ok(ToAssistant::Samples(_)) => { + bail!("received data while checking for exit, logic error") + } + Err(Empty) => Ok(()), + Err(Disconnected) => bail!("disconnected from teacher..."), + } } - } - /// Receive some learning data from the teacher. - /// - /// Returns `None` if an exit message was received. - /// - /// Error if disconnected. - pub fn recv(& self) -> Res< Option > { - match self.recver.recv() { - Ok( ToAssistant::Samples(data) ) => Ok( Some(* data) ), - Ok(ToAssistant::Exit) => Ok(None), - Err(_) => bail!("disconnected from teacher"), + /// Receive some learning data from the teacher. + /// + /// Returns `None` if an exit message was received. + /// + /// Error if disconnected. + pub fn recv(&self) -> Res> { + match self.recver.recv() { + Ok(ToAssistant::Samples(data)) => Ok(Some(*data)), + Ok(ToAssistant::Exit) => Ok(None), + Err(_) => bail!("disconnected from teacher"), + } } - } } - diff --git a/src/common/profiling.rs b/src/common/profiling.rs index 83134798..1c274c9d 100644 --- a/src/common/profiling.rs +++ b/src/common/profiling.rs @@ -7,222 +7,226 @@ //! (Profiler type) #[allow(unused_imports)] -use std::time::{ Instant, Duration } ; +use std::time::{Duration, Instant}; -use common::* ; +use common::*; /// Extends duration with a pretty printing. pub trait DurationExt { - /// Nice string representation. - fn to_str(& self) -> String ; + /// Nice string representation. + fn to_str(&self) -> String; } impl DurationExt for Duration { - fn to_str(& self) -> String { - format!("{}.{:0>9}", self.as_secs(), self.subsec_nanos()) - } + fn to_str(&self) -> String { + format!("{}.{:0>9}", self.as_secs(), self.subsec_nanos()) + } } /// Profile Tree. #[derive(PartialEq, Eq)] pub struct ProfileTree { - /// Duration stored at this level. - duration: Option, - /// Sub-branches. - branches: BTreeMap<& 'static str, ProfileTree>, + /// Duration stored at this level. + duration: Option, + /// Sub-branches. + branches: BTreeMap<&'static str, ProfileTree>, } impl ProfileTree { - /// Tree with nothing but the top level. - pub fn top(top: Duration) -> Self { - ProfileTree { - duration: Some(top), - branches: BTreeMap::new(), + /// Tree with nothing but the top level. + pub fn top(top: Duration) -> Self { + ProfileTree { + duration: Some(top), + branches: BTreeMap::new(), + } } - } - /// Empty tree, not visible outside. - fn empty() -> Self { - ProfileTree { duration: None, branches: BTreeMap::new() } - } - - /// Debug printing (multi-line). - #[cfg(feature = "bench")] - #[allow(dead_code)] - fn print( - & self, _: S, _: & [ & 'static str ] - ) {} - #[cfg(not (feature = "bench") )] - #[cfg_attr(feature = "cargo-clippy", allow(print_literal))] - fn print( - & self, pref: S, set_sum: & [ & 'static str ] - ) where S: Into { - let pref = pref.into() ; - self.fold( - None, - |prev, scope, time, sub_time| if let Some(last) = scope.last() { - debug_assert! { ! scope.is_empty() } - let art = match prev { - Some(n) if n < scope.len() => "\\", - Some(_) | None => "|", - } ; - println!( - "; {5}{0: >1$}{6}- {2}s {3}{4}", - "", - // Can't be negative because `scope` contains `last`. - scope.len() - 1, - time.to_str(), - last, - if sub_time != Duration::from_secs(0) { - format!(" ({}s)", sub_time.to_str()) - } else { - "".into() - }, - pref, - art - ) ; - Some(scope.len()) - } else { - println!( - "; {}{} {}s{}", pref, conf.happy("total"), time.to_str(), - if sub_time != Duration::from_secs(0) { - format!(" ({}s)", sub_time.to_str()) - } else { - "".into() - } - ) ; - None - }, - set_sum - ) ; - } - - /// Inserts something in the tree. - pub fn insert( - & mut self, scope: Vec<& 'static str>, duration: Duration - ) { - let (mut current, mut last_scope) = (self, "top") ; - - for scope in scope { - let tmp = current ; - current = tmp.branches.entry(scope).or_insert_with( - ProfileTree::empty - ) ; - last_scope = scope + /// Empty tree, not visible outside. + fn empty() -> Self { + ProfileTree { + duration: None, + branches: BTreeMap::new(), + } } - if current.duration.is_some() { - panic!( - "ProfileTree: trying to insert the same scope twice `{}`", - conf.emph(last_scope) - ) + + /// Debug printing (multi-line). + #[cfg(feature = "bench")] + #[allow(dead_code)] + fn print(&self, _: S, _: &[&'static str]) {} + #[cfg(not(feature = "bench"))] + #[cfg_attr(feature = "cargo-clippy", allow(print_literal))] + fn print(&self, pref: S, set_sum: &[&'static str]) + where + S: Into, + { + let pref = pref.into(); + self.fold( + None, + |prev, scope, time, sub_time| { + if let Some(last) = scope.last() { + debug_assert! { ! scope.is_empty() } + let art = match prev { + Some(n) if n < scope.len() => "\\", + Some(_) | None => "|", + }; + println!( + "; {5}{0: >1$}{6}- {2}s {3}{4}", + "", + // Can't be negative because `scope` contains `last`. + scope.len() - 1, + time.to_str(), + last, + if sub_time != Duration::from_secs(0) { + format!(" ({}s)", sub_time.to_str()) + } else { + "".into() + }, + pref, + art + ); + Some(scope.len()) + } else { + println!( + "; {}{} {}s{}", + pref, + conf.happy("total"), + time.to_str(), + if sub_time != Duration::from_secs(0) { + format!(" ({}s)", sub_time.to_str()) + } else { + "".into() + } + ); + None + } + }, + set_sum, + ); } - current.duration = Some(duration) - } - /// Iterator on the tree. - /// - /// Scopes are guaranteed to follow the topological order. - pub fn fold(& self, init: T, f: F, set_sum: & [& 'static str]) - where F: Fn(T, & [& 'static str], & Duration, Duration) -> T { - let mut prev = init ; - if let Some(duration) = self.duration.as_ref() { - let sub_duration = self.branches.iter().fold( - Duration::from_secs(0), - |acc, (_, time)| acc + time.duration.unwrap_or_else( - || Duration::from_secs(0) - ) - ) ; - prev = f(prev, &[], duration, sub_duration) - } else { - panic!("ProfileTree: no top duration set but already iterating") + /// Inserts something in the tree. + pub fn insert(&mut self, scope: Vec<&'static str>, duration: Duration) { + let (mut current, mut last_scope) = (self, "top"); + + for scope in scope { + let tmp = current; + current = tmp.branches.entry(scope).or_insert_with(ProfileTree::empty); + last_scope = scope + } + if current.duration.is_some() { + panic!( + "ProfileTree: trying to insert the same scope twice `{}`", + conf.emph(last_scope) + ) + } + current.duration = Some(duration) } - let mut stack: Vec< (_, Vec<_>) > = vec![ - ( vec![], self.branches.iter().map(|(s, p)| (*s, p)).collect() ) - ] ; - while let Some( (scope, mut branches) ) = stack.pop() { - if let Some( (s, profile) ) = branches.pop() { - let mut this_scope = scope.clone() ; - stack.push( (scope, branches) ) ; - this_scope.push( s ) ; - let sub_duration = profile.branches.iter().fold( - Duration::from_secs(0), - |acc, (_, time)| acc + time.duration.unwrap_or_else( - || Duration::from_secs(0) - ) - ) ; - if let Some(duration) = profile.duration.as_ref() { - prev = f(prev, & this_scope, duration, sub_duration) + /// Iterator on the tree. + /// + /// Scopes are guaranteed to follow the topological order. + pub fn fold(&self, init: T, f: F, set_sum: &[&'static str]) + where + F: Fn(T, &[&'static str], &Duration, Duration) -> T, + { + let mut prev = init; + if let Some(duration) = self.duration.as_ref() { + let sub_duration = self + .branches + .iter() + .fold(Duration::from_secs(0), |acc, (_, time)| { + acc + time.duration.unwrap_or_else(|| Duration::from_secs(0)) + }); + prev = f(prev, &[], duration, sub_duration) } else { - if set_sum.iter().any( - |scope| s == * scope - ) { - let mut scope_str = "".to_string() ; - for s in & this_scope { - scope_str.push_str("::") ; scope_str.push_str(s) - } - warn!{ - "no duration for scope {}, setting to sum of branches", - conf.emph(& scope_str) + panic!("ProfileTree: no top duration set but already iterating") + } + let mut stack: Vec<(_, Vec<_>)> = + vec![(vec![], self.branches.iter().map(|(s, p)| (*s, p)).collect())]; + + while let Some((scope, mut branches)) = stack.pop() { + if let Some((s, profile)) = branches.pop() { + let mut this_scope = scope.clone(); + stack.push((scope, branches)); + this_scope.push(s); + let sub_duration = profile + .branches + .iter() + .fold(Duration::from_secs(0), |acc, (_, time)| { + acc + time.duration.unwrap_or_else(|| Duration::from_secs(0)) + }); + if let Some(duration) = profile.duration.as_ref() { + prev = f(prev, &this_scope, duration, sub_duration) + } else { + if set_sum.iter().any(|scope| s == *scope) { + let mut scope_str = "".to_string(); + for s in &this_scope { + scope_str.push_str("::"); + scope_str.push_str(s) + } + warn!{ + "no duration for scope {}, setting to sum of branches", + conf.emph(& scope_str) + } + } + prev = f(prev, &this_scope, &sub_duration, sub_duration) + } + stack.push(( + this_scope, + profile.branches.iter().map(|(s, p)| (*s, p)).collect(), + )) } - } - prev = f(prev, & this_scope, & sub_duration, sub_duration) } - stack.push( - ( - this_scope, - profile.branches.iter().map(|(s, p)| (*s, p)).collect() - ) - ) - } } - } } - /// Maps strings to counters. -pub type Stats = BTreeMap ; +pub type Stats = BTreeMap; /// Provides a debug print function. pub trait CanPrint { - /// True if at least one value is not `0`. - fn has_non_zero(& self) -> bool ; - /// Debug print (multi-line). - fn print(& self, S) where S: Into ; + /// True if at least one value is not `0`. + fn has_non_zero(&self) -> bool; + /// Debug print (multi-line). + fn print(&self, S) + where + S: Into; } -static STAT_LEN: usize = 29 ; +static STAT_LEN: usize = 29; impl CanPrint for Stats { - fn has_non_zero(& self) -> bool { - self.values().any(|n| * n > 0) - } - #[cfg_attr(feature = "cargo-clippy", allow(print_literal))] - fn print(& self, pref: S) where S: Into { - let pref = pref.into() ; - let mut stats: Vec<_> = self.iter().collect() ; - stats.sort_unstable() ; - for (stat, count) in stats { - if * count > 0 { - let stat_len = ::std::cmp::min( STAT_LEN, stat.len() ) ; - println!( - "; {4} {0: >1$}{2}: {3: >5}", - "", STAT_LEN - stat_len, conf.emph(stat), count, pref - ) - } + fn has_non_zero(&self) -> bool { + self.values().any(|n| *n > 0) + } + #[cfg_attr(feature = "cargo-clippy", allow(print_literal))] + fn print(&self, pref: S) + where + S: Into, + { + let pref = pref.into(); + let mut stats: Vec<_> = self.iter().collect(); + stats.sort_unstable(); + for (stat, count) in stats { + if *count > 0 { + let stat_len = ::std::cmp::min(STAT_LEN, stat.len()); + println!( + "; {4} {0: >1$}{2}: {3: >5}", + "", + STAT_LEN - stat_len, + conf.emph(stat), + count, + pref + ) + } + } } - } } - /// Maps scopes to /// /// - a (start) instant option: `Some` if the scope is currently active, and /// - a duration representing the total runtime of this scope. -pub type InstantMap = BTreeMap< - Vec<& 'static str>, (Option, Duration) -> ; - +pub type InstantMap = BTreeMap, (Option, Duration)>; // The following import is not used in bench mode. #[allow(unused_imports)] -use std::cell::RefCell ; - +use std::cell::RefCell; /// Profiling structure, only in `not(bench)`. /// @@ -230,241 +234,226 @@ use std::cell::RefCell ; /// /// Internally, the structures are wrapped in `RefCell`s so that mutation /// does not require `& mut self`. -#[cfg( not(feature = "bench") )] +#[cfg(not(feature = "bench"))] #[derive(Clone)] pub struct Profiler { - /// String-indexed durations. - map: RefCell, - /// Starting tick, for total time. - start: Instant, - /// Other statistics. - stats: RefCell, - /// Sub-profilers. - subs: RefCell< Vec<(String, Profiler)> >, - /// Other profilers. - others: RefCell< Vec<(String, Profiler)> >, + /// String-indexed durations. + map: RefCell, + /// Starting tick, for total time. + start: Instant, + /// Other statistics. + stats: RefCell, + /// Sub-profilers. + subs: RefCell>, + /// Other profilers. + others: RefCell>, } #[cfg(feature = "bench")] #[derive(Clone)] -pub struct Profiler ; +pub struct Profiler; impl Default for Profiler { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl Profiler { - /// Constructor. - #[cfg( not(feature = "bench") )] - pub fn new() -> Self { - use std::cell::RefCell ; - Profiler { - map: RefCell::new( InstantMap::new() ), - start: Instant::now(), - stats: RefCell::new( Stats::new() ), - subs: RefCell::new( Vec::new() ), - others: RefCell::new( Vec::new() ), - } - } - #[cfg(feature = "bench")] - pub fn new() -> Self { Profiler } - - /// Merges two profilers. - #[cfg(feature = "bench")] - pub fn merge(& mut self, _: Self) {} - /// Merges two profilers. - #[cfg(not(feature = "bench"))] - pub fn merge(& mut self, other: Self) { - let map = other.map.into_inner() ; - let stats = other.stats.into_inner() ; - let subs = other.subs.into_inner() ; - for sub in subs { - self.subs.get_mut().push(sub) + /// Constructor. + #[cfg(not(feature = "bench"))] + pub fn new() -> Self { + use std::cell::RefCell; + Profiler { + map: RefCell::new(InstantMap::new()), + start: Instant::now(), + stats: RefCell::new(Stats::new()), + subs: RefCell::new(Vec::new()), + others: RefCell::new(Vec::new()), + } } - for (scope, (_, duration)) in map { - self.map.get_mut().entry(scope).or_insert_with( - || (None, Duration::new(0, 0)) - ).1 += duration + #[cfg(feature = "bench")] + pub fn new() -> Self { + Profiler } - for (scope, val) in stats { - * self.stats.get_mut().entry(scope).or_insert_with( - || 0 - ) += val + + /// Merges two profilers. + #[cfg(feature = "bench")] + pub fn merge(&mut self, _: Self) {} + /// Merges two profilers. + #[cfg(not(feature = "bench"))] + pub fn merge(&mut self, other: Self) { + let map = other.map.into_inner(); + let stats = other.stats.into_inner(); + let subs = other.subs.into_inner(); + for sub in subs { + self.subs.get_mut().push(sub) + } + for (scope, (_, duration)) in map { + self.map + .get_mut() + .entry(scope) + .or_insert_with(|| (None, Duration::new(0, 0))) + .1 += duration + } + for (scope, val) in stats { + *self.stats.get_mut().entry(scope).or_insert_with(|| 0) += val + } } - } - /// Merges the durations of two profilers. - #[cfg(feature = "bench")] - pub fn merge_set(& mut self, _: Self) {} - /// Merges the durations of two profilers and sets the stats. - #[cfg(not(feature = "bench"))] - pub fn merge_set(& mut self, other: Self) { - let map = other.map.into_inner() ; - let stats = other.stats.into_inner() ; - let subs = other.subs.into_inner() ; - for sub in subs { - self.subs.get_mut().push(sub) + /// Merges the durations of two profilers. + #[cfg(feature = "bench")] + pub fn merge_set(&mut self, _: Self) {} + /// Merges the durations of two profilers and sets the stats. + #[cfg(not(feature = "bench"))] + pub fn merge_set(&mut self, other: Self) { + let map = other.map.into_inner(); + let stats = other.stats.into_inner(); + let subs = other.subs.into_inner(); + for sub in subs { + self.subs.get_mut().push(sub) + } + for (scope, (_, duration)) in map { + self.map + .get_mut() + .entry(scope) + .or_insert_with(|| (None, Duration::new(0, 0))) + .1 += duration + } + for (scope, val) in stats { + *self.stats.get_mut().entry(scope).or_insert_with(|| 0) = val + } } - for (scope, (_, duration)) in map { - self.map.get_mut().entry(scope).or_insert_with( - || (None, Duration::new(0, 0)) - ).1 += duration + + /// Acts on a statistic. + #[cfg(not(feature = "bench"))] + pub fn stat_do(&self, stat: S, f: F) + where + F: Fn(usize) -> usize, + S: Into, + { + let stat = stat.into(); + let mut map = self.stats.borrow_mut(); + let val = map.get(&stat).cloned().unwrap_or(0); + let _ = map.insert(stat, f(val)); + () } - for (scope, val) in stats { - * self.stats.get_mut().entry(scope).or_insert_with( - || 0 - ) = val + + /// Ticks. + #[cfg(not(feature = "bench"))] + pub fn tick(&self, scope: Vec<&'static str>) { + if scope.is_empty() { + panic!("Profile: can't use scope `total`") + } + let mut map = self.map.borrow_mut(); + let time = map + .entry(scope) + .or_insert_with(|| (None, Duration::from_secs(0))); + time.0 = Some(Instant::now()) } - } - /// Acts on a statistic. - #[cfg( not(feature = "bench") )] - pub fn stat_do(& self, stat: S, f: F) - where F: Fn(usize) -> usize, S: Into { - let stat = stat.into() ; - let mut map = self.stats.borrow_mut() ; - let val = map.get(& stat).cloned().unwrap_or(0) ; - let _ = map.insert(stat, f(val)) ; - () - } + /// Registers the time since the last tick. + /// + /// Panics if there was no tick since the last time registration. + #[cfg(not(feature = "bench"))] + #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] + pub fn mark(&self, scope: Vec<&'static str>) { + if scope.is_empty() { + panic!("Profile: can't use scope `total`") + } + let mut map = self.map.borrow_mut(); + if let Some(&mut (ref mut tick, ref mut sum)) = map.get_mut(&scope) { + let mut instant = None; + ::std::mem::swap(&mut instant, tick); + if let Some(instant) = instant { + *sum += Instant::now().duration_since(instant); + *tick = None + } + } else { + panic!( + "profiling: trying to mark the time for {:?} without ticking first", + scope + ) + } + } - /// Ticks. - #[cfg( not(feature = "bench") )] - pub fn tick(& self, scope: Vec<& 'static str>) { - if scope.is_empty() { - panic!("Profile: can't use scope `total`") + /// Extracts the profile tree and the stats. + #[cfg(not(feature = "bench"))] + fn extract(self) -> (ProfileTree, Stats, Vec<(String, Profiler)>) { + let mut tree = ProfileTree::top(Instant::now().duration_since(self.start)); + for (scope, &(ref should_be_none, ref time)) in self.map.borrow().iter() { + if should_be_none.is_some() { + warn!( + "Profile::extract_tree: \ + still have a live instant for {:?}", + scope + ) + } + tree.insert(scope.clone(), *time) + } + (tree, self.stats.into_inner(), self.subs.into_inner()) } - let mut map = self.map.borrow_mut() ; - let time = map.entry(scope).or_insert_with( - || ( None, Duration::from_secs(0) ) - ) ; - time.0 = Some( Instant::now() ) - } - /// Registers the time since the last tick. - /// - /// Panics if there was no tick since the last time registration. - #[cfg( not(feature = "bench") )] - #[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] - pub fn mark(& self, scope: Vec<& 'static str>) { - if scope.is_empty() { - panic!("Profile: can't use scope `total`") + /// Adds a sub-profiler. + #[cfg(not(feature = "bench"))] + pub fn add_sub>(&self, name: S, sub: Self) { + self.subs.borrow_mut().push((name.into(), sub)) } - let mut map = self.map.borrow_mut() ; - if let Some( - & mut (ref mut tick, ref mut sum) - ) = map.get_mut(& scope) { - let mut instant = None ; - ::std::mem::swap(& mut instant, tick) ; - if let Some(instant) = instant { - * sum += Instant::now().duration_since(instant) ; - * tick = None - } - } else { - panic!( - "profiling: trying to mark the time for {:?} without ticking first", - scope - ) + #[cfg(feature = "bench")] + pub fn add_sub>(&self, _: S, _: Self) {} + + /// Adds an other (not a sub) profiler to this profiler. + #[cfg(not(feature = "bench"))] + pub fn drain_subs(&self) -> Vec<(String, Profiler)> { + let mut res = vec![]; + ::std::mem::swap(&mut res, &mut *self.subs.borrow_mut()); + res } - } - /// Extracts the profile tree and the stats. - #[cfg( not(feature = "bench") )] - fn extract(self) -> (ProfileTree, Stats, Vec<(String, Profiler)>) { - let mut tree = ProfileTree::top( - Instant::now().duration_since(self.start) - ) ; - for ( - scope, & (ref should_be_none, ref time) - ) in self.map.borrow().iter() { - if should_be_none.is_some() { - warn!( - "Profile::extract_tree: \ - still have a live instant for {:?}", scope - ) - } - tree.insert( scope.clone(), * time ) + /// Adds an other (not a sub) profiler to this profiler. + #[cfg(not(feature = "bench"))] + pub fn add_other>(&self, name: S, other: Self) -> () { + self.others.borrow_mut().push((name.into(), other)) } - ( tree, self.stats.into_inner(), self.subs.into_inner() ) - } - - /// Adds a sub-profiler. - #[cfg( not(feature = "bench") )] - pub fn add_sub< S: Into >( - & self, name: S, sub: Self - ) { - self.subs.borrow_mut().push( (name.into(), sub) ) - } - #[cfg(feature = "bench")] - pub fn add_sub< S: Into >( - & self, _: S, _: Self - ) {} - - /// Adds an other (not a sub) profiler to this profiler. - #[cfg( not(feature = "bench") )] - pub fn drain_subs( - & self, - ) -> Vec<(String, Profiler)> { - let mut res = vec![] ; - ::std::mem::swap( - & mut res, & mut * self.subs.borrow_mut() - ) ; - res - } - - /// Adds an other (not a sub) profiler to this profiler. - #[cfg( not(feature = "bench") )] - pub fn add_other>( - & self, name: S, other: Self - ) -> () { - self.others.borrow_mut().push( - (name.into(), other) - ) - } - #[cfg(feature = "bench")] - pub fn add_other( - & self, _: S, _: Self - ) -> () {} - - /// Adds an other (not a sub) profiler to this profiler. - #[cfg( not(feature = "bench") )] - pub fn drain_others( - & self, - ) -> Vec<(String, Profiler)> { - let mut res = vec![] ; - ::std::mem::swap( - & mut res, & mut * self.others.borrow_mut() - ) ; - res - } - - - /// Consumes and prints a profiler. - /// - /// - `set_sum` is a slice of scopes which have no duration and will be set - /// to the sum of their branches (without triggering a warning) - #[cfg( not(feature = "bench") )] - pub fn print( - self, name: S1, pref: S2, set_sum: & [ & 'static str ] - ) where S1: Into, S2: Into { - let name = name.into() ; - let pref = pref.into() ; - - println!("; {}{} {}", pref, conf.emph(& name), conf.emph("{")) ; - let sub_pref = format!("{} ", pref) ; - - let (tree, stats, subs) = self.extract() ; - tree.print(sub_pref.clone(), set_sum) ; - if stats.has_non_zero() { - println!("; {}{}:", sub_pref, conf.happy("metrics")) ; - stats.print( format!("{}{} ", sub_pref, conf.happy("|")) ) + #[cfg(feature = "bench")] + pub fn add_other(&self, _: S, _: Self) -> () {} + + /// Adds an other (not a sub) profiler to this profiler. + #[cfg(not(feature = "bench"))] + pub fn drain_others(&self) -> Vec<(String, Profiler)> { + let mut res = vec![]; + ::std::mem::swap(&mut res, &mut *self.others.borrow_mut()); + res } - scoped! { - for (sub_name, sub) in subs { - println!("; ") ; - sub.print( - format!("{}/{}", name, sub_name), sub_pref.clone(), set_sum - ) ; - } + /// Consumes and prints a profiler. + /// + /// - `set_sum` is a slice of scopes which have no duration and will be set + /// to the sum of their branches (without triggering a warning) + #[cfg(not(feature = "bench"))] + pub fn print(self, name: S1, pref: S2, set_sum: &[&'static str]) + where + S1: Into, + S2: Into, + { + let name = name.into(); + let pref = pref.into(); + + println!("; {}{} {}", pref, conf.emph(&name), conf.emph("{")); + let sub_pref = format!("{} ", pref); + + let (tree, stats, subs) = self.extract(); + tree.print(sub_pref.clone(), set_sum); + if stats.has_non_zero() { + println!("; {}{}:", sub_pref, conf.happy("metrics")); + stats.print(format!("{}{} ", sub_pref, conf.happy("|"))) + } + + scoped! { + for (sub_name, sub) in subs { + println!("; ") ; + sub.print( + format!("{}/{}", name, sub_name), sub_pref.clone(), set_sum + ) ; + } + } + println!("; {}{}", pref, conf.emph("}")) } - println!("; {}{}", pref, conf.emph("}")) - } -} \ No newline at end of file +} diff --git a/src/common/smt.rs b/src/common/smt.rs index 70d4cf9b..32c88b5b 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -1,32 +1,31 @@ //! SMT-related zero-cost wrappers. -use std::str::FromStr ; +use std::str::FromStr; use rsmt2::{ - print::*, - parse::{ IdentParser, ModelParser }, -} ; + parse::{IdentParser, ModelParser}, + print::*, +}; use common::{ - *, - var_to::vals::{ VarValsMap, VarValsSet } -} ; -use data::Constraint ; -use instance::Clause ; - + var_to::vals::{VarValsMap, VarValsSet}, + *, +}; +use data::Constraint; +use instance::Clause; /// Initial setup for a solver. /// /// - declares all the datatypes /// - defines all the functions /// - asserts all the side-clauses if `preproc` is false -pub fn init( - solver: & mut Solver

, instance: I -) -> Res<()> -where I: AsRef { - dtyp::write_all(solver, "") ? ; - fun::write_all(solver, "", true) ? ; - instance.as_ref().assert_side_clauses(solver) +pub fn init(solver: &mut Solver

, instance: I) -> Res<()> +where + I: AsRef, +{ + dtyp::write_all(solver, "")?; + fun::write_all(solver, "", true)?; + instance.as_ref().assert_side_clauses(solver) } /// Initial setup for a preprocessing solver. @@ -34,340 +33,325 @@ where I: AsRef { /// - declares all the datatypes /// - defines all the functions /// - asserts all the side-clauses if `preproc` is false -pub fn preproc_init

( solver: & mut Solver

) -> Res<()> { - dtyp::write_all(solver, "") ? ; - fun::write_all(solver, "", true) ? ; - Ok(()) +pub fn preproc_init

(solver: &mut Solver

) -> Res<()> { + dtyp::write_all(solver, "")?; + fun::write_all(solver, "", true)?; + Ok(()) } - /// Resets a smt solver. /// /// Use this and not `solver.reset()`. This declares all the /// datatypes/functions used in the instance. -pub fn reset( - solver: & mut Solver

, instance: I -) -> Res<()> -where I: AsRef { - solver.reset() ? ; - init(solver, instance) +pub fn reset(solver: &mut Solver

, instance: I) -> Res<()> +where + I: AsRef, +{ + solver.reset()?; + init(solver, instance) } /// Resets a smt preprocessing solver. /// /// Use this and not `solver.reset()`. This declares all the /// datatypes/functions used in the instance. -pub fn preproc_reset

( solver: & mut Solver

) -> Res<()> { - solver.reset() ? ; - preproc_init(solver) +pub fn preproc_reset

(solver: &mut Solver

) -> Res<()> { + solver.reset()?; + preproc_init(solver) } - - /// SMT-prints a term using the default var writer. pub struct SmtTerm<'a> { - /// The term. - pub term: & 'a Term, + /// The term. + pub term: &'a Term, } impl<'a> SmtTerm<'a> { - /// Constructor. - pub fn new(term: & 'a Term) -> Self { - SmtTerm { term } - } + /// Constructor. + pub fn new(term: &'a Term) -> Self { + SmtTerm { term } + } } impl<'a> Expr2Smt<()> for SmtTerm<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - self.term.write( - w, |w, var| var.default_write(w) - ) ? ; - Ok(()) - } + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + self.term.write(w, |w, var| var.default_write(w))?; + Ok(()) + } } - - /// Smt-prints a clause that has no predicate application. pub struct SmtSideClause<'a> { - /// The clause. - pub clause: & 'a Clause, + /// The clause. + pub clause: &'a Clause, } impl<'a> SmtSideClause<'a> { - /// Constructor. - pub fn new(clause: & 'a Clause) -> Self { - SmtSideClause { clause } - } + /// Constructor. + pub fn new(clause: &'a Clause) -> Self { + SmtSideClause { clause } + } } impl<'a> Expr2Smt<()> for SmtSideClause<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - self.clause.forall_write( - w, |w, var_info| var_info.idx.default_write(w), |_, _, _| panic!( - "illegal side clause: found predicate application(s)" - ), 2 - ) ? ; - Ok(()) - } + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + self.clause.forall_write( + w, + |w, var_info| var_info.idx.default_write(w), + |_, _, _| panic!("illegal side clause: found predicate application(s)"), + 2, + )?; + Ok(()) + } } - /// SMT-prints a collection of terms as a conjunction with default var writer. pub struct SmtConj<'a, Trms> { - /// Conjunction. - terms: Trms, - /// True if the terms have function applications. - has_fun_apps: bool, - /// Variable informations. - infos: & 'a VarInfos + /// Conjunction. + terms: Trms, + /// True if the terms have function applications. + has_fun_apps: bool, + /// Variable informations. + infos: &'a VarInfos, } impl<'a, 'b, Trms> SmtConj<'b, Trms> -where Trms: Iterator + ExactSizeIterator + Clone { - /// Constructor. - pub fn new(terms: IntoIter, infos: & 'b VarInfos) -> Self - where IntoIter: IntoIterator { - let terms = terms.into_iter() ; - let mut has_fun_apps = false ; - for term in terms.clone() { - if term.has_fun_apps() { - has_fun_apps = true ; - break - } +where + Trms: Iterator + ExactSizeIterator + Clone, +{ + /// Constructor. + pub fn new(terms: IntoIter, infos: &'b VarInfos) -> Self + where + IntoIter: IntoIterator, + { + let terms = terms.into_iter(); + let mut has_fun_apps = false; + for term in terms.clone() { + if term.has_fun_apps() { + has_fun_apps = true; + break; + } + } + SmtConj { + terms, + has_fun_apps, + infos, + } } - SmtConj { terms, has_fun_apps, infos } - } - - /// Checks if this conjunction is unsatisfiable. - fn is_unsat( - & self, solver: & mut Solver, actlit: Option<& Actlit> - ) -> Res { - if self.terms.len() == 0 { return Ok(false) } - if ! self.has_fun_apps { - for var in self.infos { - if var.active { - solver.declare_const(& var.idx, var.typ.get()) ? + + /// Checks if this conjunction is unsatisfiable. + fn is_unsat( + &self, + solver: &mut Solver, + actlit: Option<&Actlit>, + ) -> Res { + if self.terms.len() == 0 { + return Ok(false); + } + if !self.has_fun_apps { + for var in self.infos { + if var.active { + solver.declare_const(&var.idx, var.typ.get())? + } + } } - } + solver.assert(self)?; + let sat = solver.check_sat_act(actlit)?; + Ok(!sat) } - solver.assert( self ) ? ; - let sat = solver.check_sat_act(actlit) ? ; - Ok(! sat) - } } impl<'a, 'b, Trms> Expr2Smt<()> for SmtConj<'b, Trms> -where Trms: Iterator + ExactSizeIterator + Clone { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - let suffix = if self.has_fun_apps { - write!(w, "(exists (") ? ; - let mut inactive = 0 ; - for var in self.infos { - if var.active { - write!(w, " (") ? ; - var.idx.default_write(w) ? ; - write!(w, " {})", var.typ) ? +where + Trms: Iterator + ExactSizeIterator + Clone, +{ + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + let suffix = if self.has_fun_apps { + write!(w, "(exists (")?; + let mut inactive = 0; + for var in self.infos { + if var.active { + write!(w, " (")?; + var.idx.default_write(w)?; + write!(w, " {})", var.typ)? + } else { + inactive += 1 + } + } + if inactive == self.infos.len() { + write!(w, " (unused Bool)")? + } + write!(w, " ) ")?; + ")" } else { - inactive += 1 + "" + }; + + if self.terms.len() == 0 { + write!(w, "true")? + } else { + write!(w, "(and")?; + for term in self.terms.clone() { + write!(w, " ")?; + term.write(w, |w, var| var.default_write(w))?; + } + write!(w, ")")? } - } - if inactive == self.infos.len() { - write!(w, " (unused Bool)") ? - } - write!(w, " ) ") ? ; - ")" - } else { - "" - } ; - - if self.terms.len() == 0 { - write!(w, "true") ? - } else { - write!(w, "(and") ? ; - for term in self.terms.clone() { - write!(w, " ") ? ; - term.write( - w, |w, var| var.default_write(w) - ) ? ; - } - write!(w, ")") ? + write!(w, "{}", suffix)?; + Ok(()) } - write!(w, "{}", suffix) ? ; - Ok(()) - } } - /// SMT-prints a collection of terms as a conjunction with default var writer. pub struct TermConj { - /// Conjunction. - terms: Trms, + /// Conjunction. + terms: Trms, } impl<'a, Trms> TermConj -where Trms: Iterator + ExactSizeIterator + Clone { - /// Constructor. - pub fn new(terms: IntoIter) -> Self - where IntoIter: IntoIterator { - let terms = terms.into_iter() ; - TermConj { terms } - } +where + Trms: Iterator + ExactSizeIterator + Clone, +{ + /// Constructor. + pub fn new(terms: IntoIter) -> Self + where + IntoIter: IntoIterator, + { + let terms = terms.into_iter(); + TermConj { terms } + } } impl<'a, Trms> Expr2Smt for TermConj -where Trms: Iterator + ExactSizeIterator + Clone { - fn expr_to_smt2( - & self, w: & mut Writer, pos: bool - ) -> SmtRes<()> { - if ! pos { - write!(w, "(not ") ? - } - if self.terms.len() == 0 { - write!(w, "true") ? - } else { - write!(w, "(and") ? ; - for term in self.terms.clone() { - write!(w, " ") ? ; - term.write( - w, |w, var| var.default_write(w) - ) ? ; - } - write!(w, ")") ? - } - if ! pos { - write!(w, ")") ? +where + Trms: Iterator + ExactSizeIterator + Clone, +{ + fn expr_to_smt2(&self, w: &mut Writer, pos: bool) -> SmtRes<()> { + if !pos { + write!(w, "(not ")? + } + if self.terms.len() == 0 { + write!(w, "true")? + } else { + write!(w, "(and")?; + for term in self.terms.clone() { + write!(w, " ")?; + term.write(w, |w, var| var.default_write(w))?; + } + write!(w, ")")? + } + if !pos { + write!(w, ")")? + } + Ok(()) } - Ok(()) - } } - - /// SMT-prints an implication `/\ (set \ term) => term`. pub struct SmtImpl<'a> { - /// Set of terms. - pub set: & 'a TermSet, - /// Term to remove from `set`. - pub trm: & 'a Term, + /// Set of terms. + pub set: &'a TermSet, + /// Term to remove from `set`. + pub trm: &'a Term, } impl<'a> SmtImpl<'a> { - /// Constructor. - /// - /// Returns `None` if `set.is_empty()`. - pub fn new(set: & 'a TermSet, trm: & 'a Term) -> Option { - if ! set.is_empty() { - Some( SmtImpl { set, trm } ) - } else { - None + /// Constructor. + /// + /// Returns `None` if `set.is_empty()`. + pub fn new(set: &'a TermSet, trm: &'a Term) -> Option { + if !set.is_empty() { + Some(SmtImpl { set, trm }) + } else { + None + } } - } } impl<'a> Expr2Smt<()> for SmtImpl<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - debug_assert! { ! self.set.is_empty() } - write!(w, "(and (not ") ? ; - self.trm.write(w, |w, var| var.default_write(w)) ? ; - write!(w, ") ") ? ; - if self.set.len() <= 1 { - write!(w, "true") ? - } else { - write!(w, "(and ") ? ; - for term in self.set { - if term != self.trm { - write!(w, " ") ? ; - term.write(w, |w, var| var.default_write(w)) ? + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + debug_assert! { ! self.set.is_empty() } + write!(w, "(and (not ")?; + self.trm.write(w, |w, var| var.default_write(w))?; + write!(w, ") ")?; + if self.set.len() <= 1 { + write!(w, "true")? + } else { + write!(w, "(and ")?; + for term in self.set { + if term != self.trm { + write!(w, " ")?; + term.write(w, |w, var| var.default_write(w))? + } + } + write!(w, ")")? } - } - write!(w, ")") ? + write!(w, ")")?; + Ok(()) } - write!(w, ")") ? ; - Ok(()) - } } - - /// Wrapper around a predicate/sample pair that SMT-prints it as an identifier. /// /// In practice, will be printed as `format!("|p_{} {}|", pred, smpl.uid())`. pub struct SmtSample<'a> { - /// Predicate index. - pub pred: PrdIdx, - /// Reference to a sample. - pub smpl: & 'a VarVals, + /// Predicate index. + pub pred: PrdIdx, + /// Reference to a sample. + pub smpl: &'a VarVals, } impl<'a> SmtSample<'a> { - /// Constructor. - pub fn new(pred: PrdIdx, smpl: & 'a VarVals) -> Self { - SmtSample { pred, smpl } - } + /// Constructor. + pub fn new(pred: PrdIdx, smpl: &'a VarVals) -> Self { + SmtSample { pred, smpl } + } } impl<'a> Expr2Smt<()> for SmtSample<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!( w, "|p_{} {}|", self.pred, self.smpl.uid() ) ? ; - Ok(()) - } + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "|p_{} {}|", self.pred, self.smpl.uid())?; + Ok(()) + } } impl<'a> Sym2Smt<()> for SmtSample<'a> { - fn sym_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> where Writer: Write { - self.expr_to_smt2(w, ()) - } + fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> + where + Writer: Write, + { + self.expr_to_smt2(w, ()) + } } - /// Wrapper around constraints that forces smt printing consistent with /// [`SmtSample`][swrap]. /// /// [swrap]: struct.SmtSample.html (SmtSample struct) pub struct SmtConstraint<'a> { - /// Reference to the constraint. - pub constr: & 'a Constraint, + /// Reference to the constraint. + pub constr: &'a Constraint, } impl<'a> SmtConstraint<'a> { - /// Constructor. - pub fn new(constr: & 'a Constraint) -> Self { - SmtConstraint { constr } - } + /// Constructor. + pub fn new(constr: &'a Constraint) -> Self { + SmtConstraint { constr } + } } impl<'a> Expr2Smt<()> for SmtConstraint<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!(w, "(=> ") ? ; - if let Some(lhs) = self.constr.lhs() { - write!(w, "(and") ? ; - for (pred, samples) in lhs { - for sample in samples { - write!(w, " ", ) ? ; - SmtSample::new( - * pred, sample - ).expr_to_smt2(w, ()) ? + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "(=> ")?; + if let Some(lhs) = self.constr.lhs() { + write!(w, "(and")?; + for (pred, samples) in lhs { + for sample in samples { + write!(w, " ",)?; + SmtSample::new(*pred, sample).expr_to_smt2(w, ())? + } + } + write!(w, ") ")? + } else { + write!(w, "false ")? } - } - write!(w, ") ") ? - } else { - write!(w, "false ") ? - } - if let Some(rhs) = self.constr.rhs() { - SmtSample::new( - rhs.pred, & rhs.args - ).expr_to_smt2(w, ()) ? - } else { - write!(w, "false") ? ; + if let Some(rhs) = self.constr.rhs() { + SmtSample::new(rhs.pred, &rhs.args).expr_to_smt2(w, ())? + } else { + write!(w, "false")?; + } + write!(w, ")")?; + Ok(()) } - write!(w, ")") ? ; - Ok(()) - } } - /// Wrapper for activation literals activating samples for some predicate. /// /// `Sym2Smt` implementation just yields the actlit, used to declare said @@ -379,210 +363,189 @@ impl<'a> Expr2Smt<()> for SmtConstraint<'a> { /// /// Used by the ICE learner. pub struct SmtActSamples { - /// Activation literal. - pub actlit: Actlit, - /// Predicate. - pub pred: PrdIdx, - /// Samples. - pub unc: Samples, - /// Indicates whether we're assuming the samples positive or negative. - pub pos: bool, + /// Activation literal. + pub actlit: Actlit, + /// Predicate. + pub pred: PrdIdx, + /// Samples. + pub unc: Samples, + /// Indicates whether we're assuming the samples positive or negative. + pub pos: bool, } impl SmtActSamples { - /// Constructor. - pub fn new( - solver: & mut Solver, pred: PrdIdx, unc: Samples, pos: bool - ) -> Res { - let actlit = solver.get_actlit() ? ; - Ok( SmtActSamples { actlit, pred, unc, pos } ) - } - - /// Sets the actlit to `pos` and destroys itself. - pub fn force( - self, solver: & mut Solver, pos: bool - ) -> Res<()> { - solver.set_actlit(self.actlit, pos) ? ; - Ok(()) - } -} -impl<'a> Expr2Smt<()> for SmtActSamples<& 'a [ VarVals ]> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!(w, "(=> ") ? ; - self.actlit.write(w) ? ; - write!( - w, " ({}", if self.pos { "and" } else { "not (or" } - ) ? ; - for unc in self.unc { - write!(w, " ", ) ? ; - SmtSample::new(self.pred, unc).expr_to_smt2(w, ()) ? + /// Constructor. + pub fn new( + solver: &mut Solver, + pred: PrdIdx, + unc: Samples, + pos: bool, + ) -> Res { + let actlit = solver.get_actlit()?; + Ok(SmtActSamples { + actlit, + pred, + unc, + pos, + }) } - write!(w, "))") ? ; - if ! self.pos { - write!(w, ")") ? + + /// Sets the actlit to `pos` and destroys itself. + pub fn force(self, solver: &mut Solver, pos: bool) -> Res<()> { + solver.set_actlit(self.actlit, pos)?; + Ok(()) } - Ok(()) - } -} -impl<'a, T> Expr2Smt<()> for SmtActSamples< & 'a VarValsMap > { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!(w, "(=> ") ? ; - self.actlit.write(w) ? ; - write!( - w, " ({}", if self.pos { "and" } else { "not (or" } - ) ? ; - for (unc, _) in self.unc { - write!(w, " ", ) ? ; - SmtSample::new(self.pred, unc).expr_to_smt2(w, ()) ? +} +impl<'a> Expr2Smt<()> for SmtActSamples<&'a [VarVals]> { + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "(=> ")?; + self.actlit.write(w)?; + write!(w, " ({}", if self.pos { "and" } else { "not (or" })?; + for unc in self.unc { + write!(w, " ",)?; + SmtSample::new(self.pred, unc).expr_to_smt2(w, ())? + } + write!(w, "))")?; + if !self.pos { + write!(w, ")")? + } + Ok(()) } - write!(w, "))") ? ; - if ! self.pos { - write!(w, ")") ? +} +impl<'a, T> Expr2Smt<()> for SmtActSamples<&'a VarValsMap> { + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "(=> ")?; + self.actlit.write(w)?; + write!(w, " ({}", if self.pos { "and" } else { "not (or" })?; + for (unc, _) in self.unc { + write!(w, " ",)?; + SmtSample::new(self.pred, unc).expr_to_smt2(w, ())? + } + write!(w, "))")?; + if !self.pos { + write!(w, ")")? + } + Ok(()) } - Ok(()) - } } - - /// Wrapper around some terms and some values for these terms. /// /// Asserts that each term is equal to the corresponding value. pub struct EqConj<'a> { - /// Terms. - pub terms: & 'a [ Term ], - /// Values. - pub vals: & 'a VarVals, + /// Terms. + pub terms: &'a [Term], + /// Values. + pub vals: &'a VarVals, } impl<'a> EqConj<'a> { - /// Constructor. - /// - /// Both lists must have the same length. - pub fn new(terms: & 'a [ Term ], vals: & 'a VarVals) -> Self { - debug_assert_eq! { terms.len(), vals.len() } + /// Constructor. + /// + /// Both lists must have the same length. + pub fn new(terms: &'a [Term], vals: &'a VarVals) -> Self { + debug_assert_eq! { terms.len(), vals.len() } - EqConj { terms, vals } - } + EqConj { terms, vals } + } } impl<'a> Expr2Smt<()> for EqConj<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - let mut is_first = true ; - for (term, val) in self.terms.iter().zip( self.vals.iter() ) { - if ! val.is_known() { - continue - } - if is_first { - write!(w, "(and") ? ; - is_first = false - } - write!(w, " (= ") ? ; - term.write(w, |w, var| write!(w, "{}", var.default_str())) ? ; - write!(w, " ") ? ; - val.expr_to_smt2(w, ()) ? ; - write!(w, ")") ? - } - if is_first { - write!(w, "true") ? - } else { - write!(w, ")") ? + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + let mut is_first = true; + for (term, val) in self.terms.iter().zip(self.vals.iter()) { + if !val.is_known() { + continue; + } + if is_first { + write!(w, "(and")?; + is_first = false + } + write!(w, " (= ")?; + term.write(w, |w, var| write!(w, "{}", var.default_str()))?; + write!(w, " ")?; + val.expr_to_smt2(w, ())?; + write!(w, ")")? + } + if is_first { + write!(w, "true")? + } else { + write!(w, ")")? + } + Ok(()) } - Ok(()) - } } - - /// Wrapper for some arguments and a disjunction of terms. /// /// Corresponds to the disjunction of `(= args v)` for `v` in `vals`. pub struct DisjArgs<'a> { - /// Arguments. - pub args: & 'a VarTerms, - /// Values to force the arguments to. - pub vals: & 'a VarValsSet, + /// Arguments. + pub args: &'a VarTerms, + /// Values to force the arguments to. + pub vals: &'a VarValsSet, } impl<'a> DisjArgs<'a> { - /// Constructor. - /// - /// Error if `args` or `vals` is empty. - #[inline] - pub fn new( - args: & 'a VarTerms, vals: & 'a VarValsSet - ) -> Res { - if args.is_empty() { - bail!("can't create a `DisjArgs` with empty `args`") - } - if vals.is_empty() { - bail!("can't create an empty `DisjArgs`") + /// Constructor. + /// + /// Error if `args` or `vals` is empty. + #[inline] + pub fn new(args: &'a VarTerms, vals: &'a VarValsSet) -> Res { + if args.is_empty() { + bail!("can't create a `DisjArgs` with empty `args`") + } + if vals.is_empty() { + bail!("can't create an empty `DisjArgs`") + } + Ok(DisjArgs { args, vals }) } - Ok( DisjArgs { args, vals } ) - } } impl<'a> Expr2Smt<()> for DisjArgs<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!(w, "(or") ? ; - for vals in self.vals { - write!(w, " (and") ? ; - debug_assert_eq! { self.args.len(), vals.len() } - let mut at_least_one = false ; - for (arg, val) in self.args.iter().zip( vals.iter() ) { - if val.is_known() { - at_least_one = true ; - write!(w, " (= ") ? ; - arg.write(w, |w, var| write!(w, "{}", var.default_str())) ? ; - write!(w, " ") ? ; - val.expr_to_smt2(w, ()) ? ; - write!(w, ")") ? + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "(or")?; + for vals in self.vals { + write!(w, " (and")?; + debug_assert_eq! { self.args.len(), vals.len() } + let mut at_least_one = false; + for (arg, val) in self.args.iter().zip(vals.iter()) { + if val.is_known() { + at_least_one = true; + write!(w, " (= ")?; + arg.write(w, |w, var| write!(w, "{}", var.default_str()))?; + write!(w, " ")?; + val.expr_to_smt2(w, ())?; + write!(w, ")")? + } + } + if !at_least_one { + write!(w, " true")? + } + write!(w, ")")? } - } - if ! at_least_one { - write!(w, " true") ? - } - write!(w, ")") ? - } - write!(w, ")") ? ; + write!(w, ")")?; - Ok(()) - } + Ok(()) + } } - - - - /// Type of values returned by the full parser. #[derive(Debug)] pub enum FPVal { - /// A normal value. - Val(Val), - /// A function to array conversion. - FunToArray(String), - /// A function declaration. - FunDef(String), + /// A normal value. + Val(Val), + /// A function to array conversion. + FunToArray(String), + /// A function declaration. + FunDef(String), } /// Type of variables returned by the full parser. #[derive(Debug)] pub enum FPVar { - /// A normal variable. - Var(VarIdx), - /// A symbol that's not a normal variable. - Sym(String), + /// A normal variable. + Var(VarIdx), + /// A symbol that's not a normal variable. + Sym(String), } - - - /// Input signature for a model that's not fixed yet. -type FPSig = Vec<(FPVar, Typ)> ; - - +type FPSig = Vec<(FPVar, Typ)>; /// Unit type parsing the output of the SMT solver. /// @@ -590,350 +553,326 @@ type FPSig = Vec<(FPVar, Typ)> ; /// parse models of the falsification of a single clause, where the /// variables of the clause are written as `v` in smt2. #[derive(Clone, Copy)] -pub struct FullParser ; +pub struct FullParser; impl FullParser { - /// Translates a raw model to a normal model. - pub fn fix_model( - self, mut model: Vec<(FPVar, FPSig, Typ, FPVal)> - ) -> Res< Vec<(VarIdx, Typ, Val)> > { - let mut fun_defs: HashMap = HashMap::new() ; - let mut res = Vec::with_capacity( model.len() ) ; - let mut postponed = Vec::new() ; - - let mut instance = Instance::new() ; - let mut context = ::parse::ParserCxt::new() ; - let dummy_profiler = Profiler::new() ; - - let mut stuck ; - - while ! model.is_empty() { - let model_len = model.len() ; - - while let Some((var, sig, mut typ, val)) = model.pop() { - match var { - - FPVar::Var(var) => match val { - FPVal::Val(val) => { - res.push((var, typ, val)) - }, - FPVal::FunToArray(fun) => if let Some( - (sig, def) - ) = fun_defs.get(& fun) { - debug_assert_eq! { sig.len(), 1 } - let idx_type = sig.iter().next().unwrap() ; - let array = val::array_of_fun(idx_type.clone(), def).chain_err( - || "while recovering array from function definition" - ) ? ; - res.push( (var, typ, array) ) - } else { - postponed.push( - (FPVar::Var(var), sig, typ, FPVal::FunToArray(fun)) - ) - }, - FPVal::FunDef(def) => bail!( - "inconsistent model, \ - normal variable {} associated to function definition {}", - var.default_str(), def - ), - }, - - FPVar::Sym(name) => { - use ::instance::info::VarInfo ; - - let (mut nu_sig, mut var_infos): ( - VarMap, VarMap<_> - ) = ( - VarMap::with_capacity( sig.len() ), - VarMap::with_capacity( sig.len() ), - ) ; - - for (var, typ) in & sig { - let idx = var_infos.next_index() ; - if let FPVar::Sym(ref var) = * var { - nu_sig.push( typ.clone() ) ; - var_infos.push( - VarInfo::new(var.clone(), typ.clone(), idx) - ) ; - } else { - bail!("inconsistent function parameters: variable index") - } - } - - if let Ok( Some(mut term) ) = match val { - FPVal::FunDef(ref fun) => { - let mut var_hmap: BTreeMap< - & str, VarIdx - > = BTreeMap::new() ; - - for (idx, info) in var_infos.index_iter() { - let prev = var_hmap.insert( - & info.name, idx - ) ; - debug_assert_eq! { prev, None } + /// Translates a raw model to a normal model. + pub fn fix_model( + self, + mut model: Vec<(FPVar, FPSig, Typ, FPVal)>, + ) -> Res> { + let mut fun_defs: HashMap = HashMap::new(); + let mut res = Vec::with_capacity(model.len()); + let mut postponed = Vec::new(); + + let mut instance = Instance::new(); + let mut context = ::parse::ParserCxt::new(); + let dummy_profiler = Profiler::new(); + + let mut stuck; + + while !model.is_empty() { + let model_len = model.len(); + + while let Some((var, sig, mut typ, val)) = model.pop() { + match var { + FPVar::Var(var) => match val { + FPVal::Val(val) => res.push((var, typ, val)), + FPVal::FunToArray(fun) => if let Some((sig, def)) = fun_defs.get(&fun) { + debug_assert_eq! { sig.len(), 1 } + let idx_type = sig.iter().next().unwrap(); + let array = val::array_of_fun(idx_type.clone(), def) + .chain_err(|| "while recovering array from function definition")?; + res.push((var, typ, array)) + } else { + postponed.push((FPVar::Var(var), sig, typ, FPVal::FunToArray(fun))) + }, + FPVal::FunDef(def) => bail!( + "inconsistent model, \ + normal variable {} associated to function definition {}", + var.default_str(), + def + ), + }, + + FPVar::Sym(name) => { + use instance::info::VarInfo; + + let (mut nu_sig, mut var_infos): ( + VarMap, + VarMap<_>, + ) = ( + VarMap::with_capacity(sig.len()), + VarMap::with_capacity(sig.len()), + ); + + for (var, typ) in &sig { + let idx = var_infos.next_index(); + if let FPVar::Sym(ref var) = *var { + nu_sig.push(typ.clone()); + var_infos.push(VarInfo::new(var.clone(), typ.clone(), idx)); + } else { + bail!("inconsistent function parameters: variable index") + } + } + + if let Ok(Some(mut term)) = match val { + FPVal::FunDef(ref fun) => { + let mut var_hmap: BTreeMap< + &str, + VarIdx, + > = BTreeMap::new(); + + for (idx, info) in var_infos.index_iter() { + let prev = var_hmap.insert(&info.name, idx); + debug_assert_eq! { prev, None } + } + + let mut parser = context.parser(fun, 0, &dummy_profiler); + + parser.term_opt(&var_infos, &var_hmap, &instance) + } + + FPVal::Val(ref val) => Ok(val.to_term()), + + FPVal::FunToArray(fun) => bail!( + "inconsistent model, function name {} \ + associated to function to array conversion {}", + name, + fun + ), + } { + if let Some(new_typ) = term.typ().merge(&typ) { + if typ != new_typ { + typ = new_typ.clone() + } + if term.typ() != new_typ { + if let Some(nu_term) = term.force_dtyp(new_typ) { + term = nu_term + } + } + } + debug_assert_eq! { term.typ(), typ } + let prev = instance.add_define_fun( + name.clone(), + var_infos, + ::parse::PTTerms::tterm(TTerm::T(term.clone())), + ); + debug_assert! { prev.is_none() } + let prev = fun_defs.insert(name, (nu_sig, term)); + debug_assert_eq! { prev, None } + } else { + postponed.push((FPVar::Sym(name), sig, typ, val)) + } + } } + } - let mut parser = context.parser(fun, 0, & dummy_profiler) ; - - parser.term_opt( - & var_infos, & var_hmap, & instance - ) - }, - - FPVal::Val(ref val) => Ok( val.to_term() ), + stuck = model_len == postponed.len(); - FPVal::FunToArray(fun) => bail!( - "inconsistent model, function name {} \ - associated to function to array conversion {}", - name, fun - ), - } { - if let Some(new_typ) = term.typ().merge(& typ) { - if typ != new_typ { - typ = new_typ.clone() - } - if term.typ() != new_typ { - if let Some(nu_term) = term.force_dtyp(new_typ) { - term = nu_term - } - } - } - debug_assert_eq! { term.typ(), typ } - let prev = instance.add_define_fun( - name.clone(), var_infos, - ::parse::PTTerms::tterm( TTerm::T(term.clone()) ) - ) ; - debug_assert! { prev.is_none() } - let prev = fun_defs.insert(name, (nu_sig, term)) ; - debug_assert_eq! { prev, None } - } else { - postponed.push( - (FPVar::Sym(name), sig, typ, val) - ) + if stuck { + bail!("failed to parse model from solver") } - }, + model.extend(postponed.drain(0..)) } - } - - stuck = model_len == postponed.len() ; - - if stuck { - bail!("failed to parse model from solver") - } - - model.extend( postponed.drain(0..) ) - - } - - Ok(res) - } -} - -impl<'a> IdentParser for FullParser { - fn parse_ident(self, input: & 'a str) -> SmtRes { - if input.len() >= 2 && & input[0..2] == "v_" { - match usize::from_str(& input[2..]) { - Ok(idx) => Ok( FPVar::Var( idx.into() ) ), - Err(e) => bail!( - "could not retrieve var index from `{}`: {}", input, e - ), - } - } else { - Ok( FPVar::Sym( input.into() ) ) + Ok(res) } - } - fn parse_type(self, input: & 'a str) -> SmtRes { - let mut cxt = ::parse::ParserCxt::new() ; - let dummy_profiler = Profiler::new() ; - let mut parser = cxt.parser(input, 0, & dummy_profiler) ; - match parser.sort_opt() { - Ok( Some(s) ) => Ok(s), - _ => Err( - format!("unexpected type `{}`", input).into() - ), - } - } -} - -impl<'a> ModelParser for FullParser { - fn parse_value( - self, input: & 'a str, - _id: & FPVar, _params: & [ (FPVar, Typ) ], _out: & Typ - ) -> SmtRes { - let mut cxt = ::parse::ParserCxt::new() ; - let dummy_profiler = Profiler::new() ; - let mut parser = cxt.parser(input, 0, & dummy_profiler) ; - - let negated = { - let start = parser.pos() ; - if parser.tag_opt("(") && { - parser.ws_cmt() ; parser.tag_opt("-") - } { - parser.ws_cmt() ; - true - } else { - parser.backtrack_to(start) ; - false - } - } ; - - if let Some(val) = parser.int() { - let val = if negated { - parser.ws_cmt() ; - if parser.tag_opt(")") { - - val - } else { - bail!("illegal integer value, missing closing `)`") - } - } else { - val - } ; - - Ok( FPVal::Val( val::int(val) ) ) +} - } else if let Ok(Some(val)) = parser.real() { - let val = if negated { - parser.ws_cmt() ; - if parser.tag_opt(")") { - - val +impl<'a> IdentParser for FullParser { + fn parse_ident(self, input: &'a str) -> SmtRes { + if input.len() >= 2 && &input[0..2] == "v_" { + match usize::from_str(&input[2..]) { + Ok(idx) => Ok(FPVar::Var(idx.into())), + Err(e) => bail!("could not retrieve var index from `{}`: {}", input, e), + } } else { - bail!("illegal real value, missing closing `)`") + Ok(FPVar::Sym(input.into())) } - } else { - val - } ; - - Ok( FPVal::Val( val::real(val) ) ) - - } else if let Some(val) = parser.bool() { - debug_assert! { ! negated } - - Ok( FPVal::Val( val::bool(val) ) ) - - } else if let Ok( Some(term) ) = parser.term_opt( - & vec![].into(), & BTreeMap::new(), & Instance::new() - ) { - if let Some(val) = term.val() { - Ok( FPVal::Val(val) ) - } else { - bail!("cannot turn term into a value: {}", term) - } - - } else if parser.tag_opt("(") && { - parser.ws_cmt() ; parser.tag_opt("_") - } && { - parser.ws_cmt() ; parser.tag_opt("as-array") - } { - // Try function to array conversion. - - debug_assert! { ! negated } - parser.ws_cmt() ; - - if let Ok((_, ident)) = parser.ident() { - parser.ws_cmt() ; - if parser.tag_opt(")") { - Ok( FPVal::FunToArray( ident.into() ) ) - } else { - bail!("ill-formed value, missing closing paren") + } + fn parse_type(self, input: &'a str) -> SmtRes { + let mut cxt = ::parse::ParserCxt::new(); + let dummy_profiler = Profiler::new(); + let mut parser = cxt.parser(input, 0, &dummy_profiler); + match parser.sort_opt() { + Ok(Some(s)) => Ok(s), + _ => Err(format!("unexpected type `{}`", input).into()), } - } else { - bail!("expected symbol in function to array conversion `{}`", input) - } - - } else { - debug_assert! { ! negated } - Ok( FPVal::FunDef(input.into()) ) } - } } +impl<'a> ModelParser for FullParser { + fn parse_value( + self, + input: &'a str, + _id: &FPVar, + _params: &[(FPVar, Typ)], + _out: &Typ, + ) -> SmtRes { + let mut cxt = ::parse::ParserCxt::new(); + let dummy_profiler = Profiler::new(); + let mut parser = cxt.parser(input, 0, &dummy_profiler); + + let negated = { + let start = parser.pos(); + if parser.tag_opt("(") && { + parser.ws_cmt(); + parser.tag_opt("-") + } { + parser.ws_cmt(); + true + } else { + parser.backtrack_to(start); + false + } + }; + + if let Some(val) = parser.int() { + let val = if negated { + parser.ws_cmt(); + if parser.tag_opt(")") { + -val + } else { + bail!("illegal integer value, missing closing `)`") + } + } else { + val + }; + + Ok(FPVal::Val(val::int(val))) + } else if let Ok(Some(val)) = parser.real() { + let val = if negated { + parser.ws_cmt(); + if parser.tag_opt(")") { + -val + } else { + bail!("illegal real value, missing closing `)`") + } + } else { + val + }; + + Ok(FPVal::Val(val::real(val))) + } else if let Some(val) = parser.bool() { + debug_assert! { ! negated } + + Ok(FPVal::Val(val::bool(val))) + } else if let Ok(Some(term)) = + parser.term_opt(&vec![].into(), &BTreeMap::new(), &Instance::new()) + { + if let Some(val) = term.val() { + Ok(FPVal::Val(val)) + } else { + bail!("cannot turn term into a value: {}", term) + } + } else if parser.tag_opt("(") + && { + parser.ws_cmt(); + parser.tag_opt("_") + } + && { + parser.ws_cmt(); + parser.tag_opt("as-array") + } { + // Try function to array conversion. + debug_assert! { ! negated } + parser.ws_cmt(); - + if let Ok((_, ident)) = parser.ident() { + parser.ws_cmt(); + if parser.tag_opt(")") { + Ok(FPVal::FunToArray(ident.into())) + } else { + bail!("ill-formed value, missing closing paren") + } + } else { + bail!( + "expected symbol in function to array conversion `{}`", + input + ) + } + } else { + debug_assert! { ! negated } + Ok(FPVal::FunDef(input.into())) + } + } +} /// Extends a solver so that it's able to check clause triviality. pub trait ClauseTrivialExt { - /// Checks whether a clause is trivial. - fn is_clause_trivial( - & mut self, & mut ::instance::Clause - ) -> Res< Option > ; + /// Checks whether a clause is trivial. + fn is_clause_trivial(&mut self, &mut ::instance::Clause) -> Res>; } impl ClauseTrivialExt for Solver { - fn is_clause_trivial( - & mut self, clause: & mut ::instance::Clause - ) -> Res< Option > { - let mut lhs: Vec = Vec::with_capacity(17) ; - - for term in clause.lhs_terms() { - match term.bool() { - Some(true) => (), - Some(false) => return Ok( Some(true) ), - _ => { - let neg = term::not( term.clone() ) ; - for term in & lhs { - if neg == * term { - return Ok( Some(true) ) + fn is_clause_trivial(&mut self, clause: &mut ::instance::Clause) -> Res> { + let mut lhs: Vec = Vec::with_capacity(17); + + for term in clause.lhs_terms() { + match term.bool() { + Some(true) => (), + Some(false) => return Ok(Some(true)), + _ => { + let neg = term::not(term.clone()); + for term in &lhs { + if neg == *term { + return Ok(Some(true)); + } + } + lhs.push(term.clone()) + } } - } - lhs.push( term.clone() ) - }, - } - } - - let mut actlit = None ; - - let res = { - - let conj = SmtConj::new( lhs.iter(), & clause.vars ) ; - - if clause.rhs().is_none() && clause.lhs_preds().is_empty() { - - if conj.has_fun_apps { - actlit = Some( self.get_actlit() ? ) } - // Either it is trivial, or falsifiable regardless of the predicates. - let res = if conj.is_unsat( self, actlit.as_ref() ) ? { - Ok( Some(true) ) - } else { - Ok(None) - } ; + let mut actlit = None; - res + let res = { + let conj = SmtConj::new(lhs.iter(), &clause.vars); - } else { + if clause.rhs().is_none() && clause.lhs_preds().is_empty() { + if conj.has_fun_apps { + actlit = Some(self.get_actlit()?) + } - if let Some((pred, args)) = clause.rhs() { - if clause.lhs_preds().get(& pred).map( - |set| set.contains(args) - ).unwrap_or(false) { - return Ok( Some(true) ) - } - } + // Either it is trivial, or falsifiable regardless of the predicates. + if conj.is_unsat(self, actlit.as_ref())? { + Ok(Some(true)) + } else { + Ok(None) + } + } else { + if let Some((pred, args)) = clause.rhs() { + if clause + .lhs_preds() + .get(&pred) + .map(|set| set.contains(args)) + .unwrap_or(false) + { + return Ok(Some(true)); + } + } - if lhs.is_empty() { - Ok( Some(false) ) - } else { + if lhs.is_empty() { + Ok(Some(false)) + } else { + if conj.has_fun_apps { + actlit = Some(self.get_actlit()?) + } - if conj.has_fun_apps { - actlit = Some( self.get_actlit() ? ) - } + conj.is_unsat(self, actlit.as_ref()).map(Some) + } + } + }; - conj.is_unsat( self, actlit.as_ref() ).map(Some) + if let Some(actlit) = actlit { + self.de_actlit(actlit)? } - } - } ; + clause.lhs_terms_checked(); - if let Some(actlit) = actlit { - self.de_actlit(actlit) ? + res } - - clause.lhs_terms_checked() ; - - res - } } diff --git a/src/common/wrappers.rs b/src/common/wrappers.rs index 92f55511..a034ced0 100644 --- a/src/common/wrappers.rs +++ b/src/common/wrappers.rs @@ -1,15 +1,12 @@ //! Zero-cost wrappers for safe indexing. -use std::io::Write ; -use std::fmt ; +use std::fmt; +use std::io::Write; -use rsmt2::print::* ; +use rsmt2::print::*; -use common::{ - SmtRes, VarIndexed, VarTerms, - var_to, -} ; -use term::Term ; +use common::{var_to, SmtRes, VarIndexed, VarTerms}; +use term::Term; wrap_usize!{ #[doc = "Predicate indices."] @@ -24,7 +21,6 @@ wrap_usize!{ map: PrdMap with iter: PrdMapIter } - wrap_usize!{ #[doc = "Variable indices."] VarIdx @@ -38,109 +34,112 @@ wrap_usize!{ map: VarMap with iter: VarMapIter } impl VarIdx { - /// Default way to write variables: `v_`. - pub fn default_write(self, w: & mut W) -> ::std::io::Result<()> - where W: Write { - write!(w, "v_{}", self) - } - /// Default string representation of a variable. - pub fn default_str(self) -> String { - let mut s = vec![] ; - self.default_write(& mut s).unwrap() ; - ::std::str::from_utf8(& s).unwrap().into() - } + /// Default way to write variables: `v_`. + pub fn default_write(self, w: &mut W) -> ::std::io::Result<()> + where + W: Write, + { + write!(w, "v_{}", self) + } + /// Default string representation of a variable. + pub fn default_str(self) -> String { + let mut s = vec![]; + self.default_write(&mut s).unwrap(); + ::std::str::from_utf8(&s).unwrap().into() + } } -impl Into< VarTerms > for VarMap<::term::Term> { - fn into(self) -> VarTerms { - var_to::terms::new(self) - } +impl Into for VarMap<::term::Term> { + fn into(self) -> VarTerms { + var_to::terms::new(self) + } } -impl VarMap< Term > { - /// Removes the arguments of indices **not** in the set. Preserves the order. - /// - /// This is used when useless arguments are detected, to slice predicate - /// applications. - pub fn remove(& self, to_keep: & VarSet) -> VarTerms { - debug_assert! { self.len() >= to_keep.len() } - debug_assert! {{ - let mut okay = true ; - for var in to_keep { - if * var >= self.len() { - okay = false ; break +impl VarMap { + /// Removes the arguments of indices **not** in the set. Preserves the order. + /// + /// This is used when useless arguments are detected, to slice predicate + /// applications. + pub fn remove(&self, to_keep: &VarSet) -> VarTerms { + debug_assert! { self.len() >= to_keep.len() } + debug_assert! {{ + let mut okay = true ; + for var in to_keep { + if * var >= self.len() { + okay = false ; break + } + } + okay + }} + let mut nu_args = VarMap::with_capacity(self.len()); + for (var, term) in self.index_iter() { + if to_keep.contains(&var) { + nu_args.push(term.clone()) + } } - } - okay - }} - let mut nu_args = VarMap::with_capacity( self.len() ) ; - for (var, term) in self.index_iter() { - if to_keep.contains(& var) { - nu_args.push( term.clone() ) - } + nu_args.into() } - nu_args.into() - } - - /// Variable substitution. - pub fn subst>(& self, map: & Map) -> Self { - let mut var_map = VarMap::with_capacity( self.len() ) ; - for term in self.iter() { - let (term, _) = term.subst(map) ; - var_map.push(term) + + /// Variable substitution. + pub fn subst>(&self, map: &Map) -> Self { + let mut var_map = VarMap::with_capacity(self.len()); + for term in self.iter() { + let (term, _) = term.subst(map); + var_map.push(term) + } + var_map } - var_map - } - - /// True if all terms are different variables. - pub fn are_diff_vars(& self) -> bool { - let mut iter = self.iter() ; - while let Some(term) = iter.next() { - if term.var_idx().is_some() { - for other in iter.clone() { - if term == other { - return false - } + + /// True if all terms are different variables. + pub fn are_diff_vars(&self) -> bool { + let mut iter = self.iter(); + while let Some(term) = iter.next() { + if term.var_idx().is_some() { + for other in iter.clone() { + if term == other { + return false; + } + } + } else { + // Not a var. + return false; + } } - } else { - // Not a var. - return false - } + true } - true - } } impl fmt::Display for VarMap { - fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result { - for_first!{ - self.iter() => { - |fst| write!(fmt, "{}", fst) ?, - then |nxt| write!(fmt, " {}", nxt) ? - } + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + for_first!{ + self.iter() => { + |fst| write!(fmt, "{}", fst) ?, + then |nxt| write!(fmt, " {}", nxt) ? + } + } + Ok(()) } - Ok(()) - } } impl Sym2Smt for VarIdx { - fn sym_to_smt2( - & self, w: & mut Writer, _: T - ) -> SmtRes<()> where Writer: Write { - self.default_write(w) ? ; - Ok(()) - } + fn sym_to_smt2(&self, w: &mut Writer, _: T) -> SmtRes<()> + where + Writer: Write, + { + self.default_write(w)?; + Ok(()) + } } impl Expr2Smt for VarIdx { - fn expr_to_smt2( - & self, w: & mut Writer, _: T - ) -> SmtRes<()> where Writer: Write { - self.sym_to_smt2(w, & ()) - } + fn expr_to_smt2(&self, w: &mut Writer, _: T) -> SmtRes<()> + where + Writer: Write, + { + self.sym_to_smt2(w, &()) + } } - wrap_usize!{ #[doc = "Arity."] Arity @@ -150,7 +149,6 @@ wrap_usize!{ map: ArityMap with iter: ArityMapIter } - wrap_usize!{ #[doc = "Clause indices."] ClsIdx @@ -164,7 +162,6 @@ wrap_usize!{ map: ClsMap with iter: ClsMapIter } - wrap_usize! { #[doc = "Clause cluster indices."] CtrIdx @@ -189,7 +186,6 @@ wrap_usize!{ map: CstrMap with iter: CstrMapIter } - wrap_usize!{ #[doc = "Learner index."] LrnIdx @@ -200,7 +196,6 @@ wrap_usize!{ } unsafe impl Send for LrnIdx {} - wrap_usize!{ #[doc = "Teacher Assistant index."] TAsIdx diff --git a/src/data/constraint.rs b/src/data/constraint.rs index 500b4c14..07f9c6cf 100644 --- a/src/data/constraint.rs +++ b/src/data/constraint.rs @@ -1,8 +1,7 @@ -use common::* ; -use var_to::vals::VarValsSet ; - -use super::Sample ; +use common::*; +use var_to::vals::VarValsSet; +use super::Sample; /// Constraints using hashconsed samples. /// @@ -22,371 +21,370 @@ use super::Sample ; /// [lhs rm]: #method.lhs_rm (lhs_rm function) #[derive(Clone, Debug)] pub struct Constraint { - /// Left-hand side. - lhs: Option< PrdHMap< VarValsSet > >, - /// Right-hand side. - rhs: Option< Sample >, + /// Left-hand side. + lhs: Option>, + /// Right-hand side. + rhs: Option, } impl Constraint { - /// Constructor. - /// - /// None if the constraint is a tautology: - /// - /// - `lhs.is_empty` and `rhs.is_empty()` - pub fn new( - lhs: PrdHMap< VarValsSet >, rhs: Option - ) -> Constraint { - Constraint { lhs: Some(lhs), rhs } - } + /// Constructor. + /// + /// None if the constraint is a tautology: + /// + /// - `lhs.is_empty` and `rhs.is_empty()` + pub fn new(lhs: PrdHMap, rhs: Option) -> Constraint { + Constraint { + lhs: Some(lhs), + rhs, + } + } - /// Checks itself. - /// - /// See `Constraint`'s documentation for the list of invariant. - #[cfg(not(debug_assertions))] - pub fn check(& self) -> Res<()> { Ok(()) } - /// Checks itself. - /// - /// See `Constraint`'s documentation for the list of invariant. - #[cfg(debug_assertions)] - pub fn check(& self) -> Res<()> { - if self.lhs.is_none() && self.rhs.is_some() { - bail!("lhs is empty but rhs is not none") + /// Checks itself. + /// + /// See `Constraint`'s documentation for the list of invariant. + #[cfg(not(debug_assertions))] + pub fn check(&self) -> Res<()> { + Ok(()) } - if let Some(lhs) = self.lhs() { - for (_, argss) in lhs { - if argss.is_empty() { - bail!("lhs maps a predicate to nothing") + /// Checks itself. + /// + /// See `Constraint`'s documentation for the list of invariant. + #[cfg(debug_assertions)] + pub fn check(&self) -> Res<()> { + if self.lhs.is_none() && self.rhs.is_some() { + bail!("lhs is empty but rhs is not none") } - for args in argss { - if args.is_partial() { - bail!("partial arguments in constraint ({})", args) - } + if let Some(lhs) = self.lhs() { + for (_, argss) in lhs { + if argss.is_empty() { + bail!("lhs maps a predicate to nothing") + } + for args in argss { + if args.is_partial() { + bail!("partial arguments in constraint ({})", args) + } + } + } } - } - } - if let Some(Sample { ref args, .. }) = self.rhs { - if args.is_partial() { - bail!("partial arguments in constraint ({})", args) - } - } - if let Some(Sample{ pred, ref args }) = self.rhs { - if let Some(argss) = self.lhs.as_ref().and_then( - |map| map.get(& pred) - ) { - if args.set_subsumed(argss) { - bail!("rhs is subsumed by lhs") + if let Some(Sample { ref args, .. }) = self.rhs { + if args.is_partial() { + bail!("partial arguments in constraint ({})", args) + } + } + if let Some(Sample { pred, ref args }) = self.rhs { + if let Some(argss) = self.lhs.as_ref().and_then(|map| map.get(&pred)) { + if args.set_subsumed(argss) { + bail!("rhs is subsumed by lhs") + } + } } - } - } - - Ok(()) - } - /// Number of samples in the lhs. - pub fn lhs_len(& self) -> usize { - let mut count = 0 ; - if let Some(lhs) = self.lhs.as_ref() { - for samples in lhs.values() { - count += samples.len() - } + Ok(()) } - count - } - - /// Lhs accessor. - pub fn lhs(& self) -> Option<& PrdHMap> { - self.lhs.as_ref() - } - /// Rhs accessor. - pub fn rhs(& self) -> Option<& Sample> { - self.rhs.as_ref() - } - /// Removes samples subsumed by a sample from the lhs. - /// - /// Returns the number of sample removed. - /// - /// This function guarantees that `lhs` does not map to an empty `VarValsSet`, - /// so please use this. Do not access `lhs` directly for sample removal. - fn lhs_rm(& mut self, pred: PrdIdx, args: & VarVals) -> usize { - self.lhs.as_mut().map( - |lhs| { - let (pred_rm, rmed) = if let Some(argss) = lhs.get_mut(& pred) { - let mut rmed = if argss.remove(args) { 1 } else { 0 } ; - if conf.teacher.partial && args.is_partial() { - let (subsumed, nu_rmed) = args.set_subsumed_rm(argss) ; - debug_assert! { ! subsumed } - rmed += nu_rmed - } - (argss.is_empty(), rmed) - } else { - (false, 0) - } ; - if pred_rm { - let prev = lhs.remove(& pred) ; - debug_assert! { prev.is_some() } + /// Number of samples in the lhs. + pub fn lhs_len(&self) -> usize { + let mut count = 0; + if let Some(lhs) = self.lhs.as_ref() { + for samples in lhs.values() { + count += samples.len() + } } - rmed - } - ).unwrap_or(0) - } - - /// Transforms a constraint in a tautology. - /// - /// Applies `f` to all samples. - pub fn tautologize(& mut self, mut f: F) -> Res<()> - where F: FnMut(PrdIdx, VarVals) -> Res<()> { - let rhs = ::std::mem::replace(& mut self.rhs, None) ; - if let Some(Sample { pred, args }) = rhs { - f(pred, args) ? + count } - if let Some(lhs) = self.lhs.as_mut() { - for (pred, argss) in lhs.drain() { - for args in argss { - f(pred, args) ? - } - } + /// Lhs accessor. + pub fn lhs(&self) -> Option<&PrdHMap> { + self.lhs.as_ref() + } + /// Rhs accessor. + pub fn rhs(&self) -> Option<&Sample> { + self.rhs.as_ref() } - self.lhs = None ; - Ok(()) - } - /// Checks whether the lhs of the constraint is empty. - pub fn is_tautology(& self) -> bool { - if self.lhs.is_none() { - debug_assert!( self.rhs.is_none() ) ; - true - } else { - false + /// Removes samples subsumed by a sample from the lhs. + /// + /// Returns the number of sample removed. + /// + /// This function guarantees that `lhs` does not map to an empty `VarValsSet`, + /// so please use this. Do not access `lhs` directly for sample removal. + fn lhs_rm(&mut self, pred: PrdIdx, args: &VarVals) -> usize { + self.lhs + .as_mut() + .map(|lhs| { + let (pred_rm, rmed) = if let Some(argss) = lhs.get_mut(&pred) { + let mut rmed = if argss.remove(args) { 1 } else { 0 }; + if conf.teacher.partial && args.is_partial() { + let (subsumed, nu_rmed) = args.set_subsumed_rm(argss); + debug_assert! { ! subsumed } + rmed += nu_rmed + } + (argss.is_empty(), rmed) + } else { + (false, 0) + }; + if pred_rm { + let prev = lhs.remove(&pred); + debug_assert! { prev.is_some() } + } + rmed + }).unwrap_or(0) } - } - /// Constraint comparison. - /// - /// Returns `Greater` if both - /// - /// - `None` if one of the constraints is a tautology - /// - `Greater` if `self.rhs == other.rhs` and `self.lhs` is a subset of - /// `other.rhs` - /// - `Less` in the dual case from above - /// - `None` otherwise - /// - /// So `c >= c'` means `c` has a lhs strictly more generic than `c'`, so `c'` - /// is redundant. - /// - /// Error if `self` or `other` is a tautology. - pub fn compare(& self, other: & Constraint) -> Res< - Option<::std::cmp::Ordering> - > { - use std::cmp::Ordering ; + /// Transforms a constraint in a tautology. + /// + /// Applies `f` to all samples. + pub fn tautologize(&mut self, mut f: F) -> Res<()> + where + F: FnMut(PrdIdx, VarVals) -> Res<()>, + { + let rhs = ::std::mem::replace(&mut self.rhs, None); + if let Some(Sample { pred, args }) = rhs { + f(pred, args)? + } - if self.is_tautology() { - bail!("self is tautology") - } else if other.is_tautology() { - bail!("other is tautology") + if let Some(lhs) = self.lhs.as_mut() { + for (pred, argss) in lhs.drain() { + for args in argss { + f(pred, args)? + } + } + } + self.lhs = None; + Ok(()) } - if self.rhs != other.rhs { - Ok(None) - } else { + /// Checks whether the lhs of the constraint is empty. + pub fn is_tautology(&self) -> bool { + if self.lhs.is_none() { + debug_assert!(self.rhs.is_none()); + true + } else { + false + } + } + + /// Constraint comparison. + /// + /// Returns `Greater` if both + /// + /// - `None` if one of the constraints is a tautology + /// - `Greater` if `self.rhs == other.rhs` and `self.lhs` is a subset of + /// `other.rhs` + /// - `Less` in the dual case from above + /// - `None` otherwise + /// + /// So `c >= c'` means `c` has a lhs strictly more generic than `c'`, so `c'` + /// is redundant. + /// + /// Error if `self` or `other` is a tautology. + pub fn compare(&self, other: &Constraint) -> Res> { + use std::cmp::Ordering; + + if self.is_tautology() { + bail!("self is tautology") + } else if other.is_tautology() { + bail!("other is tautology") + } - let (reversed, c_1, c_2) = match self.lhs_len().cmp( - & other.lhs_len() - ) { - Ordering::Less => (false, self, other), - Ordering::Equal => if self == other { - return Ok( Some( Ordering::Equal ) ) + if self.rhs != other.rhs { + Ok(None) } else { - return Ok( None ) - }, - Ordering::Greater => (true, other, self), - } ; + let (reversed, c_1, c_2) = match self.lhs_len().cmp(&other.lhs_len()) { + Ordering::Less => (false, self, other), + Ordering::Equal => if self == other { + return Ok(Some(Ordering::Equal)); + } else { + return Ok(None); + }, + Ordering::Greater => (true, other, self), + }; + + match (c_1.lhs.as_ref(), c_2.lhs.as_ref()) { + (Some(lhs_1), Some(lhs_2)) => { + for (pred, samples_1) in lhs_1 { + if let Some(samples_2) = lhs_2.get(pred) { + if !samples_1.is_subset(samples_2) { + return Ok(None); + } + } else { + return Ok(None); + } + } + } + // Should be unreachable. + (None, _) | (_, None) => unreachable!(), + } - match (c_1.lhs.as_ref(), c_2.lhs.as_ref()) { - (Some(lhs_1), Some(lhs_2)) => { - for (pred, samples_1) in lhs_1 { - if let Some(samples_2) = lhs_2.get(pred) { - if ! samples_1.is_subset(samples_2) { - return Ok(None) - } + if reversed { + Ok(Some(Ordering::Less)) } else { - return Ok(None) + Ok(Some(Ordering::Greater)) } - } - }, - // Should be unreachable. - (None, _) | - (_, None) => unreachable!(), - } - - - if reversed { - Ok( Some(Ordering::Less) ) - } else { - Ok( Some(Ordering::Greater) ) - } + } } - } - /// Sets a sample in the constraint. - /// - /// Returns true if the constraint became a tautology. In this case, - /// `self.tautologize(if_tautology)` is called **after** removing the - /// specified sample. - /// - /// Error if the sample was not there. - pub fn force_sample( - & mut self, pred: PrdIdx, args: & VarVals, pos: bool, if_tautology: F - ) -> Res - where F: FnMut(PrdIdx, VarVals) -> Res<()> { - let rmed = self.lhs_rm(pred, args) ; - let was_in_lhs = rmed > 0 ; + /// Sets a sample in the constraint. + /// + /// Returns true if the constraint became a tautology. In this case, + /// `self.tautologize(if_tautology)` is called **after** removing the + /// specified sample. + /// + /// Error if the sample was not there. + pub fn force_sample( + &mut self, + pred: PrdIdx, + args: &VarVals, + pos: bool, + if_tautology: F, + ) -> Res + where + F: FnMut(PrdIdx, VarVals) -> Res<()>, + { + let rmed = self.lhs_rm(pred, args); + let was_in_lhs = rmed > 0; + + let is_in_rhs = if let Some(Sample { + pred: rhs_pred, + args: ref mut rhs_args, + }) = self.rhs + { + rhs_pred == pred && args.subsumes(rhs_args) + } else { + false + }; - let is_in_rhs = if let Some( - Sample { pred: rhs_pred, args: ref mut rhs_args } - ) = self.rhs { - rhs_pred == pred && args.subsumes(rhs_args) - } else { - false - } ; + let was_in_rhs = if is_in_rhs { + self.rhs = None; + true + } else { + false + }; - let was_in_rhs = if is_in_rhs { - self.rhs = None ; - true - } else { - false - } ; + if (was_in_lhs && !pos) || (was_in_rhs && pos) { + self.tautologize(if_tautology)?; + return Ok(true); + } - if (was_in_lhs && ! pos) - || (was_in_rhs && pos) { - self.tautologize(if_tautology) ? ; - return Ok(true) - } + if !was_in_rhs && !was_in_lhs { + bail!("asked to remove sample from a clause where it wasn't") + } - if ! was_in_rhs && ! was_in_lhs { - bail!("asked to remove sample from a clause where it wasn't") + Ok(false) } - Ok(false) - } - - /// Forces all samples of a predicate to `pos`. - /// - /// Returns `true` iff the constraint became a tautology. In this case, - /// `self.tautologize(if_tautology)` is called **after** removing all - /// applications of `pred`. - pub fn force( - & mut self, pred: PrdIdx, pos: bool, if_tautology: F - ) -> Res - where F: FnMut(PrdIdx, VarVals) -> Res<()> { - let mut tautology = false ; - if self.rhs.as_ref().map( - |& Sample { pred: p, .. }| p == pred - ).unwrap_or(false) { - self.rhs = None ; - if pos { - tautology = true - } - } - if let Some(ref mut lhs) = self.lhs { - if lhs.remove(& pred).is_some() && ! pos { - tautology = true - } - } - if tautology { - self.tautologize(if_tautology) ? + /// Forces all samples of a predicate to `pos`. + /// + /// Returns `true` iff the constraint became a tautology. In this case, + /// `self.tautologize(if_tautology)` is called **after** removing all + /// applications of `pred`. + pub fn force(&mut self, pred: PrdIdx, pos: bool, if_tautology: F) -> Res + where + F: FnMut(PrdIdx, VarVals) -> Res<()>, + { + let mut tautology = false; + if self + .rhs + .as_ref() + .map(|&Sample { pred: p, .. }| p == pred) + .unwrap_or(false) + { + self.rhs = None; + if pos { + tautology = true + } + } + if let Some(ref mut lhs) = self.lhs { + if lhs.remove(&pred).is_some() && !pos { + tautology = true + } + } + if tautology { + self.tautologize(if_tautology)? + } + Ok(tautology) } - Ok(tautology) - } - - /// Checks if the constraint is trivial. - /// - /// - `Left(sample, true)` if the constraint is `true => sample` (`sample` - /// needs to be true) - /// - `Left(sample, false)` if the constraint is `sample => false` (`sample` - /// needs to be false) - /// - `Right(true)` if the constraint is trivial (`false => _` or `_ => - /// true`) - /// - `Right(false)` otherwise. - /// - /// If the result isn't `Right(_)`, the sample returned has been removed and - /// the constraint is now a tautology. - pub fn try_trivial(& mut self) -> Either<(Sample, bool), bool> { - if self.lhs().map(|lhs| lhs.is_empty()).unwrap_or(false) { - let mut rhs = None ; - ::std::mem::swap(& mut rhs, & mut self.rhs) ; - let mut lhs = None ; - ::std::mem::swap(& mut lhs, & mut self.lhs) ; - if let Some(s) = rhs { - Either::Left((s, true)) - } else { - // true => false - return Either::Right(true) - } - } else if self.rhs.is_none() { - if let Some(lhs) = self.lhs() { - let mut first = true ; - for (_, argss) in lhs.iter() { - for _ in argss { - if first { - first = false + /// Checks if the constraint is trivial. + /// + /// - `Left(sample, true)` if the constraint is `true => sample` (`sample` + /// needs to be true) + /// - `Left(sample, false)` if the constraint is `sample => false` (`sample` + /// needs to be false) + /// - `Right(true)` if the constraint is trivial (`false => _` or `_ => + /// true`) + /// - `Right(false)` otherwise. + /// + /// If the result isn't `Right(_)`, the sample returned has been removed and + /// the constraint is now a tautology. + pub fn try_trivial(&mut self) -> Either<(Sample, bool), bool> { + if self.lhs().map(|lhs| lhs.is_empty()).unwrap_or(false) { + let mut rhs = None; + ::std::mem::swap(&mut rhs, &mut self.rhs); + let mut lhs = None; + ::std::mem::swap(&mut lhs, &mut self.lhs); + + if let Some(s) = rhs { + Either::Left((s, true)) } else { - return Either::Right(false) + // true => false + return Either::Right(true); + } + } else if self.rhs.is_none() { + if let Some(lhs) = self.lhs() { + let mut first = true; + for (_, argss) in lhs.iter() { + for _ in argss { + if first { + first = false + } else { + return Either::Right(false); + } + } + } + } else { + return Either::Right(false); } - } - } - } else { - return Either::Right(false) - } - let mut old_lhs = None ; - ::std::mem::swap(& mut self.lhs, & mut old_lhs) ; + let mut old_lhs = None; + ::std::mem::swap(&mut self.lhs, &mut old_lhs); - // Only reachable if there's one pred app in lhs. - let (pred, argss) = old_lhs.unwrap().into_iter().next().unwrap() ; - let args = argss.into_iter().next().unwrap() ; - Either::Left(( - Sample { pred, args }, false - )) - } else { - Either::Right(false) + // Only reachable if there's one pred app in lhs. + let (pred, argss) = old_lhs.unwrap().into_iter().next().unwrap(); + let args = argss.into_iter().next().unwrap(); + Either::Left((Sample { pred, args }, false)) + } else { + Either::Right(false) + } } - } } - - - impl<'a> PebcakFmt<'a> for Constraint { - type Info = & 'a PrdInfos ; - fn pebcak_err(& self) -> ErrorKind { - "during constraint pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, map: & 'a PrdInfos - ) -> IoRes<()> { - if let Some(ref lhs) = self.lhs { - if lhs.is_empty() { - write!(w, "true ") ? - } - for (pred, samples) in lhs { - for sample in samples { - write!(w, "({} {}) ", map[* pred], sample) ? - } - } - } else { - write!(w, "false ") ? + type Info = &'a PrdInfos; + fn pebcak_err(&self) -> ErrorKind { + "during constraint pebcak formatting".into() } - write!(w, "=> ") ? ; - if let Some(ref rhs) = self.rhs { - rhs.pebcak_io_fmt(w, map) - } else { - write!(w, "false") + fn pebcak_io_fmt(&self, w: &mut W, map: &'a PrdInfos) -> IoRes<()> { + if let Some(ref lhs) = self.lhs { + if lhs.is_empty() { + write!(w, "true ")? + } + for (pred, samples) in lhs { + for sample in samples { + write!(w, "({} {}) ", map[*pred], sample)? + } + } + } else { + write!(w, "false ")? + } + write!(w, "=> ")?; + if let Some(ref rhs) = self.rhs { + rhs.pebcak_io_fmt(w, map) + } else { + write!(w, "false") + } } - } } impl_fmt!{ Constraint(self, fmt) { @@ -411,37 +409,34 @@ impl_fmt!{ } } - impl Eq for Constraint {} impl PartialEq for Constraint { - fn eq(& self, other: & Constraint) -> bool { - if self.rhs == other.rhs { - match (self.lhs.as_ref(), other.lhs.as_ref()) { - (Some(lhs_1), Some(lhs_2)) => { - for ( - (lhs_pred, lhs_samples), (rhs_pred, rhs_samples) - ) in lhs_1.iter().zip( lhs_2.iter() ) { - if lhs_pred == rhs_pred - && lhs_samples.len() == rhs_samples.len() { - for (lhs_sample, rhs_sample) in lhs_samples.iter().zip( - rhs_samples.iter() - ) { - if lhs_sample != rhs_sample { - return false + fn eq(&self, other: &Constraint) -> bool { + if self.rhs == other.rhs { + match (self.lhs.as_ref(), other.lhs.as_ref()) { + (Some(lhs_1), Some(lhs_2)) => { + for ((lhs_pred, lhs_samples), (rhs_pred, rhs_samples)) in + lhs_1.iter().zip(lhs_2.iter()) + { + if lhs_pred == rhs_pred && lhs_samples.len() == rhs_samples.len() { + for (lhs_sample, rhs_sample) in + lhs_samples.iter().zip(rhs_samples.iter()) + { + if lhs_sample != rhs_sample { + return false; + } + } + } else { + return false; + } + } + true } - } - } else { - return false + (None, None) => true, + (None, Some(_)) | (Some(_), None) => false, } - } - true - }, - (None, None) => true, - (None, Some(_)) | - (Some(_), None) => false - } - } else { - false + } else { + false + } } - } -} \ No newline at end of file +} diff --git a/src/data/info.rs b/src/data/info.rs index bdd3583f..22a8354d 100644 --- a/src/data/info.rs +++ b/src/data/info.rs @@ -1,63 +1,58 @@ -use common::* ; -use data::Constraint ; +use common::*; +use data::Constraint; /// Maintains information about the constraints. #[derive(Clone)] pub struct CstrInfo { - /// Set of negative constraints, for constraint pruning. - neg_constraints: CstrSet, - /// Constraints that have changed or are new since the last reset. - modded_constraints: CstrSet, + /// Set of negative constraints, for constraint pruning. + neg_constraints: CstrSet, + /// Constraints that have changed or are new since the last reset. + modded_constraints: CstrSet, } impl CstrInfo { - /// Constructor. - pub fn new() -> Self { - CstrInfo { - neg_constraints: CstrSet::with_capacity(11), - modded_constraints: CstrSet::with_capacity(11), + /// Constructor. + pub fn new() -> Self { + CstrInfo { + neg_constraints: CstrSet::with_capacity(11), + modded_constraints: CstrSet::with_capacity(11), + } } - } - /// Modified constraints accessor. - pub fn modded(& self) -> & CstrSet { - & self.modded_constraints - } - - /// Negative constraints accessor. - pub fn neg(& self) -> & CstrSet { - & self.neg_constraints - } + /// Modified constraints accessor. + pub fn modded(&self) -> &CstrSet { + &self.modded_constraints + } - /// Clears modified constraints. - pub fn clear_modded(& mut self) { - self.modded_constraints.clear() - } + /// Negative constraints accessor. + pub fn neg(&self) -> &CstrSet { + &self.neg_constraints + } - /// Forget a constraint. - /// - /// Used when tautologizing. - pub fn forget(& mut self, constraint: CstrIdx) { - self.neg_constraints.remove(& constraint) ; - self.modded_constraints.remove(& constraint) ; - () - } + /// Clears modified constraints. + pub fn clear_modded(&mut self) { + self.modded_constraints.clear() + } - /// Registers a constraint as modified. - /// - /// Error if the constraint is a tautology. - pub fn register_modded( - & mut self, index: CstrIdx, constraint: & Constraint - ) -> Res<()> { - if constraint.is_tautology() { - bail!("registering tautology") + /// Forget a constraint. + /// + /// Used when tautologizing. + pub fn forget(&mut self, constraint: CstrIdx) { + self.neg_constraints.remove(&constraint); + self.modded_constraints.remove(&constraint); + () } - self.modded_constraints.insert(index) ; - if constraint.rhs().is_none() { - self.neg_constraints.insert(index) ; + + /// Registers a constraint as modified. + /// + /// Error if the constraint is a tautology. + pub fn register_modded(&mut self, index: CstrIdx, constraint: &Constraint) -> Res<()> { + if constraint.is_tautology() { + bail!("registering tautology") + } + self.modded_constraints.insert(index); + if constraint.rhs().is_none() { + self.neg_constraints.insert(index); + } + Ok(()) } - Ok(()) - } } - - - diff --git a/src/data/mod.rs b/src/data/mod.rs index b78883cf..a7bb607b 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,1529 +1,1411 @@ //! Sample storage used by the teacher and the learner(s). use common::{ - *, - var_to::vals::{ - RVarVals, VarValsSet, VarValsMap - }, -} ; -use learning::ice::data::CData ; + var_to::vals::{RVarVals, VarValsMap, VarValsSet}, + *, +}; +use learning::ice::data::CData; // use unsat_core::sample_graph::SampleGraph ; -pub mod sample ; -pub mod constraint ; -mod info ; - -pub use self::sample::{ Sample } ; -pub use self::constraint::Constraint ; -use self::info::CstrInfo ; - - - +pub mod constraint; +mod info; +pub mod sample; +pub use self::constraint::Constraint; +use self::info::CstrInfo; +pub use self::sample::Sample; /// Structure manipulating unprojected learning data. pub struct Data { - /// Instance, only used for printing. - pub instance: Arc, - /// Positive examples. - pub pos: PrdMap< VarValsSet >, - /// Negative examples. - pub neg: PrdMap< VarValsSet >, - /// Constraints. - pub constraints: CstrMap, - - /// Positive samples with a single non-partial sample. - pos_single: PrdMap< VarValsSet >, - /// Negative samples with a single non-partial sample. - neg_single: PrdMap< VarValsSet >, - - /// Map from samples to constraints. - map: PrdMap< VarValsMap >, - - /// Stores pos/neg samples temporarily before they're added. - staged: Staged, - /// Constraint info. - cstr_info: CstrInfo, - // /// Sample dependency graph for unsat cores extraction. - // /// - // /// Different from `None` iff `conf.unsat_cores()` - // graph: Option, - - /// Profiler. - _profiler: Profiler, + /// Instance, only used for printing. + pub instance: Arc, + /// Positive examples. + pub pos: PrdMap, + /// Negative examples. + pub neg: PrdMap, + /// Constraints. + pub constraints: CstrMap, + + /// Positive samples with a single non-partial sample. + pos_single: PrdMap, + /// Negative samples with a single non-partial sample. + neg_single: PrdMap, + + /// Map from samples to constraints. + map: PrdMap>, + + /// Stores pos/neg samples temporarily before they're added. + staged: Staged, + /// Constraint info. + cstr_info: CstrInfo, + // /// Sample dependency graph for unsat cores extraction. + // /// + // /// Different from `None` iff `conf.unsat_cores()` + // graph: Option, + /// Profiler. + _profiler: Profiler, } impl Clone for Data { - fn clone(& self) -> Self { - Data { - instance: self.instance.clone(), - pos: self.pos.clone(), - neg: self.neg.clone(), - constraints: self.constraints.clone(), - map: self.map.clone(), - - pos_single: self.pos_single.clone(), - neg_single: self.neg_single.clone(), - - staged: self.staged.clone(), // Empty anyway. - cstr_info: self.cstr_info.clone(), - // graph: None, - _profiler: Profiler::new(), + fn clone(&self) -> Self { + Data { + instance: self.instance.clone(), + pos: self.pos.clone(), + neg: self.neg.clone(), + constraints: self.constraints.clone(), + map: self.map.clone(), + + pos_single: self.pos_single.clone(), + neg_single: self.neg_single.clone(), + + staged: self.staged.clone(), // Empty anyway. + cstr_info: self.cstr_info.clone(), + // graph: None, + _profiler: Profiler::new(), + } } - } } - impl ::std::fmt::Debug for Data { - fn fmt(& self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!( - fmt, - "Data {{ {} pos, {} neg, {} constraints }}", - self.pos.iter().fold(0, |acc, argss| acc + argss.len()), - self.neg.iter().fold(0, |acc, argss| acc + argss.len()), - self.constraints.len(), - ) - } + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!( + fmt, + "Data {{ {} pos, {} neg, {} constraints }}", + self.pos.iter().fold(0, |acc, argss| acc + argss.len()), + self.neg.iter().fold(0, |acc, argss| acc + argss.len()), + self.constraints.len(), + ) + } } - impl Data { - - /// Constructor. - pub fn new(instance: Arc) -> Self { - let pred_count = instance.preds().len() ; - - let ( - mut map, mut pos, mut neg, - mut pos_single, mut neg_single, - ) = ( - PrdMap::with_capacity(pred_count), - PrdMap::with_capacity(pred_count), - PrdMap::with_capacity(pred_count), - PrdMap::with_capacity(pred_count), - PrdMap::with_capacity(pred_count), - ) ; - - for _ in instance.preds() { - map.push( VarValsMap::with_capacity(103) ) ; - pos.push( VarValsSet::with_capacity(103) ) ; - neg.push( VarValsSet::with_capacity(103) ) ; - pos_single.push( VarValsSet::with_capacity(13) ) ; - neg_single.push( VarValsSet::with_capacity(13) ) ; - } - // let track_samples = instance.track_samples() ; - - let constraints = CstrMap::with_capacity(103) ; - Data { - instance, pos, neg, constraints, map, - staged: Staged::with_capacity(pred_count), - cstr_info: CstrInfo::new(), pos_single, neg_single, - // graph: if track_samples { - // Some( SampleGraph::new() ) - // } else { - // None - // }, - _profiler: Profiler::new(), - } - } - - /// Accessor for the map from samples to constraints. - pub fn map(& self) -> & PrdMap< VarValsMap > { - & self.map - } - - /// Destroys the data and returns profiling info. - pub fn destroy(self) -> Profiler { - let _pos_len = self.pos.iter().fold( - 0, |acc, samples| acc + samples.len() - ) ; - let _neg_len = self.neg.iter().fold( - 0, |acc, samples| acc + samples.len() - ) ; - let _all = _pos_len + _neg_len + self.map.iter().fold( - 0, |acc, map| acc + map.len() - ) ; - profile! { - self "> constraints" => add self.constraints.len() - } - profile! { - self "> pos samples" => add _pos_len - } - profile! { - self "> neg samples" => add _neg_len + /// Constructor. + pub fn new(instance: Arc) -> Self { + let pred_count = instance.preds().len(); + + let (mut map, mut pos, mut neg, mut pos_single, mut neg_single) = ( + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), + PrdMap::with_capacity(pred_count), + ); + + for _ in instance.preds() { + map.push(VarValsMap::with_capacity(103)); + pos.push(VarValsSet::with_capacity(103)); + neg.push(VarValsSet::with_capacity(103)); + pos_single.push(VarValsSet::with_capacity(13)); + neg_single.push(VarValsSet::with_capacity(13)); + } + // let track_samples = instance.track_samples() ; + + let constraints = CstrMap::with_capacity(103); + Data { + instance, + pos, + neg, + constraints, + map, + staged: Staged::with_capacity(pred_count), + cstr_info: CstrInfo::new(), + pos_single, + neg_single, + // graph: if track_samples { + // Some( SampleGraph::new() ) + // } else { + // None + // }, + _profiler: Profiler::new(), + } } - profile! { - self "> all samples" => add _all + + /// Accessor for the map from samples to constraints. + pub fn map(&self) -> &PrdMap> { + &self.map } - self._profiler - } - - /// Clears the modified constraint set. - pub fn clear_modded(& mut self) { - self.cstr_info.clear_modded() - } - - /// String representation of a constraint. - #[allow(dead_code)] - fn str_of(& self, c: CstrIdx) -> String { - format!( - "# {}\n{}", c, - self.constraints[c].to_string_info( - self.instance.preds() - ).unwrap() - ) - } - - // /// The sample graph, used for unsat cores. - // pub fn sample_graph(& mut self) -> Option { - // if let Some(ref mut graph) = self.graph { - // let mut old_graph = SampleGraph::new() ; - // ::std::mem::swap(graph, & mut old_graph) ; - // Some(old_graph) - // } else { - // None - // } - // } - - /// Clones the new/modded constraints to create a new `Data`. - /// - /// **Clears the set of modified constraints.** - /// - /// Used to send modified implications to the assistant. - pub fn clone_new_constraints(& mut self) -> Res< Option > { - let mut data = None ; - for idx in self.cstr_info.modded() { - let constraint = & self.constraints[* idx] ; - if ! constraint.is_tautology() { - data.get_or_insert_with( - || Data::new( self.instance.clone() ) - ).raw_add_cstr( constraint.clone() ) ? ; - } + + /// Destroys the data and returns profiling info. + pub fn destroy(self) -> Profiler { + let _pos_len = self.pos.iter().fold(0, |acc, samples| acc + samples.len()); + let _neg_len = self.neg.iter().fold(0, |acc, samples| acc + samples.len()); + let _all = _pos_len + _neg_len + self.map.iter().fold(0, |acc, map| acc + map.len()); + profile! { + self "> constraints" => add self.constraints.len() + } + profile! { + self "> pos samples" => add _pos_len + } + profile! { + self "> neg samples" => add _neg_len + } + profile! { + self "> all samples" => add _all + } + self._profiler } - self.cstr_info.clear_modded() ; - Ok(data) - } - - /// Merges the positive and negative samples in `other` to `self`. - /// - /// Does not propagate. - /// - /// Returns the number of new positive/negative examples. - pub fn merge_samples(& mut self, other: Data) -> Res<(usize, usize)> { - for (pred, samples) in other.pos.into_index_iter() { - for sample in samples { - self.staged.add_pos(pred, sample) ; - } + + /// Clears the modified constraint set. + pub fn clear_modded(&mut self) { + self.cstr_info.clear_modded() } - for (pred, samples) in other.neg.into_index_iter() { - for sample in samples { - self.staged.add_neg(pred, sample) ; - } + + /// String representation of a constraint. + #[allow(dead_code)] + fn str_of(&self, c: CstrIdx) -> String { + format!( + "# {}\n{}", + c, + self.constraints[c] + .to_string_info(self.instance.preds()) + .unwrap() + ) } - // if let Some(graph) = self.graph.as_mut() { - // if let Some(other) = other.graph { - // graph.merge(other) + + // /// The sample graph, used for unsat cores. + // pub fn sample_graph(& mut self) -> Option { + // if let Some(ref mut graph) = self.graph { + // let mut old_graph = SampleGraph::new() ; + // ::std::mem::swap(graph, & mut old_graph) ; + // Some(old_graph) // } else { - // bail!("inconsistent sample dependency tracking") + // None // } // } - self.propagate() - } - - - /// Checks whether a constraint is useful. - /// - /// Remove all constraints that this constraint makes useless, including the - /// one(s) it is equal to. - pub fn cstr_useful(& mut self, index: CstrIdx) -> Res { - profile! { self tick "constraint subsumption" } - let mut to_check = CstrSet::new() ; - scoped! { - let constraint = & self.constraints[index] ; - let similar = if let Some( - & Sample { pred, ref args } - ) = constraint.rhs() { - if let Some(similar) = self.map[pred].get(args) { - similar - } else { - bail!("sample to constraint map is inconsistent") + + /// Clones the new/modded constraints to create a new `Data`. + /// + /// **Clears the set of modified constraints.** + /// + /// Used to send modified implications to the assistant. + pub fn clone_new_constraints(&mut self) -> Res> { + let mut data = None; + for idx in self.cstr_info.modded() { + let constraint = &self.constraints[*idx]; + if !constraint.is_tautology() { + data.get_or_insert_with(|| Data::new(self.instance.clone())) + .raw_add_cstr(constraint.clone())?; + } } - } else { - self.cstr_info.neg() - } ; - for idx in similar { - if * idx != index { - let is_new = to_check.insert(* idx) ; - debug_assert! { is_new } + self.cstr_info.clear_modded(); + Ok(data) + } + + /// Merges the positive and negative samples in `other` to `self`. + /// + /// Does not propagate. + /// + /// Returns the number of new positive/negative examples. + pub fn merge_samples(&mut self, other: Data) -> Res<(usize, usize)> { + for (pred, samples) in other.pos.into_index_iter() { + for sample in samples { + self.staged.add_pos(pred, sample); + } + } + for (pred, samples) in other.neg.into_index_iter() { + for sample in samples { + self.staged.add_neg(pred, sample); + } } - } + // if let Some(graph) = self.graph.as_mut() { + // if let Some(other) = other.graph { + // graph.merge(other) + // } else { + // bail!("inconsistent sample dependency tracking") + // } + // } + self.propagate() } - let mut useful = true ; - - for similar in to_check.drain() { - use std::cmp::Ordering::* ; - match self.constraints[index].compare( - & self.constraints[similar] - ).chain_err( - || "in cstr_useful" - ) ? { - // `similar` is implied by `index`, drop it. - Some(Equal) | Some(Greater) => { - profile! { self "useless constraints" => add 1 } - self.tautologize(similar) ? - }, - // `index` is useless. - Some(Less) => { - profile! { self "useless constraints" => add 1 } - useful = false - }, - // Not comparable. - None => (), - } + /// Checks whether a constraint is useful. + /// + /// Remove all constraints that this constraint makes useless, including the + /// one(s) it is equal to. + pub fn cstr_useful(&mut self, index: CstrIdx) -> Res { + profile! { self tick "constraint subsumption" } + let mut to_check = CstrSet::new(); + scoped! { + let constraint = & self.constraints[index] ; + let similar = if let Some( + & Sample { pred, ref args } + ) = constraint.rhs() { + if let Some(similar) = self.map[pred].get(args) { + similar + } else { + bail!("sample to constraint map is inconsistent") + } + } else { + self.cstr_info.neg() + } ; + for idx in similar { + if * idx != index { + let is_new = to_check.insert(* idx) ; + debug_assert! { is_new } + } + } + } + + let mut useful = true; + + for similar in to_check.drain() { + use std::cmp::Ordering::*; + match self.constraints[index] + .compare(&self.constraints[similar]) + .chain_err(|| "in cstr_useful")? + { + // `similar` is implied by `index`, drop it. + Some(Equal) | Some(Greater) => { + profile! { self "useless constraints" => add 1 } + self.tautologize(similar)? + } + // `index` is useless. + Some(Less) => { + profile! { self "useless constraints" => add 1 } + useful = false + } + // Not comparable. + None => (), + } + } + profile! { self mark "constraint subsumption" } + + Ok(useful) } - profile! { self mark "constraint subsumption" } - - Ok(useful) - } - - - /// Adds a positive example. - /// - /// The `clause` input is necessary for unsat core extraction. - /// - /// Does not propagate. - pub fn add_raw_pos( - & mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals - ) -> bool { - let args = var_to::vals::new(args) ; - self.add_pos( clause, pred, args.clone() ) - } - - /// Adds a negative example. - /// - /// The `clause` input is necessary for unsat core extraction. - /// - /// Does not propagate. - pub fn add_raw_neg( - & mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals - ) -> bool { - let args = var_to::vals::new(args) ; - self.add_neg( clause, pred, args.clone() ) - } - - - - /// Adds a positive example. - /// - /// The `clause` input is necessary for unsat core extraction. - /// - /// Does not propagate. - pub fn add_pos( - & mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals - ) -> bool { - // if self.add_pos_untracked( pred, args.clone() ) { - // if let Some(graph) = self.graph.as_mut() { - // graph.add( - // pred, self.instance[clause].rhs().unwrap().1.clone(), - // args.clone(), clause, PrdHMap::new() - // ) - // } - // true - // } else { - // false - // } - self.add_pos_untracked(pred, args) - } - /// Adds a positive example. - /// - /// Does track dependencies for unsat core. - /// - /// Used by the learner(s). - pub fn add_pos_untracked( - & mut self, pred: PrdIdx, args: VarVals - ) -> bool { - self.staged.add_pos(pred, args) - } - - /// Adds a new negative example. - /// - /// The `clause` input is necessary for unsat core extraction. - /// - /// Does not propagate. - pub fn add_neg( - & mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals - ) -> bool { - // if self.add_neg_untracked( pred, args.clone() ) { - // if let Some(graph) = self.graph.as_mut() { - // let mut lhs = PrdHMap::with_capacity(1) ; - // let mut farg_map = HConMap::new() ; - // // debug_assert_eq! { 1, self.instance[clause].lhs_preds().len() } - - // let ( - // p, argss - // ) = self.instance[clause].lhs_preds().iter().next().unwrap() ; - // debug_assert_eq! { pred, * p } - // debug_assert_eq! { 1, argss.len() } - // let prev = farg_map.insert( - // argss.iter().next().unwrap().clone(), args - // ) ; - // debug_assert! { prev.is_none() } - - // let prev = lhs.insert(pred, farg_map) ; - // debug_assert! { prev.is_none() } - // graph.add_neg(clause, lhs) - // } - // true - // } else { - // false - // } - self.add_neg_untracked(pred, args) - } - /// Adds a negative example. - /// - /// Does track dependencies for unsat core. - /// - /// Used by the learner(s). - pub fn add_neg_untracked( - & mut self, pred: PrdIdx, args: VarVals - ) -> bool { - self.staged.add_neg(pred, args) - } - - - - - /// Number of positive/negative samples. - pub fn pos_neg_count(& self) -> (usize, usize) { - let (mut pos, mut neg) = (0, 0) ; - for samples in & self.pos { - pos += samples.len() + + /// Adds a positive example. + /// + /// The `clause` input is necessary for unsat core extraction. + /// + /// Does not propagate. + pub fn add_raw_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { + let args = var_to::vals::new(args); + self.add_pos(clause, pred, args.clone()) } - for samples in & self.neg { - neg += samples.len() + + /// Adds a negative example. + /// + /// The `clause` input is necessary for unsat core extraction. + /// + /// Does not propagate. + pub fn add_raw_neg(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { + let args = var_to::vals::new(args); + self.add_neg(clause, pred, args.clone()) } - (pos, neg) - } - - /// Shrinks the list of constraints. - /// - /// - pops all trailing empty constraints from [`self.constraints`][cstrs]. - /// - /// Called at the end of [`propagate`][prop]. - /// - /// [cstrs]: #structfield.constraints (constraints field) - /// [prop]: #method.propagate (propagate function) - fn shrink_constraints(& mut self) { - for map in self.map.iter_mut() { - map.retain( - |_, set| ! set.is_empty() - ) + + /// Adds a positive example. + /// + /// The `clause` input is necessary for unsat core extraction. + /// + /// Does not propagate. + pub fn add_pos(&mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { + // if self.add_pos_untracked( pred, args.clone() ) { + // if let Some(graph) = self.graph.as_mut() { + // graph.add( + // pred, self.instance[clause].rhs().unwrap().1.clone(), + // args.clone(), clause, PrdHMap::new() + // ) + // } + // true + // } else { + // false + // } + self.add_pos_untracked(pred, args) } - loop { - scoped! { - if let Some(last) = self.constraints.last() { - if ! last.is_tautology() { - return () - } - } else { - return () - } - } - let last = self.constraints.pop() ; - debug_assert_eq!( - last.map(|c| c.is_tautology()), Some(true) - ) + /// Adds a positive example. + /// + /// Does track dependencies for unsat core. + /// + /// Used by the learner(s). + pub fn add_pos_untracked(&mut self, pred: PrdIdx, args: VarVals) -> bool { + self.staged.add_pos(pred, args) } - } - - /// Function used when tautologizing a constraint, to forget the samples. - fn tauto_fun( - map: & mut PrdMap< VarValsMap >, constraint: CstrIdx, - pred: PrdIdx, args: & VarVals - ) -> Res<()> { - let mut remove = false ; - let was_there = map[pred].get_mut(& args).map( - |set| { - let was_there = set.remove(& constraint) ; - remove = set.is_empty() ; - was_there - } - ).unwrap_or(false) ; - if ! was_there { - bail!("tautology treatment failed: unknown arguments {}", args) + + /// Adds a new negative example. + /// + /// The `clause` input is necessary for unsat core extraction. + /// + /// Does not propagate. + pub fn add_neg(&mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { + // if self.add_neg_untracked( pred, args.clone() ) { + // if let Some(graph) = self.graph.as_mut() { + // let mut lhs = PrdHMap::with_capacity(1) ; + // let mut farg_map = HConMap::new() ; + // // debug_assert_eq! { 1, self.instance[clause].lhs_preds().len() } + + // let ( + // p, argss + // ) = self.instance[clause].lhs_preds().iter().next().unwrap() ; + // debug_assert_eq! { pred, * p } + // debug_assert_eq! { 1, argss.len() } + // let prev = farg_map.insert( + // argss.iter().next().unwrap().clone(), args + // ) ; + // debug_assert! { prev.is_none() } + + // let prev = lhs.insert(pred, farg_map) ; + // debug_assert! { prev.is_none() } + // graph.add_neg(clause, lhs) + // } + // true + // } else { + // false + // } + self.add_neg_untracked(pred, args) } - if remove { - let prev = map[pred].remove(& args) ; - debug_assert! { prev.is_some() } + /// Adds a negative example. + /// + /// Does track dependencies for unsat core. + /// + /// Used by the learner(s). + pub fn add_neg_untracked(&mut self, pred: PrdIdx, args: VarVals) -> bool { + self.staged.add_neg(pred, args) } - Ok(()) - } - - /// Tautologizes a constraint and removes the links with its samples in - /// the map. - pub fn tautologize( - & mut self, constraint: CstrIdx - ) -> Res<()> { - scoped! { - let map = & mut self.map ; - self.constraints[constraint].tautologize( - |pred, args| Self::tauto_fun(map, constraint, pred, & args) - ).chain_err( - || "in tautologize" - ) ? ; + + /// Number of positive/negative samples. + pub fn pos_neg_count(&self) -> (usize, usize) { + let (mut pos, mut neg) = (0, 0); + for samples in &self.pos { + pos += samples.len() + } + for samples in &self.neg { + neg += samples.len() + } + (pos, neg) } - self.cstr_info.forget(constraint) ; - Ok(()) - } + /// Shrinks the list of constraints. + /// + /// - pops all trailing empty constraints from [`self.constraints`][cstrs]. + /// + /// Called at the end of [`propagate`][prop]. + /// + /// [cstrs]: #structfield.constraints (constraints field) + /// [prop]: #method.propagate (propagate function) + fn shrink_constraints(&mut self) { + for map in self.map.iter_mut() { + map.retain(|_, set| !set.is_empty()) + } + loop { + scoped! { + if let Some(last) = self.constraints.last() { + if ! last.is_tautology() { + return () + } + } else { + return () + } + } + let last = self.constraints.pop(); + debug_assert_eq!(last.map(|c| c.is_tautology()), Some(true)) + } + } + /// Function used when tautologizing a constraint, to forget the samples. + fn tauto_fun( + map: &mut PrdMap>, + constraint: CstrIdx, + pred: PrdIdx, + args: &VarVals, + ) -> Res<()> { + let mut remove = false; + let was_there = map[pred] + .get_mut(&args) + .map(|set| { + let was_there = set.remove(&constraint); + remove = set.is_empty(); + was_there + }).unwrap_or(false); + if !was_there { + bail!("tautology treatment failed: unknown arguments {}", args) + } + if remove { + let prev = map[pred].remove(&args); + debug_assert! { prev.is_some() } + } + Ok(()) + } + /// Tautologizes a constraint and removes the links with its samples in + /// the map. + pub fn tautologize(&mut self, constraint: CstrIdx) -> Res<()> { + scoped! { + let map = & mut self.map ; + self.constraints[constraint].tautologize( + |pred, args| Self::tauto_fun(map, constraint, pred, & args) + ).chain_err( + || "in tautologize" + ) ? ; + } + self.cstr_info.forget(constraint); + Ok(()) + } - /// Retrieves all args `s` from `self.map` such that `args.subsumes(s)` - fn remove_subs( - & mut self, pred: PrdIdx, args: & VarVals - ) -> Option { - if ! conf.teacher.partial || ! args.is_partial() { - return self.map[pred].remove(args) + /// Retrieves all args `s` from `self.map` such that `args.subsumes(s)` + fn remove_subs(&mut self, pred: PrdIdx, args: &VarVals) -> Option { + if !conf.teacher.partial || !args.is_partial() { + return self.map[pred].remove(args); + } + profile! { self tick "remove_sub" } + let mut res = None; + self.map[pred].retain(|s, set| { + if args.subsumes(s) { + res.get_or_insert_with(|| CstrSet::with_capacity(set.len())) + .extend(set.drain()); + false + } else { + true + } + }); + profile! { self mark "remove_sub" } + res } - profile! { self tick "remove_sub" } - let mut res = None ; - self.map[pred].retain( - |s, set| if args.subsumes(s) { - res.get_or_insert_with( - || CstrSet::with_capacity(set.len()) - ).extend( set.drain() ) ; - false - } else { - true - } - ) ; - profile! { self mark "remove_sub" } - res - } - - - - /// Checks whether the data is contradictory. - pub fn is_unsat(& self) -> Option< - Vec<(PrdIdx, VarVals)> - > { - for (pred, samples) in self.pos.index_iter() { - for sample in samples { - for neg in & self.neg[pred] { - if sample.is_complementary(neg) { - return Some( - vec![(pred, sample.clone()), (pred, neg.clone())] - ) - } + + /// Checks whether the data is contradictory. + pub fn is_unsat(&self) -> Option> { + for (pred, samples) in self.pos.index_iter() { + for sample in samples { + for neg in &self.neg[pred] { + if sample.is_complementary(neg) { + return Some(vec![(pred, sample.clone()), (pred, neg.clone())]); + } + } + } } - } + None } - None - } + /// Propagates all staged samples. + /// + /// Returns the number of pos/neg samples added. + pub fn propagate(&mut self) -> Res<(usize, usize)> { + profile! { self tick "propagate" } + + // println!("{}", self.to_string_info(& ()).unwrap()) ; + + let (mut pos_cnt, mut neg_cnt) = (0, 0); + + // This is used to remember new constraints from this propagation phase, to + // check for useless constraints after propagation is over. + let mut modded_constraints = CstrSet::new(); + + 'propagate: while let Some((pred, mut argss, pos)) = self.staged.pop() { + macro_rules! single_target_set { + () => { + if pos { + &mut self.pos_single[pred] + } else { + &mut self.neg_single[pred] + } + }; + } + macro_rules! target_set { + () => { + if pos { + &mut self.pos[pred] + } else { + &mut self.neg[pred] + } + }; + } + profile! { self tick "propagate", "filtering" } + // Only keep those that are actually new. + argss.retain(|s| { + // Note that we're removing elements of the target set that are + // subsumed by `s`. + let (subsumed, rmed) = s.set_subsumed_rm(target_set!()); + if subsumed { + debug_assert! { rmed == 0 } + false + } else { + if s.len() > 1 { + let count = s + .iter() + .fold(0, |acc, val| if !val.is_known() { acc + 1 } else { acc }); + if count + 1 == s.len() { + let _ = single_target_set!().insert(s.clone()); + () + } + } + + let is_new = target_set!().insert(s.clone()); + + debug_assert! { is_new } + true + } + }); + profile! { self mark "propagate", "filtering" } - /// Propagates all staged samples. - /// - /// Returns the number of pos/neg samples added. - pub fn propagate(& mut self) -> Res<(usize, usize)> { + // Move on if nothing's left. + if argss.is_empty() { + continue 'propagate; + } - profile! { self tick "propagate" } + if pos { + pos_cnt += argss.len() + } else { + neg_cnt += argss.len() + } - // println!("{}", self.to_string_info(& ()).unwrap()) ; + // Update the constraints that mention these new `pos` samples. + for args in argss { + profile! { + self "partial samples" => add { + if args.is_partial() { 1 } else { 0 } + } + } - let (mut pos_cnt, mut neg_cnt) = (0, 0) ; + if let Some(constraints) = self.remove_subs(pred, &args) { + profile! { self tick "propagate", "cstr update" } + for constraint_idx in constraints { + let constraint = &mut self.constraints[constraint_idx]; + let map = &mut self.map; + + let tautology = constraint + .force_sample(pred, &args, pos, |pred, args| { + Self::tauto_fun(map, constraint_idx, pred, &args) + }).chain_err(|| "in propagate")?; + + if tautology { + // Tautology, discard. + self.cstr_info.forget(constraint_idx) + } else { + match constraint.try_trivial() { + Either::Left((Sample { pred, args }, pos)) => { + // Constraint is trivial: unlink and forget. + if let Some(set) = map[pred].get_mut(&args) { + let was_there = set.remove(&constraint_idx); + debug_assert! { was_there } + } + self.cstr_info.forget(constraint_idx); + // Stage the consequence of the triviality. + self.staged.add(pred, args, pos); + } + Either::Right(false) => { + // Otherwise, the constraint was modified and we're keeping + // it. + self.cstr_info + .register_modded(constraint_idx, &constraint)?; + modded_constraints.insert(constraint_idx); + } + Either::Right(true) => { + unsat!("by `true => false` in constraint (data, propagate)") + } + } + } + } + profile! { self mark "propagate", "cstr update" } + + for constraint in modded_constraints.drain() { + if !self.constraints[constraint].is_tautology() + && !self.cstr_useful(constraint).chain_err(|| "in propagate")? + { + self.tautologize(constraint)? + } + } + } + } + } - // This is used to remember new constraints from this propagation phase, to - // check for useless constraints after propagation is over. - let mut modded_constraints = CstrSet::new() ; + profile! { self tick "propagate", "check shrink" } + self.check("after propagate")?; - 'propagate: while let Some( - (pred, mut argss, pos) - ) = self.staged.pop() { + self.shrink_constraints(); + profile! { self mark "propagate", "check shrink" } - macro_rules! single_target_set { - () => ( - if pos { - & mut self.pos_single[pred] - } else { - & mut self.neg_single[pred] - } - ) ; - } + profile! { self mark "propagate" } - macro_rules! target_set { - () => ( - if pos { - & mut self.pos[pred] - } else { - & mut self.neg[pred] - } - ) ; - } - - profile! { self tick "propagate", "filtering" } - // Only keep those that are actually new. - argss.retain( - |s| { - // Note that we're removing elements of the target set that are - // subsumed by `s`. - let (subsumed, rmed) = s.set_subsumed_rm( - target_set!() - ) ; - if subsumed { - debug_assert! { rmed == 0 } - false - } else { - if s.len() > 1 { - let count = s.iter().fold( - 0, |acc, val| if ! val.is_known() { acc + 1 } else { acc } - ) ; - if count + 1 == s.len() { - let _ = single_target_set!().insert( s.clone() ) ; - () - } - } + Ok((pos_cnt, neg_cnt)) + } - let is_new = target_set!().insert( s.clone() ) ; + /// Length of positive/negative samples and constraints. + pub fn metrics(&self) -> (usize, usize, usize) { + ( + self.pos.iter().fold(0, |acc, samples| acc + samples.len()), + self.neg.iter().fold(0, |acc, samples| acc + samples.len()), + self.constraints.len(), + ) + } + /// Adds a constraint, creates links, no trivial/tautology checks. + /// + /// - should only be called by `add_cstr` + /// - shrinks the constraints first + /// - registers the constraint in the constraint info structure + /// - performs the usefulness check + fn raw_add_cstr(&mut self, constraint: Constraint) -> Res { + self.shrink_constraints(); + let cstr_index = self.constraints.next_index(); + + // Create links. + if let Some(lhs) = constraint.lhs() { + for (pred, argss) in lhs { + for args in argss { + let is_new = self.map[*pred] + .entry(args.clone()) + .or_insert_with(|| CstrSet::with_capacity(17)) + .insert(cstr_index); + debug_assert! { is_new } + } + } + } + if let Some(&Sample { pred, ref args }) = constraint.rhs() { + let is_new = self.map[pred] + .entry(args.clone()) + .or_insert_with(|| CstrSet::with_capacity(17)) + .insert(cstr_index); debug_assert! { is_new } - true - } } - ) ; - profile! { self mark "propagate", "filtering" } - // Move on if nothing's left. - if argss.is_empty() { continue 'propagate } + self.cstr_info.register_modded(cstr_index, &constraint)?; - if pos { - pos_cnt += argss.len() - } else { - neg_cnt += argss.len() - } + self.constraints.push(constraint); - // Update the constraints that mention these new `pos` samples. - for args in argss { - profile! { - self "partial samples" => add { - if args.is_partial() { 1 } else { 0 } + if !self + .cstr_useful(cstr_index) + .chain_err(|| "in raw_add_cstr")? + { + self.tautologize(cstr_index)?; + Ok(false) + } else { + Ok(true) + } + } + + /// Adds a sample or a constraint. + pub fn add_data( + &mut self, + clause: ClsIdx, + mut lhs: Vec<(PrdIdx, RVarVals)>, + rhs: Option<(PrdIdx, RVarVals)>, + ) -> Res { + let rhs = match rhs { + Some((pred, sample)) => if lhs.is_empty() { + // Positive sample. + let new = self.add_raw_pos(clause, pred, sample); + return Ok(new); + } else { + // Constraint. + Some((pred, sample)) + }, + + None => if lhs.len() == 1 { + // Negative sample. + let (pred, sample) = lhs.pop().expect("failed pop on vector of length 1"); + debug_assert_eq! { lhs.len(), 0 } + let new = self.add_raw_neg(clause, pred, sample); + return Ok(new); + } else { + // Constraint. + None + }, + }; + + // Only reachable if we're not adding a pos/neg sample, or the input isn't + // legal. + debug_assert! { + lhs.len() > 1 || ( + lhs.len() == 1 && rhs.is_some() + ) + } + + self.add_cstr(clause, lhs, rhs) + } + + /// Prunes the lhs and the rhs of a constraint. + /// + /// Removes samples that are known to be true/false. Returns `None` if the + /// constraint is trivial. + pub fn prune_cstr( + &mut self, + lhs: Vec<(PrdIdx, RVarVals)>, + rhs: Option<(PrdIdx, RVarVals)>, + ) -> Res, Option)>> { + let mut nu_lhs = PrdHMap::with_capacity(lhs.len()); + + // Look at the lhs and remove stuff we know is true. + 'lhs_iter: for (pred, args) in lhs { + let (args, is_new) = var_to::vals::new_is_new(args); + + // If no partial examples and sample is new, no need to check anything. + if conf.teacher.partial || !is_new { + if args.set_subsumed(&self.pos[pred]) { + // Positive, skip. + continue 'lhs_iter; + } else if args.set_subsumed(&self.neg[pred]) { + // Negative, constraint is trivial. + profile! { self mark "add cstr", "pre-checks" } + profile! { self "trivial constraints" => add 1 } + return Ok(None); + } + } + + let set = nu_lhs.entry(pred).or_insert_with(VarValsSet::new); + + // Partial samples are not allowed in constraints, no subsumption + // check in set. + set.insert(args); + () + } + + let nu_rhs = if let Some((pred, args)) = rhs { + let (args, is_new) = var_to::vals::new_is_new(args.clone()); + + let args = if conf.teacher.partial || !is_new { + if args.set_subsumed(&self.pos[pred]) { + profile! { self mark "add cstr", "pre-checks" } + profile! { self "trivial constraints" => add 1 } + // Positive, constraint is trivial. + return Ok(None); + } else if args.set_subsumed(&self.neg[pred]) { + // Negative, ignore. + None + } else { + Some(args) + } + } else { + Some(args) + }; + + if let Some(args) = args.as_ref() { + // Subsumed by lhs? + if let Some(argss) = nu_lhs.get(&pred) { + // Partial samples are not allowed in constraints, no subsumption + // check. + if argss.contains(&args) { + profile! { self mark "add cstr", "pre-checks" } + profile! { self "trivial constraints" => add 1 } + // Trivially implied by lhs. + return Ok(None); + } + } + } + + args.map(|args| Sample { pred, args }) + } else { + None + }; + + nu_lhs.shrink_to_fit(); + + Ok(Some((nu_lhs, nu_rhs))) + } + + /// Adds a constraint. + /// + /// Returns `true` and if something new was added. + /// + /// The `clause` input is necessary for unsat core extraction. + /// + /// Partial samples ARE NOT ALLOWED in constraints. + /// + /// - propagates staged samples beforehand + pub fn add_cstr( + &mut self, + _clause: ClsIdx, + lhs: Vec<(PrdIdx, RVarVals)>, + rhs: Option<(PrdIdx, RVarVals)>, + ) -> Res { + profile!( + self wrap { self.propagate() } + "add cstr", "pre-propagate" + )?; + + if_log! { @4 + log! { @4 "adding constraint" } + if let Some((pred, args)) = rhs.as_ref() { + log! { @4 "({} {})", self.instance[* pred], args } + } else { + log! { @4 "false" } + } + let mut pref = "<=" ; + for (pred, args) in & lhs { + log! { @4 "{} ({} {})", pref, self.instance[* pred], args } + pref = " " } } - if let Some(constraints) = self.remove_subs(pred, & args) { + profile! { self tick "add cstr", "pre-checks" } + + let (nu_lhs, nu_rhs) = if let Some(res) = self.prune_cstr(lhs, rhs)? { + res + } else { + return Ok(false); + }; + + let mut constraint = Constraint::new(nu_lhs, nu_rhs); + constraint.check().chain_err(|| { + format!( + "while checking {}", + constraint.to_string_info(self.instance.preds()).unwrap() + ) + })?; + debug_assert! { ! constraint.is_tautology() } + + profile! { self mark "add cstr", "pre-checks" } + + match constraint.try_trivial() { + Either::Left((Sample { pred, args }, pos)) => { + let is_new = self.staged.add(pred, args, pos); + Ok(is_new) + } + Either::Right(false) => { + // Handles linking and constraint info registration. + let is_new = profile!( + self wrap { self.raw_add_cstr(constraint) } + "add cstr", "raw" + )?; - profile! { self tick "propagate", "cstr update" } - for constraint_idx in constraints { - let constraint = & mut self.constraints[constraint_idx] ; - let map = & mut self.map ; + self.check("after add_cstr")?; - let tautology = constraint.force_sample( - pred, & args, pos, |pred, args| Self::tauto_fun( - map, constraint_idx, pred, & args + Ok(is_new) + } + Either::Right(true) => unsat!("by `true => false` in constraint (data, add_cstr)"), + } + } + + /// Sets all the unknown data of a given predicate to `pos`, and + /// propagates. + /// + /// This is only used by learner(s). + pub fn force_pred(&mut self, pred: PrdIdx, pos: bool) -> Res<()> { + profile! { self tick "force pred", "pre-checks" } + let mut modded_constraints = CstrSet::new(); + scoped! { + let map = & mut self.map ; + let mut constraints = CstrSet::new() ; + for (_, cs) in map[pred].drain() { + for c in cs { + constraints.insert(c) ; + } + } + for constraint in constraints { + let tautology = self.constraints[constraint].force( + pred, pos, |pred, args| Self::tauto_fun( + map, constraint, pred, & args ) - ).chain_err( - || "in propagate" ) ? ; if tautology { // Tautology, discard. - self.cstr_info.forget(constraint_idx) + self.cstr_info.forget(constraint) } else { - match constraint.try_trivial() { + match self.constraints[constraint].try_trivial() { Either::Left((Sample { pred, args }, pos)) => { // Constraint is trivial: unlink and forget. if let Some(set) = map[pred].get_mut(& args) { - let was_there = set.remove(& constraint_idx) ; + let was_there = set.remove(& constraint) ; debug_assert! { was_there } } - self.cstr_info.forget(constraint_idx) ; + self.cstr_info.forget(constraint) ; // Stage the consequence of the triviality. self.staged.add(pred, args, pos) ; }, Either::Right(false) => { - // Otherwise, the constraint was modified and we're keeping - // it. + // Otherwise, the constraint was modified and we're keeping it. self.cstr_info.register_modded( - constraint_idx, & constraint + constraint, & self.constraints[constraint] ) ? ; - modded_constraints.insert(constraint_idx) ; - } + modded_constraints.insert(constraint) ; + }, Either::Right(true) => unsat!( - "by `true => false` in constraint (data, propagate)" + "by `true => false` in constraint (data, force_pred)" ), } } } - profile! { self mark "propagate", "cstr update" } - - for constraint in modded_constraints.drain() { - if ! self.constraints[constraint].is_tautology() - && ! self.cstr_useful(constraint).chain_err( - || "in propagate" - ) ? { - self.tautologize(constraint) ? - } - } - } - } - } - - profile! { self tick "propagate", "check shrink" } - self.check("after propagate") ? ; - - self.shrink_constraints() ; - profile! { self mark "propagate", "check shrink" } - - profile! { self mark "propagate" } - - Ok((pos_cnt, neg_cnt)) - } - - - - /// Length of positive/negative samples and constraints. - pub fn metrics(& self) -> (usize, usize, usize) { - ( - self.pos.iter().fold(0, |acc, samples| acc + samples.len()), - self.neg.iter().fold(0, |acc, samples| acc + samples.len()), - self.constraints.len() - ) - } - - - /// Adds a constraint, creates links, no trivial/tautology checks. - /// - /// - should only be called by `add_cstr` - /// - shrinks the constraints first - /// - registers the constraint in the constraint info structure - /// - performs the usefulness check - fn raw_add_cstr(& mut self, constraint: Constraint) -> Res { - self.shrink_constraints() ; - let cstr_index = self.constraints.next_index() ; - - // Create links. - if let Some(lhs) = constraint.lhs() { - for (pred, argss) in lhs { - for args in argss { - let is_new = self.map[* pred].entry( args.clone() ).or_insert_with( - || CstrSet::with_capacity(17) - ).insert(cstr_index) ; - debug_assert! { is_new } + for constraint in modded_constraints.drain() { + if !self.constraints[constraint].is_tautology() && !self.cstr_useful(constraint)? { + self.tautologize(constraint)? + } } - } - } - if let Some( & Sample { pred, ref args } ) = constraint.rhs() { - let is_new = self.map[pred].entry( args.clone() ).or_insert_with( - || CstrSet::with_capacity(17) - ).insert(cstr_index) ; - debug_assert! { is_new } - } - - self.cstr_info.register_modded( - cstr_index, & constraint - ) ? ; - - self.constraints.push(constraint) ; + profile! { self mark "force pred", "pre-checks" } - if ! self.cstr_useful(cstr_index).chain_err( - || "in raw_add_cstr" - ) ? { - self.tautologize(cstr_index) ? ; - Ok(false) - } else { - Ok(true) - } - } - - /// Adds a sample or a constraint. - pub fn add_data( - & mut self, clause: ClsIdx, - mut lhs: Vec< (PrdIdx, RVarVals) >, rhs: Option<(PrdIdx, RVarVals)>, - ) -> Res { - - let rhs = match rhs { - - Some((pred, sample)) => if lhs.is_empty() { - // Positive sample. - let new = self.add_raw_pos(clause, pred, sample) ; - return Ok(new) - } else { - // Constraint. - Some((pred, sample)) - }, - - None => if lhs.len() == 1 { - // Negative sample. - let (pred, sample) = lhs.pop().expect( - "failed pop on vector of length 1" - ) ; - debug_assert_eq! { lhs.len(), 0 } - let new = self.add_raw_neg(clause, pred, sample) ; - return Ok(new) - } else { - // Constraint. - None - } - - } ; + profile!( + self wrap { self.propagate() } + "force pred", "propagate" + )?; - // Only reachable if we're not adding a pos/neg sample, or the input isn't - // legal. - debug_assert! { - lhs.len() > 1 || ( - lhs.len() == 1 && rhs.is_some() - ) + Ok(()) } - self.add_cstr(clause, lhs, rhs) - } - - - /// Prunes the lhs and the rhs of a constraint. - /// - /// Removes samples that are known to be true/false. Returns `None` if the - /// constraint is trivial. - pub fn prune_cstr( - & mut self, lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)> - ) -> Res< Option<( - PrdHMap, Option - )> > { - let mut nu_lhs = PrdHMap::with_capacity( lhs.len() ) ; - - // Look at the lhs and remove stuff we know is true. - 'lhs_iter: for (pred, args) in lhs { - let (args, is_new) = var_to::vals::new_is_new( args ) ; - - // If no partial examples and sample is new, no need to check anything. - if conf.teacher.partial || ! is_new { - if args.set_subsumed(& self.pos[pred]) { - // Positive, skip. - continue 'lhs_iter - } else if args.set_subsumed(& self.neg[pred]) { - // Negative, constraint is trivial. - profile! { self mark "add cstr", "pre-checks" } - profile! { self "trivial constraints" => add 1 } - return Ok(None) + /// The projected data for some predicate. + pub fn data_of(&self, pred: PrdIdx) -> CData { + profile! { self tick "data of" } + let unc_set = &self.map[pred]; + let pos_set = &self.pos[pred]; + let neg_set = &self.neg[pred]; + let pos_single_set = &self.pos_single[pred]; + let neg_single_set = &self.neg_single[pred]; + + let (mut pos, mut neg, mut unc, mut pos_single, mut neg_single) = ( + Vec::with_capacity(pos_set.len()), + Vec::with_capacity(neg_set.len()), + Vec::with_capacity(unc_set.len()), + Vec::with_capacity(pos_single_set.len()), + Vec::with_capacity(neg_single_set.len()), + ); + + for sample in pos_set.iter() { + pos.push(sample.clone()) + } + for sample in neg_set.iter() { + neg.push(sample.clone()) + } + for (sample, set) in unc_set.iter() { + if !set.is_empty() { + unc.push(sample.clone()) + } } - } - let set = nu_lhs.entry(pred).or_insert_with(VarValsSet::new) ; + for sample in pos_single_set { + if pos.contains(sample) { + pos_single.push(sample.clone()) + } + } + for sample in neg_single_set { + if neg.contains(sample) { + neg_single.push(sample.clone()) + } + } - // Partial samples are not allowed in constraints, no subsumption - // check in set. - set.insert(args) ; - () + profile! { self mark "data of" } + CData::new(pos, neg, unc, pos_single, neg_single) } - let nu_rhs = if let Some((pred, args)) = rhs { - let (args, is_new) = var_to::vals::new_is_new( args.clone() ) ; - - let args = if conf.teacher.partial || ! is_new { - if args.set_subsumed(& self.pos[pred]) { - profile! { self mark "add cstr", "pre-checks" } - profile! { self "trivial constraints" => add 1 } - // Positive, constraint is trivial. - return Ok(None) - } else if args.set_subsumed(& self.neg[pred]) { - // Negative, ignore. - None - } else { - Some(args) - } - } else { - Some(args) - } ; - - if let Some(args) = args.as_ref() { - // Subsumed by lhs? - if let Some(argss) = nu_lhs.get(& pred) { - // Partial samples are not allowed in constraints, no subsumption - // check. - if argss.contains(& args) { - profile! { self mark "add cstr", "pre-checks" } - profile! { self "trivial constraints" => add 1 } - // Trivially implied by lhs. - return Ok(None) - } + /// Applies the classification represented by the data to some projected + /// data. + /// + /// This is used when backtracking in the learner. The assumption is that all + /// arguments in `data` are in `self`. That is, there is no subsumption + /// checking. + pub fn classify(&self, pred: PrdIdx, data: &mut CData) { + profile!{ + self wrap { + data.classify( + |sample| if self.pos[pred].contains(sample) { + Some(true) + } else if self.neg[pred].contains(sample) { + Some(false) + } else { + None + } + ) + } "classify" } - } - - args.map(|args| Sample { pred, args }) - } else { - None - } ; - - nu_lhs.shrink_to_fit() ; - - Ok( Some((nu_lhs, nu_rhs)) ) - } - - - /// Adds a constraint. - /// - /// Returns `true` and if something new was added. - /// - /// The `clause` input is necessary for unsat core extraction. - /// - /// Partial samples ARE NOT ALLOWED in constraints. - /// - /// - propagates staged samples beforehand - pub fn add_cstr( - & mut self, _clause: ClsIdx, - lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)> - ) -> Res< bool > { - profile!( - self wrap { self.propagate() } - "add cstr", "pre-propagate" - ) ? ; - - if_log! { @4 - log! { @4 "adding constraint" } - if let Some((pred, args)) = rhs.as_ref() { - log! { @4 "({} {})", self.instance[* pred], args } - } else { - log! { @4 "false" } - } - let mut pref = "<=" ; - for (pred, args) in & lhs { - log! { @4 "{} ({} {})", pref, self.instance[* pred], args } - pref = " " - } } - profile! { self tick "add cstr", "pre-checks" } - - let (nu_lhs, nu_rhs) = if let Some(res) = self.prune_cstr(lhs, rhs) ? { - res - } else { - return Ok(false) - } ; - - let mut constraint = Constraint::new(nu_lhs, nu_rhs) ; - constraint.check().chain_err( - || format!( - "while checking {}", constraint.to_string_info( - self.instance.preds() - ).unwrap() - ) - ) ? ; - debug_assert! { ! constraint.is_tautology() } - - profile! { self mark "add cstr", "pre-checks" } - - match constraint.try_trivial() { - Either::Left((Sample { pred, args }, pos)) => { - let is_new = self.staged.add(pred, args, pos) ; - Ok(is_new) - }, - Either::Right(false) => { - // Handles linking and constraint info registration. - let is_new = profile!( - self wrap { self.raw_add_cstr(constraint) } - "add cstr", "raw" - ) ? ; - - self.check("after add_cstr") ? ; - - Ok(is_new) - }, - Either::Right(true) => unsat!( - "by `true => false` in constraint (data, add_cstr)" - ), + /// Checks the state of the data. Does nothing in release. + /// + /// Checks: + /// + /// - no positive or negative examples staged + /// - set of modified clauses makes sense + /// - positive / negative samples are not redundant + /// - no positive / negative data is linked to some constraints + /// - `(pred, sample, constraint)` in [`self.map`][map] implies `(pred + /// sample)` in [`self.constraints`][cstrs]`[constraint]`'s lhs or rhs + /// - ...and conversely + /// - no redundant constraints + /// + /// [map]: #structfield.map (map field) + /// [cstrs]: #structfield.constraints (constraints field) + #[cfg(debug_assertions)] + pub fn check(&self, blah: &'static str) -> Res<()> { + self.check_internal() + .chain_err(|| self.string_do(&(), |s| s.to_string()).unwrap()) + .chain_err(|| blah) } - } - - - - - - - - - - /// Sets all the unknown data of a given predicate to `pos`, and - /// propagates. - /// - /// This is only used by learner(s). - pub fn force_pred( - & mut self, pred: PrdIdx, pos: bool - ) -> Res<()> { - profile! { self tick "force pred", "pre-checks" } - let mut modded_constraints = CstrSet::new() ; - scoped! { - let map = & mut self.map ; - let mut constraints = CstrSet::new() ; - for (_, cs) in map[pred].drain() { - for c in cs { - constraints.insert(c) ; + /// Checks the data is consistent. + #[cfg(debug_assertions)] + fn check_internal(&self) -> Res<()> { + if !self.staged.is_empty() { + bail!("there are staged samples...") } - } - for constraint in constraints { - let tautology = self.constraints[constraint].force( - pred, pos, |pred, args| Self::tauto_fun( - map, constraint, pred, & args - ) - ) ? ; - if tautology { - // Tautology, discard. - self.cstr_info.forget(constraint) - } else { + self.check_modded()?; + self.check_neg()?; + self.check_pos()?; + self.check_constraint_data()?; + self.check_redundant()?; + + macro_rules! constraint_map { + ($cstr:expr => |$pred:ident, $sample:ident| $body:expr) => { + if let Some(lhs) = $cstr.lhs() { + for (pred, samples) in lhs { + let $pred = *pred; + for $sample in samples { + $body + } + } + } + if let Some(&Sample { + pred: $pred, + args: ref $sample, + }) = $cstr.rhs() + { + $body + } + }; + } - match self.constraints[constraint].try_trivial() { - Either::Left((Sample { pred, args }, pos)) => { - // Constraint is trivial: unlink and forget. - if let Some(set) = map[pred].get_mut(& args) { - let was_there = set.remove(& constraint) ; - debug_assert! { was_there } + // Constraints are consistent with map. + for (idx, constraint) in self.constraints.index_iter() { + constraint_map!{ + constraint => |prd, sample| { + if ! self.map[prd].get(sample).map( + |set| set.contains(& idx) + ).unwrap_or(false) { + bail!( + "{}\n({} {}) appears in constraint #{} \ + but is not registered in map", + self.string_do(& (), |s| s.to_string()).unwrap(), + self.instance[prd], sample, idx + ) + } } - self.cstr_info.forget(constraint) ; - // Stage the consequence of the triviality. - self.staged.add(pred, args, pos) ; - }, - Either::Right(false) => { - // Otherwise, the constraint was modified and we're keeping it. - self.cstr_info.register_modded( - constraint, & self.constraints[constraint] - ) ? ; - modded_constraints.insert(constraint) ; - }, - Either::Right(true) => unsat!( - "by `true => false` in constraint (data, force_pred)" - ), - } + } } - } - } - for constraint in modded_constraints.drain() { - if ! self.constraints[constraint].is_tautology() - && ! self.cstr_useful(constraint) ? { - self.tautologize(constraint) ? - } - } - profile! { self mark "force pred", "pre-checks" } + // Map is consistent with constraints. + for (pred, map) in self.map.index_iter() { + for (sample, set) in map { + for idx in set { + let c = &self.constraints[*idx]; + let mut okay = false; + constraint_map! { + c => |p, s| if p == pred && s == sample { + okay = true + } + } + if !okay { + bail!( + "{}\n({} {}) registered in map for constraint #{} \ + but does not appear in this constraint", + self.string_do(&(), |s| s.to_string()).unwrap(), + self.instance[pred], + sample, + idx + ) + } + } + } + } - profile!( - self wrap { self.propagate() } - "force pred", "propagate" - ) ? ; - - Ok(()) - } - - /// The projected data for some predicate. - pub fn data_of(& self, pred: PrdIdx) -> CData { - profile! { self tick "data of" } - let unc_set = & self.map[pred] ; - let pos_set = & self.pos[pred] ; - let neg_set = & self.neg[pred] ; - let pos_single_set = & self.pos_single[pred] ; - let neg_single_set = & self.neg_single[pred] ; - - let (mut pos, mut neg, mut unc, mut pos_single, mut neg_single) = ( - Vec::with_capacity( pos_set.len() ), - Vec::with_capacity( neg_set.len() ), - Vec::with_capacity( unc_set.len() ), - Vec::with_capacity( pos_single_set.len() ), - Vec::with_capacity( neg_single_set.len() ), - ) ; - - for sample in pos_set.iter() { - pos.push( sample.clone() ) - } - for sample in neg_set.iter() { - neg.push( sample.clone() ) - } - for (sample, set) in unc_set.iter() { - if ! set.is_empty() { - unc.push( sample.clone() ) - } - } + for constraint in &self.constraints { + constraint.check().chain_err(|| { + format!( + "while checking {}", + constraint.to_string_info(self.instance.preds()).unwrap() + ) + })? + } - for sample in pos_single_set { - if pos.contains(sample) { - pos_single.push( sample.clone() ) - } - } - for sample in neg_single_set { - if neg.contains(sample) { - neg_single.push( sample.clone() ) - } + Ok(()) } - profile! { self mark "data of" } - CData::new(pos, neg, unc, pos_single, neg_single) - } - - - /// Applies the classification represented by the data to some projected - /// data. - /// - /// This is used when backtracking in the learner. The assumption is that all - /// arguments in `data` are in `self`. That is, there is no subsumption - /// checking. - pub fn classify( - & self, pred: PrdIdx, data: & mut CData - ) { - profile!{ - self wrap { - data.classify( - |sample| if self.pos[pred].contains(sample) { - Some(true) - } else if self.neg[pred].contains(sample) { - Some(false) - } else { - None - } - ) - } "classify" - } - } - - - - - - /// Checks the state of the data. Does nothing in release. - /// - /// Checks: - /// - /// - no positive or negative examples staged - /// - set of modified clauses makes sense - /// - positive / negative samples are not redundant - /// - no positive / negative data is linked to some constraints - /// - `(pred, sample, constraint)` in [`self.map`][map] implies `(pred - /// sample)` in [`self.constraints`][cstrs]`[constraint]`'s lhs or rhs - /// - ...and conversely - /// - no redundant constraints - /// - /// [map]: #structfield.map (map field) - /// [cstrs]: #structfield.constraints (constraints field) - #[cfg(debug_assertions)] - pub fn check(& self, blah: & 'static str) -> Res<()> { - self.check_internal().chain_err( - || self.string_do(& (), |s| s.to_string()).unwrap() - ).chain_err(|| blah) - } - - - /// Checks the data is consistent. - #[cfg(debug_assertions)] - fn check_internal(& self) -> Res<()> { - if ! self.staged.is_empty() { - bail!("there are staged samples...") + /// Checks modded constraints. + #[cfg(debug_assertions)] + fn check_modded(&self) -> Res<()> { + for constraint in self.cstr_info.modded() { + let oob = *constraint >= self.constraints.len(); + let tautology = self.constraints[*constraint].is_tautology(); + if oob || tautology { + bail!("modded_constraints is out of sync") + } + } + Ok(()) } - self.check_modded() ? ; - self.check_neg() ? ; - self.check_pos() ? ; - self.check_constraint_data() ? ; - self.check_redundant() ? ; - - - macro_rules! constraint_map { - ($cstr:expr => |$pred:ident, $sample:ident| $body:expr) => ( - if let Some(lhs) = $cstr.lhs() { - for (pred, samples) in lhs { - let $pred = * pred ; - for $sample in samples { $body } - } + /// Checks negative constraints. + #[cfg(debug_assertions)] + fn check_neg(&self) -> Res<()> { + for constraint in self.cstr_info.neg() { + if *constraint >= self.constraints.len() { + bail!("neg_constraints is out of sync") + } + if self.constraints[*constraint].rhs().is_some() { + bail!( + "neg_constraints contains non-negative constraint {}", + self.constraints[*constraint] + .to_string_info(self.instance.preds()) + .unwrap() + ) + } + if self.constraints[*constraint].is_tautology() { + bail!( + "neg_constraints contains tautology {}", + self.constraints[*constraint] + .to_string_info(self.instance.preds()) + .unwrap() + ) + } } - if let Some( - & Sample { pred: $pred, args: ref $sample } - ) = $cstr.rhs() { - $body + for (index, constraint) in self.constraints.index_iter() { + if !constraint.is_tautology() + && constraint.rhs().is_none() + && !self.cstr_info.neg().contains(&index) + { + bail!( + "unregistered negative constraint {}", + constraint.to_string_info(self.instance.preds()).unwrap() + ) + } } - ) ; + Ok(()) } - // Constraints are consistent with map. - for (idx, constraint) in self.constraints.index_iter() { - constraint_map!{ - constraint => |prd, sample| { - if ! self.map[prd].get(sample).map( - |set| set.contains(& idx) - ).unwrap_or(false) { - bail!( - "{}\n({} {}) appears in constraint #{} \ - but is not registered in map", - self.string_do(& (), |s| s.to_string()).unwrap(), - self.instance[prd], sample, idx - ) - } + /// Checks positive constraints. + #[cfg(debug_assertions)] + fn check_pos(&self) -> Res<()> { + for set in &self.pos { + for sample in set { + for s in set { + if sample.subsumes(s) && sample != s { + bail!("positive samples are redundant: {} => {}", sample, s) + } + } + } } - } + for set in &self.neg { + for sample in set { + for s in set { + if sample.subsumes(s) && sample != s { + bail!("negative samples are redundant: {} => {}", sample, s) + } + } + } + } + Ok(()) } - // Map is consistent with constraints. - for (pred, map) in self.map.index_iter() { - for (sample, set) in map { - for idx in set { - let c = & self.constraints[* idx] ; - let mut okay = false ; - constraint_map! { - c => |p, s| if p == pred && s == sample { - okay = true + /// Checks pos/neg data does not appear in constraints. + #[cfg(debug_assertions)] + fn check_constraint_data(&self) -> Res<()> { + for pred in self.instance.pred_indices() { + let pos = self.pos[pred].iter().map(|p| (p, "positive")); + let neg = self.neg[pred].iter().map(|n| (n, "negative")); + for (sample, polarity) in pos.chain(neg) { + for (s, set) in &self.map[pred] { + if sample.subsumes(s) { + let mut s: String = "{".into(); + for idx in set { + s.push_str(&format!(" {}", idx)) + } + s.push_str(" }"); + bail!( + "({} {}) is {} but appears in constraint(s) {}", + self.instance[pred], + sample, + polarity, + s + ) + } + } } - } - if ! okay { - bail!( - "{}\n({} {}) registered in map for constraint #{} \ - but does not appear in this constraint", - self.string_do(& (), |s| s.to_string()).unwrap(), - self.instance[pred], sample, idx - ) - } } - } + Ok(()) } - for constraint in & self.constraints { - constraint.check().chain_err( - || format!( - "while checking {}", constraint.to_string_info( - self.instance.preds() - ).unwrap() - ) - ) ? + /// Checks that there are no redundant constraints. + #[cfg(debug_assertions)] + fn check_redundant(&self) -> Res<()> { + let mut constraint_iter = self.constraints.iter(); + while let Some(c_1) = constraint_iter.next() { + c_1.check()?; + for c_2 in constraint_iter.clone() { + if !c_1.is_tautology() && !c_2.is_tautology() && c_1.compare(c_2)?.is_some() { + bail!(format!( + "found two redundant constraints:\n{}\n{}", + c_1.string_do(&self.instance.preds(), |s| s.to_string()) + .unwrap(), + c_2.string_do(&self.instance.preds(), |s| s.to_string()) + .unwrap(), + )) + } + } + } + Ok(()) } - Ok(()) - } - - /// Checks modded constraints. - #[cfg(debug_assertions)] - fn check_modded(& self) -> Res<()> { - for constraint in self.cstr_info.modded() { - if * constraint >= self.constraints.len() - || self.constraints[* constraint].is_tautology() { - bail!("modded_constraints is out of sync") - } + #[cfg(not(debug_assertions))] + #[inline] + pub fn check(&self, _: &'static str) -> Res<()> { + Ok(()) } - Ok(()) - } - - /// Checks negative constraints. - #[cfg(debug_assertions)] - fn check_neg(& self) -> Res<()> { - for constraint in self.cstr_info.neg() { - if * constraint >= self.constraints.len() { - bail!("neg_constraints is out of sync") - } - if self.constraints[* constraint].rhs().is_some() { - bail!( - "neg_constraints contains non-negative constraint {}", - self.constraints[* constraint].to_string_info( - self.instance.preds() - ).unwrap() - ) - } - if self.constraints[* constraint].is_tautology() { - bail!( - "neg_constraints contains tautology {}", - self.constraints[* constraint].to_string_info( - self.instance.preds() - ).unwrap() - ) - } - } - for (index, constraint) in self.constraints.index_iter() { - if ! constraint.is_tautology() - && constraint.rhs().is_none() - && ! self.cstr_info.neg().contains(& index) { - bail!( - "unregistered negative constraint {}", - constraint.to_string_info( - self.instance.preds() - ).unwrap() - ) - } +} + +impl<'a> PebcakFmt<'a> for Data { + type Info = &'a (); + fn pebcak_err(&self) -> ErrorKind { + "during data pebcak formatting".into() } - Ok(()) - } - - /// Checks positive constraints. - #[cfg(debug_assertions)] - fn check_pos(& self) -> Res<()> { - for set in & self.pos { - for sample in set { - for s in set { - if sample.subsumes(s) - && sample != s { - bail!( - "positive samples are redundant: {} => {}", sample, s - ) - } + fn pebcak_io_fmt(&self, w: &mut W, _: &'a ()) -> IoRes<()> { + let map = self.instance.preds(); + write!(w, "pos (")?; + for (pred, set) in self.pos.index_iter() { + for args in set.iter() { + write!(w, "\n ({}", map[pred])?; + for arg in args.iter() { + write!(w, " {}", arg)? + } + write!(w, ")")? + } } - } - } - for set in & self.neg { - for sample in set { - for s in set { - if sample.subsumes(s) - && sample != s { - bail!( - "negative samples are redundant: {} => {}", sample, s - ) - } + write!(w, "\n) neg (")?; + for (pred, set) in self.neg.index_iter() { + for args in set.iter() { + write!(w, "\n ({}", map[pred])?; + for arg in args.iter() { + write!(w, " {}", arg)? + } + write!(w, ")")? + } } - } - } - Ok(()) - } - - /// Checks pos/neg data does not appear in constraints. - #[cfg(debug_assertions)] - fn check_constraint_data(& self) -> Res<()> { - for pred in self.instance.pred_indices() { - let pos = self.pos[pred].iter().map( - |p| (p, "positive") - ) ; - let neg = self.neg[pred].iter().map( - |n| (n, "negative") - ) ; - for (sample, polarity) in pos.chain(neg) { - for (s, set) in & self.map[pred] { - if sample.subsumes(s) { - let mut s: String = "{".into() ; - for idx in set { - s.push_str(& format!(" {}", idx)) + write!(w, "\n) constraints (")?; + for (index, cstr) in self.constraints.index_iter() { + write!(w, "\n {: >3} | ", index)?; + if cstr.is_tautology() { + write!(w, "_")? + } else { + cstr.pebcak_io_fmt(w, map)? } - s.push_str(" }") ; - bail!( - "({} {}) is {} but appears in constraint(s) {}", - self.instance[pred], sample, polarity, s - ) - } } - } - } - Ok(()) - } - - /// Checks that there are no redundant constraints. - #[cfg(debug_assertions)] - fn check_redundant(& self) -> Res<()> { - let mut constraint_iter = self.constraints.iter() ; - while let Some(c_1) = constraint_iter.next() { - c_1.check() ? ; - for c_2 in constraint_iter.clone() { - if ! c_1.is_tautology() - && ! c_2.is_tautology() - && c_1.compare(c_2)?.is_some() { - bail!( - format!( - "found two redundant constraints:\n{}\n{}", - c_1.string_do( - & self.instance.preds(), |s| s.to_string() - ).unwrap(), - c_2.string_do( - & self.instance.preds(), |s| s.to_string() - ).unwrap(), - ) - ) + write!(w, "\n) constraint map(")?; + for (pred, samples) in self.map.index_iter() { + for (sample, set) in samples.iter() { + write!(w, "\n ({}", map[pred])?; + for arg in sample.iter() { + write!(w, " {}", arg)? + } + write!(w, ") ->")?; + for pred in set.iter() { + write!(w, " {}", pred)? + } + } } - } - } - Ok(()) - } - - - #[cfg(not(debug_assertions))] - #[inline] - pub fn check(& self, _: & 'static str) -> Res<()> { Ok(()) } - -} - -impl<'a> PebcakFmt<'a> for Data { - type Info = & 'a () ; - fn pebcak_err(& self) -> ErrorKind { - "during data pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, _: & 'a () - ) -> IoRes<()> { - let map = self.instance.preds() ; - write!(w, "pos (") ? ; - for (pred, set) in self.pos.index_iter() { - for args in set.iter() { - write!(w, "\n ({}", map[pred]) ? ; - for arg in args.iter() { - write!(w, " {}", arg)? + write!(w, "\n) positive examples staged (")?; + for (pred, set) in &self.staged.pos { + write!(w, "\n {} |", self.instance[*pred])?; + for sample in set { + write!(w, " ({})", sample)? + } } - write!(w, ")") ? - } - } - write!(w, "\n) neg (") ? ; - for (pred, set) in self.neg.index_iter() { - for args in set.iter() { - write!(w, "\n ({}", map[pred]) ? ; - for arg in args.iter() { - write!(w, " {}", arg)? + writeln!(w, "\n) negative examples staged (")?; + for (pred, set) in &self.staged.neg { + write!(w, " {} |", self.instance[*pred])?; + for sample in set { + write!(w, " ({})", sample)? + } + writeln!(w)? } - write!(w, ")") ? - } - } - write!(w, "\n) constraints (") ? ; - for (index, cstr) in self.constraints.index_iter() { - write!(w, "\n {: >3} | ", index) ? ; - if cstr.is_tautology() { - write!(w, "_") ? - } else { - cstr.pebcak_io_fmt(w, map) ? - } - } - write!(w, "\n) constraint map(") ? ; - for (pred, samples) in self.map.index_iter() { - for (sample, set) in samples.iter() { - write!(w, "\n ({}", map[pred]) ? ; - for arg in sample.iter() { - write!(w, " {}", arg) ? + writeln!(w, ") modded (")?; + for cstr in self.cstr_info.modded() { + writeln!(w, " #{}", cstr)? } - write!(w, ") ->") ? ; - for pred in set.iter() { - write!(w, " {}", pred) ? + writeln!(w, ") neg (")?; + for cstr in self.cstr_info.neg() { + writeln!(w, " #{}", cstr)? } - } - } - write!(w, "\n) positive examples staged (") ? ; - for (pred, set) in & self.staged.pos { - write!(w, "\n {} |", self.instance[* pred]) ? ; - for sample in set { - write!(w, " ({})", sample) ? - } - } - writeln!(w, "\n) negative examples staged (") ? ; - for (pred, set) in & self.staged.neg { - write!(w, " {} |", self.instance[* pred]) ? ; - for sample in set { - write!(w, " ({})", sample) ? - } - writeln!(w) ? - } - writeln!(w, ") modded (") ? ; - for cstr in self.cstr_info.modded() { - writeln!(w, " #{}", cstr) ? + writeln!(w, ")")?; + // if let Some(graph) = self.graph.as_ref() { + // graph.write(w, "", & self.instance) ? ; + // } + Ok(()) } - writeln!(w, ") neg (") ? ; - for cstr in self.cstr_info.neg() { - writeln!(w, " #{}", cstr) ? - } - writeln!(w, ")") ? ; - // if let Some(graph) = self.graph.as_ref() { - // graph.write(w, "", & self.instance) ? ; - // } - Ok(()) - } } - - - /// Tiny internal structure storing samples for future propagation. #[derive(Clone)] struct Staged { - pos: PrdHMap, - neg: PrdHMap, + pos: PrdHMap, + neg: PrdHMap, } impl Staged { - /// Constructor. - pub fn with_capacity(capa: usize) -> Self { - Staged { - pos: PrdHMap::with_capacity(capa), - neg: PrdHMap::with_capacity(capa), + /// Constructor. + pub fn with_capacity(capa: usize) -> Self { + Staged { + pos: PrdHMap::with_capacity(capa), + neg: PrdHMap::with_capacity(capa), + } } - } - - /// True if empty. - #[allow(dead_code)] - pub fn is_empty(& self) -> bool { - self.pos.is_empty() && self.neg.is_empty() - } - - /// Returns a predicate used as a key in `pos` or `neg`. - /// - /// # Guarantees - /// - /// - the predicate returned is in `pos` (`neg`) if the boolean is true - /// (false) - fn get_pred(& self) -> Option<(PrdIdx, bool)> { - if let Some(pred) = self.pos.keys().next() { - Some( (* pred, true) ) - } else if let Some(pred) = self.neg.keys().next() { - Some( (* pred, false) ) - } else { - None + + /// True if empty. + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.pos.is_empty() && self.neg.is_empty() } - } - - /// Returns some staged arguments for a predicate. - /// - /// The boolean indicates whether the sample is positive. - pub fn pop(& mut self) -> Option<(PrdIdx, VarValsSet, bool)> { - if let Some((pred, pos)) = self.get_pred() { - if let Some(argss) = { - if pos { - self.pos.remove(& pred) + + /// Returns a predicate used as a key in `pos` or `neg`. + /// + /// # Guarantees + /// + /// - the predicate returned is in `pos` (`neg`) if the boolean is true + /// (false) + fn get_pred(&self) -> Option<(PrdIdx, bool)> { + if let Some(pred) = self.pos.keys().next() { + Some((*pred, true)) + } else if let Some(pred) = self.neg.keys().next() { + Some((*pred, false)) } else { - self.neg.remove(& pred) + None } - } { - Some((pred, argss, pos)) - } else { - fail_with!( - "In `Staged`: illegal `get_pred` result" - ) - } - } else { - None - } - } - - /// Adds a sample. - pub fn add(& mut self, pred: PrdIdx, args: VarVals, pos: bool) -> bool { - let map = if pos { - & mut self.pos - } else { - & mut self.neg - } ; - let set = map.entry(pred).or_insert_with( - || VarValsSet::with_capacity(11) - ) ; - let (subsumed, rmed) = args.set_subsumed_rm(set) ; - if subsumed { - debug_assert_eq! { rmed, 0 } - return false } - let is_new = set.insert(args) ; - // We checked `args` is not subsumed already, so it's necessarily new. - debug_assert! { is_new } - - true - } + /// Returns some staged arguments for a predicate. + /// + /// The boolean indicates whether the sample is positive. + pub fn pop(&mut self) -> Option<(PrdIdx, VarValsSet, bool)> { + if let Some((pred, pos)) = self.get_pred() { + if let Some(argss) = { + if pos { + self.pos.remove(&pred) + } else { + self.neg.remove(&pred) + } + } { + Some((pred, argss, pos)) + } else { + fail_with!("In `Staged`: illegal `get_pred` result") + } + } else { + None + } + } - /// Adds a positive sample. - #[inline] - pub fn add_pos(& mut self, pred: PrdIdx, args: VarVals) -> bool { - self.add(pred, args, true) - } + /// Adds a sample. + pub fn add(&mut self, pred: PrdIdx, args: VarVals, pos: bool) -> bool { + let map = if pos { &mut self.pos } else { &mut self.neg }; + let set = map + .entry(pred) + .or_insert_with(|| VarValsSet::with_capacity(11)); + let (subsumed, rmed) = args.set_subsumed_rm(set); + if subsumed { + debug_assert_eq! { rmed, 0 } + return false; + } - /// Adds a negative sample. - #[inline] - pub fn add_neg(& mut self, pred: PrdIdx, args: VarVals) -> bool { - self.add(pred, args, false) - } -} + let is_new = set.insert(args); + // We checked `args` is not subsumed already, so it's necessarily new. + debug_assert! { is_new } + true + } + /// Adds a positive sample. + #[inline] + pub fn add_pos(&mut self, pred: PrdIdx, args: VarVals) -> bool { + self.add(pred, args, true) + } + /// Adds a negative sample. + #[inline] + pub fn add_neg(&mut self, pred: PrdIdx, args: VarVals) -> bool { + self.add(pred, args, false) + } +} diff --git a/src/data/sample.rs b/src/data/sample.rs index 6587e30f..690aa549 100644 --- a/src/data/sample.rs +++ b/src/data/sample.rs @@ -1,65 +1,56 @@ -use common::{ - *, - var_to::vals::VarValsSet, -} ; +use common::{var_to::vals::VarValsSet, *}; /// A sample is some values for a predicate. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct Sample { - /// Predicate the sample is for. - pub pred: PrdIdx, - /// Arguments. - pub args: VarVals, + /// Predicate the sample is for. + pub pred: PrdIdx, + /// Arguments. + pub args: VarVals, } impl Sample { - /// Constructor. - pub fn new(pred: PrdIdx, args: VarVals) -> Self { - Sample { pred, args } - } + /// Constructor. + pub fn new(pred: PrdIdx, args: VarVals) -> Self { + Sample { pred, args } + } - /// Tests if a sample is about some predicate and its arguments is subsumed - /// by one of the elements of a set. - pub fn set_subsumed( - & self, pred: PrdIdx, samples: & VarValsSet - ) -> bool { - if self.pred != pred { - false - } else { - self.args.set_subsumed(samples) + /// Tests if a sample is about some predicate and its arguments is subsumed + /// by one of the elements of a set. + pub fn set_subsumed(&self, pred: PrdIdx, samples: &VarValsSet) -> bool { + if self.pred != pred { + false + } else { + self.args.set_subsumed(samples) + } } - } - /// Tests if a sample is about some predicate and its arguments is subsumed - /// by one of the elements of a set. - /// - /// Samples from the set that are subsumed by `self` are removed if `rm`. - pub fn set_subsumed_rm( - & self, pred: PrdIdx, samples: & mut VarValsSet - ) -> (bool, usize) { - if self.pred != pred { - (false, 0) - } else { - self.args.set_subsumed_rm(samples) + /// Tests if a sample is about some predicate and its arguments is subsumed + /// by one of the elements of a set. + /// + /// Samples from the set that are subsumed by `self` are removed if `rm`. + pub fn set_subsumed_rm(&self, pred: PrdIdx, samples: &mut VarValsSet) -> (bool, usize) { + if self.pred != pred { + (false, 0) + } else { + self.args.set_subsumed_rm(samples) + } } - } } impl<'a> PebcakFmt<'a> for Sample { - type Info = & 'a PrdInfos ; - fn pebcak_err(& self) -> ErrorKind { - "during sample pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, map: & 'a PrdInfos - ) -> IoRes<()> { - write!(w, "({}", map[self.pred].name) ? ; - for arg in self.args.iter() { - write!(w, " {}", arg) ? + type Info = &'a PrdInfos; + fn pebcak_err(&self) -> ErrorKind { + "during sample pebcak formatting".into() + } + fn pebcak_io_fmt(&self, w: &mut W, map: &'a PrdInfos) -> IoRes<()> { + write!(w, "({}", map[self.pred].name)?; + for arg in self.args.iter() { + write!(w, " {}", arg)? + } + write!(w, ")") } - write!(w, ")") - } } impl_fmt!{ Sample(self, fmt) { write!(fmt, "p_{} {}", self.pred, self.args) } -} \ No newline at end of file +} diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 7ffb61ba..5f8bd9ea 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -1,6 +1,6 @@ //! Datatypes. -use common::* ; +use common::*; wrap_usize! { #[doc = "Type parameter indices."] @@ -19,267 +19,236 @@ wrap_usize! { /// A concrete type, a polymorphic type, a datatype or a type parameter. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PartialTyp { - /// Array. - Array(Box, Box), - /// Datatype. - DTyp(String, ::parse::Pos, TPrmMap), - /// Concrete type. - Typ(Typ), - /// Type parameter. - Param(TPrmIdx), + /// Array. + Array(Box, Box), + /// Datatype. + DTyp(String, ::parse::Pos, TPrmMap), + /// Concrete type. + Typ(Typ), + /// Type parameter. + Param(TPrmIdx), } impl From for PartialTyp { - fn from(typ: Typ) -> Self { - PartialTyp::Typ(typ) - } + fn from(typ: Typ) -> Self { + PartialTyp::Typ(typ) + } } impl PartialTyp { - /// True if the type mentions the datatype provided. - pub fn mentions_dtyp( - & self, dtyp_name: & str - ) -> bool { - let mut to_do = vec![ self ] ; - let mut typ_to_do = vec![] ; - - loop { - if to_do.is_empty() && typ_to_do.is_empty() { - return false - } + /// True if the type mentions the datatype provided. + pub fn mentions_dtyp(&self, dtyp_name: &str) -> bool { + let mut to_do = vec![self]; + let mut typ_to_do = vec![]; + + loop { + if to_do.is_empty() && typ_to_do.is_empty() { + return false; + } - while let Some(current) = to_do.pop() { - match current { - PartialTyp::Array(src, tgt) => { - to_do.push( & ** src ) ; - to_do.push( & ** tgt ) - }, + while let Some(current) = to_do.pop() { + match current { + PartialTyp::Array(src, tgt) => { + to_do.push(&**src); + to_do.push(&**tgt) + } - PartialTyp::DTyp(name, _, _) if name == dtyp_name => return true, + PartialTyp::DTyp(name, _, _) if name == dtyp_name => return true, - PartialTyp::DTyp(_, _, prms) => for typ in prms { - to_do.push(typ) - }, + PartialTyp::DTyp(_, _, prms) => for typ in prms { + to_do.push(typ) + }, - PartialTyp::Typ(typ) => typ_to_do.push(typ), + PartialTyp::Typ(typ) => typ_to_do.push(typ), - PartialTyp::Param(_) => (), - } - } + PartialTyp::Param(_) => (), + } + } - while let Some(current) = typ_to_do.pop() { - use typ::RTyp ; - - match current.get() { - RTyp::Unk | RTyp::Int | RTyp::Real | RTyp::Bool => (), - RTyp::Array { src, tgt } => { - typ_to_do.push(src) ; - typ_to_do.push(tgt) - }, - RTyp::DTyp { dtyp, .. } if dtyp.name == dtyp_name => return true, - RTyp::DTyp { prms, .. } => for typ in prms { - typ_to_do.push(typ) - }, + while let Some(current) = typ_to_do.pop() { + use typ::RTyp; + + match current.get() { + RTyp::Unk | RTyp::Int | RTyp::Real | RTyp::Bool => (), + RTyp::Array { src, tgt } => { + typ_to_do.push(src); + typ_to_do.push(tgt) + } + RTyp::DTyp { dtyp, .. } if dtyp.name == dtyp_name => return true, + RTyp::DTyp { prms, .. } => for typ in prms { + typ_to_do.push(typ) + }, + } + } } - } - } - } - - /// Resolves a partial type against a type. - pub fn unify( - & self, typ: & Typ, map: & mut TPrmMap - ) -> Res<()> { - let mut stack = vec![ (self, typ) ] ; - - while let Some((ptyp, typ)) = stack.pop() { - use typ::RTyp ; - - match ( ptyp, typ.get() ) { - ( PartialTyp::Array(p_src, p_tgt), RTyp::Array { src, tgt } ) => { - stack.push( (p_src, src) ) ; - stack.push( (p_tgt, tgt) ) - }, - - ( PartialTyp::Typ(p_typ), _ ) => if ! p_typ.is_compatible(typ) { - bail!("cannot unify type {} and {}", ptyp, typ) - }, - ( PartialTyp::Param(idx), _ ) => if let Some(merged) = typ.merge( - & map[* idx] - ) { - map[* idx] = merged - } else { - bail!("cannot unify type {} and {}", ptyp, typ) - }, - - ( PartialTyp::DTyp(name, _, p_prms), RTyp::DTyp { dtyp, prms } ) - if name == & dtyp.name - && p_prms.len() == prms.len() => { - for (p_typ, typ) in p_prms.iter().zip( prms.iter() ) { - stack.push( (p_typ, typ) ) - } - }, + /// Resolves a partial type against a type. + pub fn unify(&self, typ: &Typ, map: &mut TPrmMap) -> Res<()> { + let mut stack = vec![(self, typ)]; + + while let Some((ptyp, typ)) = stack.pop() { + use typ::RTyp; + + match (ptyp, typ.get()) { + (PartialTyp::Array(p_src, p_tgt), RTyp::Array { src, tgt }) => { + stack.push((p_src, src)); + stack.push((p_tgt, tgt)) + } + + (PartialTyp::Typ(p_typ), _) => if !p_typ.is_compatible(typ) { + bail!("cannot unify type {} and {}", ptyp, typ) + }, + + (PartialTyp::Param(idx), _) => if let Some(merged) = typ.merge(&map[*idx]) { + map[*idx] = merged + } else { + bail!("cannot unify type {} and {}", ptyp, typ) + }, + + (PartialTyp::DTyp(name, _, p_prms), RTyp::DTyp { dtyp, prms }) + if name == &dtyp.name && p_prms.len() == prms.len() => + { + for (p_typ, typ) in p_prms.iter().zip(prms.iter()) { + stack.push((p_typ, typ)) + } + } + + _ => bail!("cannot unify {} with ({})", ptyp, typ), + } + } - _ => bail!( - "cannot unify {} with ({})", - ptyp, typ - ), - } + Ok(()) } - Ok(()) - } - - - - - fn write( - & self, w: & mut W, prms: & TPrmMap - ) -> ::std::io::Result<()> { - let mut stack = vec![ ("", self, "") ] ; + fn write(&self, w: &mut W, prms: &TPrmMap) -> ::std::io::Result<()> { + let mut stack = vec![("", self, "")]; + + while let Some((sep, typ, close)) = stack.pop() { + write!(w, "{}", sep)?; + match typ { + PartialTyp::Array(src, tgt) => { + stack.push((" ", &**tgt, ")")); + stack.push((" ", &**src, "")) + } + PartialTyp::DTyp(name, _, prms) => { + if !prms.is_empty() { + write!(w, "(")? + } + write!(w, "{}", name)?; + let mut first = true; + for sub in prms.iter().rev() { + let close = if first { + first = false; + ")" + } else { + "" + }; + stack.push((" ", sub, close)) + } + } + PartialTyp::Typ(typ) => write!(w, "{}", typ)?, + PartialTyp::Param(idx) => write!(w, "{}", prms[*idx])?, + } + write!(w, "{}", close)? + } - while let Some((sep, typ, close)) = stack.pop() { - write!(w, "{}", sep) ? ; - match typ { - PartialTyp::Array(src, tgt) => { - stack.push( (" ", & ** tgt, ")") ) ; - stack.push( (" ", & ** src, "") ) - }, - PartialTyp::DTyp(name, _, prms) => { - if ! prms.is_empty() { - write!(w, "(") ? - } - write!(w, "{}", name) ? ; - let mut first = true ; - for sub in prms.iter().rev() { - let close = if first { - first = false ; - ")" - } else { - "" - } ; - stack.push( (" ", sub, close) ) - } - }, - PartialTyp::Typ(typ) => write!(w, "{}", typ) ?, - PartialTyp::Param(idx) => write!(w, "{}", prms[* idx]) ?, - } - write!(w, "{}", close) ? + Ok(()) } - Ok(()) - } - - - - /// Checks a partial type is legal. - pub fn check(& self) -> Result<(), (::parse::Pos, String)> { - let mut stack = vec![self] ; - - while let Some(ptyp) = stack.pop() { - if let PartialTyp::DTyp(name, pos, args) = ptyp { - if get(name).is_err() { - return Err(( - * pos, format!("unknown sort `{}`", name) - )) - } - for arg in args { - stack.push(arg) + /// Checks a partial type is legal. + pub fn check(&self) -> Result<(), (::parse::Pos, String)> { + let mut stack = vec![self]; + + while let Some(ptyp) = stack.pop() { + if let PartialTyp::DTyp(name, pos, args) = ptyp { + if get(name).is_err() { + return Err((*pos, format!("unknown sort `{}`", name))); + } + for arg in args { + stack.push(arg) + } + } } - } - } - - Ok(()) - } - /// Turns a partial type into a concrete type. - pub fn to_type( - & self, prms: & TPrmMap - ) -> Result { - enum Frame<'a> { - ArrayLft(& 'a PartialTyp), - ArrayRgt(Typ), - DTyp(DTyp, TPrmMap, ::std::slice::Iter<'a, PartialTyp>), + Ok(()) } - let mut curr = self ; - let mut stack = vec![] ; - - 'go_down: loop { - - let mut typ = match curr { - - PartialTyp::Array(src, tgt) => { - curr = & ** src ; - stack.push( Frame::ArrayLft(& ** tgt) ) ; - - continue 'go_down - }, - - PartialTyp::DTyp(name, pos, prms) => { - let mut nu_prms = TPrmMap::with_capacity( prms.len() ) ; - let mut prms = prms.iter() ; - - let dtyp = if let Ok(dtyp) = get(name) { - dtyp - } else { - return Err( - ( * pos, "unknown datatype".into() ) - ) - } ; - - if let Some(partial) = prms.next() { - curr = partial ; - stack.push( - Frame::DTyp( dtyp, nu_prms, prms ) - ) ; - - continue 'go_down - } else { - typ::dtyp(dtyp, nu_prms) - } - }, - - PartialTyp::Typ(typ) => typ.clone(), - - PartialTyp::Param(idx) => prms[* idx].clone(), - - } ; - - 'go_up: loop { - - match stack.pop() { - None => return Ok(typ), - - Some( Frame::ArrayLft(tgt) ) => { - curr = tgt ; - stack.push( Frame::ArrayRgt(typ) ) ; - continue 'go_down - }, - - Some( Frame::ArrayRgt(src) ) => { - typ = typ::array(src, typ) ; - continue 'go_up - }, + /// Turns a partial type into a concrete type. + pub fn to_type(&self, prms: &TPrmMap) -> Result { + enum Frame<'a> { + ArrayLft(&'a PartialTyp), + ArrayRgt(Typ), + DTyp(DTyp, TPrmMap, ::std::slice::Iter<'a, PartialTyp>), + } - Some( Frame::DTyp(dtyp, mut prms, mut to_do) ) => { - prms.push(typ) ; - if let Some(typ_to_do) = to_do.next() { - stack.push( Frame::DTyp(dtyp, prms, to_do) ) ; - curr = typ_to_do ; - continue 'go_down - } else { - typ = typ::dtyp(dtyp, prms) ; - continue 'go_up + let mut curr = self; + let mut stack = vec![]; + + 'go_down: loop { + let mut typ = match curr { + PartialTyp::Array(src, tgt) => { + curr = &**src; + stack.push(Frame::ArrayLft(&**tgt)); + + continue 'go_down; + } + + PartialTyp::DTyp(name, pos, prms) => { + let mut nu_prms = TPrmMap::with_capacity(prms.len()); + let mut prms = prms.iter(); + + let dtyp = if let Ok(dtyp) = get(name) { + dtyp + } else { + return Err((*pos, "unknown datatype".into())); + }; + + if let Some(partial) = prms.next() { + curr = partial; + stack.push(Frame::DTyp(dtyp, nu_prms, prms)); + + continue 'go_down; + } else { + typ::dtyp(dtyp, nu_prms) + } + } + + PartialTyp::Typ(typ) => typ.clone(), + + PartialTyp::Param(idx) => prms[*idx].clone(), + }; + + 'go_up: loop { + match stack.pop() { + None => return Ok(typ), + + Some(Frame::ArrayLft(tgt)) => { + curr = tgt; + stack.push(Frame::ArrayRgt(typ)); + continue 'go_down; + } + + Some(Frame::ArrayRgt(src)) => { + typ = typ::array(src, typ); + continue 'go_up; + } + + Some(Frame::DTyp(dtyp, mut prms, mut to_do)) => { + prms.push(typ); + if let Some(typ_to_do) = to_do.next() { + stack.push(Frame::DTyp(dtyp, prms, to_do)); + curr = typ_to_do; + continue 'go_down; + } else { + typ = typ::dtyp(dtyp, prms); + continue 'go_up; + } + } + } } - }, } - - } - } - } } impl_fmt! { @@ -327,18 +296,14 @@ impl_fmt! { } } - /// A datatype. -pub type DTyp = Arc ; +pub type DTyp = Arc; /// Constructor arguments. -pub type CArgs = Vec<(String, PartialTyp)> ; - - - +pub type CArgs = Vec<(String, PartialTyp)>; /// Type of the datatype factory. -type Factory = RwLock< BTreeMap > ; +type Factory = RwLock>; lazy_static! { /// Datatype factory. static ref factory: Factory = RwLock::new( @@ -363,26 +328,26 @@ lazy_static! { ) ; } - /// True if there is at least one datatype declared. pub fn one_or_more() -> Res { - if let Ok(f) = factory.read() { - Ok( f.len() > 0 ) - } else { - bail!("could not access dtyp factory") - } + if let Ok(f) = factory.read() { + Ok(f.len() > 0) + } else { + bail!("could not access dtyp factory") + } } - /// Checks whether a datatype is reserved. -pub fn check_reserved(name: & str) -> Res<()> { - if reserved_dtyps.contains(name) { - bail!("attempting to redefine built-in datatype {}", conf.bad(name)) - } - Ok(()) +pub fn check_reserved(name: &str) -> Res<()> { + if reserved_dtyps.contains(name) { + bail!( + "attempting to redefine built-in datatype {}", + conf.bad(name) + ) + } + Ok(()) } - /// Creates a datatype. /// /// Will fail if either @@ -393,531 +358,521 @@ pub fn check_reserved(name: & str) -> Res<()> { /// - the datatype has no constructor /// - the datatype has no constructor that don't mention itself pub fn mk(mut dtyp: RDTyp) -> Res { - let name = dtyp.name.clone() ; - - if dtyp.news.is_empty() { - bail!("illegal datatype: no constructor") - } - - // Number of constructor that mention `dtyp`. - let mut rec_count = 0 ; - let mut default = None ; - - for (constructor, args) in & dtyp.news { - let mut recursive = false ; - for (_, ty) in args { - let rec = ty.mentions_dtyp(& dtyp.name) ; - if rec { - recursive = true ; - break - } - } + let name = dtyp.name.clone(); - if recursive { - rec_count += 1 ; - } else { - let default = default.get_or_insert_with( - || (constructor.clone(), args.len()) - ) ; - if default.1 > args.len() { - default.0 = constructor.clone() ; - default.1 = args.len() - } + if dtyp.news.is_empty() { + bail!("illegal datatype: no constructor") } - } - if rec_count == dtyp.news.len() { - bail!("all constructors for datatype `{}` are recursive", dtyp.name) - } + // Number of constructor that mention `dtyp`. + let mut rec_count = 0; + let mut default = None; + + for (constructor, args) in &dtyp.news { + let mut recursive = false; + for (_, ty) in args { + let rec = ty.mentions_dtyp(&dtyp.name); + if rec { + recursive = true; + break; + } + } - if let Some((default, _)) = default { - dtyp.default = default - } else { - bail!("could not find a default constructor for datatype `{}`", dtyp.name) - } + if recursive { + rec_count += 1; + } else { + let default = default.get_or_insert_with(|| (constructor.clone(), args.len())); + if default.1 > args.len() { + default.0 = constructor.clone(); + default.1 = args.len() + } + } + } - let dtyp = Arc::new(dtyp) ; + if rec_count == dtyp.news.len() { + bail!( + "all constructors for datatype `{}` are recursive", + dtyp.name + ) + } - // Update constructor map. - if let Ok(mut map) = constructor_map.write() { - for constructor in dtyp.news.keys() { - let prev = map.insert( constructor.clone(), dtyp.clone() ) ; - if let Some(prev) = prev { + if let Some((default, _)) = default { + dtyp.default = default + } else { bail!( - "constructor `{}` is already used by datatype `{}`", - constructor, prev.name + "could not find a default constructor for datatype `{}`", + dtyp.name ) - } } - } else { - bail!("failed to retrieve datatype constructor map") - } - // Update selector set. - if let Ok(mut set) = selector_set.write() { - for selectors in dtyp.news.values() { - for (selector, _) in selectors { - set.insert( selector.clone() ) ; - } + let dtyp = Arc::new(dtyp); + + // Update constructor map. + if let Ok(mut map) = constructor_map.write() { + for constructor in dtyp.news.keys() { + let prev = map.insert(constructor.clone(), dtyp.clone()); + if let Some(prev) = prev { + bail!( + "constructor `{}` is already used by datatype `{}`", + constructor, + prev.name + ) + } + } + } else { + bail!("failed to retrieve datatype constructor map") } - } - let prev = if let Ok(mut f) = factory.write() { - f.insert( name, dtyp.clone() ) - } else { - bail!("failed to access datatype factory") - } ; + // Update selector set. + if let Ok(mut set) = selector_set.write() { + for selectors in dtyp.news.values() { + for (selector, _) in selectors { + set.insert(selector.clone()); + } + } + } - if let Some(prev) = prev { - bail!("attempting to redeclare datatype `{}`", prev.name) - } + let prev = if let Ok(mut f) = factory.write() { + f.insert(name, dtyp.clone()) + } else { + bail!("failed to access datatype factory") + }; - Ok(dtyp) -} + if let Some(prev) = prev { + bail!("attempting to redeclare datatype `{}`", prev.name) + } + Ok(dtyp) +} /// Retrieves the datatype corresponding to a constructor. -pub fn of_constructor(constructor: & str) -> Option { - constructor_map.read().expect( - "failed to access constructor to datatype map" - ).get(constructor).cloned() +pub fn of_constructor(constructor: &str) -> Option { + constructor_map + .read() + .expect("failed to access constructor to datatype map") + .get(constructor) + .cloned() } /// True if the identifier is known to be a selector. -pub fn is_selector(selector: & str) -> bool { - selector_set.read().expect( - "failed to access selector set" - ).contains(selector) +pub fn is_selector(selector: &str) -> bool { + selector_set + .read() + .expect("failed to access selector set") + .contains(selector) } - /// Lists (some of) the constructors of a datatype as an error. -pub fn constructors_as_error(dtyp: & str) -> Error { - let dtyp = match get(dtyp) { - Ok(res) => res, - Err(e) => return e, - } ; - let mut s = String::new() ; - for (count, (constructor, args)) in dtyp.news.iter().enumerate() { - if count >= 3 { - s.push_str( - & format!("and {} others...", dtyp.news.len() - 3) - ) - } else { - if count > 0 { s.push_str("\n") } - s.push_str(constructor) ; - if ! args.is_empty() { - for (selector, typ) in args { - s.push_str( - & format!(" ({} {})", selector, typ) - ) +pub fn constructors_as_error(dtyp: &str) -> Error { + let dtyp = match get(dtyp) { + Ok(res) => res, + Err(e) => return e, + }; + let mut s = String::new(); + for (count, (constructor, args)) in dtyp.news.iter().enumerate() { + if count >= 3 { + s.push_str(&format!("and {} others...", dtyp.news.len() - 3)) + } else { + if count > 0 { + s.push_str("\n") + } + s.push_str(constructor); + if !args.is_empty() { + for (selector, typ) in args { + s.push_str(&format!(" ({} {})", selector, typ)) + } + } } - } } - } - s.into() + s.into() } - /// Retrieves a datatype from its name. /// /// Will fail if /// /// - the datatype does not exist, or /// - can't access the datatype map. -pub fn get(dtyp: & str) -> Res { - let maybe_res = if let Ok(f) = factory.read() { - f.get(dtyp).cloned() - } else { - bail!("failed to access datatype factory") - } ; +pub fn get(dtyp: &str) -> Res { + let maybe_res = if let Ok(f) = factory.read() { + f.get(dtyp).cloned() + } else { + bail!("failed to access datatype factory") + }; - if let Some(res) = maybe_res { - Ok(res) - } else if dtyp == "List" { - mk( RDTyp::list() ) - } else { - bail!("unknown datatype `{}`", dtyp) - } + if let Some(res) = maybe_res { + Ok(res) + } else if dtyp == "List" { + mk(RDTyp::list()) + } else { + bail!("unknown datatype `{}`", dtyp) + } } - /// All the datatypes. -pub fn get_all() -> impl ::std::ops::Deref< Target = BTreeMap > { - factory.read().expect( - "failed to access datatype factory" - ) +pub fn get_all() -> impl ::std::ops::Deref> { + factory.read().expect("failed to access datatype factory") } - - /// Writes the map from constructors to datatypes. -pub fn write_constructor_map( - w: & mut W, pref: & str -) -> ::std::io::Result<()> { - for (constructor, dtyp) in constructor_map.read().expect( - "unable to retrieve dtyp constructor map" - ).iter() { - writeln!(w, "{}{:>10} -> {:>10}", pref, constructor, dtyp.name) ? - } - Ok(()) +pub fn write_constructor_map(w: &mut W, pref: &str) -> ::std::io::Result<()> { + for (constructor, dtyp) in constructor_map + .read() + .expect("unable to retrieve dtyp constructor map") + .iter() + { + writeln!(w, "{}{:>10} -> {:>10}", pref, constructor, dtyp.name)? + } + Ok(()) } - - /// Writes all the datatypes. -pub fn write_all(w: & mut W, pref: & str) -> ::std::io::Result<()> { - let decs = get_all() ; +pub fn write_all(w: &mut W, pref: &str) -> ::std::io::Result<()> { + let decs = get_all(); - if decs.is_empty() { return Ok(()) } - - let mut known = HashSet::new() ; - let dtyp_pref = & format!("{} ", pref) ; - - for (name, dtyp) in decs.iter() { - if reserved_dtyps.contains( name.as_str() ) { - continue + if decs.is_empty() { + return Ok(()); } - let is_new = known.insert(name) ; - if ! is_new { - continue - } - - let mut all = vec![ dtyp.clone() ] ; - for dtyp in & dtyp.deps { - let is_new = known.insert(dtyp) ; - assert! { is_new } + let mut known = HashSet::new(); + let dtyp_pref = &format!("{} ", pref); - if let Ok(dtyp) = get(dtyp) { - all.push(dtyp) - } else { - panic!("inconsistent datatype factory/maps state") - } - } + for (name, dtyp) in decs.iter() { + if reserved_dtyps.contains(name.as_str()) { + continue; + } + let is_new = known.insert(name); + if !is_new { + continue; + } - writeln!(w, "{}({} (", pref, keywords::cmd::dec_dtyps) ? ; - write!(w, "{} ", pref) ? ; + let mut all = vec![dtyp.clone()]; - for dtyp in all.clone() { - write!(w, " ({} {})", dtyp.name, dtyp.prms.len()) ? - } + for dtyp in &dtyp.deps { + let is_new = known.insert(dtyp); + assert! { is_new } - writeln!(w, "\n{}) (", pref) ? ; + if let Ok(dtyp) = get(dtyp) { + all.push(dtyp) + } else { + panic!("inconsistent datatype factory/maps state") + } + } - for dtyp in all { - dtyp.write_dec(w, dtyp_pref) ? - } + writeln!(w, "{}({} (", pref, keywords::cmd::dec_dtyps)?; + write!(w, "{} ", pref)?; - for dtyp in & dtyp.deps { - let is_new = known.insert(dtyp) ; - assert!(is_new) - } + for dtyp in all.clone() { + write!(w, " ({} {})", dtyp.name, dtyp.prms.len())? + } - writeln!(w, "{}) )", pref) ? - } + writeln!(w, "\n{}) (", pref)?; - writeln!(w) ? ; + for dtyp in all { + dtyp.write_dec(w, dtyp_pref)? + } - Ok(()) -} + for dtyp in &dtyp.deps { + let is_new = known.insert(dtyp); + assert!(is_new) + } + writeln!(w, "{}) )", pref)? + } + writeln!(w)?; + Ok(()) +} /// Types a constructor application. -pub fn type_constructor( - constructor: & str, args: & [ Term ] -) -> Res< Option > { - let dtyp = if let Some(dtyp) = of_constructor(constructor) { - dtyp - } else { - return Ok(None) - } ; - - let params = { - let dtyp_constructor = dtyp.news.get(constructor).expect( - "inconsistent datatype constructor map" - ) ; - - if args.len() != dtyp_constructor.len() { - bail!( - "constructor `{}` of type `{}` expects {} arguments, got {}", - constructor, dtyp.name, dtyp_constructor.len(), args.len() - ) - } +pub fn type_constructor(constructor: &str, args: &[Term]) -> Res> { + let dtyp = if let Some(dtyp) = of_constructor(constructor) { + dtyp + } else { + return Ok(None); + }; + + let params = { + let dtyp_constructor = dtyp + .news + .get(constructor) + .expect("inconsistent datatype constructor map"); + + if args.len() != dtyp_constructor.len() { + bail!( + "constructor `{}` of type `{}` expects {} arguments, got {}", + constructor, + dtyp.name, + dtyp_constructor.len(), + args.len() + ) + } - let mut params = TPrmMap::with_capacity( dtyp.prms.len() ) ; - for _ in 0 .. dtyp.prms.len() { - params.push( typ::unk() ) - } + let mut params = TPrmMap::with_capacity(dtyp.prms.len()); + for _ in 0..dtyp.prms.len() { + params.push(typ::unk()) + } - for ( arg, (_, typ) ) in args.iter().zip( - dtyp_constructor.iter() - ) { - typ.unify( & arg.typ(), & mut params ) ? - } + for (arg, (_, typ)) in args.iter().zip(dtyp_constructor.iter()) { + typ.unify(&arg.typ(), &mut params)? + } - params - } ; + params + }; - Ok( Some( typ::dtyp(dtyp, params) ) ) + Ok(Some(typ::dtyp(dtyp, params))) } - - /// Types a datatype selector application. pub fn type_selector( - selector: & str, slc_pos: ::parse::Pos, term: & Term + selector: &str, + slc_pos: ::parse::Pos, + term: &Term, ) -> Result { - if let Some((dtyp, prms)) = term.typ().dtyp_inspect() { - - for args in dtyp.news.values() { - for (slc, partial_typ) in args { - if slc == selector { - - return partial_typ.to_type(prms) - + if let Some((dtyp, prms)) = term.typ().dtyp_inspect() { + for args in dtyp.news.values() { + for (slc, partial_typ) in args { + if slc == selector { + return partial_typ.to_type(prms); + } + } } - } } - } - - Err( - ( - slc_pos, format!( - "cannot apply selector `{}` to term of type {}", - conf.bad(selector), term.typ() - ) - ) - ) + Err(( + slc_pos, + format!( + "cannot apply selector `{}` to term of type {}", + conf.bad(selector), + term.typ() + ), + )) } - - /// Types a datatype tester application. pub fn type_tester( - tester: & str, tst_pos: ::parse::Pos, term: & Term + tester: &str, + tst_pos: ::parse::Pos, + term: &Term, ) -> Result<(), (::parse::Pos, String)> { - if let Some((dtyp, _)) = term.typ().dtyp_inspect() { - if dtyp.news.contains_key(tester) { - return Ok(()) + if let Some((dtyp, _)) = term.typ().dtyp_inspect() { + if dtyp.news.contains_key(tester) { + return Ok(()); + } } - } - Err( - ( - tst_pos, format!( - "cannot apply tester `{}` to term of type {}, no such constructor", - conf.bad(tester), term.typ() - ) - ) - ) + Err(( + tst_pos, + format!( + "cannot apply tester `{}` to term of type {}, no such constructor", + conf.bad(tester), + term.typ() + ), + )) } - - - - - /// The actual datatype type. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RDTyp { - /// Name of the datatype. - pub name: String, - /// Other datatypes attached to this one. - pub deps: Vec, - /// Type parameters. - pub prms: TPrmMap, - /// Constructors. - pub news: BTreeMap, - /// Default type constructor. - pub default: String, + /// Name of the datatype. + pub name: String, + /// Other datatypes attached to this one. + pub deps: Vec, + /// Type parameters. + pub prms: TPrmMap, + /// Constructors. + pub news: BTreeMap, + /// Default type constructor. + pub default: String, } impl RDTyp { - /// Constructor. - pub fn new>(name: S) -> Self { - let name = name.into() ; - RDTyp { - name, deps: vec![], prms: TPrmMap::new(), news: BTreeMap::new(), - default: "".into() + /// Constructor. + pub fn new>(name: S) -> Self { + let name = name.into(); + RDTyp { + name, + deps: vec![], + prms: TPrmMap::new(), + news: BTreeMap::new(), + default: "".into(), + } } - } - /// Generates the definition for the `List` datatype. - fn list() -> Self { - use parse::Pos ; + /// Generates the definition for the `List` datatype. + fn list() -> Self { + use parse::Pos; - let mut news = BTreeMap::new() ; + let mut news = BTreeMap::new(); - let mut prms = TPrmMap::new() ; - let param = prms.next_index() ; - prms.push( "T".into() ) ; + let mut prms = TPrmMap::new(); + let param = prms.next_index(); + prms.push("T".into()); - let prev = news.insert( - "nil".to_string(), vec![] - ) ; - debug_assert! { prev.is_none() } + let prev = news.insert("nil".to_string(), vec![]); + debug_assert! { prev.is_none() } - let head = ( "head".to_string(), PartialTyp::Param(param) ) ; - let tail = ( - "tail".to_string(), PartialTyp::DTyp( - "List".into(), Pos::default(), vec![ PartialTyp::Param(param) ].into() - ) - ) ; + let head = ("head".to_string(), PartialTyp::Param(param)); + let tail = ( + "tail".to_string(), + PartialTyp::DTyp( + "List".into(), + Pos::default(), + vec![PartialTyp::Param(param)].into(), + ), + ); - let prev = news.insert( - "insert".to_string(), vec![ head, tail ] - ) ; - debug_assert! { prev.is_none() } + let prev = news.insert("insert".to_string(), vec![head, tail]); + debug_assert! { prev.is_none() } - let default = "nil".to_string() ; + let default = "nil".to_string(); - RDTyp { - name: "List".into(), - deps: vec![], - prms, news, default + RDTyp { + name: "List".into(), + deps: vec![], + prms, + news, + default, + } } - } - - /// Pushes a type parameter. - pub fn push_typ_param>(& mut self, name: S) -> TPrmIdx { - let idx = self.prms.next_index() ; - self.prms.push( name.into() ) ; - idx - } - /// Writes the declaration for a datatype. - pub fn write_dec( - & self, w: & mut W, pref: & str - ) -> ::std::io::Result<()> { - let close_par = if self.prms.is_empty() { - writeln!(w, "{}( ; {}", pref, self.name) ? ; - false - } else { - write!(w, "{}(par (", pref) ? ; - for prm in & self.prms { - write!(w, " {}", prm) ? - } - writeln!(w, " ) (") ? ; - true - } ; - - for (name, args) in & self.news { - write!(w, "{} ({}", pref, name) ? ; - for (name, typ) in args { - write!(w, " ({} ", name) ? ; - typ.write(w, & self.prms) ? ; - write!(w, ")") ? - } - writeln!(w, ")") ? + /// Pushes a type parameter. + pub fn push_typ_param>(&mut self, name: S) -> TPrmIdx { + let idx = self.prms.next_index(); + self.prms.push(name.into()); + idx } - writeln!( - w, "{}){}", pref, if close_par { " )" } else { "" } - ) - } + /// Writes the declaration for a datatype. + pub fn write_dec(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { + let close_par = if self.prms.is_empty() { + writeln!(w, "{}( ; {}", pref, self.name)?; + false + } else { + write!(w, "{}(par (", pref)?; + for prm in &self.prms { + write!(w, " {}", prm)? + } + writeln!(w, " ) (")?; + true + }; + + for (name, args) in &self.news { + write!(w, "{} ({}", pref, name)?; + for (name, typ) in args { + write!(w, " ({} ", name)?; + typ.write(w, &self.prms)?; + write!(w, ")")? + } + writeln!(w, ")")? + } - /// Writes a single datatype. - pub fn write( - & self, w: & mut W, pref: & str - ) -> ::std::io::Result<()> { - writeln!(w, "{}({}", pref, self.name) ? ; - for (name, args) in & self.news { - if args.is_empty() { - write!(w, "{} {}", pref, name) ? - } else { - write!(w, "{} ({}", pref, name) ? ; - for (name, typ) in args { - write!(w, " ({} ", name) ? ; - typ.write(w, & self.prms) ? ; - write!(w, ")") ? + writeln!(w, "{}){}", pref, if close_par { " )" } else { "" }) + } + + /// Writes a single datatype. + pub fn write(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { + writeln!(w, "{}({}", pref, self.name)?; + for (name, args) in &self.news { + if args.is_empty() { + write!(w, "{} {}", pref, name)? + } else { + write!(w, "{} ({}", pref, name)?; + for (name, typ) in args { + write!(w, " ({} ", name)?; + typ.write(w, &self.prms)?; + write!(w, ")")? + } + write!(w, ")")? + } + writeln!(w)? } - write!(w, ")") ? - } - writeln!(w) ? + writeln!(w, "{})", pref)?; + Ok(()) } - writeln!(w, "{})", pref) ? ; - Ok(()) - } - /// Adds a datatype dependency. - pub fn add_dep(& mut self, dep: S) - where S: Into { - self.deps.push( dep.into() ) - } + /// Adds a datatype dependency. + pub fn add_dep(&mut self, dep: S) + where + S: Into, + { + self.deps.push(dep.into()) + } - /// Checks a datatype is legal. - pub fn check(& self) -> Result<(), (::parse::Pos, String)> { - for (constructor, cargs) in & self.news { - for (selector, ptyp) in cargs { - if let Err((pos, err)) = ptyp.check() { - return Err(( - pos, format!( - "{} on selector `{}` of constructor `{}`", - err, selector, constructor - ) - )) + /// Checks a datatype is legal. + pub fn check(&self) -> Result<(), (::parse::Pos, String)> { + for (constructor, cargs) in &self.news { + for (selector, ptyp) in cargs { + if let Err((pos, err)) = ptyp.check() { + return Err(( + pos, + format!( + "{} on selector `{}` of constructor `{}`", + err, selector, constructor + ), + )); + } + } } - } + Ok(()) } - Ok(()) - } - /// Adds a constructor. - pub fn add_constructor(& mut self, name: S, args: CArgs) -> Res<()> - where S: Into { - let name = name.into() ; - for (other_name, other_args) in & self.news { - if name == * other_name { - bail!("attempting to redeclare constructor `{}`", name) - } - for (arg, _) in & args { - for (other_arg, _) in other_args { - if arg == other_arg { - bail!("attempting to redeclare selector `{}`", arg) - } + /// Adds a constructor. + pub fn add_constructor(&mut self, name: S, args: CArgs) -> Res<()> + where + S: Into, + { + let name = name.into(); + for (other_name, other_args) in &self.news { + if name == *other_name { + bail!("attempting to redeclare constructor `{}`", name) + } + for (arg, _) in &args { + for (other_arg, _) in other_args { + if arg == other_arg { + bail!("attempting to redeclare selector `{}`", arg) + } + } + } } - } - } - let _prev = self.news.insert(name, args) ; - debug_assert_eq! { _prev, None } + let _prev = self.news.insert(name, args); + debug_assert_eq! { _prev, None } - Ok(()) - } + Ok(()) + } - /// Returns a recursive constructor. - /// - /// Only returns something if - /// - /// - there are only two constructors - /// - one of them is recursive - pub fn rec_constructor(& self) -> Option<& str> { - if self.news.len() == 2 { - for (new, args) in & self.news { - for (_, ptyp) in args { - if ptyp.mentions_dtyp(& self.name) { - return Some(new) - } + /// Returns a recursive constructor. + /// + /// Only returns something if + /// + /// - there are only two constructors + /// - one of them is recursive + pub fn rec_constructor(&self) -> Option<&str> { + if self.news.len() == 2 { + for (new, args) in &self.news { + for (_, ptyp) in args { + if ptyp.mentions_dtyp(&self.name) { + return Some(new); + } + } + } } - } + None } - None - } - /// Returns the selectors of a constructor. - pub fn selectors_of(& self, constructor: & str) -> Res<& CArgs> { - if let Some(selectors) = self.news.get(constructor) { - Ok(selectors) - } else { - bail!( - "unknown constructor {} for dtyp {}, no selectors", - conf.bad(constructor), conf.emph(& self.name) - ) + /// Returns the selectors of a constructor. + pub fn selectors_of(&self, constructor: &str) -> Res<&CArgs> { + if let Some(selectors) = self.news.get(constructor) { + Ok(selectors) + } else { + bail!( + "unknown constructor {} for dtyp {}, no selectors", + conf.bad(constructor), + conf.emph(&self.name) + ) + } } - } } impl_fmt! { RDTyp(self, fmt) { write!(fmt, "{}", self.name) } diff --git a/src/errors.rs b/src/errors.rs index 1966a37c..318c94c3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -12,57 +12,53 @@ //! [exit]: learners/enum.LError.html#variant.Exit //! (Exit variant of the LError enum) -use common::* ; - - - - +use common::*; /// A term type-checking error. pub enum TypError { - /// No type info, just an error message. - Msg(String), - /// Type info: - /// - /// - the type expected (if known), - /// - the type obtained, - /// - the index of the argument that caused it. - Typ { - expected: Option, - obtained: Typ, - index: usize, - } + /// No type info, just an error message. + Msg(String), + /// Type info: + /// + /// - the type expected (if known), + /// - the type obtained, + /// - the index of the argument that caused it. + Typ { + expected: Option, + obtained: Typ, + index: usize, + }, } impl TypError { - /// Message constructor. - pub fn msg>(s: S) -> Self { - TypError::Msg( s.into() ) - } + /// Message constructor. + pub fn msg>(s: S) -> Self { + TypError::Msg(s.into()) + } - /// Type info constructor. - pub fn typ( - expected: Option, obtained: Typ, index: usize - ) -> Self { - TypError::Typ { expected, obtained, index } - } + /// Type info constructor. + pub fn typ(expected: Option, obtained: Typ, index: usize) -> Self { + TypError::Typ { + expected, + obtained, + index, + } + } } - - /// Parse error data. #[derive(Debug)] pub struct ParseErrorData { - /// Error message. - pub msg: String, - /// Portion of the line *before* the error token. - pub pref: String, - /// Token that caused the error. - pub token: String, - /// Portion of the line *after* the error token. - pub suff: String, - /// Line of the error, relative to the portion of the input accessible by - /// whoever constructed the error. - pub line: Option, + /// Error message. + pub msg: String, + /// Portion of the line *before* the error token. + pub pref: String, + /// Token that caused the error. + pub token: String, + /// Portion of the line *after* the error token. + pub suff: String, + /// Line of the error, relative to the portion of the input accessible by + /// whoever constructed the error. + pub line: Option, } impl_fmt!{ ParseErrorData(self, fmt) { @@ -151,86 +147,80 @@ error_chain!{ } impl Error { - /// True if the kind of the error is [`ErrorKind::Unsat`][unsat]. - /// - /// [unsat]: enum.ErrorKind.html#variant.Unsat - /// (ErrorKind's Unsat variant) - pub fn is_unsat(& self) -> bool { - match * self.kind() { - ErrorKind::Unsat => true, - ErrorKind::UnsatFrom(_) => true, - _ => false, + /// True if the kind of the error is [`ErrorKind::Unsat`][unsat]. + /// + /// [unsat]: enum.ErrorKind.html#variant.Unsat + /// (ErrorKind's Unsat variant) + pub fn is_unsat(&self) -> bool { + match *self.kind() { + ErrorKind::Unsat => true, + ErrorKind::UnsatFrom(_) => true, + _ => false, + } } - } - /// True if the kind of the error is [`ErrorKind::SmtError::Unknown`]. - pub fn is_smt_unknown(& self) -> bool { - match * self.kind() { - ErrorKind::SmtError( - ::rsmt2::errors::ErrorKind::Unknown - ) => true, - _ => false, + /// True if the kind of the error is [`ErrorKind::SmtError::Unknown`]. + pub fn is_smt_unknown(&self) -> bool { + match *self.kind() { + ErrorKind::SmtError(::rsmt2::errors::ErrorKind::Unknown) => true, + _ => false, + } } - } - /// True if the kind of the error is [`ErrorKind::Unknown`][unknown]. - /// - /// [unknown]: enum.ErrorKind.html#variant.Unknown - /// (ErrorKind's Unknown variant) - pub fn is_unknown(& self) -> bool { - match * self.kind() { - ErrorKind::Unknown => true, - _ => self.is_smt_unknown(), + /// True if the kind of the error is [`ErrorKind::Unknown`][unknown]. + /// + /// [unknown]: enum.ErrorKind.html#variant.Unknown + /// (ErrorKind's Unknown variant) + pub fn is_unknown(&self) -> bool { + match *self.kind() { + ErrorKind::Unknown => true, + _ => self.is_smt_unknown(), + } } - } - /// Returns the clause explaining an unsat result if any. - pub fn unsat_cause(& self) -> Option { - match self.kind() { - ErrorKind::UnsatFrom(clause) => Some(* clause), - _ => None, + /// Returns the clause explaining an unsat result if any. + pub fn unsat_cause(&self) -> Option { + match self.kind() { + ErrorKind::UnsatFrom(clause) => Some(*clause), + _ => None, + } } - } - - /// True if the kind of the error is a timeout. - /// - /// [timeout]: enum.ErrorKind.html#variant.Timeout - /// (ErrorKind's Timeout variant) - pub fn is_timeout(& self) -> bool { - match self.kind() { - ErrorKind::Timeout => true, - ErrorKind::SmtError(smt_err) => smt_err.is_timeout(), - _ => false, + /// True if the kind of the error is a timeout. + /// + /// [timeout]: enum.ErrorKind.html#variant.Timeout + /// (ErrorKind's Timeout variant) + pub fn is_timeout(&self) -> bool { + match self.kind() { + ErrorKind::Timeout => true, + ErrorKind::SmtError(smt_err) => smt_err.is_timeout(), + _ => false, + } } - } - /// True if the kind of the error is [`ErrorKind::Exit`][exit]. - /// - /// [exit]: enum.ErrorKind.html#variant.Exit - /// (ErrorKind's Exit variant) - pub fn is_exit(& self) -> bool { - match * self.kind() { - ErrorKind::Exit => true, - _ => false, + /// True if the kind of the error is [`ErrorKind::Exit`][exit]. + /// + /// [exit]: enum.ErrorKind.html#variant.Exit + /// (ErrorKind's Exit variant) + pub fn is_exit(&self) -> bool { + match *self.kind() { + ErrorKind::Exit => true, + _ => false, + } } - } } - /// Prints an error. -pub fn print_err(errs: & Error) { - println!( - "({} \"", conf.bad("error") - ) ; - let mut list = vec![] ; - for err in errs.iter() { - list.push( err ) - } - for err in list.into_iter().rev() { - for line in format!("{}", err).lines() { - println!(" {}", line) +pub fn print_err(errs: &Error) { + println!("({} \"", conf.bad("error")); + let mut list = vec![]; + for err in errs.iter() { + list.push(err) } - } - println!("\")") + for err in list.into_iter().rev() { + for line in format!("{}", err).lines() { + println!(" {}", line) + } + } + println!("\")") } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index c6227d8b..a5359324 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -4,14 +4,12 @@ //! //! Move this in the instance to avoid the unsafe code to borrow definitions. -use std::sync::{ - RwLockReadGuard, RwLockWriteGuard -} ; +use std::sync::{RwLockReadGuard, RwLockWriteGuard}; -use common::* ; +use common::*; /// A function. -pub type Fun = Arc ; +pub type Fun = Arc; /// Type of the function factory. /// @@ -21,7 +19,7 @@ pub type Fun = Arc ; /// /// To avoid problems, **always** use the `factory` macro to access the /// factory. -type Factory = RwLock< (BTreeMap, usize) > ; +type Factory = RwLock<(BTreeMap, usize)>; lazy_static! { /// Function factory. static ref factory: Factory = RwLock::new( @@ -29,7 +27,6 @@ lazy_static! { ) ; } - lazy_static! { /// Stores function declarations before obtaining the actual definition. static ref fun_decs: RwLock< BTreeMap > = RwLock::new( @@ -39,279 +36,265 @@ lazy_static! { /// Registers a function declaration. pub fn register_dec(fun: RFun) -> Res<()> { - if let Ok(mut decs) = fun_decs.write() { - let prev = decs.insert( fun.name.clone(), fun ) ; - if let Some(prev) = prev { - bail!("the function {} is declared twice", conf.bad(& prev.name)) + if let Ok(mut decs) = fun_decs.write() { + let prev = decs.insert(fun.name.clone(), fun); + if let Some(prev) = prev { + bail!("the function {} is declared twice", conf.bad(&prev.name)) + } + } else { + bail!("unable to access function declarations") } - } else { - bail!("unable to access function declarations") - } - Ok(()) + Ok(()) } /// Retrieves a function declaration. -pub fn retrieve_dec(fun: & str) -> Res { - if let Ok(mut decs) = fun_decs.write() { - if let Some(dec) = decs.remove(fun) { - Ok(dec) +pub fn retrieve_dec(fun: &str) -> Res { + if let Ok(mut decs) = fun_decs.write() { + if let Some(dec) = decs.remove(fun) { + Ok(dec) + } else { + bail!( + "unable to retrieve function declaration for {}", + conf.bad(fun) + ) + } } else { - bail!("unable to retrieve function declaration for {}", conf.bad(fun)) + bail!("unable to access function declarations") } - } else { - bail!("unable to access function declarations") - } } - - /// Accesses the declaration of a function. -pub fn dec_do(fun: & str, mut f: F) -> Res -where F: for<'a> FnMut(& 'a RFun) -> Res { - if let Ok(defs) = factory.read() { - if let Some(def) = defs.0.get(fun) { - return f(def) +pub fn dec_do(fun: &str, mut f: F) -> Res +where + F: for<'a> FnMut(&'a RFun) -> Res, +{ + if let Ok(defs) = factory.read() { + if let Some(def) = defs.0.get(fun) { + return f(def); + } + } else { + bail!("unable to retrieve function factory") } - } else { - bail!("unable to retrieve function factory") - } - if let Ok(defs) = fun_decs.read() { - if let Some(def) = defs.get(fun) { - f(def) + if let Ok(defs) = fun_decs.read() { + if let Some(def) = defs.get(fun) { + f(def) + } else { + bail!( + "trying to retrieve declaration for unknown function {}", + conf.bad(fun) + ) + } } else { - bail!( - "trying to retrieve declaration for unknown function {}", - conf.bad(fun) - ) + bail!("unable to retrieve function declarations") } - } else { - bail!("unable to retrieve function declarations") - } } - - /// Read version of the factory. -fn read_factory<'a>() -> RwLockReadGuard< - 'a, (BTreeMap, usize) -> { - if let Ok(res) = factory.read() { - res - } else { - panic!("failed to access function factory (read)") - } +fn read_factory<'a>() -> RwLockReadGuard<'a, (BTreeMap, usize)> { + if let Ok(res) = factory.read() { + res + } else { + panic!("failed to access function factory (read)") + } } /// Write version of the factory. -fn write_factory<'a>() -> RwLockWriteGuard< - 'a, (BTreeMap, usize) -> { - loop { - if let Ok(res) = factory.write() { - if res.1 != 0 { continue } - return res - } else { - panic!("failed to access function factory (write)") +fn write_factory<'a>() -> RwLockWriteGuard<'a, (BTreeMap, usize)> { + loop { + if let Ok(res) = factory.write() { + if res.1 != 0 { + continue; + } + return res; + } else { + panic!("failed to access function factory (write)") + } } - } } macro_rules! factory { - (read) => (& read_factory().0) ; - (write) => (& mut write_factory().0) ; + (read) => { + &read_factory().0 + }; + (write) => { + &mut write_factory().0 + }; } - /// Iterates over all function definitions. pub fn iter(mut f: F) -> Res<()> -where F: FnMut(& Fun) -> Res<()> { - let defs = read_factory() ; - for def in defs.0.values() { f(def) ? } - Ok(()) +where + F: FnMut(&Fun) -> Res<()>, +{ + let defs = read_factory(); + for def in defs.0.values() { + f(def)? + } + Ok(()) } - /// Creates a function definition. pub fn mk(fun: RFun) -> Res { - let fun = Arc::new( fun ) ; - let f = factory!(write) ; - let prev = f.insert( fun.name.clone(), fun.clone() ) ; + let fun = Arc::new(fun); + let f = factory!(write); + let prev = f.insert(fun.name.clone(), fun.clone()); - if let Some(prev) = prev { - bail!("attempting to redefine function `{}`", prev.name) - } + if let Some(prev) = prev { + bail!("attempting to redefine function `{}`", prev.name) + } - Ok(fun) + Ok(fun) } - /// Groups all functions by dependencies. -pub fn ordered() -> Res< Vec< Vec > > { - let mut all: Vec<_> = read_factory().0.values().cloned().collect() ; - - let mut groups = vec![] ; - - while let Some(fun) = all.pop() { - let mut group = vec![ fun ] ; - if ! group[0].deps.is_empty() { - all.retain( - |fun| if group[0].deps.contains(& fun.name) { - group.push( fun.clone() ) ; - false - } else { - true +pub fn ordered() -> Res>> { + let mut all: Vec<_> = read_factory().0.values().cloned().collect(); + + let mut groups = vec![]; + + while let Some(fun) = all.pop() { + let mut group = vec![fun]; + if !group[0].deps.is_empty() { + all.retain(|fun| { + if group[0].deps.contains(&fun.name) { + group.push(fun.clone()); + false + } else { + true + } + }); } - ) ; + groups.push(group) } - groups.push(group) - } - Ok(groups) + Ok(groups) } - - /// Defines a function, and all functions related to it. -pub fn write(w: & mut W, fun: & RFun, pref: & str) -> Res<()> -where W: Write { - let f = factory!(read) ; - let mut all = vec![] ; - - all.reserve( fun.deps.len() + 1 ) ; - all.push(fun) ; - for dep in & fun.deps { - if let Some(dep) = f.get(dep) { - all.push(dep) - } else { - bail!( - "function `{}` depends on unknown function `{}`", - conf.emph(& fun.name), conf.bad(& dep) - ) +pub fn write(w: &mut W, fun: &RFun, pref: &str) -> Res<()> +where + W: Write, +{ + let f = factory!(read); + let mut all = vec![]; + + all.reserve(fun.deps.len() + 1); + all.push(fun); + for dep in &fun.deps { + if let Some(dep) = f.get(dep) { + all.push(dep) + } else { + bail!( + "function `{}` depends on unknown function `{}`", + conf.emph(&fun.name), + conf.bad(&dep) + ) + } } - } - writeln!(w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec) ? ; + writeln!(w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec)?; - // Write all signatures. - for fun in & all { - write!(w, "{} (", pref) ? ; - write!(w, "{} (", fun.name) ? ; - for info in & fun.sig { - write!(w, " ({} {})", info.idx.default_str(), info.typ) ? + // Write all signatures. + for fun in &all { + write!(w, "{} (", pref)?; + write!(w, "{} (", fun.name)?; + for info in &fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ)? + } + writeln!(w, " ) {})", fun.typ)? } - writeln!(w, " ) {})", fun.typ) ? - } - writeln!(w, "{}) (", pref) ? ; + writeln!(w, "{}) (", pref)?; - // Write all definitions. - for fun in all.drain( 0 .. ) { - write!(w, "{} ", pref) ? ; - fun.def.write( - w, |w, var| var.default_write(w) - ) ? ; - writeln!(w) ? - } + // Write all definitions. + for fun in all.drain(0..) { + write!(w, "{} ", pref)?; + fun.def.write(w, |w, var| var.default_write(w))?; + writeln!(w)? + } - writeln!(w, "{}) )", pref) ? ; + writeln!(w, "{}) )", pref)?; - Ok(()) + Ok(()) } - /// Defines all the functions. -pub fn write_all( - w: & mut W, pref: & str, invariants: bool -) -> Res<()> { - for mut group in ordered() ? { - - if group.len() == 1 { - let fun = & group[0] ; - - let def_key = if fun.recursive { - consts::keywords::cmd::def_fun_rec - } else { - consts::keywords::cmd::def_fun - } ; - - writeln!( - w, "{}({} {}", pref, def_key, fun.name - ) ? ; - write!(w, "{} (", pref) ? ; - for info in & fun.sig { - write!(w, " ({} {})", info.idx.default_str(), info.typ) ? - } - writeln!(w, " ) {}", fun.typ) ? ; - - write!(w, "{} ", pref) ? ; - - fun.def.write( - w, |w, var| var.default_write(w) - ) ? ; - writeln!(w, "\n{})", pref) ? - - } else if group.len() > 1 { - writeln!( - w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec - ) ? ; - - // Write all signatures. - for fun in & group { - write!(w, "{} (", pref) ? ; - write!(w, "{} (", fun.name) ? ; - for info in & fun.sig { - write!(w, " ({} {})", info.idx.default_str(), info.typ) ? +pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { + for mut group in ordered()? { + if group.len() == 1 { + let fun = &group[0]; + + let def_key = if fun.recursive { + consts::keywords::cmd::def_fun_rec + } else { + consts::keywords::cmd::def_fun + }; + + writeln!(w, "{}({} {}", pref, def_key, fun.name)?; + write!(w, "{} (", pref)?; + for info in &fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ)? + } + writeln!(w, " ) {}", fun.typ)?; + + write!(w, "{} ", pref)?; + + fun.def.write(w, |w, var| var.default_write(w))?; + writeln!(w, "\n{})", pref)? + } else if group.len() > 1 { + writeln!(w, "{}({} (", pref, consts::keywords::cmd::def_funs_rec)?; + + // Write all signatures. + for fun in &group { + write!(w, "{} (", pref)?; + write!(w, "{} (", fun.name)?; + for info in &fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ)? + } + writeln!(w, " ) {})", fun.typ)? + } + + writeln!(w, "{}) (", pref)?; + + // Write all definitions. + for fun in &group { + write!(w, "{} ", pref)?; + fun.def.write(w, |w, var| var.default_write(w))?; + writeln!(w)? + } + + writeln!(w, "{}) )", pref)? } - writeln!(w, " ) {})", fun.typ) ? - } - - writeln!(w, "{}) (", pref) ? ; - - // Write all definitions. - for fun in & group { - write!(w, "{} ", pref) ? ; - fun.def.write( - w, |w, var| var.default_write(w) - ) ? ; - writeln!(w) ? - } - - writeln!(w, "{}) )", pref) ? - } - writeln!(w) ? ; - - if invariants { - let mut one_or_more = false ; - for fun in group { - for inv in & fun.invariants { - one_or_more = true ; - writeln!(w, "{}(assert", pref) ? ; - writeln!(w, "{} (forall", pref) ? ; - write!(w, "{} (", pref) ? ; - for info in & fun.sig { - write!(w, " ({} {})", info.idx.default_str(), info.typ) ? - } - writeln!(w, " )") ? ; - write!(w, "{} ", pref) ? ; - inv.write( - w, |w, var| var.default_write(w) - ) ? ; - writeln!(w, "\n{}) )", pref) ? + writeln!(w)?; + + if invariants { + let mut one_or_more = false; + for fun in group { + for inv in &fun.invariants { + one_or_more = true; + writeln!(w, "{}(assert", pref)?; + writeln!(w, "{} (forall", pref)?; + write!(w, "{} (", pref)?; + for info in &fun.sig { + write!(w, " ({} {})", info.idx.default_str(), info.typ)? + } + writeln!(w, " )")?; + write!(w, "{} ", pref)?; + inv.write(w, |w, var| var.default_write(w))?; + writeln!(w, "\n{}) )", pref)? + } + } + + if one_or_more { + writeln!(w)? + } } - } - - if one_or_more { - writeln!(w) ? - } } - } - Ok(()) + Ok(()) } - - /// Retrieves the definition of a function as a reference. /// /// This actually uses unsafe code, this kind of borrow should not be possible. @@ -326,268 +309,260 @@ pub fn write_all( /// /// link: fun/fn.decrease_ref_count.html /// (decrease_ref_count function) -pub fn get_as_ref<'a>(name: & 'a str) -> Option<& 'a Fun> { - let mut pair = if let Ok(mut f) = factory.write() { - f - } else { - panic!("failed to access function factory (write)") - } ; - pair.1 += 1 ; - unsafe { - ::std::mem::transmute::, Option<& 'a Fun>>( - pair.0.get(name) - ) - } +pub fn get_as_ref<'a>(name: &'a str) -> Option<&'a Fun> { + let mut pair = if let Ok(mut f) = factory.write() { + f + } else { + panic!("failed to access function factory (write)") + }; + pair.1 += 1; + unsafe { ::std::mem::transmute::, Option<&'a Fun>>(pair.0.get(name)) } } pub fn decrease_ref_count(count: usize) { - if count == 0 { return () } - if let Ok(mut f) = factory.write() { - if count <= f.1 { - f.1 -= count + if count == 0 { + return (); + } + if let Ok(mut f) = factory.write() { + if count <= f.1 { + f.1 -= count + } else { + panic!("trying to decrease ref count for function factory by too much") + } } else { - panic!("trying to decrease ref count for function factory by too much") + panic!("failed to access function factory (write)") } - } else { - panic!("failed to access function factory (write)") - } } - - /// Retrieves the definition of a function. -pub fn get(name: & str) -> Option { - let f = factory!(read) ; - f.get(name).cloned() +pub fn get(name: &str) -> Option { + let f = factory!(read); + f.get(name).cloned() } - /// Types and creates a function application. pub fn type_apply( - name: String, var_info: & VarInfos, out: & Typ, args: Vec + name: String, + var_info: &VarInfos, + out: &Typ, + args: Vec, ) -> Result { - if args.len() != var_info.len() { - return Err( - TypError::Msg( - format!( - "function `{}` is applied to {} arguments, expected {}", - conf.emph(name), args.len(), var_info.len() - ) - ) - ) - } - - for (arg, info) in args.iter().zip( var_info.iter() ) { - if ! arg.typ().is_compatible( & info.typ ) { - return Err( - TypError::Typ { - expected: Some( info.typ.clone() ), - obtained: arg.typ().clone(), - index: * info.idx, + if args.len() != var_info.len() { + return Err(TypError::Msg(format!( + "function `{}` is applied to {} arguments, expected {}", + conf.emph(name), + args.len(), + var_info.len() + ))); + } + + for (arg, info) in args.iter().zip(var_info.iter()) { + if !arg.typ().is_compatible(&info.typ) { + return Err(TypError::Typ { + expected: Some(info.typ.clone()), + obtained: arg.typ().clone(), + index: *info.idx, + }); } - ) } - } - Ok( - term::fun( out.clone(), name, args ) - ) + Ok(term::fun(out.clone(), name, args)) } - /// Creates a function application. -pub fn apply( - name: String, args: Vec -) -> Result { - use ::errors::TypError ; +pub fn apply(name: String, args: Vec) -> Result { + use errors::TypError; - let def = if let Some(def) = get(& name) { def } else { - return Err( - TypError::Msg( format!("unknown function `{}`", conf.bad(name)) ) - ) - } ; + let def = if let Some(def) = get(&name) { + def + } else { + return Err(TypError::Msg(format!( + "unknown function `{}`", + conf.bad(name) + ))); + }; - type_apply(name, & def.sig, & def.typ, args) + type_apply(name, &def.sig, &def.typ, args) } - - - - - /// Real structure for functions. #[derive(Debug, Clone)] pub struct RFun { - /// Name. - pub name: String, - /// Other functions this function depends on. - pub deps: BTreeSet, - /// Signature. - /// - /// The string stored is the original name of the argument. - pub sig: VarInfos, - /// Type. - pub typ: Typ, - /// Definition. - pub def: Term, - /// The index of the predicate this function was created for. - pub synthetic: Option, - /// Invariants of the function. - pub invariants: TermSet, - /// True if the function is recursive. - recursive: bool, + /// Name. + pub name: String, + /// Other functions this function depends on. + pub deps: BTreeSet, + /// Signature. + /// + /// The string stored is the original name of the argument. + pub sig: VarInfos, + /// Type. + pub typ: Typ, + /// Definition. + pub def: Term, + /// The index of the predicate this function was created for. + pub synthetic: Option, + /// Invariants of the function. + pub invariants: TermSet, + /// True if the function is recursive. + recursive: bool, } impl PartialEq for RFun { - fn eq(& self, other: & Self) -> bool { - self.name == other.name - } + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } } impl Eq for RFun {} impl PartialOrd for RFun { - fn partial_cmp(& self, other: & Self) -> Option< ::std::cmp::Ordering > { - self.name.partial_cmp(& other.name) - } + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + self.name.partial_cmp(&other.name) + } } impl Ord for RFun { - fn cmp(& self, other: & Self) -> ::std::cmp::Ordering { - self.name.cmp(& other.name) - } + fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + self.name.cmp(&other.name) + } } impl ::std::hash::Hash for RFun { - fn hash(& self, state: & mut H) { - self.name.hash(state) - } + fn hash(&self, state: &mut H) { + self.name.hash(state) + } } impl RFun { - /// Constructor. - /// - /// The dependencies are initially empty, and the definition is set to - /// `true`. - pub fn new>( - name: S, sig: VarInfos, typ: Typ - ) -> Self { - let name = name.into() ; - RFun { - name, deps: BTreeSet::new(), - sig, typ, def: term::tru(), synthetic: None, - invariants: TermSet::new(), recursive: false, - } - } - - /// Insert a dependency. - /// - /// Only inserts if `dep` is not `self.name`. - pub fn insert_dep>(& mut self, dep: S) -> bool { - let dep = dep.into() ; - if self.name == dep { - false - } else { - self.deps.insert(dep) + /// Constructor. + /// + /// The dependencies are initially empty, and the definition is set to + /// `true`. + pub fn new>(name: S, sig: VarInfos, typ: Typ) -> Self { + let name = name.into(); + RFun { + name, + deps: BTreeSet::new(), + sig, + typ, + def: term::tru(), + synthetic: None, + invariants: TermSet::new(), + recursive: false, + } } - } - - /// Flips the flag indicating that the function was created internally for a - /// predicate. - pub fn set_synthetic(& mut self, pred: PrdIdx) { - self.synthetic = Some(pred) - } - - /// True if the function is recursive. - pub fn is_recursive(& self) -> bool { - self.recursive - } - - /// Sets the definition of a function. - /// - /// # Panics - /// - /// - if `self.def` is not `term::tru()` - pub fn set_def(& mut self, def: Term) { - def.iter( - |trm| if let Some((name, _)) = trm.fun_inspect() { - if name == & self.name { - self.recursive = true + + /// Insert a dependency. + /// + /// Only inserts if `dep` is not `self.name`. + pub fn insert_dep>(&mut self, dep: S) -> bool { + let dep = dep.into(); + if self.name == dep { + false } else { - self.deps.insert( name.to_string() ) ; + self.deps.insert(dep) } - } - ) ; - match * self.def { - RTerm::Cst(ref cst) if cst.is_true() => (), - _ => panic!("trying to set the definition of a function twice"), - } - self.def = def - } - - /// Checks the function is legal. - pub fn check(& self) -> Res<()> { - for dep in & self.deps { - if get(dep).is_none() { - bail!( - "function `{}` depends on unknown function `{}`", - conf.emph(& self.name), conf.bad(dep) - ) - } } - Ok(()) - } - /// Writes itself and all its dependencies. - pub fn write(& self, w: & mut W, pref: & str) -> Res<()> - where W: Write { - write(w, self, pref) - } -} + /// Flips the flag indicating that the function was created internally for a + /// predicate. + pub fn set_synthetic(&mut self, pred: PrdIdx) { + self.synthetic = Some(pred) + } + /// True if the function is recursive. + pub fn is_recursive(&self) -> bool { + self.recursive + } + /// Sets the definition of a function. + /// + /// # Panics + /// + /// - if `self.def` is not `term::tru()` + pub fn set_def(&mut self, def: Term) { + def.iter(|trm| { + if let Some((name, _)) = trm.fun_inspect() { + if name == &self.name { + self.recursive = true + } else { + self.deps.insert(name.to_string()); + } + } + }); + match *self.def { + RTerm::Cst(ref cst) if cst.is_true() => (), + _ => panic!("trying to set the definition of a function twice"), + } + self.def = def + } + /// Checks the function is legal. + pub fn check(&self) -> Res<()> { + for dep in &self.deps { + if get(dep).is_none() { + bail!( + "function `{}` depends on unknown function `{}`", + conf.emph(&self.name), + conf.bad(dep) + ) + } + } + Ok(()) + } + /// Writes itself and all its dependencies. + pub fn write(&self, w: &mut W, pref: &str) -> Res<()> + where + W: Write, + { + write(w, self, pref) + } +} /// Stores functions from and to some type. #[derive(Debug, Clone)] pub struct Functions { - /// Type these functions are for. - pub typ: Typ, - /// Functions from this type to another one. - pub from_typ: Vec, - /// Function from another type to this one. - pub to_typ: Vec, - /// Functions from this type to itself. - pub from_to_typ: Vec, + /// Type these functions are for. + pub typ: Typ, + /// Functions from this type to another one. + pub from_typ: Vec, + /// Function from another type to this one. + pub to_typ: Vec, + /// Functions from this type to itself. + pub from_to_typ: Vec, } impl Functions { + /// Constructor. + pub fn new(typ: Typ) -> Self { + let f = factory!(read); + + let mut from_typ = vec![]; + let mut to_typ = vec![]; + let mut from_to_typ = vec![]; + + 'find_funs: for fun in f.values() { + let mut sig = fun.sig.iter(); + + let ftyp = match sig.next() { + Some(info) => info.typ == typ && sig.next().is_none(), + _ => false, + }; + + let ttyp = fun.typ == typ; + + match (ftyp, ttyp) { + (true, true) => from_to_typ.push(fun.clone()), + (true, false) => from_typ.push(fun.clone()), + (false, true) => to_typ.push(fun.clone()), + (false, false) => continue 'find_funs, + } + } - /// Constructor. - pub fn new(typ: Typ) -> Self { - let f = factory!(read) ; - - let mut from_typ = vec![] ; - let mut to_typ = vec![] ; - let mut from_to_typ = vec![] ; - - 'find_funs: for fun in f.values() { - let mut sig = fun.sig.iter() ; - - let ftyp = match sig.next() { - Some(info) => info.typ == typ && sig.next().is_none(), - _ => false, - } ; - - let ttyp = fun.typ == typ ; - - match (ftyp, ttyp) { - (true, true) => from_to_typ.push( fun.clone() ), - (true, false) => from_typ.push( fun.clone() ), - (false, true) => to_typ.push( fun.clone() ), - (false, false) => continue 'find_funs, - } + Functions { + typ, + from_typ, + to_typ, + from_to_typ, + } } - - Functions { typ, from_typ, to_typ, from_to_typ } - } - -} \ No newline at end of file +} diff --git a/src/hoice.rs b/src/hoice.rs index 257c807f..5edf9abb 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -10,82 +10,77 @@ #![doc(test(attr(deny(warnings))))] #![allow(non_upper_case_globals)] -extern crate lazy_static ; +extern crate lazy_static; #[macro_use] -extern crate mylib ; +extern crate mylib; #[macro_use] -extern crate error_chain ; +extern crate error_chain; #[macro_use] -extern crate clap ; -extern crate ansi_term as ansi ; +extern crate clap; +extern crate ansi_term as ansi; #[macro_use] -extern crate hashconsing ; -extern crate rsmt2 ; -extern crate num ; -extern crate either ; -extern crate rand ; -extern crate isatty ; - -pub mod errors ; +extern crate hashconsing; +extern crate either; +extern crate isatty; +extern crate num; +extern crate rand; +extern crate rsmt2; + +pub mod errors; #[macro_use] -pub mod common ; - -pub mod val ; -pub mod term ; -pub mod fun ; -pub mod var_to ; -pub mod dtyp ; - -pub mod data ; -pub mod instance ; -pub mod parse ; -pub mod teacher ; -pub mod learning ; -pub mod check ; -pub mod split ; -pub mod unsat_core ; - -use common::* ; -use instance::Instance ; +pub mod common; + +pub mod dtyp; +pub mod fun; +pub mod term; +pub mod val; +pub mod var_to; + +pub mod check; +pub mod data; +pub mod instance; +pub mod learning; +pub mod parse; +pub mod split; +pub mod teacher; +pub mod unsat_core; + +use common::*; +use instance::Instance; /// Parses command-line arguments and works. pub fn work() -> Res<()> { + // Reading from file? + if let Some(file_path) = conf.in_file() { + use std::fs::OpenOptions; - // Reading from file? - if let Some(file_path) = conf.in_file() { - use std::fs::{ OpenOptions } ; - - // Are we in check mode? - if let Some(output_file) = conf.check_file() { - return ::check::do_it(file_path, output_file) - } - - // Not in check mode, open file - let file = OpenOptions::new().read(true).open(file_path).chain_err( - || format!("while opening input file `{}`", conf.emph(file_path)) - ) ? ; - - read_and_work(file, true, false, false) ? ; - Ok(()) + // Are we in check mode? + if let Some(output_file) = conf.check_file() { + return ::check::do_it(file_path, output_file); + } - } else { - // Reading from stdin. + // Not in check mode, open file + let file = OpenOptions::new() + .read(true) + .open(file_path) + .chain_err(|| format!("while opening input file `{}`", conf.emph(file_path)))?; - let stdin = ::std::io::stdin() ; + read_and_work(file, true, false, false)?; + Ok(()) + } else { + // Reading from stdin. - read_and_work(stdin, false, false, false) ? ; - Ok(()) + let stdin = ::std::io::stdin(); - } + read_and_work(stdin, false, false, false)?; + Ok(()) + } } - - - /// Reads a script from a `Read`er and works. /// /// Arguments: -/// +/// /// - `file_input`: indicates whether we're reading from a file. If true, parse /// errors will mention the line where the error occured. /// @@ -94,241 +89,227 @@ pub fn work() -> Res<()> { /// /// - `stop_on_err`: forces to stop at the first error. Only used in tests. pub fn read_and_work( - reader: R, file_input: bool, stop_on_check: bool, stop_on_err: bool -) -> Res< (Option, Instance) > { - use parse::ItemRead ; - - let profiler = Profiler::new() ; - - let mut reader = ::std::io::BufReader::new(reader) ; - // String buffer. - let buf = & mut String::with_capacity(2000) ; - // Parser context. - let mut parser_cxt = ::parse::ParserCxt::new() ; - // Line offset of the parser. - let mut line_off = 0 ; - // Instance. - let mut instance = Instance::new() ; - // Current model. - let mut model = None ; - // Any error encountered? - // let mut error = false ; - - // Unsat core. - // - // - `None` if not unsat - let mut unsat = None ; - - 'parse_work: loop { - use parse::Parsed ; - - profile!{ |profiler| tick "parsing" } - - buf.clear() ; - let lines_parsed = reader.read_item(buf).chain_err( - || "while reading input" - ) ? ; - - if lines_parsed == 0 && file_input { - profile!{ |profiler| mark "parsing" } - break 'parse_work - } - let parse_res = parser_cxt.parser( - & buf, line_off, & profiler - ).parse(& mut instance) ; - - line_off += lines_parsed ; - - let parse_res = match parse_res { - Ok(res) => res, - Err(e) => { - if stop_on_err { return Err(e) } - // error = true ; - print_err(& e) ; - profile!{ |profiler| mark "parsing" } - continue 'parse_work - }, - } ; - - profile!{ |profiler| mark "parsing" } - - match parse_res { + reader: R, + file_input: bool, + stop_on_check: bool, + stop_on_err: bool, +) -> Res<(Option, Instance)> { + use parse::ItemRead; + + let profiler = Profiler::new(); + + let mut reader = ::std::io::BufReader::new(reader); + // String buffer. + let buf = &mut String::with_capacity(2000); + // Parser context. + let mut parser_cxt = ::parse::ParserCxt::new(); + // Line offset of the parser. + let mut line_off = 0; + // Instance. + let mut instance = Instance::new(); + // Current model. + let mut model = None; + // Any error encountered? + // let mut error = false ; + + // Unsat core. + // + // - `None` if not unsat + let mut unsat = None; + + 'parse_work: loop { + use parse::Parsed; + + profile!{ |profiler| tick "parsing" } + + buf.clear(); + let lines_parsed = reader.read_item(buf).chain_err(|| "while reading input")?; + + if lines_parsed == 0 && file_input { + profile!{ |profiler| mark "parsing" } + break 'parse_work; + } + let parse_res = parser_cxt + .parser(&buf, line_off, &profiler) + .parse(&mut instance); - // Check-sat on unsat instance? - Parsed::CheckSat if unsat.is_some() => { - println!("unsat") ; + line_off += lines_parsed; - if stop_on_check { - return Ok( (model, instance) ) - } - }, - - // Check-sat, start class. - Parsed::CheckSat => { - - log! { @info "Running top pre-processing" } - - let preproc_profiler = Profiler::new() ; - match profile! { - |profiler| wrap { - instance::preproc::work(& mut instance, & preproc_profiler) - } "top preproc" - } { - Ok(()) => (), - Err(e) => if e.is_timeout() { - println!("timeout") ; - print_stats("top", profiler) ; - ::std::process::exit(0) - } else if e.is_unknown() { - println!("unknown") ; - continue - } else if e.is_unsat() { - if let Some(clause) = e.unsat_cause() { - unsat = Some( unsat_core::UnsatRes::Clause(clause) ) - } else { - unsat = Some( unsat_core::UnsatRes::None ) - } - () - } else { - bail!(e) - }, - } - print_stats("top preproc", preproc_profiler) ; - - model = if let Some(maybe_model) = instance.is_trivial_conj() ? { - // Pre-processing already decided satisfiability. - log! { @info "solved by pre-processing" } - if ! maybe_model.is_unsat() { - println!("sat") - } else { - println!("unsat") ; - if unsat.as_ref().map( - |unsat| unsat.is_none() - ).unwrap_or(true) && ( - instance.unsat_cores() || instance.proofs() - ) { - bail!("unsat cores/proofs from preprocessing") - } - } - maybe_model.into_option() - } else { - - let arc_instance = Arc::new(instance) ; - let solve_res = split::work(& arc_instance, & profiler) ; - - instance = unwrap_arc(arc_instance).chain_err( - || "while trying to recover instance" - ) ? ; - - match solve_res { - Ok(Some(Either::Left(res))) => { - println!("sat") ; - Some( - instance.extend_model(res) ? - ) - }, - Ok(None) => { - println!("unknown") ; - None - }, - Ok(Some(Either::Right(res))) => { - unsat = Some(res) ; - println!("unsat") ; - None - } - Err(ref e) if e.is_unsat() => { - unsat = Some(unsat_core::UnsatRes::None) ; - warn!( - "unsat was obtained by a legacy mechanism, \ - core/proof will not be available" - ) ; - println!("unsat") ; - None - }, - Err(ref e) if e.is_timeout() => { - println!("timeout") ; - print_stats("top", profiler) ; - ::std::process::exit(0) - }, - Err(ref e) if e.is_smt_unknown() => { - println!( - "; underlying SMT solver reported unknown, giving up..." - ) ; - println!("unknown") ; - None - }, - Err(ref e) if e.is_unknown() => { - println!("unknown") ; - None - }, + let parse_res = match parse_res { + Ok(res) => res, Err(e) => { - println!("bail, {}", e.description()) ; - bail!(e) - }, - } - } ; - - if stop_on_check { - return Ok( (model, instance) ) - } + if stop_on_err { + return Err(e); + } + // error = true ; + print_err(&e); + profile!{ |profiler| mark "parsing" } + continue 'parse_work; + } + }; - }, + profile!{ |profiler| mark "parsing" } - Parsed::GetUnsatCore | - Parsed::GetModel if ! conf.infer => (), + match parse_res { + // Check-sat on unsat instance? + Parsed::CheckSat if unsat.is_some() => { + println!("unsat"); + if stop_on_check { + return Ok((model, instance)); + } + } - // Print unsat core if available. - Parsed::GetUnsatCore => println!("unsupported"), + // Check-sat, start class. + Parsed::CheckSat => { + log! { @info "Running top pre-processing" } + + let preproc_profiler = Profiler::new(); + match profile! { + |profiler| wrap { + instance::preproc::work(& mut instance, & preproc_profiler) + } "top preproc" + } { + Ok(()) => (), + Err(e) => if e.is_timeout() { + println!("timeout"); + print_stats("top", profiler); + ::std::process::exit(0) + } else if e.is_unknown() { + println!("unknown"); + continue; + } else if e.is_unsat() { + if let Some(clause) = e.unsat_cause() { + unsat = Some(unsat_core::UnsatRes::Clause(clause)) + } else { + unsat = Some(unsat_core::UnsatRes::None) + } + () + } else { + bail!(e) + }, + } + print_stats("top preproc", preproc_profiler); + + model = if let Some(maybe_model) = instance.is_trivial_conj()? { + // Pre-processing already decided satisfiability. + log! { @info "solved by pre-processing" } + if !maybe_model.is_unsat() { + println!("sat") + } else { + println!("unsat"); + if unsat.as_ref().map(|unsat| unsat.is_none()).unwrap_or(true) + && (instance.unsat_cores() || instance.proofs()) + { + bail!("unsat cores/proofs from preprocessing") + } + } + maybe_model.into_option() + } else { + let arc_instance = Arc::new(instance); + let solve_res = split::work(&arc_instance, &profiler); + + instance = unwrap_arc(arc_instance) + .chain_err(|| "while trying to recover instance")?; + + match solve_res { + Ok(Some(Either::Left(res))) => { + println!("sat"); + Some(instance.extend_model(res)?) + } + Ok(None) => { + println!("unknown"); + None + } + Ok(Some(Either::Right(res))) => { + unsat = Some(res); + println!("unsat"); + None + } + Err(ref e) if e.is_unsat() => { + unsat = Some(unsat_core::UnsatRes::None); + warn!( + "unsat was obtained by a legacy mechanism, \ + core/proof will not be available" + ); + println!("unsat"); + None + } + Err(ref e) if e.is_timeout() => { + println!("timeout"); + print_stats("top", profiler); + ::std::process::exit(0) + } + Err(ref e) if e.is_smt_unknown() => { + println!("; underlying SMT solver reported unknown, giving up..."); + println!("unknown"); + None + } + Err(ref e) if e.is_unknown() => { + println!("unknown"); + None + } + Err(e) => { + println!("bail, {}", e.description()); + bail!(e) + } + } + }; + + if stop_on_check { + return Ok((model, instance)); + } + } + Parsed::GetUnsatCore | Parsed::GetModel if !conf.infer => (), - // Print unsat core if available. - Parsed::GetProof => println!("unsupported"), + // Print unsat core if available. + Parsed::GetUnsatCore => println!("unsupported"), + // Print unsat core if available. + Parsed::GetProof => println!("unsupported"), - // Print model if available. - Parsed::GetModel => if let Some(model) = model.as_mut() { - // Simplify model before writing it. - // instance.simplify_pred_defs(model) ? ; - let stdout = & mut stdout() ; - instance.write_model(& model, stdout) ? - } else { - bail!("no model available") - }, + // Print model if available. + Parsed::GetModel => if let Some(model) = model.as_mut() { + // Simplify model before writing it. + // instance.simplify_pred_defs(model) ? ; + let stdout = &mut stdout(); + instance.write_model(&model, stdout)? + } else { + bail!("no model available") + }, - Parsed::Items => if instance.print_success() { - println!("success") - }, + Parsed::Items => if instance.print_success() { + println!("success") + }, - Parsed::Reset => { - parser_cxt.reset() ; - instance = Instance::new() ; - model = None - }, + Parsed::Reset => { + parser_cxt.reset(); + instance = Instance::new(); + model = None + } - Parsed::Eof => if stop_on_check { - bail!("reached without reading a check-sat...") - } else { - () - }, + Parsed::Eof => if stop_on_check { + bail!("reached without reading a check-sat...") + } else { + () + }, - Parsed::Exit => break 'parse_work, + Parsed::Exit => break 'parse_work, + } } - } - print_stats("top", profiler) ; + print_stats("top", profiler); - Ok( (model, instance) ) + Ok((model, instance)) } /// Waits until an `Arc` is unwrap-able. fn unwrap_arc(arc: Arc) -> Res { - while Arc::strong_count(& arc) != 1 {} - if let Ok( - res - ) = Arc::try_unwrap(arc) { - Ok(res) - } else { - bail!("unable to unwrap `Arc`") - } -} \ No newline at end of file + while Arc::strong_count(&arc) != 1 {} + if let Ok(res) = Arc::try_unwrap(arc) { + Ok(res) + } else { + bail!("unable to unwrap `Arc`") + } +} diff --git a/src/instance/info.rs b/src/instance/info.rs index 16e18794..007c7854 100644 --- a/src/instance/info.rs +++ b/src/instance/info.rs @@ -1,47 +1,54 @@ //! Types to store information about variables and predicates. -use rsmt2::print::{ Sym2Smt, Sort2Smt } ; +use rsmt2::print::{Sort2Smt, Sym2Smt}; -use common::* ; +use common::*; -use super::Typ ; +use super::Typ; /// Variable info. #[derive(Clone, Debug)] pub struct VarInfo { - /// Variable's name. - pub name: String, - /// Variable's type. - pub typ: Typ, - /// Variable's index. - pub idx: VarIdx, - /// Is the variable active? - pub active: bool, + /// Variable's name. + pub name: String, + /// Variable's type. + pub typ: Typ, + /// Variable's index. + pub idx: VarIdx, + /// Is the variable active? + pub active: bool, } impl VarInfo { - /// Constructor. - pub fn new(name: String, typ: Typ, idx: VarIdx) -> Self { - VarInfo { name, typ, idx, active: true } - } - /// Name of the variable as bytes. - pub fn as_bytes(& self) -> & [u8] { - self.name.as_bytes() - } + /// Constructor. + pub fn new(name: String, typ: Typ, idx: VarIdx) -> Self { + VarInfo { + name, + typ, + idx, + active: true, + } + } + /// Name of the variable as bytes. + pub fn as_bytes(&self) -> &[u8] { + self.name.as_bytes() + } } impl Sym2Smt<()> for VarInfo { - fn sym_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> where Writer: Write { - write!(w, "v{}", self.idx) ? ; - Ok(()) - } + fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> + where + Writer: Write, + { + write!(w, "v{}", self.idx)?; + Ok(()) + } } impl Sort2Smt for VarInfo { - fn sort_to_smt2( - & self, w: & mut Writer - ) -> SmtRes<()> where Writer: Write { - self.typ.get().sort_to_smt2(w) - } + fn sort_to_smt2(&self, w: &mut Writer) -> SmtRes<()> + where + Writer: Write, + { + self.typ.get().sort_to_smt2(w) + } } impl_fmt!{ VarInfo(self, fmt) { @@ -49,22 +56,21 @@ impl_fmt!{ } } - /// Predicate info. #[derive(Clone)] pub struct PrdInfo { - /// Predicate's name. - pub name: String, - /// Predicate's index. - pub idx: PrdIdx, - /// Signature. - pub sig: VarMap, + /// Predicate's name. + pub name: String, + /// Predicate's index. + pub idx: PrdIdx, + /// Signature. + pub sig: VarMap, } impl PrdInfo { - /// Name of the variable as bytes. - pub fn as_bytes(& self) -> & [u8] { - self.name.as_bytes() - } + /// Name of the variable as bytes. + pub fn as_bytes(&self) -> &[u8] { + self.name.as_bytes() + } } impl_fmt!{ PrdInfo(self, fmt) { @@ -72,10 +78,8 @@ impl_fmt!{ } } impl Sym2Smt<()> for PrdInfo { - fn sym_to_smt2( - &self, w: & mut Writer, _: () - ) -> SmtRes<()> { - write!(w, "p_{}", self.idx) ? ; - Ok(()) - } -} \ No newline at end of file + fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + write!(w, "p_{}", self.idx)?; + Ok(()) + } +} diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 7f20fb00..aed4085d 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -1,29 +1,39 @@ //! Contains the clause structure for encapsulation. -use common::* ; -use var_to::terms::VarTermsSet ; -use instance::info::VarInfo ; +use common::*; +use instance::info::VarInfo; +use var_to::terms::VarTermsSet; /// Creates a clause. /// /// Only accessible from the instance. pub fn new( - vars: VarInfos, lhs: Vec, rhs: Option, - info: & 'static str, cls: ClsIdx, + vars: VarInfos, + lhs: Vec, + rhs: Option, + info: &'static str, + cls: ClsIdx, ) -> Clause { - let from = cls ; - let lhs_terms = TermSet::with_capacity( lhs.len() ) ; - let lhs_preds = PredApps::with_capacity( lhs.len() ) ; - let mut clause = Clause { - vars, lhs_terms, lhs_preds, rhs, - terms_changed: true, preds_changed: true, - from_unrolling: false, info, from, - } ; - for tterm in lhs { clause.lhs_insert(tterm) ; } - clause + let from = cls; + let lhs_terms = TermSet::with_capacity(lhs.len()); + let lhs_preds = PredApps::with_capacity(lhs.len()); + let mut clause = Clause { + vars, + lhs_terms, + lhs_preds, + rhs, + terms_changed: true, + preds_changed: true, + from_unrolling: false, + info, + from, + }; + for tterm in lhs { + clause.lhs_insert(tterm); + } + clause } - /// A clause. /// /// Fields are public because a clause is important only if it's in the @@ -34,1019 +44,1014 @@ pub fn new( /// - if `! vars[var].active`, then `var` does not appear in `lhs` or `rhs` #[derive(Clone)] pub struct Clause { - /// Variables of the clause. - pub vars: VarInfos, - /// Terms of the left-hand side. - lhs_terms: TermSet, - /// Predicate applications of the left-hand side. - lhs_preds: PredApps, - /// Single term right-hand side. - rhs: Option, - /// Indicates wether `lhs_terms` has changed since the last call to - /// `lhs_terms_checked`. - terms_changed: bool, - /// Indicates wether the predicate applications have changed since the last - /// call to `preds_checked`. - preds_changed: bool, - /// True if the clause is the result of unrolling. - /// - /// Means the terms will be ignored during mining. - pub from_unrolling: bool, - /// Info about who created this clause. - pub info: & 'static str, - - /// Index of the original clause this comes from. - from: ClsIdx, + /// Variables of the clause. + pub vars: VarInfos, + /// Terms of the left-hand side. + lhs_terms: TermSet, + /// Predicate applications of the left-hand side. + lhs_preds: PredApps, + /// Single term right-hand side. + rhs: Option, + /// Indicates wether `lhs_terms` has changed since the last call to + /// `lhs_terms_checked`. + terms_changed: bool, + /// Indicates wether the predicate applications have changed since the last + /// call to `preds_checked`. + preds_changed: bool, + /// True if the clause is the result of unrolling. + /// + /// Means the terms will be ignored during mining. + pub from_unrolling: bool, + /// Info about who created this clause. + pub info: &'static str, + + /// Index of the original clause this comes from. + from: ClsIdx, } - - /// Functions mutating the clauses. impl Clause { - /// Sets the internal flag `terms_changed` to false. - /// - /// Also shrinks the variables. - pub fn lhs_terms_checked(& mut self) { - self.terms_changed = false ; - self.shrink_vars() - } - - /// Sets the internal flag `preds_changed` to false. - pub fn preds_checked(& mut self) { - self.preds_changed = false ; - self.shrink_vars() - } - - - /// Inserts a top term in the lhs. - /// - /// Returns true if it was not there (`is_new`). - #[inline] - pub fn lhs_insert(& mut self, tterm: TTerm) -> bool { - match tterm { - TTerm::T(term) => if let Some(true) = term.bool() { - false - } else { - let is_new = self.insert_term(term) ; - self.terms_changed = self.terms_changed || is_new ; - is_new - }, - TTerm::P { pred, args } => { - let is_new = self.insert_pred_app(pred, args) ; - self.preds_changed = self.preds_changed || is_new ; - is_new - }, - } - } - - - - /// Removes all predicate application of `pred` in the LHS. - /// - /// Returns true if the predicate application was there. - #[inline] - pub fn drop_lhs_pred( - & mut self, pred: PrdIdx - ) -> Option< VarTermsSet > { - let res = self.lhs_preds.remove(& pred) ; - if res.is_some() { - self.preds_changed = true ; - if self.lhs_preds.is_empty() - && self.rhs.is_none() { - self.terms_changed = true - } + /// Sets the internal flag `terms_changed` to false. + /// + /// Also shrinks the variables. + pub fn lhs_terms_checked(&mut self) { + self.terms_changed = false; + self.shrink_vars() } - res - } - - /// Inserts a predicate application in the LHS. - /// - /// Returns true if the predicate application is new. - #[inline] - pub fn insert_pred_app( - & mut self, pred: PrdIdx, args: VarTerms - ) -> bool { - let is_new = self.lhs_preds.insert_pred_app(pred, args) ; - self.preds_changed = self.preds_changed || is_new ; - is_new - } - - /// Inserts a term in the LHS. - pub fn insert_term( - & mut self, term: Term - ) -> bool { - let is_new = Self::lhs_insert_term(& mut self.lhs_terms, term) ; - self.terms_changed = self.terms_changed || is_new ; - is_new - } - - /// Removes a term from the LHS. - pub fn rm_term(& mut self, term: & Term) -> bool { - let was_there = self.lhs_terms.remove(term) ; - self.terms_changed = self.terms_changed || was_there ; - was_there - } - - /// Drains all LHS applications. - #[inline] - pub fn drain_lhs_preds( - & mut self - ) -> ::std::collections::hash_map::Drain { - self.terms_changed = self.terms_changed || self.rhs.is_none() ; - self.preds_changed = true ; - - self.lhs_preds.drain() - } - - /// Maps over the arguments of a predicate in the lhs. - #[inline] - pub fn lhs_map_args_of( - & mut self, pred: PrdIdx, mut f: F - ) where F: FnMut(& VarTerms) -> VarTerms { - if let Some(argss) = self.lhs_preds.get_mut(& pred) { - self.preds_changed = true ; - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss.iter() { - nu_argss.insert( f(args) ) ; - } - ::std::mem::swap( & mut nu_argss, argss ) ; + + /// Sets the internal flag `preds_changed` to false. + pub fn preds_checked(&mut self) { + self.preds_changed = false; + self.shrink_vars() } - } - - /// Map over the arguments of a predicate in the rhs. - #[inline] - pub fn rhs_map_args(& mut self, mut f: F) - where F: FnMut(PrdIdx, & VarTerms) -> (PrdIdx, VarTerms) { - if let Some( - & mut (ref mut pred, ref mut args) - ) = self.rhs.as_mut() { - self.preds_changed = true ; - let (nu_pred, mut nu_args) = f(* pred, args) ; - * args = nu_args ; - * pred = nu_pred + + /// Inserts a top term in the lhs. + /// + /// Returns true if it was not there (`is_new`). + #[inline] + pub fn lhs_insert(&mut self, tterm: TTerm) -> bool { + match tterm { + TTerm::T(term) => if let Some(true) = term.bool() { + false + } else { + let is_new = self.insert_term(term); + self.terms_changed = self.terms_changed || is_new; + is_new + }, + TTerm::P { pred, args } => { + let is_new = self.insert_pred_app(pred, args); + self.preds_changed = self.preds_changed || is_new; + is_new + } + } } - } - - /// Discards the RHS of a clause. - /// - /// Turns the clause into a negative one. - #[inline] - pub fn unset_rhs(& mut self) -> Option { - let mut old_rhs = None ; - ::std::mem::swap( & mut self.rhs, & mut old_rhs ) ; - self.terms_changed = self.terms_changed || ( - old_rhs.is_some() && self.lhs_preds.is_empty() - ) ; - self.preds_changed = self.preds_changed || old_rhs.is_some() ; - old_rhs - } - - /// Forces the RHS of a clause. - #[inline] - #[cfg_attr( - feature = "cargo-clippy", allow(block_in_if_condition_stmt) - )] - pub fn set_rhs(& mut self, pred: PrdIdx, args: VarTerms) -> Res<()> { - debug_assert! {{ - let mut vars = VarSet::new() ; - - for arg in args.iter() { - term::map_vars(arg, |v| { vars.insert(v) ; () }) - } - for var in vars { - if var >= self.vars.len() { - err_chain! { - "while forcing a clause's rhs" - => format!( - "illegal variable {}, current clause only has {} variables", - conf.bad(& var.default_str() ), self.vars.len() - ) - } - } else if ! self.vars[var].active { - err_chain! { - "while forcing a clause's rhs" - => format!( - "found variable {}, but it is currently inactive", - conf.bad(& self.vars[var].name) - ) - } + + /// Removes all predicate application of `pred` in the LHS. + /// + /// Returns true if the predicate application was there. + #[inline] + pub fn drop_lhs_pred(&mut self, pred: PrdIdx) -> Option { + let res = self.lhs_preds.remove(&pred); + if res.is_some() { + self.preds_changed = true; + if self.lhs_preds.is_empty() && self.rhs.is_none() { + self.terms_changed = true + } } - } - true - }} - - self.rhs = Some((pred, args)) ; - self.preds_changed = true ; - Ok(()) - } - - /// Clones a clause without the lhs predicate applications of `pred`. - pub fn clone_except_lhs_of( - & self, pred: PrdIdx, info: & 'static str - ) -> Self { - let mut lhs_preds = PredApps::with_capacity( self.lhs_preds.len() ) ; - let mut was_there = false ; - for (p, argss) in & self.lhs_preds { - if pred != * p { - let prev = lhs_preds.insert(* p, argss.clone()) ; - debug_assert_eq! { prev, None } - } else { - was_there = true - } + res } - let (terms_changed, preds_changed) = ( - self.terms_changed || ( - was_there && lhs_preds.is_empty() - ), - self.preds_changed || was_there - ) ; - - Clause { - vars: self.vars.clone(), - lhs_terms: self.lhs_terms.clone(), lhs_preds, - rhs: self.rhs.clone(), - terms_changed, preds_changed, - from_unrolling: self.from_unrolling, - info, - from: self.from, + /// Inserts a predicate application in the LHS. + /// + /// Returns true if the predicate application is new. + #[inline] + pub fn insert_pred_app(&mut self, pred: PrdIdx, args: VarTerms) -> bool { + let is_new = self.lhs_preds.insert_pred_app(pred, args); + self.preds_changed = self.preds_changed || is_new; + is_new } - } - - /// Clones a clause but changes the rhs. - #[inline] - pub fn clone_with_rhs( - & self, rhs: Option, info: & 'static str - ) -> Self { - let mut lhs_terms = self.lhs_terms.clone() ; - - let (rhs, terms_changed, preds_changed) = match rhs { - Some( TTerm::P { pred, args } ) => ( - Some((pred, args)), self.terms_changed, true - ), - Some( TTerm::T(term) ) => { - let added = if term.bool() != Some(false) { - lhs_terms.insert( term::not(term) ) - } else { - false - } ; - ( - None, - self.terms_changed || added, - self.preds_changed || self.rhs.is_some() - ) - }, - None => ( - None, - self.terms_changed || self.rhs.is_some(), - self.preds_changed || self.rhs.is_some(), - ), - } ; - - Clause { - vars: self.vars.clone(), - lhs_terms, - lhs_preds: self.lhs_preds.clone(), - rhs, - terms_changed, preds_changed, - from_unrolling: self.from_unrolling, - info, - from: self.from, + + /// Inserts a term in the LHS. + pub fn insert_term(&mut self, term: Term) -> bool { + let is_new = Self::lhs_insert_term(&mut self.lhs_terms, term); + self.terms_changed = self.terms_changed || is_new; + is_new } - } + /// Removes a term from the LHS. + pub fn rm_term(&mut self, term: &Term) -> bool { + let was_there = self.lhs_terms.remove(term); + self.terms_changed = self.terms_changed || was_there; + was_there + } - /// Removes all redundant terms from `lhs_terms`. - fn prune(& mut self) { - use std::cmp::Ordering::* ; - use term::simplify::SimplRes::* ; + /// Drains all LHS applications. + #[inline] + pub fn drain_lhs_preds(&mut self) -> ::std::collections::hash_map::Drain { + self.terms_changed = self.terms_changed || self.rhs.is_none(); + self.preds_changed = true; - let mut to_rmv: Option = Option::None ; - let mut to_add: Option = Option::None ; + self.lhs_preds.drain() + } - let new_set = || TermSet::new() ; + /// Maps over the arguments of a predicate in the lhs. + #[inline] + pub fn lhs_map_args_of(&mut self, pred: PrdIdx, mut f: F) + where + F: FnMut(&VarTerms) -> VarTerms, + { + if let Some(argss) = self.lhs_preds.get_mut(&pred) { + self.preds_changed = true; + let mut nu_argss = VarTermsSet::with_capacity(argss.len()); + for args in argss.iter() { + nu_argss.insert(f(args)); + } + ::std::mem::swap(&mut nu_argss, argss); + } + } - macro_rules! mem { - (apply) => ({ - if let Some(to_rmv) = to_rmv.as_mut() { - for term in to_rmv.drain() { - let was_there = self.lhs_terms.remove(& term) ; - debug_assert! { was_there } - } + /// Map over the arguments of a predicate in the rhs. + #[inline] + pub fn rhs_map_args(&mut self, mut f: F) + where + F: FnMut(PrdIdx, &VarTerms) -> (PrdIdx, VarTerms), + { + if let Some(&mut (ref mut pred, ref mut args)) = self.rhs.as_mut() { + self.preds_changed = true; + let (nu_pred, mut nu_args) = f(*pred, args); + *args = nu_args; + *pred = nu_pred } - let mut added_stuff = false ; - if let Some(to_add) = to_add.as_mut() { - for term in to_add.drain() { - let is_new = self.lhs_terms.insert(term) ; - added_stuff = added_stuff || is_new - } + } + + /// Discards the RHS of a clause. + /// + /// Turns the clause into a negative one. + #[inline] + pub fn unset_rhs(&mut self) -> Option { + let mut old_rhs = None; + ::std::mem::swap(&mut self.rhs, &mut old_rhs); + if (old_rhs.is_some() && self.lhs_preds.is_empty()) || old_rhs.is_some() { + self.terms_changed = true } - added_stuff - }) ; - - (check empty) => ({ - debug_assert!( mem!(rmv empty) ) ; - debug_assert!( mem!(add empty) ) ; - }) ; - - (rmv empty) => ( mem!( @empty to_rmv ) ) ; - (add empty) => ( mem!( @empty to_add ) ) ; - (@ empty $coll:expr) => ( - to_add.as_ref().map( - |to_add| to_add.is_empty() - ).unwrap_or(true) - ) ; - - (rmv $term:expr) => ( mem!(@ to_rmv, $term) ) ; - (add $term:expr) => ( mem!(@ to_add, $term) ) ; - (@ $coll:expr, $term:expr) => ( - $coll.get_or_insert_with(new_set).insert($term) - ) ; + old_rhs } - let mut prune_things = true ; - let mut pruned = false ; - - while prune_things { - prune_things = false ; - mem! { check empty } - - scoped! { - let mut terms = self.lhs_terms.iter() ; - while let Some(term) = terms.next() { - for t in terms.clone() { - match t.conj_simpl(term) { - // `t` is more generic, `term` is redundant, keep `t`. - Cmp(Equal) | Cmp(Greater) => { - mem!( rmv term.clone() ) ; - }, - // `term` is more generic, discard `t`. - Cmp(Less) => { - mem!( rmv t.clone() ) ; - }, - // No relation. - None => (), - // The conjunction of the two terms yields a new one. - Yields(nu_term) => { - mem!( rmv t.clone() ) ; - mem!( rmv term.clone() ) ; - mem!( add nu_term ) ; - }, + /// Forces the RHS of a clause. + #[inline] + #[cfg_attr(feature = "cargo-clippy", allow(block_in_if_condition_stmt))] + pub fn set_rhs(&mut self, pred: PrdIdx, args: VarTerms) -> Res<()> { + debug_assert! {{ + let mut vars = VarSet::new() ; + + for arg in args.iter() { + term::map_vars(arg, |v| { vars.insert(v) ; () }) + } + for var in vars { + if var >= self.vars.len() { + err_chain! { + "while forcing a clause's rhs" + => format!( + "illegal variable {}, current clause only has {} variables", + conf.bad(& var.default_str() ), self.vars.len() + ) + } + } else if ! self.vars[var].active { + err_chain! { + "while forcing a clause's rhs" + => format!( + "found variable {}, but it is currently inactive", + conf.bad(& self.vars[var].name) + ) + } } } - } - } - - pruned = pruned || ! mem!(rmv empty) || ! mem!(add empty) ; + true + }} - self.terms_changed = self.terms_changed - || ! mem!(rmv empty) || ! mem!(add empty) ; + self.rhs = Some((pred, args)); + self.preds_changed = true; + Ok(()) + } - let added_stuff = mem!(apply) ; - prune_things = prune_things || added_stuff + /// Clones a clause without the lhs predicate applications of `pred`. + pub fn clone_except_lhs_of(&self, pred: PrdIdx, info: &'static str) -> Self { + let mut lhs_preds = PredApps::with_capacity(self.lhs_preds.len()); + let mut was_there = false; + for (p, argss) in &self.lhs_preds { + if pred != *p { + let prev = lhs_preds.insert(*p, argss.clone()); + debug_assert_eq! { prev, None } + } else { + was_there = true + } + } - } - } - - - /// Variable substitution. - /// - /// Returns a boolean indicating whether any substitution occured. - /// - /// Used for substitutions in the same clause / predicate scope. - pub fn subst>( - & mut self, map: & Map - ) -> bool { - let mut changed = false ; - let mut lhs_terms = TermSet::with_capacity( self.lhs_terms.len() ) ; - ::std::mem::swap(& mut lhs_terms, & mut self.lhs_terms) ; - for term in lhs_terms.drain() { - let (term, b) = term.subst(map) ; - self.insert_term(term) ; - changed = changed || b - } - for (_, argss) in & mut self.lhs_preds { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - debug_assert!( nu_argss.is_empty() ) ; - for args in argss.iter() { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let (nu_arg, b) = arg.subst(map) ; - changed = changed || b ; - nu_args.push(nu_arg) + let (terms_changed, preds_changed) = ( + self.terms_changed || (was_there && lhs_preds.is_empty()), + self.preds_changed || was_there, + ); + + Clause { + vars: self.vars.clone(), + lhs_terms: self.lhs_terms.clone(), + lhs_preds, + rhs: self.rhs.clone(), + terms_changed, + preds_changed, + from_unrolling: self.from_unrolling, + info, + from: self.from, } - nu_argss.insert( nu_args.into() ) ; - } - ::std::mem::swap(& mut nu_argss, argss) ; - self.preds_changed = true ; } - self.rhs_map_args( - |pred, args| { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let (nu_arg, b) = arg.subst(map) ; - nu_args.push(nu_arg) ; - changed = changed || b + /// Clones a clause but changes the rhs. + #[inline] + pub fn clone_with_rhs(&self, rhs: Option, info: &'static str) -> Self { + let mut lhs_terms = self.lhs_terms.clone(); + + let (rhs, terms_changed, preds_changed) = match rhs { + Some(TTerm::P { pred, args }) => (Some((pred, args)), self.terms_changed, true), + Some(TTerm::T(term)) => { + let added = if term.bool() != Some(false) { + lhs_terms.insert(term::not(term)) + } else { + false + }; + ( + None, + self.terms_changed || added, + self.preds_changed || self.rhs.is_some(), + ) + } + None => ( + None, + self.terms_changed || self.rhs.is_some(), + self.preds_changed || self.rhs.is_some(), + ), + }; + + Clause { + vars: self.vars.clone(), + lhs_terms, + lhs_preds: self.lhs_preds.clone(), + rhs, + terms_changed, + preds_changed, + from_unrolling: self.from_unrolling, + info, + from: self.from, } - ( pred, nu_args.into() ) - } - ) ; - - if changed { - self.terms_changed = true ; - self.preds_changed = true ; - self.prune() } - changed - } - - /// Adds fresh variables to the clause for each of the input variables. - /// Returns a map from the input variables to the fresh ones (as terms). - /// - /// Used when inlining a predicate with quantified variables. - pub fn fresh_vars_for(& mut self, vars: & Quantfed) -> VarHMap { - let mut map = VarHMap::with_capacity( vars.len() ) ; - for (var, typ) in vars { - let fresh = self.vars.next_index() ; - let fresh_name = format!("hoice_fresh_var@{}", fresh) ; - let info = VarInfo::new(fresh_name, typ.clone(), fresh) ; - self.vars.push(info) ; - let _prev = map.insert(* var, term::var(fresh, typ.clone())) ; - debug_assert!( _prev.is_none() ) - } - map - } - - /// Adds fresh variables to the clause for each of the input variables. - /// Returns a map from the input variables to the fresh ones (as terms). - /// - /// Used when inlining a predicate with quantified variables. - pub fn nu_fresh_vars_for( - & mut self, quant: & Option - ) -> VarHMap { - if let Some(quant) = quant.as_ref() { - let vars = quant.vars() ; - let mut map = VarHMap::with_capacity( vars.len() ) ; - for (var, typ) in vars { - let fresh = self.vars.next_index() ; - let fresh_name = format!("hoice_fresh_var@{}", fresh) ; - let info = VarInfo::new(fresh_name, typ.clone(), fresh) ; - self.vars.push(info) ; - let _prev = map.insert(* var, term::var(fresh, typ.clone())) ; - debug_assert!( _prev.is_none() ) - } - map - } else { - VarHMap::new() - } - } + /// Removes all redundant terms from `lhs_terms`. + fn prune(&mut self) { + use std::cmp::Ordering::*; + use term::simplify::SimplRes::*; + + let mut to_rmv: Option = Option::None; + let mut to_add: Option = Option::None; + + let new_set = || TermSet::new(); + + macro_rules! mem { + (apply) => {{ + if let Some(to_rmv) = to_rmv.as_mut() { + for term in to_rmv.drain() { + let was_there = self.lhs_terms.remove(&term); + debug_assert! { was_there } + } + } + let mut added_stuff = false; + if let Some(to_add) = to_add.as_mut() { + for term in to_add.drain() { + let is_new = self.lhs_terms.insert(term); + added_stuff = added_stuff || is_new + } + } + added_stuff + }}; + + (check empty) => {{ + debug_assert!(mem!(rmv empty)); + debug_assert!(mem!(add empty)); + }}; + + (rmv empty) => { + mem!( @empty to_rmv ) + }; + (add empty) => { + mem!( @empty to_add ) + }; + (@ empty $coll:expr) => { + to_add + .as_ref() + .map(|to_add| to_add.is_empty()) + .unwrap_or(true) + }; + + (rmv $term:expr) => { + mem!(@ to_rmv, $term) + }; + (add $term:expr) => { + mem!(@ to_add, $term) + }; + (@ $coll:expr, $term:expr) => { + $coll.get_or_insert_with(new_set).insert($term) + }; + } + + let mut prune_things = true; + let mut pruned = false; + + while prune_things { + prune_things = false; + mem! { check empty } + + scoped! { + let mut terms = self.lhs_terms.iter() ; + while let Some(term) = terms.next() { + for t in terms.clone() { + match t.conj_simpl(term) { + // `t` is more generic, `term` is redundant, keep `t`. + Cmp(Equal) | Cmp(Greater) => { + mem!( rmv term.clone() ) ; + }, + // `term` is more generic, discard `t`. + Cmp(Less) => { + mem!( rmv t.clone() ) ; + }, + // No relation. + None => (), + // The conjunction of the two terms yields a new one. + Yields(nu_term) => { + mem!( rmv t.clone() ) ; + mem!( rmv term.clone() ) ; + mem!( add nu_term ) ; + }, + } + } + } + } + pruned = pruned || !mem!(rmv empty) || !mem!(add empty); + self.terms_changed = self.terms_changed || !mem!(rmv empty) || !mem!(add empty); + let added_stuff = mem!(apply); + prune_things = prune_things || added_stuff + } + } + /// Variable substitution. + /// + /// Returns a boolean indicating whether any substitution occured. + /// + /// Used for substitutions in the same clause / predicate scope. + pub fn subst>(&mut self, map: &Map) -> bool { + let mut changed = false; + let mut lhs_terms = TermSet::with_capacity(self.lhs_terms.len()); + ::std::mem::swap(&mut lhs_terms, &mut self.lhs_terms); + for term in lhs_terms.drain() { + let (term, b) = term.subst(map); + self.insert_term(term); + changed = changed || b + } + for (_, argss) in &mut self.lhs_preds { + let mut nu_argss = VarTermsSet::with_capacity(argss.len()); + debug_assert!(nu_argss.is_empty()); + for args in argss.iter() { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let (nu_arg, b) = arg.subst(map); + changed = changed || b; + nu_args.push(nu_arg) + } + nu_argss.insert(nu_args.into()); + } + ::std::mem::swap(&mut nu_argss, argss); + self.preds_changed = true; + } + self.rhs_map_args(|pred, args| { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let (nu_arg, b) = arg.subst(map); + nu_args.push(nu_arg); + changed = changed || b + } + (pred, nu_args.into()) + }); - /// Deactivates a variable. - pub fn deactivate(& mut self, var: VarIdx) -> Res<()> { - debug_assert!( self.vars[var].active ) ; - self.vars[var].active = false ; - self.check( "after `deactivate`" ) - } + if changed { + self.terms_changed = true; + self.preds_changed = true; + self.prune() + } - /// Shrinks the clause: detects inactive variables. - pub fn shrink_vars(& mut self) { - let mut active = 0 ; - for var in & self.vars { - if var.active { active += 1 } + changed } - let mut vars = VarSet::with_capacity( active ) ; - for term in & self.lhs_terms { - term::map_vars(term, |v| { vars.insert(v) ; () }) ; - if vars.len() == active { - return () - } + /// Adds fresh variables to the clause for each of the input variables. + /// Returns a map from the input variables to the fresh ones (as terms). + /// + /// Used when inlining a predicate with quantified variables. + pub fn fresh_vars_for(&mut self, vars: &Quantfed) -> VarHMap { + let mut map = VarHMap::with_capacity(vars.len()); + for (var, typ) in vars { + let fresh = self.vars.next_index(); + let fresh_name = format!("hoice_fresh_var@{}", fresh); + let info = VarInfo::new(fresh_name, typ.clone(), fresh); + self.vars.push(info); + let _prev = map.insert(*var, term::var(fresh, typ.clone())); + debug_assert!(_prev.is_none()) + } + map } - for (_, argss) in & self.lhs_preds { - for args in argss { - for arg in args.iter() { - term::map_vars(arg, |v| { vars.insert(v) ; () }) ; + /// Adds fresh variables to the clause for each of the input variables. + /// Returns a map from the input variables to the fresh ones (as terms). + /// + /// Used when inlining a predicate with quantified variables. + pub fn nu_fresh_vars_for(&mut self, quant: &Option) -> VarHMap { + if let Some(quant) = quant.as_ref() { + let vars = quant.vars(); + let mut map = VarHMap::with_capacity(vars.len()); + for (var, typ) in vars { + let fresh = self.vars.next_index(); + let fresh_name = format!("hoice_fresh_var@{}", fresh); + let info = VarInfo::new(fresh_name, typ.clone(), fresh); + self.vars.push(info); + let _prev = map.insert(*var, term::var(fresh, typ.clone())); + debug_assert!(_prev.is_none()) + } + map + } else { + VarHMap::new() } - } - if vars.len() == active { - return () - } } - if let Some(& (_, ref args)) = self.rhs.as_ref() { - for arg in args.iter() { - term::map_vars(arg, |v| { vars.insert(v) ; () }) ; - } + /// Deactivates a variable. + pub fn deactivate(&mut self, var: VarIdx) -> Res<()> { + debug_assert!(self.vars[var].active); + self.vars[var].active = false; + self.check("after `deactivate`") } - for (index, info) in self.vars.index_iter_mut() { - if ! vars.contains(& index) { - info.active = false - } - } - } - - /// Inserts a term in an LHS. Externalized for ownership reasons. - fn lhs_insert_term(lhs_terms: & mut TermSet, term: Term) -> bool { - // println!("inserting {}", term) ; - let mut new_stuff = false ; - let mut stack = vec![term] ; - - while let Some(term) = stack.pop() { - debug_assert! { term.typ().is_bool() } - if let Some(kids) = term.conj_inspect() { - for kid in kids { - stack.push( kid.clone() ) + /// Shrinks the clause: detects inactive variables. + pub fn shrink_vars(&mut self) { + let mut active = 0; + for var in &self.vars { + if var.active { + active += 1 + } } - } else if let Some(b) = term.bool() { - if ! b { - lhs_terms.clear() ; - lhs_terms.insert( term::fls() ) ; - return true + let mut vars = VarSet::with_capacity(active); + + for term in &self.lhs_terms { + term::map_vars(term, |v| { + vars.insert(v); + () + }); + if vars.len() == active { + return (); + } + } + + for (_, argss) in &self.lhs_preds { + for args in argss { + for arg in args.iter() { + term::map_vars(arg, |v| { + vars.insert(v); + () + }); + } + } + if vars.len() == active { + return (); + } + } + + if let Some(&(_, ref args)) = self.rhs.as_ref() { + for arg in args.iter() { + term::map_vars(arg, |v| { + vars.insert(v); + () + }); + } + } + + for (index, info) in self.vars.index_iter_mut() { + if !vars.contains(&index) { + info.active = false + } } - } else { - let is_new = Self::clever_insert(term.clone(), lhs_terms) ; - new_stuff = new_stuff || is_new - } } - new_stuff - } - - /// Checks if `term` is implied by a term in `set`. - /// - /// Removes the terms from `set` that are implied (strictly) by `term`. - fn clever_insert( - mut term: Term, set: & mut TermSet - ) -> bool { - use std::cmp::Ordering::* ; - use term::simplify::SimplRes::* ; - - let mut redundant = false ; - let mut rmed_stuff = false ; - let mut keep_checking = true ; - - while keep_checking { - keep_checking = false ; - - set.retain( - |t| match t.conj_simpl(& term) { - // `t` is more generic, `term` is redundant, keep `t`. - Cmp(Equal) | Cmp(Greater) => { - redundant = true ; + /// Inserts a term in an LHS. Externalized for ownership reasons. + fn lhs_insert_term(lhs_terms: &mut TermSet, term: Term) -> bool { + // println!("inserting {}", term) ; + let mut new_stuff = false; + let mut stack = vec![term]; + + while let Some(term) = stack.pop() { + debug_assert! { term.typ().is_bool() } + if let Some(kids) = term.conj_inspect() { + for kid in kids { + stack.push(kid.clone()) + } + } else if let Some(b) = term.bool() { + if !b { + lhs_terms.clear(); + lhs_terms.insert(term::fls()); + return true; + } + } else { + let is_new = Self::clever_insert(term.clone(), lhs_terms); + new_stuff = new_stuff || is_new + } + } + + new_stuff + } + + /// Checks if `term` is implied by a term in `set`. + /// + /// Removes the terms from `set` that are implied (strictly) by `term`. + fn clever_insert(mut term: Term, set: &mut TermSet) -> bool { + use std::cmp::Ordering::*; + use term::simplify::SimplRes::*; + + let mut redundant = false; + let mut rmed_stuff = false; + let mut keep_checking = true; + + while keep_checking { + keep_checking = false; + + set.retain(|t| match t.conj_simpl(&term) { + // `t` is more generic, `term` is redundant, keep `t`. + Cmp(Equal) | Cmp(Greater) => { + redundant = true; + true + } + // `term` is more generic, discard. + Cmp(Less) => { + rmed_stuff = true; + false + } + // No relation, keep `t`. + None => true, + // New term. + Yields(nu_term) => { + rmed_stuff = true; + redundant = false; + keep_checking = true; + term = nu_term; + false + } + }) + } + + // If we removed stuff, it means the term should not be redundant. + if !redundant { + let is_new = set.insert(term); + debug_assert! { is_new } true - }, - // `term` is more generic, discard. - Cmp(Less) => { - rmed_stuff = true ; - false - }, - // No relation, keep `t`. - None => true, - // New term. - Yields(nu_term) => { - rmed_stuff = true ; - redundant = false ; - keep_checking = true ; - term = nu_term ; + } else { false - }, } - ) } +} - // If we removed stuff, it means the term should not be redundant. - if ! redundant { - let is_new = set.insert(term) ; - debug_assert! { is_new } - true - } else { - false +impl Clause { + /// Checks if two clauses are the same. + pub fn same_as(&self, other: &Self) -> bool { + self.rhs == other.rhs + && self.lhs_preds == other.lhs_preds + && self.lhs_terms == other.lhs_terms } - } -} + /// Cheap unsat check. + /// + /// Does not use smt-solving, as this is the responsability of the + /// preprocessing. + pub fn is_unsat(&self) -> bool { + self.lhs_terms.is_empty() && self.lhs_preds.is_empty() && self.rhs.is_none() + } + /// True if the clause is positive (lhs is empty). + pub fn is_positive(&self) -> bool { + self.lhs_preds.is_empty() && self.rhs.is_some() + } -impl Clause { + /// True if the clause is a strictly negative clause. + /// + /// True if + /// + /// - no rhs + /// - only one predicate application + pub fn is_strict_neg(&self) -> bool { + self.rhs.is_none() && self.lhs_preds.len() == 1 && self + .lhs_preds() + .iter() + .next() + .map(|(_, argss)| argss.len() == 1) + .unwrap_or(false) + } + + /// True if the same predicate application appears both in the lhs and the + /// rhs. + pub fn is_pred_trivial(&self) -> bool { + if let Some((pred, args)) = self.rhs() { + if let Some(argss) = self.lhs_preds.get(&pred) { + argss.contains(args) + } else { + false + } + } else { + false + } + } - /// Checks if two clauses are the same. - pub fn same_as(& self, other: & Self) -> bool { - self.rhs == other.rhs && - self.lhs_preds == other.lhs_preds && - self.lhs_terms == other.lhs_terms - } - - /// Cheap unsat check. - /// - /// Does not use smt-solving, as this is the responsability of the - /// preprocessing. - pub fn is_unsat(& self) -> bool { - self.lhs_terms.is_empty() - && self.lhs_preds.is_empty() - && self.rhs.is_none() - } - - /// True if the clause is positive (lhs is empty). - pub fn is_positive(& self) -> bool { - self.lhs_preds.is_empty() && self.rhs.is_some() - } - - /// True if the clause is a strictly negative clause. - /// - /// True if - /// - /// - no rhs - /// - only one predicate application - pub fn is_strict_neg(& self) -> bool { - self.rhs.is_none() - && self.lhs_preds.len() == 1 - && self.lhs_preds().iter().next().map( - |(_, argss)| argss.len() == 1 - ).unwrap_or(false) - } - - - /// True if the same predicate application appears both in the lhs and the - /// rhs. - pub fn is_pred_trivial(& self) -> bool { - if let Some((pred, args)) = self.rhs() { - if let Some(argss) = self.lhs_preds.get(& pred) { - argss.contains(args) - } else { - false - } - } else { - false + /// True iff the lhs terms changed since the last call to + /// [`lhs_terms_checked`][checked]. + /// + /// [checked]: #methods.lhs_terms_checked + /// (lhs_terms_checked method) + pub fn terms_changed(&self) -> bool { + self.terms_changed } - } - - /// True iff the lhs terms changed since the last call to - /// [`lhs_terms_checked`][checked]. - /// - /// [checked]: #methods.lhs_terms_checked - /// (lhs_terms_checked method) - pub fn terms_changed(& self) -> bool { self.terms_changed } - - /// True iff the predicate applications have changed since the last call to - /// [`preds_checked`][checked]. - /// - /// [checked]: #methods.preds_checked - /// (preds_checked method) - pub fn preds_changed(& self) -> bool { self.preds_changed } - - /// Checks a clause is well-formed. - #[cfg(debug_assertions)] - pub fn check(& self, blah: & 'static str) -> Res<()> { - use std::iter::Extend ; - let mut vars = VarSet::with_capacity( self.vars.len() ) ; - for term in & self.lhs_terms { - vars.extend( term::vars( term ) ) + + /// True iff the predicate applications have changed since the last call to + /// [`preds_checked`][checked]. + /// + /// [checked]: #methods.preds_checked + /// (preds_checked method) + pub fn preds_changed(&self) -> bool { + self.preds_changed } - scoped! { - let mut terms = self.lhs_terms.iter() ; + /// Checks a clause is well-formed. + #[cfg(debug_assertions)] + pub fn check(&self, blah: &'static str) -> Res<()> { + use std::iter::Extend; + let mut vars = VarSet::with_capacity(self.vars.len()); + for term in &self.lhs_terms { + vars.extend(term::vars(term)) + } - while let Some(term) = terms.next() { - for t in terms.clone() { - if term.conj_simpl(t).is_some() { - bail!( - "redundant atoms in clause:\n {}\n {}\n{}", - term, t, conf.bad(blah) - ) + scoped! { + let mut terms = self.lhs_terms.iter() ; + + while let Some(term) = terms.next() { + for t in terms.clone() { + if term.conj_simpl(t).is_some() { + bail!( + "redundant atoms in clause:\n {}\n {}\n{}", + term, t, conf.bad(blah) + ) + } + } } } - } - } - for (_, argss) in & self.lhs_preds { - for args in argss { - for arg in args.iter() { - vars.extend( term::vars(arg) ) + for (_, argss) in &self.lhs_preds { + for args in argss { + for arg in args.iter() { + vars.extend(term::vars(arg)) + } + } } - } - } - if let Some((_, ref args)) = self.rhs { - for arg in args.iter() { - vars.extend( term::vars(arg) ) - } - } - for var in vars { - if ! self.vars[var].active { - bail!( - "ill-formed clause: {}, \ - variable {} appears in the clause but is not active", - blah, self[var] - ) - } + if let Some((_, ref args)) = self.rhs { + for arg in args.iter() { + vars.extend(term::vars(arg)) + } + } + for var in vars { + if !self.vars[var].active { + bail!( + "ill-formed clause: {}, \ + variable {} appears in the clause but is not active", + blah, + self[var] + ) + } + } + Ok(()) } - Ok(()) - } - #[cfg(not(debug_assertions))] - #[inline] - pub fn check(& self, _: & 'static str) -> Res<()> { - Ok(()) - } - - /// Length of a clause's LHS. - #[inline] - pub fn lhs_len(& self) -> usize { - self.lhs_terms.len() + self.lhs_preds.len() - } - /// True if the clause's LHS is empty. - #[inline] - pub fn lhs_is_empty(& self) -> bool { - self.lhs_terms.is_empty() && self.lhs_preds.is_empty() - } - - /// LHS accessor (terms). - #[inline] - pub fn lhs_terms(& self) -> & TermSet { - & self.lhs_terms - } - /// LHS accessor (predicate applications). - #[inline] - pub fn lhs_preds(& self) -> & PredApps { - & self.lhs_preds - } - - /// Number of predicate applications in the lhs (>= number of predicates). - pub fn lhs_pred_apps_len(& self) -> usize { - let mut sum = 0 ; - for argss in self.lhs_preds.values() { - sum += argss.len() + #[cfg(not(debug_assertions))] + #[inline] + pub fn check(&self, _: &'static str) -> Res<()> { + Ok(()) } - sum - } - - /// RHS accessor. - #[inline] - pub fn rhs(& self) -> Option<(PrdIdx, & VarTerms)> { - if let Some((prd, ref args)) = self.rhs { - Some((prd, args)) - } else { - None + + /// Length of a clause's LHS. + #[inline] + pub fn lhs_len(&self) -> usize { + self.lhs_terms.len() + self.lhs_preds.len() } - } - - /// Iterator over all predicate applications (including the rhs). - pub fn all_pred_apps_do(& self, mut f: F) -> Res<()> - where F: FnMut(PrdIdx, & VarTerms) -> Res<()> { - for (pred, argss) in & self.lhs_preds { - for args in argss { - f(* pred, args) ? - } + /// True if the clause's LHS is empty. + #[inline] + pub fn lhs_is_empty(&self) -> bool { + self.lhs_terms.is_empty() && self.lhs_preds.is_empty() } - if let Some(& (pred, ref args)) = self.rhs.as_ref() { - f(pred, args) ? + + /// LHS accessor (terms). + #[inline] + pub fn lhs_terms(&self) -> &TermSet { + &self.lhs_terms } - Ok(()) - } - - /// Variables accessor. - #[inline] - pub fn vars(& self) -> & VarInfos { - & self.vars - } - - /// Returns the source clauses. - /// - /// Source clauses are original clauses this clause stems from. - pub fn from(& self) -> ClsIdx { - self.from - } - - /// Declares all active clause variables. - pub fn declare( - & self, solver: & mut Solver - ) -> Res<()> { - for var in self.vars() { - if var.active { - solver.declare_const(& var.idx, var.typ.get()) ? - } + /// LHS accessor (predicate applications). + #[inline] + pub fn lhs_preds(&self) -> &PredApps { + &self.lhs_preds } - Ok(()) - } - - /// Writes a clause given a special function to write predicates. - pub fn write( - & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd, info: bool - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - if info { - writeln!( - w, "\ - ; {} inactive variable(s)\n\ - ; unroll: {}\n\ - ; terms_changed: {}\n\ - ; preds_changed: {}\n\ - ; created by `{}`\ - ", - self.vars.iter().fold( - 0, |acc, var| acc + if var.active { 1 } else { 0 } - ), self.from_unrolling, - self.terms_changed, self.preds_changed, self.info, - ) ? + + /// Number of predicate applications in the lhs (>= number of predicates). + pub fn lhs_pred_apps_len(&self) -> usize { + let mut sum = 0; + for argss in self.lhs_preds.values() { + sum += argss.len() + } + sum } - writeln!(w, "({} ", keywords::cmd::assert) ? ; - self.forall_write(w, write_var, write_prd, 2) ? ; - writeln!(w, ")") ? ; - Ok(()) - } - - /// Writes the body of a clause with its quantifier. - pub fn forall_write( - & self, w: & mut W, - write_var: WriteVar, write_prd: WritePrd, indent: usize - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - write!( - w, "{nil: >indent$}({}\n{nil: >indent$} (", - keywords::forall, nil="", indent=indent - ) ? ; - - let mut inactive = 0 ; - for var in & self.vars { - if var.active { - write!(w, " (") ? ; - write_var(w, var) ? ; - write!(w, " {})", var.typ) ? - } else { - inactive += 1 ; - } + /// RHS accessor. + #[inline] + pub fn rhs(&self) -> Option<(PrdIdx, &VarTerms)> { + if let Some((prd, ref args)) = self.rhs { + Some((prd, args)) + } else { + None + } } - if inactive == self.vars.len() { - write!(w, " (unused Bool)") ? + + /// Iterator over all predicate applications (including the rhs). + pub fn all_pred_apps_do(&self, mut f: F) -> Res<()> + where + F: FnMut(PrdIdx, &VarTerms) -> Res<()>, + { + for (pred, argss) in &self.lhs_preds { + for args in argss { + f(*pred, args)? + } + } + if let Some(&(pred, ref args)) = self.rhs.as_ref() { + f(pred, args)? + } + Ok(()) } - writeln!(w, " )") ? ; - - self.qf_write(w, write_var, write_prd, indent + 2) ? ; - - writeln!(w, "{nil: >indent$})", nil="", indent=indent) ? ; - - Ok(()) - } - - - /// Writes a clause without the quantifiers around it. - pub fn qf_write( - & self, w: & mut W, - write_var: WriteVar, write_prd: WritePrd, indent: usize - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, & VarInfo) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - write!( - w, "{nil: >indent$}(=>\n{nil: >indent$} (and\n{nil: >indent$} ", - nil="", indent=indent - ) ? ; - - if self.lhs_terms.is_empty() { - write!(w, " true") ? - } else { - for term in & self.lhs_terms { - write!(w, " ") ? ; - term.write( - w, |w, var| write_var( w, & self.vars[var] ) - ) ? - } + + /// Variables accessor. + #[inline] + pub fn vars(&self) -> &VarInfos { + &self.vars } - write!(w, "\n{nil: >indent$} ", nil="", indent=indent) ? ; + /// Returns the source clauses. + /// + /// Source clauses are original clauses this clause stems from. + pub fn from(&self) -> ClsIdx { + self.from + } - if self.lhs_preds.is_empty() { - write!(w, " true") ? - } else { - for (pred, argss) in & self.lhs_preds { - for args in argss { - write!(w, " ") ? ; - write_prd(w, * pred, args) ? + /// Declares all active clause variables. + pub fn declare(&self, solver: &mut Solver) -> Res<()> { + for var in self.vars() { + if var.active { + solver.declare_const(&var.idx, var.typ.get())? + } } - } + Ok(()) } - write!( - w, "\n{nil: >indent$} )\n{nil: >indent$} ", nil="", indent=indent - ) ? ; + /// Writes a clause given a special function to write predicates. + pub fn write( + &self, + w: &mut W, + write_var: WriteVar, + write_prd: WritePrd, + info: bool, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + if info { + writeln!( + w, + "\ + ; {} inactive variable(s)\n\ + ; unroll: {}\n\ + ; terms_changed: {}\n\ + ; preds_changed: {}\n\ + ; created by `{}`\ + ", + self.vars + .iter() + .fold(0, |acc, var| acc + if var.active { 1 } else { 0 }), + self.from_unrolling, + self.terms_changed, + self.preds_changed, + self.info, + )? + } - if let Some((pred, ref args)) = self.rhs { - write_prd(w, pred, args) ? - } else { - write!(w, "false") ? + writeln!(w, "({} ", keywords::cmd::assert)?; + self.forall_write(w, write_var, write_prd, 2)?; + writeln!(w, ")")?; + Ok(()) } - writeln!( - w, "\n{nil: >indent$})", nil="", indent=indent - ) ? ; - Ok(()) - } + /// Writes the body of a clause with its quantifier. + pub fn forall_write( + &self, + w: &mut W, + write_var: WriteVar, + write_prd: WritePrd, + indent: usize, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + write!( + w, + "{nil: >indent$}({}\n{nil: >indent$} (", + keywords::forall, + nil = "", + indent = indent + )?; + + let mut inactive = 0; + for var in &self.vars { + if var.active { + write!(w, " (")?; + write_var(w, var)?; + write!(w, " {})", var.typ)? + } else { + inactive += 1; + } + } + if inactive == self.vars.len() { + write!(w, " (unused Bool)")? + } + writeln!(w, " )")?; -} + self.qf_write(w, write_var, write_prd, indent + 2)?; -impl ::std::ops::Index for Clause { - type Output = VarInfo ; - fn index(& self, index: VarIdx) -> & VarInfo { - & self.vars[index] - } -} + writeln!(w, "{nil: >indent$})", nil = "", indent = indent)?; -impl<'a, 'b> ::rsmt2::print::Expr2Smt< - & 'b (bool, & 'a PrdSet, & 'a PrdSet, & 'a PrdInfos) -> for Clause { - /// Writes the clause in SMT-LIB format. - /// - /// The boolean flag in the info specifies whether the clause should be - /// asserted positively (when `true`) or negatively (when `false`). - fn expr_to_smt2( - & self, writer: & mut Writer, info: & 'b ( - bool, & 'a PrdSet, & 'a PrdSet, & 'a PrdInfos - ) - ) -> SmtRes<()> { - let ( - pos, ref true_preds, ref false_preds, ref prd_info - ) = * info ; - - if ! pos { - write!(writer, "(not ") ? + Ok(()) } - if ! self.lhs_is_empty() { - write!(writer, "(=> (and") ? - } + /// Writes a clause without the quantifiers around it. + pub fn qf_write( + &self, + w: &mut W, + write_var: WriteVar, + write_prd: WritePrd, + indent: usize, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + write!( + w, + "{nil: >indent$}(=>\n{nil: >indent$} (and\n{nil: >indent$} ", + nil = "", + indent = indent + )?; + + if self.lhs_terms.is_empty() { + write!(w, " true")? + } else { + for term in &self.lhs_terms { + write!(w, " ")?; + term.write(w, |w, var| write_var(w, &self.vars[var]))? + } + } - for term in & self.lhs_terms { - write!(writer, " ") ? ; - term.write( writer, |w, var| var.default_write(w) ) ? - } + write!(w, "\n{nil: >indent$} ", nil = "", indent = indent)?; - for (pred, argss) in & self.lhs_preds { - if true_preds.contains(pred) { - write!(writer, " true") ? - } else if false_preds.contains(pred) { - write!(writer, " false") ? - } else { - for args in argss { - write!(writer, " (") ? ; - writer.write_all( prd_info[* pred].name.as_bytes() ) ? ; - for arg in args.iter() { - write!(writer, " ") ? ; - arg.write(writer, |w, var| var.default_write(w)) ? - } - write!(writer, ")") ? + if self.lhs_preds.is_empty() { + write!(w, " true")? + } else { + for (pred, argss) in &self.lhs_preds { + for args in argss { + write!(w, " ")?; + write_prd(w, *pred, args)? + } + } } - } + + write!( + w, + "\n{nil: >indent$} )\n{nil: >indent$} ", + nil = "", + indent = indent + )?; + + if let Some((pred, ref args)) = self.rhs { + write_prd(w, pred, args)? + } else { + write!(w, "false")? + } + writeln!(w, "\n{nil: >indent$})", nil = "", indent = indent)?; + + Ok(()) } +} - if ! self.lhs_is_empty() { - write!(writer, ") ") ? +impl ::std::ops::Index for Clause { + type Output = VarInfo; + fn index(&self, index: VarIdx) -> &VarInfo { + &self.vars[index] } +} - if let Some((prd, ref args)) = self.rhs { - if true_preds.contains(& prd) { - write!(writer, "true") ? - } else if false_preds.contains(& prd) { - write!(writer, "false") ? - } else { - if ! args.is_empty() { - write!(writer, "(") ? +impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clause { + /// Writes the clause in SMT-LIB format. + /// + /// The boolean flag in the info specifies whether the clause should be + /// asserted positively (when `true`) or negatively (when `false`). + fn expr_to_smt2( + &self, + writer: &mut Writer, + info: &'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos), + ) -> SmtRes<()> { + let (pos, ref true_preds, ref false_preds, ref prd_info) = *info; + + if !pos { + write!(writer, "(not ")? } - write!(writer, "{}", prd_info[prd].name) ? ; - for arg in args.iter() { - write!(writer, " ") ? ; - arg.write(writer, |w, var| var.default_write(w)) ? + + if !self.lhs_is_empty() { + write!(writer, "(=> (and")? } - if ! args.is_empty() { - write!(writer, ")") ? + + for term in &self.lhs_terms { + write!(writer, " ")?; + term.write(writer, |w, var| var.default_write(w))? } - } - } else { - write!(writer, "false") ? - } - if ! self.lhs_is_empty() { - write!(writer, ")") ? - } + for (pred, argss) in &self.lhs_preds { + if true_preds.contains(pred) { + write!(writer, " true")? + } else if false_preds.contains(pred) { + write!(writer, " false")? + } else { + for args in argss { + write!(writer, " (")?; + writer.write_all(prd_info[*pred].name.as_bytes())?; + for arg in args.iter() { + write!(writer, " ")?; + arg.write(writer, |w, var| var.default_write(w))? + } + write!(writer, ")")? + } + } + } - if ! pos { - write!(writer, ")") ? - } + if !self.lhs_is_empty() { + write!(writer, ") ")? + } - Ok(()) - } -} \ No newline at end of file + if let Some((prd, ref args)) = self.rhs { + if true_preds.contains(&prd) { + write!(writer, "true")? + } else if false_preds.contains(&prd) { + write!(writer, "false")? + } else { + if !args.is_empty() { + write!(writer, "(")? + } + write!(writer, "{}", prd_info[prd].name)?; + for arg in args.iter() { + write!(writer, " ")?; + arg.write(writer, |w, var| var.default_write(w))? + } + if !args.is_empty() { + write!(writer, ")")? + } + } + } else { + write!(writer, "false")? + } + + if !self.lhs_is_empty() { + write!(writer, ")")? + } + + if !pos { + write!(writer, ")")? + } + + Ok(()) + } +} diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index e6f7d6bb..77d507c7 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -1,18 +1,15 @@ //! Actual instance structure. -use common::* ; -use instance::info::* ; -use data::Data ; +use common::*; +use data::Data; +use instance::info::*; use var_to::terms::VarTermsSet; -mod clause ; -mod pre_instance ; - -pub use self::clause::Clause ; -pub use self::pre_instance::PreInstance ; - - +mod clause; +mod pre_instance; +pub use self::clause::Clause; +pub use self::pre_instance::PreInstance; /// Stores the instance: the clauses, the factory and so on. /// @@ -25,1580 +22,1517 @@ pub use self::pre_instance::PreInstance ; /// do this is to never access an instance's fields directly from the outside. #[derive(Clone)] pub struct Instance { - /// Predicates. - preds: PrdInfos, - /// Original predicates, for reconstruction. - /// - /// Stores the original signature of the predicates, and a map from the - /// variables of `preds` to the original signature. - old_preds: PrdMap< (Sig, VarMap) >, - /// Maps new variables to the new ones. - /// - /// Only available after finalize. - old_var_maps: PrdMap>, - /// Predicates for which a suitable term has been found. - pred_terms: PrdMap< Option< TTerms > >, - - /// Predicates defined in `pred_terms`, sorted by predicate dependencies. - /// - /// Populated by the `finalize` function. - sorted_pred_terms: Vec, - - /// Strengthener for predicates. - pred_str: PrdMap< Option >, - - /// Companion function for predicates. - /// - /// A companion function is a function that was created internally - /// specifically for a predicate. Meaning it needs to be given to the user - /// when printing the model. - companion_funs: PrdHMap< Vec >, - - /// Side-clauses. - /// - /// A side clause - /// - does not mention any predicate - /// - mentions a user-defined function - /// - /// It will be asserted when the instance is asked to initialize a solver. A - /// side-clause is viewed as additional information provided by the user, a - /// kind of lemma for the actual clauses. - side_clauses: Vec, - - /// Clauses. - clauses: ClsMap, - /// Maps predicates to the clauses where they appear in the lhs and rhs - /// respectively. - pred_to_clauses: PrdMap< (ClsSet, ClsSet) >, - /// Unsat flag. - is_unsat: bool, - /// Set of positive clauses. - /// - /// Only available after finalize. - pos_clauses: ClsSet, - /// Set of strictly negative clauses. - /// - /// A clause is strictly negative if it has strictly one predicate - /// application, and it's in the clause's body. Only available after - /// finalize. - strict_neg_clauses: ClsSet, - /// Set of non-strictly negative clauses. - /// - /// A clause is strictly negative if it has strictly one predicate - /// application, and it's in the clause's body. Only available after - /// finalize. - non_strict_neg_clauses: ClsSet, - /// Set of (non-strictly) negative clauses. - /// - /// Super set of strictly negative clauses. Only available after finalize. - neg_clauses: ClsSet, - /// Set of implication clauses. - /// - /// Only available after finalize. - imp_clauses: ClsSet, - /// True if finalized already ran. - is_finalized: bool, - /// If this instance is the result of a split, contains the index of the - /// clause of the original instance that the split was on. - /// - /// The constructor sets this to `None`. Function `clone_with_clauses` - /// automatically sets it to the clause kept. - split: Option, - - /// Define-funs parsed. - define_funs: HashMap, - - /// Maps **original** clause indexes to their optional name. - old_names: ClsHMap, - - /// Print success. - /// - /// Can only be set by `(set-option :print-success true)`. - print_success: bool, - /// Unsat core production. - /// - /// Can only be set by `(set-option :produce-unsat-cores true)`. - unsat_cores: bool, - /// Unsat core production. - /// - /// Can only be set by `(set-option :produce-proofs true)`. - proofs: bool, + /// Predicates. + preds: PrdInfos, + /// Original predicates, for reconstruction. + /// + /// Stores the original signature of the predicates, and a map from the + /// variables of `preds` to the original signature. + old_preds: PrdMap<(Sig, VarMap)>, + /// Maps new variables to the new ones. + /// + /// Only available after finalize. + old_var_maps: PrdMap>, + /// Predicates for which a suitable term has been found. + pred_terms: PrdMap>, + + /// Predicates defined in `pred_terms`, sorted by predicate dependencies. + /// + /// Populated by the `finalize` function. + sorted_pred_terms: Vec, + + /// Strengthener for predicates. + pred_str: PrdMap>, + + /// Companion function for predicates. + /// + /// A companion function is a function that was created internally + /// specifically for a predicate. Meaning it needs to be given to the user + /// when printing the model. + companion_funs: PrdHMap>, + + /// Side-clauses. + /// + /// A side clause + /// - does not mention any predicate + /// - mentions a user-defined function + /// + /// It will be asserted when the instance is asked to initialize a solver. A + /// side-clause is viewed as additional information provided by the user, a + /// kind of lemma for the actual clauses. + side_clauses: Vec, + + /// Clauses. + clauses: ClsMap, + /// Maps predicates to the clauses where they appear in the lhs and rhs + /// respectively. + pred_to_clauses: PrdMap<(ClsSet, ClsSet)>, + /// Unsat flag. + is_unsat: bool, + /// Set of positive clauses. + /// + /// Only available after finalize. + pos_clauses: ClsSet, + /// Set of strictly negative clauses. + /// + /// A clause is strictly negative if it has strictly one predicate + /// application, and it's in the clause's body. Only available after + /// finalize. + strict_neg_clauses: ClsSet, + /// Set of non-strictly negative clauses. + /// + /// A clause is strictly negative if it has strictly one predicate + /// application, and it's in the clause's body. Only available after + /// finalize. + non_strict_neg_clauses: ClsSet, + /// Set of (non-strictly) negative clauses. + /// + /// Super set of strictly negative clauses. Only available after finalize. + neg_clauses: ClsSet, + /// Set of implication clauses. + /// + /// Only available after finalize. + imp_clauses: ClsSet, + /// True if finalized already ran. + is_finalized: bool, + /// If this instance is the result of a split, contains the index of the + /// clause of the original instance that the split was on. + /// + /// The constructor sets this to `None`. Function `clone_with_clauses` + /// automatically sets it to the clause kept. + split: Option, + + /// Define-funs parsed. + define_funs: HashMap, + + /// Maps **original** clause indexes to their optional name. + old_names: ClsHMap, + + /// Print success. + /// + /// Can only be set by `(set-option :print-success true)`. + print_success: bool, + /// Unsat core production. + /// + /// Can only be set by `(set-option :produce-unsat-cores true)`. + unsat_cores: bool, + /// Unsat core production. + /// + /// Can only be set by `(set-option :produce-proofs true)`. + proofs: bool, } impl Default for Instance { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl Instance { - /// Instance constructor. - pub fn new() -> Instance { - let pred_capa = conf.instance.pred_capa ; - let clause_capa = conf.instance.clause_capa ; - Instance { - preds: PrdMap::with_capacity(pred_capa), - old_preds: PrdMap::with_capacity(pred_capa), - old_var_maps: PrdMap::with_capacity(pred_capa), - pred_terms: PrdMap::with_capacity(pred_capa), - sorted_pred_terms: Vec::with_capacity(pred_capa), - pred_str: PrdMap::with_capacity(pred_capa), - companion_funs: PrdHMap::new(), - - side_clauses: Vec::with_capacity(7), - clauses: ClsMap::with_capacity(clause_capa), - // clusters: CtrMap::with_capacity( clause_capa / 3 ), - pred_to_clauses: PrdMap::with_capacity(pred_capa), - is_unsat: false, - pos_clauses: ClsSet::new(), - strict_neg_clauses: ClsSet::new(), - non_strict_neg_clauses: ClsSet::new(), - neg_clauses: ClsSet::new(), - imp_clauses: ClsSet::new(), - is_finalized: false, - split: None, - define_funs: HashMap::new(), - old_names: ClsHMap::with_capacity(clause_capa), - print_success: false, - unsat_cores: false, - proofs: false, - } - } - - /// Clones itself. - /// - /// This is only used when splitting. `clause` will be remembered as the - /// clause the split is on. - /// - /// Fails (in debug) if `clause` is not a negative clause of `self` or if - /// `self` is not finalized. - pub fn clone_with_clauses(& self, clause: ClsIdx) -> Self { - debug_assert! { self.neg_clauses.contains(& clause) } - debug_assert! { self.is_finalized } - - Instance { - preds: self.preds.clone(), - old_preds: self.old_preds.clone(), - old_var_maps: PrdMap::with_capacity(self.old_preds.len()), - pred_terms: self.pred_terms.clone(), - sorted_pred_terms: Vec::with_capacity( self.preds.len() ), - pred_str: vec! [ None ; self.old_preds.len() ].into(), - companion_funs: self.companion_funs.clone(), - - side_clauses: self.side_clauses.clone(), - clauses: self.clauses.clone(), - pred_to_clauses: self.pred_to_clauses.clone(), - is_unsat: false, - pos_clauses: ClsSet::new(), - strict_neg_clauses: ClsSet::new(), - non_strict_neg_clauses: ClsSet::new(), - neg_clauses: ClsSet::new(), - imp_clauses: ClsSet::new(), - is_finalized: false, - split: Some(clause), - define_funs: self.define_funs.clone(), - old_names: self.old_names.clone(), - print_success: false, - unsat_cores: false, - proofs: false, - } - } - - /// Set of positive clauses. - /// - /// Only available after finalize. - pub fn pos_clauses(& self) -> & ClsSet { - & self.pos_clauses - } - /// Set of negative clauses with exactly one predicate application. - /// - /// Only available after finalize. - pub fn strict_neg_clauses(& self) -> & ClsSet { - & self.strict_neg_clauses - } - /// Set of negative clauses. - /// - /// Only available after finalize. - pub fn neg_clauses(& self) -> & ClsSet { - & self.neg_clauses - } - /// Set of non-strict negative clauses. - /// - /// Only available after finalize. - pub fn non_strict_neg_clauses(& self) -> & ClsSet { - & self.non_strict_neg_clauses - } - /// Set of implication clauses ad negative clausesh. - /// - /// Only available after finalize. - pub fn imp_clauses(& self) -> & ClsSet { - & self.imp_clauses - } - - /// Number of active (not forced) predicates. - pub fn active_pred_count(& self) -> usize { - let mut count = 0 ; - for pred in self.pred_indices() { - if ! self.is_known(pred) { count += 1 } - } - count - } - - /// Returns true if the instance is already solved. - pub fn is_solved(& self) -> bool { - if self.is_unsat { return true } - for def in & self.pred_terms { - if def.is_none() { return false } - } - true - } - - /// Map from the original signature of a predicate. - pub fn map_from_original_sig_of( - & self, pred: PrdIdx - ) -> VarHMap { - let mut res = VarHMap::with_capacity( self.old_preds[pred].1.len() ) ; - for (tgt, src) in self.old_preds[pred].1.index_iter() { - res.insert( - * src, term::var(tgt, self.old_preds[pred].0[* src].clone()) - ) ; - } - res - } - - /// Original signature of a predicate. - pub fn original_sig_of(& self, pred: PrdIdx) -> & Sig { - & self.old_preds[pred].0 - } - /// Map to the original signature of a predicate. - pub fn map_to_original_sig_of(& self, pred: PrdIdx) -> & VarMap { - & self.old_preds[pred].1 - } - - /// If this instance is the result of a split, returns the index of the - /// clause of the original instance that the split was on. - /// - /// Used mainly to create different folders for log files when splitting. - pub fn split(& self) -> Option { self.split } - - /// True if the unsat flag is set. - pub fn is_unsat(& self) -> bool { - self.is_unsat - } - - /// Sets the unsat flag in the instance. - pub fn set_unsat(& mut self) { - self.is_unsat = true - } - - /// True if a predicate is forced to something. - #[inline] - pub fn is_known(& self, pred: PrdIdx) -> bool { - self.pred_terms[pred].is_some() - } - - /// Adds a define fun. - pub fn add_define_fun>( - & mut self, name: S, sig: VarInfos, body: ::parse::PTTerms - ) -> Option<(VarInfos, ::parse::PTTerms)> { - self.define_funs.insert(name.into(), (sig, body)) - } - /// Retrieves a define fun. - pub fn get_define_fun( - & self, name: & str - ) -> Option<& (VarInfos, ::parse::PTTerms)> { - self.define_funs.get(name) - } - - /// Returns the model corresponding to the input predicates and the forced - /// predicates. - /// - /// The model is sorted in topological order. - pub fn model_of(& self, candidates: Candidates) -> Res { - use std::iter::Extend ; - - let mut model = Model::with_capacity( self.preds.len() ) ; - model.extend( - candidates.into_index_iter().filter_map( - |(pred, tterms_opt)| tterms_opt.map( - |term| { - let (term, _) = term.subst(& self.old_var_maps[pred]) ; - (pred, TTerms::of_term(None, term)) - } - ) - ) - ) ; - - for pred in & self.sorted_pred_terms { - let pred = * pred ; - if let Some(ref tterms) = self.pred_terms[pred] { - model.push( - (pred, tterms.subst(& self.old_var_maps[pred])) - ) - } else { - bail!("inconsistency in sorted forced predicates") - } - } - - Ok( model ) - } - - /// Returns the model corresponding to the input predicates and the forced - /// predicates. - /// - /// The model is sorted in topological order. - pub fn extend_model(& self, candidates: ConjCandidates) -> Res { - use std::iter::Extend ; - let mut model = ConjModel::with_capacity( self.preds.len() ) ; - let mut known_preds = PrdSet::new() ; - let mut tmp: Vec<_> = candidates.into_iter().map( - |(pred, conj)| { - let mut preds = PrdSet::new() ; - for tterms in & conj { - preds.extend( tterms.preds() ) - } - (pred, preds, conj) - } - ).collect() ; - let mut cnt ; - let mut changed ; - while ! tmp.is_empty() { - cnt = 0 ; - changed = false ; - while cnt < tmp.len() { - if tmp[cnt].1.iter().all( - |pred| known_preds.contains(pred) - ) { - changed = true ; - let (pred, _, dnf) = tmp.swap_remove(cnt) ; - let is_new = known_preds.insert(pred) ; - debug_assert! { is_new } - model.push( vec![ (pred, dnf) ] ) - } else { - cnt += 1 - } - } - if ! changed { - break - } - } - if ! tmp.is_empty() { - model.push( - tmp.into_iter().map( - |(pred, _, dnf)| (pred, dnf) - ).collect() - ) - } - - for pred in & self.sorted_pred_terms { - let pred = * pred ; - if let Some(ref tterms) = self.pred_terms[pred] { - let tterms = tterms.subst(& self.old_var_maps[pred]) ; - model.push( - vec![ (pred, vec![ tterms ]) ] - ) - } else { - bail!("inconsistency in sorted forced predicates") - } - } - Ok( model ) - } - - /// True if the instance is sat, false if unsat. - fn is_trivial(& self) -> Option { - if self.is_unsat { - Some(false) - } else if self.pred_terms.iter().all(|term| term.is_some()) { - Some(true) - } else { - None - } - } - - /// Returns a model for the instance when all the predicates have terms - /// assigned to them. - pub fn is_trivial_conj(& self) -> Res< - Option< MaybeModel > - > { - match self.is_trivial() { - None => Ok(None), - Some(false) => Ok( Some(MaybeModel::Unsat) ), - Some(true) => self.extend_model( - PrdHMap::new() - ).map( - |res| Some( MaybeModel::Model(res) ) - ), - } - } - - /// Returns a model for the instance when all the predicates have terms - /// assigned to them. - pub fn is_trivial_model(& self) -> Res< - Option< MaybeModel > - > { - match self.is_trivial() { - None => Ok(None), - Some(false) => Ok( Some(MaybeModel::Unsat) ), - Some(true) => self.model_of( - PrdMap::new() - ).map( - |res| Some( MaybeModel::Model(res) ) - ), - } - } - - - /// Lhs and rhs predicates of a clause. - #[inline] - pub fn preds_of_clause( - & self, clause: ClsIdx - ) -> (& PredApps, Option) { - ( - self[clause].lhs_preds(), self[clause].rhs().map(|(prd, _)| prd) - ) - } - - - /// Prints some top terms as a model. - /// - /// Meaning variables are printed with default printing: `` is - /// printed as `v_`. - pub fn print_tterms_as_model( - & self, w: & mut W, tterms: & TTerms - ) -> IoRes<()> { - tterms.write( - w, |w, var| var.default_write(w), - |w, pred, args| { - write!(w, "({}", self[pred]) ? ; - let mut prev: VarIdx = 0.into() ; - for (var, arg) in args.index_iter() { - let old_var = self.old_preds[pred].1[var] ; - for var in VarRange::new(prev, old_var) { - write!( - w, " {}", self.old_preds[pred].0[var].default_val() - ) ? - } - prev = old_var ; - prev.inc() ; - write!(w, " {}", arg) ? - } - for var in VarRange::new(prev, self.old_preds[pred].0.next_index()) { - write!( - w, " {}", self.old_preds[pred].0[var].default_val() - ) ? - } - write!(w, ")") - } - ) - } - - /// Finalizes instance creation. - /// - /// - shrinks all collections - /// - sorts forced predicates by dependencies - /// - /// # TO DO - /// - /// - optimize sorting of forced preds by dependencies (low priority) - pub fn finalize(& mut self) -> Res<()> { - if self.is_finalized { return Ok(()) } - self.is_finalized = true ; - - self.sorted_pred_terms.clear() ; - self.preds.shrink_to_fit() ; - self.old_preds.shrink_to_fit() ; - self.pred_terms.shrink_to_fit() ; - self.clauses.shrink_to_fit() ; - - let mut tmp: Vec< (PrdIdx, PrdSet) > = Vec::with_capacity( - self.preds.len() - ) ; - - for (idx, clause) in self.clauses.index_iter_mut() { - if clause.rhs().is_none() { - if clause.lhs_pred_apps_len() == 1 { - let is_new = self.strict_neg_clauses.insert(idx) ; - debug_assert! { is_new } + /// Instance constructor. + pub fn new() -> Instance { + let pred_capa = conf.instance.pred_capa; + let clause_capa = conf.instance.clause_capa; + Instance { + preds: PrdMap::with_capacity(pred_capa), + old_preds: PrdMap::with_capacity(pred_capa), + old_var_maps: PrdMap::with_capacity(pred_capa), + pred_terms: PrdMap::with_capacity(pred_capa), + sorted_pred_terms: Vec::with_capacity(pred_capa), + pred_str: PrdMap::with_capacity(pred_capa), + companion_funs: PrdHMap::new(), + + side_clauses: Vec::with_capacity(7), + clauses: ClsMap::with_capacity(clause_capa), + // clusters: CtrMap::with_capacity( clause_capa / 3 ), + pred_to_clauses: PrdMap::with_capacity(pred_capa), + is_unsat: false, + pos_clauses: ClsSet::new(), + strict_neg_clauses: ClsSet::new(), + non_strict_neg_clauses: ClsSet::new(), + neg_clauses: ClsSet::new(), + imp_clauses: ClsSet::new(), + is_finalized: false, + split: None, + define_funs: HashMap::new(), + old_names: ClsHMap::with_capacity(clause_capa), + print_success: false, + unsat_cores: false, + proofs: false, + } + } + + /// Clones itself. + /// + /// This is only used when splitting. `clause` will be remembered as the + /// clause the split is on. + /// + /// Fails (in debug) if `clause` is not a negative clause of `self` or if + /// `self` is not finalized. + pub fn clone_with_clauses(&self, clause: ClsIdx) -> Self { + debug_assert! { self.neg_clauses.contains(& clause) } + debug_assert! { self.is_finalized } + + Instance { + preds: self.preds.clone(), + old_preds: self.old_preds.clone(), + old_var_maps: PrdMap::with_capacity(self.old_preds.len()), + pred_terms: self.pred_terms.clone(), + sorted_pred_terms: Vec::with_capacity(self.preds.len()), + pred_str: vec![None; self.old_preds.len()].into(), + companion_funs: self.companion_funs.clone(), + + side_clauses: self.side_clauses.clone(), + clauses: self.clauses.clone(), + pred_to_clauses: self.pred_to_clauses.clone(), + is_unsat: false, + pos_clauses: ClsSet::new(), + strict_neg_clauses: ClsSet::new(), + non_strict_neg_clauses: ClsSet::new(), + neg_clauses: ClsSet::new(), + imp_clauses: ClsSet::new(), + is_finalized: false, + split: Some(clause), + define_funs: self.define_funs.clone(), + old_names: self.old_names.clone(), + print_success: false, + unsat_cores: false, + proofs: false, + } + } + + /// Set of positive clauses. + /// + /// Only available after finalize. + pub fn pos_clauses(&self) -> &ClsSet { + &self.pos_clauses + } + /// Set of negative clauses with exactly one predicate application. + /// + /// Only available after finalize. + pub fn strict_neg_clauses(&self) -> &ClsSet { + &self.strict_neg_clauses + } + /// Set of negative clauses. + /// + /// Only available after finalize. + pub fn neg_clauses(&self) -> &ClsSet { + &self.neg_clauses + } + /// Set of non-strict negative clauses. + /// + /// Only available after finalize. + pub fn non_strict_neg_clauses(&self) -> &ClsSet { + &self.non_strict_neg_clauses + } + /// Set of implication clauses ad negative clausesh. + /// + /// Only available after finalize. + pub fn imp_clauses(&self) -> &ClsSet { + &self.imp_clauses + } + + /// Number of active (not forced) predicates. + pub fn active_pred_count(&self) -> usize { + let mut count = 0; + for pred in self.pred_indices() { + if !self.is_known(pred) { + count += 1 + } + } + count + } + + /// Returns true if the instance is already solved. + pub fn is_solved(&self) -> bool { + if self.is_unsat { + return true; + } + for def in &self.pred_terms { + if def.is_none() { + return false; + } + } + true + } + + /// Map from the original signature of a predicate. + pub fn map_from_original_sig_of(&self, pred: PrdIdx) -> VarHMap { + let mut res = VarHMap::with_capacity(self.old_preds[pred].1.len()); + for (tgt, src) in self.old_preds[pred].1.index_iter() { + res.insert(*src, term::var(tgt, self.old_preds[pred].0[*src].clone())); + } + res + } + + /// Original signature of a predicate. + pub fn original_sig_of(&self, pred: PrdIdx) -> &Sig { + &self.old_preds[pred].0 + } + /// Map to the original signature of a predicate. + pub fn map_to_original_sig_of(&self, pred: PrdIdx) -> &VarMap { + &self.old_preds[pred].1 + } + + /// If this instance is the result of a split, returns the index of the + /// clause of the original instance that the split was on. + /// + /// Used mainly to create different folders for log files when splitting. + pub fn split(&self) -> Option { + self.split + } + + /// True if the unsat flag is set. + pub fn is_unsat(&self) -> bool { + self.is_unsat + } + + /// Sets the unsat flag in the instance. + pub fn set_unsat(&mut self) { + self.is_unsat = true + } + + /// True if a predicate is forced to something. + #[inline] + pub fn is_known(&self, pred: PrdIdx) -> bool { + self.pred_terms[pred].is_some() + } + + /// Adds a define fun. + pub fn add_define_fun>( + &mut self, + name: S, + sig: VarInfos, + body: ::parse::PTTerms, + ) -> Option<(VarInfos, ::parse::PTTerms)> { + self.define_funs.insert(name.into(), (sig, body)) + } + /// Retrieves a define fun. + pub fn get_define_fun(&self, name: &str) -> Option<&(VarInfos, ::parse::PTTerms)> { + self.define_funs.get(name) + } + + /// Returns the model corresponding to the input predicates and the forced + /// predicates. + /// + /// The model is sorted in topological order. + pub fn model_of(&self, candidates: Candidates) -> Res { + use std::iter::Extend; + + let mut model = Model::with_capacity(self.preds.len()); + model.extend( + candidates + .into_index_iter() + .filter_map(|(pred, tterms_opt)| { + tterms_opt.map(|term| { + let (term, _) = term.subst(&self.old_var_maps[pred]); + (pred, TTerms::of_term(None, term)) + }) + }), + ); + + for pred in &self.sorted_pred_terms { + let pred = *pred; + if let Some(ref tterms) = self.pred_terms[pred] { + model.push((pred, tterms.subst(&self.old_var_maps[pred]))) + } else { + bail!("inconsistency in sorted forced predicates") + } + } + + Ok(model) + } + + /// Returns the model corresponding to the input predicates and the forced + /// predicates. + /// + /// The model is sorted in topological order. + pub fn extend_model(&self, candidates: ConjCandidates) -> Res { + use std::iter::Extend; + let mut model = ConjModel::with_capacity(self.preds.len()); + let mut known_preds = PrdSet::new(); + let mut tmp: Vec<_> = candidates + .into_iter() + .map(|(pred, conj)| { + let mut preds = PrdSet::new(); + for tterms in &conj { + preds.extend(tterms.preds()) + } + (pred, preds, conj) + }).collect(); + let mut cnt; + let mut changed; + while !tmp.is_empty() { + cnt = 0; + changed = false; + while cnt < tmp.len() { + if tmp[cnt].1.iter().all(|pred| known_preds.contains(pred)) { + changed = true; + let (pred, _, dnf) = tmp.swap_remove(cnt); + let is_new = known_preds.insert(pred); + debug_assert! { is_new } + model.push(vec![(pred, dnf)]) + } else { + cnt += 1 + } + } + if !changed { + break; + } + } + if !tmp.is_empty() { + model.push(tmp.into_iter().map(|(pred, _, dnf)| (pred, dnf)).collect()) + } + + for pred in &self.sorted_pred_terms { + let pred = *pred; + if let Some(ref tterms) = self.pred_terms[pred] { + let tterms = tterms.subst(&self.old_var_maps[pred]); + model.push(vec![(pred, vec![tterms])]) + } else { + bail!("inconsistency in sorted forced predicates") + } + } + Ok(model) + } + + /// True if the instance is sat, false if unsat. + fn is_trivial(&self) -> Option { + if self.is_unsat { + Some(false) + } else if self.pred_terms.iter().all(|term| term.is_some()) { + Some(true) } else { - let is_new = self.non_strict_neg_clauses.insert(idx) ; - debug_assert! { is_new } - } - let is_new = self.neg_clauses.insert(idx) ; - debug_assert! { is_new } - } else if clause.lhs_preds().is_empty() { - if ! ( - self.is_unsat || clause.rhs().is_some() - ) { - bail!( - "{}\nfound a clause with no predicate during finalization", - clause.to_string_info(& self.preds).unwrap() - ) - } - let is_new = self.pos_clauses.insert(idx) ; - debug_assert! { is_new } - } else { - let is_new = self.imp_clauses.insert(idx) ; - debug_assert! { is_new } - } - } - - // Populate `tmp`. - let mut known_preds = PrdSet::with_capacity( self.preds.len() ) ; - - for pred in self.pred_indices() { - if let Some(ref tterms) = self.pred_terms[pred] { - tmp.push( (pred, tterms.preds()) ) - } else { - known_preds.insert(pred) ; - } - } - // Sort by dependencies. - while ! tmp.is_empty() { - let mut cnt = 0 ; // Will use swap remove. - 'find_preds: while cnt < tmp.len() { - for pred in & tmp[cnt].1 { - if ! known_preds.contains(pred) { - // Don't know this predicate, keep going in `tmp`. - cnt += 1 ; - continue 'find_preds - } + None } - // Reachable only we already have all of the current pred's - // dependencies. - let (pred, _) = tmp.swap_remove(cnt) ; - self.sorted_pred_terms.push(pred) ; - known_preds.insert(pred) ; - () // No `cnt` increment after swap remove. - } } - for (pred, info) in self.preds.index_iter() { - let mut map = VarMap::with_capacity( info.sig.len() ) ; - for (var, typ) in info.sig.index_iter() { - map.push( - term::var(self.old_preds[pred].1[var], typ.clone()) - ) - } - self.old_var_maps.push(map) - } - - self.sorted_pred_terms.shrink_to_fit() ; - - Ok(()) - } - - - /// Returns the term we already know works for a predicate, if any. - pub fn forced_terms_of(& self, pred: PrdIdx) -> Option<& TTerms> { - self.pred_terms[pred].as_ref() - } - - /// If the input predicate is forced to a constant boolean, returns its - /// value. - pub fn bool_value_of(& self, pred: PrdIdx) -> Option { - self.forced_terms_of(pred).and_then( - |terms| terms.bool() - ) - } - - /// Forced predicates in topological order. - #[inline] - pub fn sorted_forced_terms(& self) -> & Vec { - & self.sorted_pred_terms - } - - /// Returns the clauses in which the predicate appears in the lhs and rhs - /// respectively. - #[inline] - pub fn clauses_of(& self, pred: PrdIdx) -> (& ClsSet, & ClsSet) { - (& self.pred_to_clauses[pred].0, & self.pred_to_clauses[pred].1) - } - /// Returns the clauses in which `pred` appears in the lhs. - #[inline] - pub fn lhs_clauses_of(& self, pred: PrdIdx) -> & ClsSet { - & self.pred_to_clauses[pred].0 - } - /// Returns the clauses in which `pred` appears in the rhs. - #[inline] - pub fn rhs_clauses_of(& self, pred: PrdIdx) -> & ClsSet { - & self.pred_to_clauses[pred].1 - } - - /// Adds a predicate application to a clause's lhs. - pub fn clause_add_lhs_pred( - & mut self, clause: ClsIdx, pred: PrdIdx, args: VarMap - ) { - self.pred_to_clauses[pred].0.insert(clause) ; - self.clauses[clause].insert_pred_app( - pred, args.into() - ) ; - } - - /// Adds a term to a clause's lhs. - pub fn clause_add_lhs_term( - & mut self, clause: ClsIdx, term: Term - ) { - self.clauses[clause].insert_term(term) ; - } - - /// Forces the rhs of a clause to a predicate application. - pub fn clause_force_rhs( - & mut self, clause: ClsIdx, pred: PrdIdx, args: VarMap - ) -> Res<()> { - self.pred_to_clauses[pred].1.insert(clause) ; - self.clauses[clause].set_rhs( - pred, args.into() - ) - } - - /// Adds some terms to the lhs of a clause. - /// - /// Updates `pred_to_clauses`. - pub fn clause_lhs_extend>( - & mut self, clause_idx: ClsIdx, tterms: I - ) { - let clause = & mut self.clauses[clause_idx] ; - for tterm in tterms { - match tterm { - TTerm::P { pred, args } => { - self.pred_to_clauses[pred].0.insert(clause_idx) ; - clause.insert_pred_app(pred, args) ; - }, - TTerm::T(term) => { - clause.insert_term(term) ; - }, - } - } - } - - /// Replaces the rhs of a clause. - /// - /// Updates `pred_to_clauses` for the term it inserts but **not** the one it - /// removes. - pub fn clause_rhs_force( - & mut self, clause_idx: ClsIdx, tterm: TTerm - ) -> Res<()> { - let clause = & mut self.clauses[clause_idx] ; - match tterm { - TTerm::P { pred, args } => { - clause.set_rhs(pred, args) ? ; - let is_new = self.pred_to_clauses[pred].1.insert(clause_idx) ; - debug_assert!( is_new ) - }, - TTerm::T(term) => { - if term.bool() != Some(false) { - clause.insert_term( term::not(term) ) ; - } - clause.unset_rhs() ; - }, - } - Ok(()) - } - - /// Range over the predicate indices. - pub fn pred_indices(& self) -> PrdRange { - PrdRange::zero_to( self.preds.len() ) - } - /// Range over the clause indices. - pub fn clause_indices(& self) -> ClsRange { - ClsRange::zero_to( self.clauses.len() ) - } - - /// Predicate accessor. - pub fn preds(& self) -> & PrdInfos { - & self.preds - } - /// Clause accessor. - pub fn clauses(& self) -> & ClsMap { - & self.clauses - } - - /// Pushes a new predicate and returns its index. - pub fn push_pred( - & mut self, name: String, sig: Sig - ) -> PrdIdx { - let idx = self.preds.next_index() ; - let mut var_map = VarMap::with_capacity( sig.len() ) ; - for (var, _) in sig.index_iter() { - var_map.push(var) - } - self.old_preds.push( - (sig.clone(), var_map) - ) ; - self.preds.push( PrdInfo { - name, idx, sig - } ) ; - self.pred_terms.push(None) ; - self.pred_str.push(None) ; - - self.pred_to_clauses.push( - ( ClsSet::with_capacity(17), ClsSet::with_capacity(17) ) - ) ; - idx - } - - - /// Sets the strengthener for a predicate. - pub fn set_str(& mut self, pred: PrdIdx, term: Term) -> Option { - ::std::mem::replace( - & mut self.pred_str[pred], - Some(term) - ) - } - - /// Retrieves the strengthener for a predicate if any. - pub fn get_str(& self, pred: PrdIdx) -> Option<& Term> { - self.pred_str[pred].as_ref() - } - - /// Adds a companion function for a predicate. - pub fn add_companion_fun(& mut self, pred: PrdIdx, fun: Fun) { - self.companion_funs.entry(pred).or_insert_with( - Vec::new - ).push(fun) - } - - /// Retrieves the companion functions for a predicate. - pub fn get_companion_funs(& self, pred: PrdIdx) -> Option< & Vec > { - self.companion_funs.get(& pred) - } - - - /// Removes and returns the indices of the clauses `pred` appears in the lhs - /// of from `self.pred_to_clauses`. - fn unlink_pred_lhs(& mut self, pred: PrdIdx, lhs: & mut LHS) - where LHS: ::std::iter::Extend { - lhs.extend( self.pred_to_clauses[pred].0.drain() ) - } - - /// Removes and returns the indices of the clauses `pred` appears in the rhs - /// of from `self.pred_to_clauses`. - fn unlink_pred_rhs(& mut self, pred: PrdIdx, rhs: & mut RHS) - where RHS: ::std::iter::Extend { - rhs.extend( self.pred_to_clauses[pred].1.drain() ) - } - - /// Goes trough the predicates in `from`, and updates `pred_to_clauses` so - /// that they point to `to` instead. - fn relink_preds_to_clauses( - & mut self, from: ClsIdx, to: ClsIdx - ) -> Res<()> { - for pred in self.clauses[from].lhs_preds().keys() { - let pred = * pred ; - let was_there = self.pred_to_clauses[pred].0.remove(& from) ; - let _ = self.pred_to_clauses[pred].0.insert(to) ; - debug_assert!(was_there) - } - if let Some((pred, _)) = self.clauses[from].rhs() { - let was_there = self.pred_to_clauses[pred].1.remove(& from) ; - let _ = self.pred_to_clauses[pred].1.insert(to) ; - debug_assert!(was_there) - } - Ok(()) - } - - /// Forget some clauses. - /// - /// Duplicates are handled as if there was only one. - pub fn forget_clauses( - & mut self, clauses: & mut Vec - ) -> Res<()> { - // Forgetting is done by swap remove, we sort in DESCENDING order so that - // indices always make sense. - clauses.sort_unstable_by( - |c_1, c_2| c_2.cmp(c_1) - ) ; - let mut prev = None ; - for clause in clauses.drain(0..) { - log_debug!{ - " forgetting {}", self[clause].to_string_info(& self.preds) ? - } - if prev == Some(clause) { continue } - prev = Some(clause) ; - let _ = self.forget_clause(clause) ? ; - } - Ok(()) - } - - /// Forget a clause. **Does not preserve the order of the clauses.** - /// - /// After calling this function, clause indices kept outside of the instance - /// will in general refer to clauses different from the ones they pointed to - /// before the call. - /// - /// Also unlinks predicates from `pred_to_clauses`. - pub fn forget_clause(& mut self, clause: ClsIdx) -> Res { - for pred in self.clauses[clause].lhs_preds().keys() { - let pred = * pred ; - let was_there = self.pred_to_clauses[pred].0.remove(& clause) ; - debug_assert!( - was_there || self.is_known(pred) - ) - } - if let Some((pred, _)) = self.clauses[clause].rhs() { - self.pred_to_clauses[pred].1.remove(& clause) ; - } - // Relink the last clause as its index is going to be `clause`. Except if - // `clause` is already the last one. - let last_clause: ClsIdx = ( self.clauses.len() - 1 ).into() ; - if clause != last_clause { - self.relink_preds_to_clauses(last_clause, clause) ? - } - let res = self.clauses.swap_remove(clause) ; - Ok(res) - } - - /// First free clause index. - pub fn next_clause_index(& self) -> ClsIdx { - self.clauses.next_index() - } - - /// Pushes a new clause. - pub fn push_new_clause( - & mut self, vars: VarInfos, lhs: Vec, rhs: Option, - info: & 'static str - ) -> Res< Option > { - let idx = self.clauses.next_index() ; - let clause = clause::new(vars, lhs, rhs, info, idx) ; - self.push_clause(clause) - } - - /// The name of an original clause if any. - pub fn name_of_old_clause( - & self, cls: ClsIdx - ) -> Option<& String> { - self.old_names.get(& cls) - } - - /// Sets the name for an original clause. - pub fn set_old_clause_name( - & mut self, cls: ClsIdx, name: String - ) -> Res<()> { - let prev = self.old_names.insert(cls, name) ; - if let Some(prev) = prev { - bail!( - format!( - "trying to name clause #{}, but it's already called {}", - cls, conf.bad(& prev) + /// Returns a model for the instance when all the predicates have terms + /// assigned to them. + pub fn is_trivial_conj(&self) -> Res>> { + match self.is_trivial() { + None => Ok(None), + Some(false) => Ok(Some(MaybeModel::Unsat)), + Some(true) => self + .extend_model(PrdHMap::new()) + .map(|res| Some(MaybeModel::Model(res))), + } + } + + /// Returns a model for the instance when all the predicates have terms + /// assigned to them. + pub fn is_trivial_model(&self) -> Res>> { + match self.is_trivial() { + None => Ok(None), + Some(false) => Ok(Some(MaybeModel::Unsat)), + Some(true) => self + .model_of(PrdMap::new()) + .map(|res| Some(MaybeModel::Model(res))), + } + } + + /// Lhs and rhs predicates of a clause. + #[inline] + pub fn preds_of_clause(&self, clause: ClsIdx) -> (&PredApps, Option) { + ( + self[clause].lhs_preds(), + self[clause].rhs().map(|(prd, _)| prd), ) - ) - } - Ok(()) - } - - /// Mutable accessor for side clauses. - /// - /// Does not expose function invariants. - pub fn side_clauses_retain( - & mut self, mut keep: Keep - ) -> Res - where Keep: FnMut(& mut Clause) -> Res { - let mut info = RedInfo::new() ; - let mut cnt = 0 ; - while cnt < self.side_clauses.len() { - if ! keep(& mut self.side_clauses[cnt]) ? { - info.clauses_rmed += 1 ; - self.side_clauses.swap_remove(cnt) ; - () - } else { - cnt += 1 - } - } - Ok(info) - } - - /// Asserts all the side-clauses in a solver. - pub fn assert_side_clauses

( - & self, solver: & mut Solver

- ) -> Res<()> { - for side_clause in & self.side_clauses { - let side_clause = smt::SmtSideClause::new(side_clause) ; - solver.assert(& side_clause) ? - } - Ok(()) - } - - /// Registers a clause as a side-clause. - /// - /// A side clause is a clause that does not mention any predicate, but - /// mentions a user-defined function. - pub fn add_side_clause(& mut self, clause: Clause) -> Res<()> { - if clause.rhs().is_some() { - bail!("cannot convert to side-clause: predicate application in rhs") - } - if ! clause.lhs_preds().is_empty() { - bail!("cannot convert to side-clause: predicate application(s) in lhs") - } - - self.side_clauses.push(clause) ; - - Ok(()) - } - - /// Registers a new side clause: forces the term to be true. - /// - /// A side clause is a clause that does not mention any predicate, but - /// mentions a user-defined function. - pub fn add_new_side_clause( - & mut self, - vars: VarInfos, term: Term, info: & 'static str - ) -> Res<()> { - let clause = clause::new( - vars, vec![ TTerm::T( term::not(term) ) ], None, - info, self.side_clauses.len().into() - ) ; - - self.add_side_clause(clause) - } - - /// Pushes a clause. - /// - /// Returns its index, if it was added. - pub fn push_clause(& mut self, clause: Clause) -> Res< Option > { - for term in clause.lhs_terms() { - if let Some(false) = term.bool() { - return Ok(None) - } - } - - if clause.lhs_preds().is_empty() - && clause.rhs().is_none() - && clause.lhs_terms().iter().any( - |term| term.has_fun_app_or_adt() - ) { - self.add_side_clause(clause) ? ; - return Ok(None) - } - - let idx = self.clauses.next_index() ; - let is_new = self.push_clause_unchecked(clause) ; - // self.check("after `push_clause`") ? ; - Ok( if is_new { Some(idx) } else { None } ) - } - - /// True if the clause is redundant. - pub fn is_redundant(& self, idx: ClsIdx) -> bool { - let clause = & self.clauses[idx] ; - - if let Some((pred, _)) = clause.rhs() { - for i in & self.pred_to_clauses[pred].1 { - if * i != idx && self[* i].same_as(& clause) { - return true - } - } - } else if let Some((pred, _)) = clause.lhs_preds().iter().next() { - for i in & self.pred_to_clauses[* pred].0 { - if * i != idx && self[* i].same_as(& clause) { - return true - } - } - } else { - for (i, c) in self.clauses.index_iter() { - if i != idx && c.same_as(& clause) { return true } - } - } - false - } - - /// Pushes a new clause, does not sanity-check but redundancy-checks. - fn push_clause_unchecked(& mut self, clause: Clause) -> bool { - let clause_index = self.clauses.next_index() ; - self.clauses.push(clause) ; - - if self.is_redundant(clause_index) { - self.clauses.pop() ; - return false - } - - for pred in self.clauses[clause_index].lhs_preds().keys() { - let pred = * pred ; - let is_new = self.pred_to_clauses[pred].0.insert(clause_index) ; - debug_assert!(is_new) - } - if let Some((pred, _)) = self.clauses[clause_index].rhs() { - let is_new = self.pred_to_clauses[pred].1.insert(clause_index) ; - debug_assert!(is_new) - } - true - } - - - - /// Checks that the instance has no inconsistencies. - /// - /// Only active in debug. - #[cfg(not(debug_assertions))] - #[inline(always)] - pub fn check(& self, _: & 'static str) -> Res<()> { Ok(()) } - - #[cfg(debug_assertions)] - pub fn check(& self, s: & 'static str) -> Res<()> { - for clause in & self.clauses { - clause.check(s) ? - } - self.check_pred_to_clauses().chain_err( - || format!("while checking `{}`", conf.sad("pred_to_clauses")) - ).chain_err( - || format!("instance consistency check failed: {}", conf.emph(s)) - ) ? ; - self.check_preds_consistency() ? ; - - for (idx, clause) in self.clauses.index_iter() { - for term in clause.lhs_terms() { - if let Some(false) = term.bool() { - bail!( - "({}) found a trivial clause: #{} {}", s, idx, - clause.to_string_info(self.preds()).unwrap() - ) - } - } - - for pred in clause.lhs_preds().iter().map( - |(pred, _)| * pred - ).chain( clause.rhs().into_iter().map(|(pred, _)| pred) ) { - if let Some(tterms) = self.forced_terms_of(pred) { - bail! { - "predicate {} is forced{} but appears in a clause: {}", - conf.bad( & self[pred].name ), - match tterms.bool() { - Some(true) => " to true", - Some(false) => " to false", - None => "", + } + + /// Prints some top terms as a model. + /// + /// Meaning variables are printed with default printing: `` is + /// printed as `v_`. + pub fn print_tterms_as_model(&self, w: &mut W, tterms: &TTerms) -> IoRes<()> { + tterms.write( + w, + |w, var| var.default_write(w), + |w, pred, args| { + write!(w, "({}", self[pred])?; + let mut prev: VarIdx = 0.into(); + for (var, arg) in args.index_iter() { + let old_var = self.old_preds[pred].1[var]; + for var in VarRange::new(prev, old_var) { + write!(w, " {}", self.old_preds[pred].0[var].default_val())? + } + prev = old_var; + prev.inc(); + write!(w, " {}", arg)? + } + for var in VarRange::new(prev, self.old_preds[pred].0.next_index()) { + write!(w, " {}", self.old_preds[pred].0[var].default_val())? + } + write!(w, ")") }, - s - } - } - } + ) } - scoped! { - let mut clauses = self.clauses.iter() ; - while let Some(clause) = clauses.next() { - for c in clauses.clone() { - if clause.same_as(c) { - bail!( - "{}\n\n{}\n\nsame clause appears multiple times\n{}", - clause.to_string_info(self.preds()).unwrap(), - c.to_string_info(self.preds()).unwrap(), - conf.bad(s) - ) - } + /// Finalizes instance creation. + /// + /// - shrinks all collections + /// - sorts forced predicates by dependencies + /// + /// # TO DO + /// + /// - optimize sorting of forced preds by dependencies (low priority) + pub fn finalize(&mut self) -> Res<()> { + if self.is_finalized { + return Ok(()); } - } - } - - Ok(()) - } - - /// Checks `preds` and `old_preds` are consistent. - #[cfg(debug_assertions)] - fn check_preds_consistency(& self) -> Res<()> { - for (pred, info) in self.preds.index_iter() { - for (var, typ) in info.sig.index_iter() { - let (ref old_sig, ref var_map) = self.old_preds[pred] ; - if old_sig[ var_map[var] ] != * typ { - bail!( - "type inconsistency between current and original predicates:\n\ - on {}, variable {}: {} is routed to {}: {}", - self[pred], - var.default_str(), typ, - var_map[var].default_str(), old_sig[ var_map[var] ] - ) - } - } - } - Ok(()) - } - - /// Pretty printer for a set of clauses. - #[cfg(debug_assertions)] - fn pretty_clauses(& self, clauses: & ClsSet) -> String { - let mut s = String::new() ; - s.push('{') ; - for clause in clauses { - s.push(' ') ; - s.push_str(& format!("{}", clause)) - } - s.push(' ') ; - s.push('}') ; - s - } - - /// Checks the consistency of `pred_to_clauses`. - #[cfg(debug_assertions)] - fn check_pred_to_clauses(& self) -> Res<()> { - for (cls_idx, clause) in self.clauses.index_iter() { - for (pred, _) in clause.lhs_preds() { - let pred = * pred ; - if self.is_known(pred) { - bail!( - "predicate {} is forced but appears in lhs of clause {}", - self[pred], cls_idx - ) - } - if ! self.pred_to_clauses[pred].0.contains(& cls_idx) { - bail!( - "predicate {} appears in lhs of clause {} \ - but is not registered as such\n{}\nlhs: {}\nrhs: {}", - self[pred], cls_idx, - self.clauses[cls_idx].to_string_info(self.preds()) ?, - self.pretty_clauses( - & self.pred_to_clauses[pred].0 - ), self.pretty_clauses( - & self.pred_to_clauses[pred].1 - ) - ) - } - } - if let Some((pred, _)) = clause.rhs() { - if self.is_known(pred) { - bail!( - "predicate {} is forced but appears in rhs of clause {}", - self[pred], cls_idx - ) - } - if ! self.pred_to_clauses[pred].1.contains(& cls_idx) { - bail!( - "predicate {} appears in rhs of clause {} \ - but is not registered as such\n{}\nlhs: {}\nrhs: {}", - self[pred], cls_idx, - self.clauses[cls_idx].to_string_info(self.preds()) ?, - self.pretty_clauses( - & self.pred_to_clauses[pred].0 - ), self.pretty_clauses( - & self.pred_to_clauses[pred].1 - ) - ) - } - } - } - - for (pred, & (ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { - for clause in lhs { - if * clause >= self.clauses.len() { - bail!( - "predicate {} is registered as appearing in lhs of clause {} \ - which is above the maximal clause index", self[pred], clause - ) - } - if self.clauses[* clause].lhs_preds().get(& pred).is_none() { - bail!( - "predicate {} is registered as appearing in lhs of clause {} \ - but it's not the case\n{}\nlhs: {}\nrhs: {}", - self[pred], clause, - self.clauses[* clause].to_string_info(self.preds()) ?, - self.pretty_clauses( - & self.pred_to_clauses[pred].0 - ), self.pretty_clauses( - & self.pred_to_clauses[pred].1 - ) - ) - } - } - for clause in rhs { - if * clause >= self.clauses.len() { - bail!( - "predicate {} is registered as appearing in rhs of clause {} \ - which is above the maximal clause index", self[pred], clause - ) - } - if let Some((this_pred, _)) = self.clauses[* clause].rhs() { - if this_pred == pred { - continue - } + self.is_finalized = true; + + self.sorted_pred_terms.clear(); + self.preds.shrink_to_fit(); + self.old_preds.shrink_to_fit(); + self.pred_terms.shrink_to_fit(); + self.clauses.shrink_to_fit(); + + let mut tmp: Vec<(PrdIdx, PrdSet)> = Vec::with_capacity(self.preds.len()); + + for (idx, clause) in self.clauses.index_iter_mut() { + if clause.rhs().is_none() { + if clause.lhs_pred_apps_len() == 1 { + let is_new = self.strict_neg_clauses.insert(idx); + debug_assert! { is_new } + } else { + let is_new = self.non_strict_neg_clauses.insert(idx); + debug_assert! { is_new } + } + let is_new = self.neg_clauses.insert(idx); + debug_assert! { is_new } + } else if clause.lhs_preds().is_empty() { + if !(self.is_unsat || clause.rhs().is_some()) { + bail!( + "{}\nfound a clause with no predicate during finalization", + clause.to_string_info(&self.preds).unwrap() + ) + } + let is_new = self.pos_clauses.insert(idx); + debug_assert! { is_new } + } else { + let is_new = self.imp_clauses.insert(idx); + debug_assert! { is_new } + } } - bail!( - "predicate {} is registered to appear in rhs of clause {} \ - but it's not the case\n{}\nlhs: {}\nrhs: {}", - self[pred], clause, - self.clauses[* clause].to_string_info(self.preds()) ?, - self.pretty_clauses( - & self.pred_to_clauses[pred].0 - ), self.pretty_clauses( - & self.pred_to_clauses[pred].1 - ) - ) - } + + // Populate `tmp`. + let mut known_preds = PrdSet::with_capacity(self.preds.len()); + + for pred in self.pred_indices() { + if let Some(ref tterms) = self.pred_terms[pred] { + tmp.push((pred, tterms.preds())) + } else { + known_preds.insert(pred); + } + } + // Sort by dependencies. + while !tmp.is_empty() { + let mut cnt = 0; // Will use swap remove. + 'find_preds: while cnt < tmp.len() { + for pred in &tmp[cnt].1 { + if !known_preds.contains(pred) { + // Don't know this predicate, keep going in `tmp`. + cnt += 1; + continue 'find_preds; + } + } + // Reachable only we already have all of the current pred's + // dependencies. + let (pred, _) = tmp.swap_remove(cnt); + self.sorted_pred_terms.push(pred); + known_preds.insert(pred); + () // No `cnt` increment after swap remove. + } + } + + for (pred, info) in self.preds.index_iter() { + let mut map = VarMap::with_capacity(info.sig.len()); + for (var, typ) in info.sig.index_iter() { + map.push(term::var(self.old_preds[pred].1[var], typ.clone())) + } + self.old_var_maps.push(map) + } + + self.sorted_pred_terms.shrink_to_fit(); + + Ok(()) } - Ok(()) - } + /// Returns the term we already know works for a predicate, if any. + pub fn forced_terms_of(&self, pred: PrdIdx) -> Option<&TTerms> { + self.pred_terms[pred].as_ref() + } - /// Dumps the instance as an SMT-LIB 2 problem. - pub fn dump_as_smt2( - & self, w: & mut File, blah: Blah - ) -> Res<()> - where File: Write, Blah: AsRef { - let blah = blah.as_ref() ; + /// If the input predicate is forced to a constant boolean, returns its + /// value. + pub fn bool_value_of(&self, pred: PrdIdx) -> Option { + self.forced_terms_of(pred).and_then(|terms| terms.bool()) + } - for line in blah.lines() { - writeln!(w, "; {}", line) ? + /// Forced predicates in topological order. + #[inline] + pub fn sorted_forced_terms(&self) -> &Vec { + &self.sorted_pred_terms } - writeln!(w) ? ; - writeln!(w, "(set-logic HORN)") ? ; - writeln!(w) ? ; - writeln!(w, "; Datatypes") ? ; + /// Returns the clauses in which the predicate appears in the lhs and rhs + /// respectively. + #[inline] + pub fn clauses_of(&self, pred: PrdIdx) -> (&ClsSet, &ClsSet) { + (&self.pred_to_clauses[pred].0, &self.pred_to_clauses[pred].1) + } + /// Returns the clauses in which `pred` appears in the lhs. + #[inline] + pub fn lhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { + &self.pred_to_clauses[pred].0 + } + /// Returns the clauses in which `pred` appears in the rhs. + #[inline] + pub fn rhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { + &self.pred_to_clauses[pred].1 + } - dtyp::write_all(w, "") ? ; + /// Adds a predicate application to a clause's lhs. + pub fn clause_add_lhs_pred(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarMap) { + self.pred_to_clauses[pred].0.insert(clause); + self.clauses[clause].insert_pred_app(pred, args.into()); + } - dtyp::write_constructor_map(w, "; ") ? ; - writeln!(w) ? ; + /// Adds a term to a clause's lhs. + pub fn clause_add_lhs_term(&mut self, clause: ClsIdx, term: Term) { + self.clauses[clause].insert_term(term); + } - writeln!(w, "; Functions") ? ; - fun::write_all(w, "", true) ? ; + /// Forces the rhs of a clause to a predicate application. + pub fn clause_force_rhs( + &mut self, + clause: ClsIdx, + pred: PrdIdx, + args: VarMap, + ) -> Res<()> { + self.pred_to_clauses[pred].1.insert(clause); + self.clauses[clause].set_rhs(pred, args.into()) + } - writeln!(w) ? ; + /// Adds some terms to the lhs of a clause. + /// + /// Updates `pred_to_clauses`. + pub fn clause_lhs_extend>( + &mut self, + clause_idx: ClsIdx, + tterms: I, + ) { + let clause = &mut self.clauses[clause_idx]; + for tterm in tterms { + match tterm { + TTerm::P { pred, args } => { + self.pred_to_clauses[pred].0.insert(clause_idx); + clause.insert_pred_app(pred, args); + } + TTerm::T(term) => { + clause.insert_term(term); + } + } + } + } + + /// Replaces the rhs of a clause. + /// + /// Updates `pred_to_clauses` for the term it inserts but **not** the one it + /// removes. + pub fn clause_rhs_force(&mut self, clause_idx: ClsIdx, tterm: TTerm) -> Res<()> { + let clause = &mut self.clauses[clause_idx]; + match tterm { + TTerm::P { pred, args } => { + clause.set_rhs(pred, args)?; + let is_new = self.pred_to_clauses[pred].1.insert(clause_idx); + debug_assert!(is_new) + } + TTerm::T(term) => { + if term.bool() != Some(false) { + clause.insert_term(term::not(term)); + } + clause.unset_rhs(); + } + } + Ok(()) + } - writeln!(w, "; Side-clauses") ? ; - for side_clause in & self.side_clauses { - side_clause.write( - w, |w, var_info| write!(w, "{}", var_info.name), |_, _, _| panic!( - "illegal side-clause: found predicate application(s)" - ), true - ) ? ; - writeln!(w) ? ; + /// Range over the predicate indices. + pub fn pred_indices(&self) -> PrdRange { + PrdRange::zero_to(self.preds.len()) + } + /// Range over the clause indices. + pub fn clause_indices(&self) -> ClsRange { + ClsRange::zero_to(self.clauses.len()) } - writeln!(w) ? ; - writeln!(w) ? ; + /// Predicate accessor. + pub fn preds(&self) -> &PrdInfos { + &self.preds + } + /// Clause accessor. + pub fn clauses(&self) -> &ClsMap { + &self.clauses + } - for (pred_idx, pred) in self.preds.index_iter() { - if self.pred_terms[pred_idx].is_none() { - write!( - w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name - ) ? ; - for typ in & pred.sig { - write!(w, " {}", typ) ? + /// Pushes a new predicate and returns its index. + pub fn push_pred(&mut self, name: String, sig: Sig) -> PrdIdx { + let idx = self.preds.next_index(); + let mut var_map = VarMap::with_capacity(sig.len()); + for (var, _) in sig.index_iter() { + var_map.push(var) } - writeln!(w, " ) Bool\n)") ? - } + self.old_preds.push((sig.clone(), var_map)); + self.preds.push(PrdInfo { name, idx, sig }); + self.pred_terms.push(None); + self.pred_str.push(None); + + self.pred_to_clauses + .push((ClsSet::with_capacity(17), ClsSet::with_capacity(17))); + idx } - writeln!( - w, "\n; Original clauses' names ({}) {{", self.old_names.len() - ) ? ; - for (idx, name) in & self.old_names { - writeln!(w, "; #{}: `{}`.", idx, name) ? + /// Sets the strengthener for a predicate. + pub fn set_str(&mut self, pred: PrdIdx, term: Term) -> Option { + ::std::mem::replace(&mut self.pred_str[pred], Some(term)) } - writeln!(w, "; }}") ? ; - for (idx, clause) in self.clauses.index_iter() { - writeln!(w, "\n; Clause #{}", idx) ? ; + /// Retrieves the strengthener for a predicate if any. + pub fn get_str(&self, pred: PrdIdx) -> Option<&Term> { + self.pred_str[pred].as_ref() + } - // Print source. - let from = clause.from() ; - write!(w, "; from: #{}", from) ? ; - if let Some(name) = self.old_names.get(& from) { - write!(w, ": {}", name) ? - } - writeln!(w) ? ; + /// Adds a companion function for a predicate. + pub fn add_companion_fun(&mut self, pred: PrdIdx, fun: Fun) { + self.companion_funs + .entry(pred) + .or_insert_with(Vec::new) + .push(fun) + } - clause.write( - w, |w, var_info| write!(w, "{}", var_info.name), |w, p, args| { - if ! args.is_empty() { - write!(w, "(") ? - } - w.write_all( self[p].name.as_bytes() ) ? ; - for arg in args.iter() { - write!(w, " ") ? ; - arg.write(w, |w, var| write!(w, "{}", clause.vars[var])) ? - } - if ! args.is_empty() { - write!(w, ")") - } else { - Ok(()) - } - }, true - ) ? ; - writeln!(w) ? ; - writeln!(w) ? + /// Retrieves the companion functions for a predicate. + pub fn get_companion_funs(&self, pred: PrdIdx) -> Option<&Vec> { + self.companion_funs.get(&pred) } - writeln!(w, "\n(check-sat)") ? ; + /// Removes and returns the indices of the clauses `pred` appears in the lhs + /// of from `self.pred_to_clauses`. + fn unlink_pred_lhs(&mut self, pred: PrdIdx, lhs: &mut LHS) + where + LHS: ::std::iter::Extend, + { + lhs.extend(self.pred_to_clauses[pred].0.drain()) + } - Ok(()) - } + /// Removes and returns the indices of the clauses `pred` appears in the rhs + /// of from `self.pred_to_clauses`. + fn unlink_pred_rhs(&mut self, pred: PrdIdx, rhs: &mut RHS) + where + RHS: ::std::iter::Extend, + { + rhs.extend(self.pred_to_clauses[pred].1.drain()) + } + /// Goes trough the predicates in `from`, and updates `pred_to_clauses` so + /// that they point to `to` instead. + fn relink_preds_to_clauses(&mut self, from: ClsIdx, to: ClsIdx) -> Res<()> { + for pred in self.clauses[from].lhs_preds().keys() { + let pred = *pred; + let was_there = self.pred_to_clauses[pred].0.remove(&from); + let _ = self.pred_to_clauses[pred].0.insert(to); + debug_assert!(was_there) + } + if let Some((pred, _)) = self.clauses[from].rhs() { + let was_there = self.pred_to_clauses[pred].1.remove(&from); + let _ = self.pred_to_clauses[pred].1.insert(to); + debug_assert!(was_there) + } + Ok(()) + } + + /// Forget some clauses. + /// + /// Duplicates are handled as if there was only one. + pub fn forget_clauses(&mut self, clauses: &mut Vec) -> Res<()> { + // Forgetting is done by swap remove, we sort in DESCENDING order so that + // indices always make sense. + clauses.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); + let mut prev = None; + for clause in clauses.drain(0..) { + log_debug!{ + " forgetting {}", self[clause].to_string_info(& self.preds) ? + } + if prev == Some(clause) { + continue; + } + prev = Some(clause); + let _ = self.forget_clause(clause)?; + } + Ok(()) + } + + /// Forget a clause. **Does not preserve the order of the clauses.** + /// + /// After calling this function, clause indices kept outside of the instance + /// will in general refer to clauses different from the ones they pointed to + /// before the call. + /// + /// Also unlinks predicates from `pred_to_clauses`. + pub fn forget_clause(&mut self, clause: ClsIdx) -> Res { + for pred in self.clauses[clause].lhs_preds().keys() { + let pred = *pred; + let was_there = self.pred_to_clauses[pred].0.remove(&clause); + debug_assert!(was_there || self.is_known(pred)) + } + if let Some((pred, _)) = self.clauses[clause].rhs() { + self.pred_to_clauses[pred].1.remove(&clause); + } + // Relink the last clause as its index is going to be `clause`. Except if + // `clause` is already the last one. + let last_clause: ClsIdx = (self.clauses.len() - 1).into(); + if clause != last_clause { + self.relink_preds_to_clauses(last_clause, clause)? + } + let res = self.clauses.swap_remove(clause); + Ok(res) + } + + /// First free clause index. + pub fn next_clause_index(&self) -> ClsIdx { + self.clauses.next_index() + } + + /// Pushes a new clause. + pub fn push_new_clause( + &mut self, + vars: VarInfos, + lhs: Vec, + rhs: Option, + info: &'static str, + ) -> Res> { + let idx = self.clauses.next_index(); + let clause = clause::new(vars, lhs, rhs, info, idx); + self.push_clause(clause) + } + + /// The name of an original clause if any. + pub fn name_of_old_clause(&self, cls: ClsIdx) -> Option<&String> { + self.old_names.get(&cls) + } + + /// Sets the name for an original clause. + pub fn set_old_clause_name(&mut self, cls: ClsIdx, name: String) -> Res<()> { + let prev = self.old_names.insert(cls, name); + if let Some(prev) = prev { + bail!(format!( + "trying to name clause #{}, but it's already called {}", + cls, + conf.bad(&prev) + )) + } + Ok(()) + } + + /// Mutable accessor for side clauses. + /// + /// Does not expose function invariants. + pub fn side_clauses_retain(&mut self, mut keep: Keep) -> Res + where + Keep: FnMut(&mut Clause) -> Res, + { + let mut info = RedInfo::new(); + let mut cnt = 0; + while cnt < self.side_clauses.len() { + if !keep(&mut self.side_clauses[cnt])? { + info.clauses_rmed += 1; + self.side_clauses.swap_remove(cnt); + () + } else { + cnt += 1 + } + } + Ok(info) + } - /// Simplifies some predicate definitions. - /// - /// Simplifies its internal predicate definitions and the ones in the model. - pub fn simplify_pred_defs(& mut self, model: & mut Model) -> Res<()> { - let mut old_model = Vec::with_capacity( model.len() ) ; - ::std::mem::swap( & mut old_model, model ) ; - for (pred, def) in old_model { - let simplified = def.simplify_pred_apps(& model, & self.pred_terms) ; - model.push( (pred, simplified) ) + /// Asserts all the side-clauses in a solver. + pub fn assert_side_clauses

(&self, solver: &mut Solver

) -> Res<()> { + for side_clause in &self.side_clauses { + let side_clause = smt::SmtSideClause::new(side_clause); + solver.assert(&side_clause)? + } + Ok(()) } - if self.sorted_pred_terms.is_empty() { - self.finalize() ? + /// Registers a clause as a side-clause. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_side_clause(&mut self, clause: Clause) -> Res<()> { + if clause.rhs().is_some() { + bail!("cannot convert to side-clause: predicate application in rhs") + } + if !clause.lhs_preds().is_empty() { + bail!("cannot convert to side-clause: predicate application(s) in lhs") + } + + self.side_clauses.push(clause); + + Ok(()) + } + + /// Registers a new side clause: forces the term to be true. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_new_side_clause( + &mut self, + vars: VarInfos, + term: Term, + info: &'static str, + ) -> Res<()> { + let clause = clause::new( + vars, + vec![TTerm::T(term::not(term))], + None, + info, + self.side_clauses.len().into(), + ); + + self.add_side_clause(clause) + } + + /// Pushes a clause. + /// + /// Returns its index, if it was added. + pub fn push_clause(&mut self, clause: Clause) -> Res> { + for term in clause.lhs_terms() { + if let Some(false) = term.bool() { + return Ok(None); + } + } + + if clause.lhs_preds().is_empty() && clause.rhs().is_none() && clause + .lhs_terms() + .iter() + .any(|term| term.has_fun_app_or_adt()) + { + self.add_side_clause(clause)?; + return Ok(None); + } + + let idx = self.clauses.next_index(); + let is_new = self.push_clause_unchecked(clause); + // self.check("after `push_clause`") ? ; + Ok(if is_new { Some(idx) } else { None }) } - let mut old_tterms: PrdMap> = vec![ - None ; self.pred_terms.len() - ].into() ; - ::std::mem::swap( & mut old_tterms, & mut self.pred_terms ) ; - for pred in & self.sorted_pred_terms { - let mut curr_def = None ; - ::std::mem::swap(& mut curr_def, & mut old_tterms[* pred]) ; - if let Some(def) = curr_def { - let simplified = def.simplify_pred_apps(& model, & self.pred_terms) ; - self.pred_terms[* pred] = Some(simplified) - } + /// True if the clause is redundant. + pub fn is_redundant(&self, idx: ClsIdx) -> bool { + let clause = &self.clauses[idx]; + + if let Some((pred, _)) = clause.rhs() { + for i in &self.pred_to_clauses[pred].1 { + if *i != idx && self[*i].same_as(&clause) { + return true; + } + } + } else if let Some((pred, _)) = clause.lhs_preds().iter().next() { + for i in &self.pred_to_clauses[*pred].0 { + if *i != idx && self[*i].same_as(&clause) { + return true; + } + } + } else { + for (i, c) in self.clauses.index_iter() { + if i != idx && c.same_as(&clause) { + return true; + } + } + } + false } - Ok(()) - } + /// Pushes a new clause, does not sanity-check but redundancy-checks. + fn push_clause_unchecked(&mut self, clause: Clause) -> bool { + let clause_index = self.clauses.next_index(); + self.clauses.push(clause); - /// Writes a conjunction of top terms. - pub fn write_tterms_conj( - & self, w: & mut W, conj: & [ TTerms ] - ) -> Res<()> { - if conj.is_empty() { - write!(w, "true") ? - } else if conj.len() == 1 { - self.print_tterms_as_model(w, & conj[0]) ? - } else { - write!(w, "(and") ? ; - for tterms in conj { - write!(w, " ") ? ; - self.print_tterms_as_model(w, tterms) ? - } - write!(w, ")") ? + if self.is_redundant(clause_index) { + self.clauses.pop(); + return false; + } + + for pred in self.clauses[clause_index].lhs_preds().keys() { + let pred = *pred; + let is_new = self.pred_to_clauses[pred].0.insert(clause_index); + debug_assert!(is_new) + } + if let Some((pred, _)) = self.clauses[clause_index].rhs() { + let is_new = self.pred_to_clauses[pred].1.insert(clause_index); + debug_assert!(is_new) + } + true } - Ok(()) - } + /// Checks that the instance has no inconsistencies. + /// + /// Only active in debug. + #[cfg(not(debug_assertions))] + #[inline(always)] + pub fn check(&self, _: &'static str) -> Res<()> { + Ok(()) + } - /// Writes a predicate signature. - /// - /// Does not write the name of the predicate. - pub fn write_pred_sig( - & self, w: & mut W, pred: PrdIdx - ) -> Res<()> { - let (ref old_sig, _) = self.old_preds[pred] ; - write!(w, "(") ?; - for (var, typ) in old_sig.index_iter() { - write!(w, " ({} {})", var.default_str(), typ) ? + #[cfg(debug_assertions)] + pub fn check(&self, s: &'static str) -> Res<()> { + for clause in &self.clauses { + clause.check(s)? + } + self.check_pred_to_clauses() + .chain_err(|| format!("while checking `{}`", conf.sad("pred_to_clauses"))) + .chain_err(|| format!("instance consistency check failed: {}", conf.emph(s)))?; + self.check_preds_consistency()?; + + for (idx, clause) in self.clauses.index_iter() { + for term in clause.lhs_terms() { + if let Some(false) = term.bool() { + bail!( + "({}) found a trivial clause: #{} {}", + s, + idx, + clause.to_string_info(self.preds()).unwrap() + ) + } + } + + for pred in clause + .lhs_preds() + .iter() + .map(|(pred, _)| *pred) + .chain(clause.rhs().into_iter().map(|(pred, _)| pred)) + { + if let Some(tterms) = self.forced_terms_of(pred) { + bail! { + "predicate {} is forced{} but appears in a clause: {}", + conf.bad( & self[pred].name ), + match tterms.bool() { + Some(true) => " to true", + Some(false) => " to false", + None => "", + }, + s + } + } + } + } + + scoped! { + let mut clauses = self.clauses.iter() ; + while let Some(clause) = clauses.next() { + for c in clauses.clone() { + if clause.same_as(c) { + bail!( + "{}\n\n{}\n\nsame clause appears multiple times\n{}", + clause.to_string_info(self.preds()).unwrap(), + c.to_string_info(self.preds()).unwrap(), + conf.bad(s) + ) + } + } + } + } + + Ok(()) + } + + /// Checks `preds` and `old_preds` are consistent. + #[cfg(debug_assertions)] + fn check_preds_consistency(&self) -> Res<()> { + for (pred, info) in self.preds.index_iter() { + for (var, typ) in info.sig.index_iter() { + let (ref old_sig, ref var_map) = self.old_preds[pred]; + if old_sig[var_map[var]] != *typ { + bail!( + "type inconsistency between current and original predicates:\n\ + on {}, variable {}: {} is routed to {}: {}", + self[pred], + var.default_str(), + typ, + var_map[var].default_str(), + old_sig[var_map[var]] + ) + } + } + } + Ok(()) } - write!(w, " ) {}", typ::bool()) ? ; - Ok(()) - } + /// Pretty printer for a set of clauses. + #[cfg(debug_assertions)] + fn pretty_clauses(&self, clauses: &ClsSet) -> String { + let mut s = String::new(); + s.push('{'); + for clause in clauses { + s.push(' '); + s.push_str(&format!("{}", clause)) + } + s.push(' '); + s.push('}'); + s + } + + /// Checks the consistency of `pred_to_clauses`. + #[cfg(debug_assertions)] + fn check_pred_to_clauses(&self) -> Res<()> { + for (cls_idx, clause) in self.clauses.index_iter() { + for (pred, _) in clause.lhs_preds() { + let pred = *pred; + if self.is_known(pred) { + bail!( + "predicate {} is forced but appears in lhs of clause {}", + self[pred], + cls_idx + ) + } + if !self.pred_to_clauses[pred].0.contains(&cls_idx) { + bail!( + "predicate {} appears in lhs of clause {} \ + but is not registered as such\n{}\nlhs: {}\nrhs: {}", + self[pred], + cls_idx, + self.clauses[cls_idx].to_string_info(self.preds())?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + if let Some((pred, _)) = clause.rhs() { + if self.is_known(pred) { + bail!( + "predicate {} is forced but appears in rhs of clause {}", + self[pred], + cls_idx + ) + } + if !self.pred_to_clauses[pred].1.contains(&cls_idx) { + bail!( + "predicate {} appears in rhs of clause {} \ + but is not registered as such\n{}\nlhs: {}\nrhs: {}", + self[pred], + cls_idx, + self.clauses[cls_idx].to_string_info(self.preds())?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + } + for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { + for clause in lhs { + if *clause >= self.clauses.len() { + bail!( + "predicate {} is registered as appearing in lhs of clause {} \ + which is above the maximal clause index", + self[pred], + clause + ) + } + if self.clauses[*clause].lhs_preds().get(&pred).is_none() { + bail!( + "predicate {} is registered as appearing in lhs of clause {} \ + but it's not the case\n{}\nlhs: {}\nrhs: {}", + self[pred], + clause, + self.clauses[*clause].to_string_info(self.preds())?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + for clause in rhs { + if *clause >= self.clauses.len() { + bail!( + "predicate {} is registered as appearing in rhs of clause {} \ + which is above the maximal clause index", + self[pred], + clause + ) + } + if let Some((this_pred, _)) = self.clauses[*clause].rhs() { + if this_pred == pred { + continue; + } + } + bail!( + "predicate {} is registered to appear in rhs of clause {} \ + but it's not the case\n{}\nlhs: {}\nrhs: {}", + self[pred], + clause, + self.clauses[*clause].to_string_info(self.preds())?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + Ok(()) + } + + /// Dumps the instance as an SMT-LIB 2 problem. + pub fn dump_as_smt2(&self, w: &mut File, blah: Blah) -> Res<()> + where + File: Write, + Blah: AsRef, + { + let blah = blah.as_ref(); + + for line in blah.lines() { + writeln!(w, "; {}", line)? + } + writeln!(w)?; + writeln!(w, "(set-logic HORN)")?; + writeln!(w)?; + + writeln!(w, "; Datatypes")?; + + dtyp::write_all(w, "")?; + + dtyp::write_constructor_map(w, "; ")?; + writeln!(w)?; + + writeln!(w, "; Functions")?; + fun::write_all(w, "", true)?; - /// Writes some definitions. - pub fn write_definitions( - & self, w: & mut W, pref: & str, model: ConjModelRef - ) -> Res<()> { + writeln!(w)?; - fun::write_all(w, pref, false) ? ; + writeln!(w, "; Side-clauses")?; + for side_clause in &self.side_clauses { + side_clause.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |_, _, _| panic!("illegal side-clause: found predicate application(s)"), + true, + )?; + writeln!(w)?; + } - for defs in model { + writeln!(w)?; + writeln!(w)?; - if defs.is_empty() { - () - } else if defs.len() == 1 { - let (pred, ref tterms) = defs[0] ; + for (pred_idx, pred) in self.preds.index_iter() { + if self.pred_terms[pred_idx].is_none() { + write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; + for typ in &pred.sig { + write!(w, " {}", typ)? + } + writeln!(w, " ) Bool\n)")? + } + } writeln!( - w, "{}({} {}", pref, keywords::cmd::def_fun, self[pred].name - ) ? ; - write!(w, "{} ", pref) ?; - self.write_pred_sig(w, pred) ? ; - write!(w, "\n{} ", pref) ? ; - self.write_tterms_conj(w, tterms) ? ; - writeln!(w, "\n{})", pref) ? - - } else { - write!( - w, "{}({} (", pref, keywords::cmd::def_funs_rec - ) ? ; - for & (pred, _) in defs { - write!(w, "\n{} {} ", pref, self[pred].name) ? ; - self.write_pred_sig(w, pred) ? ; - } - write!(w, "\n{}) (", pref) ? ; - for & (_, ref tterms) in defs { - write!(w, "\n{} ", pref) ? ; - self.write_tterms_conj(w, tterms) ? ; - } - writeln!(w, "\n{}) )", pref) ? ; - } - } - - Ok(()) - } - - - /// Writes a model. - pub fn write_model( - & self, model: ConjModelRef, w: & mut W - ) -> Res<()> { - writeln!(w, "(model") ? ; - self.write_definitions(w, " ", model) ? ; - writeln!(w, ")") ? ; - Ok(()) - } - - - - - /// Sets print-success flag. - pub fn set_print_success(& mut self, b: bool) { - self.print_success = b - } - /// Print-success flag accessor. - pub fn print_success(& self) -> bool { - self.print_success - } - /// Sets unsat-cores flag. - pub fn set_unsat_cores(& mut self, b: bool) { - self.unsat_cores = b - } - /// Unsat-cores flag. - pub fn unsat_cores(& self) -> bool { - self.unsat_cores - } - /// Sets proofs flag. - pub fn set_proofs(& mut self, b: bool) { - self.proofs = b - } - /// Unsat-cores flag. - pub fn proofs(& self) -> bool { - self.proofs - } - - /// True if the teacher needs to maintain a sample graph (unsat - /// cores/proofs). - pub fn track_samples(& self) -> bool { - self.unsat_cores() || self.proofs() - } - - - /// Converts `"true"` to `true`, `"false"` to `false`, and everything else to - /// an error. - fn bool_of_str(s: & str) -> Res { - match s { - "true" => Ok(true), - "false" => Ok(false), - _ => bail!("expected boolean `true/false`, got `{}`", s), - } - } - - - - /// Sets an option. - pub fn set_option(& mut self, flag: & str, val: & str) -> Res<()> { - let flag_err = || format!("while handling set-option for {}", flag) ; - match flag { - "print-success" => self.set_print_success( - Self::bool_of_str(& val).chain_err(flag_err) ? - ), - "produce-unsat-cores" => self.set_unsat_cores( - Self::bool_of_str(& val).chain_err(flag_err) ? - ), - "produce-proofs" => self.set_proofs( - Self::bool_of_str(& val).chain_err(flag_err) ? - ), - _ => warn!( - "ignoring (set-option :{} {}): unknown flag {}", flag, val, flag - ), - } - Ok(()) - } + w, + "\n; Original clauses' names ({}) {{", + self.old_names.len() + )?; + for (idx, name) in &self.old_names { + writeln!(w, "; #{}: `{}`.", idx, name)? + } + writeln!(w, "; }}")?; -} + for (idx, clause) in self.clauses.index_iter() { + writeln!(w, "\n; Clause #{}", idx)?; + // Print source. + let from = clause.from(); + write!(w, "; from: #{}", from)?; + if let Some(name) = self.old_names.get(&from) { + write!(w, ": {}", name)? + } + writeln!(w)?; + + clause.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |w, p, args| { + if !args.is_empty() { + write!(w, "(")? + } + w.write_all(self[p].name.as_bytes())?; + for arg in args.iter() { + write!(w, " ")?; + arg.write(w, |w, var| write!(w, "{}", clause.vars[var]))? + } + if !args.is_empty() { + write!(w, ")") + } else { + Ok(()) + } + }, + true, + )?; + writeln!(w)?; + writeln!(w)? + } + + writeln!(w, "\n(check-sat)")?; + + Ok(()) + } + + /// Simplifies some predicate definitions. + /// + /// Simplifies its internal predicate definitions and the ones in the model. + pub fn simplify_pred_defs(&mut self, model: &mut Model) -> Res<()> { + let mut old_model = Vec::with_capacity(model.len()); + ::std::mem::swap(&mut old_model, model); + for (pred, def) in old_model { + let simplified = def.simplify_pred_apps(&model, &self.pred_terms); + model.push((pred, simplified)) + } + + if self.sorted_pred_terms.is_empty() { + self.finalize()? + } + + let mut old_tterms: PrdMap> = vec![None; self.pred_terms.len()].into(); + ::std::mem::swap(&mut old_tterms, &mut self.pred_terms); + for pred in &self.sorted_pred_terms { + let mut curr_def = None; + ::std::mem::swap(&mut curr_def, &mut old_tterms[*pred]); + if let Some(def) = curr_def { + let simplified = def.simplify_pred_apps(&model, &self.pred_terms); + self.pred_terms[*pred] = Some(simplified) + } + } + Ok(()) + } + + /// Writes a conjunction of top terms. + pub fn write_tterms_conj(&self, w: &mut W, conj: &[TTerms]) -> Res<()> { + if conj.is_empty() { + write!(w, "true")? + } else if conj.len() == 1 { + self.print_tterms_as_model(w, &conj[0])? + } else { + write!(w, "(and")?; + for tterms in conj { + write!(w, " ")?; + self.print_tterms_as_model(w, tterms)? + } + write!(w, ")")? + } + Ok(()) + } + + /// Writes a predicate signature. + /// + /// Does not write the name of the predicate. + pub fn write_pred_sig(&self, w: &mut W, pred: PrdIdx) -> Res<()> { + let (ref old_sig, _) = self.old_preds[pred]; + write!(w, "(")?; + for (var, typ) in old_sig.index_iter() { + write!(w, " ({} {})", var.default_str(), typ)? + } + write!(w, " ) {}", typ::bool())?; + Ok(()) + } + + /// Writes some definitions. + pub fn write_definitions( + &self, + w: &mut W, + pref: &str, + model: ConjModelRef, + ) -> Res<()> { + fun::write_all(w, pref, false)?; + + for defs in model { + if defs.is_empty() { + () + } else if defs.len() == 1 { + let (pred, ref tterms) = defs[0]; + + writeln!(w, "{}({} {}", pref, keywords::cmd::def_fun, self[pred].name)?; + write!(w, "{} ", pref)?; + self.write_pred_sig(w, pred)?; + write!(w, "\n{} ", pref)?; + self.write_tterms_conj(w, tterms)?; + writeln!(w, "\n{})", pref)? + } else { + write!(w, "{}({} (", pref, keywords::cmd::def_funs_rec)?; + for &(pred, _) in defs { + write!(w, "\n{} {} ", pref, self[pred].name)?; + self.write_pred_sig(w, pred)?; + } + write!(w, "\n{}) (", pref)?; + for &(_, ref tterms) in defs { + write!(w, "\n{} ", pref)?; + self.write_tterms_conj(w, tterms)?; + } + writeln!(w, "\n{}) )", pref)?; + } + } + + Ok(()) + } + + /// Writes a model. + pub fn write_model(&self, model: ConjModelRef, w: &mut W) -> Res<()> { + writeln!(w, "(model")?; + self.write_definitions(w, " ", model)?; + writeln!(w, ")")?; + Ok(()) + } + + /// Sets print-success flag. + pub fn set_print_success(&mut self, b: bool) { + self.print_success = b + } + /// Print-success flag accessor. + pub fn print_success(&self) -> bool { + self.print_success + } + /// Sets unsat-cores flag. + pub fn set_unsat_cores(&mut self, b: bool) { + self.unsat_cores = b + } + /// Unsat-cores flag. + pub fn unsat_cores(&self) -> bool { + self.unsat_cores + } + /// Sets proofs flag. + pub fn set_proofs(&mut self, b: bool) { + self.proofs = b + } + /// Unsat-cores flag. + pub fn proofs(&self) -> bool { + self.proofs + } + + /// True if the teacher needs to maintain a sample graph (unsat + /// cores/proofs). + pub fn track_samples(&self) -> bool { + self.unsat_cores() || self.proofs() + } + + /// Converts `"true"` to `true`, `"false"` to `false`, and everything else to + /// an error. + fn bool_of_str(s: &str) -> Res { + match s { + "true" => Ok(true), + "false" => Ok(false), + _ => bail!("expected boolean `true/false`, got `{}`", s), + } + } + + /// Sets an option. + pub fn set_option(&mut self, flag: &str, val: &str) -> Res<()> { + let flag_err = || format!("while handling set-option for {}", flag); + match flag { + "print-success" => { + let print_succ = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_print_success(print_succ) + } + "produce-unsat-cores" => { + let unsat_cores = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_unsat_cores(unsat_cores) + } + "produce-proofs" => { + let proofs = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_proofs(proofs) + } + _ => warn!( + "ignoring (set-option :{} {}): unknown flag {}", + flag, val, flag + ), + } + Ok(()) + } +} /// Lhs part of a cex. -type CexLhs = Vec< (PrdIdx, VarTermsSet) > ; +type CexLhs = Vec<(PrdIdx, VarTermsSet)>; /// Lhs part of a cex, reference version. -type CexLhsRef<'a> = & 'a [ (PrdIdx, VarTermsSet) ] ; +type CexLhsRef<'a> = &'a [(PrdIdx, VarTermsSet)]; /// Rhs part of a cex. -type CexRhs<'a> = Option<(PrdIdx, & 'a VarTerms)> ; - +type CexRhs<'a> = Option<(PrdIdx, &'a VarTerms)>; /// Cex-related functions. impl Instance { - - /// Retrieves the lhs and rhs cex part from a bias. - fn break_cex( - & self, clause_idx: ClsIdx, bias: Bias - ) -> (CexLhs, CexRhs) { - let clause = & self[clause_idx] ; - match bias { - - // Consider the whole lhs of the clause positive. - Bias::Lft => (vec![], clause.rhs()), - - // Consider the rhs of the clause negative. - Bias::Rgt => ( - clause.lhs_preds().iter().map( - |(pred, argss)| (* pred, argss.clone()) - ).collect(), - None - ), - - // Consider the rhs of the clause negative, and all lhs applications - // positive except this one. - Bias::NuRgt(pred, args) => { - use var_to::terms::VarTermsSet ; - debug_assert! { clause.lhs_preds().get(& pred).is_some() } - debug_assert! { - clause.lhs_preds().get(& pred).unwrap().contains(& args) - } - let mut argss = VarTermsSet::with_capacity(1) ; - argss.insert(args) ; - ( vec![(pred, argss)], None ) - }, - - // No bias. - Bias::Non => ( - clause.lhs_preds().iter().map( - |(pred, argss)| (* pred, argss.clone()) - ).collect(), - clause.rhs() - ), - - } - } - - - - - /// Forces non-values in the cex if needed. - pub fn force_non_values( - & self, cex: & mut Cex, lhs: CexLhsRef, rhs: & CexRhs - ) { - // Factored set of variables when fixing cex for arguments. - let mut known_vars = VarSet::new() ; - - macro_rules! fix_cex { - ($args:expr) => ({ - log! { @6 "fixing {}", $args } - for arg in $args.iter() { - if let Some(var) = arg.var_idx() { - if ! cex[var].is_known() { - // Value for `var` is a non-value. - let is_new = known_vars.insert(var) ; - // Variable appears in more than one arg, force its value. - if ! is_new { - cex[var] = cex[var].typ().default_val() - } - } - } else { - for var in term::vars(arg) { - if ! cex[var].is_known() { - cex[var] = cex[var].typ().default_val() - } + /// Retrieves the lhs and rhs cex part from a bias. + fn break_cex(&self, clause_idx: ClsIdx, bias: Bias) -> (CexLhs, CexRhs) { + let clause = &self[clause_idx]; + match bias { + // Consider the whole lhs of the clause positive. + Bias::Lft => (vec![], clause.rhs()), + + // Consider the rhs of the clause negative. + Bias::Rgt => ( + clause + .lhs_preds() + .iter() + .map(|(pred, argss)| (*pred, argss.clone())) + .collect(), + None, + ), + + // Consider the rhs of the clause negative, and all lhs applications + // positive except this one. + Bias::NuRgt(pred, args) => { + use var_to::terms::VarTermsSet; + debug_assert! { clause.lhs_preds().get(& pred).is_some() } + debug_assert! { + clause.lhs_preds().get(& pred).unwrap().contains(& args) + } + let mut argss = VarTermsSet::with_capacity(1); + argss.insert(args); + (vec![(pred, argss)], None) } - } + + // No bias. + Bias::Non => ( + clause + .lhs_preds() + .iter() + .map(|(pred, argss)| (*pred, argss.clone())) + .collect(), + clause.rhs(), + ), } - }) } - // Force non-values in the cex if we're dealing with a constraint, not a - // sample. - if ( + /// Forces non-values in the cex if needed. + pub fn force_non_values(&self, cex: &mut Cex, lhs: CexLhsRef, rhs: &CexRhs) { + // Factored set of variables when fixing cex for arguments. + let mut known_vars = VarSet::new(); + + macro_rules! fix_cex { + ($args:expr) => {{ + log! { @6 "fixing {}", $args } + for arg in $args.iter() { + if let Some(var) = arg.var_idx() { + if !cex[var].is_known() { + // Value for `var` is a non-value. + let is_new = known_vars.insert(var); + // Variable appears in more than one arg, force its value. + if !is_new { + cex[var] = cex[var].typ().default_val() + } + } + } else { + for var in term::vars(arg) { + if !cex[var].is_known() { + cex[var] = cex[var].typ().default_val() + } + } + } + } + }}; + } + + // Force non-values in the cex if we're dealing with a constraint, not a + // sample. + if ( // Positive sample? lhs.is_empty() && rhs.is_some() ) || ( @@ -1607,168 +1541,145 @@ impl Instance { |(_, argss)| argss.len() == 1 ) && rhs.is_none() ) { - // We're generating a sample. Still need to force variables that appear - // more than once in arguments. - for (_, argss) in lhs { - debug_assert_eq! { argss.len(), 1 } - for args in argss { - fix_cex!(args) - } - } - if let Some((_, args)) = rhs.as_ref() { - fix_cex!(args) - } - } else { - // We're dealing with a constraint, not a sample. Force non-values. - for val in cex.iter_mut() { - if ! val.is_known() { - * val = val.typ().default_val() - } - } - } - } - - - - - /// Transforms a cex for a clause into some learning data. - /// - /// Returns `true` if some new data was generated. - pub fn clause_cex_to_data( - & self, data: & mut Data, clause_idx: ClsIdx, cex: BCex - ) -> Res { - let (mut cex, bias) = cex ; - - if_log! { @6 - let clause = & self[clause_idx] ; - let mut s = String::new() ; - for (var, val) in cex.index_iter() { - s.push_str(& format!("{}: {}, ", var.default_str(), val)) - } - log! { @6 - "lhs preds: {}", clause.lhs_pred_apps_len() ; - " rhs: {}", if clause.rhs().is_some() { "some" } else { "none" } ; - " bias: {}", bias ; - " cex: {}", s - } - } - - let (lhs, rhs) = self.break_cex(clause_idx, bias) ; - self.force_non_values(& mut cex, & lhs, & rhs) ; - - // Evaluates some arguments for a predicate. - macro_rules! eval { - ($args:expr) => ({ - use var_to::vals::RVarVals ; - let mut sample = RVarVals::with_capacity( $args.len() ) ; - for arg in $args.get() { - let val = arg.eval(& cex) ? ; - sample.push(val) - } - sample - }) ; - } - - // Evaluate antecedents. - let mut antecedents = vec![] ; - for (pred, argss) in lhs { - for args in argss { - let sample = eval!(args) ; - antecedents.push((pred, sample)) - } - } - - let consequent = if let Some((pred, args)) = rhs { - let sample = eval!(args) ; - Some( (pred, sample) ) - } else { - None - } ; - - if_log! { @6 - let mut s = String::new() ; - if ! antecedents.is_empty() { - for (pred, sample) in & antecedents { - s.push_str( & format!("({} {}) ", self[* pred], sample) ) - } - } else { - s.push_str("true ") - } - s.push_str("=> ") ; - if let Some((pred, sample)) = consequent.as_ref() { - s.push_str( & format!("({} {})", self[* pred], sample) ) - } else { - s.push_str("false") - } - log! { @6 "{}", s } - } - - data.add_data(clause_idx, antecedents, consequent) - } - - - - - /// Turns some teacher counterexamples into learning data. - pub fn cexs_to_data( - & self, data: & mut Data, cexs: Cexs - ) -> Res { - let mut changed = false ; - - for (clause_idx, cexs) in cexs { - log! { @5 "adding cexs for #{}", clause_idx } - - for cex in cexs { - let new_stuff = self.clause_cex_to_data( - data, clause_idx, cex - ) ? ; - changed = changed || new_stuff - } + // We're generating a sample. Still need to force variables that appear + // more than once in arguments. + for (_, argss) in lhs { + debug_assert_eq! { argss.len(), 1 } + for args in argss { + fix_cex!(args) + } + } + if let Some((_, args)) = rhs.as_ref() { + fix_cex!(args) + } + } else { + // We're dealing with a constraint, not a sample. Force non-values. + for val in cex.iter_mut() { + if !val.is_known() { + *val = val.typ().default_val() + } + } + } } - let (pos, neg) = data.propagate() ? ; - - Ok( changed || pos > 0 || neg > 0 ) - } -} - + /// Transforms a cex for a clause into some learning data. + /// + /// Returns `true` if some new data was generated. + pub fn clause_cex_to_data(&self, data: &mut Data, clause_idx: ClsIdx, cex: BCex) -> Res { + let (mut cex, bias) = cex; + if_log! { @6 + let clause = & self[clause_idx] ; + let mut s = String::new() ; + for (var, val) in cex.index_iter() { + s.push_str(& format!("{}: {}, ", var.default_str(), val)) + } + log! { @6 + "lhs preds: {}", clause.lhs_pred_apps_len() ; + " rhs: {}", if clause.rhs().is_some() { "some" } else { "none" } ; + " bias: {}", bias ; + " cex: {}", s + } + } + let (lhs, rhs) = self.break_cex(clause_idx, bias); + self.force_non_values(&mut cex, &lhs, &rhs); + + // Evaluates some arguments for a predicate. + macro_rules! eval { + ($args:expr) => {{ + use var_to::vals::RVarVals; + let mut sample = RVarVals::with_capacity($args.len()); + for arg in $args.get() { + let val = arg.eval(&cex)?; + sample.push(val) + } + sample + }}; + } + // Evaluate antecedents. + let mut antecedents = vec![]; + for (pred, argss) in lhs { + for args in argss { + let sample = eval!(args); + antecedents.push((pred, sample)) + } + } + let consequent = if let Some((pred, args)) = rhs { + let sample = eval!(args); + Some((pred, sample)) + } else { + None + }; + + if_log! { @6 + let mut s = String::new() ; + if ! antecedents.is_empty() { + for (pred, sample) in & antecedents { + s.push_str( & format!("({} {}) ", self[* pred], sample) ) + } + } else { + s.push_str("true ") + } + s.push_str("=> ") ; + if let Some((pred, sample)) = consequent.as_ref() { + s.push_str( & format!("({} {})", self[* pred], sample) ) + } else { + s.push_str("false") + } + log! { @6 "{}", s } + } + data.add_data(clause_idx, antecedents, consequent) + } + /// Turns some teacher counterexamples into learning data. + pub fn cexs_to_data(&self, data: &mut Data, cexs: Cexs) -> Res { + let mut changed = false; + for (clause_idx, cexs) in cexs { + log! { @5 "adding cexs for #{}", clause_idx } + for cex in cexs { + let new_stuff = self.clause_cex_to_data(data, clause_idx, cex)?; + changed = changed || new_stuff + } + } + let (pos, neg) = data.propagate()?; + Ok(changed || pos > 0 || neg > 0) + } +} impl ::std::ops::Index for Instance { - type Output = PrdInfo ; - fn index(& self, index: PrdIdx) -> & PrdInfo { - & self.preds[index] - } + type Output = PrdInfo; + fn index(&self, index: PrdIdx) -> &PrdInfo { + &self.preds[index] + } } impl ::std::ops::Index for Instance { - type Output = Clause ; - fn index(& self, index: ClsIdx) -> & Clause { - & self.clauses[index] - } + type Output = Clause; + fn index(&self, index: ClsIdx) -> &Clause { + &self.clauses[index] + } } impl ::std::ops::IndexMut for Instance { - fn index_mut(& mut self, index: ClsIdx) -> & mut Clause { - & mut self.clauses[index] - } + fn index_mut(&mut self, index: ClsIdx) -> &mut Clause { + &mut self.clauses[index] + } } impl AsRef for Instance { - fn as_ref(& self) -> & Self { - self - } + fn as_ref(&self) -> &Self { + self + } } impl AsMut for Instance { - fn as_mut(& mut self) -> & mut Self { - self - } + fn as_mut(&mut self) -> &mut Self { + self + } } // impl ::std::ops::Deref for Instance { // type Target = Self ; @@ -1782,140 +1693,130 @@ impl AsMut for Instance { // } // } +impl<'a> PebcakFmt<'a> for Clause { + type Info = &'a PrdInfos; + fn pebcak_err(&self) -> ErrorKind { + "during clause pebcak formatting".into() + } + fn pebcak_io_fmt(&self, w: &mut W, prds: &'a PrdInfos) -> IoRes<()> { + self.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |w, prd, args| { + write!(w, "(")?; + w.write_all(prds[prd].as_bytes())?; + for arg in args.iter() { + write!(w, " ")?; + arg.write(w, |w, var| write!(w, "{}", self.vars[var]))? + } + write!(w, ")") + }, + false, + ) + } +} +impl<'a> PebcakFmt<'a> for Instance { + type Info = (); + fn pebcak_err(&self) -> ErrorKind { + "during instance pebcak formatting".into() + } + fn pebcak_io_fmt(&self, w: &mut W, _: ()) -> IoRes<()> { + writeln!(w, "; Datatypes:")?; + + dtyp::write_all(w, "")?; + + dtyp::write_constructor_map(w, "; ")?; + + for (pred_idx, pred) in self.preds.index_iter() { + if self.pred_terms[pred_idx].is_none() { + write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; + for typ in &pred.sig { + write!(w, " {}", typ)? + } + writeln!(w, " ) Bool\n)")?; + if pred.sig.len() != self.old_preds[pred_idx].0.len() { + write!(w, "; original signature:\n;")?; + for (var, typ) in self.old_preds[pred_idx].0.index_iter() { + write!(w, " ({} {})", var.default_str(), typ)? + } + writeln!(w, "\n; variable map (new -> old):\n;")?; + for (src, tgt) in self.old_preds[pred_idx].1.index_iter() { + write!(w, " {} -> {},", src.default_str(), tgt.default_str())? + } + writeln!(w)? + } + } + } + let empty_prd_set = PrdSet::new(); + if self.sorted_pred_terms.is_empty() { + // Either there's no forced predicate, or we are printing before + // finalizing. + for (pred, tterms) in self + .pred_terms + .index_iter() + .filter_map(|(pred, tterms_opt)| tterms_opt.as_ref().map(|tt| (pred, tt))) + { + write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred])?; + for (var, typ) in self[pred].sig.index_iter() { + write!(w, " (v_{} {})", var, typ)? + } + write!(w, " ) Bool\n ")?; + tterms + .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) + .unwrap(); + writeln!(w, "\n)")? + } + } else { + for pred in &self.sorted_pred_terms { + write!(w, "({} {}\n (", keywords::cmd::def_fun, self[*pred])?; + for (var, typ) in self[*pred].sig.index_iter() { + write!(w, " (v_{} {})", var, typ)? + } + let tterms = self.pred_terms[*pred].as_ref().unwrap(); + write!(w, " ) Bool\n ")?; + tterms + .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) + .unwrap(); + writeln!(w, "\n)",)? + } + } + writeln!( + w, + "\n; Original clauses' names ({}) {{", + self.old_names.len() + )?; + for (idx, name) in &self.old_names { + writeln!(w, "; Original clause #{} is called `{}`.", idx, name)? + } + writeln!(w, "; }}")?; + + for (idx, clause) in self.clauses.index_iter() { + writeln!(w, "\n; Clause #{}", idx)?; + let from = clause.from(); + write!(w, "; from: #{}", from)?; + if let Some(name) = self.old_names.get(&from) { + write!(w, ": {}", name)? + } + writeln!(w)?; + clause.pebcak_io_fmt(w, &self.preds)? + } -impl<'a> PebcakFmt<'a> for Clause { - type Info = & 'a PrdInfos ; - fn pebcak_err(& self) -> ErrorKind { - "during clause pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, prds: & 'a PrdInfos - ) -> IoRes<()> { - self.write( - w, |w, var_info| write!(w, "{}", var_info.name), |w, prd, args| { - write!(w, "(") ? ; - w.write_all( prds[prd].as_bytes() ) ? ; - for arg in args.iter() { - write!(w, " ") ? ; - arg.write( - w, |w, var| write!(w, "{}", self.vars[var]) - ) ? - } - write!(w, ")") - }, false - ) - } -} + writeln!(w, "\npred to clauses:")?; + for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { + write!(w, " {}: lhs {{", self[pred])?; + for lhs in lhs { + write!(w, " {}", lhs)? + } + write!(w, " }}, rhs {{")?; + for rhs in rhs { + write!(w, " {}", rhs)? + } + writeln!(w, " }}")? + } -impl<'a> PebcakFmt<'a> for Instance { - type Info = () ; - fn pebcak_err(& self) -> ErrorKind { - "during instance pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, _: () - ) -> IoRes<()> { - writeln!(w, "; Datatypes:") ? ; - - dtyp::write_all(w, "") ? ; - - dtyp::write_constructor_map(w, "; ") ? ; - - for (pred_idx, pred) in self.preds.index_iter() { - if self.pred_terms[pred_idx].is_none() { - write!( - w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name - ) ? ; - for typ in & pred.sig { - write!(w, " {}", typ) ? - } - writeln!(w, " ) Bool\n)") ? ; - if pred.sig.len() != self.old_preds[pred_idx].0.len() { - write!(w, "; original signature:\n;") ? ; - for (var, typ) in self.old_preds[pred_idx].0.index_iter() { - write!( - w, " ({} {})", var.default_str(), typ - ) ? - } - writeln!(w, "\n; variable map (new -> old):\n;") ? ; - for (src, tgt) in self.old_preds[pred_idx].1.index_iter() { - write!( - w, " {} -> {},", src.default_str(), tgt.default_str() - ) ? - } - writeln!(w) ? - } - } - } - - use rsmt2::print::Expr2Smt ; - let empty_prd_set = PrdSet::new() ; - if self.sorted_pred_terms.is_empty() { - // Either there's no forced predicate, or we are printing before - // finalizing. - for (pred, tterms) in self.pred_terms.index_iter().filter_map( - |(pred, tterms_opt)| tterms_opt.as_ref().map(|tt| (pred, tt)) - ) { - write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred]) ? ; - for (var, typ) in self[pred].sig.index_iter() { - write!(w, " (v_{} {})", var, typ) ? - } - write!(w, " ) Bool\n ") ? ; - tterms.expr_to_smt2( - w, & (& empty_prd_set, & empty_prd_set, & self.preds) - ).unwrap() ; - writeln!(w, "\n)") ? - } - } else { - for pred in & self.sorted_pred_terms { - write!(w, "({} {}\n (", keywords::cmd::def_fun, self[* pred]) ? ; - for (var, typ) in self[* pred].sig.index_iter() { - write!(w, " (v_{} {})", var, typ) ? - } - let tterms = self.pred_terms[* pred].as_ref().unwrap() ; - write!(w, " ) Bool\n ") ? ; - tterms.expr_to_smt2( - w, & (& empty_prd_set, & empty_prd_set, & self.preds) - ).unwrap() ; - writeln!(w, "\n)", ) ? - } - } - - writeln!( - w, "\n; Original clauses' names ({}) {{", self.old_names.len() - ) ? ; - for (idx, name) in & self.old_names { - writeln!(w, "; Original clause #{} is called `{}`.", idx, name) ? - } - writeln!(w, "; }}") ? ; - - for (idx, clause) in self.clauses.index_iter() { - writeln!(w, "\n; Clause #{}", idx) ? ; - let from = clause.from() ; - write!(w, "; from: #{}", from) ? ; - if let Some(name) = self.old_names.get(& from) { - write!(w, ": {}", name) ? - } - writeln!(w) ? ; - clause.pebcak_io_fmt(w, & self.preds) ? - } - - writeln!(w, "\npred to clauses:") ? ; - for (pred, & (ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { - write!(w, " {}: lhs {{", self[pred]) ? ; - for lhs in lhs { - write!(w, " {}", lhs) ? - } - write!(w, " }}, rhs {{") ? ; - for rhs in rhs { - write!(w, " {}", rhs) ? - } - writeln!(w, " }}") ? - } - - Ok(()) - } + Ok(()) + } } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index e574d5a5..cfab3323 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -2,2304 +2,2110 @@ //! pre-processing. use common::{ - *, - smt::{ SmtImpl, ClauseTrivialExt }, -} ; -use instance::{ - Clause, preproc::utils::ExtractionCxt -} ; - - + smt::{ClauseTrivialExt, SmtImpl}, + *, +}; +use instance::{preproc::utils::ExtractionCxt, Clause}; /// Performs a checksat. macro_rules! check_sat { - ($pre_instance:expr) => ({ - // let actlit = if $pre_instance.reset_solver { - // Some( $pre_instance.solver.get_actlit() ? ) - // } else { - // None - // } ; - - // let sat = - $pre_instance.solver.check_sat() ? - // ; - - // if let Some(actlit) = actlit { - // $pre_instance.solver.de_actlit(actlit) ? - // } - - // sat - }) ; + ($pre_instance:expr) => {{ + // let actlit = if $pre_instance.reset_solver { + // Some( $pre_instance.solver.get_actlit() ? ) + // } else { + // None + // } ; + + // let sat = + $pre_instance.solver.check_sat()? + // ; + + // if let Some(actlit) = actlit { + // $pre_instance.solver.de_actlit(actlit) ? + // } + + // sat + }}; } - - - /// Wraps an instance for pre-processing. pub struct PreInstance<'a> { - /// The instance wrapped. - instance: & 'a mut Instance, - /// Solver used for triviality-checking. - solver: Solver<()>, - /// Clause simplifier. - simplifier: ClauseSimplifier, + /// The instance wrapped. + instance: &'a mut Instance, + /// Solver used for triviality-checking. + solver: Solver<()>, + /// Clause simplifier. + simplifier: ClauseSimplifier, - /// Factored vector of clauses to simplify. - clauses_to_simplify: Vec, + /// Factored vector of clauses to simplify. + clauses_to_simplify: Vec, - /// Set of variables used by some simplification techniques. - vars: VarSet, + /// Set of variables used by some simplification techniques. + vars: VarSet, - /// Term extraction context. - extraction: ExtractionCxt, + /// Term extraction context. + extraction: ExtractionCxt, - /// Use actlits in checksats. - reset_solver: bool, + /// Use actlits in checksats. + reset_solver: bool, } impl<'a> PreInstance<'a> { - /// Constructor. - pub fn new(instance: & 'a mut Instance) -> Res { - let solver = conf.solver.spawn("preproc", (), &* instance) ? ; + /// Constructor. + pub fn new(instance: &'a mut Instance) -> Res { + let solver = conf.solver.spawn("preproc", (), &*instance)?; - let simplifier = ClauseSimplifier::new() ; - let clauses_to_simplify = Vec::with_capacity(7) ; + let simplifier = ClauseSimplifier::new(); + let clauses_to_simplify = Vec::with_capacity(7); - let mut reset_solver = false ; + let mut reset_solver = false; - fun::iter( - |_| { reset_solver = true ; Ok(()) } - ) ? ; + fun::iter(|_| { + reset_solver = true; + Ok(()) + })?; - if dtyp::get_all().iter().next().is_some() { - reset_solver = true - } + if dtyp::get_all().iter().next().is_some() { + reset_solver = true + } - Ok( - PreInstance { - instance, solver, simplifier, - clauses_to_simplify, - vars: VarSet::new(), - extraction: ExtractionCxt::new(), - reset_solver, - } - ) - } - - /// Resets the solver. - pub fn reset_solver(& mut self) -> Res<()> { - smt::preproc_reset(& mut self.solver) - } - - /// Accessor for the solver. - pub fn solver(& mut self) -> & mut Solver<()> { - & mut self.solver - } - - /// Accessor for the extraction context and the instance. - pub fn extraction(& mut self) -> (& mut ExtractionCxt, & mut Instance) { - (& mut self.extraction, self.instance) - } - - /// Destroys the pre instance, kills the internal solver. - pub fn destroy(mut self) -> Res<()> { - self.solver.kill().chain_err( - || "While killing preproc solver" - ) ? ; - Ok(()) - } - - /// Checks whether a clause alone forces the definition of a predicate. - /// - forces to true all predicates appearing in `terms => (p vars)` where - /// `vars` are all distinct and don't appear in `terms` - /// - forces to false all predicates appearing in `terms /\ (p vars) => - /// false` where `vars` are all distinct and don't appear in `terms` - pub fn force_trivial_from_clause( - & mut self, clause_idx: ClsIdx - ) -> Option<(PrdIdx, bool)> { - if ! self.instance[clause_idx].preds_changed() - && ! self.instance[clause_idx].terms_changed() { - return None + Ok(PreInstance { + instance, + solver, + simplifier, + clauses_to_simplify, + vars: VarSet::new(), + extraction: ExtractionCxt::new(), + reset_solver, + }) } - self.instance[clause_idx].preds_checked() ; - - let clause = & self.instance[clause_idx] ; - - let ( - pred, positive, args - ) = if let Some((pred, args)) = clause.rhs() { - - // Positive clause. - if clause.lhs_preds().is_empty() { // No lhs predicate applications. - (pred, true, args) - } else { - return None - } - - } else { - - // Negative clause. - let mut lhs_preds = clause.lhs_preds().iter() ; - - if let Some((pred, argss)) = lhs_preds.next() { - if lhs_preds.next().is_none() // Only one predicate? - && argss.len() == 1 { // Only one application? - let args = argss.iter().next().unwrap() ; - (* pred, false, args) - } else { - return None - } - - } else { - return None - } + /// Resets the solver. + pub fn reset_solver(&mut self) -> Res<()> { + smt::preproc_reset(&mut self.solver) + } - } ; + /// Accessor for the solver. + pub fn solver(&mut self) -> &mut Solver<()> { + &mut self.solver + } - self.vars.clear() ; - // Are all arguments different variables? - for arg in args.iter() { - if let Some(v) = arg.var_idx() { - let is_new = self.vars.insert(v) ; - if ! is_new { - return None - } - } else { - return None - } + /// Accessor for the extraction context and the instance. + pub fn extraction(&mut self) -> (&mut ExtractionCxt, &mut Instance) { + (&mut self.extraction, self.instance) } - for term in clause.lhs_terms() { - // `vars` don't appear in any lhs term? - if term.mentions_one_of(& self.vars) { - return None - } + /// Destroys the pre instance, kills the internal solver. + pub fn destroy(mut self) -> Res<()> { + self.solver + .kill() + .chain_err(|| "While killing preproc solver")?; + Ok(()) } - Some((pred, positive)) - - } - - /// Performs cheap triviality checks. - /// - /// - forces to false (true) all the predicates that only appear in clauses' - /// lhs (rhs) - /// - forces to true all predicates appearing in `terms => (p vars)` where - /// `vars` are all distinct and don't appear in `terms` - /// - forces to false all predicates appearing in `terms /\ (p vars) => - /// false` where `vars` are all distinct and don't appear in `terms` - pub fn force_trivial(& mut self) -> Res< RedInfo > { - let mut info: RedInfo = (0, 0, 0).into() ; - let mut fixed_point = false ; - - while ! fixed_point { - - fixed_point = true ; - - 'all_preds: for pred in PrdRange::zero_to( - self.instance.preds.len() - ) { - if self.instance.is_known(pred) { - continue 'all_preds - } - - let force = if self.instance.pred_to_clauses[pred].1.is_empty() { - // Only appears as an antecedent. - Some(false) - } else if self.instance.pred_to_clauses[pred].0.is_empty() - || self.instance.pred_to_clauses[pred].1.is_superset( - & self.instance.pred_to_clauses[pred].0 - ) { - // Only appears as a consequent. - // || - // Only appears as a antecedent in clauses where it's also an - // consequent. - Some(true) - } else if self.instance.pred_to_clauses[pred].0.is_superset( - & self.instance.pred_to_clauses[pred].1 - ) { - // Only appears as a consequent in clauses where it's also an - // antecedent. - Some(false) - } else { - None - } ; - - if let Some(pos) = force { - info.preds += 1 ; - fixed_point = false ; - info += if pos { - self.force_true(pred) ? - } else { - self.force_false(pred) ? - } + /// Checks whether a clause alone forces the definition of a predicate. + /// - forces to true all predicates appearing in `terms => (p vars)` where + /// `vars` are all distinct and don't appear in `terms` + /// - forces to false all predicates appearing in `terms /\ (p vars) => + /// false` where `vars` are all distinct and don't appear in `terms` + pub fn force_trivial_from_clause(&mut self, clause_idx: ClsIdx) -> Option<(PrdIdx, bool)> { + if !self.instance[clause_idx].preds_changed() && !self.instance[clause_idx].terms_changed() + { + return None; } + self.instance[clause_idx].preds_checked(); - } + let clause = &self.instance[clause_idx]; - let mut force = vec![] ; + let (pred, positive, args) = if let Some((pred, args)) = clause.rhs() { + // Positive clause. + if clause.lhs_preds().is_empty() { + // No lhs predicate applications. + (pred, true, args) + } else { + return None; + } + } else { + // Negative clause. + let mut lhs_preds = clause.lhs_preds().iter(); - for clause_idx in ClsRange::zero_to( - self.clauses.len() - ) { - if let Some( - (pred, positive) - ) = self.force_trivial_from_clause(clause_idx) { - force.push((pred, positive)) - } - } + if let Some((pred, argss)) = lhs_preds.next() { + if lhs_preds.next().is_none() // Only one predicate? + && argss.len() == 1 + { + // Only one application? + let args = argss.iter().next().unwrap(); - for (pred, positive) in force { - if ! self.is_known(pred) { - fixed_point = false ; - info.preds += 1 ; - if positive { - info += self.force_true(pred) ? - } else { - info += self.force_false(pred) ? - } + (*pred, false, args) + } else { + return None; + } + } else { + return None; + } + }; + + self.vars.clear(); + // Are all arguments different variables? + for arg in args.iter() { + if let Some(v) = arg.var_idx() { + let is_new = self.vars.insert(v); + if !is_new { + return None; + } + } else { + return None; + } } - } - - } - Ok(info) - } - - - - /// Strict negative clauses. - pub fn strict_neg_clauses(& mut self) -> ( - & mut ExtractionCxt, & Instance, impl Iterator - ) { - let extraction = & mut self.extraction ; - let instance = & self.instance ; - let clauses = & instance.clauses ; - ( - extraction, instance, clauses.index_iter().filter( - |(_, clause)| clause.rhs().is_none() - && clause.lhs_preds().len() == 1 && ( - clause.lhs_preds().iter().next().map( - |(_, apps)| apps.len() == 1 - ).unwrap_or(false) - ) - ) - ) - } - - - - /// Non-strict negative clauses. - pub fn non_strict_neg_clauses(& mut self) -> ( - & mut ExtractionCxt, & Instance, impl Iterator - ) { - let extraction = & mut self.extraction ; - let instance = & self.instance ; - let clauses = & instance.clauses ; - ( - extraction, instance, clauses.index_iter().filter( - |(_, clause)| clause.rhs().is_none() - && ( - clause.lhs_preds().len() > 1 || clause.lhs_preds().iter().any( - |(_, apps)| apps.len() > 1 - ) - ) - ) - ) - } - + for term in clause.lhs_terms() { + // `vars` don't appear in any lhs term? + if term.mentions_one_of(&self.vars) { + return None; + } + } - /// Simplifies all the clauses. - pub fn simplify_all(& mut self) -> Res { - let mut info = RedInfo::new() ; // self.force_trivial() ? ; - - // Go through the clauses in reverse so that swap removes are safe. - let mut clause = self.instance.clauses.next_index() ; - - while clause > 0 { - clause.dec() ; - info += self.simplify_clause(clause) ? ; - conf.check_timeout() ? + Some((pred, positive)) } - info += self.force_trivial() ? ; - - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } + /// Performs cheap triviality checks. + /// + /// - forces to false (true) all the predicates that only appear in clauses' + /// lhs (rhs) + /// - forces to true all predicates appearing in `terms => (p vars)` where + /// `vars` are all distinct and don't appear in `terms` + /// - forces to false all predicates appearing in `terms /\ (p vars) => + /// false` where `vars` are all distinct and don't appear in `terms` + pub fn force_trivial(&mut self) -> Res { + let mut info: RedInfo = (0, 0, 0).into(); + let mut fixed_point = false; - // Check side-clauses. - scoped! { - let instance = & mut self.instance ; - let solver = & mut self.solver ; + while !fixed_point { + fixed_point = true; - log! { @4 "checking side clauses" } + 'all_preds: for pred in PrdRange::zero_to(self.instance.preds.len()) { + if self.instance.is_known(pred) { + continue 'all_preds; + } - info += instance.side_clauses_retain( - |clause| { - solver.push(1) ? ; - let res = match solver.is_clause_trivial(clause) ? { - None => bail!( ErrorKind::Unsat ), - Some(is_trivial) => Ok(is_trivial), - } ; - solver.pop(1) ? ; - res - } - ) ? ; - } + let force = if self.instance.pred_to_clauses[pred].1.is_empty() { + // Only appears as an antecedent. + Some(false) + } else if self.instance.pred_to_clauses[pred].0.is_empty() || self + .instance + .pred_to_clauses[pred] + .1 + .is_superset(&self.instance.pred_to_clauses[pred].0) + { + // Only appears as a consequent. + // || + // Only appears as a antecedent in clauses where it's also an + // consequent. + Some(true) + } else if self.instance.pred_to_clauses[pred] + .0 + .is_superset(&self.instance.pred_to_clauses[pred].1) + { + // Only appears as a consequent in clauses where it's also an + // antecedent. + Some(false) + } else { + None + }; + + if let Some(pos) = force { + info.preds += 1; + fixed_point = false; + info += if pos { + self.force_true(pred)? + } else { + self.force_false(pred)? + } + } + } - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } + let mut force = vec![]; - Ok(info) - } - - - /// Simplifies some clauses. - /// - /// - can change **all** clause indices because of potential swap removes - /// - does not run `force_trivial` - fn simplify_clauses(& mut self) -> Res { - let mut info = RedInfo::new() ; - // We're **popping**, so sort lowest to highest to avoid problems with swap - // removes. - self.clauses_to_simplify.sort_unstable_by( - |c_1, c_2| c_1.cmp( c_2 ) - ) ; - log! { @4 "simplify clauses ({})", self.clauses_to_simplify.len() } - let mut prev = None ; - - let mut force = PrdHMap::new() ; - - while let Some(clause) = self.clauses_to_simplify.pop() { - log! { @5 "{}", self[clause].to_string_info(self.preds()).unwrap() } - prev = { - if let Some(prev) = prev { - if clause == prev { continue } - } - Some(clause) - } ; - info += self.simplify_clause(clause) ? ; - if ! info.non_zero() { - if let Some((pred, pos)) = self.force_trivial_from_clause(clause) { - let prev = force.insert(pred, pos) ; - debug_assert! { - if let Some(prev) = prev { - prev == pos - } else { - true + for clause_idx in ClsRange::zero_to(self.clauses.len()) { + if let Some((pred, positive)) = self.force_trivial_from_clause(clause_idx) { + force.push((pred, positive)) + } } - } - } - } - } - for (pred, pos) in force { - if pos { - info += self.force_true(pred) ? - } else { - info += self.force_false(pred) ? - } - } - - self.check("after `simplify_clauses`") ? ; - Ok(info) - } - - /// Simplifies the terms of a clause. - /// - /// Returns true if the clause should be removed. - fn simplify_clause_term(& mut self, clause: ClsIdx) -> Res { - if self.instance[clause].terms_changed() { - log! { @3 "propagation..." } - self.simplifier.clause_propagate( - & mut self.instance.clauses[clause], & self.instance.preds - ) ? ; - log! { @3 "pruning..." } - // Remove redundant atoms. - if conf.preproc.prune_terms { - self.prune_atoms(clause) ? - } - self.instance[clause].lhs_terms_checked() ; - log! { @3 "trivial?" } - self.is_clause_trivial(clause) - } else { - Ok(false) - } - } - - - /// Simplifies a clause. - /// - /// This function might create new clauses. Potentially voids the semantics - /// of clause indices *after* `clause`. Modifying this function by making it - /// void clause indices *before* `clause` will break the whole - /// pre-processing. - // #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] - fn simplify_clause(& mut self, clause: ClsIdx) -> Res { - macro_rules! rm_return { - ($blah:expr) => ({ - log! { @debug - " removing clause #{} by {}", clause, $blah + for (pred, positive) in force { + if !self.is_known(pred) { + fixed_point = false; + info.preds += 1; + if positive { + info += self.force_true(pred)? + } else { + info += self.force_false(pred)? + } + } + } } - self.instance.forget_clause(clause) ? ; - return Ok( RedInfo::of_clauses_rmed(1) ) - }) ; - } - - log! { @debug - "simplifying clause #{} (terms_changed: {})", - clause, self.instance[clause].terms_changed() - } - - if self.instance[clause].is_pred_trivial() { - rm_return!("trivial implication") - } - - if self.instance[clause].is_unsat() { - unsat!("by preprocessing, clause simplification") + Ok(info) + } + + /// Strict negative clauses. + pub fn strict_neg_clauses( + &mut self, + ) -> ( + &mut ExtractionCxt, + &Instance, + impl Iterator, + ) { + let extraction = &mut self.extraction; + let instance = &self.instance; + let clauses = &instance.clauses; + ( + extraction, + instance, + clauses.index_iter().filter(|(_, clause)| { + clause.rhs().is_none() + && clause.lhs_preds().len() == 1 + && (clause + .lhs_preds() + .iter() + .next() + .map(|(_, apps)| apps.len() == 1) + .unwrap_or(false)) + }), + ) } - if self.simplify_clause_term(clause) ? { - rm_return!("term simplification") + /// Non-strict negative clauses. + pub fn non_strict_neg_clauses( + &mut self, + ) -> ( + &mut ExtractionCxt, + &Instance, + impl Iterator, + ) { + let extraction = &mut self.extraction; + let instance = &self.instance; + let clauses = &instance.clauses; + ( + extraction, + instance, + clauses.index_iter().filter(|(_, clause)| { + clause.rhs().is_none() + && (clause.lhs_preds().len() > 1 + || clause.lhs_preds().iter().any(|(_, apps)| apps.len() > 1)) + }), + ) } - log! { @3 "redundancy check..." } + /// Simplifies all the clauses. + pub fn simplify_all(&mut self) -> Res { + let mut info = RedInfo::new(); // self.force_trivial() ? ; - if self.is_redundant(clause) { - rm_return!("clause redundant") - } + // Go through the clauses in reverse so that swap removes are safe. + let mut clause = self.instance.clauses.next_index(); - log! { @3 "split disj..." } - - // Try to split the clause. - let res = self.split(clause) ; - - log! { @3 "done" } - - res - } - - /// Splits disjunctions. - /// - /// Splits a clause if - /// - /// - it contains a disjunction with kids `subs` - /// - `f_subs` is not empty, where `f_subs` are the kids on which `f` is true - /// - /// Generates one clause `C` per element `f_sub` of `f_subs` where `C` is - /// `idx` augmented with the term `sub`. If `f_subs == subs`, drops clause - /// `idx`. Otherwise, removes the disjunction from `idx` and adds the - /// disjunction of `subs` minus `f_subs`. - fn split_on(& mut self, idx: ClsIdx, f: F) -> Res< RedInfo > - where F: Fn(& Term) -> bool { - let mut info = RedInfo::new() ; - macro_rules! clause { - () => ( self.instance[idx] ) - } + while clause > 0 { + clause.dec(); + info += self.simplify_clause(clause)?; + conf.check_timeout()? + } - let mut split: Option<(Term, Vec<_>, Vec<_>)> = None ; - let (mut f_subs, mut others) = (vec![], vec![]) ; + info += self.force_trivial()?; - for maybe_disj in clause!().lhs_terms() { - if let Some(subs) = maybe_disj.disj_inspect() { - for sub in subs { - if f(sub) { - f_subs.push( sub.clone() ) - } else { - others.push( sub.clone() ) - } + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; } - if ! f_subs.is_empty() { - if let Some((_, prev, _)) = split.as_ref() { - if prev.len() > 1 && f_subs.len() > 1 { - // Skip if we already have - return Ok(info) + // Check side-clauses. + scoped! { + let instance = & mut self.instance ; + let solver = & mut self.solver ; + + log! { @4 "checking side clauses" } + + info += instance.side_clauses_retain( + |clause| { + solver.push(1) ? ; + let res = match solver.is_clause_trivial(clause) ? { + None => bail!( ErrorKind::Unsat ), + Some(is_trivial) => Ok(is_trivial), + } ; + solver.pop(1) ? ; + res } - } + ) ? ; + } - split = Some( - ( - maybe_disj.clone(), - ::std::mem::replace(& mut f_subs, vec![]), - ::std::mem::replace(& mut others, vec![]), - ) - ) - } else { - others.clear() + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; } - } - } - if let Some((disj, f_subs, others)) = split { - - debug_assert! { ! f_subs.is_empty() } - - let was_there = clause!().rm_term(& disj) ; - debug_assert! { was_there } - - let clause = if others.is_empty() { - info.clauses_rmed += 1 ; - self.instance.forget_clause(idx) ? - } else { - let clause = clause!().clone() ; - clause!().insert_term( - term::or(others) - ) ; - info += self.simplify_clause(idx) ? ; - clause - } ; - - for f_sub in f_subs { - let mut clause = clause.clone() ; - clause.insert_term(f_sub) ; - info.clauses_added += 1 ; - if let Some(idx) = self.instance.push_clause(clause) ? { - info += self.simplify_clause(idx) ? - } - () - } - - } else { - - let mut split: Option<(VarIdx, _, _)> = None ; - - // We haven't split. Maybe we can split on the arguments of the rhs. - if let Some((_, args)) = clause!().rhs() { - - 'args: for (index, arg) in args.index_iter() { - - if let Some((c, t, e)) = arg.ite_inspect() { - debug_assert! { split.is_none() } - if f(c) - || { c.rm_neg().as_ref().map(|c| f(c)).unwrap_or(false) } { - split = Some( - ( - index, - ( c.clone(), t.clone() ), - ( term::not( c.clone() ), e.clone() ), - ) - ) + Ok(info) + } + + /// Simplifies some clauses. + /// + /// - can change **all** clause indices because of potential swap removes + /// - does not run `force_trivial` + fn simplify_clauses(&mut self) -> Res { + let mut info = RedInfo::new(); + // We're **popping**, so sort lowest to highest to avoid problems with swap + // removes. + self.clauses_to_simplify + .sort_unstable_by(|c_1, c_2| c_1.cmp(c_2)); + log! { @4 "simplify clauses ({})", self.clauses_to_simplify.len() } + let mut prev = None; + + let mut force = PrdHMap::new(); + + while let Some(clause) = self.clauses_to_simplify.pop() { + log! { @5 "{}", self[clause].to_string_info(self.preds()).unwrap() } + prev = { + if let Some(prev) = prev { + if clause == prev { + continue; + } + } + Some(clause) + }; + info += self.simplify_clause(clause)?; + if !info.non_zero() { + if let Some((pred, pos)) = self.force_trivial_from_clause(clause) { + let prev = force.insert(pred, pos); + debug_assert! { + if let Some(prev) = prev { + prev == pos + } else { + true + } + } + } } - break 'args - } } - } - - if let Some( (index, (lhs_1, rhs_1), (lhs_2, rhs_2)) ) = split { - - let (pred, args) = clause!().unset_rhs().unwrap() ; - - // let (mut args_1, mut args_2) = (args.clone(), args) ; - let mut args_1 = args.get().clone() ; - let mut args_2 = args.get().clone() ; - - args_1[index] = rhs_1 ; - args_2[index] = rhs_2 ; - - let mut other_clause = clause!().clone_with_rhs( - Some( TTerm::P { pred, args: var_to::terms::new(args_2) } ), - "split" - ) ; - clause!().set_rhs( pred, var_to::terms::new(args_1) ) ? ; - clause!().insert_term(lhs_1) ; - - other_clause.insert_term(lhs_2) ; - info += self.simplify_clause(idx) ? ; - - if let Some(idx) = self.instance.push_clause( other_clause ) ? { - info += self.simplify_clause(idx) ? + for (pred, pos) in force { + if pos { + info += self.force_true(pred)? + } else { + info += self.force_false(pred)? + } } - } + self.check("after `simplify_clauses`")?; + Ok(info) + } + + /// Simplifies the terms of a clause. + /// + /// Returns true if the clause should be removed. + fn simplify_clause_term(&mut self, clause: ClsIdx) -> Res { + if self.instance[clause].terms_changed() { + log! { @3 "propagation..." } + self.simplifier + .clause_propagate(&mut self.instance.clauses[clause], &self.instance.preds)?; + log! { @3 "pruning..." } + // Remove redundant atoms. + if conf.preproc.prune_terms { + self.prune_atoms(clause)? + } + self.instance[clause].lhs_terms_checked(); + log! { @3 "trivial?" } + self.is_clause_trivial(clause) + } else { + Ok(false) + } } - Ok(info) - } - - - /// Checks whether a term is worth splitting on. - fn should_split(term: & Term) -> bool { - term.var_idx().is_some() || term.neg_inspect().map( - |sub| sub.var_idx().is_some() - ).unwrap_or(false) || term.eq_inspect().is_some() - } + /// Simplifies a clause. + /// + /// This function might create new clauses. Potentially voids the semantics + /// of clause indices *after* `clause`. Modifying this function by making it + /// void clause indices *before* `clause` will break the whole + /// pre-processing. + // #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] + fn simplify_clause(&mut self, clause: ClsIdx) -> Res { + macro_rules! rm_return { + ($blah:expr) => {{ + log! { @debug + " removing clause #{} by {}", clause, $blah + } + self.instance.forget_clause(clause)?; + return Ok(RedInfo::of_clauses_rmed(1)); + }}; + } + log! { @debug + "simplifying clause #{} (terms_changed: {})", + clause, self.instance[clause].terms_changed() + } - /// Clever disjunction splitting. - fn split(& mut self, idx: ClsIdx) -> Res { - self.split_on( - idx, |sub| if let Some(subs) = sub.conj_inspect() { - for sub in subs { - if Self::should_split(sub) { return true } + if self.instance[clause].is_pred_trivial() { + rm_return!("trivial implication") } - false - } else { - Self::should_split(sub) - } - ) - } + if self.instance[clause].is_unsat() { + unsat!("by preprocessing, clause simplification") + } - /// Removes redundant atoms. - fn prune_atoms(& mut self, clause: ClsIdx) -> Res<()> { - let atoms: Vec = self.instance[ - clause - ].lhs_terms().iter().cloned().collect() ; + if self.simplify_clause_term(clause)? { + rm_return!("term simplification") + } - if atoms.is_empty() { return Ok(()) } + log! { @3 "redundancy check..." } - let clause = & mut self.instance[clause] ; + if self.is_redundant(clause) { + rm_return!("clause redundant") + } - self.solver.push(1) ? ; + log! { @3 "split disj..." } - self.solver.comment("Pruning atoms...") ? ; + // Try to split the clause. + let res = self.split(clause); - clause.declare(& mut self.solver) ? ; + log! { @3 "done" } - for atom in atoms { - let keep = if let Some(implication) = SmtImpl::new( - clause.lhs_terms(), & atom - ) { - let actlit = self.solver.get_actlit() ? ; - self.solver.assert_act(& actlit, & implication) ? ; - let res = self.solver.check_sat_act( Some(& actlit) ) ? ; - self.solver.de_actlit(actlit) ? ; res - } else { - bail!("failed to construct implication wrapper") - } ; - if ! keep { - let was_there = clause.rm_term(& atom) ; - debug_assert! { was_there } - } - conf.check_timeout() ? ; } - self.solver.comment("Done pruning atoms...") ? ; - - self.solver.pop(1) ? ; + /// Splits disjunctions. + /// + /// Splits a clause if + /// + /// - it contains a disjunction with kids `subs` + /// - `f_subs` is not empty, where `f_subs` are the kids on which `f` is true + /// + /// Generates one clause `C` per element `f_sub` of `f_subs` where `C` is + /// `idx` augmented with the term `sub`. If `f_subs == subs`, drops clause + /// `idx`. Otherwise, removes the disjunction from `idx` and adds the + /// disjunction of `subs` minus `f_subs`. + fn split_on(&mut self, idx: ClsIdx, f: F) -> Res + where + F: Fn(&Term) -> bool, + { + let mut info = RedInfo::new(); + macro_rules! clause { + () => { + self.instance[idx] + }; + } - Ok(()) - } + let mut split: Option<(Term, Vec<_>, Vec<_>)> = None; + let (mut f_subs, mut others) = (vec![], vec![]); + + for maybe_disj in clause!().lhs_terms() { + if let Some(subs) = maybe_disj.disj_inspect() { + for sub in subs { + if f(sub) { + f_subs.push(sub.clone()) + } else { + others.push(sub.clone()) + } + } + if !f_subs.is_empty() { + if let Some((_, prev, _)) = split.as_ref() { + if prev.len() > 1 && f_subs.len() > 1 { + // Skip if we already have + return Ok(info); + } + } + + split = Some(( + maybe_disj.clone(), + ::std::mem::replace(&mut f_subs, vec![]), + ::std::mem::replace(&mut others, vec![]), + )) + } else { + others.clear() + } + } + } + if let Some((disj, f_subs, others)) = split { + debug_assert! { ! f_subs.is_empty() } - /// Checks whether a clause is trivial. - /// - /// Returns true if - /// - /// - the terms in the lhs are equivalent to `false`, or - /// - the rhs is a predicate application contained in the lhs. - #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] - fn is_clause_trivial(& mut self, clause_idx: ClsIdx) -> Res { - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } else { - self.solver.push(1) ? ; - } - let res = self.solver.is_clause_trivial( - & mut self.instance[clause_idx] - ) ; - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } else { - self.solver.pop(1) ? ; - } + let was_there = clause!().rm_term(&disj); + debug_assert! { was_there } - if let Some(res) = res ? { - Ok(res) - } else { - log_debug!{ - "unsat because of {}", - self.instance[clause_idx].to_string_info( - self.instance.preds() - ) ? - } - bail!( ErrorKind::UnsatFrom(clause_idx) ) - } + let clause = if others.is_empty() { + info.clauses_rmed += 1; + self.instance.forget_clause(idx)? + } else { + let clause = clause!().clone(); + clause!().insert_term(term::or(others)); + info += self.simplify_clause(idx)?; + clause + }; + + for f_sub in f_subs { + let mut clause = clause.clone(); + clause.insert_term(f_sub); + info.clauses_added += 1; + if let Some(idx) = self.instance.push_clause(clause)? { + info += self.simplify_clause(idx)? + } + () + } + } else { + let mut split: Option<(VarIdx, _, _)> = None; + + // We haven't split. Maybe we can split on the arguments of the rhs. + if let Some((_, args)) = clause!().rhs() { + 'args: for (index, arg) in args.index_iter() { + if let Some((c, t, e)) = arg.ite_inspect() { + debug_assert! { split.is_none() } + if f(c) || { c.rm_neg().as_ref().map(|c| f(c)).unwrap_or(false) } { + split = Some(( + index, + (c.clone(), t.clone()), + (term::not(c.clone()), e.clone()), + )) + } + break 'args; + } + } + } - } - - /// Checks whether a clause is trivial. - /// - /// Returns `None` if the clause is unsat. - /// - /// Returns true if - /// - /// - the terms in the lhs are equivalent to `false`, or - /// - the rhs is a predicate application contained in the lhs. - #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] - pub fn is_this_clause_trivial( - & mut self, clause: & mut Clause - ) -> Res< Option > { - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } else { - self.solver.push(1) ? ; - } - let res = self.solver.is_clause_trivial(clause) ; - if self.reset_solver { - smt::reset(& mut self.solver, & self.instance) ? ; - } else { - self.solver.pop(1) ? ; - } - res - } + if let Some((index, (lhs_1, rhs_1), (lhs_2, rhs_2))) = split { + let (pred, args) = clause!().unset_rhs().unwrap(); + // let (mut args_1, mut args_2) = (args.clone(), args) ; + let mut args_1 = args.get().clone(); + let mut args_2 = args.get().clone(); + args_1[index] = rhs_1; + args_2[index] = rhs_2; + let mut other_clause = clause!().clone_with_rhs( + Some(TTerm::P { + pred, + args: var_to::terms::new(args_2), + }), + "split", + ); + clause!().set_rhs(pred, var_to::terms::new(args_1))?; + clause!().insert_term(lhs_1); + other_clause.insert_term(lhs_2); + info += self.simplify_clause(idx)?; + if let Some(idx) = self.instance.push_clause(other_clause)? { + info += self.simplify_clause(idx)? + } + } + } - /// Checks the underlying instance is correct. - pub fn check(& self, blah: & 'static str) -> Res<()> { - if ! self.clauses_to_simplify.is_empty() { - bail!("clauses_to_simplify is not empty: {}", blah) - } - self.instance.check(blah) - } - - - - - - /// Forces all the remaining predicates to some DNFs at the same time. - /// - /// Checks that the positive and negative constraints are respected. Returns - /// `true` if they are, *i.e.* the definitions are a legal model, and `false` - /// otherwise. - pub fn force_all_preds( - & mut self, defs: Defs, - ) -> Res< (bool, RedInfo) > - where Defs: IntoIterator)> { - log_debug! { "forcing all remaining predicates" } - - let mut info = RedInfo::new() ; - info.clauses_rmed += self.instance.clauses.len() ; - - // Force predicates. - for (pred, def) in defs { - log! { @4 " forcing {}", self[pred] } - let def = TTerms::dnf( - def.into_iter().map( - |(quantfed, conj)| ( - Quant::exists(quantfed), conj - ) - ).collect() - ) ; - debug_assert! { self.instance.pred_terms[pred].is_none() } - self.instance.pred_terms[pred] = Some(def) + Ok(info) } - // Drop all clauses. - log_debug! { " unlinking all predicates" } - for & mut ( - ref mut lhs, ref mut rhs - ) in self.instance.pred_to_clauses.iter_mut() { - lhs.clear() ; - rhs.clear() - } - log_debug! { " dropping non pos/neg clauses" } - let mut clause: ClsIdx = 0.into() ; - while clause < self.instance.clauses.len() { - if self.instance.clauses[clause].rhs().is_none() - || self.instance.clauses[clause].lhs_preds().is_empty() { - clause.inc() ; - continue - } else { - self.instance.clauses.swap_remove(clause) ; - } - } - log_debug! { " checking pred defs" } - - for (pred, _) in self.preds().index_iter() { - if ! self.is_known(pred) { - bail!( - format!( - "error in `force_all_preds`, no definition for {}", self[pred] - ) - ) - } + /// Checks whether a term is worth splitting on. + fn should_split(term: &Term) -> bool { + term.var_idx().is_some() + || term + .neg_inspect() + .map(|sub| sub.var_idx().is_some()) + .unwrap_or(false) + || term.eq_inspect().is_some() } - let is_sat = self.check_pred_defs() ? ; - - self.instance.clauses.clear() ; - - Ok( (is_sat, info) ) - } - - - - - - /// Forces a predicate to be equal to something. - /// - /// Does not impact `pred_to_clauses`. - fn force_pred( - & mut self, pred: PrdIdx, tterms: TTerms - ) -> Res<()> { - log! { @5 "forcing {}", conf.emph(& self.instance[pred].name) } - if self.instance.pred_terms[pred].as_ref().is_some() { - let mut s: Vec = Vec::new() ; - tterms.write_smt2( - & mut s, |w, pred, args| { - write!(w, "({}", self[pred]) ? ; - for arg in args.iter() { - write!(w, " {}", arg) ? - } - write!(w, ")") - } - ).chain_err( - || "while dumping top terms during error on `force_pred`" - ) ? ; - bail!( - "trying to force predicate {} twice\n{}", - conf.sad(& self.instance[pred].name), - String::from_utf8_lossy(& s) - ) - } else { - self.instance.pred_terms[pred] = Some(tterms) + /// Clever disjunction splitting. + fn split(&mut self, idx: ClsIdx) -> Res { + self.split_on(idx, |sub| { + if let Some(subs) = sub.conj_inspect() { + for sub in subs { + if Self::should_split(sub) { + return true; + } + } + false + } else { + Self::should_split(sub) + } + }) } - Ok(()) - } + /// Removes redundant atoms. + fn prune_atoms(&mut self, clause: ClsIdx) -> Res<()> { + let atoms: Vec = self.instance[clause].lhs_terms().iter().cloned().collect(); + if atoms.is_empty() { + return Ok(()); + } + let clause = &mut self.instance[clause]; + self.solver.push(1)?; + self.solver.comment("Pruning atoms...")?; + clause.declare(&mut self.solver)?; + for atom in atoms { + let keep = if let Some(implication) = SmtImpl::new(clause.lhs_terms(), &atom) { + let actlit = self.solver.get_actlit()?; + self.solver.assert_act(&actlit, &implication)?; + let res = self.solver.check_sat_act(Some(&actlit))?; + self.solver.de_actlit(actlit)?; + res + } else { + bail!("failed to construct implication wrapper") + }; + if !keep { + let was_there = clause.rm_term(&atom); + debug_assert! { was_there } + } + conf.check_timeout()?; + } + self.solver.comment("Done pruning atoms...")?; + self.solver.pop(1)?; + Ok(()) + } - /// Forces some predicate to false. - /// - /// Simplifies all clauses impacted. - pub fn force_false( - & mut self, pred: PrdIdx, - ) -> Res { - self.check("before force false") ? ; + /// Checks whether a clause is trivial. + /// + /// Returns true if + /// + /// - the terms in the lhs are equivalent to `false`, or + /// - the rhs is a predicate application contained in the lhs. + #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] + fn is_clause_trivial(&mut self, clause_idx: ClsIdx) -> Res { + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; + } else { + self.solver.push(1)?; + } + let res = self + .solver + .is_clause_trivial(&mut self.instance[clause_idx]); + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; + } else { + self.solver.pop(1)?; + } - let mut info = RedInfo::new() ; + if let Some(res) = res? { + Ok(res) + } else { + log_debug!{ + "unsat because of {}", + self.instance[clause_idx].to_string_info( + self.instance.preds() + ) ? + } + bail!(ErrorKind::UnsatFrom(clause_idx)) + } + } - self.force_pred( pred, TTerms::fls() ) ? ; + /// Checks whether a clause is trivial. + /// + /// Returns `None` if the clause is unsat. + /// + /// Returns true if + /// + /// - the terms in the lhs are equivalent to `false`, or + /// - the rhs is a predicate application contained in the lhs. + #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] + pub fn is_this_clause_trivial(&mut self, clause: &mut Clause) -> Res> { + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; + } else { + self.solver.push(1)?; + } + let res = self.solver.is_clause_trivial(clause); + if self.reset_solver { + smt::reset(&mut self.solver, &self.instance)?; + } else { + self.solver.pop(1)?; + } + res + } - // Forget everything in `lhs`. - debug_assert!( self.clauses_to_simplify.is_empty() ) ; - self.instance.unlink_pred_lhs( - pred, & mut self.clauses_to_simplify - ) ; - info.clauses_rmed += self.clauses_to_simplify.len() ; - self.instance.forget_clauses( & mut self.clauses_to_simplify ) ? ; + /// Checks the underlying instance is correct. + pub fn check(&self, blah: &'static str) -> Res<()> { + if !self.clauses_to_simplify.is_empty() { + bail!("clauses_to_simplify is not empty: {}", blah) + } + self.instance.check(blah) + } + + /// Forces all the remaining predicates to some DNFs at the same time. + /// + /// Checks that the positive and negative constraints are respected. Returns + /// `true` if they are, *i.e.* the definitions are a legal model, and `false` + /// otherwise. + pub fn force_all_preds(&mut self, defs: Defs) -> Res<(bool, RedInfo)> + where + Defs: IntoIterator, + { + log_debug! { "forcing all remaining predicates" } + + let mut info = RedInfo::new(); + info.clauses_rmed += self.instance.clauses.len(); + + // Force predicates. + for (pred, def) in defs { + log! { @4 " forcing {}", self[pred] } + let def = TTerms::dnf( + def.into_iter() + .map(|(quantfed, conj)| (Quant::exists(quantfed), conj)) + .collect(), + ); + debug_assert! { self.instance.pred_terms[pred].is_none() } + self.instance.pred_terms[pred] = Some(def) + } - // Update `rhs`. - debug_assert!( self.clauses_to_simplify.is_empty() ) ; - self.instance.unlink_pred_rhs( - pred, & mut self.clauses_to_simplify - ) ; - for clause in & self.clauses_to_simplify { - debug_assert_eq! { - self.instance.clauses[* clause].rhs().map(|(p, _)| p), Some(pred) - } - self.instance.clauses[* clause].unset_rhs() ; - debug_assert! { self.instance.clauses[* clause].preds_changed() } - } + // Drop all clauses. + log_debug! { " unlinking all predicates" } + for &mut (ref mut lhs, ref mut rhs) in self.instance.pred_to_clauses.iter_mut() { + lhs.clear(); + rhs.clear() + } + log_debug! { " dropping non pos/neg clauses" } + let mut clause: ClsIdx = 0.into(); + while clause < self.instance.clauses.len() { + if self.instance.clauses[clause].rhs().is_none() + || self.instance.clauses[clause].lhs_preds().is_empty() + { + clause.inc(); + continue; + } else { + self.instance.clauses.swap_remove(clause); + } + } + log_debug! { " checking pred defs" } + + for (pred, _) in self.preds().index_iter() { + if !self.is_known(pred) { + bail!(format!( + "error in `force_all_preds`, no definition for {}", + self[pred] + )) + } + } - info += self.simplify_clauses() ? ; - - self.check("after force true") ? ; - - Ok(info) - } - - /// Forces some predicates to true. - /// - /// Simplifies all clauses impacted. - pub fn force_true( - & mut self, pred: PrdIdx, - ) -> Res { - self.check("before force true") ? ; - - let mut info = RedInfo::new() ; - - self.force_pred( pred, TTerms::tru() ) ? ; - - // Forget everything in `rhs`. - debug_assert!( self.clauses_to_simplify.is_empty() ) ; - self.instance.unlink_pred_rhs( - pred, & mut self.clauses_to_simplify - ) ; - info.clauses_rmed += self.clauses_to_simplify.len() ; - self.instance.forget_clauses( & mut self.clauses_to_simplify ) ? ; - - // Update `rhs`. - debug_assert!( self.clauses_to_simplify.is_empty() ) ; - self.instance.unlink_pred_lhs( - pred, & mut self.clauses_to_simplify - ) ; - for clause in & self.clauses_to_simplify { - let prev = self.instance.clauses[* clause].drop_lhs_pred(pred) ; - debug_assert! { prev.is_some() } - debug_assert! { self.instance.clauses[* clause].preds_changed() } - } + let is_sat = self.check_pred_defs()?; + + self.instance.clauses.clear(); + + Ok((is_sat, info)) + } + + /// Forces a predicate to be equal to something. + /// + /// Does not impact `pred_to_clauses`. + fn force_pred(&mut self, pred: PrdIdx, tterms: TTerms) -> Res<()> { + log! { @5 "forcing {}", conf.emph(& self.instance[pred].name) } + if self.instance.pred_terms[pred].as_ref().is_some() { + let mut s: Vec = Vec::new(); + tterms + .write_smt2(&mut s, |w, pred, args| { + write!(w, "({}", self[pred])?; + for arg in args.iter() { + write!(w, " {}", arg)? + } + write!(w, ")") + }).chain_err(|| "while dumping top terms during error on `force_pred`")?; + bail!( + "trying to force predicate {} twice\n{}", + conf.sad(&self.instance[pred].name), + String::from_utf8_lossy(&s) + ) + } else { + self.instance.pred_terms[pred] = Some(tterms) + } + Ok(()) + } + + /// Forces some predicate to false. + /// + /// Simplifies all clauses impacted. + pub fn force_false(&mut self, pred: PrdIdx) -> Res { + self.check("before force false")?; + + let mut info = RedInfo::new(); + + self.force_pred(pred, TTerms::fls())?; + + // Forget everything in `lhs`. + debug_assert!(self.clauses_to_simplify.is_empty()); + self.instance + .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); + info.clauses_rmed += self.clauses_to_simplify.len(); + self.instance + .forget_clauses(&mut self.clauses_to_simplify)?; + + // Update `rhs`. + debug_assert!(self.clauses_to_simplify.is_empty()); + self.instance + .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); + for clause in &self.clauses_to_simplify { + debug_assert_eq! { + self.instance.clauses[* clause].rhs().map(|(p, _)| p), Some(pred) + } + self.instance.clauses[*clause].unset_rhs(); + debug_assert! { self.instance.clauses[* clause].preds_changed() } + } - info += self.simplify_clauses() ? ; - - self.check("after force true") ? ; - - Ok(info) - } - - - - - - /// Forces the lhs occurences of a predicate to be equal to something. - /// - /// If `pred` appears in `pred /\ apps /\ trms => rhs`, the clause will - /// become `apps /\ pred_apps /\ trms /\ terms => rhs`. - /// - /// Simplifies the clauses before returning. - /// - /// # Usage - /// - /// This function can only be called if `pred` appears exactly once as a - /// consequent, say in clause `c`, and `c`'s antecedent has no application - /// of `pred`. - /// - /// Otherwise, it will return an error. - /// - /// # Consequences - /// - /// - forgets the one clause `pred` is in the rhs of - /// - forces `pred` to be `exists qvars, pred_apps /\ terms` - /// - simplifies all clauses impacted - /// - /// # Used by - /// - /// - `SimpleOneRhs` - /// - `OneRhs` - pub fn force_pred_left( - & mut self, pred: PrdIdx, - qvars: Quantfed, - tterm_set: TTermSet, - ) -> Res { - self.check("before `force_pred_left`") ? ; - - // let mut tterm_set = TTermSet::new() ; - // tterm_set.insert_terms(terms) ; - // for (pred, args) in pred_apps { - // tterm_set.insert_pred_app(pred, args) ; - // } - - if tterm_set.is_empty() { - return self.force_true(pred) - } + info += self.simplify_clauses()?; - let mut info = RedInfo::new() ; + self.check("after force true")?; - log_debug! { - "force pred left on {}...", conf.emph(& self.instance[pred].name) + Ok(info) } + /// Forces some predicates to true. + /// + /// Simplifies all clauses impacted. + pub fn force_true(&mut self, pred: PrdIdx) -> Res { + self.check("before force true")?; - // Forget the rhs clause. - log_debug! { - "forgetting rhs clause" - } - debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_rhs( - pred, & mut self.clauses_to_simplify - ) ; - let clause_to_rm = if let Some(clause) = self.clauses_to_simplify.pop() { - - // Fail if illegal. - if self.clauses_to_simplify.pop().is_some() { - bail!( - "illegal context for `force_pred_left`, \ - {} appears in more than one rhs", - conf.emph(& self.instance[pred].name) - ) - } - if self.instance.preds_of_clause(clause).0.get(& pred).is_some() { - bail!( - "illegal context for `force_pred_left`, \ - {} appears as both lhs and rhs", - conf.emph(& self.instance[pred].name) - ) - } - - clause - } else { - bail!( - "illegal context for `force_pred_left`, \ - {} appears in no rhs", conf.emph( - & self.instance[pred].name - ) - ) - } ; - - info.clauses_rmed += 1 ; - self.instance.forget_clause(clause_to_rm) ? ; - - // Update lhs clauses. - debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_lhs( - pred, & mut self.clauses_to_simplify - ) ; - log! { @4 - "updating lhs clauses ({})", self.clauses_to_simplify.len() - } + let mut info = RedInfo::new(); - for clause in & self.clauses_to_simplify { - let clause = * clause ; - log! { @4 - "- working on lhs of clause {}", - self.instance[clause].to_string_info( - self.instance.preds() - ).unwrap() - } - - let argss = if let Some( - argss - ) = self.instance.clauses[clause].drop_lhs_pred(pred) { - argss - } else { - bail!( - "inconsistent instance state, \ - `pred_to_clauses` and clauses out of sync" - ) - } ; + self.force_pred(pred, TTerms::tru())?; - for args in argss { - // Generate fresh variables for the clause if needed. - let qual_map = self.instance.clauses[clause].fresh_vars_for(& qvars) ; + // Forget everything in `rhs`. + debug_assert!(self.clauses_to_simplify.is_empty()); + self.instance + .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); + info.clauses_rmed += self.clauses_to_simplify.len(); + self.instance + .forget_clauses(&mut self.clauses_to_simplify)?; - for term in tterm_set.terms() { - if let Some((term, _)) = term.subst_total( & (& args, & qual_map) ) { - self.instance.clause_add_lhs_term(clause, term) ; - } else { - bail!("error during total substitution in `force_pred_left`") - } + // Update `rhs`. + debug_assert!(self.clauses_to_simplify.is_empty()); + self.instance + .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); + for clause in &self.clauses_to_simplify { + let prev = self.instance.clauses[*clause].drop_lhs_pred(pred); + debug_assert! { prev.is_some() } + debug_assert! { self.instance.clauses[* clause].preds_changed() } } - for (pred, app_argss) in tterm_set.preds() { - let pred = * pred ; - for app_args in app_argss { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in app_args.iter() { - if let Some((arg, _)) = arg.subst_total( - & (& args, & qual_map) - ) { - nu_args.push(arg) - } - } - self.instance.clause_add_lhs_pred(clause, pred, nu_args) - } + info += self.simplify_clauses()?; + + self.check("after force true")?; + + Ok(info) + } + + /// Forces the lhs occurences of a predicate to be equal to something. + /// + /// If `pred` appears in `pred /\ apps /\ trms => rhs`, the clause will + /// become `apps /\ pred_apps /\ trms /\ terms => rhs`. + /// + /// Simplifies the clauses before returning. + /// + /// # Usage + /// + /// This function can only be called if `pred` appears exactly once as a + /// consequent, say in clause `c`, and `c`'s antecedent has no application + /// of `pred`. + /// + /// Otherwise, it will return an error. + /// + /// # Consequences + /// + /// - forgets the one clause `pred` is in the rhs of + /// - forces `pred` to be `exists qvars, pred_apps /\ terms` + /// - simplifies all clauses impacted + /// + /// # Used by + /// + /// - `SimpleOneRhs` + /// - `OneRhs` + pub fn force_pred_left( + &mut self, + pred: PrdIdx, + qvars: Quantfed, + tterm_set: TTermSet, + ) -> Res { + self.check("before `force_pred_left`")?; + + // let mut tterm_set = TTermSet::new() ; + // tterm_set.insert_terms(terms) ; + // for (pred, args) in pred_apps { + // tterm_set.insert_pred_app(pred, args) ; + // } + + if tterm_set.is_empty() { + return self.force_true(pred); } - } - - log! { @5 - "done with clause: {}", - self.instance[clause].to_string_info( - self.instance.preds() - ).unwrap() - } - debug_assert! { self.instance[clause].preds_changed() } + let mut info = RedInfo::new(); - } - - // Actually force the predicate. - self.force_pred( - pred, - TTerms::conj( - Quant::exists(qvars), tterm_set - ) - ) ? ; - - - info += self.simplify_clauses() ? ; - - self.check("after `force_pred_left`") ? ; - - Ok(info) - } - - - /// Extends the lhs occurences of a predicate with some a term. - /// - /// If `pred` appears in `pred /\ apps /\ trms => rhs` where `rhs` is a - /// predicate application, the clause will become `pred /\ apps /\ trms /\ - /// term => rhs`. - /// - /// Simplifies before returning. - /// - /// # Consequences - /// - /// - simplifies all clauses impacted - /// - /// # Used by - /// - /// - sub instance generation, when splitting on one clause - pub fn extend_pred_left( - & mut self, preds: & PrdHMap<::instance::preproc::PredExtension> - ) -> Res { - self.check("before `extend_pred_left`") ? ; - - // let mut tterm_set = TTermSet::new() ; - // tterm_set.insert_terms(terms) ; - // for (pred, args) in pred_apps { - // tterm_set.insert_pred_app(pred, args) ; - // } - - let mut info = RedInfo::new() ; - - if preds.is_empty() { - return Ok(info) - } + log_debug! { + "force pred left on {}...", conf.emph(& self.instance[pred].name) + } - // Update lhs clauses. - let mut to_simplify = ClsSet::new() ; - for pred in preds.keys() { - to_simplify.extend( - self.clauses_of(* pred).0.iter().cloned() - ) ; - } + // Forget the rhs clause. + log_debug! { + "forgetting rhs clause" + } + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); + let clause_to_rm = if let Some(clause) = self.clauses_to_simplify.pop() { + // Fail if illegal. + if self.clauses_to_simplify.pop().is_some() { + bail!( + "illegal context for `force_pred_left`, \ + {} appears in more than one rhs", + conf.emph(&self.instance[pred].name) + ) + } + if self.instance.preds_of_clause(clause).0.get(&pred).is_some() { + bail!( + "illegal context for `force_pred_left`, \ + {} appears as both lhs and rhs", + conf.emph(&self.instance[pred].name) + ) + } - debug_assert! { self.clauses_to_simplify.is_empty() } + clause + } else { + bail!( + "illegal context for `force_pred_left`, \ + {} appears in no rhs", + conf.emph(&self.instance[pred].name) + ) + }; - 'clause_iter: for clause in & to_simplify { - let clause = * clause ; - self.clauses_to_simplify.push(clause) ; + info.clauses_rmed += 1; + self.instance.forget_clause(clause_to_rm)?; - if self.clauses[clause].rhs().is_none() { - continue 'clause_iter - } + // Update lhs clauses. + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); + log! { @4 + "updating lhs clauses ({})", self.clauses_to_simplify.len() + } - log! { @4 - "- working on lhs of clause {}", - self[clause].to_string_info( - self.preds() - ).unwrap() - } + for clause in &self.clauses_to_simplify { + let clause = *clause; + log! { @4 + "- working on lhs of clause {}", + self.instance[clause].to_string_info( + self.instance.preds() + ).unwrap() + } - for (pred, & (ref terms, ref quantified)) in preds { - let pred = * pred ; + let argss = if let Some(argss) = self.instance.clauses[clause].drop_lhs_pred(pred) { + argss + } else { + bail!( + "inconsistent instance state, \ + `pred_to_clauses` and clauses out of sync" + ) + }; - let argss = if let Some( - argss - ) = self.clauses[clause].lhs_preds().get(& pred) { - argss.clone() - } else { - continue - } ; + for args in argss { + // Generate fresh variables for the clause if needed. + let qual_map = self.instance.clauses[clause].fresh_vars_for(&qvars); + + for term in tterm_set.terms() { + if let Some((term, _)) = term.subst_total(&(&args, &qual_map)) { + self.instance.clause_add_lhs_term(clause, term); + } else { + bail!("error during total substitution in `force_pred_left`") + } + } - for args in argss { + for (pred, app_argss) in tterm_set.preds() { + let pred = *pred; + for app_args in app_argss { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in app_args.iter() { + if let Some((arg, _)) = arg.subst_total(&(&args, &qual_map)) { + nu_args.push(arg) + } + } + self.instance.clause_add_lhs_pred(clause, pred, nu_args) + } + } + } - for term in terms { - if let Some((term, _)) = term.subst_total(& args) { - self.instance.clause_add_lhs_term(clause, term) - } else { - bail!("error during total substitution in `extend_pred_left`") + log! { @5 + "done with clause: {}", + self.instance[clause].to_string_info( + self.instance.preds() + ).unwrap() } - } - for & (ref qvars, ref term) in quantified { - // Generate fresh variables for the clause if needed. - let qual_map = self.instance.clauses[ - clause - ].fresh_vars_for(qvars) ; + debug_assert! { self.instance[clause].preds_changed() } + } - if let Some((term, _)) = term.subst_total( - & (& args, & qual_map) - ) { - self.instance.clause_add_lhs_term(clause, term) - } else { - bail!("error during total substitution in `extend_pred_left`") - } - } + // Actually force the predicate. + self.force_pred(pred, TTerms::conj(Quant::exists(qvars), tterm_set))?; + + info += self.simplify_clauses()?; + + self.check("after `force_pred_left`")?; + + Ok(info) + } + + /// Extends the lhs occurences of a predicate with some a term. + /// + /// If `pred` appears in `pred /\ apps /\ trms => rhs` where `rhs` is a + /// predicate application, the clause will become `pred /\ apps /\ trms /\ + /// term => rhs`. + /// + /// Simplifies before returning. + /// + /// # Consequences + /// + /// - simplifies all clauses impacted + /// + /// # Used by + /// + /// - sub instance generation, when splitting on one clause + pub fn extend_pred_left( + &mut self, + preds: &PrdHMap<::instance::preproc::PredExtension>, + ) -> Res { + self.check("before `extend_pred_left`")?; + + // let mut tterm_set = TTermSet::new() ; + // tterm_set.insert_terms(terms) ; + // for (pred, args) in pred_apps { + // tterm_set.insert_pred_app(pred, args) ; + // } + + let mut info = RedInfo::new(); + + if preds.is_empty() { + return Ok(info); } - } + // Update lhs clauses. + let mut to_simplify = ClsSet::new(); + for pred in preds.keys() { + to_simplify.extend(self.clauses_of(*pred).0.iter().cloned()); + } - log! { @4 - "done with clause: {}", - self[clause].to_string_info( - self.preds() - ).unwrap() - } + debug_assert! { self.clauses_to_simplify.is_empty() } - } + 'clause_iter: for clause in &to_simplify { + let clause = *clause; + self.clauses_to_simplify.push(clause); - info += self.simplify_clauses() ? ; + if self.clauses[clause].rhs().is_none() { + continue 'clause_iter; + } - self.check("after `extend_pred_left`") ? ; + log! { @4 + "- working on lhs of clause {}", + self[clause].to_string_info( + self.preds() + ).unwrap() + } - Ok(info) - } + for (pred, &(ref terms, ref quantified)) in preds { + let pred = *pred; + let argss = if let Some(argss) = self.clauses[clause].lhs_preds().get(&pred) { + argss.clone() + } else { + continue; + }; + + for args in argss { + for term in terms { + if let Some((term, _)) = term.subst_total(&args) { + self.instance.clause_add_lhs_term(clause, term) + } else { + bail!("error during total substitution in `extend_pred_left`") + } + } + + for &(ref qvars, ref term) in quantified { + // Generate fresh variables for the clause if needed. + let qual_map = self.instance.clauses[clause].fresh_vars_for(qvars); + + if let Some((term, _)) = term.subst_total(&(&args, &qual_map)) { + self.instance.clause_add_lhs_term(clause, term) + } else { + bail!("error during total substitution in `extend_pred_left`") + } + } + } + } + log! { @4 + "done with clause: {}", + self[clause].to_string_info( + self.preds() + ).unwrap() + } + } + info += self.simplify_clauses()?; - /// Forces all lhs occurrences of a predicate to be replaced by a DNF. - /// - /// - only legal if `pred` does not appear in any rhs - /// - in general, will create new clauses - /// - if `def` is empty, equivalent to `force_false` - /// - simplifies all clauses impacted - /// - does not call `force_trivial` - /// - /// Simplifies before returning. - /// - /// Used by `GraphRed`. - pub fn force_dnf_left( - & mut self, pred: PrdIdx, def: Vec< (Quantfed, TTermSet) > - ) -> Res { - let def: Vec<_> = def.into_iter().map( - |(qvars, conj)| ( - Quant::exists(qvars), conj - ) - ).collect() ; + self.check("after `extend_pred_left`")?; - if def.is_empty() { - return self.force_false(pred) + Ok(info) } - let mut info = RedInfo::new() ; + /// Forces all lhs occurrences of a predicate to be replaced by a DNF. + /// + /// - only legal if `pred` does not appear in any rhs + /// - in general, will create new clauses + /// - if `def` is empty, equivalent to `force_false` + /// - simplifies all clauses impacted + /// - does not call `force_trivial` + /// + /// Simplifies before returning. + /// + /// Used by `GraphRed`. + pub fn force_dnf_left(&mut self, pred: PrdIdx, def: Dnf) -> Res { + let def: Vec<_> = def + .into_iter() + .map(|(qvars, conj)| (Quant::exists(qvars), conj)) + .collect(); - self.check("before `force_dnf_left`") ? ; - - log! { @6 - "force_dnf_left {} ({} defs)", self[pred], def.len() ; - "unlinking rhs" - } + if def.is_empty() { + return self.force_false(pred); + } - // Make sure there's no rhs clause for `pred`. - debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_rhs( - pred, & mut self.clauses_to_simplify - ) ; - if ! self.clauses_to_simplify.is_empty() { - bail!( - "can't force dnf {}, it appears in some rhs", self.instance[pred] - ) - } + let mut info = RedInfo::new(); - log! { @6 "unlinking lhs" } + self.check("before `force_dnf_left`")?; - // Update lhs clauses. - debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_lhs( - pred, & mut self.clauses_to_simplify - ) ; - // Rev-sorting as we're going to swap remove stuff. - self.clauses_to_simplify.sort_unstable_by( - |c_1, c_2| c_2.cmp(c_1) - ) ; + log! { @6 + "force_dnf_left {} ({} defs)", self[pred], def.len() ; + "unlinking rhs" + } - let mut nu_clauses = vec![] ; + // Make sure there's no rhs clause for `pred`. + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); + if !self.clauses_to_simplify.is_empty() { + bail!( + "can't force dnf {}, it appears in some rhs", + self.instance[pred] + ) + } - for clause in self.clauses_to_simplify.drain(0..) { - info.clauses_rmed += 1 ; + log! { @6 "unlinking lhs" } - log! { @7 "working on #{}", clause } + // Update lhs clauses. + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); + // Rev-sorting as we're going to swap remove stuff. + self.clauses_to_simplify + .sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); - let pred_argss: Vec< VarTerms > = if let Some( - argss - ) = self.instance.clauses[clause].drop_lhs_pred(pred) { - argss.iter().cloned().collect() - } else { - bail!("inconsistent instance state") - } ; + let mut nu_clauses = vec![]; - log! { @7 " {} applications", pred_argss.len() } + for clause in self.clauses_to_simplify.drain(0..) { + info.clauses_rmed += 1; - // This is why we rev-sorted: - let clause = self.instance.forget_clause(clause) ? ; + log! { @7 "working on #{}", clause } - // Iterator over all combinations of elements from `def` with len - // `pred_argss`. - let mut all_combinations = CombinationIter::new( - def.iter(), pred_argss.len() - ) ? ; + let pred_argss: Vec = + if let Some(argss) = self.instance.clauses[clause].drop_lhs_pred(pred) { + argss.iter().cloned().collect() + } else { + bail!("inconsistent instance state") + }; + + log! { @7 " {} applications", pred_argss.len() } + + // This is why we rev-sorted: + let clause = self.instance.forget_clause(clause)?; + + // Iterator over all combinations of elements from `def` with len + // `pred_argss`. + let mut all_combinations = CombinationIter::new(def.iter(), pred_argss.len())?; + + // Go over all the combinations. + while let Some(combination) = all_combinations.next_combination() { + debug_assert_eq! { combination.len(), pred_argss.len() } + + let mut clause = clause.clone(); + + // Apply substitution and insert into the new clause. + for ((quant, def), pred_args) in combination.iter().zip(pred_argss.iter()) { + let quant_map = clause.nu_fresh_vars_for(quant); + + for term in def.terms() { + if let Some((term, _)) = term.subst_total(&(pred_args, &quant_map)) { + clause.insert_term(term); + } else { + bail!("unexpected total substitution failure on term {}", term) + } + } + + for (pred, argss) in def.preds() { + let pred = *pred; + for args in argss { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + if let Some((arg, _)) = arg.subst_total(&(pred_args, &quant_map)) { + nu_args.push(arg) + } else { + bail!( + "unexpected total substitution failure on arg {} \ + of ({} {})", + arg, + self.instance[pred], + args + ) + } + } + clause.insert_pred_app(pred, nu_args.into()); + } + } + } - // Go over all the combinations. - while let Some( - combination - ) = all_combinations.next_combination() { + nu_clauses.push(clause) + } + } - debug_assert_eq! { combination.len(), pred_argss.len() } + // Actually force the predicate. + self.force_pred(pred, TTerms::dnf(def))?; - let mut clause = clause.clone() ; + for clause in nu_clauses { + let is_new = self.instance.push_clause_unchecked(clause); + if is_new { + info.clauses_added += 1; + } + } - // Apply substitution and insert into the new clause. - for ((quant, def), pred_args) in combination.iter().zip( - pred_argss.iter() - ) { + info += self.simplify_all()?; + + self.check("after `force_dnf_left`")?; + + Ok(info) + } + + /// Retrieves the only clause a predicate appears in. + /// + /// Only legal if + /// + /// - `self.clauses_to_simplify.is_empty()` + /// - `pred` appears as a rhs in a single clause, and does not appear as a + /// lhs in said clause. + fn rm_only_lhs_clause_of(&mut self, pred: PrdIdx) -> Res { + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); + if let Some(clause) = self.clauses_to_simplify.pop() { + if self.clauses_to_simplify.pop().is_some() { + bail!( + "{} appears in more than one lhs", + conf.emph(&self.instance[pred].name) + ) + } + if self.instance.preds_of_clause(clause).1 == Some(pred) { + bail!( + "{} appears as both lhs and rhs", + conf.emph(&self.instance[pred].name) + ) + } + Ok(clause) + } else { + bail!("{} appears in no lhs", conf.emph(&self.instance[pred].name)) + } + } - let quant_map = clause.nu_fresh_vars_for(quant) ; + /// Forces the rhs occurrences of a predicate to be equal to something. + /// + /// If `pred` appears in `args /\ trms => pred`, the clause will become + /// `apps /\ pred_apps /\ trms /\ terms => pred_app`. + /// + /// Quantified variables are understood as universally quantified. + /// + /// Simplifies before returning. + /// + /// # Usage + /// + /// This function can only be called if `pred` appears exactly once as an + /// antecedent, say in clause `c`, and `c`'s consequent is not an application + /// of `pred`. + /// + /// Otherwise, it will return an error. + /// + /// # Consequences + /// + /// - forgets the one clause `pred` is in the lhs of + /// - forces `pred` to be `forall qvars, pred_app \/ (not /\ pred_apps) \/ + /// (not /\ terms)` + /// + /// # Used by + /// + /// - `SimpleOneLhs` + pub fn force_pred_right( + &mut self, + pred: PrdIdx, + qvars: Quantfed, + pred_app: Option<(PrdIdx, VarTerms)>, + negated: TTermSet, + ) -> Res { + self.check("before `force_pred_right`")?; + + let mut info = RedInfo::new(); + + let quant = Quant::forall(qvars); + + log_debug! { + "force pred right on {}...", conf.emph(& self.instance[pred].name) + } - for term in def.terms() { - if let Some((term, _)) = term.subst_total( - & (pred_args, & quant_map) - ) { - clause.insert_term(term) ; - } else { - bail!("unexpected total substitution failure on term {}", term) + // Update rhs clauses. + debug_assert! { self.clauses_to_simplify.is_empty() } + self.instance + .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); + + 'clause_iter: for clause in &self.clauses_to_simplify { + let clause = *clause; + log! { @4 "working on clause #{}", clause } + log! { @4 + "{}", self.instance[clause].to_string_info( + self.instance.preds() + ).unwrap() } - } - for (pred, argss) in def.preds() { - let pred = * pred ; - for args in argss { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - if let Some((arg, _)) = arg.subst_total( - & (pred_args, & quant_map) - ) { - nu_args.push(arg) - } else { - bail!( - "unexpected total substitution failure on arg {} \ - of ({} {})", arg, self.instance[pred], args - ) + let rhs = self.instance.clauses[clause].unset_rhs(); + + if let Some((prd, subst)) = rhs { + let qual_map = self.instance.clauses[clause].nu_fresh_vars_for(&quant); + + if pred == prd { + log! { @5 "generating new rhs" } + + // New rhs. + if let Some(&(prd, ref args)) = pred_app.as_ref() { + let mut nu_args = VarMap::with_capacity(args.len()); + + for arg in args.iter() { + if let Some((nu_arg, _)) = arg.subst_total(&(&subst, &qual_map)) { + nu_args.push(nu_arg) + } else { + bail!("unexpected failure during total substitution") + } + } + + self.instance.clause_force_rhs(clause, prd, nu_args)? + } + // No `else`, clause's rhs is already `None`. + + log! { @5 "generating new lhs pred apps" } + + // New lhs predicate applications. + for (pred, argss) in negated.preds() { + let pred = *pred; + for args in argss { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + if let Some((nu_arg, _)) = arg.subst_total(&(&subst, &qual_map)) { + nu_args.push(nu_arg) + } else { + bail!("unexpected failure during total substitution") + } + } + self.instance.clause_add_lhs_pred(clause, pred, nu_args) + } + } + + log! { @5 "generating new lhs terms" } + + // New lhs terms. + for term in negated.terms() { + if let Some((term, _)) = term.subst_total(&(&subst, &qual_map)) { + self.instance.clause_add_lhs_term(clause, term); + } + } + + // Explicitely continueing, otherwise the factored error message + // below will fire. + continue 'clause_iter; } - } - clause.insert_pred_app( pred, nu_args.into() ) ; } - } + bail!( + "inconsistent instance state, \ + `pred_to_clauses` and clauses out of sync" + ) } - nu_clauses.push( clause ) + info += self.simplify_clauses()?; - } + let clause_to_rm = self + .rm_only_lhs_clause_of(pred) + .chain_err(|| "illegal context for `force_pred_right`")?; - } + // Actually force the predicate. + self.force_pred( + pred, + TTerms::disj_of_pos_neg(quant, pred_app.map(|(pred, args)| (pred, args)), negated), + )?; - // Actually force the predicate. - self.force_pred( - pred, TTerms::dnf(def) - ) ? ; + info.clauses_rmed += 1; + self.instance.forget_clause(clause_to_rm)?; - for clause in nu_clauses { - let is_new = self.instance.push_clause_unchecked(clause) ; - if is_new { - info.clauses_added += 1 ; - } + self.check("after `force_pred_right`")?; + + Ok(info) } - info += self.simplify_all() ? ; + /// Unrolls some predicates. + /// + /// Simplifies before returning. + /// + /// For each clause `(pred args) /\ lhs => rhs`, adds `terms /\ lhs => rhs` + /// for terms in `pred_terms[p]`. + /// + /// Only unrolls negative clauses where `(pred args)` is not the only + /// application. + pub fn unroll(&mut self, pred: PrdIdx, terms: &[(Option, TTermSet)]) -> Res { + let mut info = RedInfo::new(); + let mut to_add = Vec::with_capacity(17); + let fls = term::fls(); - self.check("after `force_dnf_left`") ? ; + log_debug! { + "{} appears in {} clause's lhs", + conf.emph(& self[pred].name), + self.instance.pred_to_clauses[pred].0.len() + } - Ok(info) - } + for clause in &self.instance.pred_to_clauses[pred].0 { + let clause = &self.instance[*clause]; + // Negative clause and `pred` is the only application. + if clause.rhs().is_none() && clause.lhs_preds().len() == 1 { + continue; + } + let argss = if let Some(argss) = clause.lhs_preds().get(&pred) { + argss + } else { + bail!("inconsistent instance state, `pred_to_clauses` out of sync") + }; + + for &(ref quant, ref tterms) in terms { + let mut nu_clause = clause.clone_except_lhs_of(pred, "unrolling"); + let qual_map = nu_clause.nu_fresh_vars_for(quant); + + for args in argss { + conf.check_timeout()?; + if !tterms.preds().is_empty() { + bail!("trying to unroll predicate by another predicate") + } + for term in tterms.terms() { + if let Some((nu_term, _)) = term.subst_total(&(&args, &qual_map)) { + nu_clause.insert_term(nu_term); + } else { + bail!("unexpected failure during total substitution") + } + } + } - /// Retrieves the only clause a predicate appears in. - /// - /// Only legal if - /// - /// - `self.clauses_to_simplify.is_empty()` - /// - `pred` appears as a rhs in a single clause, and does not appear as a - /// lhs in said clause. - fn rm_only_lhs_clause_of(& mut self, pred: PrdIdx) -> Res {debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_lhs( - pred, & mut self.clauses_to_simplify - ) ; - if let Some(clause) = self.clauses_to_simplify.pop() { - if self.clauses_to_simplify.pop().is_some() { - bail!( - "{} appears in more than one lhs", - conf.emph(& self.instance[pred].name) - ) - } - if self.instance.preds_of_clause(clause).1 == Some(pred) { - bail!( - "{} appears as both lhs and rhs", - conf.emph(& self.instance[pred].name) - ) - } - Ok(clause) - } else { - bail!( - "{} appears in no lhs", - conf.emph(& self.instance[pred].name) - ) - } - } - - - - - /// Forces the rhs occurrences of a predicate to be equal to something. - /// - /// If `pred` appears in `args /\ trms => pred`, the clause will become - /// `apps /\ pred_apps /\ trms /\ terms => pred_app`. - /// - /// Quantified variables are understood as universally quantified. - /// - /// Simplifies before returning. - /// - /// # Usage - /// - /// This function can only be called if `pred` appears exactly once as an - /// antecedent, say in clause `c`, and `c`'s consequent is not an application - /// of `pred`. - /// - /// Otherwise, it will return an error. - /// - /// # Consequences - /// - /// - forgets the one clause `pred` is in the lhs of - /// - forces `pred` to be `forall qvars, pred_app \/ (not /\ pred_apps) \/ - /// (not /\ terms)` - /// - /// # Used by - /// - /// - `SimpleOneLhs` - pub fn force_pred_right( - & mut self, pred: PrdIdx, - qvars: Quantfed, - pred_app: Option< (PrdIdx, VarTerms) >, - negated: TTermSet, - ) -> Res { - self.check("before `force_pred_right`") ? ; - - let mut info = RedInfo::new() ; - - let quant = Quant::forall( qvars ) ; - - log_debug! { - "force pred right on {}...", conf.emph(& self.instance[pred].name) - } + log! { @4 + "pre-simplification {}", + nu_clause.to_string_info(& self.preds).unwrap() + } - // Update rhs clauses. - debug_assert! { self.clauses_to_simplify.is_empty() } - self.instance.unlink_pred_rhs( - pred, & mut self.clauses_to_simplify - ) ; - - 'clause_iter: for clause in & self.clauses_to_simplify { - let clause = * clause ; - log! { @4 "working on clause #{}", clause } - log! { @4 - "{}", self.instance[clause].to_string_info( - self.instance.preds() - ).unwrap() - } - - let rhs = self.instance.clauses[clause].unset_rhs() ; - - if let Some((prd, subst)) = rhs { - let qual_map = self.instance.clauses[clause].nu_fresh_vars_for( - & quant - ) ; - - if pred == prd { - - log! { @5 "generating new rhs" } - - // New rhs. - if let Some( & (prd, ref args) ) = pred_app.as_ref() { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - - for arg in args.iter() { - if let Some((nu_arg, _)) = arg.subst_total( - & (& subst, & qual_map) - ) { - nu_args.push(nu_arg) - } else { - bail!("unexpected failure during total substitution") - } - } + self.simplifier + .clause_propagate(&mut nu_clause, self.instance.preds())?; - self.instance.clause_force_rhs(clause, prd, nu_args) ? - } - // No `else`, clause's rhs is already `None`. + if !nu_clause.lhs_terms().contains(&fls) { + log! { @4 + "staging clause {}", + nu_clause.to_string_info(& self.preds).unwrap() + } + // nu_clause.from_unrolling = true ; + to_add.push(nu_clause) + } + } + } - log! { @5 "generating new lhs pred apps" } + log_debug! { "adding {} clauses", to_add.len() } - // New lhs predicate applications. - for (pred, argss) in negated.preds() { - let pred = * pred ; - for args in argss { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - if let Some((nu_arg, _)) = arg.subst_total( - & (& subst, & qual_map) - ) { - nu_args.push(nu_arg) + for mut clause in to_add { + if let Some(index) = self.instance.push_clause(clause)? { + let mut simplinfo = self.simplify_clause(index)?; + if simplinfo.clauses_rmed > 0 { + simplinfo.clauses_rmed -= 1 } else { - bail!("unexpected failure during total substitution") + simplinfo.clauses_added += 1 } - } - self.instance.clause_add_lhs_pred(clause, pred, nu_args) + info += simplinfo } - } - - log! { @5 "generating new lhs terms" } - - // New lhs terms. - for term in negated.terms() { - if let Some((term, _)) = term.subst_total( - & (& subst, & qual_map) - ) { - self.instance.clause_add_lhs_term(clause, term) ; - } - } - - // Explicitely continueing, otherwise the factored error message - // below will fire. - continue 'clause_iter } - } - - bail!( - "inconsistent instance state, \ - `pred_to_clauses` and clauses out of sync" - ) - } - - info += self.simplify_clauses() ? ; - - let clause_to_rm = self.rm_only_lhs_clause_of(pred).chain_err( - || "illegal context for `force_pred_right`" - ) ? ; - - // Actually force the predicate. - self.force_pred( - pred, TTerms::disj_of_pos_neg( - quant, pred_app.map( - |(pred, args)| ( pred, args ) - ), negated - ) - ) ? ; - - info.clauses_rmed += 1 ; - self.instance.forget_clause(clause_to_rm) ? ; - - self.check("after `force_pred_right`") ? ; - - Ok(info) - } - - - /// Unrolls some predicates. - /// - /// Simplifies before returning. - /// - /// For each clause `(pred args) /\ lhs => rhs`, adds `terms /\ lhs => rhs` - /// for terms in `pred_terms[p]`. - /// - /// Only unrolls negative clauses where `(pred args)` is not the only - /// application. - pub fn unroll( - & mut self, pred: PrdIdx, terms: & [ (Option, TTermSet) ] - ) -> Res { - let mut info = RedInfo::new() ; - let mut to_add = Vec::with_capacity(17) ; - let fls = term::fls() ; - - log_debug! { - "{} appears in {} clause's lhs", - conf.emph(& self[pred].name), - self.instance.pred_to_clauses[pred].0.len() - } - - for clause in & self.instance.pred_to_clauses[pred].0 { - let clause = & self.instance[* clause] ; - // Negative clause and `pred` is the only application. - if clause.rhs().is_none() && clause.lhs_preds().len() == 1 { - continue - } + self.check("after unroll")?; + + Ok(info) + } + + /// Reverse unrolls some predicates. + /// + /// Simplifies before returning. + /// + /// For each clause `lhs => (pred args)`, adds `(not terms) /\ lhs => false` + /// for terms in `pred_terms[p]`. + /// + /// Only unrolls clauses which have at least one lhs predicate application. + pub fn reverse_unroll( + &mut self, + pred: PrdIdx, + terms: &[(Option, TermSet)], + ) -> Res { + let mut info = RedInfo::new(); + let mut to_add = Vec::with_capacity(17); + let fls = term::fls(); + + for clause in &self.instance.pred_to_clauses[pred].1 { + let clause = &self.instance[*clause]; + + // Negative clause and `pred` is the only application. + if clause.lhs_preds().is_empty() { + continue; + } - let argss = if let Some(argss) = clause.lhs_preds().get(& pred) { - argss - } else { - bail!( "inconsistent instance state, `pred_to_clauses` out of sync" ) - } ; + let args = if let Some((p, args)) = clause.rhs() { + debug_assert_eq! { p, pred } + args + } else { + bail!("inconsistent instance state") + }; + + for &(ref quant, ref terms) in terms { + let mut nu_clause = clause.clone_with_rhs(None, "r_unroll"); + let qual_map = nu_clause.nu_fresh_vars_for(quant); + + for term in terms { + conf.check_timeout()?; + if let Some((nu_term, _)) = term.subst_total(&(&args, &qual_map)) { + nu_clause.insert_term(nu_term); + } else { + bail!("unexpected failure during total substitution") + } + } - for & (ref quant, ref tterms) in terms { - let mut nu_clause = clause.clone_except_lhs_of(pred, "unrolling") ; - let qual_map = nu_clause.nu_fresh_vars_for(quant) ; + self.simplifier + .clause_propagate(&mut nu_clause, self.instance.preds())?; - for args in argss { - conf.check_timeout() ? ; - if ! tterms.preds().is_empty() { - bail!("trying to unroll predicate by another predicate") - } - for term in tterms.terms() { - if let Some((nu_term, _)) = term.subst_total( - & (& args, & qual_map) - ) { - nu_clause.insert_term(nu_term) ; - } else { - bail!("unexpected failure during total substitution") + if !nu_clause.lhs_terms().contains(&fls) { + // nu_clause.from_unrolling = true ; + to_add.push(nu_clause) + } } - } } - log! { @4 - "pre-simplification {}", - nu_clause.to_string_info(& self.preds).unwrap() + for mut clause in to_add { + log! { @4 + "adding clause {}", + clause.to_string_info(& self.preds).unwrap() + } + if let Some(index) = self.instance.push_clause(clause)? { + let mut simplinfo = self.simplify_clause(index)?; + if simplinfo.clauses_rmed > 0 { + simplinfo.clauses_rmed -= 1 + } else { + simplinfo.clauses_added += 1 + } + info += simplinfo + } } - self.simplifier.clause_propagate( - & mut nu_clause, self.instance.preds() - ) ? ; + self.check("after runroll")?; + + Ok(info) + } + + /// Removes some arguments for a predicate. + /// + /// Returns `true` if something happened. + pub fn rm_args_of(&mut self, pred: PrdIdx, to_keep: &VarSet) -> Res { + macro_rules! rm_args { + (from $args:expr, keep nothing, swap $nu_args:expr) => {{ + debug_assert!($nu_args.is_empty()); + ::std::mem::swap($nu_args, $args); + $nu_args.clear(); + }}; + (from $args:expr, keep $to_keep:expr, to $nu_args:expr) => {{ + debug_assert!($nu_args.is_empty()); + for (var, arg) in $args.index_iter() { + if $to_keep.contains(&var) { + $nu_args.push(arg.clone()) + } + } + }}; + } - if ! nu_clause.lhs_terms().contains( & fls ) { - log! { @4 - "staging clause {}", - nu_clause.to_string_info(& self.preds).unwrap() - } - // nu_clause.from_unrolling = true ; - to_add.push( nu_clause ) + log! { @4 + "working on {} ({}/{})", + self[pred], to_keep.len(), self[pred].sig.len() } - } - } - log_debug! { "adding {} clauses", to_add.len() } + let mut rmed = 0; + let mut var_map = VarMap::with_capacity(to_keep.len()); + let mut nu_sig = VarMap::with_capacity(to_keep.len()); - for mut clause in to_add { - if let Some(index) = self.instance.push_clause(clause) ? { - let mut simplinfo = self.simplify_clause(index) ? ; - if simplinfo.clauses_rmed > 0 { - simplinfo.clauses_rmed -= 1 - } else { - simplinfo.clauses_added += 1 + for (var, typ) in self[pred].sig.index_iter() { + if to_keep.contains(&var) { + // Re-route current **new** var to the original variable `var` is + // pointing to. + var_map.push(self.old_preds[pred].1[var]); + nu_sig.push(typ.clone()) + } else { + rmed += 1 + } } - info += simplinfo - } - } - self.check("after unroll") ? ; - - Ok(info) - } - - - /// Reverse unrolls some predicates. - /// - /// Simplifies before returning. - /// - /// For each clause `lhs => (pred args)`, adds `(not terms) /\ lhs => false` - /// for terms in `pred_terms[p]`. - /// - /// Only unrolls clauses which have at least one lhs predicate application. - pub fn reverse_unroll( - & mut self, pred: PrdIdx, terms: & [ (Option, TermSet) ] - ) -> Res { - let mut info = RedInfo::new() ; - let mut to_add = Vec::with_capacity(17) ; - let fls = term::fls() ; - - for clause in & self.instance.pred_to_clauses[pred].1 { - let clause = & self.instance[* clause] ; - - // Negative clause and `pred` is the only application. - if clause.lhs_preds().is_empty() { - continue - } - - let args = if let Some((p, args)) = clause.rhs() { - debug_assert_eq! { p, pred } - args - } else { - bail!("inconsistent instance state") - } ; - - for & (ref quant, ref terms) in terms { - let mut nu_clause = clause.clone_with_rhs(None, "r_unroll") ; - let qual_map = nu_clause.nu_fresh_vars_for(quant) ; - - for term in terms { - conf.check_timeout() ? ; - if let Some((nu_term, _)) = term.subst_total( - & (& args, & qual_map) - ) { - nu_clause.insert_term(nu_term) ; - } else { - bail!("unexpected failure during total substitution") - } - } + // Update `preds` with the new signature. + self.instance.preds[pred].sig = nu_sig; + // Update `old_preds`'s map. + self.instance.old_preds[pred].1 = var_map; + + // Propagate removal to clauses. + let (ref lhs, ref rhs) = self.instance.pred_to_clauses[pred]; - self.simplifier.clause_propagate( - & mut nu_clause, self.instance.preds() - ) ? ; + for clause in lhs { + self.instance.clauses[*clause].lhs_map_args_of(pred, |args| { + let mut nu_args = VarMap::with_capacity(args.len() - to_keep.len()); + rm_args! { from args, keep to_keep, to nu_args } + nu_args.into() + }); - if ! nu_clause.lhs_terms().contains( & fls ) { - // nu_clause.from_unrolling = true ; - to_add.push( nu_clause ) + conf.check_timeout()? } - } - } - for mut clause in to_add { - log! { @4 - "adding clause {}", - clause.to_string_info(& self.preds).unwrap() - } - if let Some(index) = self.instance.push_clause(clause) ? { - let mut simplinfo = self.simplify_clause(index) ? ; - if simplinfo.clauses_rmed > 0 { - simplinfo.clauses_rmed -= 1 - } else { - simplinfo.clauses_added += 1 + for clause in rhs { + debug_assert! { self.instance.clauses[* clause].rhs().is_some() } + self.instance.clauses[*clause].rhs_map_args(|p, args| { + debug_assert_eq!(pred, p); + let mut nu_args = VarMap::with_capacity(args.len() - to_keep.len()); + rm_args! { from args, keep to_keep, to nu_args } + (p, nu_args.into()) + }); + conf.check_timeout()? } - info += simplinfo - } - } - self.check("after runroll") ? ; - - Ok(info) - } - - - /// Removes some arguments for a predicate. - /// - /// Returns `true` if something happened. - pub fn rm_args_of( - & mut self, pred: PrdIdx, to_keep: & VarSet - ) -> Res { - macro_rules! rm_args { - (from $args:expr, keep nothing, swap $nu_args:expr) => ({ - debug_assert!( $nu_args.is_empty() ) ; - ::std::mem::swap($nu_args, $args) ; - $nu_args.clear() ; - }) ; - (from $args:expr, keep $to_keep:expr, to $nu_args:expr) => ({ - debug_assert!( $nu_args.is_empty() ) ; - for (var, arg) in $args.index_iter() { - if $to_keep.contains(& var) { - $nu_args.push( arg.clone() ) + Ok(rmed) + } + + /// Removes all predicate arguments not in `to_keep`. + /// + /// Simplifies before returning. + /// + /// Removes useless arguments in the clauses. Updates `old_preds`, + /// `pred_terms`. + pub fn rm_args(&mut self, to_keep: PrdHMap) -> Res { + if_debug! { + log_debug! { " rm_args ({})", to_keep.len() } + log_debug! { " to keep {{" } + for (pred, vars) in to_keep.iter() { + let mut s = String::new() ; + for var in vars { + s.push_str(" ") ; + s.push_str( & var.default_str() ) + } + log_debug! { " {}:{}", self[* pred], s } } + log_debug! { " }}" } } - }) ; - } - log! { @4 - "working on {} ({}/{})", - self[pred], to_keep.len(), self[pred].sig.len() - } - - let mut rmed = 0 ; - let mut var_map = VarMap::with_capacity( to_keep.len() ) ; - let mut nu_sig = VarMap::with_capacity( to_keep.len() ) ; - - for (var, typ) in self[pred].sig.index_iter() { - if to_keep.contains(& var) { - // Re-route current **new** var to the original variable `var` is - // pointing to. - var_map.push( self.old_preds[pred].1[var] ) ; - nu_sig.push(typ.clone()) - } else { - rmed += 1 - } - } + self.check("rm_args")?; - // Update `preds` with the new signature. - self.instance.preds[pred].sig = nu_sig ; - // Update `old_preds`'s map. - self.instance.old_preds[pred].1 = var_map ; + let mut info = RedInfo::new(); - // Propagate removal to clauses. - let (ref lhs, ref rhs) = self.instance.pred_to_clauses[pred] ; - - for clause in lhs { - - self.instance.clauses[ - * clause - ].lhs_map_args_of( - pred, |args| { - let mut nu_args = VarMap::with_capacity( - args.len() - to_keep.len() - ) ; - rm_args! { from args, keep to_keep, to nu_args } - nu_args.into() + // Remove args from forced predicates. + for tterms_opt in &mut self.instance.pred_terms { + if let Some(tterms) = tterms_opt.as_mut() { + tterms.remove_vars(&to_keep) + } } - ) ; - conf.check_timeout() ? - } - - for clause in rhs { - debug_assert! { self.instance.clauses[* clause].rhs().is_some() } - self.instance.clauses[* clause].rhs_map_args( - |p, args| { - debug_assert_eq!( pred, p ) ; - let mut nu_args = VarMap::with_capacity( - args.len() - to_keep.len() - ) ; - rm_args! { from args, keep to_keep, to nu_args } - ( p, nu_args.into() ) - } - ) ; - conf.check_timeout() ? - } + let mut did_something = false; - Ok(rmed) - } - - - /// Removes all predicate arguments not in `to_keep`. - /// - /// Simplifies before returning. - /// - /// Removes useless arguments in the clauses. Updates `old_preds`, - /// `pred_terms`. - pub fn rm_args(& mut self, to_keep: PrdHMap) -> Res { - if_debug! { - log_debug! { " rm_args ({})", to_keep.len() } - log_debug! { " to keep {{" } - for (pred, vars) in to_keep.iter() { - let mut s = String::new() ; - for var in vars { - s.push_str(" ") ; - s.push_str( & var.default_str() ) - } - log_debug! { " {}:{}", self[* pred], s } - } - log_debug! { " }}" } - } + // Remove args from applications in clauses. + for (pred, to_keep) in to_keep { + debug_assert!(to_keep.len() <= self[pred].sig.len()); + log! { @4 "- {}", self[pred] } + if to_keep.len() == self[pred].sig.len() { + log! { @4 "skipping" } + continue; + } - self.check("rm_args") ? ; + did_something = true; - let mut info = RedInfo::new() ; + let rmed = self.rm_args_of(pred, &to_keep)?; + info.args_rmed += rmed + } - // Remove args from forced predicates. - for tterms_opt in & mut self.instance.pred_terms { - if let Some(tterms) = tterms_opt.as_mut() { - tterms.remove_vars(& to_keep) - } - } + if !did_something { + return Ok(info); + } - let mut did_something = false ; + // Simplify the clauses we just updated. + debug_assert! { self.clauses_to_simplify.is_empty() } - // Remove args from applications in clauses. - for (pred, to_keep) in to_keep { - debug_assert!( to_keep.len() <= self[pred].sig.len() ) ; - log! { @4 "- {}", self[pred] } - if to_keep.len() == self[pred].sig.len() { - log! { @4 "skipping" } - continue - } + info += self.simplify_all()?; - did_something = true ; + self.check("after `rm_args`")?; - let rmed = self.rm_args_of(pred, & to_keep) ? ; - info.args_rmed += rmed + Ok(info) } - if ! did_something { return Ok(info) } - - // Simplify the clauses we just updated. - debug_assert! { self.clauses_to_simplify.is_empty() } - - info += self.simplify_all() ? ; - - self.check("after `rm_args`") ? ; - - Ok(info) - } - - + /// Removes all clauses in which `pred` is in the rhs. + /// + /// Does not run simplifications. + pub fn rm_rhs_clauses_of(&mut self, pred: PrdIdx) -> Res { + debug_assert! { self.clauses_to_simplify.is_empty() } + let mut info = RedInfo::new(); + let to_rm = self.instance.pred_to_clauses[pred].1.clone(); + // self.instance.unlink_pred_rhs(pred, & mut self.clauses_to_simplify) ; + info.clauses_rmed += to_rm.len(); + self.instance + .forget_clauses(&mut to_rm.into_iter().collect())?; + Ok(info) + } + /// Checks the predicates' definition verify the current instance. + /// + /// Returns `true` if they work (sat). + /// + /// # Errors if + /// + /// - some predicates are not defined + pub fn check_pred_defs(&mut self) -> Res { + if self.active_pred_count() > 0 { + bail!("can't check predicate definitions, some predicates are not defined") + } - /// Removes all clauses in which `pred` is in the rhs. - /// - /// Does not run simplifications. - pub fn rm_rhs_clauses_of(& mut self, pred: PrdIdx) -> Res { - debug_assert! { self.clauses_to_simplify.is_empty() } - let mut info = RedInfo::new() ; - let to_rm = self.instance.pred_to_clauses[pred].1.clone() ; - // self.instance.unlink_pred_rhs(pred, & mut self.clauses_to_simplify) ; - info.clauses_rmed += to_rm.len() ; - self.instance.forget_clauses( & mut to_rm.into_iter().collect() ) ? ; - Ok(info) - } + let set = PrdSet::new(); + self.instance.finalize()?; + for pred in self.instance.sorted_forced_terms() { + let pred = *pred; + log! { @4 "definining {}", self[pred] } + + let sig: Vec<_> = self.instance[pred] + .sig + .index_iter() + .map(|(var, typ)| (var.default_str(), typ.get())) + .collect(); + + if let Some(ref def) = self.instance.pred_terms[pred] { + self.solver.define_fun_with( + &self.instance[pred].name, + &sig, + &typ::RTyp::Bool, + def, + &(&set, &set, &self.instance.preds), + )? + } else { + bail!( + "can't check predicate definitions, predicate {} is not defined", + self.instance.preds[pred] + ) + } + } + self.solver.comment("checking side clauses")?; + for clause in &self.instance.side_clauses { + self.solver.push(1)?; + for info in clause.vars() { + if info.active { + self.solver + .declare_const(&info.idx.default_str(), info.typ.get())? + } + } + self.solver + .assert_with(clause, &(false, &set, &set, &self.instance.preds))?; + let sat = check_sat!(self); - /// Checks the predicates' definition verify the current instance. - /// - /// Returns `true` if they work (sat). - /// - /// # Errors if - /// - /// - some predicates are not defined - pub fn check_pred_defs(& mut self) -> Res { - if self.active_pred_count() > 0 { - bail!( - "can't check predicate definitions, some predicates are not defined" - ) - } + self.solver.pop(1)?; + if sat { + return Ok(false); + } + } - let set = PrdSet::new() ; - self.instance.finalize() ? ; - for pred in self.instance.sorted_forced_terms() { - let pred = * pred ; - log! { @4 "definining {}", self[pred] } - - let sig: Vec<_> = self.instance[pred].sig.index_iter().map( - |(var, typ)| (var.default_str(), typ.get()) - ).collect() ; - - if let Some(ref def) = self.instance.pred_terms[pred] { - self.solver.define_fun_with( - & self.instance[pred].name, - & sig, - & typ::RTyp::Bool, - def, - & (& set, & set, & self.instance.preds) - ) ? - } else { - bail!( - "can't check predicate definitions, predicate {} is not defined", - self.instance.preds[pred] - ) - } - } + self.solver.comment("checking clauses")?; - self.solver.comment("checking side clauses") ? ; + for clause in &self.instance.clauses { + self.solver.push(1)?; + for info in clause.vars() { + if info.active { + self.solver + .declare_const(&info.idx.default_str(), info.typ.get())? + } + } + self.solver + .assert_with(clause, &(false, &set, &set, &self.instance.preds))?; - for clause in & self.instance.side_clauses { - self.solver.push(1) ? ; - for info in clause.vars() { - if info.active { - self.solver.declare_const( - & info.idx.default_str(), info.typ.get() - ) ? + let sat = check_sat!(self); + self.solver.pop(1)?; + if sat { + return Ok(false); + } } - } - self.solver.assert_with( - clause, & (false, & set, & set, & self.instance.preds) - ) ? ; - - let sat = check_sat!(self) ; - self.solver.pop(1) ? ; - if sat { - return Ok(false) - } - } + self.reset_solver()?; - self.solver.comment("checking clauses") ? ; - - for clause in & self.instance.clauses { - self.solver.push(1) ? ; - for info in clause.vars() { - if info.active { - self.solver.declare_const( - & info.idx.default_str(), info.typ.get() - ) ? - } - } - self.solver.assert_with( - clause, & (false, & set, & set, & self.instance.preds) - ) ? ; - - let sat = check_sat!(self) ; - self.solver.pop(1) ? ; - if sat { - return Ok(false) - } + Ok(true) } - - self.reset_solver() ? ; - - Ok(true) - } - } - - impl<'a> ::std::ops::Deref for PreInstance<'a> { - type Target = Instance ; - fn deref(& self) -> & Instance { - self.instance - } + type Target = Instance; + fn deref(&self) -> &Instance { + self.instance + } } impl<'a> ::std::ops::DerefMut for PreInstance<'a> { - fn deref_mut(& mut self) -> & mut Instance { - self.instance - } + fn deref_mut(&mut self) -> &mut Instance { + self.instance + } } - - - - /// Simplifies clauses. /// /// The goal of this type is to avoid reallocation and compartment the clause /// simplification process. pub struct ClauseSimplifier { - /// Factored variable substitution. - subst: VarHMap, - /// Terms to add to the clause. - terms_to_add: Vec, - /// Variables that can't be inserted during the substitution. - var_set: VarSet, + /// Factored variable substitution. + subst: VarHMap, + /// Terms to add to the clause. + terms_to_add: Vec, + /// Variables that can't be inserted during the substitution. + var_set: VarSet, } impl ClauseSimplifier { - /// Constructor. - pub fn new() -> Self { - ClauseSimplifier { - subst: VarHMap::with_capacity(20), - terms_to_add: Vec::with_capacity(29), - var_set: VarSet::with_capacity(27), - } - } - - /// Propagates booleans at top level. - /// - /// Returns `None` iff the clause is trivial, otherwise returns `true` is - /// something changed. - pub fn clause_propagate_bool( - & mut self, clause: & mut Clause, _preds: & PrdInfos - ) -> Res> { - debug_assert! { self.terms_to_add.is_empty() } - debug_assert! { self.subst.is_empty() } - debug_assert! { self.var_set.is_empty() } - - let mut trivial = false ; - let mut changed = false ; - - // println!("") ; - // println!("{}", clause.to_string_info(_preds).unwrap()) ; - - // Propagate all top-level (negated) boolean variables. - for term in clause.lhs_terms() { - let plain_term = term.rm_neg() ; - let plain_term = if let Some(plain) = plain_term.as_ref() { - plain - } else { - term - } ; - if let Some(idx) = plain_term.var_idx() { - let (value, b_value) = if plain_term == term { - (term::tru(), true) - } else { - (term::fls(), false) - } ; - // println!("term: {}", term) ; - // println!(" {}", clause[idx].name) ; - // println!(" {}", value) ; - let prev = self.subst.insert(idx, value) ; - if let Some(prev) = prev { - match prev.bool() { - Some(prev) if b_value != prev => { - trivial = true ; - break - }, - _ => unreachable!( - "problem in bool var propagation: value is {}, prev is {}", - b_value, prev - ), - } + /// Constructor. + pub fn new() -> Self { + ClauseSimplifier { + subst: VarHMap::with_capacity(20), + terms_to_add: Vec::with_capacity(29), + var_set: VarSet::with_capacity(27), } - } } - if trivial { - // println!("trivializing clause") ; - clause.insert_term( term::fls() ) ; - self.subst.clear() ; - return Ok(None) - } else if ! self.subst.is_empty() { - changed = clause.subst(& self.subst) || changed ; - for (var, _) in self.subst.drain() { - clause.deactivate(var) ? - } - } - // println!("{}", clause.to_string_info(_preds).unwrap()) ; - Ok( Some(changed) ) - } - - - /// Works on an equality. - /// - /// Returns `true` if the equality was not registered as a substitution - /// (`skip`). - fn work_on_eq( - & mut self, clause: & mut Clause, eq: & Term - ) -> Res { - macro_rules! skip { - () => ({ - log! { @5 "skipping" } - return Ok(true) - }) ; - } - - let args = if let Some(args) = eq.eq_inspect() { - args - } else { - bail!("illegal call to `work_on_eq` on {}", eq) - } ; + /// Propagates booleans at top level. + /// + /// Returns `None` iff the clause is trivial, otherwise returns `true` is + /// something changed. + pub fn clause_propagate_bool( + &mut self, + clause: &mut Clause, + _preds: &PrdInfos, + ) -> Res> { + debug_assert! { self.terms_to_add.is_empty() } + debug_assert! { self.subst.is_empty() } + debug_assert! { self.var_set.is_empty() } + + let mut trivial = false; + let mut changed = false; + + // println!("") ; + // println!("{}", clause.to_string_info(_preds).unwrap()) ; + + // Propagate all top-level (negated) boolean variables. + for term in clause.lhs_terms() { + let plain_term = term.rm_neg(); + let plain_term = if let Some(plain) = plain_term.as_ref() { + plain + } else { + term + }; + if let Some(idx) = plain_term.var_idx() { + let (value, b_value) = if plain_term == term { + (term::tru(), true) + } else { + (term::fls(), false) + }; + // println!("term: {}", term) ; + // println!(" {}", clause[idx].name) ; + // println!(" {}", value) ; + let prev = self.subst.insert(idx, value); + if let Some(prev) = prev { + match prev.bool() { + Some(prev) if b_value != prev => { + trivial = true; + break; + } + _ => unreachable!( + "problem in bool var propagation: value is {}, prev is {}", + b_value, prev + ), + } + } + } + } - if args.len() != 2 { - skip!() - } + if trivial { + // println!("trivializing clause") ; + clause.insert_term(term::fls()); + self.subst.clear(); + return Ok(None); + } else if !self.subst.is_empty() { + changed = clause.subst(&self.subst) || changed; + for (var, _) in self.subst.drain() { + clause.deactivate(var)? + } + } + // println!("{}", clause.to_string_info(_preds).unwrap()) ; + Ok(Some(changed)) + } + + /// Works on an equality. + /// + /// Returns `true` if the equality was not registered as a substitution + /// (`skip`). + fn work_on_eq(&mut self, clause: &mut Clause, eq: &Term) -> Res { + macro_rules! skip { + () => {{ + log! { @5 "skipping" } + return Ok(true); + }}; + } - match eq.as_subst() { - - Some((var, term)) => { - if self.subst.get(& var).is_some() { - skip!() - } - - let vars = term::vars(& term) ; - if vars.contains(& var) - || self.var_set.contains(& var) - || term::vars(& term).into_iter().any( - |v| v == var || self.subst.get(& v).is_some() - ) { - skip!() - } - - log! { @4 "{} -> {}", var.default_str(), term } - - let prev = self.subst.insert(var, term) ; - debug_assert_eq! { prev, None } - - self.var_set.extend( vars ) - }, - - // Two terms. - None => { - debug_assert_eq! { args[1].typ(), args[0].typ() } - let nu_term = if args[0].typ().is_bool() { - let not_lhs = term::not( args[0].clone() ) ; - let not_rhs = term::not( args[1].clone() ) ; - if clause.lhs_terms().contains(& args[0]) - || self.terms_to_add.iter().any( - |t| t == & args[0] - ) { - args[1].clone() - } else if clause.lhs_terms().contains(& args[1]) - || self.terms_to_add.iter().any( - |t| t == & args[1] - ) { - args[0].clone() - } else if clause.lhs_terms().contains(& not_lhs) - || self.terms_to_add.iter().any( - |t| t == & not_lhs - ) { - not_rhs - } else if clause.lhs_terms().contains(& not_rhs) - || self.terms_to_add.iter().any( - |t| t == & not_rhs - ) { - not_lhs - } else { - skip!() - } + let args = if let Some(args) = eq.eq_inspect() { + args } else { - skip!() - } ; + bail!("illegal call to `work_on_eq` on {}", eq) + }; - log! { @5 "{} -> {}", eq, nu_term } + if args.len() != 2 { + skip!() + } - clause.insert_term( nu_term ) ; - }, + match eq.as_subst() { + Some((var, term)) => { + if self.subst.get(&var).is_some() { + skip!() + } - } + let vars = term::vars(&term); + if vars.contains(&var) || self.var_set.contains(&var) || term::vars(&term) + .into_iter() + .any(|v| v == var || self.subst.get(&v).is_some()) + { + skip!() + } - Ok(false) - } + log! { @4 "{} -> {}", var.default_str(), term } + let prev = self.subst.insert(var, term); + debug_assert_eq! { prev, None } - /// Propagates equalities in a clause. - pub fn clause_propagate( - & mut self, clause: & mut Clause, _preds: & PrdInfos - ) -> Res<()> { - debug_assert! { self.terms_to_add.is_empty() } - debug_assert! { self.subst.is_empty() } - debug_assert! { self.var_set.is_empty() } + self.var_set.extend(vars) + } + // Two terms. + None => { + debug_assert_eq! { args[1].typ(), args[0].typ() } + let nu_term = if args[0].typ().is_bool() { + let not_lhs = term::not(args[0].clone()); + let not_rhs = term::not(args[1].clone()); + if clause.lhs_terms().contains(&args[0]) + || self.terms_to_add.iter().any(|t| t == &args[0]) + { + args[1].clone() + } else if clause.lhs_terms().contains(&args[1]) + || self.terms_to_add.iter().any(|t| t == &args[1]) + { + args[0].clone() + } else if clause.lhs_terms().contains(¬_lhs) + || self.terms_to_add.iter().any(|t| t == ¬_lhs) + { + not_rhs + } else if clause.lhs_terms().contains(¬_rhs) + || self.terms_to_add.iter().any(|t| t == ¬_rhs) + { + not_lhs + } else { + skip!() + } + } else { + skip!() + }; + log! { @5 "{} -> {}", eq, nu_term } - let mut eq = None ; + clause.insert_term(nu_term); + } + } - log! { @6 "working on {}", clause.to_string_info( _preds ).unwrap() } + Ok(false) + } - let mut changed = false ; + /// Propagates equalities in a clause. + pub fn clause_propagate(&mut self, clause: &mut Clause, _preds: &PrdInfos) -> Res<()> { + debug_assert! { self.terms_to_add.is_empty() } + debug_assert! { self.subst.is_empty() } + debug_assert! { self.var_set.is_empty() } - macro_rules! bool_prop { - () => ( - match self.clause_propagate_bool(clause, _preds) ? { - // Clause is trivial. - None => return Ok(()), - Some(true) => changed = true, - Some(false) => (), - } - ) - } + let mut eq = None; - bool_prop!() ; + log! { @6 "working on {}", clause.to_string_info( _preds ).unwrap() } - 'outter: loop { + let mut changed = false; - debug_assert_eq! { eq, None } - for term in clause.lhs_terms() { - if let Some((Op::Eql, _)) = term.app_inspect() { - eq = Some( term.clone() ) + macro_rules! bool_prop { + () => { + match self.clause_propagate_bool(clause, _preds)? { + // Clause is trivial. + None => return Ok(()), + Some(true) => changed = true, + Some(false) => (), + } + }; } - } - if let Some(eq) = ::std::mem::replace(& mut eq, None) { - log! { @4 "{}", eq } + bool_prop!(); - let was_there = clause.rm_term(& eq) ; - debug_assert! { was_there } + 'outter: loop { + debug_assert_eq! { eq, None } + for term in clause.lhs_terms() { + if let Some((Op::Eql, _)) = term.app_inspect() { + eq = Some(term.clone()) + } + } - let skip = self.work_on_eq(clause, & eq) ? ; + if let Some(eq) = ::std::mem::replace(&mut eq, None) { + log! { @4 "{}", eq } - if skip { - self.terms_to_add.push(eq) - } + let was_there = clause.rm_term(&eq); + debug_assert! { was_there } - } else { - if ! self.subst.is_empty() - || ! self.terms_to_add.is_empty() { - for term in self.terms_to_add.drain(0..) { - clause.insert_term(term) ; - } - changed = clause.subst(& self.subst) || changed ; - log! { @5 "yielding {}", clause.to_string_info( _preds ).unwrap() } - for (var, _) in self.subst.drain() { - clause.deactivate(var) ? - } - self.var_set.clear() - } + let skip = self.work_on_eq(clause, &eq)?; - bool_prop!() ; + if skip { + self.terms_to_add.push(eq) + } + } else { + if !self.subst.is_empty() || !self.terms_to_add.is_empty() { + for term in self.terms_to_add.drain(0..) { + clause.insert_term(term); + } + changed = clause.subst(&self.subst) || changed; + log! { @5 "yielding {}", clause.to_string_info( _preds ).unwrap() } + for (var, _) in self.subst.drain() { + clause.deactivate(var)? + } + self.var_set.clear() + } - if ! changed { - break 'outter - } else { - changed = false ; - } - } - } + bool_prop!(); - Ok(()) - } + if !changed { + break 'outter; + } else { + changed = false; + } + } + } + Ok(()) + } } diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 271980a0..0f83f7ab 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -1,11 +1,11 @@ //! The instance stores the predicates, the clauses, and a lot of information. -use common::* ; +use common::*; -pub mod info ; +pub mod info; #[cfg_attr(feature = "cargo-clippy", allow(module_inception))] -mod instance ; +mod instance; -pub mod preproc ; +pub mod preproc; -pub use self::instance::{ Clause, Instance, PreInstance } ; \ No newline at end of file +pub use self::instance::{Clause, Instance, PreInstance}; diff --git a/src/instance/preproc/arg_red.rs b/src/instance/preproc/arg_red.rs index 3a17936c..e6fe393f 100644 --- a/src/instance/preproc/arg_red.rs +++ b/src/instance/preproc/arg_red.rs @@ -1,10 +1,7 @@ //! Argument reduction. -use common::* ; -use instance::{ - Clause, preproc::RedStrat, instance::PreInstance -} ; - +use common::*; +use instance::{instance::PreInstance, preproc::RedStrat, Clause}; /// Argument reduction. /// @@ -14,226 +11,223 @@ use instance::{ /// [paper]: https://link.springer.com/chapter/10.1007%2F3-540-62718-9_6 /// (Redundant argument filtering of logic programs) pub struct ArgRed { - inner: ArgReductor, + inner: ArgReductor, } impl RedStrat for ArgRed { - fn name(& self) -> & 'static str { "arg_reduce" } + fn name(&self) -> &'static str { + "arg_reduce" + } - fn new(_: & Instance) -> Self { - ArgRed { - inner: ArgReductor::new(), + fn new(_: &Instance) -> Self { + ArgRed { + inner: ArgReductor::new(), + } } - } - - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let keep = self.inner.run(instance) ; - instance.rm_args(keep) - } -} + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let keep = self.inner.run(instance); + instance.rm_args(keep) + } +} /// Argument reduction context. pub struct ArgReductor { - /// Predicate arguments to keep. - keep: PrdMap, - /// Map from clauses to the variables appearing in their lhs. - lhs_vars: ClsMap>, - /// Map from clauses to the varibales appearing in their rhs. - rhs_vars: ClsMap< Option<(PrdIdx, VarMap)> > + /// Predicate arguments to keep. + keep: PrdMap, + /// Map from clauses to the variables appearing in their lhs. + lhs_vars: ClsMap>, + /// Map from clauses to the varibales appearing in their rhs. + rhs_vars: ClsMap)>>, } impl Default for ArgReductor { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl ArgReductor { - /// Constructor. - pub fn new() -> Self { - ArgReductor { - keep: PrdMap::new(), - lhs_vars: ClsMap::new(), - rhs_vars: ClsMap::new(), - } - } - - /// Prints itself. - #[allow(dead_code)] - fn print(& mut self, instance: & Instance) { - println!("keep {{") ; - for (pred, vars) in self.keep.index_iter() { - if instance.is_known(pred) { continue } - print!(" {}:", instance[pred]) ; - for var in vars { - print!(" {},", var.default_str()) - } - println!() - } - // println!("}} clauses {{") ; - // for (idx, _) in instance.clauses().index_iter() { - - // } - println!("}}") - } - - /// Initializes itself from an instance. - fn init(& mut self, instance: & Instance) { - self.keep.clear() ; - self.lhs_vars.clear() ; - self.rhs_vars.clear() ; - - // Empty set for each predicate. - for _ in instance.preds() { - self.keep.push( VarSet::new() ) + /// Constructor. + pub fn new() -> Self { + ArgReductor { + keep: PrdMap::new(), + lhs_vars: ClsMap::new(), + rhs_vars: ClsMap::new(), + } } - // Work on all clauses. - for (idx, clause) in instance.clauses().index_iter() { - debug_assert_eq! { self.lhs_vars.next_index(), idx } - debug_assert_eq! { self.rhs_vars.next_index(), idx } - - let mut lhs_map: VarHMap = VarHMap::new() ; - macro_rules! map { - ($map:ident: add $var:expr => $n:expr) => ({ - use std::ops::AddAssign ; - $map.entry($var).or_insert(0).add_assign(1) - }) - } - - // Increment variables appearing in terms by one. - for term in clause.lhs_terms() { - for var in term::vars(term) { - map! { lhs_map: add var => 1 } - } - } - // Increment variables appearing in pred apps by one. - for (_, argss) in clause.lhs_preds() { - for args in argss { - for arg in args.iter() { - for var in term::vars(arg) { - map! { lhs_map: add var => 1 } + /// Prints itself. + #[allow(dead_code)] + fn print(&mut self, instance: &Instance) { + println!("keep {{"); + for (pred, vars) in self.keep.index_iter() { + if instance.is_known(pred) { + continue; + } + print!(" {}:", instance[pred]); + for var in vars { + print!(" {},", var.default_str()) } - } + println!() } - } + // println!("}} clauses {{") ; + // for (idx, _) in instance.clauses().index_iter() { + + // } + println!("}}") + } - self.lhs_vars.push(lhs_map) ; + /// Initializes itself from an instance. + fn init(&mut self, instance: &Instance) { + self.keep.clear(); + self.lhs_vars.clear(); + self.rhs_vars.clear(); - // If there's a rhs, retrieve the map from its formal arguments to the - // variables appearing in the actual arguments. - let rhs_map = if let Some((pred, args)) = clause.rhs() { - let mut rhs_map = VarMap::new() ; - for arg in args.iter() { - rhs_map.push( term::vars(arg) ) + // Empty set for each predicate. + for _ in instance.preds() { + self.keep.push(VarSet::new()) } - Some((pred, rhs_map)) - } else { - None - } ; - self.rhs_vars.push(rhs_map ) ; - } - } - - /// Checks if a variable appears more than once in a clause. - /// - /// Returns `true` if `cvar` appears - /// - /// - in `clause.lhs_terms()` - /// - more than once in `clause.lhs_preds()` - /// - in the rhs `(pred args)` of the clause in position `i`, and - /// `self.keep.get(pred).unwrap().get(i)` is true. - pub fn should_keep( - & self, cvar: VarIdx, idx: ClsIdx - ) -> bool { - if * self.lhs_vars[idx].get(& cvar).expect( - "inconsistent ArgReductor state" - ) > 1 { - return true + // Work on all clauses. + for (idx, clause) in instance.clauses().index_iter() { + debug_assert_eq! { self.lhs_vars.next_index(), idx } + debug_assert_eq! { self.rhs_vars.next_index(), idx } + + let mut lhs_map: VarHMap = VarHMap::new(); + macro_rules! map { + ($map:ident: add $var:expr => $n:expr) => {{ + use std::ops::AddAssign; + $map.entry($var).or_insert(0).add_assign(1) + }}; + } + + // Increment variables appearing in terms by one. + for term in clause.lhs_terms() { + for var in term::vars(term) { + map! { lhs_map: add var => 1 } + } + } + // Increment variables appearing in pred apps by one. + for (_, argss) in clause.lhs_preds() { + for args in argss { + for arg in args.iter() { + for var in term::vars(arg) { + map! { lhs_map: add var => 1 } + } + } + } + } + self.lhs_vars.push(lhs_map); + + // If there's a rhs, retrieve the map from its formal arguments to the + // variables appearing in the actual arguments. + let rhs_map = if let Some((pred, args)) = clause.rhs() { + let mut rhs_map = VarMap::new(); + for arg in args.iter() { + rhs_map.push(term::vars(arg)) + } + Some((pred, rhs_map)) + } else { + None + }; + + self.rhs_vars.push(rhs_map); + } } - if let Some((pred, pvar_map)) = & self.rhs_vars[idx] { - for (pvar, cvars) in pvar_map.index_iter() { - if self.keep[* pred].contains(& pvar) - && cvars.contains(& cvar) { - return true + /// Checks if a variable appears more than once in a clause. + /// + /// Returns `true` if `cvar` appears + /// + /// - in `clause.lhs_terms()` + /// - more than once in `clause.lhs_preds()` + /// - in the rhs `(pred args)` of the clause in position `i`, and + /// `self.keep.get(pred).unwrap().get(i)` is true. + pub fn should_keep(&self, cvar: VarIdx, idx: ClsIdx) -> bool { + if *self.lhs_vars[idx] + .get(&cvar) + .expect("inconsistent ArgReductor state") + > 1 + { + return true; + } + + if let Some((pred, pvar_map)) = &self.rhs_vars[idx] { + for (pvar, cvars) in pvar_map.index_iter() { + if self.keep[*pred].contains(&pvar) && cvars.contains(&cvar) { + return true; + } + } } - } + + false } - false - } - - /// Works on a clause. - /// - /// Returns `true` iff something changed. - fn work_on( - & mut self, clause: & Clause, idx: ClsIdx, instance: & Instance - ) -> bool { - let mut changed = false ; - - 'all_preds: for (pred, argss) in clause.lhs_preds() { - let pred = * pred ; - if self.keep[pred].len() == instance[pred].sig.len() { - continue 'all_preds - } - - for args in argss { - for (pvar, arg) in args.index_iter() { - if self.keep[pred].contains(& pvar) { continue } - - let keep = if let Some(cvar) = arg.var_idx() { - self.should_keep(cvar, idx) - } else { - true - } ; - - if keep { - let is_new = self.keep[pred].insert(pvar) ; - debug_assert! { is_new } - changed = true - } + /// Works on a clause. + /// + /// Returns `true` iff something changed. + fn work_on(&mut self, clause: &Clause, idx: ClsIdx, instance: &Instance) -> bool { + let mut changed = false; + + 'all_preds: for (pred, argss) in clause.lhs_preds() { + let pred = *pred; + if self.keep[pred].len() == instance[pred].sig.len() { + continue 'all_preds; + } + + for args in argss { + for (pvar, arg) in args.index_iter() { + if self.keep[pred].contains(&pvar) { + continue; + } + + let keep = if let Some(cvar) = arg.var_idx() { + self.should_keep(cvar, idx) + } else { + true + }; + + if keep { + let is_new = self.keep[pred].insert(pvar); + debug_assert! { is_new } + changed = true + } + } + } } - } + + changed } - changed - } + /// Runs itself on all clauses of an instance. + pub fn run(&mut self, instance: &Instance) -> PrdHMap { + self.init(instance); - /// Runs itself on all clauses of an instance. - pub fn run( - & mut self, instance: & Instance - ) -> PrdHMap { - self.init(instance) ; + let mut changed = true; - let mut changed = true ; + // println!("\n\n") ; - // println!("\n\n") ; + while changed { + changed = false; - while changed { - changed = false ; + for (idx, clause) in instance.clauses().index_iter() { + // self.print(instance) ; + // println!("") ; + // println!("") ; + // println!("{}", clause.to_string_info(instance.preds()).unwrap()) ; + let has_changed = self.work_on(clause, idx, instance); + changed = changed || has_changed + } + } - for (idx, clause) in instance.clauses().index_iter() { - // self.print(instance) ; - // println!("") ; - // println!("") ; - // println!("{}", clause.to_string_info(instance.preds()).unwrap()) ; - let has_changed = self.work_on(clause, idx, instance) ; - changed = changed || has_changed - } - } + let mut res = PrdHMap::new(); + for (pred, vars) in ::std::mem::replace(&mut self.keep, PrdMap::new()).into_index_iter() { + if !instance.is_known(pred) { + let mut prev = res.insert(pred, vars); + debug_assert! { prev.is_none() } + } + } - let mut res = PrdHMap::new() ; - for (pred, vars) in ::std::mem::replace( - & mut self.keep, PrdMap::new() - ).into_index_iter() { - if ! instance.is_known(pred) { - let mut prev = res.insert(pred, vars) ; - debug_assert! { prev.is_none() } - } + res } - - res - } -} \ No newline at end of file +} diff --git a/src/instance/preproc/bias_unroll.rs b/src/instance/preproc/bias_unroll.rs index 042bca86..8cdbf020 100644 --- a/src/instance/preproc/bias_unroll.rs +++ b/src/instance/preproc/bias_unroll.rs @@ -1,23 +1,16 @@ //! Bias unrolling module. - -use common::* ; +use common::*; use instance::{ - instance::{ - PreInstance, Clause - }, - preproc::{ - RedStrat, utils::ExtractRes, utils::ExtractionCxt - }, -} ; - - -type NegDef = (Quantfed, TermSet) ; -type NegDefs = Vec ; -type PosDef = (Quantfed, TermSet) ; -type PosDefs = Vec ; -type IndexedArgs = Vec<(VarTerms, usize)> ; + instance::{Clause, PreInstance}, + preproc::{utils::ExtractRes, utils::ExtractionCxt, RedStrat}, +}; +type NegDef = (Quantfed, TermSet); +type NegDefs = Vec; +type PosDef = (Quantfed, TermSet); +type PosDefs = Vec; +type IndexedArgs = Vec<(VarTerms, usize)>; /// Unrolls negative and positive constraints with a bias. /// @@ -27,1059 +20,1024 @@ type IndexedArgs = Vec<(VarTerms, usize)> ; /// definitions ; then, only check clauses that mention new predicates (old /// ones have already been tried) pub struct BiasedUnroll { - /// Predicates appearing in positive clauses. - in_pos_clauses: PrdSet, - /// Predicates appearing in negative clauses. - in_neg_clauses: PrdSet, - /// Predicates not appearing in positive clauses. - not_in_pos_clauses: PrdSet, - /// Predicates not appearing in negative clauses. - not_in_neg_clauses: PrdSet, - /// Positive definitions retrieved from positive clauses. - pos_defs: PrdHMap< PosDefs >, - /// Negative definitions retrieved from negative clauses. - neg_defs: PrdHMap< NegDefs >, - /// - pos_new_preds: PrdHMap<(PrdSet, PrdSet)>, - neg_new_preds: PrdHMap<(PrdSet, PrdSet)>, - /// Maximum number of new clauses we can create by predicate. - max_new_clauses: usize, + /// Predicates appearing in positive clauses. + in_pos_clauses: PrdSet, + /// Predicates appearing in negative clauses. + in_neg_clauses: PrdSet, + /// Predicates not appearing in positive clauses. + not_in_pos_clauses: PrdSet, + /// Predicates not appearing in negative clauses. + not_in_neg_clauses: PrdSet, + /// Positive definitions retrieved from positive clauses. + pos_defs: PrdHMap, + /// Negative definitions retrieved from negative clauses. + neg_defs: PrdHMap, + /// + pos_new_preds: PrdHMap<(PrdSet, PrdSet)>, + neg_new_preds: PrdHMap<(PrdSet, PrdSet)>, + /// Maximum number of new clauses we can create by predicate. + max_new_clauses: usize, } impl BiasedUnroll { - - /// Adds a positive definition for something. - fn add_pos_def_for( - & mut self, pred: PrdIdx, def: PosDef - ) { - let defs = self.pos_defs.entry(pred).or_insert_with(|| vec![]) ; - if defs.iter().all( |d| d != & def ) { - defs.push(def) - } - } - - /// Adds a negative definition for something. - fn add_neg_def_for( - & mut self, pred: PrdIdx, def: NegDef - ) { - let defs = self.neg_defs.entry(pred).or_insert_with(|| vec![]) ; - if defs.iter().all( |d| d != & def ) { - defs.push(def) - } - } - - - - /// Prints itself. - #[allow(dead_code)] - fn print(& self, instance: & Instance) { - println!("pos {{") ; - for pred in & self.in_pos_clauses { - println!(" + {}", instance[* pred]) - } - for pred in & self.not_in_pos_clauses { - println!(" - {}", instance[* pred]) - } - println!("}}") ; - println!("neg {{") ; - for pred in & self.in_neg_clauses { - println!(" + {}", instance[* pred]) - } - for pred in & self.not_in_neg_clauses { - println!(" - {}", instance[* pred]) - } - println!("}}") ; - - for (pred, defs) in & self.pos_defs { - if ! defs.is_empty() { - println!("+ {} {{", instance[* pred]) ; - for (qvars, terms) in defs { - let (pref, end) = if qvars.is_empty() { - ("", "") - } else { - print!(" (exists (") ; - for (qvar, typ) in qvars { - print!(" ({} {})", qvar.default_str(), typ) - } - println!(" )") ; - (" ", " )\n") - } ; - print!(" {}(and", pref) ; - for term in terms { - print!(" {}", term) - } - println!(")") ; - print!("{}", end) + /// Adds a positive definition for something. + fn add_pos_def_for(&mut self, pred: PrdIdx, def: PosDef) { + let defs = self.pos_defs.entry(pred).or_insert_with(|| vec![]); + if defs.iter().all(|d| d != &def) { + defs.push(def) } - println!("}}") - } } - for (pred, defs) in & self.neg_defs { - if ! defs.is_empty() { - println!("- {} {{", instance[* pred]) ; - for (qvars, terms) in defs { - let (pref, end) = if qvars.is_empty() { - ("", "") - } else { - print!(" (forall (") ; - for (qvar, typ) in qvars { - print!(" ({} {})", qvar.default_str(), typ) - } - println!(" )") ; - (" ", " )\n") - } ; - print!(" {}(not (and", pref) ; - for term in terms { - print!(" {}", term) - } - println!(") )") ; - print!("{}", end) + /// Adds a negative definition for something. + fn add_neg_def_for(&mut self, pred: PrdIdx, def: NegDef) { + let defs = self.neg_defs.entry(pred).or_insert_with(|| vec![]); + if defs.iter().all(|d| d != &def) { + defs.push(def) } - println!("}}") - } } - println!("pos_new_preds {{") ; - for (pred, (pos, neg)) in & self.pos_new_preds { - print!(" {} +{{", instance[* pred]) ; - for p in pos { - print!(" {}", instance[* p]) - } - print!(" }}, -{{") ; - for p in neg { - print!(" {}", instance[* p]) - } - println!(" }}") - } - println!("}}") ; - - println!("neg_new_preds {{") ; - for (pred, (pos, neg)) in & self.neg_new_preds { - print!(" {} +{{", instance[* pred]) ; - for p in pos { - print!(" {}", instance[* p]) - } - print!(" }}, -{{") ; - for p in neg { - print!(" {}", instance[* p]) - } - println!(" }}") - } - println!("}}") ; - println!() ; - println!() ; - println!() ; - } - - - /// Sets up the unroller by scanning the instance. - /// - /// Returns `true` if there's nothing to do. - fn setup( - & mut self, instance: & mut PreInstance - ) -> Res { - self.max_new_clauses = ::std::cmp::min( - 10, instance.clauses().len() / 20 - ) ; - - for (pred, _) in instance.preds().index_iter() { - if instance.is_known(pred) { continue } - let (lhs_clauses, rhs_clauses) = instance.clauses_of(pred) ; - - let mut in_pos = false ; - for clause in rhs_clauses { - if instance[* clause].lhs_pred_apps_len() == 0 { - in_pos = true ; - break + /// Prints itself. + #[allow(dead_code)] + fn print(&self, instance: &Instance) { + println!("pos {{"); + for pred in &self.in_pos_clauses { + println!(" + {}", instance[*pred]) } - } - if in_pos { - let is_new = self.in_pos_clauses.insert(pred) ; - debug_assert! { is_new } - } else { - let is_new = self.not_in_pos_clauses.insert(pred) ; - debug_assert! { is_new } - } - - let mut in_neg = false ; - for clause in lhs_clauses { - if instance[* clause].rhs().is_none() - && instance[* clause].lhs_pred_apps_len() == 1 { - in_neg = true ; - break + for pred in &self.not_in_pos_clauses { + println!(" - {}", instance[*pred]) } - } - if in_neg { - let is_new = self.in_neg_clauses.insert(pred) ; - debug_assert! { is_new } - } else { - let is_new = self.not_in_neg_clauses.insert(pred) ; - debug_assert! { is_new } - } - } - - let do_nothing = self.not_in_pos_clauses.is_empty( - ) && self.not_in_neg_clauses.is_empty() ; - - if ! do_nothing { - let (extractor, instance) = instance.extraction() ; - self.retrieve_all_pos_defs(instance, extractor) ? ; - self.retrieve_all_neg_defs(instance, extractor) ? ; - - let pos_neg = ( - self.in_pos_clauses.clone(), self.in_neg_clauses.clone() - ) ; - - for pred in & self.not_in_pos_clauses { - self.pos_new_preds.entry(* pred).or_insert_with( - || pos_neg.clone() - ) ; - } - - for pred in & self.not_in_neg_clauses { - self.neg_new_preds.entry(* pred).or_insert_with( - || pos_neg.clone() - ) ; - } - } - - Ok(do_nothing) - } - - - /// Retrieves a predicate positive definition from some clause. - /// - /// The clause has to be positive. - pub fn retrieve_pos_def( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, clause: & Clause, - ) -> Res<()> { - // Check what we're asked to do makes sense. - let args = if let Some((p, args)) = clause.rhs() { - if p != pred { - bail!( - "trying to retrieve_pos for {} in clause with different rhs", - instance[pred] - ) - } - args - } else { - bail!( - "trying to retrieve_pos for {} in non-positive clause (rhs)", - instance[pred] - ) - } ; - if ! clause.lhs_preds().is_empty() { - bail!( - "trying to retrieve_pos for {} in non-positive clause (lhs)", - instance[pred] - ) - } - - // println!( - // "from clause {}", clause.to_string_info(instance.preds()).unwrap() - // ) ; - - match extractor.terms_of_rhs_app( - true, instance, clause.vars(), - clause.lhs_terms(), clause.lhs_preds(), - (pred, args) - ) ? { - ExtractRes::Failed => bail!( - "term extraction failed for {}", instance[pred] - ), - ExtractRes::Trivial | - ExtractRes::SuccessTrue | - ExtractRes::SuccessFalse => bail!( - "unexpected result for term extraction for {} (false)", - instance[pred] - ), - ExtractRes::Success((qvars, tterms)) => { - let (terms, preds) = tterms.destroy() ; - debug_assert! { preds.is_empty() } - self.add_pos_def_for(pred, (qvars, terms)) - }, - } - - Ok(()) - } - - /// Retrieves all the partial positive definitions for some predicate. - fn retrieve_pos_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, - ) -> Res { - log! { - @verb "retrieving positive partial definitions for {}", - conf.emph(& instance[pred].name) - } - let mut count = 0 ; - for clause in instance.clauses_of(pred).1 { - let clause = & instance[* clause] ; - if clause.lhs_preds().is_empty() { - self.retrieve_pos_def(instance, extractor, pred, clause) ? ; - count += 1 - } - } - Ok(count) - } - - /// Retrieves all partial positive definitions. - fn retrieve_all_pos_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt - ) -> Res<()> { - log! { @4 "retrieve all positive definitions" } - for pred in self.in_pos_clauses.clone() { - log! { @4 "-> {}", instance[pred] } - let count = self.retrieve_pos_defs(instance, extractor, pred) ? ; - if count == 0 { - bail!("failed to retrieve positive definition for {}", instance[pred]) - } - } - Ok(()) - } - - - /// Retrieves a predicate negative definition from some clause. - /// - /// The clause has to be strictly negative. - pub fn retrieve_neg_def( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, clause: & Clause - ) -> Res<()> { - // Check what we're asked to do makes sense. - if clause.rhs().is_some() { - bail! ( - "trying to retrieve_neg for {} in non-negative clause (rhs)", - instance[pred] - ) - } - let args = { - let mut preds = clause.lhs_preds().iter() ; - let mut argss = if let Some((p, argss)) = preds.next() { - debug_assert_eq! { p, & pred } - if preds.next().is_some() { - bail!( - "trying to retrieve_neg for {} in a non-strict clause (preds)", - instance[pred] - ) + println!("}}"); + println!("neg {{"); + for pred in &self.in_neg_clauses { + println!(" + {}", instance[*pred]) } - argss.iter() - } else { - bail!( - "trying to retrieve_neg for {} in empty clause", - instance[pred] - ) - } ; - - if let Some(args) = argss.next() { - debug_assert! { argss.next().is_none() } - args - } else { - bail!( - "trying to retrieve_neg for {} in a non-strict clause (argss)" - ) - } - } ; - - // println!( - // "from clause {}", clause.to_string_info(instance.preds()).unwrap() - // ) ; - - match extractor.terms_of_lhs_app( - true, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - None, (pred, args) - ) ? { - ExtractRes::Failed => bail!( - "term extraction failed for {}", instance[pred] - ), - ExtractRes::Trivial | - ExtractRes::SuccessTrue | - ExtractRes::SuccessFalse => bail!( - "unexpected result for term extraction for {} (false)", - instance[pred] - ), - ExtractRes::Success((qvars, rhs, tterms)) => { - debug_assert! { rhs.is_none() } - let (terms, preds) = tterms.destroy() ; - debug_assert! { preds.is_empty() } - let mut neg_terms = TermSet::new() ; - for term in terms { - neg_terms.insert(term) ; + for pred in &self.not_in_neg_clauses { + println!(" - {}", instance[*pred]) + } + println!("}}"); + + for (pred, defs) in &self.pos_defs { + if !defs.is_empty() { + println!("+ {} {{", instance[*pred]); + for (qvars, terms) in defs { + let (pref, end) = if qvars.is_empty() { + ("", "") + } else { + print!(" (exists ("); + for (qvar, typ) in qvars { + print!(" ({} {})", qvar.default_str(), typ) + } + println!(" )"); + (" ", " )\n") + }; + print!(" {}(and", pref); + for term in terms { + print!(" {}", term) + } + println!(")"); + print!("{}", end) + } + println!("}}") + } } - self.add_neg_def_for(pred, (qvars, neg_terms)) - }, - } - Ok(()) - } - - /// Retrieves all the partial negative definitions for some predicate. - fn retrieve_neg_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, - ) -> Res { - log! { - @verb "retrieving negative partial definitions for {}", - conf.emph(& instance[pred].name) - } - let mut count = 0 ; - for clause in instance.clauses_of(pred).0 { - let clause = & instance[* clause] ; - if clause.lhs_preds().len() == 1 - && clause.rhs().is_none() - && clause.lhs_preds().iter().next().map( - |(_, argss)| argss.len() == 1 - ).unwrap_or( false ) { - self.retrieve_neg_def(instance, extractor, pred, clause) ? ; - count += 1 - } - } - Ok(count) - } - - /// Retrieves all partial negative definitions. - fn retrieve_all_neg_defs( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - ) -> Res<()> { - for pred in self.in_neg_clauses.clone() { - let count = self.retrieve_neg_defs(instance, extractor, pred) ? ; - if count == 0 { - bail!("failed to retrieve negative definition for {}", instance[pred]) - } - } - Ok(()) - } + for (pred, defs) in &self.neg_defs { + if !defs.is_empty() { + println!("- {} {{", instance[*pred]); + for (qvars, terms) in defs { + let (pref, end) = if qvars.is_empty() { + ("", "") + } else { + print!(" (forall ("); + for (qvar, typ) in qvars { + print!(" ({} {})", qvar.default_str(), typ) + } + println!(" )"); + (" ", " )\n") + }; + print!(" {}(not (and", pref); + for term in terms { + print!(" {}", term) + } + println!(") )"); + print!("{}", end) + } + println!("}}") + } + } + println!("pos_new_preds {{"); + for (pred, (pos, neg)) in &self.pos_new_preds { + print!(" {} +{{", instance[*pred]); + for p in pos { + print!(" {}", instance[*p]) + } + print!(" }}, -{{"); + for p in neg { + print!(" {}", instance[*p]) + } + println!(" }}") + } + println!("}}"); + println!("neg_new_preds {{"); + for (pred, (pos, neg)) in &self.neg_new_preds { + print!(" {} +{{", instance[*pred]); + for p in pos { + print!(" {}", instance[*p]) + } + print!(" }}, -{{"); + for p in neg { + print!(" {}", instance[*p]) + } + println!(" }}") + } + println!("}}"); + println!(); + println!(); + println!(); + } + /// Sets up the unroller by scanning the instance. + /// + /// Returns `true` if there's nothing to do. + fn setup(&mut self, instance: &mut PreInstance) -> Res { + self.max_new_clauses = ::std::cmp::min(10, instance.clauses().len() / 20); - /// Forces some terms in a clause by inserting a predicate application. - /// - /// `terms` is understood as a conjunction. - fn insert_terms( - & self, clause: & mut Clause, args: & VarTerms, - qvars: & Quantfed, terms: & TermSet, - ) -> Res<()> { - // Generate fresh variables for the clause if needed. - let qual_map = clause.fresh_vars_for(qvars) ; + for (pred, _) in instance.preds().index_iter() { + if instance.is_known(pred) { + continue; + } + let (lhs_clauses, rhs_clauses) = instance.clauses_of(pred); - for term in terms { - if let Some((term, _)) = term.subst_total( & (args, & qual_map) ) { - clause.insert_term(term) ; - } else { - bail!("error during total substitution in `insert_terms`") - } - } + let mut in_pos = false; + for clause in rhs_clauses { + if instance[*clause].lhs_pred_apps_len() == 0 { + in_pos = true; + break; + } + } + if in_pos { + let is_new = self.in_pos_clauses.insert(pred); + debug_assert! { is_new } + } else { + let is_new = self.not_in_pos_clauses.insert(pred); + debug_assert! { is_new } + } - Ok(()) - } + let mut in_neg = false; + for clause in lhs_clauses { + let rhs_none = instance[*clause].rhs().is_none(); + let one_app = instance[*clause].lhs_pred_apps_len() == 1; + if rhs_none && one_app { + in_neg = true; + break; + } + } + if in_neg { + let is_new = self.in_neg_clauses.insert(pred); + debug_assert! { is_new } + } else { + let is_new = self.not_in_neg_clauses.insert(pred); + debug_assert! { is_new } + } + } + let do_nothing = self.not_in_pos_clauses.is_empty() && self.not_in_neg_clauses.is_empty(); + if !do_nothing { + let (extractor, instance) = instance.extraction(); + self.retrieve_all_pos_defs(instance, extractor)?; + self.retrieve_all_neg_defs(instance, extractor)?; + let pos_neg = (self.in_pos_clauses.clone(), self.in_neg_clauses.clone()); - /// Tries to generate some positive clauses for a predicate. - fn generate_pos_clauses_for( - & mut self, pred: PrdIdx, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; + for pred in &self.not_in_pos_clauses { + self.pos_new_preds + .entry(*pred) + .or_insert_with(|| pos_neg.clone()); + } - 'all_clauses: for rhs_clause in instance.clauses_of(pred).1.clone() { - let mut one_pred_is_new = false ; + for pred in &self.not_in_neg_clauses { + self.neg_new_preds + .entry(*pred) + .or_insert_with(|| pos_neg.clone()); + } + } - let mut estimation = 1 ; + Ok(do_nothing) + } - for (p, argss) in instance[rhs_clause].lhs_preds() { - if let Some(defs) = self.pos_defs.get(p) { - for _ in argss { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses + /// Retrieves a predicate positive definition from some clause. + /// + /// The clause has to be positive. + pub fn retrieve_pos_def( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + pred: PrdIdx, + clause: &Clause, + ) -> Res<()> { + // Check what we're asked to do makes sense. + let args = if let Some((p, args)) = clause.rhs() { + if p != pred { + bail!( + "trying to retrieve_pos for {} in clause with different rhs", + instance[pred] + ) } - } + args } else { - continue 'all_clauses + bail!( + "trying to retrieve_pos for {} in non-positive clause (rhs)", + instance[pred] + ) + }; + if !clause.lhs_preds().is_empty() { + bail!( + "trying to retrieve_pos for {} in non-positive clause (lhs)", + instance[pred] + ) } - if let Some((pos, _)) = self.pos_new_preds.get(& pred) { - if pos.contains(p) { - one_pred_is_new = true - } + // println!( + // "from clause {}", clause.to_string_info(instance.preds()).unwrap() + // ) ; + + match extractor.terms_of_rhs_app( + true, + instance, + clause.vars(), + clause.lhs_terms(), + clause.lhs_preds(), + (pred, args), + )? { + ExtractRes::Failed => bail!("term extraction failed for {}", instance[pred]), + ExtractRes::Trivial | ExtractRes::SuccessTrue | ExtractRes::SuccessFalse => bail!( + "unexpected result for term extraction for {} (false)", + instance[pred] + ), + ExtractRes::Success((qvars, tterms)) => { + let (terms, preds) = tterms.destroy(); + debug_assert! { preds.is_empty() } + self.add_pos_def_for(pred, (qvars, terms)) + } } - } - if ! one_pred_is_new { - continue 'all_clauses - } - - log! { @4 - "generating positive clause(s) for {} from {} ({})", - instance[pred], - instance[rhs_clause].to_string_info( instance.preds() ) ?, - estimation - } - - let mut nu_clauses = vec![] ; - - scoped! { - - let mut clause = instance[rhs_clause].clone() ; - let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; - let mut map = Vec::with_capacity( lhs_preds.len() ) ; - - for (pred, argss) in & lhs_preds { - let mut arg_map = Vec::with_capacity( argss.len() ) ; + Ok(()) + } - if let Some(defs) = self.pos_defs.get(pred) { - for args in argss { - arg_map.push( (args, 0) ) + /// Retrieves all the partial positive definitions for some predicate. + fn retrieve_pos_defs( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + pred: PrdIdx, + ) -> Res { + log! { + @verb "retrieving positive partial definitions for {}", + conf.emph(& instance[pred].name) + } + let mut count = 0; + for clause in instance.clauses_of(pred).1 { + let clause = &instance[*clause]; + if clause.lhs_preds().is_empty() { + self.retrieve_pos_def(instance, extractor, pred, clause)?; + count += 1 } - - map.push( (pred, defs, arg_map) ) - } else { - bail!("no definition for {} (positive, lhs)", instance[* pred]) - } } + Ok(count) + } - macro_rules! map_inc { - () => ({ - let mut done = true ; - 'all_apps: for & mut (_, defs, ref mut argss) in & mut map { - for (_, ref mut index) in argss { - * index += 1 ; - if * index < defs.len() { - done = false ; - break 'all_apps - } else { - * index = 0 - } - } + /// Retrieves all partial positive definitions. + fn retrieve_all_pos_defs( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + ) -> Res<()> { + log! { @4 "retrieve all positive definitions" } + for pred in self.in_pos_clauses.clone() { + log! { @4 "-> {}", instance[pred] } + let count = self.retrieve_pos_defs(instance, extractor, pred)?; + if count == 0 { + bail!( + "failed to retrieve positive definition for {}", + instance[pred] + ) } - done - }) } + Ok(()) + } - let mut done = false ; - while ! done { - let mut clause = clause.clone() ; - - for (_, defs, argss) in & map { - for (args, index) in argss { - self.insert_terms( - & mut clause, args, & defs[* index].0, & defs[* index].1 - ) ? + /// Retrieves a predicate negative definition from some clause. + /// + /// The clause has to be strictly negative. + pub fn retrieve_neg_def( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + pred: PrdIdx, + clause: &Clause, + ) -> Res<()> { + // Check what we're asked to do makes sense. + if clause.rhs().is_some() { + bail!( + "trying to retrieve_neg for {} in non-negative clause (rhs)", + instance[pred] + ) + } + let args = { + let mut preds = clause.lhs_preds().iter(); + let mut argss = if let Some((p, argss)) = preds.next() { + debug_assert_eq! { p, & pred } + if preds.next().is_some() { + bail!( + "trying to retrieve_neg for {} in a non-strict clause (preds)", + instance[pred] + ) + } + argss.iter() + } else { + bail!( + "trying to retrieve_neg for {} in empty clause", + instance[pred] + ) + }; + + if let Some(args) = argss.next() { + debug_assert! { argss.next().is_none() } + args + } else { + bail!("trying to retrieve_neg for {} in a non-strict clause (argss)") } - } - - if let Some(trivial) = instance.is_this_clause_trivial( - & mut clause - ) ? { - if ! trivial { - nu_clauses.push(clause) + }; + + // println!( + // "from clause {}", clause.to_string_info(instance.preds()).unwrap() + // ) ; + + match extractor.terms_of_lhs_app( + true, + instance, + clause.vars(), + (clause.lhs_terms(), clause.lhs_preds()), + None, + (pred, args), + )? { + ExtractRes::Failed => bail!("term extraction failed for {}", instance[pred]), + ExtractRes::Trivial | ExtractRes::SuccessTrue | ExtractRes::SuccessFalse => bail!( + "unexpected result for term extraction for {} (false)", + instance[pred] + ), + ExtractRes::Success((qvars, rhs, tterms)) => { + debug_assert! { rhs.is_none() } + let (terms, preds) = tterms.destroy(); + debug_assert! { preds.is_empty() } + let mut neg_terms = TermSet::new(); + for term in terms { + neg_terms.insert(term); + } + self.add_neg_def_for(pred, (qvars, neg_terms)) } - } else { - unimplemented!("unsat in biased unrolling") - } - - done = map_inc!() } - } - - for mut clause in nu_clauses { - clause.from_unrolling = true ; - if let Some(index) = instance.push_clause(clause) ? { - let (extractor, instance) = instance.extraction() ; - self.retrieve_pos_def( - instance, extractor, pred, & instance[index] - ) ? ; - info.clauses_added += 1 - } - } + Ok(()) } - Ok(info) - } - - - - fn init_for_neg_clause_generation( - & self, pred: PrdIdx, instance: & mut PreInstance, lhs_clause: ClsIdx - ) -> Res { - let mut clause = instance[lhs_clause].clone() ; - - let mut this_pred = None ; - let rhs_pred = clause.unset_rhs() ; - let lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; - - let mut map = Vec::with_capacity( - lhs_preds.len() - ) ; - - for (p, argss) in lhs_preds { - let mut arg_map = Vec::with_capacity( argss.len() ) ; - - if let Some(defs) = self.pos_defs.get(& p) { - for args in argss { - arg_map.push( (args, 0) ) + /// Retrieves all the partial negative definitions for some predicate. + fn retrieve_neg_defs( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + pred: PrdIdx, + ) -> Res { + log! { + @verb "retrieving negative partial definitions for {}", + conf.emph(& instance[pred].name) } - - if p == pred { - this_pred = Some((defs, arg_map)) - } else { - map.push( (p, defs, arg_map) ) + let mut count = 0; + for clause in instance.clauses_of(pred).0 { + let clause = &instance[*clause]; + if clause.lhs_preds().len() == 1 && clause.rhs().is_none() && clause + .lhs_preds() + .iter() + .next() + .map(|(_, argss)| argss.len() == 1) + .unwrap_or(false) + { + self.retrieve_neg_def(instance, extractor, pred, clause)?; + count += 1 + } } - } else if p == pred { - debug_assert_eq! { argss.len(), 1 } - let is_new = clause.insert_pred_app( - pred, argss.into_iter().next().unwrap() - ) ; - debug_assert! { is_new } - } else { - bail!("no definition for {} (negative, lhs)", instance[p]) - } + Ok(count) } - if let Some((pred, args)) = rhs_pred { - if let Some(defs) = self.neg_defs.get(& pred) { - map.push( (pred, defs, vec![ (args, 0) ]) ) - } else { - bail!("no definition for {} (negative, rhs)", instance[pred]) - } + /// Retrieves all partial negative definitions. + fn retrieve_all_neg_defs( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + ) -> Res<()> { + for pred in self.in_neg_clauses.clone() { + let count = self.retrieve_neg_defs(instance, extractor, pred)?; + if count == 0 { + bail!( + "failed to retrieve negative definition for {}", + instance[pred] + ) + } + } + Ok(()) } - Ok((clause, this_pred, map)) - } - + /// Forces some terms in a clause by inserting a predicate application. + /// + /// `terms` is understood as a conjunction. + fn insert_terms( + &self, + clause: &mut Clause, + args: &VarTerms, + qvars: &Quantfed, + terms: &TermSet, + ) -> Res<()> { + // Generate fresh variables for the clause if needed. + let qual_map = clause.fresh_vars_for(qvars); + for term in terms { + if let Some((term, _)) = term.subst_total(&(args, &qual_map)) { + clause.insert_term(term); + } else { + bail!("error during total substitution in `insert_terms`") + } + } - /// - fn generate_neg_clause_for( - & self, pred: PrdIdx, instance: & mut PreInstance, - lhs_clause: ClsIdx, clauses: & mut Vec - ) -> Res<()> { - - let (clause, mut this_pred, mut map) = self.init_for_neg_clause_generation( - pred, instance, lhs_clause - ) ? ; - - let mut active_lhs_pred_app = 0 ; + Ok(()) + } - macro_rules! map_inc { - () => ({ - let mut done = true ; - for & mut (_, defs, ref mut argss) in & mut map { - map_inc!(@argss done, defs, argss ; true) ; - if ! done { - break - } - } + /// Tries to generate some positive clauses for a predicate. + fn generate_pos_clauses_for( + &mut self, + pred: PrdIdx, + instance: &mut PreInstance, + ) -> Res { + let mut info = RedInfo::new(); + + 'all_clauses: for rhs_clause in instance.clauses_of(pred).1.clone() { + let mut one_pred_is_new = false; + + let mut estimation = 1; + + for (p, argss) in instance[rhs_clause].lhs_preds() { + if let Some(defs) = self.pos_defs.get(p) { + for _ in argss { + estimation *= defs.len(); + if estimation > self.max_new_clauses { + continue 'all_clauses; + } + } + } else { + continue 'all_clauses; + } - // println!("inc: {}", done) ; - - if done { - if let Some( - & mut (defs, ref mut argss) - ) = this_pred.as_mut() { - let argss_len = argss.len() ; - if active_lhs_pred_app < argss_len { - let mut index = 0 ; - map_inc!( - @argss done, defs, argss ; { - let iff = index != active_lhs_pred_app ; - index += 1 ; - iff + if let Some((pos, _)) = self.pos_new_preds.get(&pred) { + if pos.contains(p) { + one_pred_is_new = true + } } - ) } - - if done { - active_lhs_pred_app += 1 ; - done = active_lhs_pred_app >= argss_len + if !one_pred_is_new { + continue 'all_clauses; } - } - } - done - }) ; - (@argss $done:ident, $defs:expr, $argss:expr ; $iff:expr) => ( - for (_, ref mut index) in $argss { - if $iff { - * index += 1 ; - if * index < $defs.len() { - $done = false ; - break - } else { - * index = 0 + log! { @4 + "generating positive clause(s) for {} from {} ({})", + instance[pred], + instance[rhs_clause].to_string_info( instance.preds() ) ?, + estimation } - } - } - ) - } - let mut done = false ; - while ! done { - // println!("running: {}", active_lhs_pred_app) ; - - let mut clause = clause.clone() ; - if let Some((defs, argss)) = this_pred.as_ref() { - let mut current = 0 ; - - while current < argss.len() { - let (ref args, index) = argss[current] ; - if current == active_lhs_pred_app { - let is_new = clause.insert_pred_app(pred, args.clone()) ; - debug_assert! { is_new } - } else { - self.insert_terms( - & mut clause, args, & defs[index].0, & defs[index].1 - ) ? - } - current += 1 - } + let mut nu_clauses = vec![]; - } + scoped! { - for (_, defs, argss) in & map { - for (args, index) in argss { - self.insert_terms( - & mut clause, args, & defs[* index].0, & defs[* index].1 - ) ? - } - } - - // println!( - // "negative clause: {}", - // clause.to_string_info(instance.preds()).unwrap() - // ) ; - - if let Some(trivial) = instance.is_this_clause_trivial( - & mut clause - ) ? { - if ! trivial { - // println!("non-trivial...") ; - clauses.push(clause) - } else { - // println!("trivial...") - } - } else { - unsat!("in biased unrolling") - } + let mut clause = instance[rhs_clause].clone() ; - done = map_inc!() - } + let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; + let mut map = Vec::with_capacity( lhs_preds.len() ) ; - Ok(()) - } + for (pred, argss) in & lhs_preds { + let mut arg_map = Vec::with_capacity( argss.len() ) ; + if let Some(defs) = self.pos_defs.get(pred) { + for args in argss { + arg_map.push( (args, 0) ) + } + map.push( (pred, defs, arg_map) ) + } else { + bail!("no definition for {} (positive, lhs)", instance[* pred]) + } + } - /// Tries to generate a negative clause for a predicate. - fn generate_neg_clauses_for( - & mut self, pred: PrdIdx, instance: & mut PreInstance - ) -> Res { - // self.print(instance) ; + macro_rules! map_inc { + () => ({ + let mut done = true ; + 'all_apps: for & mut (_, defs, ref mut argss) in & mut map { + for (_, ref mut index) in argss { + * index += 1 ; + if * index < defs.len() { + done = false ; + break 'all_apps + } else { + * index = 0 + } + } + } + done + }) + } - let mut info = RedInfo::new() ; + let mut done = false ; + while ! done { + let mut clause = clause.clone() ; - 'all_clauses: for lhs_clause in instance.clauses_of(pred).0.clone() { - let mut one_pred_is_new = false ; + for (_, defs, argss) in & map { + for (args, index) in argss { + self.insert_terms( + & mut clause, args, & defs[* index].0, & defs[* index].1 + ) ? + } + } - let mut estimation = 1 ; + if let Some(trivial) = instance.is_this_clause_trivial( + & mut clause + ) ? { + if ! trivial { + nu_clauses.push(clause) + } + } else { + unimplemented!("unsat in biased unrolling") + } + + done = map_inc!() + } - if let Some((p, _)) = instance[lhs_clause].rhs() { - if let Some(defs) = self.neg_defs.get(& p) { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses - } - } else { - continue 'all_clauses - } - if let Some((_, neg)) = self.neg_new_preds.get(& pred) { - if neg.contains(& p) { - one_pred_is_new = true - } - } - } - - for (p, argss) in instance[lhs_clause].lhs_preds() { - if * p == pred { - if argss.len() == 1 { - () - } else if let Some(defs) = self.pos_defs.get(p) { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses } - } else { - continue 'all_clauses - } - } else if let Some(defs) = self.pos_defs.get(p) { - for _ in argss { - estimation *= defs.len() ; - if estimation > self.max_new_clauses { - continue 'all_clauses + + for mut clause in nu_clauses { + clause.from_unrolling = true; + if let Some(index) = instance.push_clause(clause)? { + let (extractor, instance) = instance.extraction(); + self.retrieve_pos_def(instance, extractor, pred, &instance[index])?; + info.clauses_added += 1 + } } - } - } else { - // log! { @6 "{} not in pos clauses", instance[* p] } - continue 'all_clauses } - if let Some((pos, _)) = self.neg_new_preds.get(& pred) { - if pos.contains(p) { - one_pred_is_new = true - } - } - } + Ok(info) + } - // log! { @6 "one pred new: {}", one_pred_is_new } + fn init_for_neg_clause_generation( + &self, + pred: PrdIdx, + instance: &mut PreInstance, + lhs_clause: ClsIdx, + ) -> Res { + let mut clause = instance[lhs_clause].clone(); - if ! one_pred_is_new { - continue 'all_clauses - } + let mut this_pred = None; + let rhs_pred = clause.unset_rhs(); + let lhs_preds: Vec<_> = clause.drain_lhs_preds().collect(); - log! { @4 - "generating negative clause(s) for {} from {}", - instance[pred], - instance[lhs_clause].to_string_info( instance.preds() ) ? - } + let mut map = Vec::with_capacity(lhs_preds.len()); - let mut nu_clauses = vec![] ; + for (p, argss) in lhs_preds { + let mut arg_map = Vec::with_capacity(argss.len()); - self.generate_neg_clause_for( - pred, instance, lhs_clause, & mut nu_clauses - ) ? ; + if let Some(defs) = self.pos_defs.get(&p) { + for args in argss { + arg_map.push((args, 0)) + } - for mut clause in nu_clauses { - log! { @6 - "new clause: {}", - clause.to_string_info( instance.preds() ) ? + if p == pred { + this_pred = Some((defs, arg_map)) + } else { + map.push((p, defs, arg_map)) + } + } else if p == pred { + debug_assert_eq! { argss.len(), 1 } + let is_new = clause.insert_pred_app(pred, argss.into_iter().next().unwrap()); + debug_assert! { is_new } + } else { + bail!("no definition for {} (negative, lhs)", instance[p]) + } } - clause.from_unrolling = true ; - if let Some(index) = instance.push_clause(clause) ? { - let (extractor, instance) = instance.extraction() ; - self.retrieve_neg_def( - instance, extractor, pred, & instance[index] - ) ? ; - info.clauses_added += 1 + if let Some((pred, args)) = rhs_pred { + if let Some(defs) = self.neg_defs.get(&pred) { + map.push((pred, defs, vec![(args, 0)])) + } else { + bail!("no definition for {} (negative, rhs)", instance[pred]) + } } - } - } - - Ok(info) - } + Ok((clause, this_pred, map)) + } + /// + fn generate_neg_clause_for( + &self, + pred: PrdIdx, + instance: &mut PreInstance, + lhs_clause: ClsIdx, + clauses: &mut Vec, + ) -> Res<()> { + let (clause, mut this_pred, mut map) = + self.init_for_neg_clause_generation(pred, instance, lhs_clause)?; + let mut active_lhs_pred_app = 0; - fn neg_unroll( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; - - for pred in self.not_in_neg_clauses.clone() { - log! { @4 - "trying to generate negative clauses for {}", instance[pred] - } - // self.print(instance) ; - - if self.neg_new_preds.get(& pred).map( - |(pos, neg)| ! pos.is_empty() || ! neg.is_empty() - ).unwrap_or(false) { - - let this_info = self.generate_neg_clauses_for(pred, instance) ? ; + macro_rules! map_inc { + () => {{ + let mut done = true; + for &mut (_, defs, ref mut argss) in &mut map { + map_inc!(@argss done, defs, argss ; true); + if !done { + break; + } + } - if this_info.non_zero() { - let was_there = self.not_in_neg_clauses.remove(& pred) ; - debug_assert! { was_there } + // println!("inc: {}", done) ; + + if done { + if let Some(&mut (defs, ref mut argss)) = this_pred.as_mut() { + let argss_len = argss.len(); + if active_lhs_pred_app < argss_len { + let mut index = 0; + map_inc!( + @argss done, defs, argss ; { + let iff = index != active_lhs_pred_app ; + index += 1 ; + iff + } + ) + } + + if done { + active_lhs_pred_app += 1; + done = active_lhs_pred_app >= argss_len + } + } + } - let is_new = self.in_neg_clauses.insert(pred) ; - debug_assert! { is_new } + done + }}; + (@argss $done:ident, $defs:expr, $argss:expr ; $iff:expr) => { + for (_, ref mut index) in $argss { + if $iff { + *index += 1; + if *index < $defs.len() { + $done = false; + break; + } else { + *index = 0 + } + } + } + }; + } - let prev = self.neg_new_preds.remove(& pred) ; - debug_assert! { prev.is_some() } + let mut done = false; + while !done { + // println!("running: {}", active_lhs_pred_app) ; + + let mut clause = clause.clone(); + if let Some((defs, argss)) = this_pred.as_ref() { + let mut current = 0; + + while current < argss.len() { + let (ref args, index) = argss[current]; + if current == active_lhs_pred_app { + let is_new = clause.insert_pred_app(pred, args.clone()); + debug_assert! { is_new } + } else { + self.insert_terms(&mut clause, args, &defs[index].0, &defs[index].1)? + } + current += 1 + } + } - for (_, (_, neg)) in self.pos_new_preds.iter_mut().chain( - self.neg_new_preds.iter_mut() - ) { - let is_new = neg.insert(pred) ; - debug_assert! { is_new } - } - log! { @4 "-> success" } + for (_, defs, argss) in &map { + for (args, index) in argss { + self.insert_terms(&mut clause, args, &defs[*index].0, &defs[*index].1)? + } + } - log! { @verb - "generated {} negative clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) - } + // println!( + // "negative clause: {}", + // clause.to_string_info(instance.preds()).unwrap() + // ) ; - info += this_info ; + if let Some(trivial) = instance.is_this_clause_trivial(&mut clause)? { + if !trivial { + // println!("non-trivial...") ; + clauses.push(clause) + } else { + // println!("trivial...") + } + } else { + unsat!("in biased unrolling") + } - } else { - if let Some((pos, neg)) = self.neg_new_preds.get_mut(& pred) { - pos.clear() ; - neg.clear() - } else { - bail!("inconsistent BiasedUnroll state") - } - log! { @4 "-> failure" } + done = map_inc!() } - } else { - log! { @4 "-> nothing new, skipping" } - } + Ok(()) } - Ok(info) - } - + /// Tries to generate a negative clause for a predicate. + fn generate_neg_clauses_for( + &mut self, + pred: PrdIdx, + instance: &mut PreInstance, + ) -> Res { + // self.print(instance) ; + let mut info = RedInfo::new(); + 'all_clauses: for lhs_clause in instance.clauses_of(pred).0.clone() { + let mut one_pred_is_new = false; - fn pos_unroll( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; + let mut estimation = 1; - for pred in self.not_in_pos_clauses.clone() { - log! { @4 - "trying to generate positive clauses for {}", instance[pred] - } - // self.print(instance) ; + if let Some((p, _)) = instance[lhs_clause].rhs() { + if let Some(defs) = self.neg_defs.get(&p) { + estimation *= defs.len(); + if estimation > self.max_new_clauses { + continue 'all_clauses; + } + } else { + continue 'all_clauses; + } + if let Some((_, neg)) = self.neg_new_preds.get(&pred) { + if neg.contains(&p) { + one_pred_is_new = true + } + } + } - if self.pos_new_preds.get(& pred).map( - |(pos, _)| ! pos.is_empty() - ).unwrap_or(false) { + for (p, argss) in instance[lhs_clause].lhs_preds() { + if *p == pred { + if argss.len() == 1 { + () + } else if let Some(defs) = self.pos_defs.get(p) { + estimation *= defs.len(); + if estimation > self.max_new_clauses { + continue 'all_clauses; + } + } else { + continue 'all_clauses; + } + } else if let Some(defs) = self.pos_defs.get(p) { + for _ in argss { + estimation *= defs.len(); + if estimation > self.max_new_clauses { + continue 'all_clauses; + } + } + } else { + // log! { @6 "{} not in pos clauses", instance[* p] } + continue 'all_clauses; + } - let this_info = self.generate_pos_clauses_for(pred, instance) ? ; + if let Some((pos, _)) = self.neg_new_preds.get(&pred) { + if pos.contains(p) { + one_pred_is_new = true + } + } + } - if this_info.non_zero() { - let was_there = self.not_in_pos_clauses.remove(& pred) ; - debug_assert! { was_there } + // log! { @6 "one pred new: {}", one_pred_is_new } - let is_new = self.in_pos_clauses.insert(pred) ; - debug_assert! { is_new } + if !one_pred_is_new { + continue 'all_clauses; + } - let prev = self.pos_new_preds.remove(& pred) ; - debug_assert! { prev.is_some() } + log! { @4 + "generating negative clause(s) for {} from {}", + instance[pred], + instance[lhs_clause].to_string_info( instance.preds() ) ? + } - for (_, (pos, _)) in self.pos_new_preds.iter_mut().chain( - self.neg_new_preds.iter_mut() - ) { - let is_new = pos.insert(pred) ; - debug_assert! { is_new } - } - log! { @4 "-> success" } + let mut nu_clauses = vec![]; - log! { @verb - "generated {} positive clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) - } + self.generate_neg_clause_for(pred, instance, lhs_clause, &mut nu_clauses)?; - info += this_info ; + for mut clause in nu_clauses { + log! { @6 + "new clause: {}", + clause.to_string_info( instance.preds() ) ? + } - } else { - if let Some((pos, neg)) = self.pos_new_preds.get_mut(& pred) { - pos.clear() ; - neg.clear() - } else { - bail!("inconsistent BiasedUnroll state") - } - log! { @4 "-> failure" } + clause.from_unrolling = true; + if let Some(index) = instance.push_clause(clause)? { + let (extractor, instance) = instance.extraction(); + self.retrieve_neg_def(instance, extractor, pred, &instance[index])?; + info.clauses_added += 1 + } + } } - } else { - log! { @4 "-> nothing new, skipping" } - } + Ok(info) } - Ok(info) - } - + fn neg_unroll(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); + for pred in self.not_in_neg_clauses.clone() { + log! { @4 + "trying to generate negative clauses for {}", instance[pred] + } + // self.print(instance) ; + + if self + .neg_new_preds + .get(&pred) + .map(|(pos, neg)| !pos.is_empty() || !neg.is_empty()) + .unwrap_or(false) + { + let this_info = self.generate_neg_clauses_for(pred, instance)?; + + if this_info.non_zero() { + let was_there = self.not_in_neg_clauses.remove(&pred); + debug_assert! { was_there } + + let is_new = self.in_neg_clauses.insert(pred); + debug_assert! { is_new } + + let prev = self.neg_new_preds.remove(&pred); + debug_assert! { prev.is_some() } + + for (_, (_, neg)) in self + .pos_new_preds + .iter_mut() + .chain(self.neg_new_preds.iter_mut()) + { + let is_new = neg.insert(pred); + debug_assert! { is_new } + } + log! { @4 "-> success" } + + log! { @verb + "generated {} negative clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) + } + + info += this_info; + } else { + if let Some((pos, neg)) = self.neg_new_preds.get_mut(&pred) { + pos.clear(); + neg.clear() + } else { + bail!("inconsistent BiasedUnroll state") + } + log! { @4 "-> failure" } + } + } else { + log! { @4 "-> nothing new, skipping" } + } + } -} - -impl RedStrat for BiasedUnroll { - fn name(& self) -> & 'static str { "biased_unroll" } - - fn new(_: & Instance) -> Self { - let ( - in_pos_clauses, in_neg_clauses, - not_in_pos_clauses, not_in_neg_clauses, - ) = ( - PrdSet::new(), PrdSet::new(), - PrdSet::new(), PrdSet::new(), - ) ; - - BiasedUnroll { - in_pos_clauses, in_neg_clauses, - not_in_pos_clauses, not_in_neg_clauses, - pos_defs: PrdHMap::new(), - neg_defs: PrdHMap::new(), - pos_new_preds: PrdHMap::new(), - neg_new_preds: PrdHMap::new(), - max_new_clauses: 0, + Ok(info) } - } - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; + fn pos_unroll(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); - let nothing_to_do = self.setup(instance) ? ; + for pred in self.not_in_pos_clauses.clone() { + log! { @4 + "trying to generate positive clauses for {}", instance[pred] + } + // self.print(instance) ; + + if self + .pos_new_preds + .get(&pred) + .map(|(pos, _)| !pos.is_empty()) + .unwrap_or(false) + { + let this_info = self.generate_pos_clauses_for(pred, instance)?; + + if this_info.non_zero() { + let was_there = self.not_in_pos_clauses.remove(&pred); + debug_assert! { was_there } + + let is_new = self.in_pos_clauses.insert(pred); + debug_assert! { is_new } + + let prev = self.pos_new_preds.remove(&pred); + debug_assert! { prev.is_some() } + + for (_, (pos, _)) in self + .pos_new_preds + .iter_mut() + .chain(self.neg_new_preds.iter_mut()) + { + let is_new = pos.insert(pred); + debug_assert! { is_new } + } + log! { @4 "-> success" } + + log! { @verb + "generated {} positive clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) + } + + info += this_info; + } else { + if let Some((pos, neg)) = self.pos_new_preds.get_mut(&pred) { + pos.clear(); + neg.clear() + } else { + bail!("inconsistent BiasedUnroll state") + } + log! { @4 "-> failure" } + } + } else { + log! { @4 "-> nothing new, skipping" } + } + } - if nothing_to_do { - return Ok(info) + Ok(info) } +} - // println!("done with setup") ; - // self.print(instance) ; - // println!() ; +impl RedStrat for BiasedUnroll { + fn name(&self) -> &'static str { + "biased_unroll" + } - let mut new_stuff = true ; + fn new(_: &Instance) -> Self { + let (in_pos_clauses, in_neg_clauses, not_in_pos_clauses, not_in_neg_clauses) = + (PrdSet::new(), PrdSet::new(), PrdSet::new(), PrdSet::new()); + + BiasedUnroll { + in_pos_clauses, + in_neg_clauses, + not_in_pos_clauses, + not_in_neg_clauses, + pos_defs: PrdHMap::new(), + neg_defs: PrdHMap::new(), + pos_new_preds: PrdHMap::new(), + neg_new_preds: PrdHMap::new(), + max_new_clauses: 0, + } + } - while new_stuff { - new_stuff = false ; + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); - if conf.preproc.pos_unroll { - let this_info = self.pos_unroll(instance) ? ; - if this_info.non_zero() { - new_stuff = true ; - info += this_info - } - } + let nothing_to_do = self.setup(instance)?; - if conf.preproc.neg_unroll { - let this_info = self.neg_unroll(instance) ? ; - if this_info.non_zero() { - new_stuff = true ; - info += this_info + if nothing_to_do { + return Ok(info); } - } - } + // println!("done with setup") ; + // self.print(instance) ; + // println!() ; - info += instance.simplify_all() ? ; + let mut new_stuff = true; - Ok(info) - } -} + while new_stuff { + new_stuff = false; + if conf.preproc.pos_unroll { + let this_info = self.pos_unroll(instance)?; + if this_info.non_zero() { + new_stuff = true; + info += this_info + } + } + if conf.preproc.neg_unroll { + let this_info = self.neg_unroll(instance)?; + if this_info.non_zero() { + new_stuff = true; + info += this_info + } + } + } + info += instance.simplify_all()?; + Ok(info) + } +} type NegGenInit<'a> = ( - Clause, - Option<(& 'a PosDefs, IndexedArgs)>, - Vec<(PrdIdx, & 'a PosDefs, IndexedArgs)> -) ; - - + Clause, + Option<(&'a PosDefs, IndexedArgs)>, + Vec<(PrdIdx, &'a PosDefs, IndexedArgs)>, +); diff --git a/src/instance/preproc/cfg_red.rs b/src/instance/preproc/cfg_red.rs index b451c7d7..5a78fdc3 100644 --- a/src/instance/preproc/cfg_red.rs +++ b/src/instance/preproc/cfg_red.rs @@ -1,1269 +1,1289 @@ //! Module in charge of constructing an analyzing the graph of dependencies //! between predicates. -use common::* ; -use var_to::terms::VarTermsSet ; +use common::*; use instance::{ - preproc::{ - RedStrat, utils, utils::ExtractionCxt, - }, - instance::PreInstance, -} ; - - + instance::PreInstance, + preproc::{utils, utils::ExtractionCxt, RedStrat}, +}; +use var_to::terms::VarTermsSet; /// Result of a DNF merge. struct MergeRes { - /// The dnf. - def: Dnf, - /// Clause generation estimation. - estimation: usize, + /// The dnf. + def: Dnf, + /// Clause generation estimation. + estimation: usize, } impl MergeRes { - /// Constructor. - fn new(def: Dnf, estimation: usize) -> Self { - MergeRes { def, estimation } - } + /// Constructor. + fn new(def: Dnf, estimation: usize) -> Self { + MergeRes { def, estimation } + } } - - /// Maps predicates to the predicates they depend on. -pub type Dep = PrdMap< PrdMap > ; +pub type Dep = PrdMap>; /// Graph of dependencies. #[derive(Clone)] pub struct Graph { - /// Forward dependencies. - forward: Dep, - /// Backward dependencies. - bakward: Dep, - /// Predicates appearing in a negative constraint. - neg: PrdMap, - /// Predicates appearing in a positive constraint. - pos: PrdMap, + /// Forward dependencies. + forward: Dep, + /// Backward dependencies. + bakward: Dep, + /// Predicates appearing in a negative constraint. + neg: PrdMap, + /// Predicates appearing in a positive constraint. + pos: PrdMap, } impl Graph { - /// Constructs an empty graph. - pub fn new(instance: & Instance) -> Self { - let forward: Dep = vec![ - vec![ 0 ; instance.preds().len() ].into() ; instance.preds().len() - ].into() ; - let bakward = forward.clone() ; - let neg: PrdMap<_> = vec![ 0 ; instance.preds().len() ].into() ; - let pos: PrdMap<_> = vec![ 0 ; instance.preds().len() ].into() ; - Graph { forward, bakward, neg, pos } - } - - /// Resets the graph. - fn reset(& mut self) { - for forward in self.forward.iter_mut() { - for count in forward.iter_mut() { - * count = 0 - } - } - for bakward in self.bakward.iter_mut() { - for count in bakward.iter_mut() { - * count = 0 - } - } - for count in self.pos.iter_mut() { - * count = 0 - } - for count in self.neg.iter_mut() { - * count = 0 + /// Constructs an empty graph. + pub fn new(instance: &Instance) -> Self { + let forward: Dep = + vec![vec![0; instance.preds().len()].into(); instance.preds().len()].into(); + let bakward = forward.clone(); + let neg: PrdMap<_> = vec![0; instance.preds().len()].into(); + let pos: PrdMap<_> = vec![0; instance.preds().len()].into(); + Graph { + forward, + bakward, + neg, + pos, + } } - } - - /// Clears itself and sets everything up for the input instance. - pub fn setup(& mut self, instance: & Instance) { - self.reset() ; - for clause in instance.clauses() { - if let Some((tgt, _)) = clause.rhs() { - if clause.lhs_preds().is_empty() { - self.pos[tgt] += 1 ; - } else { - for (prd, _) in clause.lhs_preds() { - self.bakward[tgt][* prd] += 1 ; - self.forward[* prd][tgt] += 1 ; - } + /// Resets the graph. + fn reset(&mut self) { + for forward in self.forward.iter_mut() { + for count in forward.iter_mut() { + *count = 0 + } } - } else { - for (prd, _) in clause.lhs_preds() { - self.neg[* prd] += 1 ; + for bakward in self.bakward.iter_mut() { + for count in bakward.iter_mut() { + *count = 0 + } + } + for count in self.pos.iter_mut() { + *count = 0 + } + for count in self.neg.iter_mut() { + *count = 0 + } + } + + /// Clears itself and sets everything up for the input instance. + pub fn setup(&mut self, instance: &Instance) { + self.reset(); + + for clause in instance.clauses() { + if let Some((tgt, _)) = clause.rhs() { + if clause.lhs_preds().is_empty() { + self.pos[tgt] += 1; + } else { + for (prd, _) in clause.lhs_preds() { + self.bakward[tgt][*prd] += 1; + self.forward[*prd][tgt] += 1; + } + } + } else { + for (prd, _) in clause.lhs_preds() { + self.neg[*prd] += 1; + } + } } - } } - } - - /// Dumps a graph in dot format. - pub fn dot_write( - & self, w: & mut W, instance: & Instance, hi_lite: & PrdSet - ) -> Res<()> { - writeln!(w, "digraph blah {{", ) ? ; - writeln!(w, " rankdir=LR ;") ? ; - writeln!(w, " edge [arrowhead=onormal] ;") ? ; - writeln!( - w, " top[\ + + /// Dumps a graph in dot format. + pub fn dot_write(&self, w: &mut W, instance: &Instance, hi_lite: &PrdSet) -> Res<()> + where + W: Write, + { + writeln!(w, "digraph blah {{",)?; + writeln!(w, " rankdir=LR ;")?; + writeln!(w, " edge [arrowhead=onormal] ;")?; + writeln!( + w, + " top[\ label = \"top\", peripheries = 2, color = darkolivegreen3, \ style = filled, fontcolor = white ] ;" - ) ? ; - writeln!( - w, " bot[\ + )?; + writeln!( + w, + " bot[\ label = \"bot\", peripheries = 2, color = indianred1, \ style = filled, fontcolor = white ] ;" - ) ? ; - for (prd, info) in instance.preds().index_iter() { - if instance.forced_terms_of(prd).is_none() { - if hi_lite.contains(& prd) { - writeln!( - w, - " p_{} [label = \"{}\", color = gray, style = filled] ;", - prd, info.name - ) ? - } else { - writeln!(w, " p_{} [label = \"{}\"] ;", prd, info.name) ? + )?; + for (prd, info) in instance.preds().index_iter() { + if instance.forced_terms_of(prd).is_none() { + if hi_lite.contains(&prd) { + writeln!( + w, + " p_{} [label = \"{}\", color = gray, style = filled] ;", + prd, info.name + )? + } else { + writeln!(w, " p_{} [label = \"{}\"] ;", prd, info.name)? + } + } } - } - } - for (prd, count) in self.pos.index_iter() { - if * count > 0 { - writeln!( - w, " top -> p_{} [color = darkolivegreen3, label = \"{}\"] ;", - prd, count - ) ? - } - } - for (prd, count) in self.neg.index_iter() { - if * count > 0 { - writeln!( - w, " p_{} -> bot [color = indianred1, label = \"{}\"] ;", - prd, count - ) ? - } - } - for (prd, targets) in self.forward.index_iter() { - for (tgt, count) in targets.index_iter() { - let count = * count ; - if count > 0 { - writeln!(w, " p_{} -> p_{} [label = \"{}\"] ;", prd, tgt, count) ? + for (prd, count) in self.pos.index_iter() { + if *count > 0 { + writeln!( + w, + " top -> p_{} [color = darkolivegreen3, label = \"{}\"] ;", + prd, count + )? + } } - } - } - writeln!(w, "}}\n") ? ; - Ok(()) - } - - /// Dumps a graph to a file as a graphviz graph, and runs `dot`. - #[inline] - pub fn to_dot>( - & self, instance: & Instance, file: S, hi_lite: & PrdSet - ) -> Res<()> { - if let Some( - (mut pred_dep_file, path) - ) = conf.preproc.pred_dep_file(file, instance) ? { - use std::process::Command ; - self.dot_write(& mut pred_dep_file, instance, hi_lite) ? ; - let mut pdf_path = path.clone() ; - pdf_path.set_extension("pdf") ; - let output = match Command::new("dot").args( - & ["-Tpdf", "-o"] - ).arg(pdf_path).arg(& path).output() { - Ok(output) => output, - Err(ref e) if e.kind() == ::std::io::ErrorKind::NotFound => { - warn!( - "failed to run `{}` on predicate dependency graphviz file `{}`", - conf.sad("dot"), - conf.emph( path.to_string_lossy() ), - ) ; - return Ok(()) - }, - Err(e) => bail!(e), - } ; - if ! output.status.success() { - let mut blah = format!( - "while running `dot` on `{}`\n|=| stdout:", - path.to_string_lossy() - ) ; - for line in String::from_utf8_lossy(& output.stdout).lines() { - blah.push_str("\n | ") ; - blah.push_str(line) + for (prd, count) in self.neg.index_iter() { + if *count > 0 { + writeln!( + w, + " p_{} -> bot [color = indianred1, label = \"{}\"] ;", + prd, count + )? + } } - blah.push_str("\n|=| stdout:") ; - for line in String::from_utf8_lossy(& output.stderr).lines() { - blah.push_str("\n | ") ; - blah.push_str(line) + for (prd, targets) in self.forward.index_iter() { + for (tgt, count) in targets.index_iter() { + let count = *count; + if count > 0 { + writeln!(w, " p_{} -> p_{} [label = \"{}\"] ;", prd, tgt, count)? + } + } } - blah.push_str("\n|=|") ; - bail!(blah) - } + writeln!(w, "}}\n")?; + Ok(()) } - Ok(()) - } - - /// Checks that the graph makes sense. - #[cfg( not(debug_assertions) )] - #[inline(always)] - pub fn check(& self, _: & Instance) -> Res<()> { - Ok(()) - } - - /// Checks that the graph makes sense. - #[cfg(debug_assertions)] - pub fn check(& self, instance: & Instance) -> Res<()> { - fn sub_check(slf: & Graph, instance: & Instance) -> Res<()> { - for (prd, targets) in slf.forward.index_iter() { - for (tgt, count) in targets.index_iter() { - let bak_count = & slf.bakward[tgt][prd] ; - if bak_count != count { - bail!( - "\ - found {} forward dependencies for `{} -> {}`, \ - but {} (!= {}) backward dependencies\ - ", count, instance[prd], instance[tgt], bak_count, count - ) - } - } - } - for (prd, targets) in slf.bakward.index_iter() { - for (tgt, count) in targets.index_iter() { - let for_count = & slf.forward[tgt][prd] ; - if for_count != count { - bail!( - "\ - found {} backward dependencies for `{} -> {}`, \ - but {} (!= {}) forward dependencies\ - ", count, instance[prd], instance[tgt], for_count, count - ) - } + + /// Dumps a graph to a file as a graphviz graph, and runs `dot`. + #[inline] + pub fn to_dot(&self, instance: &Instance, file: S, hi_lite: &PrdSet) -> Res<()> + where + S: AsRef, + { + if let Some((mut pred_dep_file, path)) = conf.preproc.pred_dep_file(file, instance)? { + use std::process::Command; + self.dot_write(&mut pred_dep_file, instance, hi_lite)?; + let mut pdf_path = path.clone(); + pdf_path.set_extension("pdf"); + let output = match Command::new("dot") + .args(&["-Tpdf", "-o"]) + .arg(pdf_path) + .arg(&path) + .output() + { + Ok(output) => output, + Err(ref e) if e.kind() == ::std::io::ErrorKind::NotFound => { + warn!( + "failed to run `{}` on predicate dependency graphviz file `{}`", + conf.sad("dot"), + conf.emph(path.to_string_lossy()), + ); + return Ok(()); + } + Err(e) => bail!(e), + }; + if !output.status.success() { + let mut blah = format!( + "while running `dot` on `{}`\n|=| stdout:", + path.to_string_lossy() + ); + for line in String::from_utf8_lossy(&output.stdout).lines() { + blah.push_str("\n | "); + blah.push_str(line) + } + blah.push_str("\n|=| stdout:"); + for line in String::from_utf8_lossy(&output.stderr).lines() { + blah.push_str("\n | "); + blah.push_str(line) + } + blah.push_str("\n|=|"); + bail!(blah) + } } - } - Ok(()) + Ok(()) } - sub_check(self, instance).chain_err( - || "graph inconsistency:" - ) - } - - - - - /// Follows a forward map. Returns the predicates it encountered and how many - /// times it encountered them. - pub fn follow( - _instance: & Instance, start: PrdIdx, forward: & PrdHMap - ) -> Res< PrdHMap > { - let mut known = PrdSet::new() ; - let mut to_do = PrdSet::new() ; - to_do.insert(start) ; - known.insert(start) ; - - let mut res = PrdHMap::new() ; - res.insert(start, 1) ; - - macro_rules! update { - ($known:ident $to_do:ident $map:ident < $set:expr) => ( - for prd in $set { - * $map.entry(* prd).or_insert(0) += 1 ; - let unknown = $known.insert(* prd) ; - if unknown { - let is_new = $to_do.insert(* prd) ; - debug_assert!( is_new ) - } + + /// Checks that the graph makes sense. + #[cfg(not(debug_assertions))] + #[inline(always)] + pub fn check(&self, _: &Instance) -> Res<()> { + Ok(()) + } + + /// Checks that the graph makes sense. + #[cfg(debug_assertions)] + pub fn check(&self, instance: &Instance) -> Res<()> { + fn sub_check(slf: &Graph, instance: &Instance) -> Res<()> { + for (prd, targets) in slf.forward.index_iter() { + for (tgt, count) in targets.index_iter() { + let bak_count = &slf.bakward[tgt][prd]; + if bak_count != count { + bail!( + "\ + found {} forward dependencies for `{} -> {}`, \ + but {} (!= {}) backward dependencies\ + ", + count, + instance[prd], + instance[tgt], + bak_count, + count + ) + } + } + } + for (prd, targets) in slf.bakward.index_iter() { + for (tgt, count) in targets.index_iter() { + let for_count = &slf.forward[tgt][prd]; + if for_count != count { + bail!( + "\ + found {} backward dependencies for `{} -> {}`, \ + but {} (!= {}) forward dependencies\ + ", + count, + instance[prd], + instance[tgt], + for_count, + count + ) + } + } + } + Ok(()) } - ) ; + sub_check(self, instance).chain_err(|| "graph inconsistency:") } - while ! to_do.is_empty() { - conf.check_timeout() ? ; - let pred = * to_do.iter().next().unwrap() ; - to_do.remove(& pred) ; - if let Some(tgts) = forward.get(& pred) { - if_debug! { - log_debug! { "following {}", _instance[pred] } ; - for pred in tgts { - log_debug! { "- {}", _instance[* pred] } - } + /// Follows a forward map. Returns the predicates it encountered and how many + /// times it encountered them. + pub fn follow( + _instance: &Instance, + start: PrdIdx, + forward: &PrdHMap, + ) -> Res> { + let mut known = PrdSet::new(); + let mut to_do = PrdSet::new(); + to_do.insert(start); + known.insert(start); + + let mut res = PrdHMap::new(); + res.insert(start, 1); + + macro_rules! update { + ($known:ident $to_do:ident $map:ident < $set:expr) => { + for prd in $set { + *$map.entry(*prd).or_insert(0) += 1; + let unknown = $known.insert(*prd); + if unknown { + let is_new = $to_do.insert(*prd); + debug_assert!(is_new) + } + } + }; } - update!( known to_do res < tgts ) - } - } + while !to_do.is_empty() { + conf.check_timeout()?; + let pred = *to_do.iter().next().unwrap(); + to_do.remove(&pred); + if let Some(tgts) = forward.get(&pred) { + if_debug! { + log_debug! { "following {}", _instance[pred] } ; + for pred in tgts { + log_debug! { "- {}", _instance[* pred] } + } + } + + update!( known to_do res < tgts ) + } + } - Ok(res) - } - - - - /// Merges two DNFs with quantifiers. - /// - /// Also returns the clause generation estimation. - /// - /// Returns `None` if inlining would have generated more clauses than `max` - /// (by `estimate`). - /// - /// Used when inlining a predicate application of `p`. `lft` is understood as - /// (part of) the definition of `p` different from `pred`. `subst` are its - /// arguments. - /// - /// The arguments of `p` are applied to `lft`, and the quantified variables - /// are bumped to the first variable index used by neither the formal - /// arguments of `pred` and the quantified variables in `rgt`. - /// - /// Renames the quantified variables so that they don't clash. - /// - /// # TODO - /// - /// - improve performance, `rgt` could just receive the result - fn merge( - instance: & Instance, pred: PrdIdx, - substs: & VarTermsSet, lft: DnfRef, rgt: DnfRef, max: Option, - ) -> Res< Option< MergeRes > > { - log! { - @6 "merging for {}, {} substitutions", instance[pred], substs.len() + Ok(res) } - let fresh_index = instance.original_sig_of(pred).next_index() ; - log! { @6 "fresh index: {}", fresh_index } - let mut result = Vec::with_capacity( lft.len() * rgt.len() ) ; - let mut estimation = 0 ; + /// Merges two DNFs with quantifiers. + /// + /// Also returns the clause generation estimation. + /// + /// Returns `None` if inlining would have generated more clauses than `max` + /// (by `estimate`). + /// + /// Used when inlining a predicate application of `p`. `lft` is understood as + /// (part of) the definition of `p` different from `pred`. `subst` are its + /// arguments. + /// + /// The arguments of `p` are applied to `lft`, and the quantified variables + /// are bumped to the first variable index used by neither the formal + /// arguments of `pred` and the quantified variables in `rgt`. + /// + /// Renames the quantified variables so that they don't clash. + /// + /// # TODO + /// + /// - improve performance, `rgt` could just receive the result + fn merge( + instance: &Instance, + pred: PrdIdx, + substs: &VarTermsSet, + lft: DnfRef, + rgt: DnfRef, + max: Option, + ) -> Res> { + log! { + @6 "merging for {}, {} substitutions", instance[pred], substs.len() + } + let fresh_index = instance.original_sig_of(pred).next_index(); + log! { @6 "fresh index: {}", fresh_index } - // This will map `lft` quantified variables to fresh index to avoid - // quantified variable clashes. - let mut qvar_map = VarHMap::with_capacity(0) ; + let mut result = Vec::with_capacity(lft.len() * rgt.len()); + let mut estimation = 0; - for & (ref r_qvars, ref r_conj) in rgt { - // Retrieve first legal index for new quantified variables. - let mut fresh_index = fresh_index ; + // This will map `lft` quantified variables to fresh index to avoid + // quantified variable clashes. + let mut qvar_map = VarHMap::with_capacity(0); - for (idx, _) in r_qvars { - log! { @7 "- rgt qvar {}", idx } - if * idx >= fresh_index { - fresh_index = (1 + ** idx).into() - } - } - log! { @7 "first legal index: {}", fresh_index } - - // All combinations of elements of `lft` of len `substs`. - let mut all_lft_combinations = CombinationIter::new( - lft.iter(), substs.len() - ).chain_err( - || format!( - "while generating all combinations during merge for predicate {}", - instance[pred] - ) - ) ? ; - - // For each combination of elements of `lft` of len `substs`, add a new - // definition to `r_conj`. - while let Some( - combination - ) = all_lft_combinations.next_combination() { - - // Done with this combination. - if let Some(res) = Self::merge_combination( - r_conj, r_qvars, & mut qvar_map, - combination, substs, & mut fresh_index - ) ? { - - // Only add if new (loose syntactic check). - if result.iter().all( - |other| other != & res - ) { - result.push(res) ; - - if let Some(max) = max { - if let Some(e) = Self::estimate( - instance, pred, result.len(), max - ) { - estimation += e - } else { - return Ok(None) - } - } - } + for &(ref r_qvars, ref r_conj) in rgt { + // Retrieve first legal index for new quantified variables. + let mut fresh_index = fresh_index; + for (idx, _) in r_qvars { + log! { @7 "- rgt qvar {}", idx } + if *idx >= fresh_index { + fresh_index = (1 + **idx).into() + } + } + log! { @7 "first legal index: {}", fresh_index } + + // All combinations of elements of `lft` of len `substs`. + let mut all_lft_combinations = CombinationIter::new(lft.iter(), substs.len()) + .chain_err(|| { + format!( + "while generating all combinations during merge for predicate {}", + instance[pred] + ) + })?; + + // For each combination of elements of `lft` of len `substs`, add a new + // definition to `r_conj`. + while let Some(combination) = all_lft_combinations.next_combination() { + // Done with this combination. + if let Some(res) = Self::merge_combination( + r_conj, + r_qvars, + &mut qvar_map, + combination, + substs, + &mut fresh_index, + )? { + // Only add if new (loose syntactic check). + if result.iter().all(|other| other != &res) { + result.push(res); + + if let Some(max) = max { + if let Some(e) = Self::estimate(instance, pred, result.len(), max) { + estimation += e + } else { + return Ok(None); + } + } + } + } + } } - } + Ok(Some(MergeRes::new(result, estimation))) } - Ok( - Some( MergeRes::new(result, estimation) ) - ) - } - - - /// Handles a merge combination. - fn merge_combination( - r_conj: & TTermSet, r_qvars: & Quantfed, qvar_map: & mut VarHMap, - combination: & [ & (Quantfed, TTermSet) ], substs: & VarTermsSet, - fresh_index: & mut VarIdx, - ) -> Res< Option<(Quantfed, TTermSet)> > { - debug_assert_eq! { combination.len(), substs.len() } - - // Cloning `rgt`'s definition to add stuff from `lft` for this - // combination. - // Cloning `rgt`'s qvars to add the renamed qvars from `lft`. - let (mut r_conj, mut r_qvars) = ( r_conj.clone(), r_qvars.clone() ) ; - - // Work on the current combination: apply a substitution to a member of - // `lft`. - for ( (l_qvars, l_conj), subst ) in combination.iter().zip( - substs.iter() - ) { - conf.check_timeout() ? ; - log! { @7 "working on substitution..." } - - // Fresh map for this substitution. - qvar_map.clear() ; - - // Generate fresh indices for `l_qvars` to avoid index clashes. - qvar_map.reserve( l_qvars.len() ) ; - r_qvars.reserve( l_qvars.len() ) ; - for (qvar, typ) in l_qvars { - let prev = qvar_map.insert( - * qvar, term::var( * fresh_index, typ.clone() ) - ) ; - debug_assert!( prev.is_none() ) ; - // Update the new qvars for the result. - let prev = r_qvars.insert( * fresh_index, typ.clone() ) ; - debug_assert!( prev.is_none() ) ; - // Increment the fresh_index. - fresh_index.inc() - } - - // Apply substitution and add to `r_conj`. - - r_conj.reserve( - l_conj.terms().len(), l_conj.preds().len() - ) ; - // Working on terms. - for term in l_conj.terms() { - log! { @8 "subst on {}", term } - let (term, _) = term.subst( & (& * qvar_map, subst) ) ; - log! { @8 "-> {}", term } - - let is_false = term::simplify::conj_term_insert( - term, r_conj.terms_mut() - ) ; - - if is_false { - return Ok(None) - } - } - - // Working on pred applications. - for (pred, argss) in l_conj.preds() { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss { - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let (arg, _) = arg.subst( & (& * qvar_map, subst) ) ; - nu_args.push(arg) - } - nu_argss.insert( nu_args.into() ) ; - } - r_conj.insert_pred_apps( * pred, nu_argss ) - } - } + /// Handles a merge combination. + fn merge_combination( + r_conj: &TTermSet, + r_qvars: &Quantfed, + qvar_map: &mut VarHMap, + combination: &[&(Quantfed, TTermSet)], + substs: &VarTermsSet, + fresh_index: &mut VarIdx, + ) -> Res> { + debug_assert_eq! { combination.len(), substs.len() } + + // Cloning `rgt`'s definition to add stuff from `lft` for this + // combination. + // Cloning `rgt`'s qvars to add the renamed qvars from `lft`. + let (mut r_conj, mut r_qvars) = (r_conj.clone(), r_qvars.clone()); + + // Work on the current combination: apply a substitution to a member of + // `lft`. + for ((l_qvars, l_conj), subst) in combination.iter().zip(substs.iter()) { + conf.check_timeout()?; + log! { @7 "working on substitution..." } + + // Fresh map for this substitution. + qvar_map.clear(); + + // Generate fresh indices for `l_qvars` to avoid index clashes. + qvar_map.reserve(l_qvars.len()); + r_qvars.reserve(l_qvars.len()); + for (qvar, typ) in l_qvars { + let prev = qvar_map.insert(*qvar, term::var(*fresh_index, typ.clone())); + debug_assert!(prev.is_none()); + // Update the new qvars for the result. + let prev = r_qvars.insert(*fresh_index, typ.clone()); + debug_assert!(prev.is_none()); + // Increment the fresh_index. + fresh_index.inc() + } - Ok( Some((r_qvars, r_conj)) ) - } - - - - /// Retrieves the definition of a predicate from all its RHS occurences. - /// - /// Also returns the clause generation estimation. - /// - /// Returns `None` if inlining would have generated more clauses than `max` - /// (by `estimate`). - fn dnf_of( - & mut self, instance: & Instance, extractor: & mut ExtractionCxt, - pred: PrdIdx, max: Option, - previous: & [ (PrdIdx, Dnf) ] - ) -> Res< Option > { - log! { @4 "dnf_of({}, {:?})", instance[pred], max } - - let forced_inlining = max.is_none() ; - - let mut estimation = 0 ; - - let clauses = instance.rhs_clauses_of(pred) ; - let mut def = Vec::with_capacity( clauses.len() ) ; - - conf.check_timeout() ? ; - - 'clause_iter: for clause in clauses { - let mut to_merge: Vec< - (PrdIdx, VarTermsSet, & Dnf) - > = Vec::with_capacity(7) ; - - let clause = & instance[* clause] ; - let args = if let Some((p, args)) = clause.rhs() { - debug_assert_eq! { p, pred } - args - } else { - bail!("instance inconsistency") - } ; - - match extractor.terms_of_rhs_app( - true, instance, & clause.vars, - clause.lhs_terms(), clause.lhs_preds(), (pred, args) - ) ? { - utils::ExtractRes::Success((qvars, mut tterms)) => { - log! { @5 - "from clause {}", clause.to_string_info(& instance.preds()) ? - } + // Apply substitution and add to `r_conj`. - if ! forced_inlining && ! tterms.preds().is_empty() { - for (pred, def) in previous { - if tterms.preds().is_empty() { break } - if let Some(argss) = tterms.preds_mut().remove(pred) { - to_merge.push( (* pred, argss, def) ) - } - } - } + r_conj.reserve(l_conj.terms().len(), l_conj.preds().len()); + // Working on terms. + for term in l_conj.terms() { + log! { @8 "subst on {}", term } + let (term, _) = term.subst(&(&*qvar_map, subst)); + log! { @8 "-> {}", term } - if let Some(partial_def) = Self::handle_partial_def_of( - instance, (def.len(), max), & mut estimation, - pred, & mut to_merge, qvars, tterms - ) ? { - def.extend( partial_def ) - } else { - return Ok(None) - } + let is_false = term::simplify::conj_term_insert(term, r_conj.terms_mut()); - if_log! { @5 - log! { @5 "current definition:" } - Self::log_definition(instance, & def) - } + if is_false { + return Ok(None); + } + } + + // Working on pred applications. + for (pred, argss) in l_conj.preds() { + let mut nu_argss = VarTermsSet::with_capacity(argss.len()); + for args in argss { + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let (arg, _) = arg.subst(&(&*qvar_map, subst)); + nu_args.push(arg) + } + nu_argss.insert(nu_args.into()); + } + r_conj.insert_pred_apps(*pred, nu_argss) + } + } - }, - utils::ExtractRes::SuccessTrue => bail!( - "unimplemented, predicate is true ({} {})", instance[pred], args - ), - utils::ExtractRes::Trivial | - utils::ExtractRes::SuccessFalse => continue 'clause_iter, - _ => bail!("failed to extract lhs terms"), - } + Ok(Some((r_qvars, r_conj))) } - Ok( - Some( MergeRes::new(def, estimation) ) - ) - } - - - /// Prints a definition for some predicate. - #[cfg(not(feature = "bench"))] - fn log_definition(_instance: & Instance, _def: DnfRef) { - for (qvars, tterms) in _def { - log! { @5 "and" } - if ! qvars.is_empty() { - log! { @5 " qvars {{" } - for (var, typ) in qvars { - log! { @5 " {}: {}", var.default_str(), typ } - } - log! { @5 " }}" } - } - for term in tterms.terms() { - log! { @5 " {}", term } - } - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @5 " ({} {})", _instance[* pred], args } + /// Retrieves the definition of a predicate from all its RHS occurences. + /// + /// Also returns the clause generation estimation. + /// + /// Returns `None` if inlining would have generated more clauses than `max` + /// (by `estimate`). + fn dnf_of( + &mut self, + instance: &Instance, + extractor: &mut ExtractionCxt, + pred: PrdIdx, + max: Option, + previous: &[(PrdIdx, Dnf)], + ) -> Res> { + log! { @4 "dnf_of({}, {:?})", instance[pred], max } + + let forced_inlining = max.is_none(); + + let mut estimation = 0; + + let clauses = instance.rhs_clauses_of(pred); + let mut def = Vec::with_capacity(clauses.len()); + + conf.check_timeout()?; + + 'clause_iter: for clause in clauses { + let mut to_merge: Vec<(PrdIdx, VarTermsSet, &Dnf)> = Vec::with_capacity(7); + + let clause = &instance[*clause]; + let args = if let Some((p, args)) = clause.rhs() { + debug_assert_eq! { p, pred } + args + } else { + bail!("instance inconsistency") + }; + + match extractor.terms_of_rhs_app( + true, + instance, + &clause.vars, + clause.lhs_terms(), + clause.lhs_preds(), + (pred, args), + )? { + utils::ExtractRes::Success((qvars, mut tterms)) => { + log! { @5 + "from clause {}", clause.to_string_info(& instance.preds()) ? + } + + if !forced_inlining && !tterms.preds().is_empty() { + for (pred, def) in previous { + if tterms.preds().is_empty() { + break; + } + if let Some(argss) = tterms.preds_mut().remove(pred) { + to_merge.push((*pred, argss, def)) + } + } + } + + if let Some(partial_def) = Self::handle_partial_def_of( + instance, + (def.len(), max), + &mut estimation, + pred, + &mut to_merge, + qvars, + tterms, + )? { + def.extend(partial_def) + } else { + return Ok(None); + } + + if_log! { @5 + log! { @5 "current definition:" } + Self::log_definition(instance, & def) + } + } + utils::ExtractRes::SuccessTrue => bail!( + "unimplemented, predicate is true ({} {})", + instance[pred], + args + ), + utils::ExtractRes::Trivial | utils::ExtractRes::SuccessFalse => { + continue 'clause_iter + } + _ => bail!("failed to extract lhs terms"), + } } - } + + Ok(Some(MergeRes::new(def, estimation))) } - log! { @5 => " " } - } - - - - /// Handles a conjunction that's part of a definition for a predicate. - fn handle_partial_def_of( - instance: & Instance, - (def_len, max): (usize, Option), estimation: & mut usize, - pred: PrdIdx, to_merge: & mut Vec< (PrdIdx, VarTermsSet, & Dnf) >, - qvars: Quantfed, tterms: TTermSet - ) -> Res< Option > { - let res = if to_merge.is_empty() { - if let Some(max) = max.map( - |max: usize| if * estimation > max { 0 } else { - max - * estimation + + /// Prints a definition for some predicate. + #[cfg(not(feature = "bench"))] + fn log_definition(_instance: &Instance, _def: DnfRef) { + for (qvars, tterms) in _def { + log! { @5 "and" } + if !qvars.is_empty() { + log! { @5 " qvars {{" } + for (var, typ) in qvars { + log! { @5 " {}: {}", var.default_str(), typ } + } + log! { @5 " }}" } + } + for term in tterms.terms() { + log! { @5 " {}", term } + } + for (pred, argss) in tterms.preds() { + for args in argss { + log! { @5 " ({} {})", _instance[* pred], args } + } + } } - ) { - if let Some(e) = Self::estimate(instance, pred, def_len + 1, max) { - * estimation += e + log! { @5 => " " } + } + + /// Handles a conjunction that's part of a definition for a predicate. + fn handle_partial_def_of( + instance: &Instance, + (def_len, max): (usize, Option), + estimation: &mut usize, + pred: PrdIdx, + to_merge: &mut Vec<(PrdIdx, VarTermsSet, &Dnf)>, + qvars: Quantfed, + tterms: TTermSet, + ) -> Res> { + let res = if to_merge.is_empty() { + if let Some(max) = max.map(|max: usize| { + if *estimation > max { + 0 + } else { + max - *estimation + } + }) { + if let Some(e) = Self::estimate(instance, pred, def_len + 1, max) { + *estimation += e + } else { + return Ok(None); + } + } + Some(vec![(qvars, tterms)]) + } else if let Some(def) = Self::sub_handle_partial_def_of( + instance, max, estimation, pred, to_merge, qvars, tterms, + )? { + Some(def) } else { - return Ok(None) - } - } - Some( vec![ (qvars, tterms) ] ) - } else if let Some(def) = Self::sub_handle_partial_def_of( - instance, max, estimation, pred, to_merge, qvars, tterms - ) ? { - Some(def) - } else { - None - } ; - - Ok(res) - } - - - - /// Handles a conjunction that's part of a definition for a predicate. - fn sub_handle_partial_def_of( - instance: & Instance, max: Option, estimation: & mut usize, - pred: PrdIdx, to_merge: & mut Vec< (PrdIdx, VarTermsSet, & Dnf) >, - qvars: Quantfed, tterms: TTermSet - ) -> Res< Option > { - if_log! { @5 - log! { @5 => "qvars {{" } - for (var, typ) in & qvars { - log! { @5 => " {}: {}", var.default_str(), typ } - } - log! { @5 => "}}" } - log! { @5 => "conj {{" } - for term in tterms.terms() { - log! { @5 => " {}", term } - } - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @5 => " ({} {})", instance[* pred], args } - } - } - log! { @5 => "}}" } + None + }; + + Ok(res) } - let mut curr = vec![ (qvars, tterms) ] ; + /// Handles a conjunction that's part of a definition for a predicate. + fn sub_handle_partial_def_of( + instance: &Instance, + max: Option, + estimation: &mut usize, + pred: PrdIdx, + to_merge: &mut Vec<(PrdIdx, VarTermsSet, &Dnf)>, + qvars: Quantfed, + tterms: TTermSet, + ) -> Res> { + if_log! { @5 + log! { @5 => "qvars {{" } + for (var, typ) in & qvars { + log! { @5 => " {}: {}", var.default_str(), typ } + } + log! { @5 => "}}" } + log! { @5 => "conj {{" } + for term in tterms.terms() { + log! { @5 => " {}", term } + } + for (pred, argss) in tterms.preds() { + for args in argss { + log! { @5 => " ({} {})", instance[* pred], args } + } + } + log! { @5 => "}}" } + } - for (_this_pred, argss, p_def) in to_merge.drain(0..) { - conf.check_timeout() ? ; + let mut curr = vec![(qvars, tterms)]; - if_log! { @6 - Self::log_merge_defs( - _this_pred, instance, & argss, p_def, & curr - ) - } + for (_this_pred, argss, p_def) in to_merge.drain(0..) { + conf.check_timeout()?; - if let Some(res) = Self::merge( - instance, pred, & argss, p_def, & curr, max.map( - |max: usize| if * estimation > max { 0 } else { - max - * estimation - } - ) - ) ? { - curr = res.def ; - * estimation += res.estimation ; - } else { - return Ok(None) - } - } + if_log! { @6 + Self::log_merge_defs( + _this_pred, instance, & argss, p_def, & curr + ) + } - Ok( Some(curr) ) - } - - - /// Prints definition before merging. - #[cfg(not(feature = "bench"))] - fn log_merge_defs( - _this_pred: PrdIdx, _instance: & Instance, - _argss: & VarTermsSet, _p_def: DnfRef, _curr: DnfRef - ) { - log! { @6 => "curr {{" } - let mut first = true ; - for & (ref qv, ref tterms) in _curr { - if first { first = false } else { log! { @6 => " " } } - for (var, typ) in qv { - log! { @6 => " {}: {}", var.default_str(), typ } - } - for term in tterms.terms() { - log! { @6 => " {}", term } - } - for (pred, _argss) in tterms.preds() { - for args in _argss { - log! { @6 => " ({} {})", _instance[* pred], args } + if let Some(res) = Self::merge( + instance, + pred, + &argss, + p_def, + &curr, + max.map(|max: usize| { + if *estimation > max { + 0 + } else { + max - *estimation + } + }), + )? { + curr = res.def; + *estimation += res.estimation; + } else { + return Ok(None); + } } - } - } - log! { @6 => "}}" } - Self::log_merge_defs_sub(_this_pred, _instance, _argss, _p_def) - } - - /// Prints definition before merging. - #[cfg(not(feature = "bench"))] - fn log_merge_defs_sub( - _this_pred: PrdIdx, _instance: & Instance, - _argss: & VarTermsSet, _p_def: DnfRef - ) { - log! { @6 => "argss for {} {{", _instance[_this_pred] } - for args in _argss { - let mut pref = " > " ; - for (var, arg) in args.index_iter() { - log! { @6 => "{}{} -> {}", pref, var.default_str(), arg } - pref = " " - } + + Ok(Some(curr)) } - log! { @6 => "}}" } - log! { @6 => "defs {{" } - let mut first = true ; - - for & (ref qv, ref tterms) in _p_def { - if first { first = false } else { log! { @6 => " " } } - for (var, typ) in qv { - log! { @6 => " {}: {}", var.default_str(), typ } - } - for term in tterms.terms() { - log! { @6 => " {}", term } - } - for (pred, _argss) in tterms.preds() { - for args in _argss { - log! { @6 => " ({} {})", _instance[* pred], args } + + /// Prints definition before merging. + #[cfg(not(feature = "bench"))] + fn log_merge_defs( + _this_pred: PrdIdx, + _instance: &Instance, + _argss: &VarTermsSet, + _p_def: DnfRef, + _curr: DnfRef, + ) { + log! { @6 => "curr {{" } + let mut first = true; + for &(ref qv, ref tterms) in _curr { + if first { + first = false + } else { + log! { @6 => " " } + } + for (var, typ) in qv { + log! { @6 => " {}: {}", var.default_str(), typ } + } + for term in tterms.terms() { + log! { @6 => " {}", term } + } + for (pred, _argss) in tterms.preds() { + for args in _argss { + log! { @6 => " ({} {})", _instance[* pred], args } + } + } } - } - } - log! { @6 => "}}" } - } - - - - /// Constructs all the predicates not in `keep` by inlining the constraints. - /// - /// Returns a disjunction of conjunctions. - pub fn inline( - & mut self, instance: & mut PreInstance, - keep: & mut PrdSet, mut upper_bound: usize, - ) -> Res< Vec< (PrdIdx, Dnf) > > { - let (extractor, instance) = instance.extraction() ; - let mut res = Vec::with_capacity( - instance.preds().len() - keep.len() - ) ; - macro_rules! res_contains { - ($pred:expr) => ( res.iter().any( |(pred, _)| pred == $pred ) ) ; + log! { @6 => "}}" } + Self::log_merge_defs_sub(_this_pred, _instance, _argss, _p_def) } - let forced_inlining = keep.len() == 0 ; - - conf.check_timeout() ? ; - - 'construct: loop { - - // Find a predicate that's not in `keep` with all its antecedents in - // `keep`. - let mut pred = None ; - 'find_pred: for (prd, srcs) in self.bakward.index_iter() { - if keep.contains(& prd) - || instance.forced_terms_of(prd).is_some() - || res_contains!(& prd) { continue 'find_pred } - log_debug! { "looking at {}", instance[prd] } - 'check_src: for (src, cnt) in srcs.index_iter() { - if * cnt == 0 { continue 'check_src } - log_debug! { "depends on {}", instance[src] } - if ! keep.contains(& src) - && ! res_contains!(& src) { continue 'find_pred } + /// Prints definition before merging. + #[cfg(not(feature = "bench"))] + fn log_merge_defs_sub( + _this_pred: PrdIdx, + _instance: &Instance, + _argss: &VarTermsSet, + _p_def: DnfRef, + ) { + log! { @6 => "argss for {} {{", _instance[_this_pred] } + for args in _argss { + let mut pref = " > "; + for (var, arg) in args.index_iter() { + log! { @6 => "{}{} -> {}", pref, var.default_str(), arg } + pref = " " + } } - pred = Some(prd) ; - break 'find_pred - } - - let pred = if let Some(p) = pred { p } else { - log_debug! { "no predicate illeligible for inlining" } - break 'construct - } ; - - log_debug! { "investigating inlining {}", instance[pred] } - - macro_rules! keep_and_continue { - ($pred:expr) => ( - keep.insert($pred) ; - continue 'construct - ) ; - () => ({ - keep.insert(pred) ; - continue 'construct - }) ; - } - - let def = if let Some(res) = self.dnf_of( - instance, extractor, pred, - if ! forced_inlining { Some(upper_bound) } else { None }, - & res - ) ? { - upper_bound += res.estimation ; - log! { @4 - "inlining {} (blow-up estimation: {})", - instance[pred], res.estimation + log! { @6 => "}}" } + log! { @6 => "defs {{" } + let mut first = true; + + for &(ref qv, ref tterms) in _p_def { + if first { + first = false + } else { + log! { @6 => " " } + } + for (var, typ) in qv { + log! { @6 => " {}: {}", var.default_str(), typ } + } + for term in tterms.terms() { + log! { @6 => " {}", term } + } + for (pred, _argss) in tterms.preds() { + for args in _argss { + log! { @6 => " ({} {})", _instance[* pred], args } + } + } } - res.def - } else { - keep_and_continue!() - } ; - - if_log! { @4 - log! { @4 => "definition:" } - Self::log_definition(instance, & def) - } - - conf.check_timeout() ? ; - - debug_assert! { ! res_contains!(& pred) } - - res.push( (pred, def) ) + log! { @6 => "}}" } } - Ok( res ) - } - - - /// Estimates clause creation blow-up. - /// - /// Estimates whether inlining `pred` with a DNF of length `len` will - /// generate more than `max` clauses. If yes, returns `None`, and returns its - /// prediction otherwise. - fn estimate( - instance: & Instance, pred: PrdIdx, len: usize, max: usize - ) -> Option { - let max = max + instance.clauses_of(pred).1.len() ; - let mut estimation: usize = 0 ; - - // println!("{}: {} / {}", instance[pred], len, max) ; - - for clause in instance.clauses_of(pred).0 { - let argss_len = instance[* clause].lhs_preds().get(& pred).map( - |argss| argss.len() - ).expect("inconsistent instance state") ; - // println!(" #{}: {}", clause, argss_len) ; - - let mut inc = len ; - for _ in 1 .. argss_len { - if inc > max { - return None - } else if let Some(nu_inc) = inc.checked_mul(len) { - inc = nu_inc - } else { - return None + /// Constructs all the predicates not in `keep` by inlining the constraints. + /// + /// Returns a disjunction of conjunctions. + pub fn inline( + &mut self, + instance: &mut PreInstance, + keep: &mut PrdSet, + mut upper_bound: usize, + ) -> Res> { + let (extractor, instance) = instance.extraction(); + let mut res = Vec::with_capacity(instance.preds().len() - keep.len()); + macro_rules! res_contains { + ($pred:expr) => { + res.iter().any(|(pred, _)| pred == $pred) + }; } - } - if let Some(e) = estimation.checked_add(inc) { - estimation = e ; - if estimation <= max { - // println!(" est: {}", estimation) ; - continue - } - } - // println!("blows up") ; - log! { @6 - "inlining for {} blows up: {} > {} (len: {})", - instance[pred], estimation, max, len - } - return None - } + let forced_inlining = keep.len() == 0; + + conf.check_timeout()?; + + 'construct: loop { + // Find a predicate that's not in `keep` with all its antecedents in + // `keep`. + let mut pred = None; + 'find_pred: for (prd, srcs) in self.bakward.index_iter() { + if keep.contains(&prd) + || instance.forced_terms_of(prd).is_some() + || res_contains!(&prd) + { + continue 'find_pred; + } + log_debug! { "looking at {}", instance[prd] } + 'check_src: for (src, cnt) in srcs.index_iter() { + if *cnt == 0 { + continue 'check_src; + } + log_debug! { "depends on {}", instance[src] } + if !keep.contains(&src) && !res_contains!(&src) { + continue 'find_pred; + } + } + pred = Some(prd); + break 'find_pred; + } - Some(estimation) - } + let pred = if let Some(p) = pred { + p + } else { + log_debug! { "no predicate illeligible for inlining" } + break 'construct; + }; + + log_debug! { "investigating inlining {}", instance[pred] } + + macro_rules! keep_and_continue { + ($pred:expr) => { + keep.insert($pred); + continue 'construct; + }; + () => {{ + keep.insert(pred); + continue 'construct; + }}; + } + let def = if let Some(res) = self.dnf_of( + instance, + extractor, + pred, + if !forced_inlining { + Some(upper_bound) + } else { + None + }, + &res, + )? { + upper_bound += res.estimation; + log! { @4 + "inlining {} (blow-up estimation: {})", + instance[pred], res.estimation + } + res.def + } else { + keep_and_continue!() + }; + + if_log! { @4 + log! { @4 => "definition:" } + Self::log_definition(instance, & def) + } + conf.check_timeout()?; + debug_assert! { ! res_contains!(& pred) } - /// Finds a predicate to start breaking cycles from in the graph. - fn find_starting_pred( - & self, _instance: & Instance, pos: & PrdSet, forward: & PrdHMap - ) -> Option { - if_log! { @3 - log! { @3 => " looking for a starting point with" } - log! { @3 => " - pos {{" } - for prd in pos { - log! { @3 => " {}", _instance[* prd] } - } - log! { @3 => " }}" } - log! { @3 => " - forward {{" } - for (prd, set) in forward { - let mut s = String::new() ; - for prd in set { - s = format!("{} {}", s, _instance[* prd]) + res.push((pred, def)) } - log! { @3 => " {} ->{}", _instance[* prd], s } - } - log! { @3 => " }}" } - } - // Find a starting point. - if let Some(pred) = pos.iter().next() { - log! { @3 " found one in `pos`" } - // There was something in `pos`, remove it and move on. - Some(* pred) - } else { - log! { @3 " no preds in `pos`, looking in `forward`" } - // There was nothing in `pos`, select something from `forward`. - forward.iter().next().map( - |(pred, _)| * pred - ) + Ok(res) } - } + /// Estimates clause creation blow-up. + /// + /// Estimates whether inlining `pred` with a DNF of length `len` will + /// generate more than `max` clauses. If yes, returns `None`, and returns its + /// prediction otherwise. + fn estimate(instance: &Instance, pred: PrdIdx, len: usize, max: usize) -> Option { + let max = max + instance.clauses_of(pred).1.len(); + let mut estimation: usize = 0; + + // println!("{}: {} / {}", instance[pred], len, max) ; + + for clause in instance.clauses_of(pred).0 { + let argss_len = instance[*clause] + .lhs_preds() + .get(&pred) + .map(|argss| argss.len()) + .expect("inconsistent instance state"); + // println!(" #{}: {}", clause, argss_len) ; + + let mut inc = len; + for _ in 1..argss_len { + if inc > max { + return None; + } else if let Some(nu_inc) = inc.checked_mul(len) { + inc = nu_inc + } else { + return None; + } + } + if let Some(e) = estimation.checked_add(inc) { + estimation = e; + if estimation <= max { + // println!(" est: {}", estimation) ; + continue; + } + } + // println!("blows up") ; + log! { @6 + "inlining for {} blows up: {} > {} (len: {})", + instance[pred], estimation, max, len + } + return None; + } - /// Looks for a minimal set of predicates to infer by breaking cycles. - /// - /// Returns the set of predicates to keep, and the set of predicates to - /// remove. - pub fn break_cycles( - & mut self, instance: & Instance - ) -> Res { - log_debug! { "breaking cycles in pred dep graph..." } + Some(estimation) + } - let (mut set, mut pos, mut forward) = self.get_current_graph(instance) ? ; + /// Finds a predicate to start breaking cycles from in the graph. + fn find_starting_pred( + &self, + _instance: &Instance, + pos: &PrdSet, + forward: &PrdHMap, + ) -> Option { + if_log! { @3 + log! { @3 => " looking for a starting point with" } + log! { @3 => " - pos {{" } + for prd in pos { + log! { @3 => " {}", _instance[* prd] } + } + log! { @3 => " }}" } + log! { @3 => " - forward {{" } + for (prd, set) in forward { + let mut s = String::new() ; + for prd in set { + s = format!("{} {}", s, _instance[* prd]) + } + log! { @3 => " {} ->{}", _instance[* prd], s } + } + log! { @3 => " }}" } + } - let mut cnt = 0 ; + // Find a starting point. + if let Some(pred) = pos.iter().next() { + log! { @3 " found one in `pos`" } + // There was something in `pos`, remove it and move on. + Some(*pred) + } else { + log! { @3 " no preds in `pos`, looking in `forward`" } + // There was nothing in `pos`, select something from `forward`. + forward.iter().next().map(|(pred, _)| *pred) + } + } - conf.check_timeout() ? ; - 'break_cycles: while ! forward.is_empty() { + /// Looks for a minimal set of predicates to infer by breaking cycles. + /// + /// Returns the set of predicates to keep, and the set of predicates to + /// remove. + pub fn break_cycles(&mut self, instance: &Instance) -> Res { + log_debug! { "breaking cycles in pred dep graph..." } - cnt += 1 ; - self.to_dot(instance, format!("pred_red_{}", cnt), & set) ? ; + let (mut set, mut pos, mut forward) = self.get_current_graph(instance)?; - let start = self.find_starting_pred(instance, & pos, & forward) ; + let mut cnt = 0; - let start = if let Some(pred) = start { pred } else { - log! { @3 " no starting point found, done" } - break 'break_cycles - } ; + conf.check_timeout()?; + 'break_cycles: while !forward.is_empty() { + cnt += 1; + self.to_dot(instance, format!("pred_red_{}", cnt), &set)?; - log! { @3 " starting point is {}, following it", instance[start] } + let start = self.find_starting_pred(instance, &pos, &forward); - // Follow it. - let weights = Self::follow(instance, start, & forward) ? ; - if weights.is_empty() { - bail!("`follow` failed to construct weights...") - } + let start = if let Some(pred) = start { + pred + } else { + log! { @3 " no starting point found, done" } + break 'break_cycles; + }; - if let Some(pred) = Self::find_heaviest(instance, & weights) ? { - log! { @3 - " removing it from everything" ; - " remembering {}", instance[pred] - } - // Remove the representative from everything. - Self::forget(pred, & mut pos, & mut forward) ; + log! { @3 " starting point is {}, following it", instance[start] } - let is_new = set.insert(pred) ; - debug_assert!( is_new ) ; - - log! { @3 "" } + // Follow it. + let weights = Self::follow(instance, start, &forward)?; + if weights.is_empty() { + bail!("`follow` failed to construct weights...") + } - } else { - // There's no cycle, forget everything in weight and keep going. - for (pred, _weight) in weights { - log! { @3 " - forgetting {} ({})", instance[pred], _weight } - Self::forget(pred, & mut pos, & mut forward) ; - debug_assert!( _weight < 2 ) + if let Some(pred) = Self::find_heaviest(instance, &weights)? { + log! { @3 + " removing it from everything" ; + " remembering {}", instance[pred] + } + // Remove the representative from everything. + Self::forget(pred, &mut pos, &mut forward); + + let is_new = set.insert(pred); + debug_assert!(is_new); + + log! { @3 "" } + } else { + // There's no cycle, forget everything in weight and keep going. + for (pred, _weight) in weights { + log! { @3 " - forgetting {} ({})", instance[pred], _weight } + Self::forget(pred, &mut pos, &mut forward); + debug_assert!(_weight < 2) + } + } } - } + set.shrink_to_fit(); + Ok(set) } - set.shrink_to_fit() ; - Ok(set) - } + /// Retrieves positive predicates and arrows in the current dependency graph. + fn get_current_graph(&self, instance: &Instance) -> Res<(PrdSet, PrdSet, PrdHMap)> { + let mut set = PrdSet::with_capacity(instance.preds().len() / 3); + conf.check_timeout()?; + for (prd, prds) in self.forward.index_iter() { + if prds[prd] > 0 { + let is_new = set.insert(prd); + debug_assert!(is_new) + } + } - /// Retrieves positive predicates and arrows in the current dependency graph. - fn get_current_graph( - & self, instance: & Instance - ) -> Res< (PrdSet, PrdSet, PrdHMap) > { - let mut set = PrdSet::with_capacity(instance.preds().len() / 3) ; + conf.check_timeout()?; + let mut pos = PrdSet::new(); + for (prd, cnt) in self.pos.index_iter() { + if set.contains(&prd) { + continue; + } + if *cnt > 0 { + pos.insert(prd); + } + } - conf.check_timeout() ? ; - for (prd, prds) in self.forward.index_iter() { - if prds[prd] > 0 { - let is_new = set.insert(prd) ; - debug_assert!( is_new ) - } + conf.check_timeout()?; + let mut forward = PrdHMap::new(); + for (prd, prds) in self.forward.index_iter() { + if set.contains(&prd) { + continue; + } + for (tgt, cnt) in prds.index_iter() { + if set.contains(&tgt) { + continue; + } + if *cnt > 0 { + let is_new = forward.entry(prd).or_insert_with(PrdSet::new).insert(tgt); + debug_assert!(is_new) + } + } + } + Ok((set, pos, forward)) } - conf.check_timeout() ? ; - let mut pos = PrdSet::new() ; - for (prd, cnt) in self.pos.index_iter() { - if set.contains(& prd) { continue } - if * cnt > 0 { - pos.insert(prd) ; - } + /// Forgets a predicate from the positive and forward set. + fn forget(pred: PrdIdx, pos: &mut PrdSet, forward: &mut PrdHMap) { + pos.remove(&pred); + forward.remove(&pred); + for (_, set) in forward.iter_mut() { + set.remove(&pred); + } } - conf.check_timeout() ? ; - let mut forward = PrdHMap::new() ; - for (prd, prds) in self.forward.index_iter() { - if set.contains(& prd) { continue } - for (tgt, cnt) in prds.index_iter() { - if set.contains(& tgt) { continue } - if * cnt > 0 { - let is_new = forward.entry(prd).or_insert_with( - PrdSet::new - ).insert(tgt) ; - debug_assert!( is_new ) + /// Looks for the heaviest predicate. + /// + /// Returns `None` if there are no cycles (max weight < 2). + fn find_heaviest(_instance: &Instance, weights: &PrdHMap) -> Res> { + log! { @3 " looking for the heaviest predicate" } + + // Representant. + let mut rep = None; + for (prd, weight) in weights { + let (prd, weight) = (*prd, *weight); + log! { @3 " {} -> {}", _instance[prd], weight } + let curr_weight = if let Some(&(_, w)) = rep.as_ref() { + w + } else { + 0 + }; + if weight > curr_weight { + rep = Some((prd, weight)) + } } - } - } - Ok( (set, pos, forward) ) - } - - - /// Forgets a predicate from the positive and forward set. - fn forget( - pred: PrdIdx, pos: & mut PrdSet, forward: & mut PrdHMap - ) { - pos.remove(& pred) ; - forward.remove(& pred) ; - for (_, set) in forward.iter_mut() { - set.remove(& pred) ; - } - } - - - /// Looks for the heaviest predicate. - /// - /// Returns `None` if there are no cycles (max weight < 2). - fn find_heaviest( - _instance: & Instance, weights: & PrdHMap - ) -> Res< Option > { - log! { @3 " looking for the heaviest predicate" } - - // Representant. - let mut rep = None ; - for (prd, weight) in weights { - let (prd, weight) = (* prd, * weight) ; - log! { @3 " {} -> {}", _instance[prd], weight } - let curr_weight = if let Some( - & (_, w) - ) = rep.as_ref() { w } else { 0 } ; - if weight > curr_weight { - rep = Some((prd, weight)) - } - } - if let Some((pred, weight)) = rep { - log! { @3 " heaviest is {} ({})", _instance[pred], weight } - if weight < 2 { - log! { @3 "no cycle, forgetting everything" } - Ok(None) - } else { - Ok( Some(pred) ) - } - } else { - bail!("try to find heaviest predicate in empty weight map") + if let Some((pred, weight)) = rep { + log! { @3 " heaviest is {} ({})", _instance[pred], weight } + if weight < 2 { + log! { @3 "no cycle, forgetting everything" } + Ok(None) + } else { + Ok(Some(pred)) + } + } else { + bail!("try to find heaviest predicate in empty weight map") + } } - } } - - - - - - - /// Detects cycles and keeps a minimal set of predicates to infer. pub struct CfgRed { - /// Internal counter for log files. - cnt: usize, - /// Upper bound computed once at the beginning to avoid a progressive - /// blow-up. - upper_bound: usize, - /// Graph, factored to avoid reallocation. - graph: Graph, + /// Internal counter for log files. + cnt: usize, + /// Upper bound computed once at the beginning to avoid a progressive + /// blow-up. + upper_bound: usize, + /// Graph, factored to avoid reallocation. + graph: Graph, } - impl CfgRed { - - /// Removes all clauses leading to some predicates and forces them in the - /// instance. - fn apply_pred_defs( - & self, instance: & mut PreInstance, pred_defs: Vec< (PrdIdx, Dnf) > - ) -> Res { - let mut info = RedInfo::new() ; - - for (pred, def) in pred_defs { - if instance.is_known(pred) { - continue - } - - conf.check_timeout() ? ; - info += instance.rm_rhs_clauses_of(pred) ? ; - - if_log! { @5 - let mut s = format!("{}(", instance[pred]) ; - let mut is_first = true ; - for (var, typ) in instance[pred].sig.index_iter() { - if ! is_first { - s.push_str(", ") - } else { - is_first = false - } - s.push_str( & var.default_str() ) ; - s.push_str( & format!(": {}", typ) ) ; - } - s.push_str(") = (or\n") ; - - for & (ref qvars, ref conj) in & def { - - let (suff, pref) = if qvars.is_empty() { - (None, " ") - } else { - s.push_str(" (exists (") ; - for (var, typ) in qvars { - s.push_str(" (") ; - s.push_str( & var.default_str() ) ; - s.push_str( & format!(" {})", typ) ) - } - s.push_str(" )\n") ; - (Some(" )"), " ") - } ; - - s.push_str(pref) ; - s.push_str("(and\n") ; - for term in conj.terms() { - s.push_str( & format!("{} {}\n", pref, term) ) - } - for (pred, argss) in conj.preds() { - for args in argss { - s.push_str( - & format!("{} ({} {})\n", pref, instance[* pred], args) - ) + /// Removes all clauses leading to some predicates and forces them in the + /// instance. + fn apply_pred_defs( + &self, + instance: &mut PreInstance, + pred_defs: Vec<(PrdIdx, Dnf)>, + ) -> Res { + let mut info = RedInfo::new(); + + for (pred, def) in pred_defs { + if instance.is_known(pred) { + continue; } - } - s.push_str(pref) ; - s.push_str(")\n") ; - if let Some(suff) = suff { - s.push_str(suff) ; - s.push_str("\n") - } - } - s.push_str(")\n") ; - log! { @4 "{}", s } - } + conf.check_timeout()?; + info += instance.rm_rhs_clauses_of(pred)?; + + if_log! { @5 + let mut s = format!("{}(", instance[pred]) ; + let mut is_first = true ; + for (var, typ) in instance[pred].sig.index_iter() { + if ! is_first { + s.push_str(", ") + } else { + is_first = false + } + s.push_str( & var.default_str() ) ; + s.push_str( & format!(": {}", typ) ) ; + } + s.push_str(") = (or\n") ; + + for & (ref qvars, ref conj) in & def { + + let (suff, pref) = if qvars.is_empty() { + (None, " ") + } else { + s.push_str(" (exists (") ; + for (var, typ) in qvars { + s.push_str(" (") ; + s.push_str( & var.default_str() ) ; + s.push_str( & format!(" {})", typ) ) + } + s.push_str(" )\n") ; + (Some(" )"), " ") + } ; + + s.push_str(pref) ; + s.push_str("(and\n") ; + for term in conj.terms() { + s.push_str( & format!("{} {}\n", pref, term) ) + } + for (pred, argss) in conj.preds() { + for args in argss { + s.push_str( + & format!("{} ({} {})\n", pref, instance[* pred], args) + ) + } + } + s.push_str(pref) ; + s.push_str(")\n") ; + if let Some(suff) = suff { + s.push_str(suff) ; + s.push_str("\n") + } + } + s.push_str(")\n") ; + + log! { @4 "{}", s } + } - log! { @4 " unfolding {}", instance[pred] } + log! { @4 " unfolding {}", instance[pred] } - info += instance.force_dnf_left(pred, def) ? ; + info += instance.force_dnf_left(pred, def)?; - preproc_dump!( + preproc_dump!( instance => format!("after_force_dnf_left_on_{}", pred), "Instance after reaching preproc fixed-point." - ) ? ; - } - - Ok(info) - } + )?; + } + Ok(info) + } } - - - impl RedStrat for CfgRed { - fn name(& self) -> & 'static str { "cfg_red" } - - fn new(instance: & Instance) -> Self { - CfgRed { - cnt: 0, - upper_bound: { - let clause_count = instance.clauses().len() ; - let adjusted = 50. * ( clause_count as f64 ).log(2.) ; - ::std::cmp::min( - clause_count, ( - adjusted - ).round() as usize - ) - }, - graph: Graph::new(instance), + fn name(&self) -> &'static str { + "cfg_red" } - } - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - // use std::time::Instant ; - // use common::profiling::DurationExt ; + fn new(instance: &Instance) -> Self { + CfgRed { + cnt: 0, + upper_bound: { + let clause_count = instance.clauses().len(); + let adjusted = 50. * (clause_count as f64).log(2.); + ::std::cmp::min(clause_count, (adjusted).round() as usize) + }, + graph: Graph::new(instance), + } + } - let mut total_info = RedInfo::new() ; + fn apply(&mut self, instance: &mut PreInstance) -> Res { + // use std::time::Instant ; + // use common::profiling::DurationExt ; - loop { + let mut total_info = RedInfo::new(); - let mut info = RedInfo::new() ; + loop { + let mut info = RedInfo::new(); - // let start = Instant::now() ; - self.graph.setup(instance) ; - // let setup_duration = Instant::now() - start ; - // println!("setup time: {}", setup_duration.to_str()) ; + // let start = Instant::now() ; + self.graph.setup(instance); + // let setup_duration = Instant::now() - start ; + // println!("setup time: {}", setup_duration.to_str()) ; - self.graph.check(& instance) ? ; + self.graph.check(&instance)?; - // let start = Instant::now() ; - let mut to_keep = self.graph.break_cycles(instance) ? ; - // let breaking_duration = Instant::now() - start ; - // println!("breaking time: {}", breaking_duration.to_str()) ; + // let start = Instant::now() ; + let mut to_keep = self.graph.break_cycles(instance)?; + // let breaking_duration = Instant::now() - start ; + // println!("breaking time: {}", breaking_duration.to_str()) ; - self.graph.to_dot( - & instance, format!("{}_pred_dep_b4", self.cnt), & to_keep - ) ? ; + self.graph + .to_dot(&instance, format!("{}_pred_dep_b4", self.cnt), &to_keep)?; - let pred_defs = self.graph.inline( - instance, & mut to_keep, self.upper_bound - ) ? ; + let pred_defs = self + .graph + .inline(instance, &mut to_keep, self.upper_bound)?; - if pred_defs.is_empty() { break } + if pred_defs.is_empty() { + break; + } - info.preds += pred_defs.len() ; + info.preds += pred_defs.len(); - self.graph.check(& instance) ? ; - if_log! { @verb - log! { @verb "inlining {} predicates", pred_defs.len() } - for (pred, _) in & pred_defs { - log! { @verb " {}", instance[* pred] } - } - } + self.graph.check(&instance)?; + if_log! { @verb + log! { @verb "inlining {} predicates", pred_defs.len() } + for (pred, _) in & pred_defs { + log! { @verb " {}", instance[* pred] } + } + } - if pred_defs.len() == instance.active_pred_count() { - let (is_sat, this_info) = instance.force_all_preds(pred_defs) ? ; - info += this_info ; - if ! is_sat { - unsat!("by preprocessing (all predicates resolved but unsat)") - } else { - total_info += info ; - break - } - } + if pred_defs.len() == instance.active_pred_count() { + let (is_sat, this_info) = instance.force_all_preds(pred_defs)?; + info += this_info; + if !is_sat { + unsat!("by preprocessing (all predicates resolved but unsat)") + } else { + total_info += info; + break; + } + } - info += self.apply_pred_defs(instance, pred_defs) ? ; + info += self.apply_pred_defs(instance, pred_defs)?; - if conf.preproc.dump_pred_dep { - self.graph.setup(instance) ; - self.graph.check(& instance) ? ; - self.graph.to_dot( - & instance, format!("{}_pred_dep_reduced", self.cnt), & to_keep - ) ? ; - } + if conf.preproc.dump_pred_dep { + self.graph.setup(instance); + self.graph.check(&instance)?; + self.graph.to_dot( + &instance, + format!("{}_pred_dep_reduced", self.cnt), + &to_keep, + )?; + } - self.cnt += 1 ; + self.cnt += 1; - if info.non_zero() { - total_info += info - } else { - break - } + if info.non_zero() { + total_info += info + } else { + break; + } + } + Ok(total_info) } - - Ok(total_info) - } -} \ No newline at end of file +} diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index ba894d2a..69407140 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -1,487 +1,460 @@ - //! Predicate-to-function reduction. - -use common::* ; -use instance::{ - instance::PreInstance, - info::VarInfo, - preproc::RedStrat, -} ; -use fun::RFun ; - -pub struct FunPreds ; +//! Predicate-to-function reduction. +use common::*; +use fun::RFun; +use instance::{info::VarInfo, instance::PreInstance, preproc::RedStrat}; +pub struct FunPreds; impl FunPreds { - /// Finalizes a definition for a predicate. - /// - /// Returns the final definition and some invariants. - pub fn finalize_definition( - instance: & mut PreInstance, - name: & str, sig: & VarInfos, typ: & Typ, - mut definitions: Vec<(TermSet, Term, bool)>, - check: bool - ) -> Res< Option<(Term, Option)> > { - instance.reset_solver() ? ; - - let invs = get_invariants(instance, name, sig, typ, & mut definitions) ? ; - - if_log! { @3 - if let Some(invs) = invs.as_ref() { - log! { @3 "found {} invariant(s)", invs.len() } - for inv in invs { - log! { @3 " {}", inv } + /// Finalizes a definition for a predicate. + /// + /// Returns the final definition and some invariants. + pub fn finalize_definition( + instance: &mut PreInstance, + name: &str, + sig: &VarInfos, + typ: &Typ, + mut definitions: Vec<(TermSet, Term, bool)>, + check: bool, + ) -> Res)>> { + instance.reset_solver()?; + + let invs = get_invariants(instance, name, sig, typ, &mut definitions)?; + + if_log! { @3 + if let Some(invs) = invs.as_ref() { + log! { @3 "found {} invariant(s)", invs.len() } + for inv in invs { + log! { @3 " {}", inv } + } + } else { + log! { @3 "none found" } + } } - } else { - log! { @3 "none found" } - } - } - if check { + if check { + log! { @3 "checking conditions are mutually exclusive" } + let solver = instance.solver(); - log! { @3 "checking conditions are mutually exclusive" } - let solver = instance.solver() ; - - for info in sig { - solver.declare_const(& info.idx, info.typ.get()) ? - } + for info in sig { + solver.declare_const(&info.idx, info.typ.get())? + } - solver.declare_fun(name, sig, typ.get()) ? ; + solver.declare_fun(name, sig, typ.get())?; - let mut actlits = Vec::with_capacity( definitions.len() ) ; + let mut actlits = Vec::with_capacity(definitions.len()); - for (cube, _, _) in & definitions { - use smt::TermConj ; - let conj = TermConj::new( cube.iter() ) ; - let actlit = solver.get_actlit() ? ; - solver.assert_act_with( & actlit, & conj, true ) ? ; - actlits.push(actlit) - } + for (cube, _, _) in &definitions { + use smt::TermConj; + let conj = TermConj::new(cube.iter()); + let actlit = solver.get_actlit()?; + solver.assert_act_with(&actlit, &conj, true)?; + actlits.push(actlit) + } - scoped! { - let mut actlits_iter = actlits.iter() ; + scoped! { + let mut actlits_iter = actlits.iter() ; - while let Some(actlit) = actlits_iter.next() { - for other in actlits_iter.clone() { - let not_exclusive = solver.check_sat_act( - vec![ actlit, other ] - ) ? ; - if not_exclusive { - return Ok(None) + while let Some(actlit) = actlits_iter.next() { + for other in actlits_iter.clone() { + let not_exclusive = solver.check_sat_act( + vec![ actlit, other ] + ) ? ; + if not_exclusive { + return Ok(None) + } + } + } } - } - } - } - for actlit in actlits { - solver.de_actlit(actlit) ? - } + for actlit in actlits { + solver.de_actlit(actlit)? + } - log! { @3 "all branches are exclusive, checking they're exhaustive" } + log! { @3 "all branches are exclusive, checking they're exhaustive" } - for (cube, _, _) in & definitions { - use smt::TermConj ; - let conj = TermConj::new( cube.iter() ) ; - solver.assert_with( & conj, false ) ? ; - } + for (cube, _, _) in &definitions { + use smt::TermConj; + let conj = TermConj::new(cube.iter()); + solver.assert_with(&conj, false)?; + } - let not_exhaustive = solver.check_sat() ? ; + let not_exhaustive = solver.check_sat()?; - if not_exhaustive { - // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; - log! { @3 "branches are not exhaustive, aborting" } - return Ok(None) - } else { - log! { @3 "branches are exhaustive, building definition" } - } + if not_exhaustive { + // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; + log! { @3 "branches are not exhaustive, aborting" } + return Ok(None); + } else { + log! { @3 "branches are exhaustive, building definition" } + } + } - } + let mut def = None; - let mut def = None ; - - for (cube, value, _) in definitions.into_iter().rev() { - let nu_def = if let Some(def) = def { - term::ite( - term::and( cube.into_iter().collect() ), - value, - def - ) - } else { - value - } ; - def = Some(nu_def) - } + for (cube, value, _) in definitions.into_iter().rev() { + let nu_def = if let Some(def) = def { + term::ite(term::and(cube.into_iter().collect()), value, def) + } else { + value + }; + def = Some(nu_def) + } - if let Some(def) = def { - Ok( Some( (def, invs) ) ) - } else { - bail!("empty definitions during finalization in fun_preds") + if let Some(def) = def { + Ok(Some((def, invs))) + } else { + bail!("empty definitions during finalization in fun_preds") + } } - } - - /// Reduces a predicate to a function. - pub fn reduce_pred( - instance: & mut PreInstance, pred: PrdIdx, use_all_args: bool - ) -> Res< Option > { - let mut info = RedInfo::new() ; - // Clauses to remove. - let mut to_rm = vec![] ; + /// Reduces a predicate to a function. + pub fn reduce_pred( + instance: &mut PreInstance, + pred: PrdIdx, + use_all_args: bool, + ) -> Res> { + let mut info = RedInfo::new(); + // Clauses to remove. + let mut to_rm = vec![]; - log!(@2 "working on {}", conf.emph(& instance[pred].name)) ; + log!(@2 "working on {}", conf.emph(& instance[pred].name)); - debug_assert! { to_rm.is_empty() } + debug_assert! { to_rm.is_empty() } - let mut var_infos = VarInfos::new() ; + let mut var_infos = VarInfos::new(); - let args_len = if use_all_args { - instance[pred].sig.len() - } else { - instance[pred].sig.len() - 1 - } ; - - for typ in & instance[pred].sig[ 0 .. args_len ] { - let idx = var_infos.next_index() ; - let var_info = VarInfo::new( - idx.default_str(), typ.clone(), idx - ) ; - var_infos.push(var_info) - } - let last: Option = if use_all_args { - None - } else { - Some( ( instance[pred].sig.len() - 1 ).into() ) - } ; - - let pred_fun_name = make_fun_name(& instance[pred].name).chain_err( - || format!( - "while creating function for predicate {}", - conf.emph(& instance[pred].name) - ) - ) ? ; - let pred_fun_typ = if let Some(last) = last.as_ref() { - instance[pred].sig[* last].clone() - } else { - typ::bool() - } ; - - let mut rfun = RFun::new( - pred_fun_name.clone(), var_infos, pred_fun_typ.clone() - ) ; - rfun.set_synthetic(pred) ; - - macro_rules! abort { - ($($stuff:tt)*) => ({ - let _ = fun::retrieve_dec(& pred_fun_name) ; - return Ok(None) - }) ; - } + let args_len = if use_all_args { + instance[pred].sig.len() + } else { + instance[pred].sig.len() - 1 + }; - fun::register_dec(rfun) ? ; + for typ in &instance[pred].sig[0..args_len] { + let idx = var_infos.next_index(); + let var_info = VarInfo::new(idx.default_str(), typ.clone(), idx); + var_infos.push(var_info) + } + let last: Option = if use_all_args { + None + } else { + Some((instance[pred].sig.len() - 1).into()) + }; - let mut definitions = vec![] ; + let pred_fun_name = make_fun_name(&instance[pred].name).chain_err(|| { + format!( + "while creating function for predicate {}", + conf.emph(&instance[pred].name) + ) + })?; + let pred_fun_typ = if let Some(last) = last.as_ref() { + instance[pred].sig[*last].clone() + } else { + typ::bool() + }; - for clause in instance.clauses_of(pred).1 { - to_rm.push(* clause) ; + let mut rfun = RFun::new(pred_fun_name.clone(), var_infos, pred_fun_typ.clone()); + rfun.set_synthetic(pred); - if_log! { @3 - log!(@3 "working on clause #{}", clause) ; - log!(@4 "lhs terms:") ; - for term in instance[* clause].lhs_terms() { - log!(@4 " {}", term) - } - log!(@4 "lhs preds:") ; - for (pred, argss) in instance[* clause].lhs_preds() { - for args in argss { - log!(@4 " ({} {})", instance[* pred], args) - } - } - } - let clause = * clause ; - let (_, rhs_args) = instance[clause].rhs().unwrap() ; - log!(@4 "rhs args:\n ({} {})", instance[pred], rhs_args) ; - - let ( - mut cube, mut subst - ) = if let Some((cube, subst)) = args_invert(rhs_args, args_len) ? { - (cube, subst) - } else { - log!(@3 "failed to invert rhs arguments") ; - abort!() - } ; - - if_log! { @4 - log!(@4 "substitution:") ; - for (var, term) in subst.iter() { - log!(@4 " v_{} -> {}", var, term) + macro_rules! abort { + ($($stuff:tt)*) => {{ + let _ = fun::retrieve_dec(&pred_fun_name); + return Ok(None); + }}; } - log!(@4 "cube:") ; - for term in cube.iter() { - log!(@4 " {}", term) - } - } - let recursive = instance[clause].lhs_preds().contains_key(& pred) ; + fun::register_dec(rfun)?; - let mut lhs_argss: Vec<_> = instance[ - clause - ].lhs_preds().get(& pred).map( - |argss| argss.iter().collect() - ).unwrap_or_else( Vec::new ) ; + let mut definitions = vec![]; - let mut nu_args = Vec::with_capacity( - args_len - ) ; + for clause in instance.clauses_of(pred).1 { + to_rm.push(*clause); - let mut value = None ; - - while ! lhs_argss.is_empty() { - let prev_len = lhs_argss.len() ; - let mut failed: Res<_> = Ok(false) ; + if_log! { @3 + log!(@3 "working on clause #{}", clause) ; + log!(@4 "lhs terms:") ; + for term in instance[* clause].lhs_terms() { + log!(@4 " {}", term) + } + log!(@4 "lhs preds:") ; + for (pred, argss) in instance[* clause].lhs_preds() { + for args in argss { + log!(@4 " ({} {})", instance[* pred], args) + } + } + } + let clause = *clause; + let (_, rhs_args) = instance[clause].rhs().unwrap(); + log!(@4 "rhs args:\n ({} {})", instance[pred], rhs_args); - lhs_argss.retain( - |args| { - for arg in & args[ 0 .. args_len ] { - if let Some((arg, _)) = arg.subst_total( & subst ) { - nu_args.push(arg) - } else { - nu_args.clear() ; - return true + let (mut cube, mut subst) = + if let Some((cube, subst)) = args_invert(rhs_args, args_len)? { + (cube, subst) + } else { + log!(@3 "failed to invert rhs arguments"); + abort!() + }; + + if_log! { @4 + log!(@4 "substitution:") ; + for (var, term) in subst.iter() { + log!(@4 " v_{} -> {}", var, term) + } + log!(@4 "cube:") ; + for term in cube.iter() { + log!(@4 " {}", term) } } - let nu_args = ::std::mem::replace( - & mut nu_args, Vec::with_capacity( - instance[pred].sig.len() - 1 - ) - ) ; - - let fun_app = term::fun( - pred_fun_typ.clone(), pred_fun_name.clone(), nu_args - ) ; - - let okay = if let Some(last) = last.as_ref() { - let last = * last ; - - map_invert( - & args[ last ], fun_app, & mut subst, & mut cube - ) - } else { - value.get_or_insert_with(Vec::new).push(fun_app) ; - Ok(true) - } ; - - match okay { - // Success, don't retain. - Ok(true) => false, - // Failure, retain. - Ok(false) => { - log!(@3 + + let recursive = instance[clause].lhs_preds().contains_key(&pred); + + let mut lhs_argss: Vec<_> = instance[clause] + .lhs_preds() + .get(&pred) + .map(|argss| argss.iter().collect()) + .unwrap_or_else(Vec::new); + + let mut nu_args = Vec::with_capacity(args_len); + + let mut value = None; + + while !lhs_argss.is_empty() { + let prev_len = lhs_argss.len(); + let mut failed: Res<_> = Ok(false); + + lhs_argss.retain(|args| { + for arg in &args[0..args_len] { + if let Some((arg, _)) = arg.subst_total(&subst) { + nu_args.push(arg) + } else { + nu_args.clear(); + return true; + } + } + let nu_args = ::std::mem::replace( + &mut nu_args, + Vec::with_capacity(instance[pred].sig.len() - 1), + ); + + let fun_app = term::fun(pred_fun_typ.clone(), pred_fun_name.clone(), nu_args); + + let okay = if let Some(last) = last.as_ref() { + let last = *last; + + map_invert(&args[last], fun_app, &mut subst, &mut cube) + } else { + value.get_or_insert_with(Vec::new).push(fun_app); + Ok(true) + }; + + match okay { + // Success, don't retain. + Ok(true) => false, + // Failure, retain. + Ok(false) => { + log!(@3 "could not invert last argument ({:?}) of ({} {})", last, instance[pred], args - ) ; - if let Ok(failed) = failed.as_mut() { - * failed = true + ); + if let Ok(failed) = failed.as_mut() { + *failed = true + } + true + } + // Error, do whatever. + err => { + failed = err.chain_err(|| { + format!("while inverting ({} {})", instance[pred], args) + }); + true + } + } + }); + + if failed? { + log!(@3 "failed"); + abort!() + } else if lhs_argss.len() == prev_len { + // not making progress. + log!(@3 "not making progress on lhs preds"); + abort!() } - true - }, - // Error, do whatever. - err => { - failed = err.chain_err( - || format!("while inverting ({} {})", instance[pred], args) - ) ; - true - }, } - } - ) ; - - if failed ? { - log!(@3 "failed") ; - abort!() - } else if lhs_argss.len() == prev_len { - // not making progress. - log!(@3 "not making progress on lhs preds") ; - abort!() - } - } - if_log! { @4 - log!(@4 "subst after lhs preds:") ; - for (var, term) in & subst { - log!(@4 " v_{} -> {}", var, term) - } - } + if_log! { @4 + log!(@4 "subst after lhs preds:") ; + for (var, term) in & subst { + log!(@4 " v_{} -> {}", var, term) + } + } - for term in instance[clause].lhs_terms() { - if let Some((term, _)) = term.subst_total( & subst ) { - cube.insert(term) ; - } else { - log!(@3 "total substitution on term {} failed", term) ; - abort!() - } - } + for term in instance[clause].lhs_terms() { + if let Some((term, _)) = term.subst_total(&subst) { + cube.insert(term); + } else { + log!(@3 "total substitution on term {} failed", term); + abort!() + } + } - if_log! { @4 - log!(@4 "cube:") ; - for term in & cube { - log!(@4 " {}", term) - } - } + if_log! { @4 + log!(@4 "cube:") ; + for term in & cube { + log!(@4 " {}", term) + } + } - let res = if let Some(last) = last.as_ref() { - if let Some((res, _)) = rhs_args[* last].subst_total(& subst) { - res - } else { - log!(@3 "failed to retrieve value, aborting") ; - abort!() - } - } else if let Some(conj) = value { - term::and(conj) - } else { - term::tru() - } ; + let res = if let Some(last) = last.as_ref() { + if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { + res + } else { + log!(@3 "failed to retrieve value, aborting"); + abort!() + } + } else if let Some(conj) = value { + term::and(conj) + } else { + term::tru() + }; - log!(@4 "value: {}", res) ; + log!(@4 "value: {}", res); - definitions.push( (cube, res, recursive) ) - } + definitions.push((cube, res, recursive)) + } - definitions.sort_by( - |(c_1, _, rec_1), (c_2, _, rec_2)| { - use std::cmp::Ordering::* ; - if * rec_1 && ! * rec_2 { - Less - } else if * rec_2 && ! * rec_1 { - Greater - } else { - c_1.len().cmp( & c_2.len() ) + definitions.sort_by(|(c_1, _, rec_1), (c_2, _, rec_2)| { + use std::cmp::Ordering::*; + if *rec_1 && !*rec_2 { + Less + } else if *rec_2 && !*rec_1 { + Greater + } else { + c_1.len().cmp(&c_2.len()) + } + }); + + if use_all_args { + let mut tru = TermSet::new(); + tru.insert(term::tru()); + definitions.push((tru, term::fls(), false)) } - } - ) ; - - if use_all_args { - let mut tru = TermSet::new() ; - tru.insert( term::tru() ) ; - definitions.push( - (tru, term::fls(), false) - ) - } - log!(@3 "done working on {}", instance[pred]) ; - if_log! { @4 - for (cube, res, recursive) in & definitions { - log!(@4 "when {{") ; - for term in cube { - log!(@4 " {}", term) + log!(@3 "done working on {}", instance[pred]); + if_log! { @4 + for (cube, res, recursive) in & definitions { + log!(@4 "when {{") ; + for term in cube { + log!(@4 " {}", term) + } + log!(@4 "}} -{}> {}", if * recursive { "rec-" } else { "" }, res) + } } - log!(@4 "}} -{}> {}", if * recursive { "rec-" } else { "" }, res) - } - } - // let mut def = pred_fun_typ.default_term() ; + // let mut def = pred_fun_typ.default_term() ; + + // for (cube, res) in definitions.into_iter().rev() { + // let cond = term::and( + // cube.into_iter().collect() + // ) ; + // def = term::ite( cond, res, def ) + // } + + let mut dec = fun::retrieve_dec(&pred_fun_name)?; + + let (def, invs) = if let Some(def) = FunPreds::finalize_definition( + instance, + &pred_fun_name, + &dec.sig, + &pred_fun_typ, + definitions, + !use_all_args, + )? { + def + } else { + log!(@3 "failed to finalize definition, aborting"); + abort!() + }; - // for (cube, res) in definitions.into_iter().rev() { - // let cond = term::and( - // cube.into_iter().collect() - // ) ; - // def = term::ite( cond, res, def ) - // } + if let Some(invs) = invs { + for inv in invs { + dec.invariants.insert(inv); + } + } - let mut dec = fun::retrieve_dec(& pred_fun_name) ? ; + instance.reset_solver()?; - let (def, invs) = if let Some(def) = FunPreds::finalize_definition( - instance, & pred_fun_name, & dec.sig, & pred_fun_typ, definitions, - ! use_all_args - ) ? { - def - } else { - log!(@3 "failed to finalize definition, aborting") ; - abort!() - } ; + log!(@3 "definition:\n {}", def); - if let Some(invs) = invs { - for inv in invs { - dec.invariants.insert(inv) ; - } - } + log!(@4 "registering..."); + dec.set_def(def); - instance.reset_solver() ? ; + let fun = fun::mk(dec).chain_err(|| { + format!( + "while creating internal function for predicate {}", + conf.bad(&instance[pred].name) + ) + })?; - log!(@3 "definition:\n {}", def) ; + instance.add_companion_fun(pred, fun.clone()); - log!(@4 "registering...") ; - dec.set_def( def ) ; + // Restarting solver so that the function is declared. + instance.reset_solver()?; - let fun = fun::mk(dec).chain_err( - || format!( - "while creating internal function for predicate {}", - conf.bad(& instance[pred].name) - ) - ) ? ; + info.clauses_rmed += to_rm.len(); + instance.forget_clauses(&mut to_rm)?; - instance.add_companion_fun( pred, fun.clone() ) ; + let mut args = Vec::with_capacity(args_len); + for (var, typ) in instance[pred].sig.index_iter().take(args_len) { + args.push(term::var(var, typ.clone())) + } + let fun_app = term::fun(fun.typ.clone(), fun.name.clone(), args); - // Restarting solver so that the function is declared. - instance.reset_solver() ? ; + let def = if let Some(last) = last.as_ref() { + term::eq(term::var(*last, fun.typ.clone()), fun_app) + } else { + fun_app + }; - info.clauses_rmed += to_rm.len() ; - instance.forget_clauses(& mut to_rm) ? ; + info.preds += 1; + let mut tterm_set = TTermSet::new(); + tterm_set.insert_term(def); + info += instance.force_dnf_left(pred, vec![(Quantfed::new(), tterm_set)])?; - let mut args = Vec::with_capacity( args_len ) ; - for (var, typ) in instance[pred].sig.index_iter().take( - args_len - ) { - args.push( term::var(var, typ.clone()) ) + Ok(Some(info)) } - let fun_app = term::fun( - fun.typ.clone(), fun.name.clone(), args - ) ; - - let def = if let Some(last) = last.as_ref() { - term::eq( - term::var( * last, fun.typ.clone() ), - fun_app - ) - } else { - fun_app - } ; - - info.preds += 1 ; - let mut tterm_set = TTermSet::new() ; - tterm_set.insert_term(def) ; - info += instance.force_dnf_left( - pred, vec![ ( Quantfed::new(), tterm_set ) ] - ) ? ; - - Ok( Some(info) ) - } } impl RedStrat for FunPreds { - fn name(& self) -> & 'static str { "fun_preds" } + fn name(&self) -> &'static str { + "fun_preds" + } - fn new(_: & Instance) -> Self { FunPreds } + fn new(_: &Instance) -> Self { + FunPreds + } - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); - let mut new_stuff = true ; + let mut new_stuff = true; - while new_stuff { - new_stuff = false ; + while new_stuff { + new_stuff = false; - if instance.active_pred_count() <= 1 { - return Ok(info) - } + if instance.active_pred_count() <= 1 { + return Ok(info); + } - let mut to_inline: Vec<_> = instance.preds().iter().filter_map( |info| { - let inline = { - let pred = info.idx ; + let mut to_inline: Vec<_> = instance + .preds() + .iter() + .filter_map(|info| { + let inline = { + let pred = info.idx; - // Predicate is still unknown. - ! instance.is_known(pred) + // Predicate is still unknown. + ! instance.is_known(pred) // When `pred` appears in the rhs of a clause, the lhs only mentions // `pred`. @@ -509,524 +482,467 @@ impl RedStrat for FunPreds { && info.sig[ 0 .. info.sig.len() - 1 ].iter().fold( 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } ) <= 1 + }; + + if inline { + Some(info.idx) + } else { + None + } + }).collect(); + + to_inline.sort_by(|p_1, p_2| { + let adt_count_1 = + instance[*p_1] + .sig + .iter() + .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); + let adt_count_2 = + instance[*p_2] + .sig + .iter() + .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); + + adt_count_1.cmp(&adt_count_2).reverse() + }); + + if to_inline.is_empty() { + return Ok(info); + } - } ; - - if inline { - Some(info.idx) - } else { - None + while let Some(pred) = to_inline.pop() { + let res = FunPreds::reduce_pred(instance, pred, false)?; + // pause("to resume fun_preds", & Profiler::new()) ; + if let Some(red_info) = res { + new_stuff = true; + info += red_info; + break; + } else { + // let res = FunPreds::reduce_pred(instance, pred, true) ? ; + // // pause("to resume fun_preds", & Profiler::new()) ; + // if let Some(red_info) = res { + // new_stuff = true ; + // info += red_info ; + // break + // } + () + } + } } - } ).collect() ; - to_inline.sort_by( - |p_1, p_2| { - let adt_count_1 = instance[* p_1].sig.iter().fold( - 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } - ) ; - let adt_count_2 = instance[* p_2].sig.iter().fold( - 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } - ) ; + Ok(info) + } +} - adt_count_1.cmp(& adt_count_2).reverse() - } - ) ; +/// Builds a cube and a substitution corresponding to inverting some arguments. +pub fn args_invert(args: &VarTerms, args_len: usize) -> Res)>> { + let (mut cube, mut subst) = (TermSet::new(), VarHMap::new()); - if to_inline.is_empty() { - return Ok(info) - } + debug_assert! { args.len() >= 1 } - while let Some(pred) = to_inline.pop() { - let res = FunPreds::reduce_pred(instance, pred, false) ? ; - // pause("to resume fun_preds", & Profiler::new()) ; - if let Some(red_info) = res { - new_stuff = true ; - info += red_info ; - break - } else { - // let res = FunPreds::reduce_pred(instance, pred, true) ? ; - // // pause("to resume fun_preds", & Profiler::new()) ; - // if let Some(red_info) = res { - // new_stuff = true ; - // info += red_info ; - // break - // } - () - } - } + let mut postponed = Vec::new(); + let mut current: Vec<_> = args + .index_iter() + .take(args_len) + .map(|(var, term)| (term::var(var, term.typ()), term)) + .collect(); - } - - Ok(info) - } -} + let mut did_something; + while !current.is_empty() { + debug_assert! { postponed.is_empty() } + did_something = false; + for (var, term) in current.drain(0..) { + log! { @4 "attempting to invert {} | {}", var, term } + if_log! { @5 + log! { @5 "subst:" } + for (var, term) in & subst { + log! { @5 " {}: {}", var.default_str(), term } + } + log! { @5 "cube:" } + for term in & cube { + log! { @5 " {}", term } + } + } + let worked = map_invert(term, var.clone(), &mut subst, &mut cube)?; -/// Builds a cube and a substitution corresponding to inverting some arguments. -pub fn args_invert( - args: & VarTerms, args_len: usize -) -> Res< Option<(TermSet, VarHMap)> > { - let (mut cube, mut subst) = ( - TermSet::new(), VarHMap::new() - ) ; - - debug_assert! { args.len() >= 1 } - - let mut postponed = Vec::new() ; - let mut current: Vec<_> = args.index_iter().take( - args_len - ).map( - |(var, term)| ( term::var(var, term.typ()), term ) - ).collect() ; - - let mut did_something ; - - while ! current.is_empty() { - debug_assert! { postponed.is_empty() } - - did_something = false ; - - for (var, term) in current.drain( 0 .. ) { - log! { @4 "attempting to invert {} | {}", var, term } - if_log! { @5 - log! { @5 "subst:" } - for (var, term) in & subst { - log! { @5 " {}: {}", var.default_str(), term } - } - log! { @5 "cube:" } - for term in & cube { - log! { @5 " {}", term } + if worked { + log! { @4 "success :)" } + did_something = true; + } else { + log! { @4 "failed :(" } + postponed.push((var, term)) + } } - } - let worked = map_invert( - term, var.clone(), & mut subst, & mut cube - ) ? ; - - if worked { - log! { @4 "success :)" } - did_something = true ; - } else { - log! { @4 "failed :(" } - postponed.push((var, term)) - } - } - - // Making progress? - if ! did_something { - return Ok(None) - } else { - ::std::mem::swap( & mut postponed, & mut current ) + // Making progress? + if !did_something { + return Ok(None); + } else { + ::std::mem::swap(&mut postponed, &mut current) + } } - } - Ok( Some((cube, subst)) ) + Ok(Some((cube, subst))) } - - - - - /// Inverts a term to complete a substitution and a cube. /// /// Returns false if the invertion failed. pub fn map_invert( - to_invert: & Term, - term: Term, subst: & mut VarHMap, cube: & mut TermSet + to_invert: &Term, + term: Term, + subst: &mut VarHMap, + cube: &mut TermSet, ) -> Res { - debug_assert_eq! { to_invert.typ(), term.typ() } + debug_assert_eq! { to_invert.typ(), term.typ() } - let mut nu_cube = vec![] ; - let mut nu_subst = VarHMap::::new() ; - - let mut stack = vec![ ( term, to_invert.get() ) ] ; - - while let Some( (term, to_invert) ) = stack.pop() { - - match to_invert { - - RTerm::DTypNew { typ, name, args } => { - let selectors = typ.selectors_of(name) ? ; - debug_assert_eq! { args.len(), selectors.len() } - - // `term` must be a specific variant: `name` - nu_cube.push( - term::dtyp_tst( name.clone(), term.clone() ) - ) ; - - for (arg, (slc, _)) in args.iter().zip( selectors.iter() ) { - stack.push(( - term::dtyp_slc( arg.typ(), slc.clone(), term.clone() ), - arg - )) - } + let mut nu_cube = vec![]; + let mut nu_subst = VarHMap::::new(); - }, - - RTerm::Cst(val) => { - nu_cube.push( term::eq( term, term::val( val.clone() ) ) ) ; - continue - }, - - RTerm::Var(_, var) => { - if let Some(t) = subst.get(var) { - nu_cube.push( - term::eq( term, t.clone() ) - ) ; - continue - } else if let Some(t) = nu_subst.get(var) { - nu_cube.push( - term::eq( term, t.clone() ) - ) ; - continue - } + let mut stack = vec![(term, to_invert.get())]; - nu_subst.insert(* var, term) ; - }, + while let Some((term, to_invert)) = stack.pop() { + match to_invert { + RTerm::DTypNew { typ, name, args } => { + let selectors = typ.selectors_of(name)?; + debug_assert_eq! { args.len(), selectors.len() } - // Constant array. - RTerm::CArray { term: inner, typ } => { - stack.push(( - // Array is constant, select any value. - term::select( term, typ.default_term() ), - inner.get() - )) - }, + // `term` must be a specific variant: `name` + nu_cube.push(term::dtyp_tst(name.clone(), term.clone())); - RTerm::App { typ, op, args } => { + for (arg, (slc, _)) in args.iter().zip(selectors.iter()) { + stack.push((term::dtyp_slc(arg.typ(), slc.clone(), term.clone()), arg)) + } + } + RTerm::Cst(val) => { + nu_cube.push(term::eq(term, term::val(val.clone()))); + continue; + } + RTerm::Var(_, var) => { + if let Some(t) = subst.get(var) { + nu_cube.push(term::eq(term, t.clone())); + continue; + } else if let Some(t) = nu_subst.get(var) { + nu_cube.push(term::eq(term, t.clone())); + continue; + } - match op { + nu_subst.insert(*var, term); + } - Op::CMul => if args[0].val().is_some() { - let nu_term = if typ.is_int() { - // Current term modulo `val` is zero. - nu_cube.push( - term::eq( - term::modulo( term.clone(), args[0].clone() ), - term::int(0) - ) - ) ; - term::idiv( vec![term, args[0].clone()] ) - } else if typ.is_real() { - term::div( vec![term, args[0].clone()] ) - } else { - bail!("unexpected multiplication over type {}", typ) - } ; - stack.push( - ( nu_term, args[1].get() ) - ) ; - continue - } else { - bail!("illegal CMul term {}", to_invert) - }, - - Op::Add => { - let mut subtraction = vec![ term ] ; - let mut not_substed = None ; - - for arg in args { - if let Some((term, _)) = arg.subst_total( - & (& * subst, & nu_subst) - ) { - subtraction.push(term) - } else if not_substed.is_some() { - return Ok(false) - } else { - not_substed = Some( arg.get() ) - } + // Constant array. + RTerm::CArray { term: inner, typ } => { + stack.push(( + // Array is constant, select any value. + term::select(term, typ.default_term()), + inner.get(), + )) } - let nu_term = term::sub(subtraction) ; + RTerm::App { typ, op, args } => { + match op { + Op::CMul => if args[0].val().is_some() { + let nu_term = if typ.is_int() { + // Current term modulo `val` is zero. + nu_cube.push(term::eq( + term::modulo(term.clone(), args[0].clone()), + term::int(0), + )); + term::idiv(vec![term, args[0].clone()]) + } else if typ.is_real() { + term::div(vec![term, args[0].clone()]) + } else { + bail!("unexpected multiplication over type {}", typ) + }; + stack.push((nu_term, args[1].get())); + continue; + } else { + bail!("illegal CMul term {}", to_invert) + }, + + Op::Add => { + let mut subtraction = vec![term]; + let mut not_substed = None; + + for arg in args { + if let Some((term, _)) = arg.subst_total(&(&*subst, &nu_subst)) { + subtraction.push(term) + } else if not_substed.is_some() { + return Ok(false); + } else { + not_substed = Some(arg.get()) + } + } + + let nu_term = term::sub(subtraction); + + if let Some(nu_to_invert) = not_substed { + stack.push((nu_term, nu_to_invert)) + } else { + nu_cube.push(term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + }, + )); + } + continue; + } + + Op::Sub => { + let mut sub = None; + let mut add = vec![term]; + let mut not_substed = None; + + let mut first = true; + for arg in args { + if let Some((term, _)) = arg.subst_total(&(&*subst, &nu_subst)) { + if first { + debug_assert! { sub.is_none() } + sub = Some(term) + } else { + add.push(term) + } + } else if not_substed.is_some() { + return Ok(false); + } else { + // Careful of the polarity here vvvvv + not_substed = Some((arg.get(), first)) + } + + first = false + } + + let nu_term = term::add(add); + let nu_term = if let Some(sub) = sub { + term::sub(vec![nu_term, sub]) + } else { + nu_term + }; + + if let Some((nu_to_invert, positive)) = not_substed { + stack.push(( + if positive { + nu_term + } else { + term::u_minus(nu_term) + }, + nu_to_invert, + )) + } else { + nu_cube.push(term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + }, + )); + } + + continue; + } + + _ => (), + } - if let Some(nu_to_invert) = not_substed { - stack.push( (nu_term, nu_to_invert) ) - } else { - nu_cube.push( - term::eq( - nu_term, - if typ.is_int() { - term::int_zero() - } else if typ.is_real() { - term::real_zero() - } else { - bail!("unexpected addition over type {}", typ) - } - ) - ) ; - } - continue - }, - - Op::Sub => { - let mut sub = None ; - let mut add = vec![ term ] ; - let mut not_substed = None ; - - let mut first = true ; - for arg in args { - if let Some((term, _)) = arg.subst_total( - & (& * subst, & nu_subst) - ) { - if first { - debug_assert! { sub.is_none() } - sub = Some(term) - } else { - add.push(term) + let mut nu_args = Vec::with_capacity(args.len()); + for arg in args { + if let Some((arg, _)) = arg.subst_total(&(&*subst, &nu_subst)) { + nu_args.push(arg) + } else { + return Ok(false); + } } - } else if not_substed.is_some() { - return Ok(false) - } else { - // Careful of the polarity here vvvvv - not_substed = Some( (arg.get(), first) ) - } - first = false + nu_cube.push(term::eq(term, term::app(*op, nu_args))) } - let nu_term = term::add(add) ; - let nu_term = if let Some(sub) = sub { - term::sub( vec![ nu_term, sub ] ) - } else { - nu_term - } ; - - if let Some((nu_to_invert, positive)) = not_substed { - stack.push( - ( - if positive { nu_term } else { term::u_minus(nu_term) }, - nu_to_invert - ) - ) + RTerm::DTypSlc { + typ, + name, + term: inner, + } => if let Some((inner, _)) = inner.subst_total(&(&*subst, &nu_subst)) { + nu_cube.push(term::eq( + term, + term::dtyp_slc(typ.clone(), name.clone(), inner), + )) } else { - nu_cube.push( - term::eq( - nu_term, - if typ.is_int() { - term::int_zero() - } else if typ.is_real() { - term::real_zero() - } else { - bail!("unexpected addition over type {}", typ) - } - ) - ) ; - } - - continue - }, - - _ => (), - - } + return Ok(false); + }, - let mut nu_args = Vec::with_capacity( args.len() ) ; - for arg in args { - if let Some((arg, _)) = arg.subst_total( - & (& * subst, & nu_subst) - ) { - nu_args.push(arg) - } else { - return Ok(false) - } - } + RTerm::DTypTst { + name, term: inner, .. + } => if let Some((inner, _)) = inner.subst_total(&(&*subst, &nu_subst)) { + nu_cube.push(term::eq(term, term::dtyp_tst(name.clone(), inner))) + } else { + return Ok(false); + }, + + RTerm::Fun { typ, name, args } => { + let mut nu_args = Vec::with_capacity(args.len()); + for arg in args { + if let Some((arg, _)) = arg.subst_total(&(&*subst, &nu_subst)) { + nu_args.push(arg) + } else { + return Ok(false); + } + } - nu_cube.push( - term::eq( - term, - term::app(* op, nu_args) - ) - ) - }, - - RTerm::DTypSlc { typ, name, term: inner } => - if let Some((inner, _)) = inner.subst_total( - & (& * subst, & nu_subst) - ) { - nu_cube.push( - term::eq( - term, term::dtyp_slc( typ.clone(), name.clone(), inner ) - ) - ) - } else { - return Ok(false) - }, - - RTerm::DTypTst { name, term: inner, .. } => - if let Some((inner, _)) = inner.subst_total( - & (& * subst, & nu_subst) - ) { - nu_cube.push( - term::eq( - term, term::dtyp_tst( name.clone(), inner ) - ) - ) - } else { - return Ok(false) - }, - - RTerm::Fun { typ, name, args } => { - let mut nu_args = Vec::with_capacity( args.len() ) ; - for arg in args { - if let Some((arg, _)) = arg.subst_total( - & (& * subst, & nu_subst) - ) { - nu_args.push(arg) - } else { - return Ok(false) - } + nu_cube.push(term::eq( + term, + term::fun(typ.clone(), name.clone(), nu_args), + )) + } } - - nu_cube.push( - term::eq( - term, term::fun( typ.clone(), name.clone(), nu_args ) - ) - ) - } - } - } - - for term in nu_cube { - cube.insert( term ) ; - } - for (var, term) in nu_subst { - let prev = subst.insert(var, term) ; - debug_assert! { prev.is_none() } - } + for term in nu_cube { + cube.insert(term); + } + for (var, term) in nu_subst { + let prev = subst.insert(var, term); + debug_assert! { prev.is_none() } + } - Ok(true) + Ok(true) } - /// Extracts invariants from a function pre-definition. /// /// Assumes the solver has nothing declared / asserted. fn get_invariants( - instance: & mut PreInstance, - name: & str, sig: & VarInfos, typ: & Typ, - definitions: & mut Vec< (TermSet, Term, bool) >, -) -> Res< Option > { - let mut candidates = TermMap::new() ; - - log! { @3 "looking for invariants..." } - - for (idx, (cube, _, _)) in definitions.iter_mut().enumerate() { - cube.retain( - |term| { - let mut applications = None ; - - term.iter( - |sub| if let Some((fun, _)) = sub.fun_inspect() { - if fun == name { - applications.get_or_insert_with( - TermSet::new - ).insert( sub.to_hcons() ) ; + instance: &mut PreInstance, + name: &str, + sig: &VarInfos, + typ: &Typ, + definitions: &mut Vec<(TermSet, Term, bool)>, +) -> Res> { + let mut candidates = TermMap::new(); + + log! { @3 "looking for invariants..." } + + for (idx, (cube, _, _)) in definitions.iter_mut().enumerate() { + cube.retain(|term| { + let mut applications = None; + + term.iter(|sub| { + if let Some((fun, _)) = sub.fun_inspect() { + if fun == name { + applications + .get_or_insert_with(TermSet::new) + .insert(sub.to_hcons()); + } + } + }); + + if let Some(applications) = applications { + candidates + .entry(term.clone()) + .or_insert_with(|| (Vec::new(), applications)) + .0 + .push(idx); + false + } else { + true } - } - ) ; - - if let Some(applications) = applications { - candidates.entry( term.clone() ).or_insert_with( - || (Vec::new(), applications) - ).0.push(idx) ; - false - } else { - true - } - } - ) - } + }) + } - if candidates.is_empty() { - return Ok(None) - } + if candidates.is_empty() { + return Ok(None); + } - let mut invariants = TermSet::new() ; + let mut invariants = TermSet::new(); - scoped! { - let solver = instance.solver() ; + scoped! { + let solver = instance.solver() ; - for info in sig { - solver.declare_const(& info.idx, info) ? - } + for info in sig { + solver.declare_const(& info.idx, info) ? + } - solver.declare_fun( name, sig, typ.get() ) ? ; + solver.declare_fun( name, sig, typ.get() ) ? ; - use smt::{ SmtTerm, TermConj} ; + use smt::{ SmtTerm, TermConj} ; - for (candidate, (cubes, apps)) in candidates { - log! { @4 "checking candidate: {}", candidate } - let mut invariant = true ; + for (candidate, (cubes, apps)) in candidates { + log! { @4 "checking candidate: {}", candidate } + let mut invariant = true ; - for (cube, value, _) in definitions.iter() { - if_log! { @5 - log! { @5 "cube:" } + for (cube, value, _) in definitions.iter() { + if_log! { @5 + log! { @5 "cube:" } + for term in cube { + log! { @5 " {}", term } + } + } + let actlit = solver.get_actlit() ? ; for term in cube { - log! { @5 " {}", term } + solver.assert_act( & actlit, & SmtTerm::new(term) ) ? } - } - let actlit = solver.get_actlit() ? ; - for term in cube { - solver.assert_act( & actlit, & SmtTerm::new(term) ) ? - } - for app in apps.iter() { - let term = term::eq( app.clone(), value.clone() ) ; - solver.assert_act( & actlit, & SmtTerm::new(& term) ) ? - } - solver.assert_act_with( - & actlit, & TermConj::new( Some(& candidate) ), false - ) ? ; + for app in apps.iter() { + let term = term::eq( app.clone(), value.clone() ) ; + solver.assert_act( & actlit, & SmtTerm::new(& term) ) ? + } + solver.assert_act_with( + & actlit, & TermConj::new( Some(& candidate) ), false + ) ? ; - let sat = solver.check_sat_act( Some(& actlit) ) ? ; + let sat = solver.check_sat_act( Some(& actlit) ) ? ; - if sat { - invariant = false ; - break + if sat { + invariant = false ; + break + } } - } - if invariant { - log! { @4 "invariant :)" } - let is_new = invariants.insert(candidate) ; - debug_assert! { is_new } - } else { - log! { @4 "not invariant :(" } - for cube in cubes { - definitions[cube].0.insert( candidate.clone() ) ; + if invariant { + log! { @4 "invariant :)" } + let is_new = invariants.insert(candidate) ; + debug_assert! { is_new } + } else { + log! { @4 "not invariant :(" } + for cube in cubes { + definitions[cube].0.insert( candidate.clone() ) ; + } } + } } - } - - instance.reset_solver() ? ; + instance.reset_solver()?; - let res = if invariants.is_empty() { - None - } else { - Some(invariants) - } ; + let res = if invariants.is_empty() { + None + } else { + Some(invariants) + }; - Ok(res) + Ok(res) } - -fn make_fun_name(other_name: & str) -> Res { - let split: Vec<_> = other_name.split('|').collect() ; - let str = match split.len() { - 1 => format!("{}_hoice_reserved_fun", other_name), - 3 if split[0] == "" && split[2] == "" && split[1] != "" => { - format!("|{}_hoice_reserved_fun|", split[1]) - }, - _ => bail!("illegal symbol `{}`", other_name), - } ; - Ok(str) +fn make_fun_name(other_name: &str) -> Res { + let split: Vec<_> = other_name.split('|').collect(); + let str = match split.len() { + 1 => format!("{}_hoice_reserved_fun", other_name), + 3 if split[0] == "" && split[2] == "" && split[1] != "" => { + format!("|{}_hoice_reserved_fun|", split[1]) + } + _ => bail!("illegal symbol `{}`", other_name), + }; + Ok(str) } diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index 691aa5ee..9ec40b76 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -3,41 +3,29 @@ //! The strategies are attached `struct`s so that they can be put in a vector //! using single dispatch. That way, they can be combined however we want. -use common::* ; -use instance::* ; +use common::*; +use instance::*; -pub mod utils ; +pub mod utils; -mod one_rhs ; -mod one_lhs ; -mod cfg_red ; -mod arg_red ; -mod bias_unroll ; -mod unroll ; -mod strict_neg_clauses ; -mod fun_preds ; +mod arg_red; +mod bias_unroll; +mod cfg_red; +mod fun_preds; +mod one_lhs; +mod one_rhs; +mod strict_neg_clauses; +mod unroll; use self::{ - one_lhs::OneLhs, - one_rhs::OneRhs, - arg_red::ArgRed, - cfg_red::CfgRed, - bias_unroll:: BiasedUnroll, - unroll::RUnroll, - strict_neg_clauses::StrictNeg, - fun_preds:: FunPreds, -} ; - - + arg_red::ArgRed, bias_unroll::BiasedUnroll, cfg_red::CfgRed, fun_preds::FunPreds, + one_lhs::OneLhs, one_rhs::OneRhs, strict_neg_clauses::StrictNeg, unroll::RUnroll, +}; /// Extension for a predicate. /// /// Used by `extend_pred_left`. -pub type PredExtension = ( - TermSet, Vec<(Quantfed, Term)> -) ; - - +pub type PredExtension = (TermSet, Vec<(Quantfed, Term)>); /// Runs pre-processing. /// @@ -46,79 +34,72 @@ pub type PredExtension = ( /// false for subsystems. /// /// Finalizes the instance. -pub fn work( - instance: & mut Instance, profiler: & Profiler, -) -> Res<()> { - let res = { - let instance = profile! { - |profiler| wrap { - PreInstance::new(instance) ? - } "preproc", "pre-instance creation" - } ; - run(instance, profiler, true) - } ; - finalize(res, instance, profiler) +pub fn work(instance: &mut Instance, profiler: &Profiler) -> Res<()> { + let res = { + let instance = profile! { + |profiler| wrap { + PreInstance::new(instance) ? + } "preproc", "pre-instance creation" + }; + run(instance, profiler, true) + }; + finalize(res, instance, profiler) } /// Runs pre-processing from a pre-instance. -fn run( - instance: PreInstance, profiler: & Profiler, simplify_first: bool -) -> Res<()> { - profile! { |profiler| tick "preproc" } - - let mut reductor = profile! { - |profiler| wrap { - Reductor::new(instance) ? - } "preproc", "creation" - } ; - let res = reductor.run(profiler, simplify_first).and_then( - |_| profile! { +fn run(instance: PreInstance, profiler: &Profiler, simplify_first: bool) -> Res<()> { + profile! { |profiler| tick "preproc" } + + let mut reductor = profile! { |profiler| wrap { - reductor.destroy() - } "preproc", "reductor destruction" - } - ) ; + Reductor::new(instance) ? + } "preproc", "creation" + }; + let res = reductor.run(profiler, simplify_first).and_then(|_| { + profile! { + |profiler| wrap { + reductor.destroy() + } "preproc", "reductor destruction" + } + }); - profile! { |profiler| mark "preproc" } - res + profile! { |profiler| mark "preproc" } + res } /// Finalizes pre-processing -fn finalize( - res: Res<()>, instance: & mut Instance, _profiler: & Profiler -) -> Res<()> { - profile!( +fn finalize(res: Res<()>, instance: &mut Instance, _profiler: &Profiler) -> Res<()> { + profile!( |_profiler| wrap { instance.finalize() } "finalizing" - ) ? ; - - profile! { - |_profiler| - "positive clauses" => add instance.pos_clauses().len() - } - profile! { - |_profiler| - "negative clauses" => add instance.neg_clauses().len() - } - profile! { - |_profiler| - "negative (strict) clauses" => add instance.strict_neg_clauses().len() - } - - match res { - Err(e) => { - if e.is_unsat() { - instance.set_unsat() - } - bail!(e) - }, - Ok(()) => () - } - - Ok(()) -} + )?; + + profile! { + |_profiler| + "positive clauses" => add instance.pos_clauses().len() + } + profile! { + |_profiler| + "negative clauses" => add instance.neg_clauses().len() + } + profile! { + |_profiler| + "negative (strict) clauses" => add instance.strict_neg_clauses().len() + } + + match res { + Err(e) => { + if e.is_unsat() { + instance.set_unsat() + } + bail!(e) + } + Ok(()) => (), + } + Ok(()) +} /// Runs pre-processing on a split version of the input instance. /// @@ -126,94 +107,96 @@ fn finalize( /// /// Does **not** remove clauses that are tagged as being from unrolling. pub fn work_on_split( - instance: & Instance, to_keep: ClsIdx, - ignore: & ClsSet, profiler: & Profiler + instance: &Instance, + to_keep: ClsIdx, + ignore: &ClsSet, + profiler: &Profiler, ) -> Res { - - profile! { |profiler| tick "splitting" } - - let mut split_instance = instance.clone_with_clauses(to_keep) ; - - let mut to_forget: Vec<_> = instance.neg_clauses( - ).iter().filter_map( - |c| if c == & to_keep /* || instance[* c].from_unrolling */ { - None - } else { - Some(* c) - } - ).collect() ; - - let mut strict_neg_clauses = Vec::with_capacity( - instance.neg_clauses().len() - ) ; - - // We're going to forget clauses (swap-remove), going in descending order. - to_forget.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)) ; - for clause_idx in to_forget { - if clause_idx != to_keep { - let clause = split_instance.forget_clause(clause_idx) ? ; - if conf.preproc.split_strengthen - && ! ignore.contains(& clause_idx) - && instance.strict_neg_clauses().contains(& clause_idx) { - strict_neg_clauses.push(clause) - } + profile! { |profiler| tick "splitting" } + + let mut split_instance = instance.clone_with_clauses(to_keep); + + let mut to_forget: Vec<_> = instance + .neg_clauses() + .iter() + .filter_map(|c| { + if c == &to_keep + /* || instance[* c].from_unrolling */ + { + None + } else { + Some(*c) + } + }).collect(); + + let mut strict_neg_clauses = Vec::with_capacity(instance.neg_clauses().len()); + + // We're going to forget clauses (swap-remove), going in descending order. + to_forget.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); + for clause_idx in to_forget { + if clause_idx != to_keep { + let clause = split_instance.forget_clause(clause_idx)?; + if conf.preproc.split_strengthen + && !ignore.contains(&clause_idx) + && instance.strict_neg_clauses().contains(&clause_idx) + { + strict_neg_clauses.push(clause) + } + } } - } - - profile! { |profiler| mark "splitting" } - - let res = { - - let mut pre_instance = PreInstance::new(& mut split_instance) ? ; - - if conf.preproc.split_strengthen && strict_neg_clauses.len() < 30 { - - profile! { |profiler| tick "strengthening" } - - log! { @debug - "strengthening using {} clauses", strict_neg_clauses.len() - } - - let mut info = RedInfo::new() ; - // Maps predicates to strengthening terms. - let mut strength_map = PrdHMap::new() ; + profile! { |profiler| mark "splitting" } - for clause in strict_neg_clauses { - macro_rules! inconsistent { - () => ({ - instance.check("work_on_split (instance)") ? ; - instance.check("work_on_split (split)") ? ; - bail!("inconsistent instance state") - }) ; - } - - let (pred, args) = { - let mut pred_apps = clause.lhs_preds().iter() ; + let res = { + let mut pre_instance = PreInstance::new(&mut split_instance)?; - if let Some((pred, argss)) = pred_apps.next() { - debug_assert! { pred_apps.next().is_none() } + if conf.preproc.split_strengthen && strict_neg_clauses.len() < 30 { + profile! { |profiler| tick "strengthening" } - let mut argss = argss.iter() ; - if let Some(args) = argss.next() { - debug_assert! { argss.next().is_none() } - (* pred, args) - } else { - inconsistent!() + log! { @debug + "strengthening using {} clauses", strict_neg_clauses.len() } - } else { - inconsistent!() - } - } ; - - log! { @debug - "Strengthening using {}", - clause.to_string_info( instance.preds() ) ? - } - - use instance::preproc::utils::ExtractRes ; - match profile!( + let mut info = RedInfo::new(); + + // Maps predicates to strengthening terms. + let mut strength_map = PrdHMap::new(); + + for clause in strict_neg_clauses { + macro_rules! inconsistent { + () => {{ + instance.check("work_on_split (instance)")?; + instance.check("work_on_split (split)")?; + bail!("inconsistent instance state") + }}; + } + + let (pred, args) = { + let mut pred_apps = clause.lhs_preds().iter(); + + if let Some((pred, argss)) = pred_apps.next() { + debug_assert! { pred_apps.next().is_none() } + + let mut argss = argss.iter(); + if let Some(args) = argss.next() { + debug_assert! { argss.next().is_none() } + (*pred, args) + } else { + inconsistent!() + } + } else { + inconsistent!() + } + }; + + log! { @debug + "Strengthening using {}", + clause.to_string_info( instance.preds() ) ? + } + + use instance::preproc::utils::ExtractRes; + + match profile!( |profiler| wrap { pre_instance.extraction().0.terms_of_lhs_app( true, & instance, & clause.vars, @@ -221,102 +204,88 @@ pub fn work_on_split( None, (pred, args) ) } "strengthening", "extraction" - ) ? { - ExtractRes::Trivial => bail!( - "trivial clause during work_on_split" - ), - ExtractRes::Failed => bail!( - "extraction failure during work_on_split" - ), - ExtractRes::SuccessTrue => bail!( - "extracted true during work_on_split" - ), - ExtractRes::SuccessFalse => bail!( - "extracted false during work_on_split" - ), - ExtractRes::Success((qvars, is_none, only_terms)) => { - debug_assert! { is_none.is_none() } ; - let (terms, preds) = only_terms.destroy() ; - debug_assert! { preds.is_empty() } ; - let entry = strength_map.entry(pred).or_insert_with( - || (TermSet::new(), Vec::new()) - ) ; - let terms = term::or( - terms.into_iter().map(term::not).collect() - ) ; - if qvars.is_empty() { - entry.0.insert( terms ) ; - } else { - entry.1.push((qvars, terms)) + )? { + ExtractRes::Trivial => bail!("trivial clause during work_on_split"), + ExtractRes::Failed => bail!("extraction failure during work_on_split"), + ExtractRes::SuccessTrue => bail!("extracted true during work_on_split"), + ExtractRes::SuccessFalse => bail!("extracted false during work_on_split"), + ExtractRes::Success((qvars, is_none, only_terms)) => { + debug_assert! { is_none.is_none() }; + let (terms, preds) = only_terms.destroy(); + debug_assert! { preds.is_empty() }; + let entry = strength_map + .entry(pred) + .or_insert_with(|| (TermSet::new(), Vec::new())); + let terms = term::or(terms.into_iter().map(term::not).collect()); + if qvars.is_empty() { + entry.0.insert(terms); + } else { + entry.1.push((qvars, terms)) + } + } + } } - }, - } - } - - info += profile! { - |profiler| wrap { - pre_instance.extend_pred_left(& strength_map) ? - } "strengthening", "extend" - } ; - - profile! { |profiler| mark "strengthening" } - - profile! { - |profiler| "strengthening pred red" => add info.preds - } - profile! { - |profiler| "strengthening clause red" => add info.clauses_rmed - } - profile! { - |profiler| "strengthening clause add" => add info.clauses_added - } - } - if conf.preproc.active { - run(pre_instance, profiler, true) - } else { - Ok(()) - } - } ; + info += profile! { + |profiler| wrap { + pre_instance.extend_pred_left(& strength_map) ? + } "strengthening", "extend" + }; - finalize(res, & mut split_instance, profiler) ? ; + profile! { |profiler| mark "strengthening" } - Ok(split_instance) -} + profile! { + |profiler| "strengthening pred red" => add info.preds + } + profile! { + |profiler| "strengthening clause red" => add info.clauses_rmed + } + profile! { + |profiler| "strengthening clause add" => add info.clauses_added + } + } + if conf.preproc.active { + run(pre_instance, profiler, true) + } else { + Ok(()) + } + }; + finalize(res, &mut split_instance, profiler)?; + Ok(split_instance) +} /// Stores and applies the reduction techniques. pub struct Reductor<'a> { - /// The pre-instance. - instance: PreInstance<'a>, - /// Preinstance simplification. - simplify: Option, - /// Optional predicate argument reduction pre-processor. - arg_red: Option, - /// Optional one rhs pre-processor. - one_rhs: Option, - /// Optional one lhs pre-processor. - one_lhs: Option, - /// Optional cfg pre-processor. - cfg_red: Option, - /// Optional biased unroller. - biased_unroll: Option, - /// Optional reverse unroller. - runroll: Option, - /// Optional strengthener by strict negative clauses. - strict_neg: Option, - /// Optional predicate-to-function reduction. - fun_preds: Option, + /// The pre-instance. + instance: PreInstance<'a>, + /// Preinstance simplification. + simplify: Option, + /// Optional predicate argument reduction pre-processor. + arg_red: Option, + /// Optional one rhs pre-processor. + one_rhs: Option, + /// Optional one lhs pre-processor. + one_lhs: Option, + /// Optional cfg pre-processor. + cfg_red: Option, + /// Optional biased unroller. + biased_unroll: Option, + /// Optional reverse unroller. + runroll: Option, + /// Optional strengthener by strict negative clauses. + strict_neg: Option, + /// Optional predicate-to-function reduction. + fun_preds: Option, } impl<'a> Reductor<'a> { - /// Constructor. - /// - /// Checks the configuration to initialize the pre-processors. - pub fn new(instance: PreInstance<'a>) -> Res { - - macro_rules! some_new { + /// Constructor. + /// + /// Checks the configuration to initialize the pre-processors. + pub fn new(instance: PreInstance<'a>) -> Res { + macro_rules! some_new { ($red:ident if $flag:ident $(and $flags:ident )*) => ( some_new! { $red |if| conf.preproc.$flag $( && conf.preproc.$flags )* } ) ; @@ -332,61 +301,63 @@ impl<'a> Reductor<'a> { ) ; } - let simplify = Some( Simplify::new(& instance) ) ; - let arg_red = some_new! { ArgRed if arg_red } ; - - let one_rhs = some_new! { - OneRhs if one_rhs and one_rhs_full and reduction - } ; - let one_lhs = some_new! { - OneLhs if one_lhs and one_lhs_full and reduction - } ; - - let cfg_red = some_new! { CfgRed if cfg_red } ; - - let biased_unroll = some_new! { - BiasedUnroll if pos_unroll or neg_unroll - } ; - let runroll = some_new! { - RUnroll if neg_unroll - } ; - let strict_neg = some_new! { - StrictNeg if strict_neg - } ; - let fun_preds = if ! dtyp::one_or_more() ? { - None - } else { - some_new! { FunPreds if fun_preds } - } ; - - Ok( - Reductor { - instance, simplify, arg_red, - one_rhs, one_lhs, - cfg_red, biased_unroll, runroll, - strict_neg, fun_preds, - } - ) - } - - /// Destroys the reductor. - pub fn destroy(self) -> Res<()> { - self.instance.destroy() - } - - /// Runs the full pre-processing. - pub fn run( - & mut self, _profiler: & Profiler, simplify_first: bool - ) -> Res<()> { - // Counter for preproc dumping. - // - // Starts at `1`, `0` is reserved for the fixed point. - let mut count = 1 ; - - // Runs and profiles a pre-processor. - // - // Returns `true` if the pre-processor did something. - macro_rules! run { + let simplify = Some(Simplify::new(&instance)); + let arg_red = some_new! { ArgRed if arg_red }; + + let one_rhs = some_new! { + OneRhs if one_rhs and one_rhs_full and reduction + }; + let one_lhs = some_new! { + OneLhs if one_lhs and one_lhs_full and reduction + }; + + let cfg_red = some_new! { CfgRed if cfg_red }; + + let biased_unroll = some_new! { + BiasedUnroll if pos_unroll or neg_unroll + }; + let runroll = some_new! { + RUnroll if neg_unroll + }; + let strict_neg = some_new! { + StrictNeg if strict_neg + }; + let fun_preds = if !dtyp::one_or_more()? { + None + } else { + some_new! { FunPreds if fun_preds } + }; + + Ok(Reductor { + instance, + simplify, + arg_red, + one_rhs, + one_lhs, + cfg_red, + biased_unroll, + runroll, + strict_neg, + fun_preds, + }) + } + + /// Destroys the reductor. + pub fn destroy(self) -> Res<()> { + self.instance.destroy() + } + + /// Runs the full pre-processing. + pub fn run(&mut self, _profiler: &Profiler, simplify_first: bool) -> Res<()> { + // Counter for preproc dumping. + // + // Starts at `1`, `0` is reserved for the fixed point. + let mut count = 1; + + // Runs and profiles a pre-processor. + // + // Returns `true` if the pre-processor did something. + macro_rules! run { (@ info $info_opt:expr) => ( $info_opt.unwrap_or( RedInfo::new() ) ) ; @@ -410,127 +381,122 @@ impl<'a> Reductor<'a> { ) ; } - utils::register_stats(& self.instance, _profiler, count) ? ; - - if simplify_first { - run! { simplify } ; - } + utils::register_stats(&self.instance, _profiler, count)?; - if ! conf.preproc.active || self.instance.track_samples() { - return Ok(()) - } + if simplify_first { + run! { simplify }; + } - // Used to avoid running cfg reduction if nothing has changed since the - // last run. - let mut changed_since_cfg_red = true ; + if !conf.preproc.active || self.instance.track_samples() { + return Ok(()); + } - loop { + // Used to avoid running cfg reduction if nothing has changed since the + // last run. + let mut changed_since_cfg_red = true; - if self.instance.is_solved() { break } - conf.check_timeout() ? ; + loop { + if self.instance.is_solved() { + break; + } + conf.check_timeout()?; - run! { arg_red } ; + run! { arg_red }; - let changed = false ; + let changed = false; - if changed { - changed_since_cfg_red = true ; - continue - } + if changed { + changed_since_cfg_red = true; + continue; + } - let changed = run! { one_rhs } ; - let changed = run! { one_lhs } || changed ; + let changed = run! { one_rhs }; + let changed = run! { one_lhs } || changed; - if changed { - changed_since_cfg_red = true ; - continue - } + if changed { + changed_since_cfg_red = true; + continue; + } - if self.instance.is_solved() { break } + if self.instance.is_solved() { + break; + } - let changed = run! { fun_preds } ; + let changed = run! { fun_preds }; - if changed { - changed_since_cfg_red = true ; - continue - } + if changed { + changed_since_cfg_red = true; + continue; + } - if changed_since_cfg_red { - let changed = run! { cfg_red } ; + if changed_since_cfg_red { + let changed = run! { cfg_red }; - if ! changed { - break - } else { - changed_since_cfg_red = false + if !changed { + break; + } else { + changed_since_cfg_red = false + } + } else { + break; + } } - } else { - break - } - - } - - conf.check_timeout() ? ; + conf.check_timeout()?; - if self.instance.split().is_none() && self.instance.clauses().len() > 20 { - let biased_info = run!(biased_unroll info) ; - if biased_info.non_zero() { - run! { simplify } ; - } + if self.instance.split().is_none() && self.instance.clauses().len() > 20 { + let biased_info = run!(biased_unroll info); + if biased_info.non_zero() { + run! { simplify }; + } - let strict_neg_count = self.instance.strict_neg_clauses().2.fold( - 0, |acc, _| acc + 1 - ) ; - if strict_neg_count <= 1 - && conf.preproc.runroll { - let info = run!( runroll info ) ; - if info.non_zero() { - run! { simplify } ; + let strict_neg_count = self + .instance + .strict_neg_clauses() + .2 + .fold(0, |acc, _| acc + 1); + if strict_neg_count <= 1 && conf.preproc.runroll { + let info = run!( runroll info ); + if info.non_zero() { + run! { simplify }; + } + } } - } - } - run! { strict_neg } ; + run! { strict_neg }; - utils::register_final_stats(& self.instance, _profiler) ? ; + utils::register_final_stats(&self.instance, _profiler)?; - Ok(()) - } + Ok(()) + } } - - - - - - /// Reduction strategy trait. pub trait RedStrat { - /// Pre-processor's name. - fn name(& self) -> & 'static str ; + /// Pre-processor's name. + fn name(&self) -> &'static str; - /// Constructor. - fn new(& Instance) -> Self ; + /// Constructor. + fn new(&Instance) -> Self; - /// Applies the reduction strategy. Returns the number of predicates reduced - /// and the number of clauses forgotten. - fn apply( - & mut self, & mut PreInstance - ) -> Res ; + /// Applies the reduction strategy. Returns the number of predicates reduced + /// and the number of clauses forgotten. + fn apply(&mut self, &mut PreInstance) -> Res; } - /// Calls `PredInstance::simplify_all`. -pub struct Simplify ; +pub struct Simplify; impl RedStrat for Simplify { - fn name(& self) -> & 'static str { "simplify" } + fn name(&self) -> &'static str { + "simplify" + } - fn new(_: & Instance) -> Self { Simplify } + fn new(_: &Instance) -> Self { + Simplify + } - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - instance.simplify_all() - } -} \ No newline at end of file + fn apply(&mut self, instance: &mut PreInstance) -> Res { + instance.simplify_all() + } +} diff --git a/src/instance/preproc/one_lhs.rs b/src/instance/preproc/one_lhs.rs index 373f8c96..adb363c9 100644 --- a/src/instance/preproc/one_lhs.rs +++ b/src/instance/preproc/one_lhs.rs @@ -1,17 +1,10 @@ //! One lhs module. - -use common::* ; +use common::*; use instance::{ - instance::PreInstance, preproc::{ - RedStrat, utils::ExtractRes - }, -} ; - - - - - + instance::PreInstance, + preproc::{utils::ExtractRes, RedStrat}, +}; /// Tries to reduce predicates that appear as an antecedent in exactly one /// clause. @@ -34,200 +27,197 @@ use instance::{ /// If `lhs` or `(not rhs)` is unsat, then the clause is dropped and `p` is /// reduced to `true` since it does not appear as an antecedent anywhere /// anymore. -pub struct OneLhs ; +pub struct OneLhs; impl OneLhs { - /// Logs an extraction result. - fn log_extraction( - & self, _instance: & Instance, - _quantfed: & Quantfed, _pred_app: & Option, _tterms: & TTermSet - ) { - if_log!{ @4 - let mut s = "(".to_string() ; - - if ! _quantfed.is_empty() { - s.push_str("exists (") ; - for (var, sort) in _quantfed { - s.push_str( - & format!(" ({} {})", var.default_str(), sort) - ) + /// Logs an extraction result. + fn log_extraction( + &self, + _instance: &Instance, + _quantfed: &Quantfed, + _pred_app: &Option, + _tterms: &TTermSet, + ) { + if_log!{ @4 + let mut s = "(".to_string() ; + + if ! _quantfed.is_empty() { + s.push_str("exists (") ; + for (var, sort) in _quantfed { + s.push_str( + & format!(" ({} {})", var.default_str(), sort) + ) + } + s.push_str(" )\n(") + } + + s.push_str("or\n") ; + + if let Some((pred, args)) = _pred_app { + s.push_str("(") ; + s.push_str(& _instance[* pred].name) ; + for arg in args.iter() { + s.push_str( & format!(" {}", arg) ) + } + s.push_str(")") + } + + s.push_str("\n (not\n (and") ; + + for (pred, argss) in _tterms.preds() { + for args in argss { + s.push_str( + & format!(" ({} {})\n", _instance[* pred], args) + ) + } + } + for term in _tterms.terms() { + s.push_str( + & format!(" {}\n", term) + ) + } + + if ! _quantfed.is_empty() { + s.push_str(") ") + } + s.push_str(")") ; + + log!{ @4 "{}", s } } - s.push_str(" )\n(") - } - - s.push_str("or\n") ; + } - if let Some((pred, args)) = _pred_app { - s.push_str("(") ; - s.push_str(& _instance[* pred].name) ; - for arg in args.iter() { - s.push_str( & format!(" {}", arg) ) + /// Attemps to unfold a predicate that appears in exactly one LHS. + /// + /// Returns `None` if unfolding failed. + fn work_on( + &self, + pred: PrdIdx, + clause: ClsIdx, + instance: &mut PreInstance, + ) -> Res> { + let extraction_res = { + let (extraction, instance) = instance.extraction(); + let clause = &instance[clause]; + + let args = if let Some(argss) = clause.lhs_preds().get(&pred) { + let mut iter = argss.iter(); + // let args = iter.next().expect( + // "illegal clause lhs_preds, argument set is empty" + // ) ; + iter.next() + .expect("illegal clause lhs_preds, argument set is empty") + + // Predicate appears more than once in the LHS, aborting. + // if false && iter.next().is_some() { + // return Ok(None) + // } else { + // args + // } + } else { + bail!("inconsistent instance state") + }; + + // Is the rhs an application of `pred`? + match clause.rhs() { + Some((p, _)) if p == pred => ExtractRes::SuccessTrue, + _ => extraction.terms_of_lhs_app( + false, + instance, + clause.vars(), + (clause.lhs_terms(), clause.lhs_preds()), + clause.rhs(), + (pred, args), + )?, + } + }; + + log! { @4 + "from {}", + instance.clauses()[clause].to_string_info( instance.preds() ) ? } - s.push_str(")") - } - - s.push_str("\n (not\n (and") ; - - for (pred, argss) in _tterms.preds() { - for args in argss { - s.push_str( - & format!(" ({} {})\n", _instance[* pred], args) - ) + log! { @2 + "unfolding {}", conf.emph(& instance[pred].name) } - } - for term in _tterms.terms() { - s.push_str( - & format!(" {}\n", term) - ) - } - - if ! _quantfed.is_empty() { - s.push_str(") ") - } - s.push_str(")") ; - - log!{ @4 "{}", s } + + use self::ExtractRes::*; + let info = match extraction_res { + SuccessTrue => { + log! { @4 "=> true" } + instance.force_true(pred)? + } + SuccessFalse => { + log! { @4 "=> false" } + instance.force_false(pred)? + } + Trivial => { + log! { @4 "=> trivial" } + instance.force_true(pred)? + } + Success((qualfed, pred_app, tterms)) => { + if pred_app.is_none() && tterms.is_empty() { + log! { @4 "=> false" } + instance.force_false(pred)? + } else { + self.log_extraction(instance, &qualfed, &pred_app, &tterms); + instance.force_pred_right(pred, qualfed, pred_app, tterms)? + } + } + // Failed is caught before this match. + Failed => return Ok(None), + }; + + Ok(Some(info)) } - } - - /// Attemps to unfold a predicate that appears in exactly one LHS. - /// - /// Returns `None` if unfolding failed. - fn work_on( - & self, pred: PrdIdx, clause: ClsIdx, instance: & mut PreInstance - ) -> Res< Option > { - - let extraction_res = { - let (extraction, instance) = instance.extraction() ; - let clause = & instance[clause] ; - - let args = if let Some(argss) = clause.lhs_preds().get(& pred) { - let mut iter = argss.iter() ; - // let args = iter.next().expect( - // "illegal clause lhs_preds, argument set is empty" - // ) ; - iter.next().expect( - "illegal clause lhs_preds, argument set is empty" - ) - - // Predicate appears more than once in the LHS, aborting. - // if false && iter.next().is_some() { - // return Ok(None) - // } else { - // args - // } - } else { - bail!("inconsistent instance state") - } ; - - // Is the rhs an application of `pred`? - match clause.rhs() { - Some((p, _)) if p == pred => { - ExtractRes::SuccessTrue - }, - _ => extraction.terms_of_lhs_app( - false, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - clause.rhs(), (pred, args) - ) ?, - } - } ; - - log! { @4 - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? +} + +impl RedStrat for OneLhs { + fn name(&self) -> &'static str { + "one_lhs" } - log! { @2 - "unfolding {}", conf.emph(& instance[pred].name) + + fn new(_: &Instance) -> Self { + OneLhs } - use self::ExtractRes::* ; - let info = match extraction_res { - SuccessTrue => { - log! { @4 "=> true" } - instance.force_true(pred) ? - }, - SuccessFalse => { - log! { @4 "=> false" } - instance.force_false(pred) ? - }, - Trivial => { - log! { @4 "=> trivial" } - instance.force_true(pred) ? - }, - Success((qualfed, pred_app, tterms)) => { - if pred_app.is_none() && tterms.is_empty() { - log! { @4 "=> false" } - instance.force_false(pred) ? - } else { - self.log_extraction( - instance, & qualfed, & pred_app, & tterms - ) ; - instance.force_pred_right( - pred, qualfed, pred_app, tterms - ) ? + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut red_info = RedInfo::new(); + + 'all_preds: for pred in instance.pred_indices() { + if instance.is_known(pred) || instance.clauses_of(pred).0.len() > 1 { + continue 'all_preds; + } + + conf.check_timeout()?; + + let clause = if let Some(clause) = instance.clauses_of(pred).0.iter().next().cloned() { + // Appears in exactly one lhs, let's do this. + clause + } else { + log! { + @3 "{} does not appear in any lhs, forcing true", instance[pred] + } + red_info.preds += 1; + red_info += instance.force_true(pred)?; + continue 'all_preds; + }; + + log! { @3 + "looking at {} ({}, {})", + instance[pred], + instance.clauses_of(pred).0.len(), + instance.clauses_of(pred).1.len(), + } + + if let Some(info) = self.work_on(pred, clause, instance)? { + red_info.preds += 1; + red_info += info; + instance.check("after unfolding (one_lhs)")?; + debug_assert! { instance.is_known(pred) } + } else { + log! { @4 "failed to unfold {}", instance[pred] } + } } - }, - // Failed is caught before this match. - Failed => return Ok(None), - } ; - - Ok( Some(info) ) - } -} -impl RedStrat for OneLhs { - fn name(& self) -> & 'static str { "one_lhs" } - - fn new(_: & Instance) -> Self { OneLhs } - - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut red_info = RedInfo::new() ; - - 'all_preds: for pred in instance.pred_indices() { - if instance.is_known(pred) - || instance.clauses_of(pred).0.len() > 1 { - continue 'all_preds - } - - conf.check_timeout() ? ; - - let clause = if let Some(clause) = instance.clauses_of( - pred - ).0.iter().next().cloned() { - // Appears in exactly one lhs, let's do this. - clause - } else { - log! { - @3 "{} does not appear in any lhs, forcing true", instance[pred] - } - red_info.preds += 1 ; - red_info += instance.force_true(pred) ? ; - continue 'all_preds - } ; - - log! { @3 - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len(), - } - - if let Some(info) = self.work_on( - pred, clause, instance - ) ? { - red_info.preds += 1 ; - red_info += info ; - instance.check("after unfolding (one_lhs)") ? ; - debug_assert! { instance.is_known(pred) } - } else { - log! { @4 "failed to unfold {}", instance[pred] } - } + Ok(red_info) } - - Ok( red_info ) - } } - diff --git a/src/instance/preproc/one_rhs.rs b/src/instance/preproc/one_rhs.rs index c0240c6a..5fb13c44 100644 --- a/src/instance/preproc/one_rhs.rs +++ b/src/instance/preproc/one_rhs.rs @@ -1,17 +1,10 @@ //! One rhs module. - -use common::* ; +use common::*; use instance::{ - instance::PreInstance, preproc::{ - RedStrat, utils::ExtractRes - }, -} ; - - - - - + instance::PreInstance, + preproc::{utils::ExtractRes, RedStrat}, +}; /// Works on predicates that appear in only one rhs. /// @@ -24,172 +17,163 @@ use instance::{ /// | `(v > 0) => (p 7 v')` | `(v_0 = 7)` | /// | `(v > 0) => (p v v )` | `(v_0 = v_1) and (v_0 > 0)` | /// | `(v > 0) and (v <= 0) => (p 7 v')` | `false` (by check-sat) | -pub struct OneRhs ; +pub struct OneRhs; impl OneRhs { - /// Logs an extraction result. - fn log_extraction( - & self, _instance: & Instance, - _quantfed: & Quantfed, _tterms: & TTermSet - ) { - if_log! { @4 - let mut s = "(".to_string() ; - - if ! _quantfed.is_empty() { - s.push_str("exists") ; - for (var, sort) in _quantfed { - s.push_str( - & format!(" ({} {})", var.default_str(), sort) - ) + /// Logs an extraction result. + fn log_extraction(&self, _instance: &Instance, _quantfed: &Quantfed, _tterms: &TTermSet) { + if_log! { @4 + let mut s = "(".to_string() ; + + if ! _quantfed.is_empty() { + s.push_str("exists") ; + for (var, sort) in _quantfed { + s.push_str( + & format!(" ({} {})", var.default_str(), sort) + ) + } + s.push_str(" )\n(") + } + + s.push_str("and\n") ; + + for (pred, argss) in _tterms.preds() { + for args in argss { + s.push_str( + & format!(" ({} {})\n", _instance[* pred], args) + ) + } + } + for term in _tterms.terms() { + s.push_str( & format!(" {}\n", term) ) + } + + if ! _quantfed.is_empty() { + s.push_str(") ") + } + s.push_str(")") ; + + log! { @4 "{}", s } } - s.push_str(" )\n(") - } - - s.push_str("and\n") ; + } - for (pred, argss) in _tterms.preds() { - for args in argss { - s.push_str( - & format!(" ({} {})\n", _instance[* pred], args) - ) + /// Attemps to unfold a predicate that appears in exactly one RHS. + /// + /// Returns `None` if unfolding failed. + fn work_on( + &self, + pred: PrdIdx, + clause: ClsIdx, + instance: &mut PreInstance, + ) -> Res> { + let extraction_res = { + let (extraction, instance) = instance.extraction(); + let clause = &instance[clause]; + + if let Some((_this_pred, args)) = clause.rhs() { + debug_assert_eq!(pred, _this_pred); + + // Does `pred` appear in the lhs? + match clause.lhs_preds().get(&pred) { + Some(apps) if !apps.is_empty() => ExtractRes::SuccessFalse, + _ => extraction.terms_of_rhs_app( + true, + instance, + clause.vars(), + clause.lhs_terms(), + clause.lhs_preds(), + (pred, args), + )?, + } + } else { + bail!("inconsistent instance state") + } + }; + + log! { @4 + "from {}", + instance.clauses()[clause].to_string_info( instance.preds() ) ? } - } - for term in _tterms.terms() { - s.push_str( & format!(" {}\n", term) ) - } - - if ! _quantfed.is_empty() { - s.push_str(") ") - } - s.push_str(")") ; - - log! { @4 "{}", s } - } - } - - - /// Attemps to unfold a predicate that appears in exactly one RHS. - /// - /// Returns `None` if unfolding failed. - fn work_on( - & self, pred: PrdIdx, clause: ClsIdx, instance: & mut PreInstance - ) -> Res< Option > { - - let extraction_res = { - let (extraction, instance) = instance.extraction() ; - let clause = & instance[clause] ; - - if let Some((_this_pred, args)) = clause.rhs() { - debug_assert_eq!( pred, _this_pred ) ; - - // Does `pred` appear in the lhs? - match clause.lhs_preds().get(& pred) { - Some(apps) if ! apps.is_empty() => { - ExtractRes::SuccessFalse - }, - _ => extraction.terms_of_rhs_app( - true, instance, clause.vars(), - clause.lhs_terms(), clause.lhs_preds(), - (pred, args) - ) ?, + log! { @2 + "unfolding {}", conf.emph(& instance[pred].name) } - } else { - bail!("inconsistent instance state") - } - } ; - - log! { @4 - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? - } - log! { @2 - "unfolding {}", conf.emph(& instance[pred].name) - } - use self::ExtractRes::* ; - let info = match extraction_res { - Trivial => { - log! { @ 4 "=> trivial" } - instance.force_false(pred) ? - }, - - SuccessTrue => { - log! { @ 4 "=> true" } - instance.force_true(pred) ? - }, - SuccessFalse => { - log! { @ 4 "=> false" } - instance.force_false(pred) ? - }, - - Success( (qvars, tterms) ) => { - self.log_extraction( - instance, & qvars, & tterms - ) ; - instance.force_pred_left( - pred, qvars, tterms - ) ? - }, - - Failed => return Ok(None), - } ; - - Ok( Some(info) ) - } + use self::ExtractRes::*; + let info = match extraction_res { + Trivial => { + log! { @ 4 "=> trivial" } + instance.force_false(pred)? + } + + SuccessTrue => { + log! { @ 4 "=> true" } + instance.force_true(pred)? + } + SuccessFalse => { + log! { @ 4 "=> false" } + instance.force_false(pred)? + } + + Success((qvars, tterms)) => { + self.log_extraction(instance, &qvars, &tterms); + instance.force_pred_left(pred, qvars, tterms)? + } + + Failed => return Ok(None), + }; + + Ok(Some(info)) + } } impl RedStrat for OneRhs { - fn name(& self) -> & 'static str { "one_rhs" } - - fn new(_: & Instance) -> Self { OneRhs } - - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut red_info = RedInfo::new() ; - - 'all_preds: for pred in instance.pred_indices() { - if instance.is_known(pred) - || instance.clauses_of(pred).1.len() > 1 { - continue 'all_preds - } - - conf.check_timeout() ? ; - - let clause = if let Some(clause) = instance.clauses_of( - pred - ).1.iter().next().cloned() { - // Appears in exactly on lhs, let's do this. - clause - } else { - log! { - @3 "{} does not appear in any rhs, forcing false", instance[pred] - } - red_info.preds += 1 ; - red_info += instance.force_false(pred) ? ; - continue 'all_preds - } ; - - log! { @3 - "looking at {} ({}, {})", - instance[pred], - instance.clauses_of(pred).0.len(), - instance.clauses_of(pred).1.len(), - } - - if let Some(info) = self.work_on( - pred, clause, instance - ) ? { - red_info.preds += 1 ; - red_info += info ; - instance.check("after unfolding (one_rhs)") ? ; - debug_assert! { instance.is_known(pred) } - } else { - log! { @4 "failed to unfold {}", instance[pred] } - } + fn name(&self) -> &'static str { + "one_rhs" + } + fn new(_: &Instance) -> Self { + OneRhs } - Ok( red_info ) - } -} \ No newline at end of file + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut red_info = RedInfo::new(); + + 'all_preds: for pred in instance.pred_indices() { + if instance.is_known(pred) || instance.clauses_of(pred).1.len() > 1 { + continue 'all_preds; + } + + conf.check_timeout()?; + + let clause = if let Some(clause) = instance.clauses_of(pred).1.iter().next().cloned() { + // Appears in exactly on lhs, let's do this. + clause + } else { + log! { + @3 "{} does not appear in any rhs, forcing false", instance[pred] + } + red_info.preds += 1; + red_info += instance.force_false(pred)?; + continue 'all_preds; + }; + + log! { @3 + "looking at {} ({}, {})", + instance[pred], + instance.clauses_of(pred).0.len(), + instance.clauses_of(pred).1.len(), + } + + if let Some(info) = self.work_on(pred, clause, instance)? { + red_info.preds += 1; + red_info += info; + instance.check("after unfolding (one_rhs)")?; + debug_assert! { instance.is_known(pred) } + } else { + log! { @4 "failed to unfold {}", instance[pred] } + } + } + + Ok(red_info) + } +} diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index 8283dec2..ee28773b 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -1,130 +1,130 @@ //! Strict negative clause. -use common::* ; +use common::*; use instance::{ - instance::PreInstance, preproc::{ - RedStrat, utils::ExtractRes - }, -} ; + instance::PreInstance, + preproc::{utils::ExtractRes, RedStrat}, +}; -pub struct StrictNeg ; +pub struct StrictNeg; impl RedStrat for StrictNeg { - fn name(& self) -> & 'static str { "strict_neg" } - - fn new(_: & Instance) -> Self { StrictNeg } - - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - let mut info = RedInfo::new() ; - - let mut partial_defs = PrdHMap::new() ; - let mut clauses_to_rm = Vec::new() ; - - macro_rules! pdef { - ($pred:expr => set false) => ({ - partial_defs.remove( & $pred ) ; - partial_defs.insert($pred, None) ; - () - }) ; - - ($pred:expr => add $term:expr) => ({ - if let Some(vec) = partial_defs.entry($pred).or_insert_with( - || Some( Vec::new() ) - ).as_mut() { - vec.push($term) - } - }) ; + fn name(&self) -> &'static str { + "strict_neg" } - scoped! { - let ( - extractor, instance, strict_clauses - ) = instance.strict_neg_clauses() ; - - for (clause_idx, clause) in strict_clauses { - log! { @3 "working on clause #{}", clause_idx } - - let (pred, args) = if let Some( - (pred, argss) - ) = clause.lhs_preds().iter().next() { - if let Some(args) = argss.iter().next() { - (* pred, args) - } else { - bail!("inconsistent instance state") - } - } else { - bail!("inconsistent instance state") - } ; - - match extractor.terms_of_lhs_app( - false, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - clause.rhs(), (pred, args) - ) ? { - ExtractRes::Trivial | - ExtractRes::SuccessTrue => clauses_to_rm.push( clause_idx ), - - ExtractRes::SuccessFalse => pdef!(pred => set false), - - ExtractRes::Success( (qvars, pred_app, tterms) ) => - if qvars.is_empty() { - if pred_app.is_some() - || ! tterms.preds().is_empty() { - bail!("inconsistent instance state") - } + fn new(_: &Instance) -> Self { + StrictNeg + } - let terms = tterms.terms() ; + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); + + let mut partial_defs = PrdHMap::new(); + let mut clauses_to_rm = Vec::new(); + + macro_rules! pdef { + ($pred:expr => set false) => {{ + partial_defs.remove(&$pred); + partial_defs.insert($pred, None); + () + }}; + + ($pred:expr => add $term:expr) => {{ + if let Some(vec) = partial_defs + .entry($pred) + .or_insert_with(|| Some(Vec::new())) + .as_mut() + { + vec.push($term) + } + }}; + } - if terms.is_empty() { - pdef!(pred => set false) + scoped! { + let ( + extractor, instance, strict_clauses + ) = instance.strict_neg_clauses() ; + + for (clause_idx, clause) in strict_clauses { + log! { @3 "working on clause #{}", clause_idx } + + let (pred, args) = if let Some( + (pred, argss) + ) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (* pred, args) + } else { + bail!("inconsistent instance state") + } } else { - let term = term::or( - terms.iter().map( - |term| term::not( term.clone() ) - ).collect() - ) ; - pdef!(pred => add term) + bail!("inconsistent instance state") + } ; + + match extractor.terms_of_lhs_app( + false, instance, clause.vars(), + ( clause.lhs_terms(), clause.lhs_preds() ), + clause.rhs(), (pred, args) + ) ? { + ExtractRes::Trivial | + ExtractRes::SuccessTrue => clauses_to_rm.push( clause_idx ), + + ExtractRes::SuccessFalse => pdef!(pred => set false), + + ExtractRes::Success( (qvars, pred_app, tterms) ) => + if qvars.is_empty() { + if pred_app.is_some() + || ! tterms.preds().is_empty() { + bail!("inconsistent instance state") + } + + let terms = tterms.terms() ; + + if terms.is_empty() { + pdef!(pred => set false) + } else { + let term = term::or( + terms.iter().map( + |term| term::not( term.clone() ) + ).collect() + ) ; + pdef!(pred => add term) + } + }, + + ExtractRes::Failed => (), } - }, - ExtractRes::Failed => (), + } } - } - } - - if ! clauses_to_rm.is_empty() { - info.clauses_rmed += clauses_to_rm.len() ; - instance.forget_clauses(& mut clauses_to_rm) ? - } - - for (pred, terms_opt) in partial_defs { - - if let Some(mut terms) = terms_opt { - if let Some(term) = instance.get_str(pred) { - terms.push( term.clone() ) + if !clauses_to_rm.is_empty() { + info.clauses_rmed += clauses_to_rm.len(); + instance.forget_clauses(&mut clauses_to_rm)? } - let conj = term::and( terms ) ; - match conj.bool() { - Some(true) => (), - Some(false) => { - info.preds += 1 ; - info += instance.force_false(pred) ? - }, - None => { - instance.set_str(pred, conj) ; - }, + + for (pred, terms_opt) in partial_defs { + if let Some(mut terms) = terms_opt { + if let Some(term) = instance.get_str(pred) { + terms.push(term.clone()) + } + let conj = term::and(terms); + match conj.bool() { + Some(true) => (), + Some(false) => { + info.preds += 1; + info += instance.force_false(pred)? + } + None => { + instance.set_str(pred, conj); + } + } + } else { + info.preds += 1; + info += instance.force_false(pred)? + } } - } else { - info.preds += 1 ; - info += instance.force_false(pred) ? - } + Ok(info) } - - - Ok(info) - } -} \ No newline at end of file +} diff --git a/src/instance/preproc/unroll.rs b/src/instance/preproc/unroll.rs index 59058a19..586034bb 100644 --- a/src/instance/preproc/unroll.rs +++ b/src/instance/preproc/unroll.rs @@ -1,20 +1,14 @@ //! Bias unrolling module. - -use common::* ; +use common::*; use instance::{ - Clause, - instance::PreInstance, - preproc::{ - RedStrat, utils::{ - ExtractRes, ExtractionCxt - } - }, -} ; - - - - + instance::PreInstance, + preproc::{ + utils::{ExtractRes, ExtractionCxt}, + RedStrat, + }, + Clause, +}; // /// Unrolls positive constraints once. // pub struct Unroll { @@ -112,179 +106,169 @@ use instance::{ // } // } - - /// Reverse-unrolls negative constraints once. pub struct RUnroll { - max_new_clauses: usize, - ignore: PrdSet, + max_new_clauses: usize, + ignore: PrdSet, } impl RUnroll { - /// Reverse unrolls a clause. - fn runroll_clause( - & mut self, instance: & Instance, clause: & Clause, - extractor: & mut ExtractionCxt, pred_map: & mut PrdHMap< - Vec<(Option, TermSet)> - > - ) -> Res<()> { - macro_rules! insert { - ($pred:expr, $q:expr, $ts:expr) =>( - pred_map.entry($pred).or_insert_with(Vec::new).push(($q, $ts)) - ) - } + /// Reverse unrolls a clause. + fn runroll_clause( + &mut self, + instance: &Instance, + clause: &Clause, + extractor: &mut ExtractionCxt, + pred_map: &mut PrdHMap, TermSet)>>, + ) -> Res<()> { + macro_rules! insert { + ($pred:expr, $q:expr, $ts:expr) => { + pred_map + .entry($pred) + .or_insert_with(Vec::new) + .push(($q, $ts)) + }; + } - if clause.rhs().is_some() { - return Ok(()) - } + if clause.rhs().is_some() { + return Ok(()); + } - conf.check_timeout() ? ; + conf.check_timeout()?; - let mut apps = clause.lhs_preds().iter() ; + let mut apps = clause.lhs_preds().iter(); - let (pred, argss) = if let Some((pred, argss)) = apps.next() { - (pred, argss) - } else { - return Ok(()) - } ; + let (pred, argss) = if let Some((pred, argss)) = apps.next() { + (pred, argss) + } else { + return Ok(()); + }; - if self.ignore.contains(& pred) { - return Ok(()) - } + if self.ignore.contains(&pred) { + return Ok(()); + } - let pred = * pred ; - let mut argss = argss.iter() ; - let args = if let Some(args) = argss.next() { - args - } else { - bail!("illegal clause, predicate application leads to nothing") - } ; - - if argss.next().is_some() - || apps.next().is_some() { - return Ok(()) - } + let pred = *pred; + let mut argss = argss.iter(); + let args = if let Some(args) = argss.next() { + args + } else { + bail!("illegal clause, predicate application leads to nothing") + }; - // Negative constraint with only one pred app, reverse-unrolling. - match extractor.terms_of_lhs_app( - true, instance, & clause.vars, - ( clause.lhs_terms(), & PredApps::with_capacity(0) ), - None, (pred, args) - ) ? { - - ExtractRes::Success((q, apps, ts)) => { - debug_assert! { apps.is_none() } - let (terms, pred_apps) = ts.destroy() ; - if_log! { @3 - let mut s = format!( - "from {}\n", - clause.to_string_info( - instance.preds() - ) ? - ) ; - s.push_str("terms {\n") ; + if argss.next().is_some() || apps.next().is_some() { + return Ok(()); + } - for term in & terms { - s.push_str( & format!(" {}\n", term) ) - } - s.push_str("}\npred apps {{") ; - - for (pred, argss) in & pred_apps { - for args in argss { - s.push_str( & format!(" ({}", instance[* pred]) ) ; - for arg in args.iter() { - s.push_str( & format!(" {}", arg) ) - } - s.push_str(")\n") + // Negative constraint with only one pred app, reverse-unrolling. + match extractor.terms_of_lhs_app( + true, + instance, + &clause.vars, + (clause.lhs_terms(), &PredApps::with_capacity(0)), + None, + (pred, args), + )? { + ExtractRes::Success((q, apps, ts)) => { + debug_assert! { apps.is_none() } + let (terms, pred_apps) = ts.destroy(); + if_log! { @3 + let mut s = format!( + "from {}\n", + clause.to_string_info( + instance.preds() + ) ? + ) ; + s.push_str("terms {\n") ; + + for term in & terms { + s.push_str( & format!(" {}\n", term) ) + } + s.push_str("}\npred apps {{") ; + + for (pred, argss) in & pred_apps { + for args in argss { + s.push_str( & format!(" ({}", instance[* pred]) ) ; + for arg in args.iter() { + s.push_str( & format!(" {}", arg) ) + } + s.push_str(")\n") + } + } + s.push_str(")") ; + log! { @3 "{}", s } + } + debug_assert! { pred_apps.is_empty() } + insert!(pred, Quant::exists(q), terms) + } + ExtractRes::SuccessFalse => { + let mut set = TermSet::new(); + insert!(pred, None, set) + } + ExtractRes::Failed => log! { + @3 "extraction failed, skipping" + }, + ExtractRes::Trivial => bail!("found a trivial clause during unrolling"), + ExtractRes::SuccessTrue => { + bail!("found a predicate equivalent to true during reverse-unrolling") } - } - s.push_str(")") ; - log! { @3 "{}", s } } - debug_assert! { pred_apps.is_empty() } - insert!( pred, Quant::exists(q), terms ) - }, - ExtractRes::SuccessFalse => { - let mut set = TermSet::new() ; - insert!( pred, None, set ) - }, - ExtractRes::Failed => log! { - @3 "extraction failed, skipping" - }, - ExtractRes::Trivial => bail!( - "found a trivial clause during unrolling" - ), - ExtractRes::SuccessTrue => bail!( - "found a predicate equivalent to true during reverse-unrolling" - ), + Ok(()) } - - Ok(()) - } } impl RedStrat for RUnroll { - fn name(& self) -> & 'static str { "runroll" } - - fn new(instance: & Instance) -> Self { - RUnroll { - max_new_clauses: ::std::cmp::max( - 5, instance.preds().len() + instance.clauses().len() * 5 / 100 - ), - ignore: PrdSet::new(), - } - } - - fn apply( - & mut self, instance: & mut PreInstance - ) -> Res { - - let mut prd_map: PrdHMap< - Vec<(Option, TermSet)> - > = PrdHMap::with_capacity(17) ; - - scoped! { - let (extractor, instance) = instance.extraction() ; - for clause in instance.clauses() { - self.runroll_clause( - instance, clause, extractor, & mut prd_map - ) ? - } + fn name(&self) -> &'static str { + "runroll" } - let mut info = RedInfo::new() ; - for (pred, terms) in prd_map { - // Anticipate blowup. - let appearances = instance.clauses_of(pred).0.len() ; - if appearances >= self.max_new_clauses { - let is_new = self.ignore.insert(pred) ; - debug_assert! { is_new } - log! { @verb - "not r_unrolling {}, {} occurence(s), \ - estimation: {} new clauses (>= {})", - conf.emph(& instance[pred].name), - appearances, - terms.len() * appearances, - self.max_new_clauses, - } - } else { - log! { @verb - "r_unrolling {}, {} variant(s)", - conf.emph(& instance[pred].name), - terms.len() + fn new(instance: &Instance) -> Self { + RUnroll { + max_new_clauses: ::std::cmp::max( + 5, + instance.preds().len() + instance.clauses().len() * 5 / 100, + ), + ignore: PrdSet::new(), } - info += instance.reverse_unroll(pred, & terms) ? - } } - Ok(info) - } -} - - - - - + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut prd_map: PrdHMap, TermSet)>> = PrdHMap::with_capacity(17); + scoped! { + let (extractor, instance) = instance.extraction() ; + for clause in instance.clauses() { + self.runroll_clause( + instance, clause, extractor, & mut prd_map + ) ? + } + } + let mut info = RedInfo::new(); + for (pred, terms) in prd_map { + // Anticipate blowup. + let appearances = instance.clauses_of(pred).0.len(); + if appearances >= self.max_new_clauses { + let is_new = self.ignore.insert(pred); + debug_assert! { is_new } + log! { @verb + "not r_unrolling {}, {} occurence(s), \ + estimation: {} new clauses (>= {})", + conf.emph(& instance[pred].name), + appearances, + terms.len() * appearances, + self.max_new_clauses, + } + } else { + log! { @verb + "r_unrolling {}, {} variant(s)", + conf.emph(& instance[pred].name), + terms.len() + } + info += instance.reverse_unroll(pred, &terms)? + } + } + Ok(info) + } +} diff --git a/src/instance/preproc/utils.rs b/src/instance/preproc/utils.rs index e2d746fa..d8359772 100644 --- a/src/instance/preproc/utils.rs +++ b/src/instance/preproc/utils.rs @@ -1,827 +1,776 @@ //! Helper types and functions for preprocessing. -use common::* ; - -use super::{ PreInstance, RedStrat } ; -use var_to::terms::VarTermsSet ; +use common::*; +use super::{PreInstance, RedStrat}; +use var_to::terms::VarTermsSet; /// Result of extracting the terms for a predicate application in a clause. #[derive(Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum ExtractRes> { - /// The clause was found to be trivially true. - Trivial, - /// Terms could not be extracted. - /// - /// Returned when the variables appearing in the application and the other - /// variables `others` of the clause are related, or if there is a predicate - /// application mentioning variables from `others`. - Failed, - /// Success, predicate is equivalent to true. - SuccessTrue, - /// Success, predicate is equivalent to false. - SuccessFalse, - /// Success, predicate is equivalent to some top terms. - Success(T), + /// The clause was found to be trivially true. + Trivial, + /// Terms could not be extracted. + /// + /// Returned when the variables appearing in the application and the other + /// variables `others` of the clause are related, or if there is a predicate + /// application mentioning variables from `others`. + Failed, + /// Success, predicate is equivalent to true. + SuccessTrue, + /// Success, predicate is equivalent to false. + SuccessFalse, + /// Success, predicate is equivalent to some top terms. + Success(T), } impl ExtractRes { - /// True if failed. - pub fn is_failed(& self) -> bool{ * self == ExtractRes::Failed } + /// True if failed. + pub fn is_failed(&self) -> bool { + *self == ExtractRes::Failed + } } - - - - - - /// Term extraction context. pub struct ExtractionCxt { - /// Map from **clause** variables to **predicate** variables. - map: VarHMap, - /// Quantified variables. - qvars: VarHMap, - /// Index of the next fresh variable. - fresh: VarIdx, - /// True if quantifier generation is active. - quantifiers: bool, + /// Map from **clause** variables to **predicate** variables. + map: VarHMap, + /// Quantified variables. + qvars: VarHMap, + /// Index of the next fresh variable. + fresh: VarIdx, + /// True if quantifier generation is active. + quantifiers: bool, } impl Default for ExtractionCxt { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl ExtractionCxt { - /// Index of the next fresh variable. - pub fn next_fresh(& mut self) -> VarIdx { - let fresh = self.fresh ; - self.fresh.inc() ; - fresh - } - - /// Constructor. - pub fn new() -> Self { - ExtractionCxt { - map: VarHMap::with_capacity(11), - qvars: VarHMap::with_capacity(11), - fresh: 0.into(), - quantifiers: true, + /// Index of the next fresh variable. + pub fn next_fresh(&mut self) -> VarIdx { + let fresh = self.fresh; + self.fresh.inc(); + fresh } - } - - /// Creates quantified variables if needed. - /// - /// Returns `true` if the process failed. - pub fn add_qvars( - & mut self, var_info: & VarInfos, app_vars: & mut VarSet, vars: VarSet, - ) -> bool { - for var in vars { - let is_new = app_vars.insert(var) ; - - if is_new { - if ! self.quantifiers { - return true - } - let fresh = self.next_fresh() ; - let _prev = self.qvars.insert( - fresh, var_info[var].typ.clone() - ) ; - debug_assert_eq! { None, _prev } - - log! { - @6 "adding fresh {} for {}", fresh.default_str(), var_info[var] + + /// Constructor. + pub fn new() -> Self { + ExtractionCxt { + map: VarHMap::with_capacity(11), + qvars: VarHMap::with_capacity(11), + fresh: 0.into(), + quantifiers: true, } - let _prev = self.map.insert( - var, term::var( fresh, var_info[var].typ.clone() ) - ) ; - debug_assert_eq! { None, _prev } - } - } - false - } - - /// Applies a map to some predicate applications, generating quantifiers if - /// necessary and `quantifiers` is true. - /// - /// Returns `None` on failure. Failure happens when some quantifiers are - /// needed but `quantifiers` is false. - fn args_of_pred_app( - & mut self, var_info: & VarInfos, args: & VarTerms, app_vars: & mut VarSet, - ) -> Res< TExtractRes > { - log! { @6 "args_of_pred_app ({})", self.quantifiers } - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let abort = self.add_qvars( var_info, app_vars, term::vars(arg) ) ; - if abort { - return Ok( TExtractRes::Failed ) - } - - if let Some((nu_arg, _)) = arg.subst_total(& self.map) { - nu_args.push(nu_arg) - } else { - bail!("unreachable, substitution was not total") - } } - Ok( TExtractRes::Success( nu_args.into() ) ) - } - - - - /// Applies a map to some predicate applications, generating quantifiers if - /// necessary and `quantifiers` is true. - /// - /// The `pred` argument is a special predicate that will be skipped when - /// handling `src`, but it's arguments will be returned. - fn terms_of_pred_apps<'a>( - & mut self, var_info: & VarInfos, - src: & 'a PredApps, tgt: & mut TTermSet, - pred: PrdIdx, app_vars: & mut VarSet, - ) -> Res< TExtractRes< Option<& 'a VarTermsSet > > > { - log! { @6 "terms_of_pred_apps" } - let mut res = None ; - for (prd, argss) in src { - - let prd = * prd ; - if prd == pred { - res = Some(argss) ; - continue - } - - for args in argss { - match self.args_of_pred_app(var_info, args, app_vars) ? { - TExtractRes::Success(nu_args) => { - tgt.insert_pred_app(prd, nu_args) ; - () - }, - TExtractRes::Failed => { - log! { @6 "failed to extract argument {}", args } - return Ok(TExtractRes::Failed) - }, + + /// Creates quantified variables if needed. + /// + /// Returns `true` if the process failed. + pub fn add_qvars(&mut self, var_info: &VarInfos, app_vars: &mut VarSet, vars: VarSet) -> bool { + for var in vars { + let is_new = app_vars.insert(var); + + if is_new { + if !self.quantifiers { + return true; + } + let fresh = self.next_fresh(); + let _prev = self.qvars.insert(fresh, var_info[var].typ.clone()); + debug_assert_eq! { None, _prev } + + log! { + @6 "adding fresh {} for {}", fresh.default_str(), var_info[var] + } + let _prev = self + .map + .insert(var, term::var(fresh, var_info[var].typ.clone())); + debug_assert_eq! { None, _prev } + } } - } - } - Ok( TExtractRes::Success(res) ) - } - - - /// Applies a map to some terms, generating quantifiers if necessary and - /// `quantifiers` is true. - /// - /// Argument `even_if_disjoint` forces to add terms even if its variables - /// are disjoint from `app_vars`. - /// - /// Returns `true` if one of the `src` terms is false (think `is_trivial`). - fn terms_of_terms<'a, TermIter, Terms, F>( - & mut self, var_info: & VarInfos, - src: Terms, tgt: & mut TermSet, - app_vars: & mut VarSet, even_if_disjoint: bool, f: F - ) -> Res< TExtractRes > - where - TermIter: Iterator + ExactSizeIterator, - Terms: IntoIterator, - F: Fn(Term) -> Term { - log! { @4 "terms_of_terms" } - - // Finds terms which variables are related to the ones from the predicate - // applications. - let src = src.into_iter() ; - - // The terms we're currently looking at. - let mut lhs_terms_vec: Vec<_> = Vec::with_capacity( src.len() ) ; - for term in src { - match term.bool() { - Some(true) => (), - Some(false) => return Ok( TExtractRes::Success(true) ), - _ => lhs_terms_vec.push(term), - } + false } - // Terms which variables are disjoint from `app_vars` **for now**. This - // might change as we generate quantified variables. - let mut postponed: Vec<_> = Vec::with_capacity( lhs_terms_vec.len() ) ; - - - // A fixed point is reached when we go through the terms in `lhs_terms_vec` - // and don't generate quantified variables. - loop { - let mut fixed_point = true ; - - for term in lhs_terms_vec.drain(0..) { - log! { @6 "{}", term.to_string_info(var_info) ? } - let vars = term::vars(term) ; - - if app_vars.len() == var_info.len() - || vars.is_subset(& app_vars) { - let term = if let Some((term, _)) = term.subst_total(& self.map) { - term - } else { - bail!("[unreachable] failure during total substitution (1)") - } ; - log! { @6 " sub {}", term } - tgt.insert( f(term) ) ; - - } else if ! even_if_disjoint && vars.is_disjoint(& app_vars) { - log! { @6 " disjoint" } - postponed.push(term) - } else { - // The term mentions variables from `app_vars` and other variables. - // We generate quantified variables to account for them and - // invalidate `fixed_point` since terms that were previously disjoint - // might now intersect. - fixed_point = false ; - let abort = self.add_qvars(var_info, app_vars, vars) ; - if abort { - return Ok( TExtractRes::Failed ) - } - - let term = if let Some((term, _)) = term.subst_total(& self.map) { - term - } else { - bail!("[unreachable] failure during total substitution (2)") - } ; - tgt.insert( f(term) ) ; - } + /// Applies a map to some predicate applications, generating quantifiers if + /// necessary and `quantifiers` is true. + /// + /// Returns `None` on failure. Failure happens when some quantifiers are + /// needed but `quantifiers` is false. + fn args_of_pred_app( + &mut self, + var_info: &VarInfos, + args: &VarTerms, + app_vars: &mut VarSet, + ) -> Res> { + log! { @6 "args_of_pred_app ({})", self.quantifiers } + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let abort = self.add_qvars(var_info, app_vars, term::vars(arg)); + if abort { + return Ok(TExtractRes::Failed); + } - } - - if fixed_point || postponed.is_empty() { - break - } else { - // Iterating over posponed terms next. - ::std::mem::swap( - & mut lhs_terms_vec, & mut postponed - ) - } - - } - - Ok( TExtractRes::Success(false) ) - } - - - - /// Given a predicate application, returns the constraints on the input and a - /// map from the variables used in the arguments to the variables of the - /// predicate. Also returns the set of variables appearing in the - /// application. - /// - /// # Assumptions - /// - /// - `self.map.is_empty()` - /// - /// # TODO - /// - /// - more doc with examples - pub fn terms_of_app( - & mut self, var_info: & VarInfos, - instance: & Instance, pred: PrdIdx, args: & VarTerms, - ) -> Res< - Option<(TermSet, VarSet)> - > { - debug_assert! { self.map.is_empty() } - - let mut app_vars = VarSet::with_capacity( instance[pred].sig.len() ) ; - let mut terms = TermSet::with_capacity( 7 ) ; - - // Will store the arguments that are not a variable or a constant. - let mut postponed = Vec::with_capacity( args.len() ) ; - - for (index, arg) in args.index_iter() { - if let Some(var) = arg.var_idx() { - let _ = app_vars.insert(var) ; - if let Some(pre) = self.map.insert( - var, term::var( index, arg.typ() ) - ) { - terms.insert( - term::eq( term::var(index, arg.typ()), pre ) - ) ; - } - } else { - match arg.as_val().to_term() { - Some(trm) => { - debug_assert_eq! { trm.typ(), arg.typ() } - let var = term::var(index, trm.typ()) ; - terms.insert( - term::eq(var, trm) - ) ; - }, - None => { - postponed.push( (index, arg) ) - }, + if let Some((nu_arg, _)) = arg.subst_total(&self.map) { + nu_args.push(nu_arg) + } else { + bail!("unreachable, substitution was not total") + } } - } + Ok(TExtractRes::Success(nu_args.into())) } - for (var, arg) in postponed { - if let Some( (term, _) ) = arg.subst_total(& self.map) { - terms.insert( - term::eq(term::var(var, arg.typ()), term) - ) ; - } else if let Some((v, inverted)) = arg.invert_var(var, arg.typ()) { - let _prev = self.map.insert(v, inverted) ; - debug_assert_eq!( _prev, None ) ; - let is_new = app_vars.insert(v) ; - debug_assert!( is_new ) - } else if let TExtractRes::Failed = self.terms_of_terms( - var_info, Some(arg), & mut terms, - & mut app_vars, true, |term| term::eq( - term::var(var, term.typ()), term - ) - ) ? { - return Ok(None) - } - } - - Ok( Some( (terms, app_vars) ) ) - } - + /// Applies a map to some predicate applications, generating quantifiers if + /// necessary and `quantifiers` is true. + /// + /// The `pred` argument is a special predicate that will be skipped when + /// handling `src`, but it's arguments will be returned. + fn terms_of_pred_apps<'a>( + &mut self, + var_info: &VarInfos, + src: &'a PredApps, + tgt: &mut TTermSet, + pred: PrdIdx, + app_vars: &mut VarSet, + ) -> Res>> { + log! { @6 "terms_of_pred_apps" } + let mut res = None; + for (prd, argss) in src { + let prd = *prd; + if prd == pred { + res = Some(argss); + continue; + } + for args in argss { + match self.args_of_pred_app(var_info, args, app_vars)? { + TExtractRes::Success(nu_args) => { + tgt.insert_pred_app(prd, nu_args); + () + } + TExtractRes::Failed => { + log! { @6 "failed to extract argument {}", args } + return Ok(TExtractRes::Failed); + } + } + } + } + Ok(TExtractRes::Success(res)) + } + /// Applies a map to some terms, generating quantifiers if necessary and + /// `quantifiers` is true. + /// + /// Argument `even_if_disjoint` forces to add terms even if its variables + /// are disjoint from `app_vars`. + /// + /// Returns `true` if one of the `src` terms is false (think `is_trivial`). + fn terms_of_terms<'a, TermIter, Terms, F>( + &mut self, + var_info: &VarInfos, + src: Terms, + tgt: &mut TermSet, + app_vars: &mut VarSet, + even_if_disjoint: bool, + f: F, + ) -> Res> + where + TermIter: Iterator + ExactSizeIterator, + Terms: IntoIterator, + F: Fn(Term) -> Term, + { + log! { @4 "terms_of_terms" } + + // Finds terms which variables are related to the ones from the predicate + // applications. + let src = src.into_iter(); + + // The terms we're currently looking at. + let mut lhs_terms_vec: Vec<_> = Vec::with_capacity(src.len()); + for term in src { + match term.bool() { + Some(true) => (), + Some(false) => return Ok(TExtractRes::Success(true)), + _ => lhs_terms_vec.push(term), + } + } + // Terms which variables are disjoint from `app_vars` **for now**. This + // might change as we generate quantified variables. + let mut postponed: Vec<_> = Vec::with_capacity(lhs_terms_vec.len()); + + // A fixed point is reached when we go through the terms in `lhs_terms_vec` + // and don't generate quantified variables. + loop { + let mut fixed_point = true; + + for term in lhs_terms_vec.drain(0..) { + log! { @6 "{}", term.to_string_info(var_info) ? } + let vars = term::vars(term); + + if app_vars.len() == var_info.len() || vars.is_subset(&app_vars) { + let term = if let Some((term, _)) = term.subst_total(&self.map) { + term + } else { + bail!("[unreachable] failure during total substitution (1)") + }; + log! { @6 " sub {}", term } + tgt.insert(f(term)); + } else if !even_if_disjoint && vars.is_disjoint(&app_vars) { + log! { @6 " disjoint" } + postponed.push(term) + } else { + // The term mentions variables from `app_vars` and other variables. + // We generate quantified variables to account for them and + // invalidate `fixed_point` since terms that were previously disjoint + // might now intersect. + fixed_point = false; + let abort = self.add_qvars(var_info, app_vars, vars); + if abort { + return Ok(TExtractRes::Failed); + } + + let term = if let Some((term, _)) = term.subst_total(&self.map) { + term + } else { + bail!("[unreachable] failure during total substitution (2)") + }; + tgt.insert(f(term)); + } + } - /// Retrieves the internal quantified variables. - pub fn get_qvars(& mut self) -> VarHMap { - self.qvars.shrink_to_fit() ; - ::std::mem::replace(& mut self.qvars, VarHMap::with_capacity(11)) - } + if fixed_point || postponed.is_empty() { + break; + } else { + // Iterating over posponed terms next. + ::std::mem::swap(&mut lhs_terms_vec, &mut postponed) + } + } + Ok(TExtractRes::Success(false)) + } - fn terms_of_lhs_part<'a>( - & mut self, instance: & Instance, var_info: & VarInfos, - (lhs_terms, lhs_preds): (& TermSet, & 'a PredApps), - (pred, args): (PrdIdx, & VarTerms), - ) -> Res< - ExtractRes<( TTermSet, VarSet, Option<& 'a VarTermsSet> )> - > { - log!{ @5 "extracting application's terms" } + /// Given a predicate application, returns the constraints on the input and a + /// map from the variables used in the arguments to the variables of the + /// predicate. Also returns the set of variables appearing in the + /// application. + /// + /// # Assumptions + /// + /// - `self.map.is_empty()` + /// + /// # TODO + /// + /// - more doc with examples + pub fn terms_of_app( + &mut self, + var_info: &VarInfos, + instance: &Instance, + pred: PrdIdx, + args: &VarTerms, + ) -> Res> { + debug_assert! { self.map.is_empty() } + + let mut app_vars = VarSet::with_capacity(instance[pred].sig.len()); + let mut terms = TermSet::with_capacity(7); + + // Will store the arguments that are not a variable or a constant. + let mut postponed = Vec::with_capacity(args.len()); + + for (index, arg) in args.index_iter() { + if let Some(var) = arg.var_idx() { + let _ = app_vars.insert(var); + if let Some(pre) = self.map.insert(var, term::var(index, arg.typ())) { + terms.insert(term::eq(term::var(index, arg.typ()), pre)); + } + } else { + match arg.as_val().to_term() { + Some(trm) => { + debug_assert_eq! { trm.typ(), arg.typ() } + let var = term::var(index, trm.typ()); + terms.insert(term::eq(var, trm)); + } + None => postponed.push((index, arg)), + } + } + } - let (terms, mut app_vars) = if let Some(res) = self.terms_of_app( - var_info, instance, pred, args - ) ? { - res - } else { - log! { @5 "failed to extract terms of application" } - return Ok(ExtractRes::Failed) - } ; - - if_log! { @5 - log! { @5 => "terms:" } - for term in & terms { - log!{ @5 => "- {}", term } - } - log! { @5 => "map:" } - for (var, term) in & self.map { - log! { @5 => "- v_{} -> {}", var, term } - } - log! { @5 => - "working on lhs predicate applications ({})", lhs_preds.len() - } - } + for (var, arg) in postponed { + if let Some((term, _)) = arg.subst_total(&self.map) { + terms.insert(term::eq(term::var(var, arg.typ()), term)); + } else if let Some((v, inverted)) = arg.invert_var(var, arg.typ()) { + let _prev = self.map.insert(v, inverted); + debug_assert_eq!(_prev, None); + let is_new = app_vars.insert(v); + debug_assert!(is_new) + } else if let TExtractRes::Failed = self.terms_of_terms( + var_info, + Some(arg), + &mut terms, + &mut app_vars, + true, + |term| term::eq(term::var(var, term.typ()), term), + )? { + return Ok(None); + } + } - let mut tterms = TTermSet::of_terms(terms, lhs_preds.len()) ; - - // Predicate applications need to be in the resulting term. Depending on - // the definition they end up having, the constraint might be trivial. - let pred_argss = match self.terms_of_pred_apps( - var_info, lhs_preds, & mut tterms, pred, & mut app_vars - ) ? { - TExtractRes::Success(res) => res, - TExtractRes::Failed => { - log!{ @5 "qualifiers required for lhs pred apps, failing" } - return Ok( ExtractRes::Failed ) - }, - } ; - - log! { @5 "working on lhs terms ({})", lhs_terms.len() } - - if let TExtractRes::Success(trivial) = self.terms_of_terms( - var_info, lhs_terms, tterms.terms_mut(), & mut app_vars, false, identity - ) ? { - if trivial { return Ok( ExtractRes::Trivial ) } - } else { - log_debug! { " could not extract terms of terms" } - return Ok( ExtractRes::Failed ) + Ok(Some((terms, app_vars))) } - Ok( - ExtractRes::Success((tterms, app_vars, pred_argss)) - ) - } - - - - /// Returns the weakest predicate `p` such that `(p args) /\ lhs_terms /\ - /// {lhs_preds \ (p args)} => rhs`. - /// - /// Quantified variables are understood as universally quantified. - /// - /// The result is `(pred_app, pred_apps, terms)` which semantics is `pred_app - /// \/ (not /\ tterms) \/ (not /\ pred_apps)`. - pub fn terms_of_lhs_app( - & mut self, - quantifiers: bool, instance: & Instance, var_info: & VarInfos, - (lhs_terms, lhs_preds): (& TermSet, & PredApps), - rhs: Option<(PrdIdx, & VarTerms)>, - (pred, args): (PrdIdx, & VarTerms), - ) -> Res< - ExtractRes<(Quantfed, Option, TTermSet)> - > { - log!{ @4 - "terms_of_lhs_app on {} {} ({})", instance[pred], args, quantifiers + /// Retrieves the internal quantified variables. + pub fn get_qvars(&mut self) -> VarHMap { + self.qvars.shrink_to_fit(); + ::std::mem::replace(&mut self.qvars, VarHMap::with_capacity(11)) } - // Index of the first quantified variable: fresh for `pred`'s variables. - self.fresh = instance.original_sig_of(pred).next_index() ; - self.map.clear() ; - self.quantifiers = quantifiers ; - - // Predicate applications need to be in the resulting term. Depending on - // the definition they end up having, the constraint might be trivial. - let (tterms, mut app_vars) = match self.terms_of_lhs_part( - instance, var_info, (lhs_terms, lhs_preds), (pred, args) - ) ? { - - ExtractRes::Success( - (tterms, app_vars, pred_argss) - ) => match pred_argss.map( |argss| argss.len() ).unwrap_or(0) { - 1 => (tterms, app_vars), - len => bail!( - "illegal call to `terms_of_lhs_app`, \ - predicate {} is applied {} times, expected 1", - instance[pred], len - ) - }, - - ExtractRes::SuccessTrue => return Ok( - ExtractRes::SuccessTrue - ), - ExtractRes::SuccessFalse => return Ok( - ExtractRes::SuccessFalse - ), - - ExtractRes::Trivial => return Ok( - ExtractRes::Trivial - ), - - ExtractRes::Failed => { - log! { @5 - "could not extract terms of predicate app ({})", instance[pred] + fn terms_of_lhs_part<'a>( + &mut self, + instance: &Instance, + var_info: &VarInfos, + (lhs_terms, lhs_preds): (&TermSet, &'a PredApps), + (pred, args): (PrdIdx, &VarTerms), + ) -> Res)>> { + log!{ @5 "extracting application's terms" } + + let (terms, mut app_vars) = + if let Some(res) = self.terms_of_app(var_info, instance, pred, args)? { + res + } else { + log! { @5 "failed to extract terms of application" } + return Ok(ExtractRes::Failed); + }; + + if_log! { @5 + log! { @5 => "terms:" } + for term in & terms { + log!{ @5 => "- {}", term } + } + log! { @5 => "map:" } + for (var, term) in & self.map { + log! { @5 => "- v_{} -> {}", var, term } + } + log! { @5 => + "working on lhs predicate applications ({})", lhs_preds.len() + } } - return Ok( ExtractRes::Failed ) - }, - } ; - - let pred_app = if let Some((pred, args)) = rhs { - log! { @5 "working on rhs predicate application" } - if let TExtractRes::Success(nu_args) = self.args_of_pred_app( - var_info, args, & mut app_vars, - ) ? { - Some((pred, nu_args)) - } else { - log! { @5 "qualifiers required for rhs pred app, failing" } - return Ok( ExtractRes::Failed ) - } - } else { - log! { @5 "no rhs predicate application" } - None - } ; - debug_assert! { self.quantifiers || self.qvars.is_empty() } + let mut tterms = TTermSet::of_terms(terms, lhs_preds.len()); + + // Predicate applications need to be in the resulting term. Depending on + // the definition they end up having, the constraint might be trivial. + let pred_argss = + match self.terms_of_pred_apps(var_info, lhs_preds, &mut tterms, pred, &mut app_vars)? { + TExtractRes::Success(res) => res, + TExtractRes::Failed => { + log!{ @5 "qualifiers required for lhs pred apps, failing" } + return Ok(ExtractRes::Failed); + } + }; + + log! { @5 "working on lhs terms ({})", lhs_terms.len() } + + if let TExtractRes::Success(trivial) = self.terms_of_terms( + var_info, + lhs_terms, + tterms.terms_mut(), + &mut app_vars, + false, + identity, + )? { + if trivial { + return Ok(ExtractRes::Trivial); + } + } else { + log_debug! { " could not extract terms of terms" } + return Ok(ExtractRes::Failed); + } - if pred_app.is_none() && tterms.is_empty() { - Ok( ExtractRes::SuccessFalse ) - } else { - let qvars = self.get_qvars() ; - Ok( - ExtractRes::Success( (qvars, pred_app, tterms) ) - ) - } - } - - - /// Returns the weakest predicate `p` such that `lhs_terms /\ lhs_preds => (p - /// args)`. - /// - /// Quantified variables are understood as existentially quantified. - /// - /// The result is `(pred_apps, terms)` which semantics is `pred_app /\ - /// tterms`. - pub fn terms_of_rhs_app( - & mut self, - quantifiers: bool, instance: & Instance, var_info: & VarInfos, - lhs_terms: & TermSet, lhs_preds: & PredApps, - (pred, args): (PrdIdx, & VarTerms) - ) -> Res< ExtractRes<(Quantfed, TTermSet)> > { - log! { @4 - "terms of rhs app on {} {} ({})", instance[pred], args, self.quantifiers + Ok(ExtractRes::Success((tterms, app_vars, pred_argss))) } - // Index of the first quantified variable: fresh for `pred`'s variables. - self.fresh = instance.original_sig_of(pred).next_index() ; - self.map.clear() ; - self.quantifiers = quantifiers ; - - // Predicate applications need to be in the resulting term. Depending on - // the definition they end up having, the constraint might be trivial. - let tterms = match self.terms_of_lhs_part( - instance, var_info, (lhs_terms, lhs_preds), (pred, args) - ) ? { - - ExtractRes::Success( - (tterms, _, pred_argss) - ) => if pred_argss.map( |argss| argss.is_empty() ).unwrap_or(true) { - tterms - } else { - bail!( - "illegal call to `terms_of_rhs_app`, \ - predicate {} appears in the lhs", - instance[pred] - ) - }, - - ExtractRes::SuccessTrue => return Ok( - ExtractRes::SuccessTrue - ), - ExtractRes::SuccessFalse => return Ok( - ExtractRes::SuccessFalse - ), - - ExtractRes::Trivial => return Ok( - ExtractRes::Trivial - ), - - ExtractRes::Failed => { - log! { @5 - "could not extract terms of predicate app ({})", instance[pred] + /// Returns the weakest predicate `p` such that `(p args) /\ lhs_terms /\ + /// {lhs_preds \ (p args)} => rhs`. + /// + /// Quantified variables are understood as universally quantified. + /// + /// The result is `(pred_app, pred_apps, terms)` which semantics is `pred_app + /// \/ (not /\ tterms) \/ (not /\ pred_apps)`. + pub fn terms_of_lhs_app( + &mut self, + quantifiers: bool, + instance: &Instance, + var_info: &VarInfos, + (lhs_terms, lhs_preds): (&TermSet, &PredApps), + rhs: Option<(PrdIdx, &VarTerms)>, + (pred, args): (PrdIdx, &VarTerms), + ) -> Res, TTermSet)>> { + log!{ @4 + "terms_of_lhs_app on {} {} ({})", instance[pred], args, quantifiers } - return Ok( ExtractRes::Failed ) - }, - } ; - - debug_assert! { self.quantifiers || self.qvars.is_empty() } - - if tterms.is_empty() { - Ok(ExtractRes::SuccessTrue) - } else { - let qvars = self.get_qvars() ; - Ok( - ExtractRes::Success( (qvars, tterms) ) - ) - } - } -} + // Index of the first quantified variable: fresh for `pred`'s variables. + self.fresh = instance.original_sig_of(pred).next_index(); + self.map.clear(); + self.quantifiers = quantifiers; + + // Predicate applications need to be in the resulting term. Depending on + // the definition they end up having, the constraint might be trivial. + let (tterms, mut app_vars) = match self.terms_of_lhs_part( + instance, + var_info, + (lhs_terms, lhs_preds), + (pred, args), + )? { + ExtractRes::Success((tterms, app_vars, pred_argss)) => { + match pred_argss.map(|argss| argss.len()).unwrap_or(0) { + 1 => (tterms, app_vars), + len => bail!( + "illegal call to `terms_of_lhs_app`, \ + predicate {} is applied {} times, expected 1", + instance[pred], + len + ), + } + } + ExtractRes::SuccessTrue => return Ok(ExtractRes::SuccessTrue), + ExtractRes::SuccessFalse => return Ok(ExtractRes::SuccessFalse), + ExtractRes::Trivial => return Ok(ExtractRes::Trivial), -/// Results of term extraction. -pub enum TExtractRes { - /// Success. - Success(T), - /// Failure: need qualifiers. - Failed, -} + ExtractRes::Failed => { + log! { @5 + "could not extract terms of predicate app ({})", instance[pred] + } + return Ok(ExtractRes::Failed); + } + }; + + let pred_app = if let Some((pred, args)) = rhs { + log! { @5 "working on rhs predicate application" } + if let TExtractRes::Success(nu_args) = + self.args_of_pred_app(var_info, args, &mut app_vars)? + { + Some((pred, nu_args)) + } else { + log! { @5 "qualifiers required for rhs pred app, failing" } + return Ok(ExtractRes::Failed); + } + } else { + log! { @5 "no rhs predicate application" } + None + }; + debug_assert! { self.quantifiers || self.qvars.is_empty() } + if pred_app.is_none() && tterms.is_empty() { + Ok(ExtractRes::SuccessFalse) + } else { + let qvars = self.get_qvars(); + Ok(ExtractRes::Success((qvars, pred_app, tterms))) + } + } + /// Returns the weakest predicate `p` such that `lhs_terms /\ lhs_preds => (p + /// args)`. + /// + /// Quantified variables are understood as existentially quantified. + /// + /// The result is `(pred_apps, terms)` which semantics is `pred_app /\ + /// tterms`. + pub fn terms_of_rhs_app( + &mut self, + quantifiers: bool, + instance: &Instance, + var_info: &VarInfos, + lhs_terms: &TermSet, + lhs_preds: &PredApps, + (pred, args): (PrdIdx, &VarTerms), + ) -> Res> { + log! { @4 + "terms of rhs app on {} {} ({})", instance[pred], args, self.quantifiers + } + // Index of the first quantified variable: fresh for `pred`'s variables. + self.fresh = instance.original_sig_of(pred).next_index(); + self.map.clear(); + self.quantifiers = quantifiers; + + // Predicate applications need to be in the resulting term. Depending on + // the definition they end up having, the constraint might be trivial. + let tterms = match self.terms_of_lhs_part( + instance, + var_info, + (lhs_terms, lhs_preds), + (pred, args), + )? { + ExtractRes::Success((tterms, _, pred_argss)) => { + if pred_argss.map(|argss| argss.is_empty()).unwrap_or(true) { + tterms + } else { + bail!( + "illegal call to `terms_of_rhs_app`, \ + predicate {} appears in the lhs", + instance[pred] + ) + } + } + ExtractRes::SuccessTrue => return Ok(ExtractRes::SuccessTrue), + ExtractRes::SuccessFalse => return Ok(ExtractRes::SuccessFalse), + ExtractRes::Trivial => return Ok(ExtractRes::Trivial), + ExtractRes::Failed => { + log! { @5 + "could not extract terms of predicate app ({})", instance[pred] + } + return Ok(ExtractRes::Failed); + } + }; + debug_assert! { self.quantifiers || self.qvars.is_empty() } + if tterms.is_empty() { + Ok(ExtractRes::SuccessTrue) + } else { + let qvars = self.get_qvars(); + Ok(ExtractRes::Success((qvars, tterms))) + } + } +} +/// Results of term extraction. +pub enum TExtractRes { + /// Success. + Success(T), + /// Failure: need qualifiers. + Failed, +} /// Registers statistics of the original instance. /// /// Dumps the instance if asked to do so. -pub fn register_stats( - instance: & Instance, _profiler: & Profiler, count: usize -) -> Res<()> { - - preproc_dump!( +pub fn register_stats(instance: &Instance, _profiler: &Profiler, count: usize) -> Res<()> { + preproc_dump!( instance => format!("preproc_{:0>4}_original_instance", count), "Instance before pre-processing." - ) ? ; - profile!{ - |_profiler| - "clause count original" => add instance.clauses().len() - } - profile!{ - |_profiler| - "nl clause count original" => add { - let mut count = 0 ; - 'clause_iter: for clause in instance.clauses() { - for (_, argss) in clause.lhs_preds() { - if argss.len() > 1 { - count += 1 ; - continue 'clause_iter + )?; + profile!{ + |_profiler| + "clause count original" => add instance.clauses().len() + } + profile!{ + |_profiler| + "nl clause count original" => add { + let mut count = 0 ; + 'clause_iter: for clause in instance.clauses() { + for (_, argss) in clause.lhs_preds() { + if argss.len() > 1 { + count += 1 ; + continue 'clause_iter + } } } + count } - count - } - } - profile!{ - |_profiler| - "pred count original" => add { - let mut count = 0 ; - for pred in instance.pred_indices() { - if ! instance.is_known(pred) { - count += 1 + } + profile!{ + |_profiler| + "pred count original" => add { + let mut count = 0 ; + for pred in instance.pred_indices() { + if ! instance.is_known(pred) { + count += 1 + } } + count } - count - } - } - profile!{ - |_profiler| - "arg count original" => add { - let mut args = 0 ; - for info in instance.preds() { - if ! instance.is_known(info.idx) { - args += info.sig.len() + } + profile!{ + |_profiler| + "arg count original" => add { + let mut args = 0 ; + for info in instance.preds() { + if ! instance.is_known(info.idx) { + args += info.sig.len() + } } + args } - args - } - } + } - Ok(()) + Ok(()) } /// Registers some info for a preprocessor. pub fn register_info( - instance: & Instance, _profiler: & Profiler, - preproc: & 'static str, _red_info: & RedInfo, - count: usize, + instance: &Instance, + _profiler: &Profiler, + preproc: &'static str, + _red_info: &RedInfo, + count: usize, ) -> Res<()> { - preproc_dump!( + preproc_dump!( instance => format!("preproc_{:0>4}_{}", count, preproc), format!("Instance after running `{}`.", preproc) - ) ? ; - - profile!{ - |_profiler| format!( - "{:>10} pred red", preproc - ) => add _red_info.preds - } - profile!{ - |_profiler| format!( - "{:>10} clause red", preproc - ) => add _red_info.clauses_rmed - } - profile!{ - |_profiler| format!( - "{:>10} clause add", preproc - ) => add _red_info.clauses_added - } - profile!{ - |_profiler| format!( - "{:>10} arg red", preproc - ) => add _red_info.args_rmed - } - log! { @verb - "{}: {}", conf.emph( preproc ), _red_info - } - Ok(()) + )?; + + profile!{ + |_profiler| format!( + "{:>10} pred red", preproc + ) => add _red_info.preds + } + profile!{ + |_profiler| format!( + "{:>10} clause red", preproc + ) => add _red_info.clauses_rmed + } + profile!{ + |_profiler| format!( + "{:>10} clause add", preproc + ) => add _red_info.clauses_added + } + profile!{ + |_profiler| format!( + "{:>10} arg red", preproc + ) => add _red_info.args_rmed + } + log! { @verb + "{}: {}", conf.emph( preproc ), _red_info + } + Ok(()) } /// Registers the final info, after preprocessing. -pub fn register_final_stats( - instance: & Instance, _profiler: & Profiler -) -> Res<()> { - preproc_dump!( +pub fn register_final_stats(instance: &Instance, _profiler: &Profiler) -> Res<()> { + preproc_dump!( instance => "preproc_0000_fixed_point", "Instance after reaching preproc fixed-point." - ) ? ; - - profile!{ - |_profiler| - "clause count final" => add instance.clauses().len() - } - profile!{ - |_profiler| - "nl clause count final" => add { - let mut count = 0 ; - 'clause_iter: for clause in instance.clauses() { - for (_, argss) in clause.lhs_preds() { - if argss.len() > 1 { - count += 1 ; - continue 'clause_iter + )?; + + profile!{ + |_profiler| + "clause count final" => add instance.clauses().len() + } + profile!{ + |_profiler| + "nl clause count final" => add { + let mut count = 0 ; + 'clause_iter: for clause in instance.clauses() { + for (_, argss) in clause.lhs_preds() { + if argss.len() > 1 { + count += 1 ; + continue 'clause_iter + } } } + count } - count - } - } - - profile!{ - |_profiler| - "pred count final" => add { - let mut count = 0 ; - for pred in instance.pred_indices() { - if ! instance.is_known(pred) { - count += 1 + } + + profile!{ + |_profiler| + "pred count final" => add { + let mut count = 0 ; + for pred in instance.pred_indices() { + if ! instance.is_known(pred) { + count += 1 + } } + count } - count - } - } - - profile!{ - |_profiler| - "arg count final" => add { - let mut args = 0 ; - for info in instance.preds() { - if ! instance.is_known(info.idx) { - args += info.sig.len() + } + + profile!{ + |_profiler| + "arg count final" => add { + let mut args = 0 ; + for info in instance.preds() { + if ! instance.is_known(info.idx) { + args += info.sig.len() + } } + args } - args - } - } + } - Ok(()) + Ok(()) } - - - /// Processes the information generated after a preprocessor run. pub fn process_red_info( - instance: & Instance, _profiler: & Profiler, - preproc: & 'static str, count: & mut usize, red_info: & RedInfo + instance: &Instance, + _profiler: &Profiler, + preproc: &'static str, + count: &mut usize, + red_info: &RedInfo, ) -> Res<()> { - if red_info.non_zero() { - * count += 1 ; - register_info( - instance, _profiler, preproc, & red_info, * count - ) ? ; - conf.check_timeout() ? - } else { - log! { @verb "{}: did nothing", conf.emph(preproc) } - } - Ok(()) + if red_info.non_zero() { + *count += 1; + register_info(instance, _profiler, preproc, &red_info, *count)?; + conf.check_timeout()? + } else { + log! { @verb "{}: did nothing", conf.emph(preproc) } + } + Ok(()) } - /// Checks whether an instance is solved. /// /// Can return `Error::Unsat` if unsat, else returns a boolean indicating /// whether the instance is solved. -pub fn check_solved( - instance: & mut PreInstance, _profiler: & Profiler -) -> Res { - let res = if instance.is_solved() { - if ! instance.is_unsat() { - profile! { - |_profiler| tick "preproc", "final simplify" - } - // Check remaining clauses, maybe some of them are unsat. - match instance.simplify_all() { - Ok(_) => (), - Err(ref e) if e.is_unsat() => instance.set_unsat(), - Err(e) => bail!(e), - } - profile! { - |_profiler| mark "preproc", "final simplify" - } - } - true - } else { - false - } ; - Ok(res) +pub fn check_solved(instance: &mut PreInstance, _profiler: &Profiler) -> Res { + let res = if instance.is_solved() { + if !instance.is_unsat() { + profile! { + |_profiler| tick "preproc", "final simplify" + } + // Check remaining clauses, maybe some of them are unsat. + match instance.simplify_all() { + Ok(_) => (), + Err(ref e) if e.is_unsat() => instance.set_unsat(), + Err(e) => bail!(e), + } + profile! { + |_profiler| mark "preproc", "final simplify" + } + } + true + } else { + false + }; + Ok(res) } - /// Runs a technique, returns it's info. /// /// Returns `None` if the instance is solved. pub fn run_preproc( - instance: & mut PreInstance, _profiler: & Profiler, - preproc: & mut Strat, count: & mut usize, -) -> Res< Option > { - profile! { - |_profiler| tick "preproc", preproc.name() - } - log! { @verb - "running {}", conf.emph( preproc.name() ) - } - let red_info = preproc.apply(instance) ? ; - profile! { - |_profiler| mark "preproc", preproc.name() - } - - process_red_info( - instance, _profiler, preproc.name(), count, & red_info - ) ? ; - - if check_solved(instance, _profiler) ? { - Ok(None) - } else { - Ok( Some(red_info) ) - } + instance: &mut PreInstance, + _profiler: &Profiler, + preproc: &mut Strat, + count: &mut usize, +) -> Res> { + profile! { + |_profiler| tick "preproc", preproc.name() + } + log! { @verb + "running {}", conf.emph( preproc.name() ) + } + let red_info = preproc.apply(instance)?; + profile! { + |_profiler| mark "preproc", preproc.name() + } + + process_red_info(instance, _profiler, preproc.name(), count, &red_info)?; + + if check_solved(instance, _profiler)? { + Ok(None) + } else { + Ok(Some(red_info)) + } } diff --git a/src/learning/ice/data.rs b/src/learning/ice/data.rs index a712404b..82195631 100644 --- a/src/learning/ice/data.rs +++ b/src/learning/ice/data.rs @@ -1,652 +1,653 @@ //! Contains stuff related to ICE's projected data representation. -use common::* ; -use data::{ - Data, Sample, -} ; - +use common::*; +use data::{Data, Sample}; /// Projected data to classify. #[derive(Clone)] pub struct CData { - /// Positive samples. - pos: Vec, - /// Negative samples. - neg: Vec, - /// Unclassified samples. - unc: Vec, - /// Total number of samples. - len: f64, - /// Positive samples with a single known value. - pos_single: Vec, - /// Negative samples with a single known value. - neg_single: Vec, + /// Positive samples. + pos: Vec, + /// Negative samples. + neg: Vec, + /// Unclassified samples. + unc: Vec, + /// Total number of samples. + len: f64, + /// Positive samples with a single known value. + pos_single: Vec, + /// Negative samples with a single known value. + neg_single: Vec, } impl CData { - /// Constructor. - #[inline] - pub fn new( - pos: Vec, neg: Vec, unc: Vec, - pos_single: Vec, neg_single: Vec, - ) -> Self { - let len = ( - pos.len() + neg.len() + unc.len() - ) as f64 ; - CData { - pos, neg, unc, len, pos_single, neg_single + /// Constructor. + #[inline] + pub fn new( + pos: Vec, + neg: Vec, + unc: Vec, + pos_single: Vec, + neg_single: Vec, + ) -> Self { + let len = (pos.len() + neg.len() + unc.len()) as f64; + CData { + pos, + neg, + unc, + len, + pos_single, + neg_single, + } + } + + /// Number of samples. + #[inline] + pub fn len(&self) -> usize { + self.len as usize + } + + /// True if the data is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0. } - } - - /// Number of samples. - #[inline] - pub fn len(& self) -> usize { self.len as usize } - - /// True if the data is empty. - #[inline] - pub fn is_empty(& self) -> bool { self.len == 0. } - - /// Destroys the data. - #[inline] - pub fn destroy(self) -> (Vec, Vec, Vec) { - (self.pos, self.neg, self.unc) - } - - /// Pops a single sample. - pub fn pop_single_sample(& mut self) -> Option { - let res = self.pos_single.pop() ; - if res.is_some() { - res - } else { - self.neg_single.pop() + + /// Destroys the data. + #[inline] + pub fn destroy(self) -> (Vec, Vec, Vec) { + (self.pos, self.neg, self.unc) } - } - - /// Adds a positive sample. - #[inline] - pub fn add_pos(& mut self, pos: VarVals) { - self.len += 1. ; - self.pos.push(pos) - } - /// Positive samples. - #[inline] - pub fn pos(& self) -> & Vec { - & self.pos - } - - /// Adds a negative sample. - #[inline] - pub fn add_neg(& mut self, neg: VarVals) { - self.len += 1. ; - self.neg.push(neg) - } - /// Negative samples. - #[inline] - pub fn neg(& self) -> & Vec { - & self.neg - } - - /// Adds a unclassified sample. - #[inline] - pub fn add_unc(& mut self, unc: VarVals) { - self.len += 1. ; - self.unc.push(unc) - } - /// Unclassified samples. - #[inline] - pub fn unc(& self) -> & Vec { - & self.unc - } - - /// Classifies its unclassified elements. - /// - /// Function `f` will be applied to each element `e`, and it will be moved - /// - /// - to `pos` if `f(e) == Some(true)`, - /// - to `neg` if `f(e) == Some(false)`, - /// - nowhere if `f(e).is_some()`. - #[inline] - pub fn classify(& mut self, mut f: F) - where F: FnMut(& VarVals) -> Option { - let mut cnt = 0 ; - while cnt < self.unc.len() { - if let Some(pos) = f(& self.unc[cnt]) { - let sample = self.unc.swap_remove(cnt) ; - if pos { - self.add_pos(sample) + + /// Pops a single sample. + pub fn pop_single_sample(&mut self) -> Option { + let res = self.pos_single.pop(); + if res.is_some() { + res } else { - self.add_neg(sample) + self.neg_single.pop() } - } else { - cnt += 1 - } } - } - - /// Shannon entropy given the number of positive and negative samples. - fn shannon_entropy(pos: f64, neg: f64) -> f64 { - let den = pos + neg ; - if den == 0. { return 1. } - let (pos, neg) = (pos / den, neg / den) ; - let (pos, neg) = ( - if pos <= 0. { 0. } else { - ( pos * pos.log2() ) }, - if neg <= 0. { 0. } else { - ( neg * neg.log2() ) } - ) ; - pos + neg - } - - /// Shannon-entropy-based information gain of a qualifier (simple, ignores - /// unclassified data). - pub fn simple_gain( - & self, qual: & Trm, verb: bool - ) -> Res< Option > { - let my_entropy = Self::shannon_entropy( - self.pos.len() as f64, self.neg.len() as f64 - ) ; - let card = (self.pos.len() as f64) + (self.neg.len() as f64) ; - let ( - mut q_pos, mut q_neg, mut nq_pos, mut nq_neg - ) = (0., 0., 0., 0.) ; - let mut none = 0. ; - for pos in & self.pos { - match qual.evaluate( pos.get() ).chain_err( - || format!("while evaluating qualifier {} on {}", qual, pos) - ) ? { - Some(true) => q_pos += 1., - Some(false) => nq_pos += 1., - None => none += 1., - } + + /// Adds a positive sample. + #[inline] + pub fn add_pos(&mut self, pos: VarVals) { + self.len += 1.; + self.pos.push(pos) } - for neg in & self.neg { - match qual.evaluate( neg.get() ).chain_err( - || format!("while evaluating qualifier {} on {}", qual, neg) - ) ? { - Some(true) => q_neg += 1., - Some(false) => nq_neg += 1., - None => none += 1., - } + /// Positive samples. + #[inline] + pub fn pos(&self) -> &Vec { + &self.pos } - if q_pos + q_neg == 0. || nq_pos + nq_neg == 0. { - Ok( None ) - } else { - let (q_entropy, nq_entropy) = ( - Self::shannon_entropy( q_pos, q_neg), - Self::shannon_entropy(nq_pos, nq_neg) - ) ; - - if verb { - println!(" q_pos: {}", q_pos) ; - println!(" q_neg: {}", q_neg) ; - println!(" nq_pos: {}", nq_pos) ; - println!(" nq_neg: {}", nq_neg) ; - println!(" q_entropy: {}", q_entropy) ; - println!(" nq_entropy: {}", nq_entropy) ; - } - - // Entropy can be 0 because we're in simple gain, which ignores - // unclassified data. - let none_adjust = if self.pos.len() + self.neg.len() == 0 { - 0. - } else { - none / ( (self.pos.len() + self.neg.len()) as f64) - } ; - let gain = if my_entropy == 0. { - 0. - } else { - (1. - none_adjust) * ( - my_entropy - ( - ( (q_pos + q_neg) * q_entropy / card ) + - ( (nq_pos + nq_neg) * nq_entropy / card ) - ) - ) / my_entropy - } ; - if gain.is_nan() { - bail!( - format!( - "gain is NaN :( - my_entropy: {} - my_card: {} - q numerator: {} * {} = {} - nq numerator: {} * {} = {} - none adjust: {}", - my_entropy, self.len, - (q_pos + q_neg), - q_entropy, - (q_pos + q_neg) * q_entropy, - (nq_pos + nq_neg), - nq_entropy, - (nq_pos + nq_neg) * nq_entropy, - none_adjust - ) - ) - } - - Ok( Some(gain) ) + + /// Adds a negative sample. + #[inline] + pub fn add_neg(&mut self, neg: VarVals) { + self.len += 1.; + self.neg.push(neg) } - } - - - /// Modified entropy, uses [`EntropyBuilder`](struct.EntropyBuilder.html). - /// - /// Only takes into account unclassified data when `conf.ice.simple_gain` - /// is false. - pub fn entropy(& self, pred: PrdIdx, data: & Data) -> Res { - let mut proba = EntropyBuilder::new() ; - proba.set_pos_count( self.pos.len() ) ; - proba.set_neg_count( self.neg.len() ) ; - for unc in & self.unc { - proba.add_unc(data, pred, unc) ? + /// Negative samples. + #[inline] + pub fn neg(&self) -> &Vec { + &self.neg } - proba.entropy() - } - - /// Modified gain, uses `entropy`. - /// - /// Only takes into account unclassified data when `conf.ice.simple_gain` - /// is false. - pub fn gain( - & self, pred: PrdIdx, data: & Data, qual: & Trm, _profiler: & Profiler, - verb: bool - ) -> Res< Option > { - let my_entropy = self.entropy(pred, data) ? ; - - let (mut q_ent, mut nq_ent) = ( - EntropyBuilder::new(), EntropyBuilder::new() - ) ; - let ( - mut q_pos, mut q_neg, mut q_unc, mut nq_pos, mut nq_neg, mut nq_unc - ) = (0, 0, 0., 0, 0, 0.) ; - // Number of samples the qualifier does not differentiate. - let mut none = 0. ; - - profile! { |_profiler| tick "learning", "qual", "gain", "pos eval" } - for pos in & self.pos { - match qual.evaluate( pos.get() ).chain_err( - || format!("while evaluating qualifier {} on {}", qual, pos) - ) ? { - Some(true) => q_pos += 1, - Some(false) => nq_pos += 1, - None => none += 1., - } + + /// Adds a unclassified sample. + #[inline] + pub fn add_unc(&mut self, unc: VarVals) { + self.len += 1.; + self.unc.push(unc) } - q_ent.set_pos_count(q_pos) ; - nq_ent.set_pos_count(nq_pos) ; - profile! { |_profiler| mark "learning", "qual", "gain", "pos eval" } - - profile! { |_profiler| tick "learning", "qual", "gain", "neg eval" } - for neg in & self.neg { - match qual.evaluate( neg.get() ).chain_err( - || format!("while evaluating qualifier {} on {}", qual, neg) - ) ? { - Some(true) => q_neg += 1, - Some(false) => nq_neg += 1, - None => none += 1., - } + /// Unclassified samples. + #[inline] + pub fn unc(&self) -> &Vec { + &self.unc } - q_ent.set_neg_count(q_neg) ; - nq_ent.set_neg_count(nq_neg) ; - profile! { |_profiler| mark "learning", "qual", "gain", "neg eval" } - - if ( - q_pos == 0 && nq_pos > 0 && - q_neg > 0 && nq_neg == 0 - ) || ( - q_pos > 0 && nq_pos == 0 && - q_neg == 0 && nq_neg > 0 - ) { - return Ok( Some(1.0) ) + + /// Classifies its unclassified elements. + /// + /// Function `f` will be applied to each element `e`, and it will be moved + /// + /// - to `pos` if `f(e) == Some(true)`, + /// - to `neg` if `f(e) == Some(false)`, + /// - nowhere if `f(e).is_some()`. + #[inline] + pub fn classify(&mut self, mut f: F) + where + F: FnMut(&VarVals) -> Option, + { + let mut cnt = 0; + while cnt < self.unc.len() { + if let Some(pos) = f(&self.unc[cnt]) { + let sample = self.unc.swap_remove(cnt); + if pos { + self.add_pos(sample) + } else { + self.add_neg(sample) + } + } else { + cnt += 1 + } + } } - profile! { |_profiler| tick "learning", "qual", "gain", "unc eval" } - // println!("{}", qual) ; - for unc in & self.unc { - // println!(" {}", unc) ; - match qual.evaluate( unc.get() ).chain_err( - || format!("while evaluating qualifier {} on {}", qual, unc) - ) ? { - Some(true) => { - q_unc += 1. ; - q_ent.add_unc(data, pred, unc) ? - }, - Some(false) => { - nq_unc += 1. ; - nq_ent.add_unc(data, pred, unc) ? - }, - None => (), - } + /// Shannon entropy given the number of positive and negative samples. + fn shannon_entropy(pos: f64, neg: f64) -> f64 { + let den = pos + neg; + if den == 0. { + return 1.; + } + let (pos, neg) = (pos / den, neg / den); + let (pos, neg) = ( + if pos <= 0. { 0. } else { -(pos * pos.log2()) }, + if neg <= 0. { 0. } else { -(neg * neg.log2()) }, + ); + pos + neg } - profile! { |_profiler| mark "learning", "qual", "gain", "unc eval" } + /// Shannon-entropy-based information gain of a qualifier (simple, ignores + /// unclassified data). + pub fn simple_gain(&self, qual: &Trm, verb: bool) -> Res> { + let my_entropy = Self::shannon_entropy(self.pos.len() as f64, self.neg.len() as f64); + let card = (self.pos.len() as f64) + (self.neg.len() as f64); + let (mut q_pos, mut q_neg, mut nq_pos, mut nq_neg) = (0., 0., 0., 0.); + let mut none = 0.; + for pos in &self.pos { + match qual + .evaluate(pos.get()) + .chain_err(|| format!("while evaluating qualifier {} on {}", qual, pos))? + { + Some(true) => q_pos += 1., + Some(false) => nq_pos += 1., + None => none += 1., + } + } + for neg in &self.neg { + match qual + .evaluate(neg.get()) + .chain_err(|| format!("while evaluating qualifier {} on {}", qual, neg))? + { + Some(true) => q_neg += 1., + Some(false) => nq_neg += 1., + None => none += 1., + } + } + if q_pos + q_neg == 0. || nq_pos + nq_neg == 0. { + Ok(None) + } else { + let (q_entropy, nq_entropy) = ( + Self::shannon_entropy(q_pos, q_neg), + Self::shannon_entropy(nq_pos, nq_neg), + ); + + if verb { + println!(" q_pos: {}", q_pos); + println!(" q_neg: {}", q_neg); + println!(" nq_pos: {}", nq_pos); + println!(" nq_neg: {}", nq_neg); + println!(" q_entropy: {}", q_entropy); + println!(" nq_entropy: {}", nq_entropy); + } - profile! { |_profiler| tick "learning", "qual", "gain", "rest" } + // Entropy can be 0 because we're in simple gain, which ignores + // unclassified data. + let none_adjust = if self.pos.len() + self.neg.len() == 0 { + 0. + } else { + none / ((self.pos.len() + self.neg.len()) as f64) + }; + let gain = if my_entropy == 0. { + 0. + } else { + (1. - none_adjust) + * (my_entropy + - (((q_pos + q_neg) * q_entropy / card) + + ((nq_pos + nq_neg) * nq_entropy / card))) + / my_entropy + }; + if gain.is_nan() { + bail!(format!( + "gain is NaN :( + my_entropy: {} + my_card: {} + q numerator: {} * {} = {} + nq numerator: {} * {} = {} + none adjust: {}", + my_entropy, + self.len, + (q_pos + q_neg), + q_entropy, + (q_pos + q_neg) * q_entropy, + (nq_pos + nq_neg), + nq_entropy, + (nq_pos + nq_neg) * nq_entropy, + none_adjust + )) + } - let (q_pos, q_neg, nq_pos, nq_neg) = ( - q_pos as f64, q_neg as f64, nq_pos as f64, nq_neg as f64 - ) ; + Ok(Some(gain)) + } + } - if verb { - println!(" q_pos: {}", q_pos) ; - println!(" q_neg: {}", q_neg) ; - println!(" q_unc: {}", q_unc) ; - println!(" nq_pos: {}", nq_pos) ; - println!(" nq_neg: {}", nq_neg) ; - println!(" nq_unc: {}", nq_unc) ; + /// Modified entropy, uses [`EntropyBuilder`](struct.EntropyBuilder.html). + /// + /// Only takes into account unclassified data when `conf.ice.simple_gain` + /// is false. + pub fn entropy(&self, pred: PrdIdx, data: &Data) -> Res { + let mut proba = EntropyBuilder::new(); + proba.set_pos_count(self.pos.len()); + proba.set_neg_count(self.neg.len()); + for unc in &self.unc { + proba.add_unc(data, pred, unc)? + } + proba.entropy() } - let q_sum = q_pos + q_neg + q_unc ; - let nq_sum = nq_pos + nq_neg + nq_unc ; + /// Modified gain, uses `entropy`. + /// + /// Only takes into account unclassified data when `conf.ice.simple_gain` + /// is false. + pub fn gain( + &self, + pred: PrdIdx, + data: &Data, + qual: &Trm, + _profiler: &Profiler, + verb: bool, + ) -> Res> { + let my_entropy = self.entropy(pred, data)?; + + let (mut q_ent, mut nq_ent) = (EntropyBuilder::new(), EntropyBuilder::new()); + let (mut q_pos, mut q_neg, mut q_unc, mut nq_pos, mut nq_neg, mut nq_unc) = + (0, 0, 0., 0, 0, 0.); + // Number of samples the qualifier does not differentiate. + let mut none = 0.; + + profile! { |_profiler| tick "learning", "qual", "gain", "pos eval" } + for pos in &self.pos { + match qual + .evaluate(pos.get()) + .chain_err(|| format!("while evaluating qualifier {} on {}", qual, pos))? + { + Some(true) => q_pos += 1, + Some(false) => nq_pos += 1, + None => none += 1., + } + } + q_ent.set_pos_count(q_pos); + nq_ent.set_pos_count(nq_pos); + profile! { |_profiler| mark "learning", "qual", "gain", "pos eval" } + + profile! { |_profiler| tick "learning", "qual", "gain", "neg eval" } + for neg in &self.neg { + match qual + .evaluate(neg.get()) + .chain_err(|| format!("while evaluating qualifier {} on {}", qual, neg))? + { + Some(true) => q_neg += 1, + Some(false) => nq_neg += 1, + None => none += 1., + } + } + q_ent.set_neg_count(q_neg); + nq_ent.set_neg_count(nq_neg); + profile! { |_profiler| mark "learning", "qual", "gain", "neg eval" } + + if (q_pos == 0 && nq_pos > 0 && q_neg > 0 && nq_neg == 0) + || (q_pos > 0 && nq_pos == 0 && q_neg == 0 && nq_neg > 0) + { + return Ok(Some(1.0)); + } + + profile! { |_profiler| tick "learning", "qual", "gain", "unc eval" } + // println!("{}", qual) ; + for unc in &self.unc { + // println!(" {}", unc) ; + match qual + .evaluate(unc.get()) + .chain_err(|| format!("while evaluating qualifier {} on {}", qual, unc))? + { + Some(true) => { + q_unc += 1.; + q_ent.add_unc(data, pred, unc)? + } + Some(false) => { + nq_unc += 1.; + nq_ent.add_unc(data, pred, unc)? + } + None => (), + } + } + profile! { |_profiler| mark "learning", "qual", "gain", "unc eval" } + + profile! { |_profiler| tick "learning", "qual", "gain", "rest" } - // Is this qualifier separating anything? - if q_sum == 0. || nq_sum == 0. { - profile! { |_profiler| mark "learning", "qual", "gain", "rest" } - return Ok(None) - } + let (q_pos, q_neg, nq_pos, nq_neg) = + (q_pos as f64, q_neg as f64, nq_pos as f64, nq_neg as f64); - let (q_entropy, nq_entropy) = ( - q_ent.entropy() ?, nq_ent.entropy() ? - ) ; - if verb { - println!(" q_entropy: {}", q_entropy) ; - println!(" nq_entropy: {}", nq_entropy) ; - } + if verb { + println!(" q_pos: {}", q_pos); + println!(" q_neg: {}", q_neg); + println!(" q_unc: {}", q_unc); + println!(" nq_pos: {}", nq_pos); + println!(" nq_neg: {}", nq_neg); + println!(" nq_unc: {}", nq_unc); + } + + let q_sum = q_pos + q_neg + q_unc; + let nq_sum = nq_pos + nq_neg + nq_unc; - let none_adjust = if self.pos.len() + self.neg.len() == 0 { - 0. - } else { - none / ( (self.pos.len() + self.neg.len()) as f64) - } ; - let gain = (1. - none_adjust) * ( - my_entropy - ( - q_sum * q_entropy / self.len + - nq_sum * nq_entropy / self.len - ) - ) / my_entropy ; - - if gain.is_nan() { - bail!( - format!( - "gain is NaN :( + // Is this qualifier separating anything? + if q_sum == 0. || nq_sum == 0. { + profile! { |_profiler| mark "learning", "qual", "gain", "rest" } + return Ok(None); + } + + let (q_entropy, nq_entropy) = (q_ent.entropy()?, nq_ent.entropy()?); + if verb { + println!(" q_entropy: {}", q_entropy); + println!(" nq_entropy: {}", nq_entropy); + } + + let none_adjust = if self.pos.len() + self.neg.len() == 0 { + 0. + } else { + none / ((self.pos.len() + self.neg.len()) as f64) + }; + let gain = (1. - none_adjust) + * (my_entropy - (q_sum * q_entropy / self.len + nq_sum * nq_entropy / self.len)) + / my_entropy; + + if gain.is_nan() { + bail!(format!( + "gain is NaN :( my_entropy: {} my_card: {} none: {} q numerator: {} * {} = {} nq numerator: {} * {} = {} none_adjust: {}", - my_entropy, self.len, none, - q_sum, - q_entropy, - q_sum * q_entropy, - nq_sum, - nq_entropy, - nq_sum * nq_entropy, - none_adjust - ) - ) - } - profile! { |_profiler| mark "learning", "qual", "gain", "rest" } - - Ok( Some(gain) ) - } - - /// Splits the data given some qualifier. First is the data for which the - /// qualifier is true. - pub fn split(self, qual: & Term) -> (Self, Self) { - let (mut q, mut nq) = ( - CData::new( - Vec::with_capacity( self.pos.len() ), - Vec::with_capacity( self.neg.len() ), - Vec::with_capacity( self.unc.len() ), - Vec::with_capacity( self.pos_single.len() ), - Vec::with_capacity( self.neg_single.len() ), - ), - CData::new( - Vec::with_capacity( self.pos.len() ), - Vec::with_capacity( self.neg.len() ), - Vec::with_capacity( self.unc.len() ), - Vec::with_capacity( self.pos_single.len() ), - Vec::with_capacity( self.neg_single.len() ), - ) - ) ; - - for pos_single in self.pos_single { - if let Some(value) = qual.bool_eval( pos_single.get() ).expect( - "During qualifier evaluation" - ) { - if value { - q.pos_single.push(pos_single) - } else { - nq.pos_single.push(pos_single) + my_entropy, + self.len, + none, + q_sum, + q_entropy, + q_sum * q_entropy, + nq_sum, + nq_entropy, + nq_sum * nq_entropy, + none_adjust + )) } - } else { - q.pos_single.push( pos_single.clone() ) ; - nq.pos_single.push(pos_single) - } + profile! { |_profiler| mark "learning", "qual", "gain", "rest" } + + Ok(Some(gain)) } - for neg_single in self.neg_single { - if let Some(value) = qual.bool_eval( neg_single.get() ).expect( - "During qualifier evaluation" - ) { - if value { - q.neg_single.push(neg_single) - } else { - nq.neg_single.push(neg_single) + /// Splits the data given some qualifier. First is the data for which the + /// qualifier is true. + pub fn split(self, qual: &Term) -> (Self, Self) { + let (mut q, mut nq) = ( + CData::new( + Vec::with_capacity(self.pos.len()), + Vec::with_capacity(self.neg.len()), + Vec::with_capacity(self.unc.len()), + Vec::with_capacity(self.pos_single.len()), + Vec::with_capacity(self.neg_single.len()), + ), + CData::new( + Vec::with_capacity(self.pos.len()), + Vec::with_capacity(self.neg.len()), + Vec::with_capacity(self.unc.len()), + Vec::with_capacity(self.pos_single.len()), + Vec::with_capacity(self.neg_single.len()), + ), + ); + + for pos_single in self.pos_single { + if let Some(value) = qual + .bool_eval(pos_single.get()) + .expect("During qualifier evaluation") + { + if value { + q.pos_single.push(pos_single) + } else { + nq.pos_single.push(pos_single) + } + } else { + q.pos_single.push(pos_single.clone()); + nq.pos_single.push(pos_single) + } } - } else { - q.neg_single.push( neg_single.clone() ) ; - nq.neg_single.push(neg_single) - } - } - for pos in self.pos { - if let Some(value) = qual.bool_eval( pos.get() ).expect( - "During qualifier evaluation" - ) { - if value { - q.add_pos( pos ) - } else { - nq.add_pos( pos ) + for neg_single in self.neg_single { + if let Some(value) = qual + .bool_eval(neg_single.get()) + .expect("During qualifier evaluation") + { + if value { + q.neg_single.push(neg_single) + } else { + nq.neg_single.push(neg_single) + } + } else { + q.neg_single.push(neg_single.clone()); + nq.neg_single.push(neg_single) + } } - } else { - q.add_pos( pos.clone() ) ; - nq.add_pos( pos ) - } - } - for neg in self.neg { - if let Some(value) = qual.bool_eval( neg.get() ).expect( - "During qualifier evaluation" - ) { - if value { - q.add_neg( neg ) - } else { - nq.add_neg( neg ) + for pos in self.pos { + if let Some(value) = qual + .bool_eval(pos.get()) + .expect("During qualifier evaluation") + { + if value { + q.add_pos(pos) + } else { + nq.add_pos(pos) + } + } else { + q.add_pos(pos.clone()); + nq.add_pos(pos) + } } - } else { - q.add_neg( neg.clone() ) ; - nq.add_neg( neg ) - } - } - for unc in self.unc { - if let Some(value) = qual.bool_eval( unc.get() ).expect( - "During qualifier evaluation" - ) { - if value { - q.add_unc( unc ) - } else { - nq.add_unc( unc ) + for neg in self.neg { + if let Some(value) = qual + .bool_eval(neg.get()) + .expect("During qualifier evaluation") + { + if value { + q.add_neg(neg) + } else { + nq.add_neg(neg) + } + } else { + q.add_neg(neg.clone()); + nq.add_neg(neg) + } + } + + for unc in self.unc { + if let Some(value) = qual + .bool_eval(unc.get()) + .expect("During qualifier evaluation") + { + if value { + q.add_unc(unc) + } else { + nq.add_unc(unc) + } + } else { + q.add_unc(unc.clone()); + nq.add_unc(unc) + } } - } else { - q.add_unc( unc.clone() ) ; - nq.add_unc( unc ) - } + + q.pos.shrink_to_fit(); + q.neg.shrink_to_fit(); + q.unc.shrink_to_fit(); + q.pos_single.shrink_to_fit(); + q.neg_single.shrink_to_fit(); + + nq.pos.shrink_to_fit(); + nq.neg.shrink_to_fit(); + nq.unc.shrink_to_fit(); + nq.pos_single.shrink_to_fit(); + nq.neg_single.shrink_to_fit(); + + (q, nq) } - q.pos.shrink_to_fit() ; - q.neg.shrink_to_fit() ; - q.unc.shrink_to_fit() ; - q.pos_single.shrink_to_fit() ; - q.neg_single.shrink_to_fit() ; - - nq.pos.shrink_to_fit() ; - nq.neg.shrink_to_fit() ; - nq.unc.shrink_to_fit() ; - nq.pos_single.shrink_to_fit() ; - nq.neg_single.shrink_to_fit() ; - - (q, nq) - } - - /// Iterator over some data. - pub fn iter(& self, include_unc: bool) -> CDataIter { - CDataIter { - pos: self.pos.iter(), - neg: self.neg.iter(), - unc: if include_unc { Some(self.unc.iter()) } else { None }, + /// Iterator over some data. + pub fn iter(&self, include_unc: bool) -> CDataIter { + CDataIter { + pos: self.pos.iter(), + neg: self.neg.iter(), + unc: if include_unc { + Some(self.unc.iter()) + } else { + None + }, + } } - } } /// Iterator over CData. pub struct CDataIter<'a> { - pos: ::std::slice::Iter<'a, VarVals>, - neg: ::std::slice::Iter<'a, VarVals>, - unc: Option<::std::slice::Iter<'a, VarVals>>, + pos: ::std::slice::Iter<'a, VarVals>, + neg: ::std::slice::Iter<'a, VarVals>, + unc: Option<::std::slice::Iter<'a, VarVals>>, } impl<'a> ::std::iter::Iterator for CDataIter<'a> { - type Item = & 'a VarVals ; - fn next(& mut self) -> Option { - let next = self.pos.next() ; - if next.is_some() { return next } - let next = self.neg.next() ; - if next.is_some() { return next } - if let Some(unc) = self.unc.as_mut() { - unc.next() - } else { - None + type Item = &'a VarVals; + fn next(&mut self) -> Option { + let next = self.pos.next(); + if next.is_some() { + return next; + } + let next = self.neg.next(); + if next.is_some() { + return next; + } + if let Some(unc) = self.unc.as_mut() { + unc.next() + } else { + None + } } - } } - - /// Wrapper around an `f64` used to compute an approximation of the ratio /// between legal positive classifications and negative ones, without actually /// splitting the data. /// /// See the paper for more details. #[derive(Default)] -pub struct EntropyBuilder { num: f64, den: usize } +pub struct EntropyBuilder { + num: f64, + den: usize, +} impl EntropyBuilder { - /// Constructor. - pub fn new() -> Self { - EntropyBuilder { num: 0., den: 0 } - } - - /// Sets the number of positive samples. - pub fn set_pos_count(& mut self, pos: usize) { - self.num += pos as f64 ; - self.den += pos - } - /// Sets the number of negative samples. - pub fn set_neg_count(& mut self, neg: usize) { - self.den += neg - } - - /// Adds the degree of an unclassified example. - pub fn add_unc( - & mut self, data: & Data, prd: PrdIdx, sample: & VarVals - ) -> Res<()> { - let degree = Self::degree(data, prd, sample) ? ; - self.den += 1 ; - self.num += (1. / 2.) + ( - degree - ).atan() / ::std::f64::consts::PI ; - Ok(()) - } - - /// Probability stored in the builder. - pub fn proba(& self) -> f64 { - self.num / (self.den as f64) - } - - /// Destroys the builder and returns the entropy. - pub fn entropy(self) -> Res { - let proba = self.proba() ; - let (pos, neg) = ( - if proba.abs() < ::std::f64::EPSILON { 0. } else { - proba * proba.log2() - }, - if (proba - 1.).abs() < ::std::f64::EPSILON { 0. } else { - (1. - proba) * (1. - proba).log2() - } - ) ; - let res = - pos - neg ; - if res.is_nan() { - bail!( - format!( - "entropy is NaN :( + /// Constructor. + pub fn new() -> Self { + EntropyBuilder { num: 0., den: 0 } + } + + /// Sets the number of positive samples. + pub fn set_pos_count(&mut self, pos: usize) { + self.num += pos as f64; + self.den += pos + } + /// Sets the number of negative samples. + pub fn set_neg_count(&mut self, neg: usize) { + self.den += neg + } + + /// Adds the degree of an unclassified example. + pub fn add_unc(&mut self, data: &Data, prd: PrdIdx, sample: &VarVals) -> Res<()> { + let degree = Self::degree(data, prd, sample)?; + self.den += 1; + self.num += (1. / 2.) + (degree).atan() / ::std::f64::consts::PI; + Ok(()) + } + + /// Probability stored in the builder. + pub fn proba(&self) -> f64 { + self.num / (self.den as f64) + } + + /// Destroys the builder and returns the entropy. + pub fn entropy(self) -> Res { + let proba = self.proba(); + let (pos, neg) = ( + if proba.abs() < ::std::f64::EPSILON { + 0. + } else { + proba * proba.log2() + }, + if (proba - 1.).abs() < ::std::f64::EPSILON { + 0. + } else { + (1. - proba) * (1. - proba).log2() + }, + ); + let res = -pos - neg; + if res.is_nan() { + bail!(format!( + "entropy is NaN :( num : {} den : {} proba: {} pos : {} - neg : {}", self.num, self.den, proba, pos, neg - ) - ) + neg : {}", + self.num, self.den, proba, pos, neg + )) + } + Ok(res) } - Ok(res) - } - - /// Degree of a sample, refer to the paper for details. - pub fn degree( - data: & Data, prd: PrdIdx, sample: & VarVals - ) -> Res { - let ( - mut sum_imp_rhs, - mut sum_imp_lhs, - mut sum_neg, - ) = (0., 0., 0.) ; - - if let Some(constraints) = data.map()[prd].get(& sample) { - for constraint in constraints { - let constraint = & data.constraints[* constraint] ; - - let lhs = if let Some(lhs) = constraint.lhs() { - lhs - } else { - bail!("computing degree of trivial clause") - } ; - - let lhs_len = constraint.lhs_len() ; - debug_assert! { lhs_len > 0 } - - match constraint.rhs() { - None => sum_neg += 1. / (lhs_len as f64), - Some( & Sample { pred, ref args } ) - if pred == prd - && args == sample => sum_imp_rhs += 1. / ( - 1. + (lhs_len as f64) - ), - _ => { - debug_assert! { - lhs.iter().fold( - false, - |b, (pred, samples)| b || samples.iter().fold( - b, |b, s| b || ( - * pred == prd && s == sample - ) - ) - ) + + /// Degree of a sample, refer to the paper for details. + pub fn degree(data: &Data, prd: PrdIdx, sample: &VarVals) -> Res { + let (mut sum_imp_rhs, mut sum_imp_lhs, mut sum_neg) = (0., 0., 0.); + + if let Some(constraints) = data.map()[prd].get(&sample) { + for constraint in constraints { + let constraint = &data.constraints[*constraint]; + + let lhs = if let Some(lhs) = constraint.lhs() { + lhs + } else { + bail!("computing degree of trivial clause") + }; + + let lhs_len = constraint.lhs_len(); + debug_assert! { lhs_len > 0 } + + match constraint.rhs() { + None => sum_neg += 1. / (lhs_len as f64), + Some(&Sample { pred, ref args }) if pred == prd && args == sample => { + sum_imp_rhs += 1. / (1. + (lhs_len as f64)) + } + _ => { + debug_assert! { + lhs.iter().fold( + false, + |b, (pred, samples)| b || samples.iter().fold( + b, |b, s| b || ( + * pred == prd && s == sample + ) + ) + ) + } + sum_imp_lhs += 1. / (1. + (lhs_len as f64)) + } + } } - sum_imp_lhs += 1. / ( 1. + (lhs_len as f64) ) - }, } - } - } - let res = sum_imp_rhs - sum_imp_lhs - sum_neg ; + let res = sum_imp_rhs - sum_imp_lhs - sum_neg; - Ok(res) - } + Ok(res) + } } - diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index 8d365b64..0bde5503 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -1,417 +1,372 @@ //! ICE learner. use common::{ - *, - msg::*, - smt::{ - SmtSample, SmtConstraint, SmtActSamples - }, - var_to::vals::VarValsMap, -} ; -use data::Data ; + msg::*, + smt::{SmtActSamples, SmtConstraint, SmtSample}, + var_to::vals::VarValsMap, + *, +}; +use data::Data; -pub mod quals ; -pub mod synth ; -pub mod data ; - -use self::quals::NuQuals ; -use self::data::CData ; -use self::synth::SynthSys ; +pub mod data; +pub mod quals; +pub mod synth; +use self::data::CData; +use self::quals::NuQuals; +use self::synth::SynthSys; /// Launcher. -pub struct Launcher ; +pub struct Launcher; unsafe impl Sync for Launcher {} unsafe impl Send for Launcher {} impl Launcher { - /// Launches an smt learner. - pub fn launch( - core: & MsgCore, instance: Arc, data: Data, mine: bool - ) -> Res<()> { - let mut learner = IceLearner::new( - & core, instance, data, mine - ).chain_err( - || "while creating ice learner" - ) ? ; - let res = learner.run() ; - learner.finalize() ? ; - res - } + /// Launches an smt learner. + pub fn launch(core: &MsgCore, instance: Arc, data: Data, mine: bool) -> Res<()> { + let mut learner = IceLearner::new(&core, instance, data, mine) + .chain_err(|| "while creating ice learner")?; + let res = learner.run(); + learner.finalize()?; + res + } } impl Learner for Launcher { - fn run( - & self, core: MsgCore, - instance: Arc, data: Data, - mine: bool - ) { - match Self::launch(& core, instance, data, mine) { - Ok(()) => core.exit(), - Err(e) => core.err(e), + fn run(&self, core: MsgCore, instance: Arc, data: Data, mine: bool) { + match Self::launch(&core, instance, data, mine) { + Ok(()) => core.exit(), + Err(e) => core.err(e), + } + } + fn description(&self, mine: bool) -> String { + format!("ice{}", if mine { "" } else { " pure synth" }) } - } - fn description(& self, mine: bool) -> String { - format!("ice{}", if mine { "" } else { " pure synth" }) - } } - /// A branch of a decision tree. /// /// Boolean is `false` if the term should be negated. -pub type Branch = Vec<(Term, bool)> ; - +pub type Branch = Vec<(Term, bool)>; /// Ice learner. pub struct IceLearner<'core> { - /// Arc to the instance. - pub instance: Arc, - /// Qualifiers for the predicates. - pub qualifiers: NuQuals, - /// Synthesizer. - synth_sys: PrdMap, - /// Current data. - data: Data, - /// Solver used to check if the constraints are respected. - solver: Solver<()>, - /// Learner core. - core: & 'core MsgCore, - /// Branches of the tree, used when constructing a decision tree. - finished: Vec, - /// Branches to construct later, used when constructing a decision tree. - unfinished: Vec< (Branch, CData) >, - /// Classifier for constraint data. - classifier: VarValsMap, - /// Declaration memory: used when declaring samples in the solver to - /// remember what's already declared. The `u64` is the sample's uid. - dec_mem: PrdMap< HashSet >, - /// Current candidate, cleared at the beginning of each learning phase. - candidate: PrdMap< Option >, - /// Vector used during learning, avoids re-allocation. - predicates: Vec<(usize, usize, PrdIdx)>, - /// Rng to decide when to sort predicates. - sort_rng_1: Rng, - /// Rng actually doing the predicate sorting. - sort_rng_2: Rng, - /// Rng to decide when to use simple gain. - simple_rng: Rng, - /// Rng to decide when skip preliminary. - pre_skip_rng: Rng, - /// Luby counter for restarts. - luby: Option, - /// Known qualifiers, factored for no reallocation. Used by synthesis. - known_quals: TermSet, - /// Gain pivot. - gain_pivot: f64, - /// Gain pivot synth. - gain_pivot_synth: Option, - /// Learn step counter. - count: usize, + /// Arc to the instance. + pub instance: Arc, + /// Qualifiers for the predicates. + pub qualifiers: NuQuals, + /// Synthesizer. + synth_sys: PrdMap, + /// Current data. + data: Data, + /// Solver used to check if the constraints are respected. + solver: Solver<()>, + /// Learner core. + core: &'core MsgCore, + /// Branches of the tree, used when constructing a decision tree. + finished: Vec, + /// Branches to construct later, used when constructing a decision tree. + unfinished: Vec<(Branch, CData)>, + /// Classifier for constraint data. + classifier: VarValsMap, + /// Declaration memory: used when declaring samples in the solver to + /// remember what's already declared. The `u64` is the sample's uid. + dec_mem: PrdMap>, + /// Current candidate, cleared at the beginning of each learning phase. + candidate: PrdMap>, + /// Vector used during learning, avoids re-allocation. + predicates: Vec<(usize, usize, PrdIdx)>, + /// Rng to decide when to sort predicates. + sort_rng_1: Rng, + /// Rng actually doing the predicate sorting. + sort_rng_2: Rng, + /// Rng to decide when to use simple gain. + simple_rng: Rng, + /// Rng to decide when skip preliminary. + pre_skip_rng: Rng, + /// Luby counter for restarts. + luby: Option, + /// Known qualifiers, factored for no reallocation. Used by synthesis. + known_quals: TermSet, + /// Gain pivot. + gain_pivot: f64, + /// Gain pivot synth. + gain_pivot_synth: Option, + /// Learn step counter. + count: usize, } impl<'core> IceLearner<'core> { + /// Ice learner constructor. + pub fn new( + core: &'core MsgCore, + instance: Arc, + data: Data, + mine: bool, // synth_solver: Slver + ) -> Res { + let solver = conf.solver.spawn("ice_learner", (), &instance)?; + + profile!{ |core._profiler| tick "mining" } + let qualifiers = + NuQuals::new(&instance, mine).chain_err(|| "while creating qualifier structure")?; + profile!{ |core._profiler| mark "mining" } + + let dec_mem = vec![HashSet::with_capacity(103); instance.preds().len()].into(); + let candidate = vec![None; instance.preds().len()].into(); + let predicates = Vec::with_capacity(instance.preds().len()); + + let mut synth_sys = PrdMap::with_capacity(instance.preds().len()); + for (pred, _) in instance.preds().index_iter() { + synth_sys.push(SynthSys::new(&instance[pred].sig)) + } - /// Ice learner constructor. - pub fn new( - core: & 'core MsgCore, instance: Arc, data: Data, - mine: bool// synth_solver: Slver - ) -> Res { - let solver = conf.solver.spawn( - "ice_learner", (), & instance - ) ? ; - - profile!{ |core._profiler| tick "mining" } - let qualifiers = NuQuals::new( & instance, mine ).chain_err( - || "while creating qualifier structure" - ) ? ; - profile!{ |core._profiler| mark "mining" } - - let dec_mem = vec![ - HashSet::with_capacity(103) ; instance.preds().len() - ].into() ; - let candidate = vec![ None ; instance.preds().len() ].into() ; - let predicates = Vec::with_capacity( instance.preds().len() ) ; - - let mut synth_sys = PrdMap::with_capacity( instance.preds().len() ) ; - for (pred, _) in instance.preds().index_iter() { - synth_sys.push( SynthSys::new( & instance[pred].sig ) ) + use rand::SeedableRng; + + Ok(IceLearner { + instance, + qualifiers, + data, + solver, // synth_solver, + synth_sys, + core, + finished: Vec::with_capacity(103), + unfinished: Vec::with_capacity(103), + classifier: VarValsMap::with_capacity(1003), + dec_mem, + candidate, + predicates, + sort_rng_1: { Rng::from_seed([42; 16]) }, + sort_rng_2: { Rng::from_seed([79; 16]) }, + simple_rng: { Rng::from_seed([107; 16]) }, + pre_skip_rng: { Rng::from_seed([245; 16]) }, + luby: if mine { None } else { Some(LubyCount::new()) }, + known_quals: TermSet::new(), + gain_pivot: conf.ice.gain_pivot, + gain_pivot_synth: conf.ice.gain_pivot_synth, + count: 0, + }) } - use rand::SeedableRng ; - - Ok( - IceLearner { - instance, qualifiers, data, solver, // synth_solver, - synth_sys, - core, - finished: Vec::with_capacity(103), - unfinished: Vec::with_capacity(103), - classifier: VarValsMap::with_capacity(1003), - dec_mem, candidate, predicates, - sort_rng_1: { - Rng::from_seed( [ 42 ; 16 ] ) - }, - sort_rng_2: { - Rng::from_seed( [ 79 ; 16 ] ) - }, - simple_rng: { - Rng::from_seed( [ 107 ; 16 ] ) - }, - pre_skip_rng: { - Rng::from_seed( [ 245 ; 16 ] ) - }, - luby: if mine { None } else { - Some( LubyCount::new() ) - }, - known_quals: TermSet::new(), - gain_pivot: conf.ice.gain_pivot, - gain_pivot_synth: conf.ice.gain_pivot_synth, - count: 0, - } - ) - } - - /// Returns true if all qualifiers should be wiped out. - pub fn restart(& mut self) -> bool { - self.luby.as_mut().map( |l| l.inc() ).unwrap_or(false) - } - - /// Runs the learner. - pub fn run(& mut self) -> Res<()> { - profile!{ self "quals synthesized" => add 0 } - profile!{ - self "quals initially" => - add self.qualifiers.real_qual_count() + /// Returns true if all qualifiers should be wiped out. + pub fn restart(&mut self) -> bool { + self.luby.as_mut().map(|l| l.inc()).unwrap_or(false) } - loop { + /// Runs the learner. + pub fn run(&mut self) -> Res<()> { + profile!{ self "quals synthesized" => add 0 } + profile!{ + self "quals initially" => + add self.qualifiers.real_qual_count() + } - match profile! ( + loop { + match profile! ( |self.core._profiler| wrap { self.recv() } "waiting" ) { - Ok(data) => { - self.count += 1 ; - if self.count % 50 == 0 { - smt::reset(& mut self.solver, & self.instance) ? - } - profile! { self "learn steps" => add 1 } - if let Some(candidates) = profile!( + Ok(data) => { + self.count += 1; + if self.count % 50 == 0 { + smt::reset(&mut self.solver, &self.instance)? + } + profile! { self "learn steps" => add 1 } + if let Some(candidates) = profile!( |self.core._profiler| wrap { self.solver.push(1) ? ; let res = self.learn(data) ; self.solver.pop(1) ? ; res } "learning" - ) ? { - self.send_cands(candidates).chain_err( - || "while sending candidates" - ) ? ; - if self.restart() { - profile! { self "restarts" => add 1 } - self.qualifiers.wipe() + )? { + self.send_cands(candidates) + .chain_err(|| "while sending candidates")?; + if self.restart() { + profile! { self "restarts" => add 1 } + self.qualifiers.wipe() + } + } else { + return Ok(()); + } + } + Err(e) => bail!(e), } - } else { - return Ok(()) - } - }, - Err(e) => bail!(e), - } + } + } + /// Finalizes the learning process and exits. + #[cfg(not(feature = "bench"))] + pub fn finalize(mut self) -> Res<()> { + self.solver.kill()?; + profile! { + self "quals once done" => add self.qualifiers.real_qual_count() + } + Ok(()) } - } - - /// Finalizes the learning process and exits. - #[cfg( not(feature = "bench") )] - pub fn finalize(mut self) -> Res<()> { - self.solver.kill() ? ; - profile! { - self "quals once done" => add self.qualifiers.real_qual_count() + #[cfg(feature = "bench")] + pub fn finalize(mut self) -> Res<()> { + self.solver.kill()?; + Ok(()) } - Ok(()) - } - #[cfg(feature = "bench")] - pub fn finalize(mut self) -> Res<()> { - self.solver.kill() ? ; - Ok(()) - } - - /// Sends some candidates. - /// - /// Also resets the solver and clears declaration memory. - pub fn send_cands(& mut self, candidates: Candidates) -> Res<()> { - profile!( + + /// Sends some candidates. + /// + /// Also resets the solver and clears declaration memory. + pub fn send_cands(&mut self, candidates: Candidates) -> Res<()> { + profile!( | self._profiler | wrap { self.send_candidates( candidates ) } "sending" - ) ? ; - // // Reset and clear declaration memory. - // smt::reset(& mut self.solver).chain_err( - // || "during solver reset" - // ) ? ; - for set in self.dec_mem.iter_mut() { - set.clear() - } ; - Ok(()) - } - - /// Looks for a classifier. - /// - /// Returns `None` if asked to exit. - pub fn learn( - & mut self, mut data: Data - ) -> Res< Option > { - use rand::Rng ; - - ::std::mem::swap(& mut data, & mut self.data) ; - self.core.merge_set_prof( "data", data.destroy() ) ; - - if self.count % conf.ice.gain_pivot_mod == 0 { - self.gain_pivot += conf.ice.gain_pivot_inc ; - if self.gain_pivot > 0.999 { - self.gain_pivot = 0.999 - } - if let Some(gain_pivot_synth) = self.gain_pivot_synth.as_mut() { - * gain_pivot_synth += conf.ice.gain_pivot_inc ; - if * gain_pivot_synth > 0.999 { - * gain_pivot_synth = 0.999 + )?; + // // Reset and clear declaration memory. + // smt::reset(& mut self.solver).chain_err( + // || "during solver reset" + // ) ? ; + for set in self.dec_mem.iter_mut() { + set.clear() } - } + Ok(()) } - if conf.ice.qual_print { - self.qualifiers.log() - } + /// Looks for a classifier. + /// + /// Returns `None` if asked to exit. + pub fn learn(&mut self, mut data: Data) -> Res> { + use rand::Rng; - let contradiction = profile!( + ::std::mem::swap(&mut data, &mut self.data); + self.core.merge_set_prof("data", data.destroy()); + + if self.count % conf.ice.gain_pivot_mod == 0 { + self.gain_pivot += conf.ice.gain_pivot_inc; + if self.gain_pivot > 0.999 { + self.gain_pivot = 0.999 + } + if let Some(gain_pivot_synth) = self.gain_pivot_synth.as_mut() { + *gain_pivot_synth += conf.ice.gain_pivot_inc; + if *gain_pivot_synth > 0.999 { + *gain_pivot_synth = 0.999 + } + } + } + + if conf.ice.qual_print { + self.qualifiers.log() + } + + let contradiction = profile!( |self.core._profiler| wrap { self.setup_solver().chain_err( || "while initializing the solver" ) } "learning", "setup" - ) ? ; + )?; - if contradiction { - unsat!("by contradiction in ICE solver") - } + if contradiction { + unsat!("by contradiction in ICE solver") + } - self.check_exit() ? ; + self.check_exit()?; - debug_assert!{ - scoped! { - let mut okay = true ; - for term_opt in & self.candidate { - okay = okay && term_opt.is_none() ; + debug_assert!{ + scoped! { + let mut okay = true ; + for term_opt in & self.candidate { + okay = okay && term_opt.is_none() ; + } + okay + } } - okay - } - } - // Decide whether to use simple gain. - let simple = conf.ice.simple_gain_ratio >= self.simple_rng.gen() ; - // Decide whether to sort the predicates. - let sorted = conf.ice.sort_preds >= self.sort_rng_1.gen() ; - // Skip preliminary decision 20% of the time. - let skip_prelim = 0.20 >= self.pre_skip_rng.gen() ; - - msg! { @verb - self => - "starting learning\n \ - simple: {},\n \ - sorted: {},\n \ - skip_prelim: {},", - simple, sorted, skip_prelim - } + // Decide whether to use simple gain. + let simple = conf.ice.simple_gain_ratio >= self.simple_rng.gen(); + // Decide whether to sort the predicates. + let sorted = conf.ice.sort_preds >= self.sort_rng_1.gen(); + // Skip preliminary decision 20% of the time. + let skip_prelim = 0.20 >= self.pre_skip_rng.gen(); - self.predicate_stats(skip_prelim) ? ; + msg! { @verb + self => + "starting learning\n \ + simple: {},\n \ + sorted: {},\n \ + skip_prelim: {},", + simple, sorted, skip_prelim + } - self.check_exit() ? ; + self.predicate_stats(skip_prelim)?; - if simple { - profile! { self "simple" => add 1 } - } + self.check_exit()?; - self.sort_predicates(sorted) ? ; + if simple { + profile! { self "simple" => add 1 } + } + + self.sort_predicates(sorted)?; - self.check_exit() ? ; + self.check_exit()?; - while let Some( - (_unc, _cla, pred) - ) = self.predicates.pop() { - msg! { - debug self => - "{}: {} unclassified, {} classified", - self.instance[pred], _unc, _cla - } + while let Some((_unc, _cla, pred)) = self.predicates.pop() { + msg! { + debug self => + "{}: {} unclassified, {} classified", + self.instance[pred], _unc, _cla + } - let data = profile!( + let data = profile!( |self.core._profiler| wrap { self.data.data_of(pred) } "learning", "data" - ) ; - self.check_exit() ? ; - - if let Some(term) = self.pred_learn( - pred, data, simple - ) ? { - self.candidate[pred] = Some(term) - } else { - return Ok(None) - } - self.check_exit() ? ; - } - - let mut candidates: PrdMap<_> = vec![ - None ; self.instance.preds().len() - ].into() ; - - ::std::mem::swap( - & mut candidates, & mut self.candidate - ) ; + ); + self.check_exit()?; - Ok( Some(candidates) ) - } - - - - /// Prepares the predicates for sorting. - fn predicate_stats( - & mut self, skip_prelim: bool - ) -> Res<()> { - debug_assert! { self.predicates.is_empty() } - - let mut stuff_changed = true ; + if let Some(term) = self.pred_learn(pred, data, simple)? { + self.candidate[pred] = Some(term) + } else { + return Ok(None); + } + self.check_exit()?; + } - while stuff_changed { - stuff_changed = false ; - self.predicates.clear() ; + let mut candidates: PrdMap<_> = vec![None; self.instance.preds().len()].into(); - for pred in self.instance.pred_indices() { + ::std::mem::swap(&mut candidates, &mut self.candidate); - if self.instance.is_known(pred) - || self.candidate[pred].is_some() { - continue - } + Ok(Some(candidates)) + } - let pos_len = self.data.pos[pred].len() ; - let neg_len = self.data.neg[pred].len() ; - let unc_len = self.data.map()[pred].len() ; - - if ! skip_prelim { - - if pos_len == 0 && neg_len > 0 { - msg! { debug self => "legal_pred (1)" } - // Maybe we can assert everything as negative right away? - if self.force_if_legal(pred, false) ? { - msg! { @verb - self => - "{} only has negative ({}) and unclassified ({}) data\n\ - legal check ok, assuming everything negative", - self.instance[pred], neg_len, unc_len - } - self.candidate[pred] = Some( term::fls() ) ; - profile!( + /// Prepares the predicates for sorting. + fn predicate_stats(&mut self, skip_prelim: bool) -> Res<()> { + debug_assert! { self.predicates.is_empty() } + + let mut stuff_changed = true; + + while stuff_changed { + stuff_changed = false; + self.predicates.clear(); + + for pred in self.instance.pred_indices() { + if self.instance.is_known(pred) || self.candidate[pred].is_some() { + continue; + } + + let pos_len = self.data.pos[pred].len(); + let neg_len = self.data.neg[pred].len(); + let unc_len = self.data.map()[pred].len(); + + if !skip_prelim { + if pos_len == 0 && neg_len > 0 { + msg! { debug self => "legal_pred (1)" } + // Maybe we can assert everything as negative right away? + if self.force_if_legal(pred, false)? { + msg! { @verb + self => + "{} only has negative ({}) and unclassified ({}) data\n\ + legal check ok, assuming everything negative", + self.instance[pred], neg_len, unc_len + } + self.candidate[pred] = Some(term::fls()); + profile!( |self.core._profiler| wrap { self.data.force_pred(pred, false).chain_err( || format!( @@ -419,24 +374,24 @@ impl<'core> IceLearner<'core> { ) ) } "learning", "data" - ) ? ; - stuff_changed = true ; - continue - } - } - - if neg_len == 0 && pos_len > 0 { - msg! { debug self => "legal_pred (2)" } - // Maybe we can assert everything as positive right away? - if self.force_if_legal(pred, true) ? { - msg! { @verb - self => - "{} only has positive ({}) and unclassified ({}) data\n\ - legal check ok, assuming everything positive", - self.instance[pred], pos_len, unc_len - } - self.candidate[pred] = Some( term::tru() ) ; - profile!( + )?; + stuff_changed = true; + continue; + } + } + + if neg_len == 0 && pos_len > 0 { + msg! { debug self => "legal_pred (2)" } + // Maybe we can assert everything as positive right away? + if self.force_if_legal(pred, true)? { + msg! { @verb + self => + "{} only has positive ({}) and unclassified ({}) data\n\ + legal check ok, assuming everything positive", + self.instance[pred], pos_len, unc_len + } + self.candidate[pred] = Some(term::tru()); + profile!( |self.core._profiler| wrap { self.data.force_pred(pred, true).chain_err( || format!( @@ -444,293 +399,254 @@ impl<'core> IceLearner<'core> { ) ) } "learning", "data" - ) ? ; - stuff_changed = true ; - continue + )?; + stuff_changed = true; + continue; + } + } + } + + self.predicates.push((unc_len, pos_len + neg_len, pred)) } - } - } - self.predicates.push(( - unc_len, pos_len + neg_len, pred - )) - } - + Ok(()) } - Ok(()) - } - - - /// Sorts predicates. - /// - /// Randomly if `! sorted`, based on the amount of (un)classified data - /// otherwise. - fn sort_predicates(& mut self, sorted: bool) -> Res<()> { - if sorted { + /// Sorts predicates. + /// + /// Randomly if `! sorted`, based on the amount of (un)classified data + /// otherwise. + fn sort_predicates(&mut self, sorted: bool) -> Res<()> { + if sorted { + profile!{ self tick "learning", "predicate sorting" } + + // The iteration starts from the end of `predicates`. The first + // predicates we want to synthesize should be last. + self.predicates + .sort_unstable_by(|&(unc_1, sum_1, _p_1), &(unc_2, sum_2, _p_2)| { + cmp_data_metrics(sum_1, unc_1, sum_2, unc_2) + }); + profile!{ self mark "learning", "predicate sorting" } + } else { + // Not sorting, forcing random order. + profile!{ self tick "learning", "predicate sorting" } + scoped! { + let sort_rng = & mut self.sort_rng_2 ; + self.predicates.sort_unstable_by( + |_, _| { + use std::cmp::Ordering::* ; + use rand::Rng ; + + let rand: f64 = sort_rng.gen() ; + if rand <= 0.33 { + Less + } else if rand <= 0.66 { + Equal + } else { + Greater + } + } + ) + } + profile!{ self mark "learning", "predicate sorting" } + } - profile!{ self tick "learning", "predicate sorting" } + Ok(()) + } - // The iteration starts from the end of `predicates`. The first - // predicates we want to synthesize should be last. - self.predicates.sort_unstable_by( - | - & (unc_1, sum_1, _p_1), - & (unc_2, sum_2, _p_2), - | cmp_data_metrics( - sum_1, unc_1, sum_2, unc_2 - ) - ) ; - profile!{ self mark "learning", "predicate sorting" } - - } else { - - // Not sorting, forcing random order. - profile!{ self tick "learning", "predicate sorting" } - scoped! { - let sort_rng = & mut self.sort_rng_2 ; - self.predicates.sort_unstable_by( - |_, _| { - use std::cmp::Ordering::* ; - use rand::Rng ; - - let rand: f64 = sort_rng.gen() ; - if rand <= 0.33 { - Less - } else if rand <= 0.66 { - Equal + /// Chooses a branch to go into next. + /// + /// Updates the data in the branches. + pub fn choose_branch(&mut self, pred: PrdIdx) -> Option<(Branch, CData)> { + profile! { self tick "learning", "choose branch" } + + let mut best = None; + + for n in 0..self.unfinished.len() { + let data = &mut self.unfinished[n].1; + self.data.classify(pred, data); + let (classified, unknown) = (data.pos().len() + data.neg().len(), data.unc().len()); + best = if let Some((idx, cla, unk)) = best { + use std::cmp::Ordering::*; + match cmp_data_metrics(classified, unknown, cla, unk) { + Greater => Some((n, classified, unknown)), + _ => Some((idx, cla, unk)), + } } else { - Greater + Some((n, classified, unknown)) } - } - ) - } - profile!{ self mark "learning", "predicate sorting" } + } + profile! { self mark "learning", "choose branch" } + if let Some((index, _, _)) = best { + Some(self.unfinished.swap_remove(index)) + } else { + None + } } - Ok(()) - } - - + /// Backtracks to the last element of `unfinished`. + /// + /// - applies the current classification to the data we're backtracking to + /// + /// Returns `None` iff `unfinished` was empty meaning the learning process + /// is over. + pub fn backtrack(&mut self, pred: PrdIdx) -> Option<(Branch, CData)> { + profile!{ self tick "learning", "backtrack" } + + // Backtracking or exit loop. + let res = if let Some((nu_branch, mut nu_data)) = self.unfinished.pop() { + // Update data, some previously unclassified data may be classified now. + self.data.classify(pred, &mut nu_data); + Some((nu_branch, nu_data)) + } else { + None + }; + profile!{ self mark "learning", "backtrack" } - /// Chooses a branch to go into next. - /// - /// Updates the data in the branches. - pub fn choose_branch(& mut self, pred: PrdIdx) -> Option<(Branch, CData)> { - profile! { self tick "learning", "choose branch" } + res + } - let mut best = None ; + /// Looks for a classifier for a given predicate. + pub fn pred_learn(&mut self, pred: PrdIdx, data: CData, simple: bool) -> Res> { + debug_assert!(self.finished.is_empty()); + debug_assert!(self.unfinished.is_empty()); + self.classifier.clear(); - for n in 0 .. self.unfinished.len() { - let data = & mut self.unfinished[n].1 ; - self.data.classify(pred, data) ; - let (classified, unknown) = ( - data.pos().len() + data.neg().len(), - data.unc().len() - ) ; - best = if let Some((idx, cla, unk)) = best { - use std::cmp::Ordering::* ; - match cmp_data_metrics(classified, unknown, cla, unk) { - Greater => Some((n, classified, unknown)), - _ => Some((idx, cla, unk)), + msg! { @verb + self => + "working on predicate {} (pos: {}, neg: {}, unc: {})", + self.instance[pred], data.pos().len(), data.neg().len(), data.unc().len() } - } else { - Some((n, classified, unknown)) - } - } - profile! { self mark "learning", "choose branch" } - if let Some((index, _, _)) = best { - Some( self.unfinished.swap_remove(index) ) - } else { - None - } - } - - /// Backtracks to the last element of `unfinished`. - /// - /// - applies the current classification to the data we're backtracking to - /// - /// Returns `None` iff `unfinished` was empty meaning the learning process - /// is over. - pub fn backtrack(& mut self, pred: PrdIdx) -> Option<(Branch, CData)> { - profile!{ self tick "learning", "backtrack" } - - // Backtracking or exit loop. - let res = if let Some( (nu_branch, mut nu_data) ) = self.unfinished.pop() { - // Update data, some previously unclassified data may be classified now. - self.data.classify(pred, & mut nu_data) ; - Some( (nu_branch, nu_data) ) - } else { None } ; - - profile!{ self mark "learning", "backtrack" } - - res - } - - /// Looks for a classifier for a given predicate. - pub fn pred_learn( - & mut self, pred: PrdIdx, data: CData, simple: bool - ) -> Res< Option > { - debug_assert!( self.finished.is_empty() ) ; - debug_assert!( self.unfinished.is_empty() ) ; - self.classifier.clear() ; - - msg! { @verb - self => - "working on predicate {} (pos: {}, neg: {}, unc: {})", - self.instance[pred], data.pos().len(), data.neg().len(), data.unc().len() - } + self.unfinished.push((vec![], data)); - self.unfinished.push( (vec![], data) ) ; + 'learning: while let Some((mut branch, data)) = self.choose_branch(pred) { + self.check_exit()?; - 'learning: while let Some( - (mut branch, data) - ) = self.choose_branch(pred) { - self.check_exit() ? ; + let branch_is_empty = branch.is_empty(); - let branch_is_empty = branch.is_empty() ; + // Checking whether we can close this branch. + let data = if let Some(data) = self.try_force_all(pred, data, true)? { + data + } else if branch_is_empty { + debug_assert!(self.finished.is_empty()); + debug_assert!(self.unfinished.is_empty()); + return Ok(Some(term::tru())); + } else { + branch.shrink_to_fit(); + self.finished.push(branch); + continue 'learning; + }; + + let data = if let Some(data) = self.try_force_all(pred, data, false)? { + data + } else if branch_is_empty { + debug_assert!(self.finished.is_empty()); + debug_assert!(self.unfinished.is_empty()); + return Ok(Some(term::fls())); + } else { + continue 'learning; + }; - // Checking whether we can close this branch. - let data = if let Some(data) = self.try_force_all(pred, data, true) ? { - data - } else if branch_is_empty { - debug_assert!( self.finished.is_empty() ) ; - debug_assert!( self.unfinished.is_empty() ) ; - return Ok( - Some( term::tru() ) - ) - } else { - branch.shrink_to_fit() ; - self.finished.push(branch) ; - continue 'learning - } ; - - let data = if let Some(data) = self.try_force_all(pred, data, false) ? { - data - } else if branch_is_empty { - debug_assert!( self.finished.is_empty() ) ; - debug_assert!( self.unfinished.is_empty() ) ; - return Ok( - Some( term::fls() ) - ) - } else { - continue 'learning - } ; + self.check_exit()?; + // Could not close the branch, look for a qualifier. + profile!{ self tick "learning", "qual" } + let res = self.get_qualifier(pred, data, simple); + profile!{ self mark "learning", "qual" } + let (qual, q_data, nq_data) = if let Some(res) = res? { + res + } else { + return Ok(None); + }; - self.check_exit() ? ; + // Remember the branch where qualifier is false. + let mut nq_branch = branch.clone(); + nq_branch.push((qual.clone(), false)); + self.unfinished.push((nq_branch, nq_data)); - // Could not close the branch, look for a qualifier. - profile!{ self tick "learning", "qual" } - let res = self.get_qualifier( - pred, data, simple - ) ; - profile!{ self mark "learning", "qual" } - let (qual, q_data, nq_data) = if let Some(res) = res ? { - res - } else { - return Ok(None) - } ; - - // Remember the branch where qualifier is false. - let mut nq_branch = branch.clone() ; - nq_branch.push( (qual.clone(), false) ) ; - self.unfinished.push( (nq_branch, nq_data) ) ; - - // Remember the branch where qualifier is true. - branch.push( (qual, true) ) ; - self.unfinished.push( (branch, q_data) ) - } + // Remember the branch where qualifier is true. + branch.push((qual, true)); + self.unfinished.push((branch, q_data)) + } - profile!{ self tick "learning", "pred finalize" } - debug_assert!( self.unfinished.is_empty() ) ; - let mut or_args = Vec::with_capacity( self.finished.len() ) ; - for branch in self.finished.drain(0..) { - let mut and_args = Vec::with_capacity( branch.len() ) ; - for (term, pos) in branch { - let term = if pos { - term - } else { - term::not(term) - } ; - and_args.push(term) - } - or_args.push( term::and(and_args) ) + profile!{ self tick "learning", "pred finalize" } + debug_assert!(self.unfinished.is_empty()); + let mut or_args = Vec::with_capacity(self.finished.len()); + for branch in self.finished.drain(0..) { + let mut and_args = Vec::with_capacity(branch.len()); + for (term, pos) in branch { + let term = if pos { term } else { term::not(term) }; + and_args.push(term) + } + or_args.push(term::and(and_args)) + } + profile!{ self mark "learning", "pred finalize" } + Ok(Some(term::or(or_args))) } - profile!{ self mark "learning", "pred finalize" } - Ok( - Some( term::or(or_args) ) - ) - } - - - /// Tries to force some data to positive/negative for some predicate. - /// - /// Returns `None` if everything was forced positive. - fn try_force_all( - & mut self, pred: PrdIdx, data: CData, pos: bool - ) -> Res< Option > { - let forcing = if pos { - // No more negative data? - data.neg().is_empty() - } else { - // No more positive data? - data.pos().is_empty() - } ; - - if forcing && self.force_legal( - pred, data.unc(), pos - ).chain_err( - || format!( - "while checking possibility of assuming {}", - if pos { "positive" } else { "negative" } - ) - ) ? { - if_verb! { - let mut s = format!( - " no more {} data, force_legal check ok\n \ - forcing {} unclassifieds positive...", - if pos { "positive" } else { "negative" }, data.unc().len() - ) ; - - // let mut and_args = vec![] ; - // for (term, pos) in & branch { - // let term = term.clone() ; - // let term = if * pos { - // term - // } else { - // term::not(term) - // } ; - // and_args.push(term) - // } - // s.push_str(& format!("\n {}", term::and(and_args))) ; - if_debug! { - s = format!("{}\n data:", s) ; - s = format!("{}\n pos {{", s) ; - for sample in data.pos() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }} neg {{", s) ; - for sample in data.neg() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }} unc {{", s) ; - for sample in data.unc() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }}", s) ; - } - msg! { self => s } - } + /// Tries to force some data to positive/negative for some predicate. + /// + /// Returns `None` if everything was forced positive. + fn try_force_all(&mut self, pred: PrdIdx, data: CData, pos: bool) -> Res> { + let forcing = if pos { + // No more negative data? + data.neg().is_empty() + } else { + // No more positive data? + data.pos().is_empty() + }; + + let polarity = || if pos { "positive" } else { "negative" }; + + if forcing && self + .force_legal(pred, data.unc(), pos) + .chain_err(|| format!("while checking possibility of assuming {}", polarity()))? + { + if_verb! { + let mut s = format!( + " no more {} data, force_legal check ok\n \ + forcing {} unclassifieds positive...", + if pos { "positive" } else { "negative" }, data.unc().len() + ) ; + + // let mut and_args = vec![] ; + // for (term, pos) in & branch { + // let term = term.clone() ; + // let term = if * pos { + // term + // } else { + // term::not(term) + // } ; + // and_args.push(term) + // } + // s.push_str(& format!("\n {}", term::and(and_args))) ; + + if_debug! { + s = format!("{}\n data:", s) ; + s = format!("{}\n pos {{", s) ; + for sample in data.pos() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }} neg {{", s) ; + for sample in data.neg() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }} unc {{", s) ; + for sample in data.unc() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }}", s) ; + } + msg! { self => s } + } - let (_, _, unc) = data.destroy() ; + let (_, _, unc) = data.destroy(); - profile!( + profile!( |self.core._profiler| wrap { if pos { for unc in unc { @@ -743,560 +659,522 @@ impl<'core> IceLearner<'core> { } self.data.propagate() } "learning", "data" - ) ? ; + )?; - Ok( None ) - } else { - Ok( Some(data) ) - } - } - - - - /// Looks for a qualifier. Requires a mutable `self` in case it needs to - /// synthesize a qualifier. - /// - /// Does **not** blacklist the qualifier it returns. - /// - /// Be careful when modifying this function as it as a (tail-)recursive call. - /// The recursive call is logically guaranteed not cause further calls and - /// terminate right away. Please be careful to preserve this. - /// - /// The `simple` flag forces to use simple, unclassified-agnostic gain. - pub fn get_qualifier( - & mut self, pred: PrdIdx, mut data: CData, simple: bool - ) -> Res< Option< (Term, CData, CData) > > { - let simple = data.unc().is_empty() || ( - ! data.pos().is_empty() && ! data.neg().is_empty() && ( - simple || data.unc().len() > 3 * (data.pos().len() + data.neg().len()) - ) - ) ; - - if_debug! { - let mut s = "data:".to_string() ; - s = format!("{}\n pos {{", s) ; - for sample in data.pos() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }} neg {{", s) ; - for sample in data.neg() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }} unc {{", s) ; - for sample in data.unc() { - s = format!("{}\n {}", s, sample) - } - s = format!("{}\n }}", s) ; - msg! { self => s } + Ok(None) + } else { + Ok(Some(data)) + } } - let mut best_qual = self.get_best_qual(simple, pred, & mut data) ? ; + /// Looks for a qualifier. Requires a mutable `self` in case it needs to + /// synthesize a qualifier. + /// + /// Does **not** blacklist the qualifier it returns. + /// + /// Be careful when modifying this function as it as a (tail-)recursive call. + /// The recursive call is logically guaranteed not cause further calls and + /// terminate right away. Please be careful to preserve this. + /// + /// The `simple` flag forces to use simple, unclassified-agnostic gain. + pub fn get_qualifier( + &mut self, + pred: PrdIdx, + mut data: CData, + simple: bool, + ) -> Res> { + let simple = data.unc().is_empty() + || (!data.pos().is_empty() + && !data.neg().is_empty() + && (simple || data.unc().len() > 3 * (data.pos().len() + data.neg().len()))); - if let Some((qual, gain)) = best_qual { - best_qual = if gain >= self.gain_pivot && gain > 0.0 { - msg! { - self => - " using qualifier {}, gain: {} >= {} (simple: {})", - qual, gain, self.gain_pivot, simple + if_debug! { + let mut s = "data:".to_string() ; + s = format!("{}\n pos {{", s) ; + for sample in data.pos() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }} neg {{", s) ; + for sample in data.neg() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }} unc {{", s) ; + for sample in data.unc() { + s = format!("{}\n {}", s, sample) + } + s = format!("{}\n }}", s) ; + msg! { self => s } } - // This qualifier is satisfactory. - profile!{ self tick "learning", "qual", "data split" } - let (q_data, nq_data) = data.split(& qual) ; - profile!{ self mark "learning", "qual", "data split" } - return Ok( Some((qual, q_data, nq_data)) ) - } else { - msg! { - self => - " qualifier {} is not good enough, gain: {} < {} (simple: {})", - qual, gain, self.gain_pivot, simple + + let mut best_qual = self.get_best_qual(simple, pred, &mut data)?; + + if let Some((qual, gain)) = best_qual { + best_qual = if gain >= self.gain_pivot && gain > 0.0 { + msg! { + self => + " using qualifier {}, gain: {} >= {} (simple: {})", + qual, gain, self.gain_pivot, simple + } + // This qualifier is satisfactory. + profile!{ self tick "learning", "qual", "data split" } + let (q_data, nq_data) = data.split(&qual); + profile!{ self mark "learning", "qual", "data split" } + return Ok(Some((qual, q_data, nq_data))); + } else { + msg! { + self => + " qualifier {} is not good enough, gain: {} < {} (simple: {})", + qual, gain, self.gain_pivot, simple + } + // Not good enough, maybe synthesis can do better. + Some((qual, gain)) + } } - // Not good enough, maybe synthesis can do better. - Some( (qual, gain) ) - } - } - if_verb!{ - let mut msg = format!( - " could not split remaining data for {}:\n", self.instance[pred] - ) ; - msg.push_str(" pos (") ; - for pos in data.pos() { - msg.push_str( & format!("\n {}", pos) ) - } - msg.push_str("\n ) neg (") ; - for neg in data.neg() { - msg.push_str( & format!("\n {}", neg) ) - } - msg.push_str("\n ) unc (") ; - for unc in data.unc() { - msg.push_str( & format!("\n {}", unc) ) - } - msg.push_str("\n )") ; - msg!{ self => msg } ; - } + if_verb!{ + let mut msg = format!( + " could not split remaining data for {}:\n", self.instance[pred] + ) ; + msg.push_str(" pos (") ; + for pos in data.pos() { + msg.push_str( & format!("\n {}", pos) ) + } + msg.push_str("\n ) neg (") ; + for neg in data.neg() { + msg.push_str( & format!("\n {}", neg) ) + } + msg.push_str("\n ) unc (") ; + for unc in data.unc() { + msg.push_str( & format!("\n {}", unc) ) + } + msg.push_str("\n )") ; + msg!{ self => msg } ; + } - if data.is_empty() { - bail!("[bug] cannot synthesize qualifier based on no data") - } + if data.is_empty() { + bail!("[bug] cannot synthesize qualifier based on no data") + } - self.check_exit() ? ; + self.check_exit()?; + + // Synthesize qualifier separating the data. + let mut best_synth_qual = None; + msg! { self => "synthesizing" } + profile!{ self tick "learning", "qual", "synthesis" }; + let res = self.synthesize(pred, &data, &mut best_synth_qual, simple); + profile!{ self mark "learning", "qual", "synthesis" }; + + if res?.is_none() { + return Ok(None); + } + let qual = self.choose_qual(best_qual, best_synth_qual)?; - // Synthesize qualifier separating the data. - let mut best_synth_qual = None ; - msg! { self => "synthesizing" } - profile!{ self tick "learning", "qual", "synthesis" } ; - let res = self.synthesize( - pred, & data, & mut best_synth_qual, simple - ) ; - profile!{ self mark "learning", "qual", "synthesis" } ; + profile!{ self tick "learning", "qual", "data split" } + let (q_data, nq_data) = data.split(&qual); + profile!{ self mark "learning", "qual", "data split" } - if res?.is_none() { - return Ok(None) + Ok(Some((qual, q_data, nq_data))) } - let qual = self.choose_qual(best_qual, best_synth_qual) ? ; + /// Gets the best qualifier available for some data. + pub fn get_best_qual( + &mut self, + simple_gain: bool, + pred: PrdIdx, + data: &mut CData, + ) -> Res> { + let core = &self.core; + + let bias = data.pop_single_sample(); + + // Run simple if in simple mode. + if simple_gain { + profile!{ self tick "learning", "qual", "simple gain" } + let res = self.qualifiers.maximize(pred, bias, |qual| { + if conf.ice.qual_step { + let _ = core.msg(format!("evaluating {} (simple gain)", qual)); + } + let res = data.simple_gain(qual, false)?; + if conf.ice.qual_step { + let _ = core.msg(format!( + "{}: {}", + qual, + res.map(|g| format!("{}", g)) + .unwrap_or_else(|| "none".into()) + )); + pause_msg(core, "to continue (--qual_step on)"); + () + } + core.check_exit()?; + Ok(res) + }); + profile!{ self mark "learning", "qual", "simple gain" } + res + } else { + let qualifiers = &mut self.qualifiers; + let self_core = &self.core; + let all_data = &self.data; + + profile!{ |self.core._profiler| tick "learning", "qual", "gain" } + let res = qualifiers.maximize(pred, bias, |qual| { + if conf.ice.qual_step { + let _ = core.msg(format!("evaluating {} (gain)", qual)); + } + let res = data.gain(pred, all_data, qual, &self_core._profiler, false)?; + if conf.ice.qual_step { + let _ = self_core.msg(format!( + "; {}: {}", + qual, + res.map(|g| format!("{}", g)) + .unwrap_or_else(|| "none".into()) + )); + pause_msg(self_core, "to continue (--qual_step on)"); + () + } + core.check_exit()?; + Ok(res) + }); + profile!{ |self.core._profiler| mark "learning", "qual", "gain" } + res + } + } - profile!{ self tick "learning", "qual", "data split" } - let (q_data, nq_data) = data.split(& qual) ; - profile!{ self mark "learning", "qual", "data split" } + /// Chooses between a qualifier and a synthesized qualifier. + fn choose_qual(&self, qual: Option<(Term, f64)>, squal: Option<(Term, f64)>) -> Res { + let res = match (qual, squal) { + (Some((qual, gain)), Some((squal, synth_gain))) => { + if synth_gain > gain { + msg! { + self => + "using synth qualifier {}, gain {} >= {} (for {})", + squal, synth_gain, gain, qual + } + squal + } else { + msg! { + self => + "synth qualifier {} is not good enough, gain: {}\n\ + using qualifier {} instead, gain: {}", + squal, synth_gain, qual, gain + } + qual + } + } + (None, Some((squal, _synth_gain))) => { + msg! { + self => + "using synth qualifier {}, gain {}", squal, _synth_gain + } + squal + } + // I think this should be unreachable. + (Some((qual, _gain)), None) => { + msg! { + self => + "using qualifier {}, gain {}", qual, _gain + } + qual + } + (None, None) => { + // let mut msg = format!( + // "\ncould not split remaining data for {} after synthesis:\n", + // self.instance[pred] + // ) ; + // msg.push_str("pos (") ; + // for pos in data.pos() { + // msg.push_str( & format!("\n {}", pos) ) + // } + // msg.push_str("\n) neg (") ; + // for neg in data.neg() { + // msg.push_str( & format!("\n {}", neg) ) + // } + // msg.push_str("\n) unc (") ; + // for unc in data.unc() { + // msg.push_str( & format!("\n {}", unc) ) + // } + // msg.push_str("\n)") ; + // bail!(msg) + unknown!("by lack of (synth) qualifier") + } + }; + Ok(res) + } - Ok( Some((qual, q_data, nq_data)) ) - } + /// Qualifier synthesis. + pub fn synthesize( + &mut self, + pred: PrdIdx, + data: &CData, + best: &mut Option<(Term, f64)>, + simple: bool, + ) -> Res> { + scoped! { + let self_data = & self.data ; + let quals = & mut self.qualifiers ; + + let self_core = & self.core ; + let known_quals = & mut self.known_quals ; + // let gain_pivot = self.gain_pivot ; + let gain_pivot_synth = self.gain_pivot_synth ; + known_quals.clear() ; + + // println!("synthesizing") ; + + let mut treatment = |term: Term| { + self_core.check_exit() ? ; + let is_new = ! quals.quals_of_contains( + pred, & term + ) && known_quals.insert( + term.clone() + ) ; + if ! is_new { + // Term already known, skip. + Ok(false) + } else { + if conf.ice.qual_step || conf.ice.qual_synth_step { + let _ = self_core.msg( + format!("synth evaluating {}", term) + ) ; + } + let gain = if simple { + data.simple_gain(& term, false) ? + } else { + data.gain( + pred, self_data, & term, & self_core._profiler, false + ) ? + } ; + + if conf.ice.qual_step || conf.ice.qual_synth_step { + let _ = self_core.msg( + format!( + "{}: {} (synthesis)", term, gain.map( + |gain| format!("{}", gain) + ).unwrap_or_else( || "none".into() ) + ) + ) ; + pause_msg(self_core, "to continue (--qual_step on)") ; + () + } - /// Gets the best qualifier available for some data. - pub fn get_best_qual( - & mut self, simple_gain: bool, pred: PrdIdx, data: & mut CData - ) -> Res< Option<(Term, f64)> > { - let core = & self.core ; + if let Some(gain) = gain { + // println!(" - {}", gain) ; + if conf.ice.add_synth && gain >= 1.0 { + msg! { self_core => " adding synth qual {}", term } + quals.insert(term.clone(), pred) ? ; + () + } + if let Some( (ref mut old_term, ref mut old_gain) ) = * best { + let diff = gain - * old_gain ; + if diff > ::std::f64::EPSILON { + * old_gain = gain ; + * old_term = term + } else if (* old_gain - gain).abs() < ::std::f64::EPSILON + && term::vars(old_term).len() < term::vars(& term).len() { + * old_term = term + } + } else { + * best = Some((term, gain)) + } + + let stop = gain >= 0.9999 + || if let Some(pivot) = gain_pivot_synth { + gain >= pivot + } else { + false + } ; + + if stop { + msg! { + self_core => + " stopping on synth qual {}, gain {}", + best.as_ref().unwrap().0, gain + } + } + Ok(stop) + } else { + Ok(false) + } + } + } ; - let bias = data.pop_single_sample() ; + self.synth_sys[pred].restart() ; - // Run simple if in simple mode. - if simple_gain { + 'synth: loop { + + for sample in data.iter(! simple) { + self_core.check_exit() ? ; + let done = self.synth_sys[pred].sample_synth( + sample, & mut treatment, & self_core._profiler + ) ? ; + if done { break } + } + + self.synth_sys[pred].increment() ; + if self.synth_sys[pred].is_done() { + break 'synth + } - profile!{ self tick "learning", "qual", "simple gain" } - let res = self.qualifiers.maximize( - pred, bias, |qual| { - if conf.ice.qual_step { - let _ = core.msg( - format!("evaluating {} (simple gain)", qual) - ) ; - } - let res = data.simple_gain(qual, false) ? ; - if conf.ice.qual_step { - let _ = core.msg( - format!( - "{}: {}", qual, - res.map(|g| format!("{}", g)).unwrap_or_else( - || "none".into() - ) - ) - ) ; - pause_msg(core, "to continue (--qual_step on)") ; - () - } - core.check_exit() ? ; - Ok(res) - } - ) ; - profile!{ self mark "learning", "qual", "simple gain" } - res - - } else { - - let qualifiers = & mut self.qualifiers ; - let self_core = & self.core ; - let all_data = & self.data ; - - profile!{ |self.core._profiler| tick "learning", "qual", "gain" } - let res = qualifiers.maximize( - pred, bias, |qual| { - if conf.ice.qual_step { - let _ = core.msg( - format!("evaluating {} (gain)", qual) - ) ; - } - let res = data.gain( - pred, all_data, qual, & self_core._profiler, false - ) ? ; - if conf.ice.qual_step { - let _ = self_core.msg( - format!( - "; {}: {}", qual, - res.map(|g| format!("{}", g)).unwrap_or_else( - || "none".into() - ) - ) - ) ; - pause_msg(self_core, "to continue (--qual_step on)") ; - () } - core.check_exit() ? ; - Ok(res) } - ) ; - profile!{ |self.core._profiler| mark "learning", "qual", "gain" } - res + Ok(Some(())) } - } - - - /// Chooses between a qualifier and a synthesized qualifier. - fn choose_qual( - & self, qual: Option<(Term, f64)>, synth_qual: Option<(Term, f64)> - ) -> Res { - let res = match ( qual, synth_qual ) { - ( Some((qual, gain)), Some((synth_qual, synth_gain)) ) => { - if synth_gain > gain { - msg! { - self => - "using synth qualifier {}, gain {} >= {} (for {})", - synth_qual, synth_gain, gain, qual - } - synth_qual - } else { - msg! { - self => - "synth qualifier {} is not good enough, gain: {}\n\ - using qualifier {} instead, gain: {}", - synth_qual, synth_gain, qual, gain - } - qual - } - }, - ( None, Some((synth_qual, _synth_gain)) ) => { - msg! { - self => - "using synth qualifier {}, gain {}", synth_qual, _synth_gain - } - synth_qual - }, - // I think this should be unreachable. - ( Some((qual, _gain)), None ) => { - msg! { - self => - "using qualifier {}, gain {}", qual, _gain + + /// Checks whether assuming some data as positive (if `pos` is true, + /// negative otherwise) is legal. + /// + /// **NB**: if assuming the data positive / negative is legal, + /// the data will be forced to be positive / negative in the solver + /// automatically. Otherwise, the actlit is deactivated. + pub fn force_legal(&mut self, pred: PrdIdx, unc: &[VarVals], pos: bool) -> Res { + if unc.is_empty() { + return Ok(true); } - qual - }, - (None, None) => { - // let mut msg = format!( - // "\ncould not split remaining data for {} after synthesis:\n", - // self.instance[pred] - // ) ; - // msg.push_str("pos (") ; - // for pos in data.pos() { - // msg.push_str( & format!("\n {}", pos) ) - // } - // msg.push_str("\n) neg (") ; - // for neg in data.neg() { - // msg.push_str( & format!("\n {}", neg) ) - // } - // msg.push_str("\n) unc (") ; - // for unc in data.unc() { - // msg.push_str( & format!("\n {}", unc) ) - // } - // msg.push_str("\n)") ; - // bail!(msg) - unknown!("by lack of (synth) qualifier") - }, - } ; - Ok(res) - } - - - - - - - /// Qualifier synthesis. - pub fn synthesize( - & mut self, pred: PrdIdx, data: & CData, - best: & mut Option<(Term, f64)>, simple: bool, - ) -> Res< Option<()> > { - - scoped! { - let self_data = & self.data ; - let quals = & mut self.qualifiers ; - - let self_core = & self.core ; - let known_quals = & mut self.known_quals ; - // let gain_pivot = self.gain_pivot ; - let gain_pivot_synth = self.gain_pivot_synth ; - known_quals.clear() ; - - // println!("synthesizing") ; - - let mut treatment = |term: Term| { - self_core.check_exit() ? ; - let is_new = ! quals.quals_of_contains( - pred, & term - ) && known_quals.insert( - term.clone() - ) ; - - if ! is_new { - // Term already known, skip. - Ok(false) + profile!{ self tick "learning", "smt", "legal" } + + // Wrap actlit and increment counter. + let samples = SmtActSamples::new(&mut self.solver, pred, unc, pos)?; + self.solver.assert(&samples)?; + + let legal = if self.solver.check_sat_act(Some(&samples.actlit))? { + profile!{ self mark "learning", "smt", "legal" } + true } else { - if conf.ice.qual_step || conf.ice.qual_synth_step { - let _ = self_core.msg( - format!("synth evaluating {}", term) - ) ; - } - let gain = if simple { - data.simple_gain(& term, false) ? - } else { - data.gain( - pred, self_data, & term, & self_core._profiler, false - ) ? - } ; + profile!{ self mark "learning", "smt", "legal" } + false + }; - if conf.ice.qual_step || conf.ice.qual_synth_step { - let _ = self_core.msg( - format!( - "{}: {} (synthesis)", term, gain.map( - |gain| format!("{}", gain) - ).unwrap_or_else( || "none".into() ) - ) - ) ; - pause_msg(self_core, "to continue (--qual_step on)") ; - () - } + samples.force(&mut self.solver, legal)?; - if let Some(gain) = gain { - // println!(" - {}", gain) ; - if conf.ice.add_synth && gain >= 1.0 { - msg! { self_core => " adding synth qual {}", term } - quals.insert(term.clone(), pred) ? ; - () - } - if let Some( (ref mut old_term, ref mut old_gain) ) = * best { - let diff = gain - * old_gain ; - if diff > ::std::f64::EPSILON { - * old_gain = gain ; - * old_term = term - } else if (* old_gain - gain).abs() < ::std::f64::EPSILON - && term::vars(old_term).len() < term::vars(& term).len() { - * old_term = term - } - } else { - * best = Some((term, gain)) - } + Ok(legal) + } - let stop = gain >= 0.9999 - || if let Some(pivot) = gain_pivot_synth { - gain >= pivot - } else { - false - } ; - - if stop { - msg! { - self_core => - " stopping on synth qual {}, gain {}", - best.as_ref().unwrap().0, gain - } - } - Ok(stop) - } else { - Ok(false) - } + /// Checks whether assuming **all** the unclassified data from a predicate as + /// `pos` is legal. + /// + /// **NB**: if assuming the data positive / negative is legal, the data will + /// be forced to be positive / negative in the solver automatically. + /// Otherwise, the actlit is deactivated (`assert (not )`). + pub fn force_if_legal(&mut self, pred: PrdIdx, pos: bool) -> Res { + profile!{ self tick "learning", "smt", "all legal" } + let unc = &self.data.map()[pred]; + if unc.is_empty() { + profile!{ self mark "learning", "smt", "all legal" } + return Ok(true); } - } ; - - self.synth_sys[pred].restart() ; - 'synth: loop { + // Wrap actlit and increment counter. + let samples = SmtActSamples::new(&mut self.solver, pred, unc, pos)?; + self.solver.assert(&samples)?; - for sample in data.iter(! simple) { - self_core.check_exit() ? ; - let done = self.synth_sys[pred].sample_synth( - sample, & mut treatment, & self_core._profiler - ) ? ; - if done { break } - } + let legal = if self.solver.check_sat_act(Some(&samples.actlit))? { + profile!{ self mark "learning", "smt", "all legal" } + true + } else { + profile!{ self mark "learning", "smt", "all legal" } + false + }; - self.synth_sys[pred].increment() ; - if self.synth_sys[pred].is_done() { - break 'synth - } + samples.force(&mut self.solver, legal)?; - } + Ok(legal) } - Ok( Some(()) ) - } - - - /// Checks whether assuming some data as positive (if `pos` is true, - /// negative otherwise) is legal. - /// - /// **NB**: if assuming the data positive / negative is legal, - /// the data will be forced to be positive / negative in the solver - /// automatically. Otherwise, the actlit is deactivated. - pub fn force_legal( - & mut self, pred: PrdIdx, unc: & [ VarVals ], pos: bool - ) -> Res { - if unc.is_empty() { return Ok(true) } - profile!{ self tick "learning", "smt", "legal" } - - // Wrap actlit and increment counter. - let samples = SmtActSamples::new( - & mut self.solver, pred, unc, pos - ) ? ; - self.solver.assert( & samples ) ? ; - - let legal = if self.solver.check_sat_act( - Some(& samples.actlit) - ) ? { - profile!{ self mark "learning", "smt", "legal" } - true - } else { - profile!{ self mark "learning", "smt", "legal" } - false - } ; - - samples.force(& mut self.solver, legal) ? ; - - Ok(legal) - } - - - /// Checks whether assuming **all** the unclassified data from a predicate as - /// `pos` is legal. - /// - /// **NB**: if assuming the data positive / negative is legal, the data will - /// be forced to be positive / negative in the solver automatically. - /// Otherwise, the actlit is deactivated (`assert (not )`). - pub fn force_if_legal( - & mut self, pred: PrdIdx, pos: bool - ) -> Res { - profile!{ self tick "learning", "smt", "all legal" } - let unc = & self.data.map()[pred] ; - if unc.is_empty() { - profile!{ self mark "learning", "smt", "all legal" } - return Ok(true) - } + /// Sets the solver to check that constraints are respected. + /// + /// Returns `true` if a contradiction was found. + /// + /// - **does not** reset the solver or clean declaration memory (must be + /// done before sending previous candidates) + /// - **defines** pos (neg) data as `true` (`false`) + /// - **declares** samples that neither pos nor neg + /// - asserts constraints + pub fn setup_solver(&mut self) -> Res { + // Positive data. + self.solver.comment("Positive data:")?; + for (pred, set) in self.data.pos.index_iter() { + for sample in set.iter() { + let is_new = self.dec_mem[pred].insert(sample.uid()); + debug_assert!(is_new); + self.solver.define_const( + &SmtSample::new(pred, sample), + typ::bool().get(), + &"true", + )? + } + } + // Negative data. + self.solver.comment("Negative data:")?; + for (pred, set) in self.data.neg.index_iter() { + for sample in set.iter() { + let is_new = self.dec_mem[pred].insert(sample.uid()); + if !is_new { + // Contradiction found. + return Ok(true); + } + self.solver.define_const( + &SmtSample::new(pred, sample), + typ::bool().get(), + &"false", + )? + } + } - // Wrap actlit and increment counter. - let samples = SmtActSamples::new( - & mut self.solver, pred, unc, pos - ) ? ; - self.solver.assert(& samples) ? ; - - let legal = if self.solver.check_sat_act( - Some(& samples.actlit) - ) ? { - profile!{ self mark "learning", "smt", "all legal" } - true - } else { - profile!{ self mark "learning", "smt", "all legal" } - false - } ; - - samples.force(& mut self.solver, legal) ? ; - - Ok(legal) - } - - - /// Sets the solver to check that constraints are respected. - /// - /// Returns `true` if a contradiction was found. - /// - /// - **does not** reset the solver or clean declaration memory (must be - /// done before sending previous candidates) - /// - **defines** pos (neg) data as `true` (`false`) - /// - **declares** samples that neither pos nor neg - /// - asserts constraints - pub fn setup_solver(& mut self) -> Res { - - // Positive data. - self.solver.comment("Positive data:") ? ; - for (pred, set) in self.data.pos.index_iter() { - for sample in set.iter() { - let is_new = self.dec_mem[pred].insert( sample.uid() ) ; - debug_assert!(is_new) ; - self.solver.define_const( - & SmtSample::new(pred, sample), - typ::bool().get(), & "true" - ) ? - } - } - // Negative data. - self.solver.comment("Negative data:") ? ; - for (pred, set) in self.data.neg.index_iter() { - for sample in set.iter() { - let is_new = self.dec_mem[pred].insert( sample.uid() ) ; - if ! is_new { - // Contradiction found. - return Ok(true) + self.solver + .comment("Sample declarations for constraints:")?; + // Declare all samples used in constraints. + for (pred, map) in self.data.map().index_iter() { + // if let Some(term) = self.instance.term_of(pred) { + // if term.is_true() { + // self.solver.comment( + // & format!( + // "Predicate {} is forced to be `true`:", self.instance[pred] + // ) + // ) ? ; + // for (sample, _) in map.read().map_err(corrupted_err)?.iter() { + // let uid = sample.uid() ; + // if ! self.dec_mem[pred].contains(& uid) { + // let _ = self.dec_mem[pred].insert(uid) ; + // self.solver.define_fun( + // & SmtSample(pred, sample), & args, & Typ::Bool, & "true", & () + // ) ? + // } + // } + // } else { + // bail!( + // "predicate {} is forced to {}, unsupported for now", + // self.instance[pred], term + // ) + // } + // } else { + for (sample, _) in map.iter() { + let uid = sample.uid(); + if !self.dec_mem[pred].contains(&uid) { + let _ = self.dec_mem[pred].insert(uid); + self.solver + .declare_const(&SmtSample::new(pred, sample), &typ::RTyp::Bool)? + } + } + // } } - self.solver.define_const( - & SmtSample::new(pred, sample), - typ::bool().get(), & "false" - ) ? - } - } - self.solver.comment("Sample declarations for constraints:") ? ; - // Declare all samples used in constraints. - for (pred, map) in self.data.map().index_iter() { - // if let Some(term) = self.instance.term_of(pred) { - // if term.is_true() { - // self.solver.comment( - // & format!( - // "Predicate {} is forced to be `true`:", self.instance[pred] - // ) - // ) ? ; - // for (sample, _) in map.read().map_err(corrupted_err)?.iter() { - // let uid = sample.uid() ; - // if ! self.dec_mem[pred].contains(& uid) { - // let _ = self.dec_mem[pred].insert(uid) ; - // self.solver.define_fun( - // & SmtSample(pred, sample), & args, & Typ::Bool, & "true", & () - // ) ? - // } - // } - // } else { - // bail!( - // "predicate {} is forced to {}, unsupported for now", - // self.instance[pred], term - // ) - // } - // } else { - for (sample, _) in map.iter() { - let uid = sample.uid() ; - if ! self.dec_mem[pred].contains(& uid) { - let _ = self.dec_mem[pred].insert(uid) ; - self.solver.declare_const( - & SmtSample::new(pred, sample), & typ::RTyp::Bool, - ) ? - } + self.solver.comment("Constraints:")?; + // Assert all constraints. + for constraint in self.data.constraints.iter() { + if !constraint.is_tautology() { + self.solver.assert(&SmtConstraint::new(constraint))? + } } - // } - } - self.solver.comment("Constraints:") ? ; - // Assert all constraints. - for constraint in self.data.constraints.iter() { - if ! constraint.is_tautology() { - self.solver.assert( - & SmtConstraint::new(constraint) - ) ? - } + Ok(false) } - - Ok(false) - } } impl<'core> ::std::ops::Deref for IceLearner<'core> { - type Target = MsgCore ; - fn deref(& self) -> & MsgCore { & self.core } + type Target = MsgCore; + fn deref(&self) -> &MsgCore { + &self.core + } } - diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 8880fc5b..35c4652f 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -58,362 +58,268 @@ // use hashconsing::* ; -use common::* ; -use instance::Clause ; - - +use common::*; +use instance::Clause; /// Extracts qualifier-related information from a predicate application. fn qual_info_of( - eq_quals: & mut VarHMap, quals: & mut NuQuals, - pred: PrdIdx, args: & VarTerms, -) -> Res< (PrdIdx, VarHMap, TermSet) > { - debug_assert!( eq_quals.is_empty() ) ; - - // Qualifiers generated while looking at predicate applications. - let mut app_quals: TermSet = TermSet::with_capacity(17) ; - - // All the *clause var* to *pred var* maps for this predicate application. - let mut map: VarHMap = VarHMap::with_capacity( args.len() ) ; - - for (pred_var, term) in args.index_iter() { - let pred_var_typ = term.typ() ; - - // Parameter's a variable? - if let Some(clause_var_index) = term.var_idx() { - - // Clause variable's already known as parameter? - if let Some(other_pred_var) = map.get( - & clause_var_index - ).cloned() { - // Equality qualifier. - app_quals.insert( - term::eq( - term::var(pred_var, pred_var_typ), other_pred_var.clone() - ) - ) ; - } else { - // Add to map. - let _prev = map.insert( - clause_var_index, term::var(pred_var, pred_var_typ) - ) ; - debug_assert!( _prev.is_none() ) - } - - } else { + eq_quals: &mut VarHMap, + quals: &mut NuQuals, + pred: PrdIdx, + args: &VarTerms, +) -> Res<(PrdIdx, VarHMap, TermSet)> { + debug_assert!(eq_quals.is_empty()); + + // Qualifiers generated while looking at predicate applications. + let mut app_quals: TermSet = TermSet::with_capacity(17); + + // All the *clause var* to *pred var* maps for this predicate application. + let mut map: VarHMap = VarHMap::with_capacity(args.len()); + + for (pred_var, term) in args.index_iter() { + let pred_var_typ = term.typ(); + + // Parameter's a variable? + if let Some(clause_var_index) = term.var_idx() { + // Clause variable's already known as parameter? + if let Some(other_pred_var) = map.get(&clause_var_index).cloned() { + // Equality qualifier. + app_quals.insert(term::eq( + term::var(pred_var, pred_var_typ), + other_pred_var.clone(), + )); + } else { + // Add to map. + let _prev = map.insert(clause_var_index, term::var(pred_var, pred_var_typ)); + debug_assert!(_prev.is_none()) + } + } else { + // Modulo constraint? + if term.typ().is_int() { + let mut maybe_cmul = Some(term); + let mut maybe_add = None; + + // Term's an addition? + if let Some(args) = term.add_inspect() { + if args.len() != 2 { + maybe_cmul = None + } else if args[0].val().is_some() { + maybe_add = Some(&args[0]); + maybe_cmul = Some(&args[1]) + } else if args[1].val().is_some() { + maybe_add = Some(&args[1]); + maybe_cmul = Some(&args[0]) + } else { + maybe_cmul = None + } + } - // Modulo constraint? - if term.typ().is_int() { - let mut maybe_cmul = Some(term) ; - let mut maybe_add = None ; - - // Term's an addition? - if let Some(args) = term.add_inspect() { - if args.len() != 2 { - maybe_cmul = None - } else if args[0].val().is_some() { - maybe_add = Some(& args[0]) ; - maybe_cmul = Some(& args[1]) - } else if args[1].val().is_some() { - maybe_add = Some(& args[1]) ; - maybe_cmul = Some(& args[0]) - } else { - maybe_cmul = None - } - } + // Multiplication by a constant? + if let Some(term) = maybe_cmul { + if let Some((val, _)) = term.cmul_inspect() { + let qual = term::eq( + term::modulo(term::var(pred_var, typ::int()), val.to_term().unwrap()), + maybe_add.cloned().unwrap_or_else(|| term::int(0)), + ); + quals.insert(qual, pred)?; + } + } + } - // Multiplication by a constant? - if let Some(term) = maybe_cmul { - if let Some((val, _)) = term.cmul_inspect() { - let qual = term::eq( - term::modulo( - term::var( pred_var, typ::int() ), val.to_term().unwrap() - ), - maybe_add.cloned().unwrap_or_else( - || term::int(0) - ) - ) ; - quals.insert(qual, pred) ? ; - } - } - } + // Parameter's not a variable, store potential equality. + let _prev = eq_quals.insert(pred_var, term.clone()); + debug_assert!(_prev.is_none()); - // Parameter's not a variable, store potential equality. - let _prev = eq_quals.insert( pred_var, term.clone() ) ; - debug_assert!( _prev.is_none() ) ; - - // Try to revert the term. - if let Some((var, term)) = term.invert_var( - pred_var, pred_var_typ - ) { - if ! map.contains_key(& var) { - map.insert(var, term) ; - } else if let Some(other_pred_var) = map.get(& var) { - app_quals.insert( - term::eq( other_pred_var.clone(), term ) - ) ; + // Try to revert the term. + if let Some((var, term)) = term.invert_var(pred_var, pred_var_typ) { + if !map.contains_key(&var) { + map.insert(var, term); + } else if let Some(other_pred_var) = map.get(&var) { + app_quals.insert(term::eq(other_pred_var.clone(), term)); + } + } } - } - } - } - - for (pred_var, term) in eq_quals.drain() { - if let Some((term, _)) = term.subst_total(& map) { - app_quals.insert( - term::eq( - term::var( pred_var, term.typ() ), term - ) - ) ; + for (pred_var, term) in eq_quals.drain() { + if let Some((term, _)) = term.subst_total(&map) { + app_quals.insert(term::eq(term::var(pred_var, term.typ()), term)); + } } - } - if ! app_quals.is_empty() { - let build_conj = app_quals.len() > 1 ; - let mut conj = Vec::with_capacity( app_quals.len() ) ; + if !app_quals.is_empty() { + let build_conj = app_quals.len() > 1; + let mut conj = Vec::with_capacity(app_quals.len()); - for term in & app_quals { - if build_conj { - conj.push( term.clone() ) - } - quals.insert(term.clone(), pred) ? ; - } + for term in &app_quals { + if build_conj { + conj.push(term.clone()) + } + quals.insert(term.clone(), pred)?; + } - if build_conj { - let term = term::and(conj) ; - quals.insert(term, pred) ? ; + if build_conj { + let term = term::and(conj); + quals.insert(term, pred)?; + } } - } - Ok( (pred, map, app_quals) ) + Ok((pred, map, app_quals)) } - - - - /// Generates qualifiers from two terms. fn qual_of_terms Res<()>>( - mut f: F, term: & Term, othr: & Term, clause_count: usize + mut f: F, + term: &Term, + othr: &Term, + clause_count: usize, ) -> Res<()> { - match ( - term.app_inspect(), othr.app_inspect() - ) { - - ( - Some((op_1 @ Op::Ge, term_args)), - Some((op_2 @ Op::Gt, othr_args)) - ) | - ( - Some((op_1 @ Op::Gt, term_args)), - Some((op_2 @ Op::Ge, othr_args)) - ) | - ( - Some((op_1 @ Op::Gt, term_args)), - Some((op_2 @ Op::Gt, othr_args)) - ) | - ( - Some((op_1 @ Op::Ge, term_args)), - Some((op_2 @ Op::Ge, othr_args)) - ) | - - ( - Some((op_1 @ Op::Eql, term_args)), - Some((op_2 @ Op::Gt, othr_args)) - ) | - ( - Some((op_1 @ Op::Gt, term_args)), - Some((op_2 @ Op::Eql, othr_args)) - ) | - - ( - Some((op_1 @ Op::Ge, term_args)), - Some((op_2 @ Op::Eql, othr_args)) - ) | - ( - Some((op_1 @ Op::Eql, term_args)), - Some((op_2 @ Op::Ge, othr_args)) - ) | - - ( - Some((op_1 @ Op::Eql, term_args)), - Some((op_2 @ Op::Eql, othr_args)) - ) => { - - if term_args[0].typ().is_arith() - && term_args[0].typ() == othr_args[0].typ() { - let nu_lhs = term::add( - vec![ term_args[0].clone(), othr_args[0].clone() ] - ) ; - - let mut old_vars_1 = term::vars(& term_args[0]) ; - let mut old_vars_2 = term::vars(& othr_args[0]) ; - let mut nu_vars = term::vars(& nu_lhs) ; - - let use_qual = clause_count < 35 || ( - nu_vars.len() <= 2 - ) || ( - nu_vars.len() < old_vars_1.len() + old_vars_2.len() - ) ; - - if use_qual { - log! { @4 - "from {}", term ; - " {}", othr - } - } else { - // log! { @1 - // " " ; - // "skipping" ; - // "from {}", term ; - // " {}", othr ; - // " -> {}", nu_lhs - // } - } - - if use_qual { - let op = match (op_1, op_2) { - (_, Op::Gt) | - (Op::Gt, _) => Op::Gt, - - (_, Op::Ge) | - (Op::Ge, _) => Op::Ge, - - (Op::Eql, Op::Eql) => Op::Eql, - - _ => unreachable!(), - } ; + match (term.app_inspect(), othr.app_inspect()) { + (Some((op_1 @ Op::Ge, term_args)), Some((op_2 @ Op::Gt, othr_args))) + | (Some((op_1 @ Op::Gt, term_args)), Some((op_2 @ Op::Ge, othr_args))) + | (Some((op_1 @ Op::Gt, term_args)), Some((op_2 @ Op::Gt, othr_args))) + | (Some((op_1 @ Op::Ge, term_args)), Some((op_2 @ Op::Ge, othr_args))) + | (Some((op_1 @ Op::Eql, term_args)), Some((op_2 @ Op::Gt, othr_args))) + | (Some((op_1 @ Op::Gt, term_args)), Some((op_2 @ Op::Eql, othr_args))) + | (Some((op_1 @ Op::Ge, term_args)), Some((op_2 @ Op::Eql, othr_args))) + | (Some((op_1 @ Op::Eql, term_args)), Some((op_2 @ Op::Ge, othr_args))) + | (Some((op_1 @ Op::Eql, term_args)), Some((op_2 @ Op::Eql, othr_args))) => { + if term_args[0].typ().is_arith() && term_args[0].typ() == othr_args[0].typ() { + let nu_lhs = term::add(vec![term_args[0].clone(), othr_args[0].clone()]); + + let mut old_vars_1 = term::vars(&term_args[0]); + let mut old_vars_2 = term::vars(&othr_args[0]); + let mut nu_vars = term::vars(&nu_lhs); + + let use_qual = clause_count < 35 + || (nu_vars.len() <= 2) + || (nu_vars.len() < old_vars_1.len() + old_vars_2.len()); + + if use_qual { + log! { @4 + "from {}", term ; + " {}", othr + } + } else { + // log! { @1 + // " " ; + // "skipping" ; + // "from {}", term ; + // " {}", othr ; + // " -> {}", nu_lhs + // } + } - let nu_term = term::app( - op, vec![ - nu_lhs, term::add( - vec![ - term_args[1].clone(), othr_args[1].clone() - ] - ) - ] - ) ; - f( nu_term.clone() ) ? ; - - log! { @4 " -> {}", nu_term } - - if op_1 == Op::Eql { - let nu_lhs = term::sub( - vec![ othr_args[0].clone(), term_args[0].clone() ] - ) ; - let nu_rhs = term::sub( - vec![ othr_args[1].clone(), term_args[1].clone() ] - ) ; - let nu_term = term::app( op, vec![ nu_lhs, nu_rhs ] ) ; - f(nu_term) ? ; - } else if op_2 == Op::Eql { - let nu_lhs = term::sub( - vec![ term_args[0].clone(), othr_args[0].clone() ] - ) ; - let nu_rhs = term::sub( - vec![ term_args[1].clone(), othr_args[1].clone() ] - ) ; - let nu_term = term::app( op, vec![ nu_lhs, nu_rhs ] ) ; - f(nu_term) ? ; - } + if use_qual { + let op = match (op_1, op_2) { + (_, Op::Gt) | (Op::Gt, _) => Op::Gt, + + (_, Op::Ge) | (Op::Ge, _) => Op::Ge, + + (Op::Eql, Op::Eql) => Op::Eql, + + _ => unreachable!(), + }; + + let nu_term = term::app( + op, + vec![ + nu_lhs, + term::add(vec![term_args[1].clone(), othr_args[1].clone()]), + ], + ); + f(nu_term.clone())?; + + log! { @4 " -> {}", nu_term } + + if op_1 == Op::Eql { + let nu_lhs = term::sub(vec![othr_args[0].clone(), term_args[0].clone()]); + let nu_rhs = term::sub(vec![othr_args[1].clone(), term_args[1].clone()]); + let nu_term = term::app(op, vec![nu_lhs, nu_rhs]); + f(nu_term)?; + } else if op_2 == Op::Eql { + let nu_lhs = term::sub(vec![term_args[0].clone(), othr_args[0].clone()]); + let nu_rhs = term::sub(vec![term_args[1].clone(), othr_args[1].clone()]); + let nu_term = term::app(op, vec![nu_lhs, nu_rhs]); + f(nu_term)?; + } + } + } } - } - - }, - _ => (), - } - Ok(()) + _ => (), + } + Ok(()) } - - - - - - - - /// Qualifier version of a term. -fn qual_of_term(term: & Term, map: & VarHMap) -> Option { - if let Some( (qual, true) ) = term.subst_total(& map) { - if let Some(qual) = qual.rm_neg() { - Some(qual) +fn qual_of_term(term: &Term, map: &VarHMap) -> Option { + if let Some((qual, true)) = term.subst_total(&map) { + if let Some(qual) = qual.rm_neg() { + Some(qual) + } else { + Some(qual) + } } else { - Some(qual) + None } - } else { - None - } } - - - - - /// Extracts some qualifiers from a clause. /// /// # TO DO /// /// - write an explanation of what actually happens /// - and some tests, probably -fn qualifiers_of_clause( - instance: & Instance, clause: & Clause, quals: & mut NuQuals -) -> Res<()> { - // if clause.from_unrolling { return Ok(()) } - - let build_conj = instance.clauses().len() < 2000 && conf.ice.mine_conjs ; +fn qualifiers_of_clause(instance: &Instance, clause: &Clause, quals: &mut NuQuals) -> Res<()> { + // if clause.from_unrolling { return Ok(()) } - // Variable to term maps, based on the way the predicates are used. - let mut maps = vec![] ; + let build_conj = instance.clauses().len() < 2000 && conf.ice.mine_conjs; - scoped! { - // Represents equalities between *pred vars* and terms over *clause - // variables*. These will be added to `app_quals` if the total - // substitution of the term by `map` succeeds. - let mut eq_quals = VarHMap::with_capacity(7) ; + // Variable to term maps, based on the way the predicates are used. + let mut maps = vec![]; - clause.all_pred_apps_do( - |pred, args| { - maps.push( - qual_info_of(& mut eq_quals, quals, pred, args) ? - ) ; - Ok(()) - } - ) ? - } + scoped! { + // Represents equalities between *pred vars* and terms over *clause + // variables*. These will be added to `app_quals` if the total + // substitution of the term by `map` succeeds. + let mut eq_quals = VarHMap::with_capacity(7) ; - apply_mappings( - quals, clause, build_conj, maps, instance.clauses().len() - ) ? ; + clause.all_pred_apps_do( + |pred, args| { + maps.push( + qual_info_of(& mut eq_quals, quals, pred, args) ? + ) ; + Ok(()) + } + ) ? + } - Ok(()) + apply_mappings(quals, clause, build_conj, maps, instance.clauses().len())?; + Ok(()) } - - - - /// Applies a mapping for some predicate on a clause. fn apply_mappings( - quals: & mut NuQuals, clause: & Clause, build_conj: bool, - maps: Vec<(PrdIdx, VarHMap, TermSet)>, clause_count: usize, + quals: &mut NuQuals, + clause: &Clause, + build_conj: bool, + maps: Vec<(PrdIdx, VarHMap, TermSet)>, + clause_count: usize, ) -> Res<()> { - let term_count = clause.lhs_terms().len() ; - - // Stores the subterms of `lhs_terms`. - let mut subterms = Vec::with_capacity(7) ; - // Stores all (sub-)terms. - let mut all_terms = if term_count <= 100 { - Some( - TermSet::with_capacity( clause.lhs_terms().len() ) - ) - } else { None } ; - - macro_rules! all_terms { + let term_count = clause.lhs_terms().len(); + + // Stores the subterms of `lhs_terms`. + let mut subterms = Vec::with_capacity(7); + // Stores all (sub-)terms. + let mut all_terms = if term_count <= 100 { + Some(TermSet::with_capacity(clause.lhs_terms().len())) + } else { + None + }; + + macro_rules! all_terms { ( $fun:ident( $($args:tt)* ) ) => ( if let Some(all_terms) = all_terms.as_mut() { all_terms.$fun($($args)*) ; @@ -421,495 +327,450 @@ fn apply_mappings( ) ; } - // Stores all top terms. - let mut conj = TermSet::with_capacity( - clause.lhs_terms().len() - ) ; - - // Look for atoms and try to apply the mappings. - for (pred, map, app_quals) in maps { - all_terms!( clear() ) ; - conj.clear() ; - - for term in clause.lhs_terms() { - - if let Some( (term, true) ) = term.subst_total(& map) { - all_terms!( insert( term.clone() ) ) ; - conj.insert( term.clone() ) ; - let term = if let Some(term) = term.rm_neg() { - term - } else { term } ; - quals.insert(term, pred) ? ; - () - } + // Stores all top terms. + let mut conj = TermSet::with_capacity(clause.lhs_terms().len()); + + // Look for atoms and try to apply the mappings. + for (pred, map, app_quals) in maps { + all_terms!(clear()); + conj.clear(); + + for term in clause.lhs_terms() { + if let Some((term, true)) = term.subst_total(&map) { + all_terms!(insert(term.clone())); + conj.insert(term.clone()); + let term = if let Some(term) = term.rm_neg() { + term + } else { + term + }; + quals.insert(term, pred)?; + () + } - debug_assert!( subterms.is_empty() ) ; - subterms.push(term) ; + debug_assert!(subterms.is_empty()); + subterms.push(term); - while let Some(subterm) = subterms.pop() { - if let Some( (qual, true) ) = subterm.subst_total(& map) { - all_terms!( insert(qual) ) ; - } + while let Some(subterm) = subterms.pop() { + if let Some((qual, true)) = subterm.subst_total(&map) { + all_terms!(insert(qual)); + } - match subterm.app_inspect() { - Some( (Op::Or, terms) ) | - Some( (Op::And, terms) ) | - Some( (Op::Not, terms) ) | - Some( (Op::Impl, terms) ) => for term in terms { - subterms.push(term) ; - if let Some(term) = qual_of_term(term, & map) { - quals.insert(term, pred) ? ; + match subterm.app_inspect() { + Some((Op::Or, terms)) + | Some((Op::And, terms)) + | Some((Op::Not, terms)) + | Some((Op::Impl, terms)) => for term in terms { + subterms.push(term); + if let Some(term) = qual_of_term(term, &map) { + quals.insert(term, pred)?; + } + }, + + // Some( (Op::Eql, terms) ) | + // Some( (Op::Distinct, terms) ) => if terms.iter().all( + // |term| term.typ().is_bool() + // ) { + // for term in terms { + // subterms.push(term) ; + // if let Some(term) = qual_of_term(term, & map) { + // quals.insert(term, pred) ? ; + // } + // } + // } else if let Some( (qual, true) ) = subterm.subst_total(& map) { + // all_terms!().insert(qual) ; + // }, + _ => if let Some((qual, true)) = subterm.subst_total(&map) { + all_terms!(insert(qual)); + }, + } } - }, - - // Some( (Op::Eql, terms) ) | - // Some( (Op::Distinct, terms) ) => if terms.iter().all( - // |term| term.typ().is_bool() - // ) { - // for term in terms { - // subterms.push(term) ; - // if let Some(term) = qual_of_term(term, & map) { - // quals.insert(term, pred) ? ; - // } - // } - // } else if let Some( (qual, true) ) = subterm.subst_total(& map) { - // all_terms!().insert(qual) ; - // }, - - _ => if let Some( (qual, true) ) = subterm.subst_total(& map) { - all_terms!( insert(qual) ) ; - } } - } - } - - if build_conj { - quals.insert( - term::and( - app_quals.iter().cloned().collect() - ), pred - ) ? ; - if conj.len() > 1 { - quals.insert( - term::and( conj.iter().cloned().collect() ), pred - ) ? ; - quals.insert( - term::and( conj.drain().chain(app_quals).collect() ), pred - ) ? ; - } - } + if build_conj { + quals.insert(term::and(app_quals.iter().cloned().collect()), pred)?; + if conj.len() > 1 { + quals.insert(term::and(conj.iter().cloned().collect()), pred)?; + quals.insert(term::and(conj.drain().chain(app_quals).collect()), pred)?; + } + } - if let Some(all_terms) = all_terms.as_ref() { - let mut all_terms = all_terms.iter() ; - - if all_terms.len() <= 100 { - while let Some(term) = all_terms.next() { - for other in all_terms.clone() { - qual_of_terms( - |qual| { quals.insert(qual, pred) ? ; Ok(()) }, - term, other, clause_count - ) ? - } + if let Some(all_terms) = all_terms.as_ref() { + let mut all_terms = all_terms.iter(); + + if all_terms.len() <= 100 { + while let Some(term) = all_terms.next() { + for other in all_terms.clone() { + qual_of_terms( + |qual| { + quals.insert(qual, pred)?; + Ok(()) + }, + term, + other, + clause_count, + )? + } + } + } } - } } - } - - Ok(()) + Ok(()) } - - - - - /// Mines the clauses in an instance for qualifiers. -fn mine_instance(instance: & Instance, quals: & mut NuQuals) -> Res<()> { - // Add boolean qualifiers for all predicate's bool vars. - for pred in instance.preds() { - for (var, typ) in pred.sig.index_iter() { - let mut bool_vars = Vec::new() ; - if typ.is_bool() { - let var = term::var( var, typ::bool() ) ; - quals.insert( var.clone(), pred.idx ) ? ; - bool_vars.push(var) - } - if bool_vars.len() > 1 { - quals.insert( - term::and( bool_vars.clone() ), pred.idx - ) ? ; - quals.insert( - term::or(bool_vars), pred.idx - ) ? ; - } +fn mine_instance(instance: &Instance, quals: &mut NuQuals) -> Res<()> { + // Add boolean qualifiers for all predicate's bool vars. + for pred in instance.preds() { + for (var, typ) in pred.sig.index_iter() { + let mut bool_vars = Vec::new(); + if typ.is_bool() { + let var = term::var(var, typ::bool()); + quals.insert(var.clone(), pred.idx)?; + bool_vars.push(var) + } + if bool_vars.len() > 1 { + quals.insert(term::and(bool_vars.clone()), pred.idx)?; + quals.insert(term::or(bool_vars), pred.idx)?; + } + } } - } - // Mine all clauses. - for clause in instance.clauses() { - qualifiers_of_clause(instance, clause, quals) ? - } + // Mine all clauses. + for clause in instance.clauses() { + qualifiers_of_clause(instance, clause, quals)? + } - Ok(()) + Ok(()) } - - - - - - - - pub struct NuQuals { - instance: Arc, - quals: PrdMap< VarHMap< TermSet > >, - rng: Rng, + instance: Arc, + quals: PrdMap>, + rng: Rng, } impl NuQuals { - pub fn new(instance: & Arc, mine: bool) -> Res { - use rand::SeedableRng ; + pub fn new(instance: &Arc, mine: bool) -> Res { + use rand::SeedableRng; - let mut quals = PrdMap::with_capacity( instance.preds().len() ) ; - for _ in 0 .. instance.preds().len() { - quals.push( VarHMap::new() ) - } - let mut quals = NuQuals { - quals, - instance: instance.clone(), - rng: Rng::from_seed( [ 42 ; 16 ] ), - } ; - - let mut prev: TypMap = TypMap::new() ; - - if mine { - - 'all_preds: for pred_info in instance.preds() { - - if instance.is_known(pred_info.idx) { continue 'all_preds } - - // Add companion functions if any. - // fun::iter( - // |fun| { - // if fun.synthetic == Some(pred_info.idx) { - // let mut args = Vec::with_capacity( pred_info.sig.len() - 1 ) ; - // for (var, typ) in pred_info.sig.index_iter().take( - // pred_info.sig.len() - 1 - // ) { - // args.push( term::var(var, typ.clone()) ) - // } - // let fun_app = term::fun( - // fun.typ.clone(), fun.name.clone(), args - // ) ; - // let last: VarIdx = ( - // pred_info.sig.len() - 1 - // ).into() ; - // quals.insert( - // term::eq( - // term::var( last, fun.typ.clone() ), - // fun_app - // ), - // pred_info.idx - // ) ? ; - // } - // Ok(()) - // } - // ) ? ; - - let mut sig = pred_info.sig.index_iter() ; - prev.clear() ; - - for (var, typ) in sig { - if let Some(vars) = prev.get(typ) { - for v in vars { - quals.insert( - term::eq( - term::var(* v, typ.clone()), - term::var(var, typ.clone()), - ), - pred_info.idx - ) ? ; - } - } - - scoped! { - prev.entry( typ.clone() ).or_insert_with(VarSet::new).insert(var) ; - } - - match ** typ { - - typ::RTyp::Int => { - quals.insert( - term::ge( term::var(var, typ.clone()), - term::int(0) ), - pred_info.idx - ) ? ; - quals.insert( - term::le( term::var(var, typ.clone()), - term::int(0) ), - pred_info.idx - ) ? ; - quals.insert( - term::eq( term::var(var, typ.clone()), - term::int(0) ), - pred_info.idx - ) ? ; - // quals.insert( - // term::ge( term::var(var, typ.clone()), - // term::int(1) ), - // pred_info.idx - // ) ? ; - // quals.insert( - // term::le( term::var(var, typ.clone()), - // term::int(1) ), - // pred_info.idx - // ) ? ; - // quals.insert( - // term::eq( term::var(var, typ.clone()), - // term::int(1) ), - // pred_info.idx - // ) ? ; - }, - - typ::RTyp::Real => { - quals.insert( - term::ge( - term::var(var, typ.clone()), - term::real(Rat::from_integer(0.into())) - ), - pred_info.idx - ) ? ; - quals.insert( - term::le( - term::var(var, typ.clone()), - term::real(Rat::from_integer(0.into())) - ), - pred_info.idx - ) ? ; - quals.insert( - term::eq( - term::var(var, typ.clone()), - term::real(Rat::from_integer(0.into())) - ), - pred_info.idx - ) ? ; - // quals.insert( - // term::ge( - // term::var(var, typ.clone()), - // term::real(Rat::from_integer(1.into())) - // ), - // pred_info.idx - // ) ? ; - // quals.insert( - // term::le( - // term::var(var, typ.clone()), - // term::real(Rat::from_integer(1.into())) - // ), - // pred_info.idx - // ) ? ; - // quals.insert( - // term::eq( - // term::var(var, typ.clone()), - // term::real(Rat::from_integer(1.into())) - // ), - // pred_info.idx - // ) ? ; - }, - - typ::RTyp::Bool => { - let var = term::bool_var(var) ; - quals.insert( var.clone(), pred_info.idx ) ? ; - quals.insert(var, pred_info.idx) ? ; - }, - - typ::RTyp::Array { .. } => { - quals.insert( - term::eq( - term::var(var, typ.clone()), - typ.default_term() - ), - pred_info.idx - ) ? ; - }, - - typ::RTyp::DTyp { ref dtyp, .. } => { - for name in dtyp.news.keys() { - quals.insert( - term::dtyp_tst( - name.clone(), - term::var( var, typ.clone() ) - ), - pred_info.idx - ) ? ; - } - let functions = fun::Functions::new( typ.clone() ) ; - for fun in functions.from_typ { - if fun.typ.is_bool() { - quals.insert( - term::fun( - fun.typ.clone(), fun.name.clone(), - vec![ term::var(var, typ.clone()) ], - ), - pred_info.idx - ) ? ; + let mut quals = PrdMap::with_capacity(instance.preds().len()); + for _ in 0..instance.preds().len() { + quals.push(VarHMap::new()) + } + let mut quals = NuQuals { + quals, + instance: instance.clone(), + rng: Rng::from_seed([42; 16]), + }; + + let mut prev: TypMap = TypMap::new(); + + if mine { + 'all_preds: for pred_info in instance.preds() { + if instance.is_known(pred_info.idx) { + continue 'all_preds; + } + + // Add companion functions if any. + // fun::iter( + // |fun| { + // if fun.synthetic == Some(pred_info.idx) { + // let mut args = Vec::with_capacity( pred_info.sig.len() - 1 ) ; + // for (var, typ) in pred_info.sig.index_iter().take( + // pred_info.sig.len() - 1 + // ) { + // args.push( term::var(var, typ.clone()) ) + // } + // let fun_app = term::fun( + // fun.typ.clone(), fun.name.clone(), args + // ) ; + // let last: VarIdx = ( + // pred_info.sig.len() - 1 + // ).into() ; + // quals.insert( + // term::eq( + // term::var( last, fun.typ.clone() ), + // fun_app + // ), + // pred_info.idx + // ) ? ; + // } + // Ok(()) + // } + // ) ? ; + + let mut sig = pred_info.sig.index_iter(); + prev.clear(); + + for (var, typ) in sig { + if let Some(vars) = prev.get(typ) { + for v in vars { + quals.insert( + term::eq(term::var(*v, typ.clone()), term::var(var, typ.clone())), + pred_info.idx, + )?; + } + } + + scoped! { + prev.entry( typ.clone() ).or_insert_with(VarSet::new).insert(var) ; + } + + match **typ { + typ::RTyp::Int => { + quals.insert( + term::ge(term::var(var, typ.clone()), term::int(0)), + pred_info.idx, + )?; + quals.insert( + term::le(term::var(var, typ.clone()), term::int(0)), + pred_info.idx, + )?; + quals.insert( + term::eq(term::var(var, typ.clone()), term::int(0)), + pred_info.idx, + )?; + // quals.insert( + // term::ge( term::var(var, typ.clone()), + // term::int(1) ), + // pred_info.idx + // ) ? ; + // quals.insert( + // term::le( term::var(var, typ.clone()), + // term::int(1) ), + // pred_info.idx + // ) ? ; + // quals.insert( + // term::eq( term::var(var, typ.clone()), + // term::int(1) ), + // pred_info.idx + // ) ? ; + } + + typ::RTyp::Real => { + quals.insert( + term::ge( + term::var(var, typ.clone()), + term::real(Rat::from_integer(0.into())), + ), + pred_info.idx, + )?; + quals.insert( + term::le( + term::var(var, typ.clone()), + term::real(Rat::from_integer(0.into())), + ), + pred_info.idx, + )?; + quals.insert( + term::eq( + term::var(var, typ.clone()), + term::real(Rat::from_integer(0.into())), + ), + pred_info.idx, + )?; + // quals.insert( + // term::ge( + // term::var(var, typ.clone()), + // term::real(Rat::from_integer(1.into())) + // ), + // pred_info.idx + // ) ? ; + // quals.insert( + // term::le( + // term::var(var, typ.clone()), + // term::real(Rat::from_integer(1.into())) + // ), + // pred_info.idx + // ) ? ; + // quals.insert( + // term::eq( + // term::var(var, typ.clone()), + // term::real(Rat::from_integer(1.into())) + // ), + // pred_info.idx + // ) ? ; + } + + typ::RTyp::Bool => { + let var = term::bool_var(var); + quals.insert(var.clone(), pred_info.idx)?; + quals.insert(var, pred_info.idx)?; + } + + typ::RTyp::Array { .. } => { + quals.insert( + term::eq(term::var(var, typ.clone()), typ.default_term()), + pred_info.idx, + )?; + } + + typ::RTyp::DTyp { ref dtyp, .. } => { + for name in dtyp.news.keys() { + quals.insert( + term::dtyp_tst(name.clone(), term::var(var, typ.clone())), + pred_info.idx, + )?; + } + let functions = fun::Functions::new(typ.clone()); + for fun in functions.from_typ { + if fun.typ.is_bool() { + quals.insert( + term::fun( + fun.typ.clone(), + fun.name.clone(), + vec![term::var(var, typ.clone())], + ), + pred_info.idx, + )?; + } + } + } + + typ::RTyp::Unk => bail!("unexpected unknown type"), + } } - } - }, + } - typ::RTyp::Unk => bail!( - "unexpected unknown type" - ), - } + mine_instance(instance, &mut quals).chain_err(|| "during qualifier mining")? } - } - mine_instance(instance, & mut quals).chain_err( - || "during qualifier mining" - ) ? + Ok(quals) } - Ok(quals) - } - - pub fn insert( - & mut self, term: Term, pred: PrdIdx - ) -> Res { - let var_count = term::vars(& term).len() ; - let set = self.quals[pred].entry( - var_count.into() - ).or_insert_with( - || TermSet::with_capacity(103) - ) ; - - let is_new = set.insert(term) ; - Ok(is_new) - } + pub fn insert(&mut self, term: Term, pred: PrdIdx) -> Res { + let var_count = term::vars(&term).len(); + let set = self.quals[pred] + .entry(var_count.into()) + .or_insert_with(|| TermSet::with_capacity(103)); - - /// Real number of qualifiers considered. - pub fn real_qual_count(& self) -> usize { - let mut count = 0 ; - for sets in & self.quals { - for (_, terms) in sets { - count += terms.len() - } + let is_new = set.insert(term); + Ok(is_new) } - count - } - /// - pub fn wipe(& mut self) -> () {} - - pub fn log(& self) { - println!("; quals {{") ; - for (pred, terms) in self.quals.index_iter() { - if terms.iter().any( - |(_, terms)| ! terms.is_empty() - ) { - println!("; {}", conf.emph(& self.instance[pred].name)) ; - println!("; {}", self.instance[pred].sig) ; - for (_, terms) in terms { - for term in terms { - println!("; | {}", term) - } + /// Real number of qualifiers considered. + pub fn real_qual_count(&self) -> usize { + let mut count = 0; + for sets in &self.quals { + for (_, terms) in sets { + count += terms.len() + } } - } + count } - println!("; }}") - } - - pub fn quals_of_contains(& self, pred: PrdIdx, term: & Term) -> bool { - self.quals[pred].iter().any( - |(_, terms)| terms.contains(term) - ) - } - - pub fn quals_of(& self, pred: PrdIdx) -> & VarHMap< TermSet > { - & self.quals[pred] - } - - - - /// Returns the qualifier that maximized the input criterion in a non-zero - /// fashion, if any. Early-returns if the criterion is `>=` to the gain pivot - /// defined in the configuration at some point. - pub fn maximize( - & mut self, pred: PrdIdx, bias: Option, mut crit: Crit - ) -> Res< Option<(Term, f64)> > - where Crit: FnMut( & Term ) -> Res< Option > { - use rand::Rng ; - - let var_bias = if let Some(sample) = bias { - let mut set = VarSet::new() ; - for (var, val) in sample.index_iter() { - if val.is_known() { - set.insert(var) ; + /// + pub fn wipe(&mut self) -> () {} + + pub fn log(&self) { + println!("; quals {{"); + for (pred, terms) in self.quals.index_iter() { + if terms.iter().any(|(_, terms)| !terms.is_empty()) { + println!("; {}", conf.emph(&self.instance[pred].name)); + println!("; {}", self.instance[pred].sig); + for (_, terms) in terms { + for term in terms { + println!("; | {}", term) + } + } + } } - } - if set.is_empty() { - bail!("empty bias sample in gain maximization") - } - Some(set) - } else { - None - } ; + println!("; }}") + } - let mut best = None ; - let rng = & mut self.rng ; + pub fn quals_of_contains(&self, pred: PrdIdx, term: &Term) -> bool { + self.quals[pred] + .iter() + .any(|(_, terms)| terms.contains(term)) + } - let mut quals: Vec<_> = self.quals[pred].iter().filter_map( - | (count, terms) | if let Some(var_bias) = var_bias.as_ref() { - if var_bias.len() == ** count { - Some(terms) - } else { - None - } - } else { - Some(terms) - } - ).collect() ; + pub fn quals_of(&self, pred: PrdIdx) -> &VarHMap { + &self.quals[pred] + } - if conf.ice.rand_quals { - quals.sort_unstable_by( - |_, _| if 0.5 < rng.gen() { - ::std::cmp::Ordering::Greater + /// Returns the qualifier that maximized the input criterion in a non-zero + /// fashion, if any. Early-returns if the criterion is `>=` to the gain pivot + /// defined in the configuration at some point. + pub fn maximize( + &mut self, + pred: PrdIdx, + bias: Option, + mut crit: Crit, + ) -> Res> + where + Crit: FnMut(&Term) -> Res>, + { + use rand::Rng; + + let var_bias = if let Some(sample) = bias { + let mut set = VarSet::new(); + for (var, val) in sample.index_iter() { + if val.is_known() { + set.insert(var); + } + } + if set.is_empty() { + bail!("empty bias sample in gain maximization") + } + Some(set) } else { - ::std::cmp::Ordering::Less + None + }; + + let mut best = None; + let rng = &mut self.rng; + + let mut quals: Vec<_> = self.quals[pred] + .iter() + .filter_map(|(count, terms)| { + if let Some(var_bias) = var_bias.as_ref() { + if var_bias.len() == **count { + Some(terms) + } else { + None + } + } else { + Some(terms) + } + }).collect(); + + if conf.ice.rand_quals { + quals.sort_unstable_by(|_, _| { + if 0.5 < rng.gen() { + ::std::cmp::Ordering::Greater + } else { + ::std::cmp::Ordering::Less + } + }) } - ) - } - - for terms in quals { - // for terms in terms { - for term in terms { - if let Some(var_bias) = var_bias.as_ref() { - if var_bias != & term::vars(term) { - continue - } - } + for terms in quals { + // for terms in terms { + for term in terms { + if let Some(var_bias) = var_bias.as_ref() { + if var_bias != &term::vars(term) { + continue; + } + } - if let Some(value) = crit(term) ? { - best = if value > 0.9999 { - return Ok( Some((term.clone(), value)) ) - } else if let Some((best, best_value)) = best { - let diff = value - best_value ; - if diff > ::std::f64::EPSILON { - Some((term, value)) - } else { - Some((best, best_value)) + if let Some(value) = crit(term)? { + best = if value > 0.9999 { + return Ok(Some((term.clone(), value))); + } else if let Some((best, best_value)) = best { + let diff = value - best_value; + if diff > ::std::f64::EPSILON { + Some((term, value)) + } else { + Some((best, best_value)) + } + } else { + Some((term, value)) + } + } } - } else { - Some((term, value)) - } } - } + Ok(best.map(|(t, v)| (t.clone(), v))) } - - Ok( best.map(|(t,v)| (t.clone(), v)) ) - } } diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs index b823e4fb..993311e0 100644 --- a/src/learning/ice/synth/adt.rs +++ b/src/learning/ice/synth/adt.rs @@ -1,256 +1,233 @@ //! ADT qualifier synthesis. -use common::* ; -use ::fun::Functions ; +use common::*; +use fun::Functions; -use super::{ TermVals, TheoSynth } ; +use super::{TermVals, TheoSynth}; #[derive(Clone, Debug)] pub struct AdtSynth { - /// Expressivity level. - expressivity: usize, - /// Type this synthesizer handles. - typ: Typ, - /// Functions relevant for this type. - pub funs: Functions, + /// Expressivity level. + expressivity: usize, + /// Type this synthesizer handles. + typ: Typ, + /// Functions relevant for this type. + pub funs: Functions, } impl PartialEq for AdtSynth { - fn eq(& self, other: & Self) -> bool { - self.typ == other.typ - } + fn eq(&self, other: &Self) -> bool { + self.typ == other.typ + } } impl Eq for AdtSynth {} impl ::std::hash::Hash for AdtSynth { - fn hash(& self, hasher: & mut H) - where H: ::std::hash::Hasher { - self.typ.hash(hasher) - } + fn hash(&self, hasher: &mut H) + where + H: ::std::hash::Hasher, + { + self.typ.hash(hasher) + } } impl PartialOrd for AdtSynth { - fn partial_cmp(& self, other: & Self) -> Option<::std::cmp::Ordering> { - self.typ.partial_cmp(& other.typ) - } + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + self.typ.partial_cmp(&other.typ) + } } impl Ord for AdtSynth { - fn cmp(& self, other: & Self) -> ::std::cmp::Ordering { - self.typ.cmp(& other.typ) - } + fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + self.typ.cmp(&other.typ) + } } - impl AdtSynth { - /// Bails by saying the internal is inconsistent. - #[inline] - fn typ_problem(& self) -> Error { - "inconsistent type for ADT synth".into() - } - /// Retrieves the content of an option or bails. - #[inline] - fn get_opt(& self, opt: Option) -> Res { - if let Some(t) = opt { - Ok(t) - } else { - bail!( self.typ_problem() ) + /// Bails by saying the internal is inconsistent. + #[inline] + fn typ_problem(&self) -> Error { + "inconsistent type for ADT synth".into() } - } - - /// Projects a single value. - fn project_val( - & self, typ: & Typ, var: VarIdx, val: & Val, map: & mut TermVals - ) -> Res<()> { - if ! val.is_known() - || val.typ() != self.typ { - return Ok(()) + /// Retrieves the content of an option or bails. + #[inline] + fn get_opt(&self, opt: Option) -> Res { + if let Some(t) = opt { + Ok(t) + } else { + bail!(self.typ_problem()) + } } - let var = term::var( var, self.typ.clone() ) ; + /// Projects a single value. + fn project_val(&self, typ: &Typ, var: VarIdx, val: &Val, map: &mut TermVals) -> Res<()> { + if !val.is_known() || val.typ() != self.typ { + return Ok(()); + } - // Apply unary functions from `self.typ` to `typ`. - for fun in & self.funs.from_typ { - if & fun.typ != typ { - continue - } + let var = term::var(var, self.typ.clone()); - let input: VarMap<_> = vec![ val.clone() ].into() ; + // Apply unary functions from `self.typ` to `typ`. + for fun in &self.funs.from_typ { + if &fun.typ != typ { + continue; + } - let val = fun.def.eval(& input).chain_err( - || format!( - "while evaluating ({} {})", fun.name, val - ) - ) ? ; + let input: VarMap<_> = vec![val.clone()].into(); - let term = term::fun( - typ.clone(), fun.name.clone(), vec![ var.clone() ] - ) ; + let val = fun + .def + .eval(&input) + .chain_err(|| format!("while evaluating ({} {})", fun.name, val))?; - let prev = map.insert(term, val) ; - debug_assert! { prev.is_none() } - } + let term = term::fun(typ.clone(), fun.name.clone(), vec![var.clone()]); - // Retrieve this value's constructor. - let (val_typ, val_cons, val_args) = self.get_opt( - val.dtyp_inspect() - ) ? ; - debug_assert_eq! { val_typ, & self.typ } - - // Apply selectors from the variant of `val` to `typ`. - let (val_dtyp, _) = self.get_opt( - val_typ.dtyp_inspect() - ) ? ; - - let selectors = self.get_opt( - val_dtyp.news.get(val_cons) - ) ? ; - - debug_assert_eq! { selectors.len(), val_args.len() } - - for ((slc, _), val_arg) in selectors.iter().zip( val_args.iter() ) { - if & val_arg.typ() == typ { - let term = term::dtyp_slc( - typ.clone(), slc.clone(), var.clone() - ) ; - let prev = map.insert( term, val_arg.clone() ) ; - debug_assert! { prev.is_none() } - } - } + let prev = map.insert(term, val); + debug_assert! { prev.is_none() } + } - Ok(()) - } + // Retrieve this value's constructor. + let (val_typ, val_cons, val_args) = self.get_opt(val.dtyp_inspect())?; + debug_assert_eq! { val_typ, & self.typ } -} + // Apply selectors from the variant of `val` to `typ`. + let (val_dtyp, _) = self.get_opt(val_typ.dtyp_inspect())?; + let selectors = self.get_opt(val_dtyp.news.get(val_cons))?; + debug_assert_eq! { selectors.len(), val_args.len() } + for ((slc, _), val_arg) in selectors.iter().zip(val_args.iter()) { + if &val_arg.typ() == typ { + let term = term::dtyp_slc(typ.clone(), slc.clone(), var.clone()); + let prev = map.insert(term, val_arg.clone()); + debug_assert! { prev.is_none() } + } + } + Ok(()) + } +} impl TheoSynth for AdtSynth { - fn typ(& self) -> & Typ { & self.typ } - - fn is_done(& self) -> bool { - self.expressivity > 0 - } + fn typ(&self) -> &Typ { + &self.typ + } - fn restart(& mut self) { - self.expressivity = 0 - } + fn is_done(&self) -> bool { + self.expressivity > 0 + } - fn increment(& mut self) { - self.expressivity += 1 - } + fn restart(&mut self) { + self.expressivity = 0 + } - fn synth( - & mut self, f: F, sample: & VarVals, others: & mut TermVals, - _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - match self.expressivity { - 0 => self.eq_synth(f, sample, others), + fn increment(&mut self) { + self.expressivity += 1 + } - _ => Ok(false), + fn synth( + &mut self, + f: F, + sample: &VarVals, + others: &mut TermVals, + _profiler: &Profiler, + ) -> Res + where + F: FnMut(Term) -> Res, + { + match self.expressivity { + 0 => self.eq_synth(f, sample, others), + + _ => Ok(false), + } } - } - fn project( - & self, sample: & VarVals, typ: & Typ, map: & mut TermVals - ) -> Res<()> { + fn project(&self, sample: &VarVals, typ: &Typ, map: &mut TermVals) -> Res<()> { + for (var, val) in sample.index_iter() { + self.project_val(typ, var, val, map)? + } - for (var, val) in sample.index_iter() { - self.project_val(typ, var, val, map) ? + Ok(()) } - - Ok(()) - } } - - impl AdtSynth { - /// Constructor. - pub fn new(typ: Typ) -> Self { - let funs = Functions::new( typ.clone() ) ; - AdtSynth { expressivity: 0, typ, funs } - } - - /// True if the synthesizer can project values to int. - pub fn can_project_to_int(& self) -> bool { - for fun in & self.funs.from_typ { - if fun.typ.is_int() { return true } + /// Constructor. + pub fn new(typ: Typ) -> Self { + let funs = Functions::new(typ.clone()); + AdtSynth { + expressivity: 0, + typ, + funs, + } } - false - } - /// True if the synthesizer can project values to real. - pub fn can_project_to_real(& self) -> bool { - for fun in & self.funs.from_typ { - if fun.typ.is_real() { return true } + /// True if the synthesizer can project values to int. + pub fn can_project_to_int(&self) -> bool { + for fun in &self.funs.from_typ { + if fun.typ.is_int() { + return true; + } + } + false } - false - } - - /// Generates equalities between variables of some ADT. - fn eq_synth( - & self, mut f: F, sample: & VarVals, others: & mut TermVals - ) -> Res - where F: FnMut(Term) -> Res { - let mut previous: BTreeSet<(Term, Val)> = BTreeSet::new() ; - - for (var, val) in sample.index_iter() { - if val.is_known() - && val.typ() == self.typ { - let var = term::var( var, self.typ.clone() ) ; - - let mut extended = Vec::with_capacity( - self.funs.from_to_typ.len() + 1 - ) ; - - if f( - term::eq( var.clone(), term::cst( val.clone() ) ) - ) ? { - return Ok(true) + + /// True if the synthesizer can project values to real. + pub fn can_project_to_real(&self) -> bool { + for fun in &self.funs.from_typ { + if fun.typ.is_real() { + return true; + } } + false + } - let mut input: Option< VarMap > = None ; + /// Generates equalities between variables of some ADT. + fn eq_synth(&self, mut f: F, sample: &VarVals, others: &mut TermVals) -> Res + where + F: FnMut(Term) -> Res, + { + let mut previous: BTreeSet<(Term, Val)> = BTreeSet::new(); - for fun in & self.funs.from_to_typ { - let input = input.get_or_insert_with( - || vec![ val.clone() ].into() - ) ; + for (var, val) in sample.index_iter() { + if val.is_known() && val.typ() == self.typ { + let var = term::var(var, self.typ.clone()); - let val = fun.def.eval(input).chain_err( - || format!( - "while evaluating ({} {})", fun.name, val - ) - ) ? ; + let mut extended = Vec::with_capacity(self.funs.from_to_typ.len() + 1); - extended.push(( - term::fun( - self.typ.clone(), fun.name.clone(), vec![ var.clone() ] - ), val - )) - } + if f(term::eq(var.clone(), term::cst(val.clone())))? { + return Ok(true); + } - extended.push(( var, val.clone() )) ; + let mut input: Option> = None; - for (t_1, v_1) in extended { + for fun in &self.funs.from_to_typ { + let input = input.get_or_insert_with(|| vec![val.clone()].into()); - for (t_2, _) in previous.iter().map( - |(t,v)| (t,v) - ).chain( others.iter() ) { - if f( - term::eq( t_1.clone(), t_2.clone() ) - ) ? { - return Ok(true) - } - } + let val = fun + .def + .eval(input) + .chain_err(|| format!("while evaluating ({} {})", fun.name, val))?; + + extended.push(( + term::fun(self.typ.clone(), fun.name.clone(), vec![var.clone()]), + val, + )) + } + + extended.push((var, val.clone())); - previous.insert( (t_1, v_1) ) ; + for (t_1, v_1) in extended { + for (t_2, _) in previous.iter().map(|(t, v)| (t, v)).chain(others.iter()) { + if f(term::eq(t_1.clone(), t_2.clone()))? { + return Ok(true); + } + } + + previous.insert((t_1, v_1)); + } + } } - } - } - Ok(false) - } + Ok(false) + } } - diff --git a/src/learning/ice/synth/helpers.rs b/src/learning/ice/synth/helpers.rs index 2a6ed314..48ab48e9 100644 --- a/src/learning/ice/synth/helpers.rs +++ b/src/learning/ice/synth/helpers.rs @@ -1,18 +1,18 @@ //! Macros factoring code for the synthesis process. - -use common::* ; -use super::TermVals ; - +use super::TermVals; +use common::*; /// Applies `$f` to `$term`. /// /// Early returns if the application is an error. Early returns `Ok(true)` if /// the application yields true. macro_rules! apply { - ($f:ident to $term:expr) => ( - if $f($term) ? { return Ok(true) } - ) ; + ($f:ident to $term:expr) => { + if $f($term)? { + return Ok(true); + } + }; } /// Simple arithmetic synthesis. @@ -25,118 +25,96 @@ macro_rules! apply { /// - `t_1 - t_2 >= n`, `t_1 - t_2 <= n`, #[macro_export] macro_rules! simple_arith_synth { - ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => ({ - - let val_term = term::$constructor( $val.clone() ) ; - let term = term::app( - Op::Ge, vec![ $term.clone(), val_term.clone() ] - ) ; - apply! { $f to term } - let term = term::app( - Op::Le, vec![ $term.clone(), val_term.clone() ] - ) ; - apply! { $f to term } - let term = term::app( - Op::Eql, vec![ $term.clone(), val_term ] - ) ; - apply! { $f to term } - - for & (ref other_term, ref other_val) in & $previous { - if & $val == other_val { - let term = term::eq( - $term.clone(), other_term.clone() - ) ; + ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => {{ + let val_term = term::$constructor($val.clone()); + let term = term::app(Op::Ge, vec![$term.clone(), val_term.clone()]); apply! { $f to term } - } - if - (& $val) == * other_val { - let term = term::eq( - $term.clone(), term::sub( vec![ other_term.clone() ] ) - ) ; + let term = term::app(Op::Le, vec![$term.clone(), val_term.clone()]); apply! { $f to term } - } - - let add = term::app( - Op::Add, vec![ $term.clone(), other_term.clone() ] - ) ; - let add_val = term::$constructor( & $val + other_val ) ; - let term = term::app( - Op::Ge, vec![ add.clone(), add_val.clone() ] - ) ; - apply! { $f to term } - let term = term::app( - Op::Le, vec![ add, add_val ] - ) ; - apply! { $f to term } - - let sub = term::app( - Op::Sub, vec![ $term.clone(), other_term.clone() ] - ) ; - let sub_val = term::$constructor( & $val - other_val ) ; - let term = term::app( - Op::Ge, vec![ sub.clone(), sub_val.clone() ] - ) ; - apply! { $f to term } - let term = term::app( - Op::Le, vec![ sub, sub_val ] - ) ; - apply! { $f to term } - } + let term = term::app(Op::Eql, vec![$term.clone(), val_term]); + apply! { $f to term } + + for &(ref other_term, ref other_val) in &$previous { + if &$val == other_val { + let term = term::eq($term.clone(), other_term.clone()); + apply! { $f to term } + } + if -(&$val) == *other_val { + let term = term::eq($term.clone(), term::sub(vec![other_term.clone()])); + apply! { $f to term } + } + + let add = term::app(Op::Add, vec![$term.clone(), other_term.clone()]); + let add_val = term::$constructor(&$val + other_val); + let term = term::app(Op::Ge, vec![add.clone(), add_val.clone()]); + apply! { $f to term } + let term = term::app(Op::Le, vec![add, add_val]); + apply! { $f to term } + + let sub = term::app(Op::Sub, vec![$term.clone(), other_term.clone()]); + let sub_val = term::$constructor(&$val - other_val); + let term = term::app(Op::Ge, vec![sub.clone(), sub_val.clone()]); + apply! { $f to term } + let term = term::app(Op::Le, vec![sub, sub_val]); + apply! { $f to term } + } - $previous.insert(($term, $val.clone())) ; - }) ; + $previous.insert(($term, $val.clone())); + }}; } /// Non-linear arithmetic synthesis for integer terms. #[macro_export] macro_rules! arith_synth_non_lin { - ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => ({ - use num::Zero ; - if ! $val.is_zero() { - for & (ref other_term, ref other_val) in & $previous { - if other_val.is_zero() { - continue + ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => {{ + use num::Zero; + if !$val.is_zero() { + for &(ref other_term, ref other_val) in &$previous { + if other_val.is_zero() { + continue; + } + let (lhs, rhs) = arith_synth_non_lin!( + @lhs $constructor $term, $val, other_term, other_val + ); + + let term = term::ge(lhs.clone(), rhs.clone()); + apply! { $f to term } + + let term = term::le(lhs.clone(), rhs.clone()); + apply! { $f to term } + + let term = term::eq(lhs, rhs); + apply! { $f to term } + } + $previous.insert(($term, $val)); } - let (lhs, rhs) = arith_synth_non_lin!( - @lhs $constructor $term, $val, other_term, other_val - ) ; - - let term = term::ge( lhs.clone(), rhs.clone() ) ; - apply! { $f to term } - - let term = term::le( lhs.clone(), rhs.clone() ) ; - apply! { $f to term } - - let term = term::eq( lhs, rhs ) ; - apply! { $f to term } - } - $previous.insert(($term, $val)) ; - } - }) ; - - // Assumes $other_val is not 0. - (@lhs int $term:expr, $val:expr, $other_term:expr, $other_val:expr) => ({ - let (lft, rgt, div, rem) = ( - $term.clone(), $other_term.clone(), - & $val / $other_val, & $val % $other_val - ) ; - - ( - term::sub( - vec![ lft, term::cmul(div, rgt), term::int(rem) ] - ), - term::int(0) - ) - }) ; - - // Assumes $other_val is not 0. - (@lhs real $term:expr, $val:expr, $other_term:expr, $other_val:expr) => (( - term::sub( - vec![ - $term.clone(), term::cmul( $val / $other_val, $other_term.clone() ) - ] - ), - term::real( Rat::new(0.into(), 1.into()) ) - )) ; + }}; + + // Assumes $other_val is not 0. + (@lhs int $term:expr, $val:expr, $other_term:expr, $other_val:expr) => {{ + let (lft, rgt, div, rem) = ( + $term.clone(), + $other_term.clone(), + &$val / $other_val, + &$val % $other_val, + ); + + ( + term::sub(vec![lft, term::cmul(div, rgt), term::int(rem)]), + term::int(0), + ) + }}; + + // Assumes $other_val is not 0. + (@lhs real $term:expr, $val:expr, $other_term:expr, $other_val:expr) => { + ( + term::sub(vec![ + $term.clone(), + term::cmul($val / $other_val, $other_term.clone()), + ]), + term::real(Rat::new(0.into(), 1.into())), + ) + }; } /// Arithmetic synthesis over three terms. @@ -154,397 +132,361 @@ macro_rules! arith_synth_non_lin { /// - `- t_1 - t_2 - t_3 n` #[macro_export] macro_rules! arith_synth_three_terms { - ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => ({ - { - let mut previous = $previous.iter() ; - - while let Some( - & (ref other_term, ref other_val) - ) = previous.next() { - for & (ref another_term, ref another_val) in previous.clone() { - - arith_synth_three_terms! { @internal - $f( - term::add( - vec![ $term.clone(), other_term.clone(), another_term.clone() ] - ), - term::$constructor( & $val + other_val + another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::add( - vec![ - $term.clone(), - other_term.clone(), - term::sub( vec![ another_term.clone() ] ), - ] - ), - term::$constructor( & $val + other_val - another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::add( - vec![ - $term.clone(), - term::sub( vec![ other_term.clone() ] ), - another_term.clone(), - ] - ), - term::$constructor( & $val - other_val + another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::sub( - vec![ - $term.clone(), other_term.clone(), another_term.clone() - ] - ), - term::$constructor( & $val - other_val - another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::add( - vec![ - term::sub( vec![ $term.clone() ] ), - other_term.clone(), - another_term.clone() - ] - ), - term::$constructor( (- & $val) + other_val + another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::sub( - vec![ - other_term.clone(), - $term.clone(), - another_term.clone() - ] - ), - term::$constructor( (- & $val) + other_val - another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::sub( - vec![ - another_term.clone(), - $term.clone(), - other_term.clone(), - ] - ), - term::$constructor( (- & $val) - other_val + another_val ) - ) - } - - arith_synth_three_terms! { @internal - $f( - term::sub( - vec![ - term::add( - vec![ - $term.clone(), - other_term.clone(), - another_term.clone() - ] - ) - ] - ), - term::$constructor( (- & $val) - other_val - another_val ) - ) - } - + ($previous:tt, $f:tt, $constructor:tt | $term:tt = $val:expr) => {{ + { + let mut previous = $previous.iter(); + + while let Some(&(ref other_term, ref other_val)) = previous.next() { + for &(ref another_term, ref another_val) in previous.clone() { + arith_synth_three_terms! { @internal + $f( + term::add( + vec![ $term.clone(), other_term.clone(), another_term.clone() ] + ), + term::$constructor( & $val + other_val + another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::add( + vec![ + $term.clone(), + other_term.clone(), + term::sub( vec![ another_term.clone() ] ), + ] + ), + term::$constructor( & $val + other_val - another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::add( + vec![ + $term.clone(), + term::sub( vec![ other_term.clone() ] ), + another_term.clone(), + ] + ), + term::$constructor( & $val - other_val + another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::sub( + vec![ + $term.clone(), other_term.clone(), another_term.clone() + ] + ), + term::$constructor( & $val - other_val - another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::add( + vec![ + term::sub( vec![ $term.clone() ] ), + other_term.clone(), + another_term.clone() + ] + ), + term::$constructor( (- & $val) + other_val + another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::sub( + vec![ + other_term.clone(), + $term.clone(), + another_term.clone() + ] + ), + term::$constructor( (- & $val) + other_val - another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::sub( + vec![ + another_term.clone(), + $term.clone(), + other_term.clone(), + ] + ), + term::$constructor( (- & $val) - other_val + another_val ) + ) + } + + arith_synth_three_terms! { @internal + $f( + term::sub( + vec![ + term::add( + vec![ + $term.clone(), + other_term.clone(), + another_term.clone() + ] + ) + ] + ), + term::$constructor( (- & $val) - other_val - another_val ) + ) + } + } + } } - } - } - $previous.insert(($term, $val.clone())) ; - }) ; - - - (@internal $f:tt($lhs:expr, $rhs:expr)) => ({ - let term = term::app( - Op::Ge, vec![ $lhs.clone(), $rhs.clone() ] - ) ; - // println!() ; - // println!("> {}", term) ; - apply! { $f to term } - let term = term::app( - Op::Le, vec![ $lhs.clone(), $rhs.clone() ] - ) ; - // println!() ; - // println!("> {}", term) ; - apply! { $f to term } - let term = term::app( - Op::Eql, vec![ $lhs.clone(), $rhs.clone() ] - ) ; - // println!() ; - // println!("> {}", term) ; - apply! { $f to term } - }) ; -} - + $previous.insert(($term, $val.clone())); + }}; + (@internal $f:tt($lhs:expr, $rhs:expr)) => {{ + let term = term::app(Op::Ge, vec![$lhs.clone(), $rhs.clone()]); + // println!() ; + // println!("> {}", term) ; + apply! { $f to term } + let term = term::app(Op::Le, vec![$lhs.clone(), $rhs.clone()]); + // println!() ; + // println!("> {}", term) ; + apply! { $f to term } + let term = term::app(Op::Eql, vec![$lhs.clone(), $rhs.clone()]); + // println!() ; + // println!("> {}", term) ; + apply! { $f to term } + }}; +} /// Bitvecs. pub struct Bitvec { - value: u64, + value: u64, } impl Bitvec { - /// Constructor. - pub fn zero() -> Self { - Bitvec { value: 0 } - } - - /// Increases the value. - pub fn inc(& mut self) { - self.value += 1 - } - - /// Retrieves the (inverted) boolean at some index. - pub fn index(& self, index: usize) -> bool { - if index < 64 { - self.value & (1 << index) == 0 - } else { - panic!("requesting synthesis over more than 64 variables") + /// Constructor. + pub fn zero() -> Self { + Bitvec { value: 0 } } - } -} - + /// Increases the value. + pub fn inc(&mut self) { + self.value += 1 + } + /// Retrieves the (inverted) boolean at some index. + pub fn index(&self, index: usize) -> bool { + if index < 64 { + self.value & (1 << index) == 0 + } else { + panic!("requesting synthesis over more than 64 variables") + } + } +} /// N-term arithmetic synthesis. pub fn n_term_arith_synth( - sample: & VarVals, others: & mut TermVals, - typ: & Typ, len: usize, mut f: F, + sample: &VarVals, + others: &mut TermVals, + typ: &Typ, + len: usize, + mut f: F, ) -> Res -where F: FnMut(Term) -> Res { - let mut previous: Vec<(Term, Val)> = Vec::with_capacity( - sample.len() - ) ; +where + F: FnMut(Term) -> Res, +{ + let mut previous: Vec<(Term, Val)> = Vec::with_capacity(sample.len()); - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if val.typ() == * typ && val.is_known() { - let var = term::var( var_idx, typ.clone() ) ; + // Iterate over the sample. + for (var_idx, val) in sample.index_iter() { + if val.typ() == *typ && val.is_known() { + let var = term::var(var_idx, typ.clone()); - let done = sum_diff_synth( - & ( var.clone(), val.clone() ), & previous, len, & mut f - ) ? ; + let done = sum_diff_synth(&(var.clone(), val.clone()), &previous, len, &mut f)?; - if done { return Ok(true) } + if done { + return Ok(true); + } - previous.push((var, val.clone())) + previous.push((var, val.clone())) + } } - } - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - let done = sum_diff_synth( - & ( term.clone(), val.clone() ), & previous, len, & mut f - ) ? ; + // Iterate over the cross-theory terms. + for (term, val) in others.drain() { + let done = sum_diff_synth(&(term.clone(), val.clone()), &previous, len, &mut f)?; - if done { - return Ok(true) - } + if done { + return Ok(true); + } - previous.push( (term.clone(), val.clone()) ) - } + previous.push((term.clone(), val.clone())) + } - Ok(false) + Ok(false) } - - - /// Arith sum/diff synth. pub fn sum_diff_synth( - term: & (Term, Val), others: & [ (Term, Val) ], - len: usize, mut f: F, + term: &(Term, Val), + others: &[(Term, Val)], + len: usize, + mut f: F, ) -> Res -where F: FnMut(Term) -> Res { - let mut stack = vec![ ( term, others.iter() ) ] ; - - loop { +where + F: FnMut(Term) -> Res, +{ + let mut stack = vec![(term, others.iter())]; + + loop { + while stack.len() < len { + if let Some((pair, others)) = stack + .last_mut() + .and_then(|(_, others)| others.next().map(|pair| (pair, others.clone()))) + { + stack.push((pair, others)) + } else if stack.pop().is_some() { + () + } else { + return Ok(false); + } + } - while stack.len() < len { - if let Some((pair, others)) = stack.last_mut().and_then( - |(_, others)| others.next().map( - |pair| ( pair, others.clone() ) - ) - ) { - stack.push( (pair, others) ) - } else if stack.pop().is_some() { - () - } else { - return Ok(false) - } - } + let done = iter_sum_diff_synth(&stack, &mut f)?; - let done = iter_sum_diff_synth(& stack, & mut f) ? ; + if done { + return Ok(true); + } - if done { - return Ok(true) + let _ = stack.pop(); + () } - - let _ = stack.pop() ; - () - - } } #[test] fn sum_diff() { - let term = & ( - term::var( 0, typ::int() ), val::int(0) - ) ; - - let others = & [ - (term::var( 1, typ::int() ), val::int(1)), - (term::var( 2, typ::int() ), val::int(2)), - (term::var( 3, typ::int() ), val::int(3)), - ] ; - - let expected = vec![ - "(= (+ v_0 v_1 v_2 (- 3)) 0)", - "(>= (+ v_0 v_1 v_2) 3)", - "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_2)) (- 3))", - "(= (+ v_1 v_2 (- 3) (* (- 1) v_0)) 0)", - "(>= (+ v_1 v_2 (* (- 1) v_0)) 3)", - "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_2)) (- 3))", - "(= (+ v_0 v_2 (- 1) (* (- 1) v_1)) 0)", - "(>= (+ v_0 v_2 (* (- 1) v_1)) 1)", - "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_2)) (- 1))", - "(= (+ v_2 (- 1) (* (- 1) v_0) (* (- 1) v_1)) 0)", - "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_1)) 1)", - "(>= (+ v_0 v_1 (* (- 1) v_2)) (- 1))", - - "(= (+ v_0 v_1 v_3 (- 4)) 0)", - "(>= (+ v_0 v_1 v_3) 4)", - "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_3)) (- 4))", - "(= (+ v_1 v_3 (* (- 1) v_0) (- 4)) 0)", - "(>= (+ v_1 v_3 (* (- 1) v_0)) 4)", - "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_3)) (- 4))", - "(= (+ v_0 v_3 (* (- 1) v_1) (- 2)) 0)", - "(>= (+ v_0 v_3 (* (- 1) v_1)) 2)", - "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_3)) (- 2))", - "(= (+ v_3 (* (- 1) v_0) (* (- 1) v_1) (- 2)) 0)", - "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_1)) 2)", - "(>= (+ v_0 v_1 (* (- 1) v_3)) (- 2))", - - "(= (+ v_0 v_2 v_3 (- 5)) 0)", - "(>= (+ v_0 v_2 v_3) 5)", - "(>= (+ (* (- 1) v_0) (* (- 1) v_2) (* (- 1) v_3)) (- 5))", - "(= (+ v_2 v_3 (* (- 1) v_0) (- 5)) 0)", - "(>= (+ v_2 v_3 (* (- 1) v_0)) 5)", - "(>= (+ v_0 (* (- 1) v_2) (* (- 1) v_3)) (- 5))", - "(= (+ v_0 v_3 (- 1) (* (- 1) v_2)) 0)", - "(>= (+ v_0 v_3 (* (- 1) v_2)) 1)", - "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_3)) (- 1))", - "(= (+ v_3 (- 1) (* (- 1) v_0) (* (- 1) v_2)) 0)", - "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_2)) 1)", - "(>= (+ v_0 v_2 (* (- 1) v_3)) (- 1))", - ] ; - let mut cnt = 0 ; - - sum_diff_synth( - term, others, 3, |term| { - println!("{}", term) ; - assert_eq! { & format!("{}", term), & expected[cnt] } ; - cnt += 1 ; - Ok(false) - }, - ).unwrap() ; + let term = &(term::var(0, typ::int()), val::int(0)); + + let others = &[ + (term::var(1, typ::int()), val::int(1)), + (term::var(2, typ::int()), val::int(2)), + (term::var(3, typ::int()), val::int(3)), + ]; + + let expected = vec![ + "(= (+ v_0 v_1 v_2 (- 3)) 0)", + "(>= (+ v_0 v_1 v_2) 3)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_2)) (- 3))", + "(= (+ v_1 v_2 (- 3) (* (- 1) v_0)) 0)", + "(>= (+ v_1 v_2 (* (- 1) v_0)) 3)", + "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_2)) (- 3))", + "(= (+ v_0 v_2 (- 1) (* (- 1) v_1)) 0)", + "(>= (+ v_0 v_2 (* (- 1) v_1)) 1)", + "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_2)) (- 1))", + "(= (+ v_2 (- 1) (* (- 1) v_0) (* (- 1) v_1)) 0)", + "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_1)) 1)", + "(>= (+ v_0 v_1 (* (- 1) v_2)) (- 1))", + "(= (+ v_0 v_1 v_3 (- 4)) 0)", + "(>= (+ v_0 v_1 v_3) 4)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_1) (* (- 1) v_3)) (- 4))", + "(= (+ v_1 v_3 (* (- 1) v_0) (- 4)) 0)", + "(>= (+ v_1 v_3 (* (- 1) v_0)) 4)", + "(>= (+ v_0 (* (- 1) v_1) (* (- 1) v_3)) (- 4))", + "(= (+ v_0 v_3 (* (- 1) v_1) (- 2)) 0)", + "(>= (+ v_0 v_3 (* (- 1) v_1)) 2)", + "(>= (+ v_1 (* (- 1) v_0) (* (- 1) v_3)) (- 2))", + "(= (+ v_3 (* (- 1) v_0) (* (- 1) v_1) (- 2)) 0)", + "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_1)) 2)", + "(>= (+ v_0 v_1 (* (- 1) v_3)) (- 2))", + "(= (+ v_0 v_2 v_3 (- 5)) 0)", + "(>= (+ v_0 v_2 v_3) 5)", + "(>= (+ (* (- 1) v_0) (* (- 1) v_2) (* (- 1) v_3)) (- 5))", + "(= (+ v_2 v_3 (* (- 1) v_0) (- 5)) 0)", + "(>= (+ v_2 v_3 (* (- 1) v_0)) 5)", + "(>= (+ v_0 (* (- 1) v_2) (* (- 1) v_3)) (- 5))", + "(= (+ v_0 v_3 (- 1) (* (- 1) v_2)) 0)", + "(>= (+ v_0 v_3 (* (- 1) v_2)) 1)", + "(>= (+ v_2 (* (- 1) v_0) (* (- 1) v_3)) (- 1))", + "(= (+ v_3 (- 1) (* (- 1) v_0) (* (- 1) v_2)) 0)", + "(>= (+ v_3 (* (- 1) v_0) (* (- 1) v_2)) 1)", + "(>= (+ v_0 v_2 (* (- 1) v_3)) (- 1))", + ]; + let mut cnt = 0; + + sum_diff_synth(term, others, 3, |term| { + println!("{}", term); + assert_eq! { & format!("{}", term), & expected[cnt] }; + cnt += 1; + Ok(false) + }).unwrap(); } - /// Arith sum/diff synth. -fn iter_sum_diff_synth( - terms: & [ (& (Term, Val), T) ], mut f: F -) -> Res -where F : FnMut(Term) -> Res { - if terms.is_empty() { - return Ok(false) - } - - let mut bitvec = Bitvec::zero() ; - let max = 2u64.pow( ( terms.len() - 1 ) as u32 ) ; - - for _ in 0 .. max { - let ( - mut sum, mut val, mut cnt - ): (Vec, Option, usize) = ( - Vec::with_capacity( terms.len() ), None, 0 - ) ; - - for ( (term, term_val), _ ) in terms { - let pos = bitvec.index(cnt) ; - - val = if let Some(val) = val { - if pos { - Some( val.add(term_val) ? ) +fn iter_sum_diff_synth(terms: &[(&(Term, Val), T)], mut f: F) -> Res +where + F: FnMut(Term) -> Res, +{ + if terms.is_empty() { + return Ok(false); + } + + let mut bitvec = Bitvec::zero(); + let max = 2u64.pow((terms.len() - 1) as u32); + + for _ in 0..max { + let (mut sum, mut val, mut cnt): (Vec, Option, usize) = + (Vec::with_capacity(terms.len()), None, 0); + + for ((term, term_val), _) in terms { + let pos = bitvec.index(cnt); + + val = if let Some(val) = val { + if pos { + Some(val.add(term_val)?) + } else { + Some(val.sub(term_val)?) + } + } else if pos { + Some(term_val.clone()) + } else { + Some(term_val.minus()?) + }; + + if pos { + sum.push(term.clone()) + } else { + sum.push(term::u_minus(term.clone())) + } + + cnt += 1 + } + + let val = if let Some(val) = val.and_then(|val| val.to_term()) { + val } else { - Some( val.sub(term_val) ? ) + bail!("unexpected non-value in synthesis") + }; + + let sum = term::add(sum); + + let done = f(term::eq(sum.clone(), val.clone()))?; + if done { + return Ok(true); } - } else if pos { - Some( term_val.clone() ) - } else { - Some( term_val.minus() ? ) - } ; - - if pos { - sum.push( term.clone() ) - } else { - sum.push( term::u_minus(term.clone()) ) - } - - cnt += 1 - } - let val = if let Some(val) = val.and_then( - |val| val.to_term() - ) { - val - } else { - bail!("unexpected non-value in synthesis") - } ; - - let sum = term::add(sum) ; - - let done = f( - term::eq(sum.clone(), val.clone()) - ) ? ; - if done { - return Ok(true) - } + let done = f(term::ge(sum.clone(), val.clone()))?; + if done { + return Ok(true); + } - let done = f( - term::ge( sum.clone(), val.clone() ) - ) ? ; - if done { - return Ok(true) - } + let done = f(term::le(sum, val))?; + if done { + return Ok(true); + } - let done = f( - term::le( sum, val ) - ) ? ; - if done { - return Ok(true) + bitvec.inc() } - - bitvec.inc() - } - Ok(false) + Ok(false) } - diff --git a/src/learning/ice/synth/int.rs b/src/learning/ice/synth/int.rs index 096368f3..34e82022 100644 --- a/src/learning/ice/synth/int.rs +++ b/src/learning/ice/synth/int.rs @@ -1,59 +1,64 @@ //! Qualifier synthesis in the theory of integers. -use common::* ; -use super::{ - helpers::n_term_arith_synth, - TermVals, TheoSynth, -} ; - +use super::{helpers::n_term_arith_synth, TermVals, TheoSynth}; +use common::*; /// Integer qualifier synthesizer. pub struct IntSynth { - /// Expressivity level. - expressivity: usize, - /// The int type. - typ: Typ, - /// True if the synth is done. - done: bool, + /// Expressivity level. + expressivity: usize, + /// The int type. + typ: Typ, + /// True if the synth is done. + done: bool, } impl Default for IntSynth { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl IntSynth { - /// Creates a new integer synthesizer. - pub fn new() -> Self { - IntSynth { - expressivity: 0, - typ: typ::int(), - done: false, + /// Creates a new integer synthesizer. + pub fn new() -> Self { + IntSynth { + expressivity: 0, + typ: typ::int(), + done: false, + } } - } } impl TheoSynth for IntSynth { - fn typ(& self) -> & Typ { & self.typ } + fn typ(&self) -> &Typ { + &self.typ + } - fn is_done(& self) -> bool { - self.done - } + fn is_done(&self) -> bool { + self.done + } - fn restart(& mut self) { - self.done = false ; - self.expressivity = 0 ; - } + fn restart(&mut self) { + self.done = false; + self.expressivity = 0; + } - fn increment(& mut self) { - self.expressivity += 1 - } + fn increment(&mut self) { + self.expressivity += 1 + } - fn synth( - & mut self, mut f: F, sample: & VarVals, others: & mut TermVals, - _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - self.done = false ; - match self.expressivity { - 0 => profile!( + fn synth( + &mut self, + mut f: F, + sample: &VarVals, + others: &mut TermVals, + _profiler: &Profiler, + ) -> Res + where + F: FnMut(Term) -> Res, + { + self.done = false; + match self.expressivity { + 0 => profile!( |_profiler| wrap { let done = n_term_arith_synth( sample, others, & self.typ, 1, & mut f @@ -66,77 +71,73 @@ impl TheoSynth for IntSynth { } "learning", "qual", "synthesis", "int", "level 0" ), - 1 => profile!( + 1 => profile!( |_profiler| wrap { non_lin_int_synth(sample, others, f) } "learning", "qual", "synthesis", "int", "level 1" ), - n if n < sample.len() => profile!( + n if n < sample.len() => profile!( |_profiler| wrap { n_term_arith_synth(sample, others, & self.typ, n + 1, f) } "learning", "qual", "synthesis", "int", "level n > 1" ), - _ => { - self.done = true ; - Ok(false) - }, + _ => { + self.done = true; + Ok(false) + } + } } - } - /// Only generates reals for now (using `to_real`). - fn project( - & self, sample: & VarVals, typ: & Typ, map: & mut TermVals - ) -> Res<()> { - if typ.is_real() { - for (var, val) in sample.index_iter() { - if let val::RVal::I(_) = val.get() { - let val = Op::ToReal.eval( vec![ val.clone() ] ) ? ; - let prev = map.insert( - term::to_real( term::var(var, typ::int()) ), val - ) ; - debug_assert_eq!( prev, None ) + /// Only generates reals for now (using `to_real`). + fn project(&self, sample: &VarVals, typ: &Typ, map: &mut TermVals) -> Res<()> { + if typ.is_real() { + for (var, val) in sample.index_iter() { + if let val::RVal::I(_) = val.get() { + let val = Op::ToReal.eval(vec![val.clone()])?; + let prev = map.insert(term::to_real(term::var(var, typ::int())), val); + debug_assert_eq!(prev, None) + } + } } - } + Ok(()) } - Ok(()) - } } - /// Non-linear int synthesis. -pub fn non_lin_int_synth( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::I(ref val) = val.get() { - let var = term::var(var_idx, typ::int()) ; - arith_synth_non_lin! { - previous_int, f, int | var = ( val.clone() ) - } +pub fn non_lin_int_synth(sample: &VarVals, others: &mut TermVals, mut f: F) -> Res +where + F: FnMut(Term) -> Res, +{ + let mut previous_int: BTreeSet<(Term, Int)> = BTreeSet::new(); + + // Iterate over the sample. + for (var_idx, val) in sample.index_iter() { + if let val::RVal::I(ref val) = val.get() { + let var = term::var(var_idx, typ::int()); + arith_synth_non_lin! { + previous_int, f, int | var = ( val.clone() ) + } + } } - } - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - match val.get() { - & val::RVal::I(ref val) => { - arith_synth_non_lin! { - previous_int, f, int | term = ( val.clone() ) + // Iterate over the cross-theory terms. + for (term, val) in others.drain() { + match val.get() { + &val::RVal::I(ref val) => { + arith_synth_non_lin! { + previous_int, f, int | term = ( val.clone() ) + } + } + val => bail!( + "int synthesis expects projected integers (1), \ + got {} for {}", + val, + term + ), } - } - val => bail!( - "int synthesis expects projected integers (1), \ - got {} for {}", val, term - ) } - } - Ok(false) + Ok(false) } - diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index 1b260eb5..3b970531 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -5,15 +5,15 @@ //! - document the workflow //! - factor code between the synthesizers (`eq_synth` etc.) -use common::* ; +use common::*; #[macro_use] -pub mod helpers ; -pub mod int ; -pub mod real ; -pub mod adt ; +pub mod helpers; +pub mod adt; +pub mod int; +pub mod real; -pub type TermVals = TermMap ; +pub type TermVals = TermMap; /// A theory synthesizer. /// @@ -27,268 +27,256 @@ pub type TermVals = TermMap ; /// /// [increment]: #tymethod.increment (increment method) pub trait TheoSynth { - /// Type of values supported by this synthesizer. - fn typ(& self) -> & Typ ; - /// Returns `true` if the synthesizer is done (will not produce new - /// qualifiers). - fn is_done(& self) -> bool ; - /// Restarts the synthesizer. - fn restart(& mut self) ; - /// Increments the synthesizer. - fn increment(& mut self) ; - /// Synthesizes qualifiers. - fn synth( - & mut self, F, & VarVals, & mut TermVals, & Profiler - ) -> Res - where F: FnMut(Term) -> Res ; - /// Generates some [`TermVal`][term val]s for some other type. - /// - /// Adds them to the input term to value map. - /// - /// [term val]: struct.TermVal.html - /// (TermVal struct) - fn project(& self, & VarVals, & Typ, & mut TermVals) -> Res<()> ; + /// Type of values supported by this synthesizer. + fn typ(&self) -> &Typ; + /// Returns `true` if the synthesizer is done (will not produce new + /// qualifiers). + fn is_done(&self) -> bool; + /// Restarts the synthesizer. + fn restart(&mut self); + /// Increments the synthesizer. + fn increment(&mut self); + /// Synthesizes qualifiers. + fn synth(&mut self, F, &VarVals, &mut TermVals, &Profiler) -> Res + where + F: FnMut(Term) -> Res; + /// Generates some [`TermVal`][term val]s for some other type. + /// + /// Adds them to the input term to value map. + /// + /// [term val]: struct.TermVal.html + /// (TermVal struct) + fn project(&self, &VarVals, &Typ, &mut TermVals) -> Res<()>; } -use self::int::IntSynth ; -use self::real::RealSynth ; -use self::adt::AdtSynth ; +use self::adt::AdtSynth; +use self::int::IntSynth; +use self::real::RealSynth; /// Manages theory synthesizers. pub struct SynthSys { - int: Option, - real: Option, - adt: Vec, - cross_synth: TermMap, + int: Option, + real: Option, + adt: Vec, + cross_synth: TermMap, } impl SynthSys { - /// Constructor. - pub fn new(sig: & Sig) -> Self { - let mut int = None ; - let mut real = None ; + /// Constructor. + pub fn new(sig: &Sig) -> Self { + let mut int = None; + let mut real = None; + + macro_rules! set { + (int) => { + if int.is_none() { + int = Some(IntSynth::new()) + } + }; + (real) => { + if real.is_none() { + real = Some(RealSynth::new()) + } + }; + } - macro_rules! set { - (int) => ( - if int.is_none() { - int = Some( IntSynth::new() ) + let mut adt: Vec = Vec::new(); + for typ in sig { + match **typ { + typ::RTyp::Int => set!(int), + typ::RTyp::Real => set!(real), + + typ::RTyp::DTyp { .. } => if adt.iter().all(|adt| adt.typ() != typ) { + let synth = AdtSynth::new(typ.clone()); + if synth.can_project_to_int() { + set!(int) + } + if synth.can_project_to_real() { + set!(real) + } + adt.push(synth) + }, + + typ::RTyp::Bool | typ::RTyp::Array { .. } | typ::RTyp::Unk => (), + } } - ) ; - (real) => ( - if real.is_none() { - real = Some( RealSynth::new() ) + + SynthSys { + int, + real, + adt, + cross_synth: TermMap::new(), } - ) ; } - let mut adt: Vec = Vec::new() ; - for typ in sig { - match ** typ { - typ::RTyp::Int => set!(int), - typ::RTyp::Real => set!(real), - - typ::RTyp::DTyp { .. } => if adt.iter().all( - |adt| adt.typ() != typ - ) { - let synth = AdtSynth::new( typ.clone() ) ; - if synth.can_project_to_int() { set!(int) } - if synth.can_project_to_real() { set!(real) } - adt.push(synth) - }, - - typ::RTyp::Bool | - typ::RTyp::Array { .. } | - typ::RTyp::Unk => (), - } + /// True if all synthesizers are done. + pub fn is_done(&self) -> bool { + self.int.as_ref().map(|i| i.is_done()).unwrap_or(true) + && self.real.as_ref().map(|r| r.is_done()).unwrap_or(true) + && self.adt.iter().all(|a| a.is_done()) } - SynthSys { - int, real, adt, cross_synth: TermMap::new(), + /// Increments all synthesizers. + pub fn increment(&mut self) { + if let Some(i) = self.int.as_mut() { + i.increment() + } + if let Some(r) = self.real.as_mut() { + r.increment() + } + for a in &mut self.adt { + a.increment() + } } - } - /// True if all synthesizers are done. - pub fn is_done(& self) -> bool { - self.int.as_ref().map(|i| i.is_done()).unwrap_or(true) && - self.real.as_ref().map(|r| r.is_done()).unwrap_or(true) && - self.adt.iter().all( - |a| a.is_done() - ) - } - - /// Increments all synthesizers. - pub fn increment(& mut self) { - if let Some(i) = self.int.as_mut() { i.increment() } - if let Some(r) = self.real.as_mut() { r.increment() } - for a in & mut self.adt { - a.increment() + /// Restarts all synthesizers. + pub fn restart(&mut self) { + if let Some(i) = self.int.as_mut() { + i.restart() + } + if let Some(r) = self.real.as_mut() { + r.restart() + } + for a in &mut self.adt { + a.restart() + } } - } - /// Restarts all synthesizers. - pub fn restart(& mut self) { - if let Some(i) = self.int.as_mut() { i.restart() } - if let Some(r) = self.real.as_mut() { r.restart() } - for a in & mut self.adt { - a.restart() + /// Synthesizes qualifiers for a sample, stops if input function returns + /// `true`. + /// + /// Returns `true` iff `f` returned true at some point. + pub fn sample_synth(&mut self, sample: &VarVals, mut f: F, _prof: &Profiler) -> Res + where + F: FnMut(Term) -> Res, + { + let done = self.int_synth(sample, &mut f, _prof)? + || self.real_synth(sample, &mut f, _prof)? + || self.adt_synth(sample, &mut f, _prof)?; + + Ok(done) } - } - - - /// Synthesizes qualifiers for a sample, stops if input function returns - /// `true`. - /// - /// Returns `true` iff `f` returned true at some point. - pub fn sample_synth( - & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - - let done = self.int_synth( - sample, & mut f, _profiler - ) ? || self.real_synth( - sample, & mut f, _profiler - ) ? || self.adt_synth( - sample, & mut f, _profiler - ) ? ; - Ok(done) - } - - /// Runs integer synthesis. - pub fn int_synth( - & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - - if let Some(int_synth) = self.int.as_mut() { - if ! int_synth.is_done() { - self.cross_synth.clear() ; - - if let Some(real_synth) = self.real.as_mut() { - profile!{ - |_profiler| tick "learning", "qual", "synthesis", "int project" - } - let res = real_synth.project( - sample, int_synth.typ(), & mut self.cross_synth - ) ; - profile!{ - |_profiler| mark "learning", "qual", "synthesis", "int project" - } - res ? - } - for adt_synth in & mut self.adt { - profile!{ - |_profiler| tick "learning", "qual", "synthesis", "adt project" - } - let res = adt_synth.project( - sample, int_synth.typ(), & mut self.cross_synth - ) ; - profile!{ - |_profiler| mark "learning", "qual", "synthesis", "adt project" - } - res ? + /// Runs integer synthesis. + pub fn int_synth(&mut self, sample: &VarVals, mut f: F, _profiler: &Profiler) -> Res + where + F: FnMut(Term) -> Res, + { + if let Some(int_synth) = self.int.as_mut() { + if !int_synth.is_done() { + self.cross_synth.clear(); + + if let Some(real_synth) = self.real.as_mut() { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "int project" + } + let res = real_synth.project(sample, int_synth.typ(), &mut self.cross_synth); + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "int project" + } + res? + } + for adt_synth in &mut self.adt { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "adt project" + } + let res = adt_synth.project(sample, int_synth.typ(), &mut self.cross_synth); + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "adt project" + } + res? + } + + profile!{ |_profiler| tick "learning", "qual", "synthesis", "int" } + let done = int_synth.synth(&mut f, sample, &mut self.cross_synth, _profiler); + profile!{ |_profiler| mark "learning", "qual", "synthesis", "int" } + if done? { + return Ok(true); + } + } } - profile!{ |_profiler| tick "learning", "qual", "synthesis", "int" } - let done = int_synth.synth( - & mut f, sample, & mut self.cross_synth, _profiler - ) ; - profile!{ |_profiler| mark "learning", "qual", "synthesis", "int" } - if done ? { return Ok(true) } - } + Ok(false) } - Ok(false) - } - - - /// Runs real synthesis. - pub fn real_synth( - & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - - if let Some(real_synth) = self.real.as_mut() { - if ! real_synth.is_done() { - self.cross_synth.clear() ; + /// Runs real synthesis. + pub fn real_synth(&mut self, sample: &VarVals, mut f: F, _profiler: &Profiler) -> Res + where + F: FnMut(Term) -> Res, + { + if let Some(real_synth) = self.real.as_mut() { + if !real_synth.is_done() { + self.cross_synth.clear(); - if let Some(int_synth) = self.int.as_mut() { - profile! ( + if let Some(int_synth) = self.int.as_mut() { + profile! ( |_profiler| wrap { int_synth.project( sample, real_synth.typ(), & mut self.cross_synth ) } "learning", "qual", "synthesis", "int project" - ) ? - } - for adt_synth in & mut self.adt { - profile!{ - |_profiler| tick "learning", "qual", "synthesis", "adt project" - } - let res = adt_synth.project( - sample, real_synth.typ(), & mut self.cross_synth - ) ; - profile!{ - |_profiler| mark "learning", "qual", "synthesis", "adt project" - } - res ? + )? + } + for adt_synth in &mut self.adt { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "adt project" + } + let res = adt_synth.project(sample, real_synth.typ(), &mut self.cross_synth); + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "adt project" + } + res? + } + + profile!{ |_profiler| tick "learning", "qual", "synthesis", "real" } + let done = real_synth.synth(&mut f, sample, &mut self.cross_synth, _profiler); + profile!{ |_profiler| mark "learning", "qual", "synthesis", "real" } + if done? { + return Ok(true); + } + } } - profile!{ |_profiler| tick "learning", "qual", "synthesis", "real" } - let done = real_synth.synth( - & mut f, sample, & mut self.cross_synth, _profiler - ) ; - profile!{ |_profiler| mark "learning", "qual", "synthesis", "real" } - if done ? { return Ok(true) } - } + Ok(false) } - Ok(false) - } - - - /// Runs adt synthesis. - pub fn adt_synth( - & mut self, sample: & VarVals, mut f: F, _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - - for adt_synth in & mut self.adt { - if ! adt_synth.is_done() { - self.cross_synth.clear() ; - - if let Some(int_synth) = self.int.as_mut() { - profile! ( + /// Runs adt synthesis. + pub fn adt_synth(&mut self, sample: &VarVals, mut f: F, _profiler: &Profiler) -> Res + where + F: FnMut(Term) -> Res, + { + for adt_synth in &mut self.adt { + if !adt_synth.is_done() { + self.cross_synth.clear(); + + if let Some(int_synth) = self.int.as_mut() { + profile! ( |_profiler| wrap { int_synth.project( sample, adt_synth.typ(), & mut self.cross_synth ) } "learning", "qual", "synthesis", "real project" - ) ? - } - if let Some(real_synth) = self.real.as_mut() { - profile!{ - |_profiler| tick "learning", "qual", "synthesis", "int project" - } - let res = real_synth.project( - sample, adt_synth.typ(), & mut self.cross_synth - ) ; - profile!{ - |_profiler| mark "learning", "qual", "synthesis", "int project" - } - res ? + )? + } + if let Some(real_synth) = self.real.as_mut() { + profile!{ + |_profiler| tick "learning", "qual", "synthesis", "int project" + } + let res = real_synth.project(sample, adt_synth.typ(), &mut self.cross_synth); + profile!{ + |_profiler| mark "learning", "qual", "synthesis", "int project" + } + res? + } + + profile!{ |_profiler| tick "learning", "qual", "synthesis", "adt" } + let done = adt_synth.synth(&mut f, sample, &mut self.cross_synth, _profiler); + profile!{ |_profiler| mark "learning", "qual", "synthesis", "adt" } + if done? { + return Ok(true); + } + } } - profile!{ |_profiler| tick "learning", "qual", "synthesis", "adt" } - let done = adt_synth.synth( - & mut f, sample, & mut self.cross_synth, _profiler - ) ; - profile!{ |_profiler| mark "learning", "qual", "synthesis", "adt" } - if done ? { return Ok(true) } - } + Ok(false) } - - Ok(false) - } - } diff --git a/src/learning/ice/synth/real.rs b/src/learning/ice/synth/real.rs index 6a20af36..cc7cf612 100644 --- a/src/learning/ice/synth/real.rs +++ b/src/learning/ice/synth/real.rs @@ -1,59 +1,64 @@ //! Qualifier synthesis in the theory of reals. -use common::* ; -use super::{ - helpers::n_term_arith_synth, - TermVals, TheoSynth, -} ; - +use super::{helpers::n_term_arith_synth, TermVals, TheoSynth}; +use common::*; /// Real qualifier synthesizer. pub struct RealSynth { - /// Expressivity level. - expressivity: usize, - /// The real type. - typ: Typ, - /// True if the synth is done. - done: bool, + /// Expressivity level. + expressivity: usize, + /// The real type. + typ: Typ, + /// True if the synth is done. + done: bool, } impl Default for RealSynth { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl RealSynth { - /// Creates a new integer synthesizer. - pub fn new() -> Self { - RealSynth { - expressivity: 0, - typ: typ::real(), - done: false, + /// Creates a new integer synthesizer. + pub fn new() -> Self { + RealSynth { + expressivity: 0, + typ: typ::real(), + done: false, + } } - } } impl TheoSynth for RealSynth { - fn typ(& self) -> & Typ { & self.typ } + fn typ(&self) -> &Typ { + &self.typ + } - fn is_done(& self) -> bool { - self.done - } + fn is_done(&self) -> bool { + self.done + } - fn restart(& mut self) { - self.done = false ; - self.expressivity = 0 - } + fn restart(&mut self) { + self.done = false; + self.expressivity = 0 + } - fn increment(& mut self) { - self.expressivity += 1 - } + fn increment(&mut self) { + self.expressivity += 1 + } - fn synth( - & mut self, mut f: F, sample: & VarVals, others: & mut TermVals, - _profiler: & Profiler - ) -> Res - where F: FnMut(Term) -> Res { - self.done = false ; - match self.expressivity { - 0 => profile!( + fn synth( + &mut self, + mut f: F, + sample: &VarVals, + others: &mut TermVals, + _profiler: &Profiler, + ) -> Res + where + F: FnMut(Term) -> Res, + { + self.done = false; + match self.expressivity { + 0 => profile!( |_profiler| wrap { let done = n_term_arith_synth( sample, others, & self.typ, 1, & mut f @@ -66,75 +71,71 @@ impl TheoSynth for RealSynth { } "learning", "qual", "synthesis", "real", "level 0" ), - 1 => profile!( + 1 => profile!( |_profiler| wrap { non_lin_real_synth(sample, others, f) } "learning", "qual", "synthesis", "real", "level 1" ), - n if n < 3 => profile!( + n if n < 3 => profile!( |_profiler| wrap { n_term_arith_synth(sample, others, & self.typ, n + 1, f) } "learning", "qual", "synthesis", "real", "level n > 1" ), - _ => { - self.done = true ; - Ok(false) - }, + _ => { + self.done = true; + Ok(false) + } + } } - } - /// Only generates ints for now (using `to_int`). - fn project( - & self, sample: & VarVals, typ: & Typ, map: & mut TermVals - ) -> Res<()> { - if let typ::RTyp::Int = ** typ { - for (var, val) in sample.index_iter() { - if let val::RVal::R(ref r) = val.get() { - let val = Op::ToInt.eval( vec![ val::real( r.clone() ) ] ) ? ; - let prev = map.insert( - term::to_int( term::var(var, typ::real()) ), val - ) ; - debug_assert_eq!( prev, None ) + /// Only generates ints for now (using `to_int`). + fn project(&self, sample: &VarVals, typ: &Typ, map: &mut TermVals) -> Res<()> { + if let typ::RTyp::Int = **typ { + for (var, val) in sample.index_iter() { + if let val::RVal::R(ref r) = val.get() { + let val = Op::ToInt.eval(vec![val::real(r.clone())])?; + let prev = map.insert(term::to_int(term::var(var, typ::real())), val); + debug_assert_eq!(prev, None) + } + } } - } + Ok(()) } - Ok(()) - } } - - /// Level 1 for real synthesis. -pub fn non_lin_real_synth( - sample: & VarVals, others: & mut TermVals, mut f: F -) -> Res -where F: FnMut(Term) -> Res { - let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new() ; - - // Iterate over the sample. - for (var_idx, val) in sample.index_iter() { - if let val::RVal::R(ref r) = val.get() { - let var = term::var(var_idx, val.typ().clone()) ; - arith_synth_non_lin! { - previous_real, f, real | var = ( r.clone() ) - } +pub fn non_lin_real_synth(sample: &VarVals, others: &mut TermVals, mut f: F) -> Res +where + F: FnMut(Term) -> Res, +{ + let mut previous_real: BTreeSet<(Term, Rat)> = BTreeSet::new(); + + // Iterate over the sample. + for (var_idx, val) in sample.index_iter() { + if let val::RVal::R(ref r) = val.get() { + let var = term::var(var_idx, val.typ().clone()); + arith_synth_non_lin! { + previous_real, f, real | var = ( r.clone() ) + } + } } - } - // Iterate over the cross-theory terms. - for (term, val) in others.drain() { - if let val::RVal::R(ref r) = val.get() { - arith_synth_non_lin! { - previous_real, f, real | term = r.clone() - } - } else { - bail!( - "real synthesis expects projected reals, got {} for {}", val, term - ) + // Iterate over the cross-theory terms. + for (term, val) in others.drain() { + if let val::RVal::R(ref r) = val.get() { + arith_synth_non_lin! { + previous_real, f, real | term = r.clone() + } + } else { + bail!( + "real synthesis expects projected reals, got {} for {}", + val, + term + ) + } } - } - Ok(false) + Ok(false) } diff --git a/src/learning/mod.rs b/src/learning/mod.rs index 0b8bf6b4..98ec7b8f 100644 --- a/src/learning/mod.rs +++ b/src/learning/mod.rs @@ -1,3 +1,3 @@ //! Learners the teacher can interact with. -pub mod ice ; \ No newline at end of file +pub mod ice; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index ec1342b6..79da938c 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,40 +1,34 @@ //! SMT-LIB 2 horn clause problem parser. -use common::* ; -use instance::{ - *, info::VarInfo -} ; - -mod ptterms ; -pub use self::ptterms::* ; - -use consts::keywords ; +use common::*; +use instance::{info::VarInfo, *}; +mod ptterms; +pub use self::ptterms::*; +use consts::keywords; /// Result yielded by the parser. #[derive(PartialEq, Eq)] pub enum Parsed { - /// Check-sat. - CheckSat, - /// Get-model. - GetModel, - /// Get unsat core. - GetUnsatCore, - /// Get unsat proof. - GetProof, - /// Exit. - Exit, - /// Only parsed some item(s), no query. - Items, - /// Reset. - Reset, - /// End of file. - Eof, + /// Check-sat. + CheckSat, + /// Get-model. + GetModel, + /// Get unsat core. + GetUnsatCore, + /// Get unsat proof. + GetProof, + /// Exit. + Exit, + /// Only parsed some item(s), no query. + Items, + /// Reset. + Reset, + /// End of file. + Eof, } - - lazy_static! { /// Set of legal special characters in identifiers. static ref id_special_chars: HashSet<& 'static str> = { @@ -60,1897 +54,1666 @@ lazy_static! { } ; } - - /// String extensions, lift char functions. pub trait StringExt { - /// Lifts `char::is_alphanumeric`. - fn is_alphanumeric(& self) -> bool ; - /// Lifts `char::is_alphabetic`. - fn is_alphabetic(& self) -> bool ; - /// Lifts `char::is_numeric`. - fn is_numeric(& self) -> bool ; + /// Lifts `char::is_alphanumeric`. + fn is_alphanumeric(&self) -> bool; + /// Lifts `char::is_alphabetic`. + fn is_alphabetic(&self) -> bool; + /// Lifts `char::is_numeric`. + fn is_numeric(&self) -> bool; } impl StringExt for str { - fn is_alphanumeric(& self) -> bool { - for char in self.chars() { - if ! char.is_alphanumeric() { return false } - } - true - } - fn is_alphabetic(& self) -> bool { - for char in self.chars() { - if ! char.is_alphabetic() { return false } - } - true - } - fn is_numeric(& self) -> bool { - for char in self.chars() { - if ! char.is_numeric() { return false } - } - true - } + fn is_alphanumeric(&self) -> bool { + for char in self.chars() { + if !char.is_alphanumeric() { + return false; + } + } + true + } + fn is_alphabetic(&self) -> bool { + for char in self.chars() { + if !char.is_alphabetic() { + return false; + } + } + true + } + fn is_numeric(&self) -> bool { + for char in self.chars() { + if !char.is_numeric() { + return false; + } + } + true + } } - - /// Extends `BufRead` with SMT-LIB 2 item parsing. pub trait ItemRead { - /// Reads the next item. - /// - /// - returns the **number of lines** read, not the number of bytes read - /// - returns `None` once it finds `eof` and no item prior - fn read_item(& mut self, buf: & mut String) -> Res ; + /// Reads the next item. + /// + /// - returns the **number of lines** read, not the number of bytes read + /// - returns `None` once it finds `eof` and no item prior + fn read_item(&mut self, buf: &mut String) -> Res; } impl ItemRead for T { - fn read_item(& mut self, buf: & mut String) -> Res { - let mut line_count = 0 ; - let mut search_start = buf.len() ; - let mut char_override: Option = None ; - let mut opn_parens = 0 ; - let mut cls_parens = 0 ; - - fn search_char( - char: char, chars: & mut ::std::str::Chars - ) -> Option { - for c in chars { - if char == c { - return None + fn read_item(&mut self, buf: &mut String) -> Res { + let mut line_count = 0; + let mut search_start = buf.len(); + let mut char_override: Option = None; + let mut opn_parens = 0; + let mut cls_parens = 0; + + fn search_char(char: char, chars: &mut ::std::str::Chars) -> Option { + for c in chars { + if char == c { + return None; + } + } + Some(char) } - } - Some(char) - } - - 'read_lines: while self.read_line( buf ) ? != 0 { - line_count += 1 ; - debug_assert!( opn_parens >= cls_parens ) ; - let mut chars = buf[search_start ..].chars() ; - - if let Some(char) = char_override { - char_override = search_char(char, & mut chars) - } - - 'inspect_chars: while let Some(c) = chars.next() { - match c { - '(' => opn_parens += 1, - ')' => cls_parens += 1, - '|' => { - debug_assert!( char_override.is_none() ) ; - char_override = search_char('|', & mut chars) - }, - '"' => { - debug_assert!( char_override.is_none() ) ; - char_override = search_char('"', & mut chars) - }, - ';' => break 'inspect_chars, - _ => (), - } - } - - if opn_parens > 0 && opn_parens == cls_parens - || opn_parens < cls_parens { - // Something's wrong, let the parser handle it. - break 'read_lines - } - - search_start = buf.len() - } - - Ok(line_count) - } -} + 'read_lines: while self.read_line(buf)? != 0 { + line_count += 1; + debug_assert!(opn_parens >= cls_parens); + let mut chars = buf[search_start..].chars(); + + if let Some(char) = char_override { + char_override = search_char(char, &mut chars) + } + + 'inspect_chars: while let Some(c) = chars.next() { + match c { + '(' => opn_parens += 1, + ')' => cls_parens += 1, + '|' => { + debug_assert!(char_override.is_none()); + char_override = search_char('|', &mut chars) + } + '"' => { + debug_assert!(char_override.is_none()); + char_override = search_char('"', &mut chars) + } + ';' => break 'inspect_chars, + _ => (), + } + } + + if opn_parens > 0 && opn_parens == cls_parens || opn_parens < cls_parens { + // Something's wrong, let the parser handle it. + break 'read_lines; + } + + search_start = buf.len() + } + + Ok(line_count) + } +} /// String cursor. -pub type Cursor = usize ; +pub type Cursor = usize; /// Position in the text. -#[derive( - Clone, Copy, Debug, - PartialEq, Eq, Hash, - PartialOrd, Ord -)] -pub struct Pos(usize) ; +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Pos(usize); impl Default for Pos { - fn default() -> Self { Pos(0) } + fn default() -> Self { + Pos(0) + } } impl ::std::ops::Deref for Pos { - type Target = usize ; - fn deref(& self) -> & usize { & self.0 } + type Target = usize; + fn deref(&self) -> &usize { + &self.0 + } } - - - /// Result of parsing a clause. enum ClauseRes { - /// Clause parsed, but it was redundant. - Skipped, - /// Clause parsed and added. - Added(ClsIdx), + /// Clause parsed, but it was redundant. + Skipped, + /// Clause parsed and added. + Added(ClsIdx), } impl ClauseRes { - /// Turns itself in an option. - pub fn into_option(self) -> Option { - if let ClauseRes::Added(i) = self { - Some(i) - } else { - None - } - } + /// Turns itself in an option. + pub fn into_option(self) -> Option { + if let ClauseRes::Added(i) = self { + Some(i) + } else { + None + } + } } - /// The operator of an s-expression. #[derive(Clone, Debug, PartialEq, Eq)] pub enum FrameOp { - /// An actual operator. - Op(Op), - /// A constant array constructor. - CArray(Typ, Pos), - /// A cast. - Cast, - /// A datatype constructor. - DTypNew(String, DTyp), - /// A datatype selector. - DTypSlc(String), - /// A datatype tester. - DTypTst(String), - /// A function application. - Fun(String), + /// An actual operator. + Op(Op), + /// A constant array constructor. + CArray(Typ, Pos), + /// A cast. + Cast, + /// A datatype constructor. + DTypNew(String, DTyp), + /// A datatype selector. + DTypSlc(String), + /// A datatype tester. + DTypTst(String), + /// A function application. + Fun(String), } impl FrameOp { - /// String representation for a frame operator. - pub fn as_str(& self) -> String { - match self { - FrameOp::Op(op) => format!("{}", op), - FrameOp::CArray(typ, _) => format!( - "array constructor for {}", typ - ), - FrameOp::Cast => "cast operator".into(), - FrameOp::DTypNew(name, typ) => format!( - "`{}` constructor ({})", name, typ - ), - FrameOp::DTypSlc(name) => format!("`{}` selector", name), - FrameOp::DTypTst(name) => format!("`{}` tester", name), - FrameOp::Fun(name) => format!("`{}` function", name), - } - } + /// String representation for a frame operator. + pub fn as_str(&self) -> String { + match self { + FrameOp::Op(op) => format!("{}", op), + FrameOp::CArray(typ, _) => format!("array constructor for {}", typ), + FrameOp::Cast => "cast operator".into(), + FrameOp::DTypNew(name, typ) => format!("`{}` constructor ({})", name, typ), + FrameOp::DTypSlc(name) => format!("`{}` selector", name), + FrameOp::DTypTst(name) => format!("`{}` tester", name), + FrameOp::Fun(name) => format!("`{}` function", name), + } + } } - - /// Term stack frame used in the parser to avoid recursion. pub struct TermFrame { - /// Operator when going up. - op: FrameOp, - /// Position of the operator. - op_pos: Pos, - /// Position of the arguments. - args_pos: Vec, - /// Arguments. - args: Vec, - /// Let-binding count. - let_count: LetCount, + /// Operator when going up. + op: FrameOp, + /// Position of the operator. + op_pos: Pos, + /// Position of the arguments. + args_pos: Vec, + /// Arguments. + args: Vec, + /// Let-binding count. + let_count: LetCount, } impl TermFrame { - /// Constructor. - pub fn new( - op: FrameOp, op_pos: Pos, let_count: LetCount - ) -> Self { - TermFrame { - op, op_pos, let_count, - args_pos: Vec::with_capacity(11), - args: Vec::with_capacity(11), - } - } - - /// True if the frame operator is a cast operation. - pub fn is_cast(& self) -> bool { - self.op == FrameOp::Cast - } - - /// Pushes an argument. - pub fn push_arg(& mut self, pos: Pos, arg: Term) { - debug_assert_eq! { self.args_pos.len(), self.args.len() } - self.args_pos.push(pos) ; - self.args.push(arg) - } - - /// True if the frame has no arguments. - pub fn is_empty(& self) -> bool { - debug_assert_eq! { self.args_pos.len(), self.args.len() } - self.args_pos.is_empty() - } - - /// Retrieves the let-binding count and sets the internal one to zero. - pub fn let_count(& mut self) -> LetCount { - ::std::mem::replace( & mut self.let_count, 0.into() ) - } - - /// Destroys the frame. - pub fn destroy(self) -> ( - FrameOp, Pos, Vec, Vec - ) { - (self.op, self.op_pos, self.args_pos, self.args) - } -} + /// Constructor. + pub fn new(op: FrameOp, op_pos: Pos, let_count: LetCount) -> Self { + TermFrame { + op, + op_pos, + let_count, + args_pos: Vec::with_capacity(11), + args: Vec::with_capacity(11), + } + } + + /// True if the frame operator is a cast operation. + pub fn is_cast(&self) -> bool { + self.op == FrameOp::Cast + } + + /// Pushes an argument. + pub fn push_arg(&mut self, pos: Pos, arg: Term) { + debug_assert_eq! { self.args_pos.len(), self.args.len() } + self.args_pos.push(pos); + self.args.push(arg) + } + + /// True if the frame has no arguments. + pub fn is_empty(&self) -> bool { + debug_assert_eq! { self.args_pos.len(), self.args.len() } + self.args_pos.is_empty() + } + + /// Retrieves the let-binding count and sets the internal one to zero. + pub fn let_count(&mut self) -> LetCount { + ::std::mem::replace(&mut self.let_count, 0.into()) + } + /// Destroys the frame. + pub fn destroy(self) -> (FrameOp, Pos, Vec, Vec) { + (self.op, self.op_pos, self.args_pos, self.args) + } +} /// Parser context. #[derive(Default)] pub struct ParserCxt { - /// Term stack to avoid recursion. - term_stack: Vec, - /// Memory for backtracking. - mem: Vec, - /// Map from predicate names to predicate indices. - pred_name_map: BTreeMap, + /// Term stack to avoid recursion. + term_stack: Vec, + /// Memory for backtracking. + mem: Vec, + /// Map from predicate names to predicate indices. + pred_name_map: BTreeMap, } impl ParserCxt { - /// Constructor. - pub fn new() -> Self { - ParserCxt { - term_stack: Vec::with_capacity(17), - mem: Vec::with_capacity(17), - pred_name_map: BTreeMap::new(), - } - } - - /// Term stack accessor. - pub fn term_stack(& self) -> & Vec { - & self.term_stack - } - - /// Generates a parser from itself. - pub fn parser<'cxt, 's>( - & 'cxt mut self, string: & 's str, line_off: usize, - _profiler: & 'cxt Profiler - ) -> Parser<'cxt, 's> { - debug_assert!( self.mem.is_empty() ) ; - Parser { - cxt: self, - string, - cursor: 0, - line_off, - bindings: Vec::with_capacity(7), - functions: BTreeMap::new(), - _profiler, - } - } - - /// Resets the parser. - pub fn reset(& mut self) { - self.pred_name_map.clear() - } -} + /// Constructor. + pub fn new() -> Self { + ParserCxt { + term_stack: Vec::with_capacity(17), + mem: Vec::with_capacity(17), + pred_name_map: BTreeMap::new(), + } + } + + /// Term stack accessor. + pub fn term_stack(&self) -> &Vec { + &self.term_stack + } + + /// Generates a parser from itself. + pub fn parser<'cxt, 's>( + &'cxt mut self, + string: &'s str, + line_off: usize, + _profiler: &'cxt Profiler, + ) -> Parser<'cxt, 's> { + debug_assert!(self.mem.is_empty()); + Parser { + cxt: self, + string, + cursor: 0, + line_off, + bindings: Vec::with_capacity(7), + functions: BTreeMap::new(), + _profiler, + } + } + /// Resets the parser. + pub fn reset(&mut self) { + self.pred_name_map.clear() + } +} /// Wraps an integer, represents a number of let-bindings parsed. #[must_use] #[derive(Clone, Copy)] pub struct LetCount { - n: usize + n: usize, } impl LetCount { - /// True if zero. - pub fn is_zero(self) -> bool{ self.n == 0 } + /// True if zero. + pub fn is_zero(self) -> bool { + self.n == 0 + } } impl From for LetCount { - fn from(n: usize) -> Self { LetCount { n } } + fn from(n: usize) -> Self { + LetCount { n } + } } - /// Parser structure. Generated from a `ParserCxt`. pub struct Parser<'cxt, 's> { - /// Parsing context. - cxt: & 'cxt mut ParserCxt, - /// Text being read (for errors). - string: & 's str, - /// Current position in the text. - cursor: Cursor, - /// Line offset (for errors). - line_off: usize, - /// Stack of bindings. - bindings: Vec< BTreeMap<& 's str, PTTerms> >, - /// Functions we are currently parsing. - /// - /// Only used when parsing a `define-funs-rec`. - functions: BTreeMap<& 's str, (VarInfos, Typ)>, - /// Profiler. - _profiler: & 'cxt Profiler, + /// Parsing context. + cxt: &'cxt mut ParserCxt, + /// Text being read (for errors). + string: &'s str, + /// Current position in the text. + cursor: Cursor, + /// Line offset (for errors). + line_off: usize, + /// Stack of bindings. + bindings: Vec>, + /// Functions we are currently parsing. + /// + /// Only used when parsing a `define-funs-rec`. + functions: BTreeMap<&'s str, (VarInfos, Typ)>, + /// Profiler. + _profiler: &'cxt Profiler, } - impl<'cxt, 's> Parser<'cxt, 's> { - /// Context accessor. - pub fn cxt(& self) -> & ParserCxt { - & * self.cxt - } - - - /// Returns the text that hasn't been parsed yet. - pub fn rest(& self) -> & str { - & self.string[self.cursor..] - } - - /// Generates a parse error at the current position. - fn error_here>(& mut self, msg: S) -> ErrorKind { - let pos = self.pos() ; - self.error(pos, msg) - } - - /// Generates a parse error at the given position. - fn error>( - & self, char_pos: Pos, msg: S - ) -> ErrorKind { - let mut char_pos = * char_pos ; - let msg = msg.into() ; - let mut line_count = self.line_off ; - let (mut pref, mut token, mut suff) = ( - "".to_string(), "".to_string(), "".to_string() - ) ; - for line in self.string.lines() { - line_count += 1 ; - if char_pos < line.len() { - pref = line[0..char_pos].to_string() ; - token = line[char_pos..(char_pos + 1)].to_string() ; - suff = line[(char_pos + 1)..line.len()].to_string() ; - break - } else if char_pos == line.len() { - pref = line.into() ; - token = "\\n".into() ; - suff = "".into() ; - break - } else { - char_pos -= line.len() + 1 - } - } - ErrorKind::ParseError( - ParseErrorData { - msg, pref, token, suff, line: Some(line_count) - } - ) - } - - - /// Returns `true` if there's still things to parse. - #[inline] - fn has_next(& self) -> bool { - self.cursor < self.string.len() - } - /// The next character, does not move the cursor. - fn peek(& self) -> Option<& 's str> { - if self.has_next() { - Some( - & self.string[ self.cursor .. self.cursor + 1 ] - ) - } else { - None + /// Context accessor. + pub fn cxt(&self) -> &ParserCxt { + &*self.cxt + } + + /// Returns the text that hasn't been parsed yet. + pub fn rest(&self) -> &str { + &self.string[self.cursor..] + } + + /// Generates a parse error at the current position. + fn error_here>(&mut self, msg: S) -> ErrorKind { + let pos = self.pos(); + self.error(pos, msg) + } + + /// Generates a parse error at the given position. + fn error>(&self, char_pos: Pos, msg: S) -> ErrorKind { + let mut char_pos = *char_pos; + let msg = msg.into(); + let mut line_count = self.line_off; + + let mut pref = "".to_string(); + let mut token = "".to_string(); + let mut suff = "".to_string(); + + for line in self.string.lines() { + line_count += 1; + if char_pos < line.len() { + pref = line[0..char_pos].to_string(); + token = line[char_pos..(char_pos + 1)].to_string(); + suff = line[(char_pos + 1)..line.len()].to_string(); + break; + } else if char_pos == line.len() { + pref = line.into(); + token = "\\n".into(); + suff = "".into(); + break; + } else { + char_pos -= line.len() + 1 + } + } + ErrorKind::ParseError(ParseErrorData { + msg, + pref, + token, + suff, + line: Some(line_count), + }) + } + + /// Returns `true` if there's still things to parse. + #[inline] + fn has_next(&self) -> bool { + self.cursor < self.string.len() + } + /// The next character, does not move the cursor. + fn peek(&self) -> Option<&'s str> { + if self.has_next() { + Some(&self.string[self.cursor..self.cursor + 1]) + } else { + None + } } - } - - /// True if the current character is a legal unquoted identifier character. - fn legal_id_char(& self) -> bool { - if self.cursor >= self.string.len() { - false - } else { - let char = & self.string[ self.cursor .. self.cursor + 1 ] ; - char.is_alphanumeric() - || id_special_chars.contains(& char) + /// True if the current character is a legal unquoted identifier character. + fn legal_id_char(&self) -> bool { + if self.cursor >= self.string.len() { + false + } else { + let char = &self.string[self.cursor..self.cursor + 1]; + char.is_alphanumeric() || id_special_chars.contains(&char) + } } - } - /// The next character. - fn next(& mut self) -> Option<& 's str> { - if self.has_next() { - self.cursor += 1 ; - Some( - & self.string[ self.cursor - 1 .. self.cursor ] - ) - } else { - None - } - } - /// Moves the cursor back by `n` character. - /// - /// # Panic - /// - /// - if `self.cursor < n` - fn move_back(& mut self, n: usize) { - debug_assert! { self.cursor >= n } - self.cursor -= n - } - - /// Backtracks to a precise position. - pub fn backtrack_to(& mut self, Pos(pos): Pos) { - self.cursor = pos - } - - /// Returns the current position. - pub fn pos(& mut self) -> Pos { - Pos( self.cursor ) - } - - /// Consumes whitespaces and comments. - pub fn ws_cmt(& mut self) { - let mut done = false ; - while ! done { - // Eat spaces. - let rest = & self.string[ self.cursor .. ] ; - let trimmed = rest.trim_left() ; - let diff = rest.len() - trimmed.len() ; - done = diff == 0 ; - self.cursor += diff ; - - // Eat comments. - match self.next() { - Some(";") => { - done = false ; - 'eat_line: while let Some(char) = self.next() { - if char == "\n" || char == "\r" { - break 'eat_line - } - } - }, - Some(_) => self.move_back(1), - None => (), - } - } - } - - /// Parses a word (a tag followed by an illegal ident character). - pub fn word(& mut self, word: & str) -> Res<()> { - if self.word_opt(word) { - Ok(()) - } else { - bail!( - self.error_here( - format!("expected `{}`", conf.emph(word)) - ) - ) - } - } - /// Parses a word (a tag followed by an illegal ident character). - pub fn word_opt(& mut self, word: & str) -> bool { - let pos = self.pos() ; - if self.tag_opt(word) { - if self.legal_id_char() { - self.backtrack_to(pos) ; - false - } else { - true - } - } else { - false - } - } - - /// Parses a string or fails. - pub fn tag(& mut self, tag: & str) -> Res<()> { - if self.tag_opt(tag) { - Ok(()) - } else { - bail!( - self.error_here( - format!("expected `{}`", conf.emph(tag)) - ) - ) - } - } - /// Parses a string or fails with error customization. - fn tag_err(& mut self, tag: & str, err: S) -> Res<()> - where S: Into { - if self.tag_opt(tag) { - Ok(()) - } else { - bail!( - self.error_here( err.into().to_string() ) - ) - } - } - /// Tries parsing a string. - pub fn tag_opt(& mut self, tag: & str) -> bool { - self.tag_opt_pos(tag).is_some() - } - /// Tries parsing a string. Returns the position of the start of the tag. - fn tag_opt_pos(& mut self, tag: & str) -> Option { - if self.string.len() < self.cursor + tag.len() { - None - } else if & self.string[ - self.cursor .. self.cursor + tag.len() - ] == tag { - let res = Some(self.pos()) ; - self.cursor += tag.len() ; - res - } else { - None - } - } - - /// Parses an ident of fails. - pub fn ident(& mut self) -> Res< (Pos, & 's str) > { - if let Some(id) = self.ident_opt() ? { - Ok(id) - } else { - bail!( - self.error_here("expected an identifier") - ) + /// The next character. + fn next(&mut self) -> Option<&'s str> { + if self.has_next() { + self.cursor += 1; + Some(&self.string[self.cursor - 1..self.cursor]) + } else { + None + } } - } - /// Tries to parse an ident. - pub fn ident_opt(& mut self) -> Res< Option< (Pos, & 's str) > > { - let ident_start_pos = self.pos() ; - if let Some(id) = self.unsafe_ident_opt() ? { - if keywords::is_keyword(id) { - bail!( - self.error( - ident_start_pos, - format!( - "illegal use of keyword `{}`", - conf.bad(id) - ) - ) - ) - } else { - Ok( Some((ident_start_pos, id)) ) - } - } else { - Ok(None) - } - } - /// Tries to parse an ident, does not check anything about the ident. - fn unsafe_ident_opt(& mut self) -> Res< Option<& 's str> > { - let ident_start_pos = self.pos() ; - if let Some(char) = self.next() { - if char == "|" { - let (mut legal_unquoted, mut is_first) = (true, true) ; - while let Some(char) = self.next() { - if char == "|" { - return Ok( - Some( - if legal_unquoted { - & self.string[ * ident_start_pos + 1 .. self.cursor - 1 ] - } else { - & self.string[ * ident_start_pos .. self.cursor ] + /// Moves the cursor back by `n` character. + /// + /// # Panic + /// + /// - if `self.cursor < n` + fn move_back(&mut self, n: usize) { + debug_assert! { self.cursor >= n } + self.cursor -= n + } + + /// Backtracks to a precise position. + pub fn backtrack_to(&mut self, Pos(pos): Pos) { + self.cursor = pos + } + + /// Returns the current position. + pub fn pos(&mut self) -> Pos { + Pos(self.cursor) + } + + /// Consumes whitespaces and comments. + pub fn ws_cmt(&mut self) { + let mut done = false; + while !done { + // Eat spaces. + let rest = &self.string[self.cursor..]; + let trimmed = rest.trim_left(); + let diff = rest.len() - trimmed.len(); + done = diff == 0; + self.cursor += diff; + + // Eat comments. + match self.next() { + Some(";") => { + done = false; + 'eat_line: while let Some(char) = self.next() { + if char == "\n" || char == "\r" { + break 'eat_line; + } + } } - ) - ) - } else { - legal_unquoted = legal_unquoted && ( - ( ! is_first && char.is_alphanumeric() ) || - ( is_first && char.is_alphabetic() ) || - id_special_chars.contains(char) - ) ; - is_first = false ; - } - } - bail!( - self.error( - ident_start_pos, - "expected `|` closing this quoted identifier, \ - found eof".to_string() - ) - ) - } else if char.is_alphabetic() || id_special_chars.contains(& char) { - while let Some(char) = self.next() { - if ! ( - char.is_alphanumeric() || id_special_chars.contains(& char) - ) { - self.move_back(1) ; - break - } - } - Ok( - Some( - & self.string[ * ident_start_pos .. self.cursor ] - ) - ) - } else { - self.backtrack_to(ident_start_pos) ; - Ok(None) - } - } else { - Ok(None) - } - } - - /// Consumes characters until some character. - /// - /// Returns `true` iff `char` was found. Hence, returns `false` iff `eof` was - /// reached. - fn eat_until(& mut self, char: char, inclusive: bool) -> bool { - for c in self.string[ self.cursor .. ].chars() { - if char == c { - if inclusive { - self.cursor += 1 - } - return true - } else { - self.cursor += 1 - } - } - false - } - - /// Returns all the characters until some character. - /// - /// `None` iff `char` was not found, i.e. `eat_until` returns `false`. - fn get_until(& mut self, char: char, inclusive: bool) -> Option<& 's str> { - let start_pos = self.pos() ; - let found_id = self.eat_until(char, inclusive) ; - if found_id { - Some( & self.string[ * start_pos .. self.cursor ] ) - } else { - None - } - } - - /// Parses a set-info. - fn set_info(& mut self) -> Res { - if ! self.word_opt("set-info") { - return Ok(false) - } - self.ws_cmt() ; - self.tag(":") ? ; - self.ws_cmt() ; - let _ = self.ident() ? ; - self.ws_cmt() ; - if self.tag_opt("\"") { - let found_it = self.eat_until('"', true) ; - if ! found_it { - bail!( - self.error_here("expected closing `\"`, found ") - ) - } - } else if self.ident_opt()?.is_some() { - () - } - Ok(true) - } - - /// Set-option. - fn set_option(& mut self) -> Res< Option<(& 's str, & 's str)> > { - let start_pos = self.pos() ; - if ! self.word_opt("set-option") { - return Ok(None) - } - self.ws_cmt() ; - self.tag(":") ? ; - let key = self.ident()?.1 ; - self.ws_cmt() ; - let val = if self.tag_opt("|") { - if let Some(res) = self.get_until('|', true) { - res - } else { - bail!( - self.error_here("could not find closing `|` opened") - ) - } - } else if self.tag_opt("\"") { - if let Some(res) = self.get_until('"', true) { - res - } else { - bail!( - self.error_here("could not find closing `\"` opened") - ) - } - } else if let Some(res) = self.get_until(')', false) { - res.trim() - } else { - self.backtrack_to(start_pos) ; - bail!( - self.error_here("could not find closing `)` for this set-option") - ) - } ; - Ok(Some((key, val))) - } - - /// Parses an echo. - fn echo(& mut self) -> Res< Option<& 's str> > { - if ! self.word_opt("echo") { - return Ok(None) - } - self.ws_cmt() ; - self.tag("\"") ? ; - let blah = self.get_until('"', false) ; - self.tag("\"") ? ; - if let Some(blah) = blah { - Ok( Some(blah) ) - } else { - bail!( - self.error_here("expected closing `\"`, found ") - ) + Some(_) => self.move_back(1), + None => (), + } + } } - } - /// Parses a set-logic. - fn set_logic(& mut self) -> Res { - if ! self.word_opt("set-logic") { - return Ok(false) + /// Parses a word (a tag followed by an illegal ident character). + pub fn word(&mut self, word: &str) -> Res<()> { + if self.word_opt(word) { + Ok(()) + } else { + bail!(self.error_here(format!("expected `{}`", conf.emph(word)))) + } } - - self.ws_cmt() ; - if ! self.word_opt("HORN") { - bail!( self.error_here("unknown logic: ") ) + /// Parses a word (a tag followed by an illegal ident character). + pub fn word_opt(&mut self, word: &str) -> bool { + let pos = self.pos(); + if self.tag_opt(word) { + if self.legal_id_char() { + self.backtrack_to(pos); + false + } else { + true + } + } else { + false + } } - Ok(true) - } - /// Parses a sort or fails. - fn sort(& mut self) -> Res { - if let Some(sort) = self.sort_opt() ? { - Ok(sort) - } else { - bail!( self.error_here("expected sort (Int or Bool)") ) + /// Parses a string or fails. + pub fn tag(&mut self, tag: &str) -> Res<()> { + if self.tag_opt(tag) { + Ok(()) + } else { + bail!(self.error_here(format!("expected `{}`", conf.emph(tag)))) + } } - } - - - /// Tries to parse a sort. - pub fn sort_opt(& mut self) -> Res> { - let start_pos = self.pos() ; - let res = self.internal_sort_opt() ; - if let Ok( Some(typ) ) = & res { - typ.check().chain_err( - || self.error(start_pos, "") - ) ? + /// Parses a string or fails with error customization. + fn tag_err(&mut self, tag: &str, err: S) -> Res<()> + where + S: Into, + { + if self.tag_opt(tag) { + Ok(()) + } else { + bail!(self.error_here(err.into().to_string())) + } } - res - } - - - - /// Tries to parse a sort. - pub fn internal_sort_opt(& mut self) -> Res> { - // Compound type under construction. - // - // The position is always that of the opening paren of the type. - enum CTyp<'a> { - // Array under construction, meaning we're parsing the index sort. - Array { pos: Pos }, - // Array with a source, meaning we're parsing the target sort. - ArraySrc { pos: Pos, src: Typ }, - // A datatype application. - DTyp { name: & 'a str, pos: Pos, typs: dtyp::TPrmMap } + /// Tries parsing a string. + pub fn tag_opt(&mut self, tag: &str) -> bool { + self.tag_opt_pos(tag).is_some() } - - let mut stack = vec![] ; - - let start_pos = self.pos() ; - - 'go_down: loop { - self.ws_cmt() ; - let current_pos = self.pos() ; - - let mut typ = if self.tag_opt("(") { - self.ws_cmt() ; - // Parsing a compound type. - - if self.tag_opt("Array") { - if ! self.legal_id_char() { - // We're parsing an array type. - stack.push( CTyp::Array { pos: current_pos } ) ; - continue 'go_down - } else { + /// Tries parsing a string. Returns the position of the start of the tag. + fn tag_opt_pos(&mut self, tag: &str) -> Option { + if self.string.len() < self.cursor + tag.len() { None - } - } else if let Some((pos, name)) = self.ident_opt() ? { - stack.push( - CTyp::DTyp { name, pos, typs: dtyp::TPrmMap::new() } - ) ; - continue 'go_down + } else if &self.string[self.cursor..self.cursor + tag.len()] == tag { + let res = Some(self.pos()); + self.cursor += tag.len(); + res } else { - None + None } + } - } else if self.tag_opt("Int") { - if ! self.legal_id_char() { - Some(typ::int()) + /// Parses an ident of fails. + pub fn ident(&mut self) -> Res<(Pos, &'s str)> { + if let Some(id) = self.ident_opt()? { + Ok(id) } else { - None + bail!(self.error_here("expected an identifier")) } - } else if self.tag_opt("Real") { - if ! self.legal_id_char() { - Some(typ::real()) + } + /// Tries to parse an ident. + pub fn ident_opt(&mut self) -> Res> { + let ident_start_pos = self.pos(); + if let Some(id) = self.unsafe_ident_opt()? { + if keywords::is_keyword(id) { + bail!(self.error( + ident_start_pos, + format!("illegal use of keyword `{}`", conf.bad(id)) + )) + } else { + Ok(Some((ident_start_pos, id))) + } } else { - None + Ok(None) } - } else if self.tag_opt("Bool") { - if ! self.legal_id_char() { - Some(typ::bool()) + } + /// Tries to parse an ident, does not check anything about the ident. + fn unsafe_ident_opt(&mut self) -> Res> { + let ident_start_pos = self.pos(); + if let Some(char) = self.next() { + if char == "|" { + let (mut legal_unquoted, mut is_first) = (true, true); + while let Some(char) = self.next() { + if char == "|" { + return Ok(Some(if legal_unquoted { + &self.string[*ident_start_pos + 1..self.cursor - 1] + } else { + &self.string[*ident_start_pos..self.cursor] + })); + } else { + legal_unquoted = legal_unquoted + && ((!is_first && char.is_alphanumeric()) + || (is_first && char.is_alphabetic()) + || id_special_chars.contains(char)); + is_first = false; + } + } + bail!( + self.error( + ident_start_pos, + "expected `|` closing this quoted identifier, \ + found eof" + .to_string() + ) + ) + } else if char.is_alphabetic() || id_special_chars.contains(&char) { + while let Some(char) = self.next() { + if !(char.is_alphanumeric() || id_special_chars.contains(&char)) { + self.move_back(1); + break; + } + } + Ok(Some(&self.string[*ident_start_pos..self.cursor])) + } else { + self.backtrack_to(ident_start_pos); + Ok(None) + } } else { - None + Ok(None) } - } else { - None - } ; + } - if typ.is_none() { - if let Some((pos, name)) = self.ident_opt() ? { - if let Ok(dtyp) = dtyp::get(name) { - typ = Some( - typ::dtyp( dtyp, vec![].into() ) - ) - } else { - bail!( - self.error( - pos, format!("unknown sort `{}`", conf.bad(name)) - ) - ) - } + /// Consumes characters until some character. + /// + /// Returns `true` iff `char` was found. Hence, returns `false` iff `eof` was + /// reached. + fn eat_until(&mut self, char: char, inclusive: bool) -> bool { + for c in self.string[self.cursor..].chars() { + if char == c { + if inclusive { + self.cursor += 1 + } + return true; + } else { + self.cursor += 1 + } } - } + false + } - 'go_up: loop { - if let Some(typ) = & typ { - typ.check().chain_err( - || self.error(start_pos, "while parsing this sort") - ) ? + /// Returns all the characters until some character. + /// + /// `None` iff `char` was not found, i.e. `eat_until` returns `false`. + fn get_until(&mut self, char: char, inclusive: bool) -> Option<&'s str> { + let start_pos = self.pos(); + let found_id = self.eat_until(char, inclusive); + if found_id { + Some(&self.string[*start_pos..self.cursor]) + } else { + None } + } - match stack.pop() { - - Some( - CTyp::Array { pos } - ) => if let Some(src) = typ { - stack.push( CTyp::ArraySrc { pos, src } ) ; - // Need to parse the domain now. - continue 'go_down - } else { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error(current_pos, "expected index sort") - ) ? - }, - - Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { - typ = Some( typ::array(src, tgt) ) ; - - // Parse closing paren. - self.ws_cmt() ; - if ! self.tag_opt(")") { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error( - current_pos, "expected expected closing paren" - ) - ) ? + /// Parses a set-info. + fn set_info(&mut self) -> Res { + if !self.word_opt("set-info") { + return Ok(false); + } + self.ws_cmt(); + self.tag(":")?; + self.ws_cmt(); + let _ = self.ident()?; + self.ws_cmt(); + if self.tag_opt("\"") { + let found_it = self.eat_until('"', true); + if !found_it { + bail!(self.error_here("expected closing `\"`, found ")) } + } else if self.ident_opt()?.is_some() { + () + } + Ok(true) + } - continue 'go_up - } else { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error(current_pos, "expected domain sort") - ) ? - }, - - Some( - CTyp::DTyp { name, pos, mut typs } - ) => if let Some(prm) = typ { - typs.push(prm) ; - - self.ws_cmt() ; - if self.tag_opt(")") { - if let Ok(dtyp) = dtyp::get(name) { - typ = Some( typ::dtyp(dtyp, typs) ) - } else { - bail!( - self.error( - pos, format!( - "unknown sort `{}`", conf.bad(name) - ) - ) - ) - } - continue 'go_up + /// Set-option. + fn set_option(&mut self) -> Res> { + let start_pos = self.pos(); + if !self.word_opt("set-option") { + return Ok(None); + } + self.ws_cmt(); + self.tag(":")?; + let key = self.ident()?.1; + self.ws_cmt(); + let val = if self.tag_opt("|") { + if let Some(res) = self.get_until('|', true) { + res } else { - stack.push( CTyp::DTyp { name, pos, typs } ) ; - continue 'go_down + bail!(self.error_here("could not find closing `|` opened")) } - } else { - Err::<_, Error>( - self.error(pos, "while parsing this sort").into() - ) ? - }, + } else if self.tag_opt("\"") { + if let Some(res) = self.get_until('"', true) { + res + } else { + bail!(self.error_here("could not find closing `\"` opened")) + } + } else if let Some(res) = self.get_until(')', false) { + res.trim() + } else { + self.backtrack_to(start_pos); + bail!(self.error_here("could not find closing `)` for this set-option")) + }; + Ok(Some((key, val))) + } - None => if typ.is_none() { - self.backtrack_to(start_pos) ; - return Ok(None) - } else { - return Ok(typ) - } + /// Parses an echo. + fn echo(&mut self) -> Res> { + if !self.word_opt("echo") { + return Ok(None); + } + self.ws_cmt(); + self.tag("\"")?; + let blah = self.get_until('"', false); + self.tag("\"")?; + if let Some(blah) = blah { + Ok(Some(blah)) + } else { + bail!(self.error_here("expected closing `\"`, found ")) } - - } } - } - - + /// Parses a set-logic. + fn set_logic(&mut self) -> Res { + if !self.word_opt("set-logic") { + return Ok(false); + } - /// Parses a sort. - pub fn nu_sort( - & mut self, type_params: & BTreeMap<& 's str, dtyp::TPrmIdx> - ) -> Res { - if let Some(res) = self.nu_sort_opt(type_params) ? { - Ok(res) - } else { - bail!( self.error_here("expected sort") ) + self.ws_cmt(); + if !self.word_opt("HORN") { + bail!(self.error_here("unknown logic: ")) + } + Ok(true) } - } - - - /// Tries to parse a sort. - pub fn nu_sort_opt( - & mut self, type_params: & BTreeMap<& 's str, dtyp::TPrmIdx> - ) -> Res> { - use dtyp::PartialTyp ; - // Compound type under construction. - // - // The position is always that of the opening paren of the type. - enum CTyp<'a> { - // Array under construction, meaning we're parsing the index sort. - Array { pos: Pos }, - // Array with a source, meaning we're parsing the target sort. - ArraySrc { pos: Pos, src: PartialTyp }, - // Datatype storing the name, the position of the name, and the types. - DTyp { name: & 'a str, pos: Pos, typs: dtyp::TPrmMap< PartialTyp > }, + /// Parses a sort or fails. + fn sort(&mut self) -> Res { + if let Some(sort) = self.sort_opt()? { + Ok(sort) + } else { + bail!(self.error_here("expected sort (Int or Bool)")) + } } - let mut stack = vec![] ; + /// Tries to parse a sort. + pub fn sort_opt(&mut self) -> Res> { + let start_pos = self.pos(); + let res = self.internal_sort_opt(); + if let Ok(Some(typ)) = &res { + typ.check().chain_err(|| self.error(start_pos, ""))? + } + res + } - let start_pos = self.pos() ; + /// Tries to parse a sort. + pub fn internal_sort_opt(&mut self) -> Res> { + // Compound type under construction. + // + // The position is always that of the opening paren of the type. + enum CTyp<'a> { + // Array under construction, meaning we're parsing the index sort. + Array { + pos: Pos, + }, + // Array with a source, meaning we're parsing the target sort. + ArraySrc { + pos: Pos, + src: Typ, + }, + // A datatype application. + DTyp { + name: &'a str, + pos: Pos, + typs: dtyp::TPrmMap, + }, + } - 'go_down: loop { - self.ws_cmt() ; - let current_pos = self.pos() ; + let mut stack = vec![]; + + let start_pos = self.pos(); + + 'go_down: loop { + self.ws_cmt(); + let current_pos = self.pos(); + + let mut typ = if self.tag_opt("(") { + self.ws_cmt(); + // Parsing a compound type. + + if self.tag_opt("Array") { + if !self.legal_id_char() { + // We're parsing an array type. + stack.push(CTyp::Array { pos: current_pos }); + continue 'go_down; + } else { + None + } + } else if let Some((pos, name)) = self.ident_opt()? { + stack.push(CTyp::DTyp { + name, + pos, + typs: dtyp::TPrmMap::new(), + }); + continue 'go_down; + } else { + None + } + } else if self.tag_opt("Int") { + if !self.legal_id_char() { + Some(typ::int()) + } else { + None + } + } else if self.tag_opt("Real") { + if !self.legal_id_char() { + Some(typ::real()) + } else { + None + } + } else if self.tag_opt("Bool") { + if !self.legal_id_char() { + Some(typ::bool()) + } else { + None + } + } else { + None + }; + + if typ.is_none() { + if let Some((pos, name)) = self.ident_opt()? { + if let Ok(dtyp) = dtyp::get(name) { + typ = Some(typ::dtyp(dtyp, vec![].into())) + } else { + bail!(self.error(pos, format!("unknown sort `{}`", conf.bad(name)))) + } + } + } - let mut typ = if self.tag_opt("(") { - self.ws_cmt() ; - // Parsing a compound type. + 'go_up: loop { + if let Some(typ) = &typ { + typ.check() + .chain_err(|| self.error(start_pos, "while parsing this sort"))? + } - if self.tag_opt("Array") { - if ! self.legal_id_char() { - // We're parsing an array type. - stack.push( CTyp::Array { pos: current_pos } ) ; - continue 'go_down - } else { - None - } - } else if let Some((pos, name)) = self.ident_opt() ? { - stack.push( - CTyp::DTyp { name, pos, typs: dtyp::TPrmMap::new() } - ) ; - continue 'go_down - } else { - None + match stack.pop() { + Some(CTyp::Array { pos }) => if let Some(src) = typ { + stack.push(CTyp::ArraySrc { pos, src }); + // Need to parse the domain now. + continue 'go_down; + } else { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| self.error(current_pos, "expected index sort"))? + }, + + Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { + typ = Some(typ::array(src, tgt)); + + // Parse closing paren. + self.ws_cmt(); + if !self.tag_opt(")") { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| { + self.error(current_pos, "expected expected closing paren") + })? + } + + continue 'go_up; + } else { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| self.error(current_pos, "expected domain sort"))? + }, + + Some(CTyp::DTyp { + name, + pos, + mut typs, + }) => if let Some(prm) = typ { + typs.push(prm); + + self.ws_cmt(); + if self.tag_opt(")") { + if let Ok(dtyp) = dtyp::get(name) { + typ = Some(typ::dtyp(dtyp, typs)) + } else { + bail!(self.error(pos, format!("unknown sort `{}`", conf.bad(name)))) + } + continue 'go_up; + } else { + stack.push(CTyp::DTyp { name, pos, typs }); + continue 'go_down; + } + } else { + Err::<_, Error>(self.error(pos, "while parsing this sort").into())? + }, + + None => if typ.is_none() { + self.backtrack_to(start_pos); + return Ok(None); + } else { + return Ok(typ); + }, + } + } } + } - } else if self.tag_opt("Int") { - if ! self.legal_id_char() { - Some( typ::int().into() ) + /// Parses a sort. + pub fn nu_sort( + &mut self, + type_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, + ) -> Res { + if let Some(res) = self.nu_sort_opt(type_params)? { + Ok(res) } else { - None + bail!(self.error_here("expected sort")) } + } - } else if self.tag_opt("Real") { - if ! self.legal_id_char() { - Some( typ::real().into() ) - } else { - None + /// Tries to parse a sort. + pub fn nu_sort_opt( + &mut self, + type_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, + ) -> Res> { + use dtyp::PartialTyp; + + // Compound type under construction. + // + // The position is always that of the opening paren of the type. + enum CTyp<'a> { + // Array under construction, meaning we're parsing the index sort. + Array { + pos: Pos, + }, + // Array with a source, meaning we're parsing the target sort. + ArraySrc { + pos: Pos, + src: PartialTyp, + }, + // Datatype storing the name, the position of the name, and the types. + DTyp { + name: &'a str, + pos: Pos, + typs: dtyp::TPrmMap, + }, } - } else if self.tag_opt("Bool") { - if ! self.legal_id_char() { - Some( typ::bool().into() ) - } else { - None - } + let mut stack = vec![]; + + let start_pos = self.pos(); + + 'go_down: loop { + self.ws_cmt(); + let current_pos = self.pos(); + + let mut typ = if self.tag_opt("(") { + self.ws_cmt(); + // Parsing a compound type. + + if self.tag_opt("Array") { + if !self.legal_id_char() { + // We're parsing an array type. + stack.push(CTyp::Array { pos: current_pos }); + continue 'go_down; + } else { + None + } + } else if let Some((pos, name)) = self.ident_opt()? { + stack.push(CTyp::DTyp { + name, + pos, + typs: dtyp::TPrmMap::new(), + }); + continue 'go_down; + } else { + None + } + } else if self.tag_opt("Int") { + if !self.legal_id_char() { + Some(typ::int().into()) + } else { + None + } + } else if self.tag_opt("Real") { + if !self.legal_id_char() { + Some(typ::real().into()) + } else { + None + } + } else if self.tag_opt("Bool") { + if !self.legal_id_char() { + Some(typ::bool().into()) + } else { + None + } + } else { + None + }; + + if typ.is_none() { + if let Some((pos, name)) = self.ident_opt()? { + // Type parameter? + typ = if let Some(idx) = type_params.get(name) { + Some(PartialTyp::Param(*idx)) + } else { + Some(PartialTyp::DTyp(name.into(), pos, vec![].into())) + } + } + } - } else { - None - } ; - - if typ.is_none() { - if let Some((pos, name)) = self.ident_opt() ? { - // Type parameter? - typ = if let Some(idx) = type_params.get(name) { - Some( PartialTyp::Param(* idx) ) - } else { - Some( - PartialTyp::DTyp( name.into(), pos, vec![].into() ) - ) - } + 'go_up: loop { + // if let Some(typ) = & typ { + // if let Err((_, err)) = typ.check() { + // let err: Error = err.into() ; + + // bail!( + // err.chain_err( + // || self.error(start_pos, "while parsing this sort") + // ) + // ) + // } + // } + + match stack.pop() { + Some(CTyp::Array { pos }) => if let Some(src) = typ { + stack.push(CTyp::ArraySrc { pos, src }); + // Need to parse the domain now. + continue 'go_down; + } else { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| self.error(current_pos, "expected index sort"))? + }, + + Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { + typ = Some(PartialTyp::Array(Box::new(src), Box::new(tgt))); + + // Parse closing paren. + self.ws_cmt(); + if !self.tag_opt(")") { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| { + self.error(current_pos, "expected expected closing paren") + })? + } + + continue 'go_up; + } else { + Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) + .chain_err(|| self.error(current_pos, "expected domain sort"))? + }, + + Some(CTyp::DTyp { + name, + pos, + mut typs, + }) => if let Some(t) = typ { + typs.push(t); + self.ws_cmt(); + if self.tag_opt(")") { + typ = Some(PartialTyp::DTyp(name.into(), pos, typs)); + continue 'go_up; + } else { + stack.push(CTyp::DTyp { name, pos, typs }); + continue 'go_down; + } + } else { + Err::<_, Error>(self.error(pos, "while parsing this datatype sort").into()) + .chain_err(|| self.error(current_pos, "expected sort"))? + }, + + None => if typ.is_none() { + self.backtrack_to(start_pos); + return Ok(None); + } else { + return Ok(typ); + }, + } + } + } + } + /// Parses some recursive function declarations. + fn define_funs_rec(&mut self, instance: &Instance) -> Res { + if !self.word_opt(keywords::cmd::def_funs_rec) { + return Ok(false); } - } - 'go_up: loop { - // if let Some(typ) = & typ { - // if let Err((_, err)) = typ.check() { - // let err: Error = err.into() ; + use fun::RFun; - // bail!( - // err.chain_err( - // || self.error(start_pos, "while parsing this sort") - // ) - // ) - // } - // } + self.functions.clear(); - match stack.pop() { + let mut funs: Vec<(RFun, Pos, _)> = vec![]; - Some(CTyp::Array { pos }) => if let Some(src) = typ { - stack.push( CTyp::ArraySrc { pos, src } ) ; - // Need to parse the domain now. - continue 'go_down - } else { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error(current_pos, "expected index sort") - ) ? - }, - - Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { - typ = Some( - PartialTyp::Array( Box::new(src), Box::new(tgt) ) - ) ; - - // Parse closing paren. - self.ws_cmt() ; - if ! self.tag_opt(")") { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error( - current_pos, "expected expected closing paren" - ) - ) ? - } + self.ws_cmt(); + self.tag("(") + .chain_err(|| "opening the list of function declarations")?; + self.ws_cmt(); - continue 'go_up - } else { - Err::<_, Error>( - self.error(pos, "while parsing this array sort").into() - ).chain_err( - || self.error(current_pos, "expected domain sort") - ) ? - }, - - Some(CTyp::DTyp { name, pos, mut typs }) => if let Some(t) = typ { - typs.push(t) ; - self.ws_cmt() ; - if self.tag_opt(")") { - typ = Some( PartialTyp::DTyp(name.into(), pos, typs) ) ; - continue 'go_up - } else { - stack.push( CTyp::DTyp { name, pos, typs } ) ; - continue 'go_down - } - } else { - Err::<_, Error>( - self.error(pos, "while parsing this datatype sort").into() - ).chain_err( - || self.error(current_pos, "expected sort") - ) ? - }, + // Parse all declarations. + while !self.tag_opt(")") { + self.tag("(") + .chain_err(|| "opening a function declaration")?; + self.ws_cmt(); - None => if typ.is_none() { - self.backtrack_to(start_pos) ; - return Ok(None) - } else { - return Ok(typ) - } - } + let (pos, name) = self + .ident() + .chain_err(|| "at the start of the function declaration")?; + self.ws_cmt(); - } - } + self.ws_cmt(); + self.tag("(") + .chain_err(|| format!("opening function `{}`'s arguments", conf.emph(name)))?; + self.ws_cmt(); - } + let mut args = VarInfos::new(); + let mut arg_map = BTreeMap::new(); + // Parse the signature of this function. + while !self.tag_opt(")") { + self.tag("(")?; + self.ws_cmt(); + let (pos, arg_name) = self.ident().chain_err(|| { + format!( + "in function `{}`'s signature (argument name)", + conf.emph(name) + ) + })?; + self.ws_cmt(); + + let sort = self.sort().chain_err(|| { + format!( + "for argument `{}` of function `{}`", + conf.emph(arg_name), + conf.emph(name) + ) + })?; + + let idx = args.next_index(); + args.push(VarInfo::new(arg_name.into(), sort, idx)); + + if arg_map.insert(arg_name, idx).is_some() { + bail!(self.error( + pos, + format!( + "found two arguments named `{}` \ + in function `{}`'s declaration", + conf.bad(arg_name), + conf.emph(name) + ) + )) + } + self.ws_cmt(); + self.tag(")").chain_err(|| { + format!( + "closing argument `{}` of function `{}`", + conf.emph(arg_name), + conf.emph(name) + ) + })?; + self.ws_cmt() + } + self.ws_cmt(); + let typ = self + .sort() + .chain_err(|| format!("sort of function `{}`", conf.emph(name)))?; + + let mut fun = RFun::new(name, args, typ); + fun::register_dec(fun.clone())?; + + // Check this is the first time we see this function and populate + // dependencies. + for (other, other_pos, _) in &mut funs { + if other.name == fun.name { + let e: Error = self + .error( + pos, + format!("found two functions named `{}`", conf.bad(name)), + ).into(); + bail!(e.chain_err(|| self.error(*other_pos, "first appearance"))) + } + other.insert_dep(fun.name.clone()); + fun.insert_dep(other.name.clone()); + } + let prev = self + .functions + .insert(name, (fun.sig.clone(), fun.typ.clone())); + debug_assert! { prev.is_none() } + funs.push((fun, pos, arg_map)); - /// Parses some recursive function declarations. - fn define_funs_rec(& mut self, instance: & Instance) -> Res { - if ! self.word_opt(keywords::cmd::def_funs_rec) { - return Ok(false) - } + self.ws_cmt(); + self.tag(")") + .chain_err(|| format!("closing function `{}`'s declaration", conf.emph(name)))?; + self.ws_cmt() + } - use fun::RFun ; + self.ws_cmt(); + self.tag("(") + .chain_err(|| "opening the list of function definitions")?; + self.ws_cmt(); + + // Parse all definitions. + for (mut fun, pos, var_map) in funs { + if let Some(term) = self + .term_opt(&fun.sig, &var_map, instance) + .chain_err(|| { + format!( + "while parsing definition (term) for function `{}`", + conf.emph(&fun.name) + ) + }).chain_err(|| self.error(pos, "declared here"))? + { + // Success. + fun.set_def(term); + self.ws_cmt() + } else { + let e: Error = self + .error_here(format!( + "expected definition (term) for function `{}`", + conf.emph(&fun.name) + )).into(); + bail!(e.chain_err(|| self.error(pos, "declared here"))) + } - self.functions.clear() ; + let _ = fun::retrieve_dec(&fun.name)?; - let mut funs: Vec<(RFun, Pos, _)> = vec![] ; + fun::mk(fun).chain_err(|| self.error(pos, "while registering this function"))?; + } - self.ws_cmt() ; - self.tag("(").chain_err( - || "opening the list of function declarations" - ) ? ; - self.ws_cmt() ; + self.ws_cmt(); + self.tag(")") + .chain_err(|| "closing the list of function definitions")?; + Ok(true) + } - // Parse all declarations. - while ! self.tag_opt(")") { + /// Parses a datatype constructor. + fn dtyp_new( + &mut self, + params_map: &BTreeMap<&'s str, dtyp::TPrmIdx>, + ) -> Res> { + if self.tag_opt("(") { + // Normal case. - self.tag("(").chain_err( - || "opening a function declaration" - ) ? ; - self.ws_cmt() ; + let (constructor_pos, constructor_ident) = self.ident()?; + self.ws_cmt(); - let (pos, name) = self.ident().chain_err( - || "at the start of the function declaration" - ) ? ; - self.ws_cmt() ; + let mut selectors = dtyp::CArgs::new(); - self.ws_cmt() ; - self.tag("(").chain_err( - || format!( - "opening function `{}`'s arguments", conf.emph(name) - ) - ) ? ; - self.ws_cmt() ; + // Parse selectors. + while self.tag_opt("(") { + self.ws_cmt(); + let (selector_pos, selector_ident) = self.ident()?; + self.ws_cmt(); - let mut args = VarInfos::new() ; - let mut arg_map = BTreeMap::new() ; + if selectors.iter().any(|(id, _)| id == selector_ident) { + bail!(self.error( + selector_pos, + format!("found a selector named `{}` twice", selector_ident) + )) + } - // Parse the signature of this function. - while ! self.tag_opt(")") { - self.tag("(") ? ; - self.ws_cmt() ; + let ptyp = self.nu_sort(¶ms_map)?; + selectors.push((selector_ident.to_string(), ptyp)); - let (pos, arg_name) = self.ident().chain_err( - || format!( - "in function `{}`'s signature (argument name)", - conf.emph(name) - ) - ) ? ; - self.ws_cmt() ; - - let sort = self.sort().chain_err( - || format!( - "for argument `{}` of function `{}`", - conf.emph(arg_name), conf.emph(name) - ) - ) ? ; - - let idx = args.next_index() ; - args.push( - VarInfo::new( arg_name.into(), sort, idx ) - ) ; + self.tag(")") + .chain_err(|| format!("closing selector `{}`", conf.emph(selector_ident)))?; + self.ws_cmt() + } - if arg_map.insert(arg_name, idx).is_some() { - bail!( - self.error( - pos, format!( - "found two arguments named `{}` \ - in function `{}`'s declaration", - conf.bad(arg_name), conf.emph(name) - ) - ) - ) - } - - self.ws_cmt() ; - self.tag(")").chain_err( - || format!( - "closing argument `{}` of function `{}`", - conf.emph(arg_name), conf.emph(name) - ) - ) ? ; - self.ws_cmt() - } - - self.ws_cmt() ; - let typ = self.sort().chain_err( - || format!( - "sort of function `{}`", conf.emph(name) - ) - ) ? ; - - let mut fun = RFun::new(name, args, typ) ; - fun::register_dec( fun.clone() ) ? ; - - // Check this is the first time we see this function and populate - // dependencies. - for (other, other_pos, _) in & mut funs { - if other.name == fun.name { - let e: Error = self.error( - pos, format!( "found two functions named `{}`", conf.bad(name) ) - ).into() ; - bail!( - e.chain_err( || self.error(* other_pos, "first appearance") ) - ) - } - - other.insert_dep( fun.name.clone() ) ; - fun.insert_dep( other.name.clone() ) ; - } - - let prev = self.functions.insert( - name, (fun.sig.clone(), fun.typ.clone()) - ) ; - debug_assert! { prev.is_none() } - - funs.push( (fun, pos, arg_map) ) ; - - self.ws_cmt() ; - self.tag(")").chain_err( - || format!("closing function `{}`'s declaration", conf.emph(name)) - ) ? ; - self.ws_cmt() - } - - self.ws_cmt() ; - self.tag("(").chain_err( - || "opening the list of function definitions" - ) ? ; - self.ws_cmt() ; - - // Parse all definitions. - for (mut fun, pos, var_map) in funs { - - if let Some(term) = self.term_opt( - & fun.sig, & var_map, instance - ).chain_err( - || format!( - "while parsing definition (term) for function `{}`", - conf.emph(& fun.name) - ) - ).chain_err( - || self.error(pos, "declared here") - ) ? { - - // Success. - fun.set_def(term) ; - self.ws_cmt() - - } else { - let e: Error = self.error_here( - format!( - "expected definition (term) for function `{}`", - conf.emph(& fun.name) - ) - ).into() ; - bail!( - e.chain_err( - || self.error(pos, "declared here") - ) - ) - } - - let _ = fun::retrieve_dec(& fun.name) ? ; - - fun::mk(fun).chain_err( - || self.error(pos, "while registering this function") - ) ? ; - } - - self.ws_cmt() ; - self.tag(")").chain_err( - || "closing the list of function definitions" - ) ? ; - - Ok(true) - } - - - - /// Parses a datatype constructor. - fn dtyp_new( - & mut self, - params_map: & BTreeMap<& 's str, dtyp::TPrmIdx>, - ) -> Res< Option<(Pos, & 's str, dtyp::CArgs)> > { - - if self.tag_opt("(") { - // Normal case. - - let (constructor_pos, constructor_ident) = self.ident() ? ; - self.ws_cmt() ; - - let mut selectors = dtyp::CArgs::new() ; - - // Parse selectors. - while self.tag_opt("(") { - self.ws_cmt() ; - let (selector_pos, selector_ident) = self.ident() ? ; - self.ws_cmt() ; - - if selectors.iter().any( - |(id, _)| id == selector_ident - ) { - bail!( - self.error( - selector_pos, format!( - "found a selector named `{}` twice", selector_ident - ) - ) - ) + self.tag(")").chain_err(|| "closing datatype constructor")?; + + Ok(Some((constructor_pos, constructor_ident, selectors))) + } else if let Some((constructor_pos, constructor_ident)) = self.ident_opt()? { + // Constant, paren-free constructor. This is actually not legal in + // SMT-LIB 2, but some people use it anyways. + return Ok(Some(( + constructor_pos, + constructor_ident, + dtyp::CArgs::new(), + ))); + } else { + return Ok(None); } - - let ptyp = self.nu_sort(& params_map) ? ; - selectors.push( - ( selector_ident.to_string(), ptyp ) - ) ; - - self.tag(")").chain_err( - || format!( - "closing selector `{}`", conf.emph(selector_ident) - ) - ) ? ; - self.ws_cmt() - } - - self.tag(")").chain_err( - || "closing datatype constructor" - ) ? ; - - Ok( - Some( (constructor_pos, constructor_ident, selectors) ) - ) - - } else if let Some( - (constructor_pos, constructor_ident) - ) = self.ident_opt() ? { - // Constant, paren-free constructor. This is actually not legal in - // SMT-LIB 2, but some people use it anyways. - return Ok( - Some( - ( constructor_pos, constructor_ident, dtyp::CArgs::new() ) - ) - ) - - } else { - return Ok( None ) } - } - + /// Parses a single datatype declaration. + fn dtyp_dec(&mut self, dtyp: &mut dtyp::RDTyp, arity: Option) -> Res<()> { + self.tag("(").chain_err(|| "opening datatype declaration")?; + self.ws_cmt(); + + let mut params_map = BTreeMap::new(); + let param_pos = self.pos(); + + // Try to parse parameters. + let closing_paren = if self.word_opt("par") { + self.ws_cmt(); + self.tag("(").chain_err(|| "opening sort parameter list")?; + self.ws_cmt(); + + while let Some((pos, ident)) = self.ident_opt()? { + let idx = dtyp.push_typ_param(ident); + if let Some(prev) = params_map.insert(ident, idx) { + bail!(self.error( + pos, + format!( + "type parameters #{} and #{} have the same name `{}`", + idx, prev, ident + ) + )) + } + self.ws_cmt() + } + if let Some(arity) = arity { + if Int::from(params_map.len()) != arity { + bail!(self.error( + param_pos, + format!("expected {} parameters, found {}", arity, params_map.len()) + )) + } + } + self.tag(")").chain_err(|| "closing sort parameter list")?; - /// Parses a single datatype declaration. - fn dtyp_dec( - & mut self, dtyp: & mut dtyp::RDTyp, arity: Option - ) -> Res<()> { - self.tag("(").chain_err( - || "opening datatype declaration" - ) ? ; - self.ws_cmt() ; + self.ws_cmt(); + self.tag("(") + .chain_err(|| "opening the list of constructor")?; + self.ws_cmt(); - let mut params_map = BTreeMap::new() ; - let param_pos = self.pos() ; + true + } else { + false + }; - // Try to parse parameters. - let closing_paren = if self.word_opt("par") { - self.ws_cmt() ; - self.tag("(").chain_err( - || "opening sort parameter list" - ) ? ; - self.ws_cmt() ; + while let Some((constructor_pos, constructor_ident, selectors)) = + self.dtyp_new(¶ms_map)? + { + self.ws_cmt(); - while let Some((pos, ident)) = self.ident_opt() ? { - let idx = dtyp.push_typ_param(ident) ; - if let Some(prev) = params_map.insert(ident, idx) { - bail!( - self.error( - pos, format!( - "type parameters #{} and #{} have the same name `{}`", - idx, prev, ident - ) - ) - ) + dtyp.add_constructor(constructor_ident, selectors) + .chain_err(|| self.error(constructor_pos, "in this constructor"))? } - self.ws_cmt() - } - if let Some(arity) = arity { - if Int::from( params_map.len() ) != arity { - bail!( - self.error( - param_pos, format!( - "expected {} parameters, found {}", arity, params_map.len() - ) - ) - ) + if closing_paren { + self.ws_cmt(); + self.tag(")") + .chain_err(|| "closing the list of constructor")?; } - } - - self.tag(")").chain_err( - || "closing sort parameter list" - ) ? ; - - self.ws_cmt() ; - self.tag("(").chain_err( - || "opening the list of constructor" - ) ? ; - self.ws_cmt() ; - - true - } else { - false - } ; - - - while let Some( - (constructor_pos, constructor_ident, selectors) - ) = self.dtyp_new(& params_map) ? { - self.ws_cmt() ; - - dtyp.add_constructor(constructor_ident, selectors).chain_err( - || self.error( - constructor_pos, "in this constructor" - ) - ) ? - } - - - if closing_paren { - self.ws_cmt() ; - self.tag(")").chain_err( - || "closing the list of constructor" - ) ? ; - } - self.ws_cmt() ; - self.tag(")").chain_err( - || "closing datatype declaration" - ) - } - - - - /// Single datatype declaration. - fn dtyp_dec_item(& mut self) -> Res { - if ! self.word_opt(keywords::cmd::dec_dtyp) { - return Ok(false) - } - - let (dtyp_pos, dtyp_ident) = self.ident().chain_err( - || "while parsing datatype declaration" - ) ? ; - - let mut dtyp = dtyp::RDTyp::new(dtyp_ident) ; - - self.dtyp_dec(& mut dtyp, None).chain_err( - || self.error( - dtyp_pos, format!( - "while parsing the declaration for datatype `{}`", - conf.emph(& dtyp.name) - ) - ) - ) ? ; - - Ok(true) - } - - - /// Multiple datatype declaration. - fn dtyp_decs_item(& mut self) -> Res { - if ! self.word_opt(keywords::cmd::dec_dtyps) { - return Ok(false) + self.ws_cmt(); + self.tag(")").chain_err(|| "closing datatype declaration") } - self.ws_cmt() ; - // List of datatypes. - self.tag("(").chain_err( - || "opening the list of symbol/arity pairs" - ) ? ; - self.ws_cmt() ; + /// Single datatype declaration. + fn dtyp_dec_item(&mut self) -> Res { + if !self.word_opt(keywords::cmd::dec_dtyp) { + return Ok(false); + } - let mut dtyps = vec![] ; + let (dtyp_pos, dtyp_ident) = self + .ident() + .chain_err(|| "while parsing datatype declaration")?; - while self.tag_opt("(") { - let (dtyp_pos, dtyp_ident) = self.ident().chain_err( - || "declaring a new datatype" - ) ? ; - self.ws_cmt() ; + let mut dtyp = dtyp::RDTyp::new(dtyp_ident); - let arity = if let Some(arity) = self.numeral() { - arity - } else { - bail!( - self.error_here( - format!( - "expected arity for datatype `{}`", conf.emph(dtyp_ident) + self.dtyp_dec(&mut dtyp, None).chain_err(|| { + self.error( + dtyp_pos, + format!( + "while parsing the declaration for datatype `{}`", + conf.emph(&dtyp.name) + ), ) - ) - ) - } ; - - dtyps.push( (dtyp_pos, dtyp::RDTyp::new(dtyp_ident), arity) ) ; + })?; - self.tag(")").chain_err( - || format!( - "closing symbol/arity pair for `{}`", conf.emph(dtyp_ident) - ) - ) ? ; - self.ws_cmt() + Ok(true) } - self.tag(")").chain_err( - || "closing the list of symbol/arity pairs" - ) ? ; - self.ws_cmt() ; - - + /// Multiple datatype declaration. + fn dtyp_decs_item(&mut self) -> Res { + if !self.word_opt(keywords::cmd::dec_dtyps) { + return Ok(false); + } + self.ws_cmt(); + // List of datatypes. + self.tag("(") + .chain_err(|| "opening the list of symbol/arity pairs")?; + self.ws_cmt(); + let mut dtyps = vec![]; - self.tag("(").chain_err( - || "opening the list of datatype declaration" - ) ? ; - self.ws_cmt() ; + while self.tag_opt("(") { + let (dtyp_pos, dtyp_ident) = self.ident().chain_err(|| "declaring a new datatype")?; + self.ws_cmt(); - let mut final_dtyps = Vec::with_capacity( dtyps.len() ) ; + let arity = if let Some(arity) = self.numeral() { + arity + } else { + bail!(self.error_here(format!( + "expected arity for datatype `{}`", + conf.emph(dtyp_ident) + ))) + }; + + dtyps.push((dtyp_pos, dtyp::RDTyp::new(dtyp_ident), arity)); + + self.tag(")").chain_err(|| { + format!("closing symbol/arity pair for `{}`", conf.emph(dtyp_ident)) + })?; + self.ws_cmt() + } - for (dtyp_pos, mut dtyp, dtyp_arity) in dtyps { - self.dtyp_dec( - & mut dtyp, Some(dtyp_arity) - ).chain_err( - || format!( - "while parsing the declaration for datatype `{}`", - conf.emph(& dtyp.name) - ) - ).chain_err( - || self.error(dtyp_pos, "declared here") - ) ? ; - self.ws_cmt() ; + self.tag(")") + .chain_err(|| "closing the list of symbol/arity pairs")?; + self.ws_cmt(); - let is_okay = dtyp::check_reserved(& dtyp.name) ; + self.tag("(") + .chain_err(|| "opening the list of datatype declaration")?; + self.ws_cmt(); - let dtyp = is_okay.and_then( - |_| dtyp::mk(dtyp) - ).chain_err( - || self.error( - dtyp_pos, "while parsing the declaration for this datatype" - ) - ) ? ; - final_dtyps.push( (dtyp_pos, dtyp) ) - } + let mut final_dtyps = Vec::with_capacity(dtyps.len()); - self.tag(")").chain_err( - || "closing the list of datatype declaration" - ) ? ; + for (dtyp_pos, mut dtyp, dtyp_arity) in dtyps { + self.dtyp_dec(&mut dtyp, Some(dtyp_arity)) + .chain_err(|| { + format!( + "while parsing the declaration for datatype `{}`", + conf.emph(&dtyp.name) + ) + }).chain_err(|| self.error(dtyp_pos, "declared here"))?; + self.ws_cmt(); - for (dtyp_pos, dtyp) in final_dtyps { - if let Err((pos, err)) = dtyp.check() { - let err: Error = self.error(pos, err).into() ; - bail!( - err.chain_err( - || self.error(dtyp_pos, "in this datatype declaration") - ) - ) - } - } + let is_okay = dtyp::check_reserved(&dtyp.name); - Ok(true) - } + let dtyp = is_okay.and_then(|_| dtyp::mk(dtyp)).chain_err(|| { + self.error(dtyp_pos, "while parsing the declaration for this datatype") + })?; + final_dtyps.push((dtyp_pos, dtyp)) + } + self.tag(")") + .chain_err(|| "closing the list of datatype declaration")?; + for (dtyp_pos, dtyp) in final_dtyps { + if let Err((pos, err)) = dtyp.check() { + let err: Error = self.error(pos, err).into(); + bail!(err.chain_err(|| self.error(dtyp_pos, "in this datatype declaration"))) + } + } - /// Predicate declaration. - fn pred_dec(& mut self, instance: & mut Instance) -> Res { - if ! self.word_opt(keywords::cmd::dec_fun) { - return Ok(false) + Ok(true) } - self.ws_cmt() ; - let (pos, ident) = self.ident() ? ; - self.ws_cmt() ; - self.tag("(") ? ; - - let mut sorts = Vec::with_capacity(11) ; - self.ws_cmt() ; - while let Some(ty) = self.sort_opt() ? { - self.ws_cmt() ; - sorts.push(ty) ; - } - sorts.shrink_to_fit() ; + /// Predicate declaration. + fn pred_dec(&mut self, instance: &mut Instance) -> Res { + if !self.word_opt(keywords::cmd::dec_fun) { + return Ok(false); + } - self.ws_cmt() ; - self.tag(")") ? ; - self.ws_cmt() ; - if ! self.word_opt("Bool") { - bail!( - self.error_here("expected Bool sort") - ) - } + self.ws_cmt(); + let (pos, ident) = self.ident()?; + self.ws_cmt(); + self.tag("(")?; - let pred_index = instance.push_pred( - ident.into(), VarMap::of(sorts) - ) ; - let prev = self.cxt.pred_name_map.insert(ident.into(), pred_index) ; - if let Some(prev) = prev { - bail!( - self.error( - pos, - format!( - "predicate `{}` is already declared", - conf.bad( & format!("{}", instance[prev]) ) - ) - ) - ) - } + let mut sorts = Vec::with_capacity(11); + self.ws_cmt(); + while let Some(ty) = self.sort_opt()? { + self.ws_cmt(); + sorts.push(ty); + } + sorts.shrink_to_fit(); - Ok(true) - } - - /// Parses some arguments `( ( ) ... )`. - fn args( - & mut self, - var_map: & mut VarInfos, hash_map: & mut BTreeMap<& 's str, VarIdx> - ) -> Res<()> { - self.tag("(") ? ; - - self.ws_cmt() ; - while self.tag_opt("(") { - self.ws_cmt() ; - let (pos, ident) = self.ident() ? ; - self.ws_cmt() ; - let sort = self.sort() ? ; - self.ws_cmt() ; - self.tag(")") ? ; - self.ws_cmt() ; - let idx = var_map.next_index() ; - let prev = hash_map.insert(ident, idx) ; - if prev.is_some() { - bail!( - self.error( - pos, format!( - "found two quantifier variables named `{}`", conf.bad(ident) - ) - ) - ) - } - var_map.push( VarInfo::new(ident.into(), sort, idx) ) - } - self.tag(")") ? ; - var_map.shrink_to_fit() ; - Ok(()) - } + self.ws_cmt(); + self.tag(")")?; + self.ws_cmt(); + if !self.word_opt("Bool") { + bail!(self.error_here("expected Bool sort")) + } + let pred_index = instance.push_pred(ident.into(), VarMap::of(sorts)); + let prev = self.cxt.pred_name_map.insert(ident.into(), pred_index); + if let Some(prev) = prev { + bail!(self.error( + pos, + format!( + "predicate `{}` is already declared", + conf.bad(&format!("{}", instance[prev])) + ) + )) + } + Ok(true) + } + + /// Parses some arguments `( ( ) ... )`. + fn args( + &mut self, + var_map: &mut VarInfos, + hash_map: &mut BTreeMap<&'s str, VarIdx>, + ) -> Res<()> { + self.tag("(")?; + + self.ws_cmt(); + while self.tag_opt("(") { + self.ws_cmt(); + let (pos, ident) = self.ident()?; + self.ws_cmt(); + let sort = self.sort()?; + self.ws_cmt(); + self.tag(")")?; + self.ws_cmt(); + let idx = var_map.next_index(); + let prev = hash_map.insert(ident, idx); + if prev.is_some() { + bail!(self.error( + pos, + format!("found two quantifier variables named `{}`", conf.bad(ident)) + )) + } + var_map.push(VarInfo::new(ident.into(), sort, idx)) + } + self.tag(")")?; + var_map.shrink_to_fit(); + Ok(()) + } } - - - - - - - - /// Let-binding-related functions. impl<'cxt, 's> Parser<'cxt, 's> { - - /// Adds a binding to the current bindings. - fn insert_bind( - & mut self, var: & 's str, term: PTTerms - ) -> Res<()> { - if let Some(bindings) = self.bindings.last_mut() { - bindings.insert(var, term) ; - Ok(()) - } else { - bail!("bug, adding binding before pushing a binding scope") - } - } - /// Pushes a binding scopes. - fn push_bind(& mut self) { - self.bindings.push( BTreeMap::new() ) - } - /// Pops a binding scope. - fn pop_bind(& mut self) -> Res<()> { - if self.bindings.pop().is_none() { - bail!("bug, popping binding scope but there's no scope") - } - Ok(()) - } - /// Finds what a variable is mapped to. - fn get_bind(& self, var: & str) -> Option<& PTTerms> { - for bindings in self.bindings.iter().rev() { - if let Some(tterms) = bindings.get(var) { - return Some(tterms) - } - } - None - } - - - /// Parses the end of some consecutive let-bindings. - #[inline] - fn close_let_bindings(& mut self, count: LetCount) -> Res<()> { - for _ in 0..count.n { - self.ws_cmt() ; - self.tag(")") ? ; - self.pop_bind() ? - } - Ok(()) - } - - - - - /// Parses some consecutive let-bindings. - /// - /// - open paren, - /// - `let` keyword, and - /// - bindings. - /// - /// Returns the number of let-bindings it parsed, *i.e.* the number of - /// corresponding closing parens. - #[inline] - fn let_bindings( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance - ) -> Res { - let mut n = 0 ; - - profile! { self tick "parsing", "let bindings" } - - 'parse_lets: loop { - conf.check_timeout() ? ; - - if let Some(pos) = self.tag_opt_pos("(") { - - self.ws_cmt() ; - if self.tag_opt( keywords::let_ ) { - n += 1 ; - self.push_bind() ; - self.ws_cmt() ; - self.tag("(") ? ; - self.ws_cmt() ; - while self.tag_opt("(") { - self.ws_cmt() ; - let (_, id) = self.ident() ? ; - self.ws_cmt() ; - - // Save term stack. - let old_stack = ::std::mem::replace( - & mut self.cxt.term_stack, vec![] - ) ; - - let tterms = self.parse_ptterms( - var_map, map, instance - ) ? ; - - // Load term stack. - debug_assert! { self.cxt.term_stack.is_empty() } - self.cxt.term_stack = old_stack ; - - self.insert_bind(id, tterms) ? ; - self.ws_cmt() ; - self.tag(")") ? ; - self.ws_cmt() ; - } - self.ws_cmt() ; - self.tag_err( - ")", format!( - "expected binding or `{}` closing the list of bindings", - conf.emph(")") - ) - ) ? ; + /// Adds a binding to the current bindings. + fn insert_bind(&mut self, var: &'s str, term: PTTerms) -> Res<()> { + if let Some(bindings) = self.bindings.last_mut() { + bindings.insert(var, term); + Ok(()) } else { - self.backtrack_to(pos) ; - break 'parse_lets + bail!("bug, adding binding before pushing a binding scope") } - } else { - break 'parse_lets - } - self.ws_cmt() } - - profile! { self mark "parsing", "let bindings" } - profile! { self "let bindings" => add n } - - Ok( LetCount { n } ) - } - - /// Bool parser. - pub fn bool(& mut self) -> Option { - let start_pos = self.pos() ; - if self.tag_opt("true") { - if ! self.legal_id_char() { - Some(true) - } else { - self.backtrack_to(start_pos) ; - None - } - } else if self.tag_opt("false") { - if ! self.legal_id_char() { - Some(false) - } else { - self.backtrack_to(start_pos) ; + /// Pushes a binding scopes. + fn push_bind(&mut self) { + self.bindings.push(BTreeMap::new()) + } + /// Pops a binding scope. + fn pop_bind(&mut self) -> Res<()> { + if self.bindings.pop().is_none() { + bail!("bug, popping binding scope but there's no scope") + } + Ok(()) + } + /// Finds what a variable is mapped to. + fn get_bind(&self, var: &str) -> Option<&PTTerms> { + for bindings in self.bindings.iter().rev() { + if let Some(tterms) = bindings.get(var) { + return Some(tterms); + } + } None - } - } else { - None } - } - -} + /// Parses the end of some consecutive let-bindings. + #[inline] + fn close_let_bindings(&mut self, count: LetCount) -> Res<()> { + for _ in 0..count.n { + self.ws_cmt(); + self.tag(")")?; + self.pop_bind()? + } + Ok(()) + } + + /// Parses some consecutive let-bindings. + /// + /// - open paren, + /// - `let` keyword, and + /// - bindings. + /// + /// Returns the number of let-bindings it parsed, *i.e.* the number of + /// corresponding closing parens. + #[inline] + fn let_bindings( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res { + let mut n = 0; + + profile! { self tick "parsing", "let bindings" } + + 'parse_lets: loop { + conf.check_timeout()?; + + if let Some(pos) = self.tag_opt_pos("(") { + self.ws_cmt(); + if self.tag_opt(keywords::let_) { + n += 1; + self.push_bind(); + self.ws_cmt(); + self.tag("(")?; + self.ws_cmt(); + while self.tag_opt("(") { + self.ws_cmt(); + let (_, id) = self.ident()?; + self.ws_cmt(); + + // Save term stack. + let old_stack = ::std::mem::replace(&mut self.cxt.term_stack, vec![]); + + let tterms = self.parse_ptterms(var_map, map, instance)?; + + // Load term stack. + debug_assert! { self.cxt.term_stack.is_empty() } + self.cxt.term_stack = old_stack; + + self.insert_bind(id, tterms)?; + self.ws_cmt(); + self.tag(")")?; + self.ws_cmt(); + } + self.ws_cmt(); + self.tag_err( + ")", + format!( + "expected binding or `{}` closing the list of bindings", + conf.emph(")") + ), + )?; + } else { + self.backtrack_to(pos); + break 'parse_lets; + } + } else { + break 'parse_lets; + } + self.ws_cmt() + } + profile! { self mark "parsing", "let bindings" } + profile! { self "let bindings" => add n } + Ok(LetCount { n }) + } + /// Bool parser. + pub fn bool(&mut self) -> Option { + let start_pos = self.pos(); + if self.tag_opt("true") { + if !self.legal_id_char() { + Some(true) + } else { + self.backtrack_to(start_pos); + None + } + } else if self.tag_opt("false") { + if !self.legal_id_char() { + Some(false) + } else { + self.backtrack_to(start_pos); + None + } + } else { + None + } + } +} /// Arithmetic values. impl<'cxt, 's> Parser<'cxt, 's> { - - /// Numeral parser. - pub fn numeral(& mut self) -> Option { - let start_pos = self.pos() ; - - if let Some(char) = self.next() { - if char.is_numeric() { - // If there's more numbers after this one, then the first one cannot be - // zero. - let mut cannot_be_zero = false ; - while let Some(char) = self.next() { - if ! char.is_numeric() { - self.move_back(1) ; - break - } - cannot_be_zero = true ; - } - if cannot_be_zero && char == "0" { - self.backtrack_to(start_pos) ; - None + /// Numeral parser. + pub fn numeral(&mut self) -> Option { + let start_pos = self.pos(); + + if let Some(char) = self.next() { + if char.is_numeric() { + // If there's more numbers after this one, then the first one cannot be + // zero. + let mut cannot_be_zero = false; + while let Some(char) = self.next() { + if !char.is_numeric() { + self.move_back(1); + break; + } + cannot_be_zero = true; + } + if cannot_be_zero && char == "0" { + self.backtrack_to(start_pos); + None + } else { + Some( + Int::parse_bytes(self.string[*start_pos..self.cursor].as_bytes(), 10) + .expect("[bug] in integer parsing"), + ) + } + } else { + self.backtrack_to(start_pos); + None + } } else { - Some( - Int::parse_bytes( - self.string[ - * start_pos .. self.cursor - ].as_bytes(), 10 - ).expect("[bug] in integer parsing") - ) - } - } else { - self.backtrack_to(start_pos) ; - None - } - } else { - None + None + } } - } - /// Decimal parser. - pub fn decimal(& mut self) -> Option { - let start_pos = self.pos() ; - macro_rules! if_not_give_up { + /// Decimal parser. + pub fn decimal(&mut self) -> Option { + let start_pos = self.pos(); + macro_rules! if_not_give_up { (( $($cond:tt)* ) => $thing:expr) => ( if $($cond)* { $thing @@ -1960,1833 +1723,1642 @@ impl<'cxt, 's> Parser<'cxt, 's> { } ) } - let num = if_not_give_up! { - (let Some(num) = self.numeral()) => num - } ; - if_not_give_up! { - ( self.tag_opt(".") ) => () - } - let mut den: Int = 1.into() ; - let ten = || consts::ten.clone() ; - while self.tag_opt("0") { den *= ten() } - let dec_start_pos = self.pos() ; - if let Some(dec) = self.numeral() { - for _ in * dec_start_pos .. * self.pos() { - den *= ten() - } - Some( Rat::new( num * den.clone() + dec, den ) ) - } else if den != 1.into() { - Some( Rat::new(num, 1.into()) ) - } else { - self.backtrack_to(start_pos) ; - None - } - } - - /// Integer parser (numeral not followed by a `.`). - pub fn int(& mut self) -> Option { - let start_pos = self.pos() ; - let num = self.numeral() ; - if num.is_some() && self.peek() == Some(".") { - self.backtrack_to(start_pos) ; - return None - } - num - } - - /// Real parser. - /// - /// Decimal or fraction. - pub fn real(& mut self) -> Res< Option > { - let start_pos = self.pos() ; - - if let Some(res) = self.decimal() { - return Ok( Some(res) ) - } - - if self.tag_opt("(") { - self.ws_cmt() ; - if self.tag_opt("/") { - self.ws_cmt() ; - if let Some(num) = self.numeral() { - self.tag_opt(".0") ; - self.ws_cmt() ; - let den_pos = self.pos() ; - if let Some(den) = self.numeral() { - self.tag_opt(".0") ; - self.ws_cmt() ; - if self.tag_opt(")") { - if den.is_zero() { - bail!( - self.error( - den_pos, "division by zero is not supported" - ) - ) - } - return Ok( - Some( Rat::new(num, den) ) - ) - } else { - bail!( - self.error( - start_pos, "division applied to more than two operands" - ) - ) + let num = if_not_give_up! { + (let Some(num) = self.numeral()) => num + }; + if_not_give_up! { + ( self.tag_opt(".") ) => () + } + let mut den: Int = 1.into(); + let ten = || consts::ten.clone(); + while self.tag_opt("0") { + den *= ten() + } + let dec_start_pos = self.pos(); + if let Some(dec) = self.numeral() { + for _ in *dec_start_pos..*self.pos() { + den *= ten() } - } + Some(Rat::new(num * den.clone() + dec, den)) + } else if den != 1.into() { + Some(Rat::new(num, 1.into())) + } else { + self.backtrack_to(start_pos); + None } - } } - self.backtrack_to(start_pos) ; - Ok(None) - } - -} + /// Integer parser (numeral not followed by a `.`). + pub fn int(&mut self) -> Option { + let start_pos = self.pos(); + let num = self.numeral(); + if num.is_some() && self.peek() == Some(".") { + self.backtrack_to(start_pos); + return None; + } + num + } + /// Real parser. + /// + /// Decimal or fraction. + pub fn real(&mut self) -> Res> { + let start_pos = self.pos(); + if let Some(res) = self.decimal() { + return Ok(Some(res)); + } + if self.tag_opt("(") { + self.ws_cmt(); + if self.tag_opt("/") { + self.ws_cmt(); + if let Some(num) = self.numeral() { + self.tag_opt(".0"); + self.ws_cmt(); + let den_pos = self.pos(); + if let Some(den) = self.numeral() { + self.tag_opt(".0"); + self.ws_cmt(); + if self.tag_opt(")") { + if den.is_zero() { + bail!(self.error(den_pos, "division by zero is not supported")) + } + return Ok(Some(Rat::new(num, den))); + } else { + bail!( + self.error(start_pos, "division applied to more than two operands") + ) + } + } + } + } + } + self.backtrack_to(start_pos); + Ok(None) + } +} /// Operator construction and type checking. impl<'cxt, 's> Parser<'cxt, 's> { + /// Type checks and builds an application. + fn build_app(&self, frame: TermFrame) -> Res<(Term, Pos)> { + let (op, op_pos, args_pos, args) = frame.destroy(); + debug_assert_eq! { args_pos.len(), args.len() } - /// Type checks and builds an application. - fn build_app(& self, frame: TermFrame) -> Res<(Term, Pos)> { - let (op, op_pos, args_pos, args) = frame.destroy() ; - debug_assert_eq! { args_pos.len(), args.len() } - - match op { - FrameOp::Op(op) => self.build_op_app( - op, op_pos, & args_pos, args - ), - - FrameOp::CArray(typ, typ_pos) => self.build_carray( - & typ, typ_pos, op_pos, & args_pos, args - ), - - FrameOp::DTypNew(name, dtyp) => self.build_dtyp_new( - name, & dtyp, op_pos, & args_pos, args - ), - - FrameOp::DTypSlc(name) => self.build_dtyp_slc( - name, op_pos, & args_pos, args - ), - - FrameOp::Fun(name) => self.build_fun_app( - name, op_pos, & args_pos, args - ), - - FrameOp::DTypTst(name) => self.build_dtyp_tst( - name, op_pos, & args_pos, args - ), - - op => bail!( - "unsupported operator {}", conf.bad( op.as_str() ) - ), - } - } - - /// Type checks and builds an operator application. - fn build_op_app( - & self, op: Op, op_pos: Pos, args_pos: & [ Pos ], args: Vec - ) -> Res<(Term, Pos)> { - debug_assert_eq! { args_pos.len(), args.len() } - - match term::try_app(op, args) { - Ok(term) => Ok((term, op_pos)), - Err( - TypError::Typ { expected, obtained, index } - ) => if let Some(exp) = expected { - err_chain! { - self.error( - args_pos[index], format!( - "expected an expression of sort {}, found {}", exp, obtained - ) - ) - => self.error(op_pos, "in this operator application") - } - } else { - err_chain! { - self.error( - args_pos[index], format!( - "expected the expression starting here has sort {} \ - which is illegal", obtained - ) - ) - => self.error(op_pos, "in this operator application") - } - }, - Err( TypError::Msg(blah) ) => bail!( - self.error(op_pos, blah) - ), - } - } - - /// Type checks and builds a constant array constructor. - fn build_carray( - & self, - // Type of the array. - typ: & Typ, typ_pos: Pos, - // Position of the array constructor. - new_pos: Pos, - // Arguments. There should be only one. - args_pos: & [ Pos ], mut args: Vec, - ) -> Res<(Term, Pos)> { - debug_assert_eq! { args_pos.len(), args.len() } - - // Retrieve input and output types. - let (src, tgt) = if let Some((src, tgt)) = typ.array_inspect() { - (src, tgt) - } else { - bail!( self.error(typ_pos, "expected array sort") ) - } ; - - // Retrieve the only argument. - let (arg, arg_pos) = if let Some(arg) = args.pop() { - if args.pop().is_some() { - bail!( - self.error( - new_pos, format!( - "array constructor applied to {} (> 1) elements", - args.len() + 2 - ) - ) - ) - } else { - (arg, args_pos[0]) - } - } else { - bail!( self.error(new_pos, "array constructor applied to nothing") ) - } ; - - // let tgt = if let Some(tgt) = tgt.merge( & arg.typ() ) { - // tgt - // } else { - // bail!( - // self.error( - // arg_pos, format!( - // "this term has type {}, expected {}", arg.typ(), tgt - // ) - // ) - // ) - // } ; - - let default = match arg.cast(tgt) { - Ok(None) => arg, - Ok( Some(term) ) => term, - Err(_) => bail!( - self.error( - arg_pos, format!( - "this term of type {} is not compatible with {}", - arg.typ(), tgt - ) - ) - ), - } ; - - let term = term::cst_array( src.clone(), default ) ; - - Ok( (term, new_pos) ) - } - - /// Type checks and builds a datatype constructor. - fn build_dtyp_new( - & self, - name: String, dtyp: & DTyp, - new_pos: Pos, _args_pos: & [ Pos ], args: Vec - ) -> Res<(Term, Pos)> { - // let mut buff: Vec = vec![] ; - // dtyp::write_all(& mut buff, "| ").unwrap() ; - // write!(& mut buff, "\n\n").unwrap() ; - // dtyp::write_constructor_map(& mut buff, "| ").unwrap() ; - // println!("{}", ::std::str::from_utf8(& buff).unwrap()) ; - - let typ = if let Some(typ) = dtyp::type_constructor( - & name, & args - ).chain_err( - || self.error( - new_pos, format!( - "while typing this constructor for `{}`", - conf.emph(& dtyp.name) - ) - ) - ) ? { - typ - } else { - bail!( - self.error( - new_pos, format!( - "unknown `{}` constructor for datatype `{}`", - conf.bad(& name), conf.emph(& dtyp.name) - ) - ) - ) - } ; - - Ok( - ( term::dtyp_new(typ, name, args), new_pos ) - ) - } - - /// Type checks and builds a datatype selector. - fn build_dtyp_slc( - & self, - name: String, slc_pos: Pos, _args_pos: & [ Pos ], mut args: Vec - ) -> Res<(Term, Pos)> { - debug_assert_eq! { _args_pos.len(), args.len() } - - let arg = if let Some(arg) = args.pop() { - - if args.pop().is_some() { - bail!( - self.error( - slc_pos, format!( - "illegal application of datatype selector {} \ - to {} (> 1) arguments", - conf.bad(name), args.len() + 2 - ) - ) - ) - } else { - arg - } - - } else { - bail!( - self.error( - slc_pos, format!( - "illegal application of datatype selector {} to nothing", - conf.bad(name) - ) - ) - ) - } ; + match op { + FrameOp::Op(op) => self.build_op_app(op, op_pos, &args_pos, args), - match dtyp::type_selector( & name, slc_pos, & arg ) { - Ok(typ) => Ok( - ( term::dtyp_slc(typ, name, arg), slc_pos ) - ), + FrameOp::CArray(typ, typ_pos) => { + self.build_carray(&typ, typ_pos, op_pos, &args_pos, args) + } - Err((pos, blah)) => bail!( self.error(pos, blah) ), - } - } + FrameOp::DTypNew(name, dtyp) => { + self.build_dtyp_new(name, &dtyp, op_pos, &args_pos, args) + } - /// Type checks and builds a datatype tester. - fn build_dtyp_tst( - & self, - name: String, tst_pos: Pos, _args_pos: & [ Pos ], mut args: Vec - ) -> Res<(Term, Pos)> { - debug_assert_eq! { _args_pos.len(), args.len() } + FrameOp::DTypSlc(name) => self.build_dtyp_slc(name, op_pos, &args_pos, args), - let arg = if let Some(arg) = args.pop() { + FrameOp::Fun(name) => self.build_fun_app(name, op_pos, &args_pos, args), - if args.pop().is_some() { - bail!( - self.error( - tst_pos, format!( - "illegal application of datatype tester {} \ - to {} (> 1) arguments", - conf.bad(name), args.len() + 2 - ) - ) - ) - } else { - arg - } - - } else { - bail!( - self.error( - tst_pos, format!( - "illegal application of datatype tester {} to nothing", - conf.bad(name) - ) - ) - ) - } ; - - match dtyp::type_tester( & name, tst_pos, & arg ) { - Ok(()) => Ok( - ( term::dtyp_tst(name, arg), tst_pos ) - ), - - Err((pos, blah)) => bail!( self.error(pos, blah) ), - } - } - - /// Type checks and builds a datatype selector. - fn build_fun_app( - & self, - name: String, name_pos: Pos, args_pos: & [ Pos ], args: Vec - ) -> Res<(Term, Pos)> { - use errors::TypError ; - - let res = if let Some((var_infos, typ)) = self.functions.get( - & name as & str - ) { - // Function application for one of the functions we are currently parsing - // the definition of? (i.e. the function is not registered yet) - fun::type_apply(name, var_infos, typ, args) - } else { - // Function should already exist. - fun::apply(name, args) - } ; - - // Parsing a application of a function that's already defined. - - match res { - Ok(term) => Ok((term, name_pos)), - - Err( - TypError::Typ { expected, obtained, index } - ) => if let Some(exp) = expected { - err_chain! { - self.error( - args_pos[index], format!( - "expected an expression of sort {}, found {}", exp, obtained - ) - ) - => self.error(name_pos, "in this function application") - } - } else { - err_chain! { - self.error( - args_pos[index], format!( - "expected the expression starting here has sort {} \ - which is illegal", obtained - ) - ) - => self.error(name_pos, "in this function application") - } - }, + FrameOp::DTypTst(name) => self.build_dtyp_tst(name, op_pos, &args_pos, args), - Err( TypError::Msg(blah) ) => { - let e: Error = blah.into() ; - bail!( - e.chain_err( - || self.error(name_pos, "in this function application") - ) - ) - }, + op => bail!("unsupported operator {}", conf.bad(op.as_str())), + } } - } -} + /// Type checks and builds an operator application. + fn build_op_app( + &self, + op: Op, + op_pos: Pos, + args_pos: &[Pos], + args: Vec, + ) -> Res<(Term, Pos)> { + debug_assert_eq! { args_pos.len(), args.len() } + + match term::try_app(op, args) { + Ok(term) => Ok((term, op_pos)), + Err(TypError::Typ { + expected, + obtained, + index, + }) => if let Some(exp) = expected { + err_chain! { + self.error( + args_pos[index], format!( + "expected an expression of sort {}, found {}", exp, obtained + ) + ) + => self.error(op_pos, "in this operator application") + } + } else { + err_chain! { + self.error( + args_pos[index], format!( + "expected the expression starting here has sort {} \ + which is illegal", obtained + ) + ) + => self.error(op_pos, "in this operator application") + } + }, + Err(TypError::Msg(blah)) => bail!(self.error(op_pos, blah)), + } + } + /// Type checks and builds a constant array constructor. + fn build_carray( + &self, + // Type of the array. + typ: &Typ, + typ_pos: Pos, + // Position of the array constructor. + new_pos: Pos, + // Arguments. There should be only one. + args_pos: &[Pos], + mut args: Vec, + ) -> Res<(Term, Pos)> { + debug_assert_eq! { args_pos.len(), args.len() } + + // Retrieve input and output types. + let (src, tgt) = if let Some((src, tgt)) = typ.array_inspect() { + (src, tgt) + } else { + bail!(self.error(typ_pos, "expected array sort")) + }; + + // Retrieve the only argument. + let (arg, arg_pos) = if let Some(arg) = args.pop() { + if args.pop().is_some() { + bail!(self.error( + new_pos, + format!( + "array constructor applied to {} (> 1) elements", + args.len() + 2 + ) + )) + } else { + (arg, args_pos[0]) + } + } else { + bail!(self.error(new_pos, "array constructor applied to nothing")) + }; + + // let tgt = if let Some(tgt) = tgt.merge( & arg.typ() ) { + // tgt + // } else { + // bail!( + // self.error( + // arg_pos, format!( + // "this term has type {}, expected {}", arg.typ(), tgt + // ) + // ) + // ) + // } ; + + let default = match arg.cast(tgt) { + Ok(None) => arg, + Ok(Some(term)) => term, + Err(_) => bail!(self.error( + arg_pos, + format!( + "this term of type {} is not compatible with {}", + arg.typ(), + tgt + ) + )), + }; + + let term = term::cst_array(src.clone(), default); + + Ok((term, new_pos)) + } + + /// Type checks and builds a datatype constructor. + fn build_dtyp_new( + &self, + name: String, + dtyp: &DTyp, + new_pos: Pos, + _args_pos: &[Pos], + args: Vec, + ) -> Res<(Term, Pos)> { + // let mut buff: Vec = vec![] ; + // dtyp::write_all(& mut buff, "| ").unwrap() ; + // write!(& mut buff, "\n\n").unwrap() ; + // dtyp::write_constructor_map(& mut buff, "| ").unwrap() ; + // println!("{}", ::std::str::from_utf8(& buff).unwrap()) ; + + let typ = if let Some(typ) = dtyp::type_constructor(&name, &args).chain_err(|| { + self.error( + new_pos, + format!( + "while typing this constructor for `{}`", + conf.emph(&dtyp.name) + ), + ) + })? { + typ + } else { + bail!(self.error( + new_pos, + format!( + "unknown `{}` constructor for datatype `{}`", + conf.bad(&name), + conf.emph(&dtyp.name) + ) + )) + }; + + Ok((term::dtyp_new(typ, name, args), new_pos)) + } + + /// Type checks and builds a datatype selector. + fn build_dtyp_slc( + &self, + name: String, + slc_pos: Pos, + _args_pos: &[Pos], + mut args: Vec, + ) -> Res<(Term, Pos)> { + debug_assert_eq! { _args_pos.len(), args.len() } + + let arg = if let Some(arg) = args.pop() { + if args.pop().is_some() { + bail!(self.error( + slc_pos, + format!( + "illegal application of datatype selector {} \ + to {} (> 1) arguments", + conf.bad(name), + args.len() + 2 + ) + )) + } else { + arg + } + } else { + bail!(self.error( + slc_pos, + format!( + "illegal application of datatype selector {} to nothing", + conf.bad(name) + ) + )) + }; + match dtyp::type_selector(&name, slc_pos, &arg) { + Ok(typ) => Ok((term::dtyp_slc(typ, name, arg), slc_pos)), + Err((pos, blah)) => bail!(self.error(pos, blah)), + } + } + /// Type checks and builds a datatype tester. + fn build_dtyp_tst( + &self, + name: String, + tst_pos: Pos, + _args_pos: &[Pos], + mut args: Vec, + ) -> Res<(Term, Pos)> { + debug_assert_eq! { _args_pos.len(), args.len() } + + let arg = if let Some(arg) = args.pop() { + if args.pop().is_some() { + bail!(self.error( + tst_pos, + format!( + "illegal application of datatype tester {} \ + to {} (> 1) arguments", + conf.bad(name), + args.len() + 2 + ) + )) + } else { + arg + } + } else { + bail!(self.error( + tst_pos, + format!( + "illegal application of datatype tester {} to nothing", + conf.bad(name) + ) + )) + }; + match dtyp::type_tester(&name, tst_pos, &arg) { + Ok(()) => Ok((term::dtyp_tst(name, arg), tst_pos)), + Err((pos, blah)) => bail!(self.error(pos, blah)), + } + } + /// Type checks and builds a datatype selector. + fn build_fun_app( + &self, + name: String, + name_pos: Pos, + args_pos: &[Pos], + args: Vec, + ) -> Res<(Term, Pos)> { + use errors::TypError; + + let res = if let Some((var_infos, typ)) = self.functions.get(&name as &str) { + // Function application for one of the functions we are currently parsing + // the definition of? (i.e. the function is not registered yet) + fun::type_apply(name, var_infos, typ, args) + } else { + // Function should already exist. + fun::apply(name, args) + }; -impl<'cxt, 's> Parser<'cxt, 's> { + // Parsing a application of a function that's already defined. - // /// Parses an operator or fails. - // fn op(& mut self) -> Res { - // if let Some(op) = self.op_opt() ? { - // Ok(op) - // } else { - // bail!( self.error_here("expected operator") ) - // } - // } - - /// Tries to parse an operator. - fn op_opt(& mut self) -> Res< Option > { - let start_pos = self.pos() ; - let res = match self.next() { - Some("a") => if self.word_opt("nd") { - Some(Op::And) - } else { - None - }, - Some("o") => if self.word_opt("r") { - Some(Op::Or) - } else { - None - }, - Some("n") => if self.word_opt("ot") { - Some(Op::Not) - } else { - None - }, - Some("i") => if self.word_opt("te") { - Some(Op::Ite) - } else { - None - }, - Some("m") => if self.word_opt("od") { - Some(Op::Mod) - } else if self.word_opt("atch") { - bail!("unsupported `{}` operator", conf.bad("match")) - } else { - None - }, - Some("r") => if self.word_opt("em") { - Some(Op::Rem) - } else { - None - }, - Some("d") => if self.word_opt("iv") { - Some(Op::IDiv) - } else if self.word_opt("istinct") { - Some(Op::Distinct) - } else { - None - }, - Some("t") => if self.word_opt("o_int") { - Some(Op::ToInt) - } else if self.word_opt("o_real") { - Some(Op::ToReal) - } else { - None - }, - - Some("s") => if self.word_opt("tore") { - Some(Op::Store) - } else if self.word_opt("elect") { - Some(Op::Select) - } else { - None - }, - - Some("=") => if self.tag_opt(">") { - Some(Op::Impl) - } else { - Some(Op::Eql) - }, - Some(">") => if self.tag_opt("=") { - Some(Op::Ge) - } else { - Some(Op::Gt) - }, - Some("<") => if self.tag_opt("=") { - Some(Op::Le) - } else { - Some(Op::Lt) - }, - Some("+") => Some(Op::Add), - Some("-") => Some(Op::Sub), - Some("*") => Some(Op::Mul), - Some("/") => Some(Op::Div), - Some(_) => None, - None => None, - } ; - - if res.is_none() { - self.backtrack_to(start_pos) - } - - Ok( res ) - } - - /// Parses a single term. - pub fn term_opt( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance - ) -> Res< Option > { - let start_pos = self.pos() ; - let res = self.inner_term_opt(var_map, map, instance) ; - - if res.as_ref().map( |res| res.is_none() ).unwrap_or(true) { - self.cxt.term_stack.clear() ; - self.backtrack_to(start_pos) ; - } else { - debug_assert! { self.cxt.term_stack.is_empty() } - } - - res - } - - /// Parses a single term. - fn inner_term_opt( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance - ) -> Res< Option > { - if ! self.cxt.term_stack.is_empty() { - let e: Error = self.error_here("non-empty term stack").into() ; - let mut blah: String = "while parsing this:\n".into() ; - for line in self.string.lines() { - blah.push_str("| ") ; - blah.push_str(line) ; - blah.push('\n') - } - let mut blah_2: String = "term stack:\n".into() ; - for frame in & self.cxt.term_stack { - blah_2 += & format!(" {:?}", frame.op) - } - print_err( - & e.chain_err(|| blah).chain_err(|| blah_2) - ) ; - panic!("non-empty term stack during parsing") - } - conf.check_timeout() ? ; - - // The correct (non-error) way to exit this loop is - // - // `break 'read_kids ` - // - // If ` == None`, the code below will automatically backtrack to - // `start_pos` and clear the `term_stack`, so there's no need to do it in - // the loop. - let res = 'read_kids: loop { - self.ws_cmt() ; - - let bind_count = self.let_bindings(var_map, map, instance) ? ; - - self.ws_cmt() ; - let mut term_pos = self.pos() ; - - - let mut term = if let Some(int) = self.int() { - term::int(int) - - } else if let Some(real) = self.real() ? { - term::real(real) - - } else if let Some(b) = self.bool() { - term::bool(b) - - } else if let Some((pos, id)) = self.ident_opt() ? { - - if let Some(idx) = map.get(id) { - term::var(* idx, var_map[* idx].typ.clone()) - - } else if let Some(ptterms) = self.get_bind(id) { - if let Some(term) = ptterms.to_term().chain_err( - || format!("while retrieving binding for {}", conf.emph(id)) - ) ? { - term - } else { - // Not in a legal term. - break 'read_kids None - } - - } else if self.cxt.pred_name_map.get(id).is_some() { - // Identifier is a predicate, we're not in a legal term. - break 'read_kids None - - } else if let Some(datatype) = dtyp::of_constructor(id) { - - if let Some(constructor) = datatype.news.get(id) { - - if constructor.is_empty() { - let (term, _) = self.build_dtyp_new( - id.into(), & datatype, pos, & [], vec![] - ) ? ; - term + match res { + Ok(term) => Ok((term, name_pos)), + Err(TypError::Typ { + expected, + obtained, + index, + }) => if let Some(exp) = expected { + err_chain! { + self.error( + args_pos[index], format!( + "expected an expression of sort {}, found {}", exp, obtained + ) + ) + => self.error(name_pos, "in this function application") + } } else { - bail!( - self.error( - pos, format!( - "constructor `{}` of datatype `{}` takes {} value(s), \ - applied here to none", - conf.bad(id), conf.emph(& datatype.name), constructor.len() + err_chain! { + self.error( + args_pos[index], format!( + "expected the expression starting here has sort {} \ + which is illegal", obtained + ) ) - ) - ) + => self.error(name_pos, "in this function application") + } + }, + + Err(TypError::Msg(blah)) => { + let e: Error = blah.into(); + bail!(e.chain_err(|| self.error(name_pos, "in this function application"))) } + } + } +} - } else { - bail!("inconsistent datatype map internal state") - } +impl<'cxt, 's> Parser<'cxt, 's> { + // /// Parses an operator or fails. + // fn op(& mut self) -> Res { + // if let Some(op) = self.op_opt() ? { + // Ok(op) + // } else { + // bail!( self.error_here("expected operator") ) + // } + // } + + /// Tries to parse an operator. + fn op_opt(&mut self) -> Res> { + let start_pos = self.pos(); + let res = match self.next() { + Some("a") => if self.word_opt("nd") { + Some(Op::And) + } else { + None + }, + Some("o") => if self.word_opt("r") { + Some(Op::Or) + } else { + None + }, + Some("n") => if self.word_opt("ot") { + Some(Op::Not) + } else { + None + }, + Some("i") => if self.word_opt("te") { + Some(Op::Ite) + } else { + None + }, + Some("m") => if self.word_opt("od") { + Some(Op::Mod) + } else if self.word_opt("atch") { + bail!("unsupported `{}` operator", conf.bad("match")) + } else { + None + }, + Some("r") => if self.word_opt("em") { + Some(Op::Rem) + } else { + None + }, + Some("d") => if self.word_opt("iv") { + Some(Op::IDiv) + } else if self.word_opt("istinct") { + Some(Op::Distinct) + } else { + None + }, + Some("t") => if self.word_opt("o_int") { + Some(Op::ToInt) + } else if self.word_opt("o_real") { + Some(Op::ToReal) + } else { + None + }, - } else { - bail!( - self.error( - pos, format!("unknown identifier `{}`", conf.bad(id)) - ) - ) + Some("s") => if self.word_opt("tore") { + Some(Op::Store) + } else if self.word_opt("elect") { + Some(Op::Select) + } else { + None + }, + + Some("=") => if self.tag_opt(">") { + Some(Op::Impl) + } else { + Some(Op::Eql) + }, + Some(">") => if self.tag_opt("=") { + Some(Op::Ge) + } else { + Some(Op::Gt) + }, + Some("<") => if self.tag_opt("=") { + Some(Op::Le) + } else { + Some(Op::Lt) + }, + Some("+") => Some(Op::Add), + Some("-") => Some(Op::Sub), + Some("*") => Some(Op::Mul), + Some("/") => Some(Op::Div), + Some(_) => None, + None => None, + }; + + if res.is_none() { + self.backtrack_to(start_pos) } - } else if self.tag_opt("(") { + Ok(res) + } - self.ws_cmt() ; - let op_pos = self.pos() ; + /// Parses a single term. + pub fn term_opt( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res> { + let start_pos = self.pos(); + let res = self.inner_term_opt(var_map, map, instance); - if let Some(op) = self.op_opt() ? { - let frame = TermFrame::new( - FrameOp::Op(op), op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids + if res.as_ref().map(|res| res.is_none()).unwrap_or(true) { + self.cxt.term_stack.clear(); + self.backtrack_to(start_pos); + } else { + debug_assert! { self.cxt.term_stack.is_empty() } + } - } else if self.tag_opt(keywords::op::as_) { - let frame = TermFrame::new( - FrameOp::Cast, op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids + res + } - } else if self.tag_opt("(") { - self.ws_cmt() ; - - // Try to parse a constant array. - if self.tag_opt(keywords::op::as_) { - self.ws_cmt() ; - self.tag(keywords::op::const_) ? ; - self.ws_cmt() ; - let sort_pos = self.pos() ; - let typ = self.sort() ? ; - - self.ws_cmt() ; - self.tag(")") ? ; - - let frame = TermFrame::new( - FrameOp::CArray(typ, sort_pos), op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids - - } else if self.tag_opt(keywords::op::lambda_) { - self.ws_cmt() ; - self.tag(keywords::op::is_) ? ; - self.ws_cmt() ; - - let (op_pos, ident) = if let Some(res) = self.ident_opt() ? { - res - } else if self.tag_opt("(") { - self.ws_cmt() ; - let res = self.ident() ? ; - self.ws_cmt() ; - self.tag("(") ? ; - self.ws_cmt() ; - while self.sort_opt()?.is_some() { - self.ws_cmt() - } - self.tag(")") ? ; - self.ws_cmt() ; - self.sort() ? ; - self.ws_cmt() ; - self.tag(")") ? ; - res - } else { - bail!( self.error_here("unexpected token") ) - } ; - - self.ws_cmt() ; - self.tag(")") ? ; - - let frame = TermFrame::new( - FrameOp::DTypTst( ident.into() ), op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids - - } else { - bail!( self.error_here("unexpected token") ) - } - - } else if let Some((pos, id)) = self.ident_opt().chain_err( - || "while trying to parse datatype" - ) ? { - - if let Some(datatype) = dtyp::of_constructor(id) { - debug_assert! { datatype.news.get(id).is_some() } - let frame = TermFrame::new( - FrameOp::DTypNew( - id.into(), datatype - ), op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids - - } else if dtyp::is_selector(id) { - let frame = TermFrame::new( - FrameOp::DTypSlc( - id.into() - ), op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids - - } else if self.functions.get(id).is_some() - || fun::get(id).is_some() { - let frame = TermFrame::new( - FrameOp::Fun( id.into() ), - op_pos, bind_count - ) ; - self.cxt.term_stack.push(frame) ; - continue 'read_kids - } - - if self.cxt.term_stack.is_empty() { - self.backtrack_to(pos) ; - break 'read_kids None - - } else { - for fun in self.functions.keys() { - println!("- {}", fun) + /// Parses a single term. + fn inner_term_opt( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res> { + if !self.cxt.term_stack.is_empty() { + let e: Error = self.error_here("non-empty term stack").into(); + let mut blah: String = "while parsing this:\n".into(); + for line in self.string.lines() { + blah.push_str("| "); + blah.push_str(line); + blah.push('\n') + } + let mut blah_2: String = "term stack:\n".into(); + for frame in &self.cxt.term_stack { + blah_2 += &format!(" {:?}", frame.op) + } + print_err(&e.chain_err(|| blah).chain_err(|| blah_2)); + panic!("non-empty term stack during parsing") + } + conf.check_timeout()?; + + // The correct (non-error) way to exit this loop is + // + // `break 'read_kids ` + // + // If ` == None`, the code below will automatically backtrack to + // `start_pos` and clear the `term_stack`, so there's no need to do it in + // the loop. + let res = 'read_kids: loop { + self.ws_cmt(); + + let bind_count = self.let_bindings(var_map, map, instance)?; + + self.ws_cmt(); + let mut term_pos = self.pos(); + + let mut term = + if let Some(int) = self.int() { + term::int(int) + } else if let Some(real) = self.real()? { + term::real(real) + } else if let Some(b) = self.bool() { + term::bool(b) + } else if let Some((pos, id)) = self.ident_opt()? { + if let Some(idx) = map.get(id) { + term::var(*idx, var_map[*idx].typ.clone()) + } else if let Some(ptterms) = self.get_bind(id) { + if let Some(term) = ptterms.to_term().chain_err(|| { + format!("while retrieving binding for {}", conf.emph(id)) + })? { + term + } else { + // Not in a legal term. + break 'read_kids None; + } + } else if self.cxt.pred_name_map.get(id).is_some() { + // Identifier is a predicate, we're not in a legal term. + break 'read_kids None; + } else if let Some(datatype) = dtyp::of_constructor(id) { + if let Some(constructor) = datatype.news.get(id) { + if constructor.is_empty() { + let (term, _) = + self.build_dtyp_new(id.into(), &datatype, pos, &[], vec![])?; + term + } else { + bail!(self.error( + pos, + format!( + "constructor `{}` of datatype `{}` takes {} value(s), \ + applied here to none", + conf.bad(id), + conf.emph(&datatype.name), + constructor.len() + ) + )) + } + } else { + bail!("inconsistent datatype map internal state") + } + } else { + bail!(self.error(pos, format!("unknown identifier `{}`", conf.bad(id)))) + } + } else if self.tag_opt("(") { + self.ws_cmt(); + let op_pos = self.pos(); + + if let Some(op) = self.op_opt()? { + let frame = TermFrame::new(FrameOp::Op(op), op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else if self.tag_opt(keywords::op::as_) { + let frame = TermFrame::new(FrameOp::Cast, op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else if self.tag_opt("(") { + self.ws_cmt(); + + // Try to parse a constant array. + if self.tag_opt(keywords::op::as_) { + self.ws_cmt(); + self.tag(keywords::op::const_)?; + self.ws_cmt(); + let sort_pos = self.pos(); + let typ = self.sort()?; + + self.ws_cmt(); + self.tag(")")?; + + let frame = + TermFrame::new(FrameOp::CArray(typ, sort_pos), op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else if self.tag_opt(keywords::op::lambda_) { + self.ws_cmt(); + self.tag(keywords::op::is_)?; + self.ws_cmt(); + + let (op_pos, ident) = if let Some(res) = self.ident_opt()? { + res + } else if self.tag_opt("(") { + self.ws_cmt(); + let res = self.ident()?; + self.ws_cmt(); + self.tag("(")?; + self.ws_cmt(); + while self.sort_opt()?.is_some() { + self.ws_cmt() + } + self.tag(")")?; + self.ws_cmt(); + self.sort()?; + self.ws_cmt(); + self.tag(")")?; + res + } else { + bail!(self.error_here("unexpected token")) + }; + + self.ws_cmt(); + self.tag(")")?; + + let frame = + TermFrame::new(FrameOp::DTypTst(ident.into()), op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else { + bail!(self.error_here("unexpected token")) + } + } else if let Some((pos, id)) = self + .ident_opt() + .chain_err(|| "while trying to parse datatype")? + { + if let Some(datatype) = dtyp::of_constructor(id) { + debug_assert! { datatype.news.get(id).is_some() } + let frame = TermFrame::new( + FrameOp::DTypNew(id.into(), datatype), + op_pos, + bind_count, + ); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else if dtyp::is_selector(id) { + let frame = + TermFrame::new(FrameOp::DTypSlc(id.into()), op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } else if self.functions.get(id).is_some() || fun::get(id).is_some() { + let frame = TermFrame::new(FrameOp::Fun(id.into()), op_pos, bind_count); + self.cxt.term_stack.push(frame); + continue 'read_kids; + } + + if self.cxt.term_stack.is_empty() { + self.backtrack_to(pos); + break 'read_kids None; + } else { + for fun in self.functions.keys() { + println!("- {}", fun) + } + bail!(self.error( + pos, + format!("unknown identifier (term) `{}`", conf.bad(id)) + )) + } + } else if self.cxt.term_stack.is_empty() { + break 'read_kids None; + } else { + bail!(self.error_here("unexpected token")) + } + } else { + break 'read_kids None; + }; + + 'go_up: while let Some(mut frame) = self.cxt.term_stack.pop() { + self.ws_cmt(); + + frame.push_arg(term_pos, term); + + if self.tag_opt(")") { + if frame.is_empty() { + bail!(self.error( + frame.op_pos, + format!( + "Illegal nullary application of operator `{}`", + conf.bad(frame.op.as_str()) + ) + )) + } + + let bind_count = frame.let_count(); + let (nu_term, nu_term_pos) = self.build_app(frame)?; + term = nu_term; + term_pos = nu_term_pos; + self.ws_cmt(); + + self.close_let_bindings(bind_count)?; + continue 'go_up; + } else if frame.is_cast() { + // Cast expect a type after the term being cast. + let sort = self + .sort() + .chain_err(|| "expected sort") + .chain_err(|| self.error(frame.op_pos, "in this cast"))?; + + self.ws_cmt(); + self.tag(")")?; + + self.ws_cmt(); + self.close_let_bindings(frame.let_count())?; + + if frame.args.len() != 1 { + bail!(self.error( + frame.op_pos, + format!( + "ill-formed cast: expected one term, found {}", + frame.args.len() + ) + )) + } + + let (sub_term, pos) = + (frame.args.pop().unwrap(), frame.args_pos.pop().unwrap()); + + if let Some(typ) = sub_term.typ().merge(&sort) { + if let Some(nu_term) = sub_term.force_dtyp(typ) { + term = nu_term + } else { + term = sub_term + } + } else { + bail!(self.error( + pos, + format!("cannot cast `{}` to `{}`", sub_term.typ(), sort) + )) + } + + continue 'go_up; + } else { + // Keep on parsing terms. + self.cxt.term_stack.push(frame); + continue 'read_kids; + } } - bail!( - self.error( - pos, format!( - "unknown identifier (term) `{}`", conf.bad(id) - ) - ) - ) - } - } else if self.cxt.term_stack.is_empty() { - break 'read_kids None + // Stack is empty, done. + debug_assert!(self.cxt.term_stack.is_empty()); + break 'read_kids Some(term); + }; - } else { - bail!( self.error_here("unexpected token") ) + Ok(res) + } + + /// Tries to parse a `define-fun`. + fn define_fun(&mut self, instance: &mut Instance) -> Res { + use fun::RFun; + + if !self.word_opt(keywords::cmd::def_fun) { + return Ok(false); } + conf.check_timeout()?; + self.ws_cmt(); - } else { - break 'read_kids None + let (name_pos, name) = self.ident()?; + self.ws_cmt(); - } ; + let mut var_info = VarInfos::new(); + let mut map = BTreeMap::new(); + self.args(&mut var_info, &mut map)?; + self.ws_cmt(); - 'go_up: while let Some(mut frame) = self.cxt.term_stack.pop() { - self.ws_cmt() ; + let sort_pos = self.pos(); + let out_sort = self.sort()?; + self.ws_cmt(); - frame.push_arg(term_pos, term) ; + let body_pos = self.pos(); + let body = self.parse_ptterms(&var_info, &map, instance)?; + self.ws_cmt(); - if self.tag_opt(")") { - if frame.is_empty() { - bail!( - self.error( - frame.op_pos, format!( - "Illegal nullary application of operator `{}`", - conf.bad( frame.op.as_str() ) - ) - ) - ) - } - - let bind_count = frame.let_count() ; - let (nu_term, nu_term_pos) = self.build_app(frame) ? ; - term = nu_term ; - term_pos = nu_term_pos ; - self.ws_cmt() ; - - self.close_let_bindings(bind_count) ? ; - continue 'go_up - - } else if frame.is_cast() { - // Cast expect a type after the term being cast. - let sort = self.sort().chain_err( - || "expected sort" - ).chain_err( - || self.error(frame.op_pos, "in this cast") - ) ? ; - - self.ws_cmt() ; - self.tag(")") ? ; - - self.ws_cmt() ; - self.close_let_bindings( frame.let_count() ) ? ; - - if frame.args.len() != 1 { - bail!( - self.error( - frame.op_pos, format!( - "ill-formed cast: expected one term, found {}", - frame.args.len() + if out_sort != body.typ() { + Err::<_, Error>( + self.error( + name_pos, + format!("in this `define-fun` for {}", conf.emph(name)), + ).into(), + ).chain_err(|| self.error(body_pos, "body is ill typed")) + .chain_err(|| { + self.error( + sort_pos, + format!( + "it has type {}, but expected {} as specified", + conf.emph(&format!("{}", body.typ())), + conf.emph(&format!("{}", out_sort)) + ), ) - ) - ) - } + })? + } - let (sub_term, pos) = ( - frame.args.pop().unwrap(), - frame.args_pos.pop().unwrap() - ) ; + if let Some(term) = body.to_term()? { + let mut fun = RFun::new(name, var_info, out_sort); + fun.set_def(term); + let _ = fun::mk(fun) + .chain_err(|| self.error(name_pos, "while registering this function"))?; + () + } else { + let prev = instance.add_define_fun(name, var_info, body); - if let Some(typ) = sub_term.typ().merge( & sort ) { - if let Some(nu_term) = sub_term.force_dtyp(typ) { - term = nu_term - } else { - term = sub_term + if prev.is_some() { + bail!(self.error(name_pos, format!("redefinition of {}", conf.emph(name)))) } - } else { - bail!( - self.error( - pos, format!( - "cannot cast `{}` to `{}`", sub_term.typ(), sort - ) - ) - ) - } + } - continue 'go_up + Ok(true) + } - } else { - // Keep on parsing terms. - self.cxt.term_stack.push(frame) ; - continue 'read_kids - } - } - - // Stack is empty, done. - debug_assert!( self.cxt.term_stack.is_empty() ) ; - break 'read_kids Some(term) - } ; + /// Parses some PTTerm arguments. + fn ptterm_args( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res> { + let mut res = VarMap::with_capacity(11); - Ok(res) - } + let mut backtrack_pos = self.pos(); + let mut term_pos = self.pos(); + while !self.tag_opt(")") { + conf.check_timeout()?; + let ptterms = self.parse_ptterms(var_map, map, instance)?; + res.push((term_pos, ptterms)); + backtrack_pos = self.pos(); + self.ws_cmt(); + term_pos = self.pos() + } - /// Tries to parse a `define-fun`. - fn define_fun( - & mut self, instance: & mut Instance - ) -> Res { - use fun::RFun ; + self.backtrack_to(backtrack_pos); - if ! self.word_opt(keywords::cmd::def_fun) { - return Ok(false) - } - conf.check_timeout() ? ; - self.ws_cmt() ; - - let (name_pos, name) = self.ident() ? ; - self.ws_cmt() ; - - let mut var_info = VarInfos::new() ; - let mut map = BTreeMap::new() ; - self.args(& mut var_info, & mut map) ? ; - self.ws_cmt() ; - - let sort_pos = self.pos() ; - let out_sort = self.sort() ? ; - self.ws_cmt() ; - - let body_pos = self.pos() ; - let body = self.parse_ptterms(& var_info, & map, instance) ? ; - self.ws_cmt() ; + res.shrink_to_fit(); - if out_sort != body.typ() { - Err::<_, Error>( - self.error( - name_pos, format!("in this `define-fun` for {}", conf.emph(name)) - ).into() - ).chain_err( - || self.error(body_pos, "body is ill typed") - ).chain_err( - || self.error( - sort_pos, format!( - "it has type {}, but expected {} as specified", - conf.emph(& format!("{}", body.typ())), - conf.emph(& format!("{}", out_sort)) - ) - ) - ) ? - } - - if let Some(term) = body.to_term() ? { - let mut fun = RFun::new(name, var_info, out_sort) ; - fun.set_def(term) ; - let _ = fun::mk(fun).chain_err( - || self.error(name_pos, "while registering this function") - ) ? ; - () - - } else { - - let prev = instance.add_define_fun(name, var_info, body) ; - - if prev.is_some() { - bail!( - self.error(name_pos, format!("redefinition of {}", conf.emph(name))) - ) - } - - } - - Ok(true) - } - - - /// Parses some PTTerm arguments. - fn ptterm_args( - & mut self, - var_map: & VarInfos, - map : & BTreeMap<& 's str, VarIdx>, - instance: & Instance - ) -> Res< VarMap<(Pos, PTTerms)> > { - let mut res = VarMap::with_capacity(11) ; - - let mut backtrack_pos = self.pos() ; - let mut term_pos = self.pos() ; - - while ! self.tag_opt(")") { - conf.check_timeout() ? ; - let ptterms = self.parse_ptterms( - var_map, map, instance - ) ? ; - res.push((term_pos, ptterms)) ; - backtrack_pos = self.pos() ; - self.ws_cmt() ; - term_pos = self.pos() - } - - self.backtrack_to(backtrack_pos) ; - - res.shrink_to_fit() ; - - Ok(res) - } - - - /// Parses arguments for a predicate application and type-checks it. - fn pred_args( - & mut self, - pred: PrdIdx, - pred_pos: Pos, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance - ) -> Res< Option > { - let mut args = VarMap::with_capacity(11) ; - let mut kid_pos = Vec::with_capacity(11) ; - - let mut backtrack_pos = self.pos() ; - let mut term_pos = self.pos() ; - - while let Some(term) = self.term_opt( - var_map, map, instance - ) ? { - kid_pos.push(term_pos) ; - args.push(term) ; - backtrack_pos = self.pos() ; - self.ws_cmt() ; - term_pos = self.pos() - } - - self.backtrack_to(backtrack_pos) ; - - args.shrink_to_fit() ; - - let sig = & instance[pred].sig ; - - if sig.len() != kid_pos.len() { - bail!( - self.error( - pred_pos, format!( - "predicate {} takes {} arguments, but is applied to {}", - conf.emph(& instance[pred].name), sig.len(), kid_pos.len() - ) - ) - ) - } else { - for ((index, exp), pos) in sig.index_iter().zip( kid_pos.into_iter() ) { - let found = args[index].typ() ; - if exp != & found { - if let Some(nu) = exp.merge(& found) { - if let Some(term) = args[index].force_dtyp(nu) { - args[index] = term - } - } else { - err_chain! { - self.error( - pos, format!( - "expected an expression of sort {}, found {} ({})", - exp, & args[index], found - ) - ) - => self.error( - pred_pos, format!( - "in this application of {}, parameter #{}", - conf.emph(& instance[pred].name), index - ) - ) - } - } - } - } + Ok(res) } - Ok( - Some( - PTTerms::tterm( TTerm::P { pred, args: args.into() } ) - ) - ) - } - - - /// Parses a top term or fails. - #[allow(dead_code)] - fn top_term( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance, - ) -> Res { - if let Some(res) = self.top_term_opt(var_map, map, instance) ? { - Ok(res) - } else { - bail!( self.error_here("expected term") ) - } - } - /// Tries to parse a top term. - fn top_term_opt( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance, - ) -> Res< Option< PTTerms > > { - conf.check_timeout() ? ; - let bind_count = self.let_bindings(var_map, map, instance) ? ; - - self.ws_cmt() ; - let start_pos = self.pos() ; - - let res = if let Some(term) = self.term_opt( - var_map, map, instance - ) ? { - Ok( Some( - PTTerms::tterm( TTerm::T( term ) ) - ) ) - } else if let Some((pos, id)) = self.ident_opt().chain_err( - || "while trying to parse a top term (1)" - ) ? { - if let Some(idx) = self.cxt.pred_name_map.get(id) { - let idx = * idx ; - if instance[idx].sig.is_empty() { - Ok( Some( - PTTerms::TTerm( - TTerm::P { pred: idx, args: VarMap::with_capacity(0).into() } - ) - ) ) - } else { - bail!( - self.error( - pos, format!( - "illegal nullary application of predicate `{}`, \ - this predicate takes {} arguments", - conf.bad(& instance[idx].name), instance[idx].sig.len() - ) - ) - ) - } + /// Parses arguments for a predicate application and type-checks it. + fn pred_args( + &mut self, + pred: PrdIdx, + pred_pos: Pos, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res> { + let mut args = VarMap::with_capacity(11); + let mut kid_pos = Vec::with_capacity(11); - } else if let Some(ptterms) = self.get_bind(id) { - Ok( Some( ptterms.clone() ) ) + let mut backtrack_pos = self.pos(); + let mut term_pos = self.pos(); - } else { - bail!( - self.error( - pos, format!( - "unknown ident `{}`", conf.bad(id) - ) - ) - ) - } - } else if self.tag_opt("(") { - self.ws_cmt() ; - - if self.tag_opt(keywords::forall) - || self.tag_opt(keywords::exists) { - bail!( - self.error( - start_pos, - "unable to work on clauses that are not ground".to_string() - ) - ) - } else if let Some((ident_pos, ident)) = self.ident_opt().chain_err( - || "while trying to parse a top term (2)" - ) ? { - - if let Some(idx) = self.cxt.pred_name_map.get(ident).cloned() { - let res = self.pred_args(idx, ident_pos, var_map, map, instance) ? ; - self.ws_cmt() ; - self.tag(")") ? ; - Ok(res) - } else if let Some( - & (ref var_info, ref body) - ) = instance.get_define_fun(ident) { - // Parse arguments. - self.ws_cmt() ; - let args = self.ptterm_args(var_map, map, instance) ? ; - self.ws_cmt() ; - self.tag(")") ? ; - - if var_info.len() != args.len() { - bail!( - self.error( - ident_pos, format!( - "wrong number of arguments, expected {} but got {}", - var_info.len(), args.len() - ) - ) - ) - } + while let Some(term) = self.term_opt(var_map, map, instance)? { + kid_pos.push(term_pos); + args.push(term); + backtrack_pos = self.pos(); + self.ws_cmt(); + term_pos = self.pos() + } - for (var, info) in var_info.index_iter() { - if info.typ != args[var].1.typ() { - bail!( - self.error( - args[var].0, format!( - "sort error, expected term of sort {}, found {}", - info.typ, args[var].1.typ() - ) - ) - ) - } - } + self.backtrack_to(backtrack_pos); - let args: VarMap<_> = args.into_iter().map(|(_, t)| t).collect() ; + args.shrink_to_fit(); - let res = body.subst_total(& args).chain_err( - || self.error( - ident_pos, format!( - "while inlining the application of {}", conf.emph(ident) - ) - ) - ) ? ; + let sig = &instance[pred].sig; - Ok( Some(res) ) + if sig.len() != kid_pos.len() { + bail!(self.error( + pred_pos, + format!( + "predicate {} takes {} arguments, but is applied to {}", + conf.emph(&instance[pred].name), + sig.len(), + kid_pos.len() + ) + )) } else { - bail!( - self.error( - ident_pos, - format!( - "unknown identifier (tterm) `{}`", conf.bad(ident) - ) - ) - ) - } - } else { - bail!( - self.error_here("expected operator, let binding or predicate") - ) - } - - } else { - // In theory, we should check if the top term is an ident that's either a - // quantified or bound variable. In practice, this is done at the level - // above this one, in `parse_ptterms`. - Ok(None) - } ; - - self.ws_cmt() ; - self.close_let_bindings(bind_count) ? ; - - res - } - - - /// Parses some top terms (parsing variant, for simplifications). - fn parse_ptterms( - & mut self, - var_map: & VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & Instance, - ) -> Res { - enum Frame { - And(Vec), - Or(Vec), - Impl(Vec), - Not, - Let(LetCount) - } - let mut stack: Vec = vec![] ; - - 'go_down: loop { - - self.ws_cmt() ; - - let bind_count = self.let_bindings(var_map, & map, instance) ? ; - if ! bind_count.is_zero() { - stack.push( Frame::Let(bind_count) ) ; - } - - self.ws_cmt() ; - - let start_pos = self.pos() ; - let mut ptterm = if let Some(pos) = self.tag_opt_pos("(") { - - self.ws_cmt() ; - - if self.tag_opt("and") { - stack.push( Frame::And(vec![]) ) ; - continue 'go_down - } else if self.tag_opt("or") { - stack.push( Frame::Or(vec![]) ) ; - continue 'go_down - } else if self.tag_opt("not") { - stack.push( Frame::Not ) ; - continue 'go_down - } else if self.tag_opt("=>") { - stack.push( Frame::Impl(vec![]) ) ; - continue 'go_down + for ((index, exp), pos) in sig.index_iter().zip(kid_pos.into_iter()) { + let found = args[index].typ(); + if exp != &found { + if let Some(nu) = exp.merge(&found) { + if let Some(term) = args[index].force_dtyp(nu) { + args[index] = term + } + } else { + err_chain! { + self.error( + pos, format!( + "expected an expression of sort {}, found {} ({})", + exp, & args[index], found + ) + ) + => self.error( + pred_pos, format!( + "in this application of {}, parameter #{}", + conf.emph(& instance[pred].name), index + ) + ) + } + } + } + } + } + + Ok(Some(PTTerms::tterm(TTerm::P { + pred, + args: args.into(), + }))) + } + + /// Parses a top term or fails. + #[allow(dead_code)] + fn top_term( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res { + if let Some(res) = self.top_term_opt(var_map, map, instance)? { + Ok(res) } else { - self.backtrack_to(pos) ; - if let Some(top) = self.top_term_opt( - var_map, & map, instance - ) ? { - if top.typ().is_bool() { - top - } else if stack.is_empty() { - // If we get here, it means what we're parsing does not have type - // bool. Which means we're not inside a top-term (we're most - // likely parsing a let-binding). - return Ok(top) + bail!(self.error_here("expected term")) + } + } + /// Tries to parse a top term. + fn top_term_opt( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res> { + conf.check_timeout()?; + let bind_count = self.let_bindings(var_map, map, instance)?; + + self.ws_cmt(); + let start_pos = self.pos(); + + let res = if let Some(term) = self.term_opt(var_map, map, instance)? { + Ok(Some(PTTerms::tterm(TTerm::T(term)))) + } else if let Some((pos, id)) = self + .ident_opt() + .chain_err(|| "while trying to parse a top term (1)")? + { + if let Some(idx) = self.cxt.pred_name_map.get(id) { + let idx = *idx; + if instance[idx].sig.is_empty() { + Ok(Some(PTTerms::TTerm(TTerm::P { + pred: idx, + args: VarMap::with_capacity(0).into(), + }))) + } else { + bail!(self.error( + pos, + format!( + "illegal nullary application of predicate `{}`, \ + this predicate takes {} arguments", + conf.bad(&instance[idx].name), + instance[idx].sig.len() + ) + )) + } + } else if let Some(ptterms) = self.get_bind(id) { + Ok(Some(ptterms.clone())) } else { - err_chain! { - "while parsing top term" - => self.error( - start_pos, format!( - "expected expression of type Bool, found {}", top.typ() - ) - ) - } + bail!(self.error(pos, format!("unknown ident `{}`", conf.bad(id)))) } - } else if let Some(top) = self.term_opt( - var_map, & map, instance - ) ? { - if top.typ().is_bool() { - PTTerms::TTerm( TTerm::T(top) ) - } else if stack.is_empty() { - // If we get here, it means what we're parsing does not have type - // bool. Which means we're not inside a top-term (we're most - // likely parsing a let-binding). - return Ok( - PTTerms::TTerm( TTerm::T(top) ) - ) + } else if self.tag_opt("(") { + self.ws_cmt(); + + if self.tag_opt(keywords::forall) || self.tag_opt(keywords::exists) { + bail!(self.error( + start_pos, + "unable to work on clauses that are not ground".to_string() + )) + } else if let Some((ident_pos, ident)) = self + .ident_opt() + .chain_err(|| "while trying to parse a top term (2)")? + { + if let Some(idx) = self.cxt.pred_name_map.get(ident).cloned() { + let res = self.pred_args(idx, ident_pos, var_map, map, instance)?; + self.ws_cmt(); + self.tag(")")?; + Ok(res) + } else if let Some(&(ref var_info, ref body)) = instance.get_define_fun(ident) { + // Parse arguments. + self.ws_cmt(); + let args = self.ptterm_args(var_map, map, instance)?; + self.ws_cmt(); + self.tag(")")?; + + if var_info.len() != args.len() { + bail!(self.error( + ident_pos, + format!( + "wrong number of arguments, expected {} but got {}", + var_info.len(), + args.len() + ) + )) + } + + for (var, info) in var_info.index_iter() { + if info.typ != args[var].1.typ() { + bail!(self.error( + args[var].0, + format!( + "sort error, expected term of sort {}, found {}", + info.typ, + args[var].1.typ() + ) + )) + } + } + + let args: VarMap<_> = args.into_iter().map(|(_, t)| t).collect(); + + let res = body.subst_total(&args).chain_err(|| { + self.error( + ident_pos, + format!("while inlining the application of {}", conf.emph(ident)), + ) + })?; + + Ok(Some(res)) + } else { + bail!(self.error( + ident_pos, + format!("unknown identifier (tterm) `{}`", conf.bad(ident)) + )) + } } else { - err_chain! { - "while parsing subterm" - => self.error( - start_pos, format!( - "expected expression of type Bool, found {}", top.typ() - ) - ) - } + bail!(self.error_here("expected operator, let binding or predicate")) } - } else { - bail!( - self.error( - start_pos, "failed to parse expression top term" - ) - ) - } - } - } else if let Some(top) = self.top_term_opt( - var_map, & map, instance - ) ? { - if top.typ().is_bool() { - top - } else if stack.is_empty() { - // If we get here, it means what we're parsing does not have type - // bool. Which means we're not inside a top-term (we're most likely - // parsing a let-binding). - return Ok(top) - } else { - err_chain! { - "while parsing top term" - => self.error( - start_pos, format!( - "expected expression of type Bool, found {}", top.typ() - ) - ) - } - } - } else if let Some(top) = self.term_opt( - var_map, & map, instance - ) ? { - if top.typ().is_bool() { - PTTerms::TTerm( TTerm::T(top) ) - } else if stack.is_empty() { - // If we get here, it means what we're parsing does not have type - // bool. Which means we're not inside a top-term (we're most likely - // parsing a let-binding). - return Ok( - PTTerms::TTerm( TTerm::T(top) ) - ) } else { - err_chain! { - "while parsing subterm (ident or constant)" - => self.error( - start_pos, format!( - "expected expression of type Bool, found {}", top.typ() - ) - ) - } - } - } else { - bail!( - self.error( - start_pos, "failed to parse top expression" - ) - ) - } ; - - 'go_up: loop { - match stack.pop() { - Some( Frame::And(mut args) ) => { - args.push(ptterm) ; - self.ws_cmt() ; - if self.tag_opt(")") { - ptterm = PTTerms::and(args) ; - continue 'go_up - } else { - stack.push( Frame::And(args) ) ; - continue 'go_down + // In theory, we should check if the top term is an ident that's either a + // quantified or bound variable. In practice, this is done at the level + // above this one, in `parse_ptterms`. + Ok(None) + }; + + self.ws_cmt(); + self.close_let_bindings(bind_count)?; + + res + } + + /// Parses some top terms (parsing variant, for simplifications). + fn parse_ptterms( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &Instance, + ) -> Res { + enum Frame { + And(Vec), + Or(Vec), + Impl(Vec), + Not, + Let(LetCount), + } + let mut stack: Vec = vec![]; + + 'go_down: loop { + self.ws_cmt(); + + let bind_count = self.let_bindings(var_map, &map, instance)?; + if !bind_count.is_zero() { + stack.push(Frame::Let(bind_count)); } - }, - Some( Frame::Or(mut args) ) => { - args.push(ptterm) ; - self.ws_cmt() ; - if self.tag_opt(")") { - ptterm = PTTerms::or(args) ; - continue 'go_up + + self.ws_cmt(); + + let start_pos = self.pos(); + let mut ptterm = if let Some(pos) = self.tag_opt_pos("(") { + self.ws_cmt(); + + if self.tag_opt("and") { + stack.push(Frame::And(vec![])); + continue 'go_down; + } else if self.tag_opt("or") { + stack.push(Frame::Or(vec![])); + continue 'go_down; + } else if self.tag_opt("not") { + stack.push(Frame::Not); + continue 'go_down; + } else if self.tag_opt("=>") { + stack.push(Frame::Impl(vec![])); + continue 'go_down; + } else { + self.backtrack_to(pos); + if let Some(top) = self.top_term_opt(var_map, &map, instance)? { + if top.typ().is_bool() { + top + } else if stack.is_empty() { + // If we get here, it means what we're parsing does not have type + // bool. Which means we're not inside a top-term (we're most + // likely parsing a let-binding). + return Ok(top); + } else { + err_chain! { + "while parsing top term" + => self.error( + start_pos, format!( + "expected expression of type Bool, found {}", top.typ() + ) + ) + } + } + } else if let Some(top) = self.term_opt(var_map, &map, instance)? { + if top.typ().is_bool() { + PTTerms::TTerm(TTerm::T(top)) + } else if stack.is_empty() { + // If we get here, it means what we're parsing does not have type + // bool. Which means we're not inside a top-term (we're most + // likely parsing a let-binding). + return Ok(PTTerms::TTerm(TTerm::T(top))); + } else { + err_chain! { + "while parsing subterm" + => self.error( + start_pos, format!( + "expected expression of type Bool, found {}", top.typ() + ) + ) + } + } + } else { + bail!(self.error(start_pos, "failed to parse expression top term")) + } + } + } else if let Some(top) = self.top_term_opt(var_map, &map, instance)? { + if top.typ().is_bool() { + top + } else if stack.is_empty() { + // If we get here, it means what we're parsing does not have type + // bool. Which means we're not inside a top-term (we're most likely + // parsing a let-binding). + return Ok(top); + } else { + err_chain! { + "while parsing top term" + => self.error( + start_pos, format!( + "expected expression of type Bool, found {}", top.typ() + ) + ) + } + } + } else if let Some(top) = self.term_opt(var_map, &map, instance)? { + if top.typ().is_bool() { + PTTerms::TTerm(TTerm::T(top)) + } else if stack.is_empty() { + // If we get here, it means what we're parsing does not have type + // bool. Which means we're not inside a top-term (we're most likely + // parsing a let-binding). + return Ok(PTTerms::TTerm(TTerm::T(top))); + } else { + err_chain! { + "while parsing subterm (ident or constant)" + => self.error( + start_pos, format!( + "expected expression of type Bool, found {}", top.typ() + ) + ) + } + } } else { - stack.push( Frame::Or(args) ) ; - continue 'go_down + bail!(self.error(start_pos, "failed to parse top expression")) + }; + + 'go_up: loop { + match stack.pop() { + Some(Frame::And(mut args)) => { + args.push(ptterm); + self.ws_cmt(); + if self.tag_opt(")") { + ptterm = PTTerms::and(args); + continue 'go_up; + } else { + stack.push(Frame::And(args)); + continue 'go_down; + } + } + Some(Frame::Or(mut args)) => { + args.push(ptterm); + self.ws_cmt(); + if self.tag_opt(")") { + ptterm = PTTerms::or(args); + continue 'go_up; + } else { + stack.push(Frame::Or(args)); + continue 'go_down; + } + } + Some(Frame::Impl(mut args)) => { + args.push(ptterm); + self.ws_cmt(); + if self.tag_opt(")") { + if args.len() != 2 { + bail!( + "unexpected implication over {} (!= 2) arguments", + args.len() + ) + } + let (rhs, lhs) = (args.pop().unwrap(), args.pop().unwrap()); + ptterm = PTTerms::or(vec![PTTerms::not(lhs)?, rhs]); + continue 'go_up; + } else { + stack.push(Frame::Impl(args)); + continue 'go_down; + } + } + Some(Frame::Not) => { + self.ws_cmt(); + ptterm = PTTerms::not(ptterm)?; + self.tag(")")?; + continue 'go_up; + } + Some(Frame::Let(bind_count)) => { + self.close_let_bindings(bind_count)?; + continue 'go_up; + } + None => break 'go_down Ok(ptterm), + } } - }, - Some( Frame::Impl(mut args) ) => { - args.push(ptterm) ; - self.ws_cmt() ; - if self.tag_opt(")") { - if args.len() != 2 { - bail!( - "unexpected implication over {} (!= 2) arguments", args.len() - ) - } - let (rhs, lhs) = (args.pop().unwrap(), args.pop().unwrap()) ; - ptterm = PTTerms::or( vec![ PTTerms::not(lhs) ?, rhs ] ) ; - continue 'go_up + } + } + + /// Parses a forall. + /// + /// Returns + /// + /// - `None` if nothing was parsed ; + /// - `Some(None)` if a clause was parsed but it was not actually added + /// (*e.g.* redundant) ; + /// - `Some(idx)` if a clause was parsed and added, and it has index `idx`. + fn forall(&mut self, instance: &mut Instance) -> Res> { + if !self.word_opt(keywords::forall) { + return Ok(None); + } + + let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = + (VarMap::with_capacity(11), BTreeMap::new(), true, 0); + + while parse_args { + self.ws_cmt(); + self.args(&mut var_map, &mut hash_map)?; + + self.ws_cmt(); + parse_args = if let Some(pos) = self.tag_opt_pos("(") { + self.ws_cmt(); + if self.tag_opt(keywords::forall) { + closing_parens += 1; + true + } else { + self.backtrack_to(pos); + false + } } else { - stack.push( Frame::Impl(args) ) ; - continue 'go_down + false } - }, - Some( Frame::Not ) => { - self.ws_cmt() ; - ptterm = PTTerms::not(ptterm) ? ; - self.tag(")") ? ; - continue 'go_up - }, - Some( Frame::Let(bind_count) ) => { - self.close_let_bindings(bind_count) ? ; - continue 'go_up - }, - None => break 'go_down Ok(ptterm), - } - } - - } - } - - - /// Parses a forall. - /// - /// Returns - /// - /// - `None` if nothing was parsed ; - /// - `Some(None)` if a clause was parsed but it was not actually added - /// (*e.g.* redundant) ; - /// - `Some(idx)` if a clause was parsed and added, and it has index `idx`. - fn forall( - & mut self, instance: & mut Instance - ) -> Res< Option > { - if ! self.word_opt(keywords::forall) { - return Ok(None) - } - - let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = ( - VarMap::with_capacity(11), BTreeMap::new(), true, 0 - ) ; - - while parse_args { - self.ws_cmt() ; - self.args(& mut var_map, & mut hash_map) ? ; - - self.ws_cmt() ; - parse_args = if let Some(pos) = self.tag_opt_pos("(") { - self.ws_cmt() ; - if self.tag_opt(keywords::forall) { - closing_parens += 1 ; - true - } else { - self.backtrack_to(pos) ; - false } - } else { - false - } - } - - self.ws_cmt() ; - let outter_bind_count = self.let_bindings( - & var_map, & hash_map, instance - ) ? ; - - self.ws_cmt() ; - let idx = self.parse_clause( - var_map, & hash_map, instance, false - ) ? ; - - self.ws_cmt() ; - self.close_let_bindings(outter_bind_count) ? ; - - for _ in 0..closing_parens { - self.ws_cmt() ; - self.tag(")") ? - } - - Ok( Some(idx) ) - } - - - /// Parses a negated exists. - /// - /// Returns - /// - /// - `None` if nothing was parsed ; - /// - `Some(None)` if a clause was parsed but it was not actually added - /// (*e.g.* redundant) ; - /// - `Some(idx)` if a clause was parsed and added, and it has index `idx`. - fn nexists( - & mut self, instance: & mut Instance - ) -> Res< Option > { - if ! self.word_opt(keywords::op::not_) { - return Ok(None) - } - self.ws_cmt() ; - let outter_bind_count = self.let_bindings( - & VarMap::new(), & BTreeMap::new(), instance - ) ? ; - - self.ws_cmt() ; - self.tag("(") ? ; - - self.ws_cmt() ; - self.word(keywords::exists) ? ; - - let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = ( - VarMap::with_capacity(11), BTreeMap::new(), true, 0 - ) ; - - while parse_args { - self.ws_cmt() ; - self.args(& mut var_map, & mut hash_map) ? ; - - self.ws_cmt() ; - parse_args = if let Some(pos) = self.tag_opt_pos("(") { - self.ws_cmt() ; - if self.tag_opt(keywords::exists) { - closing_parens += 1 ; - true - } else { - self.backtrack_to(pos) ; - false + + self.ws_cmt(); + let outter_bind_count = self.let_bindings(&var_map, &hash_map, instance)?; + + self.ws_cmt(); + let idx = self.parse_clause(var_map, &hash_map, instance, false)?; + + self.ws_cmt(); + self.close_let_bindings(outter_bind_count)?; + + for _ in 0..closing_parens { + self.ws_cmt(); + self.tag(")")? } - } else { - false - } + + Ok(Some(idx)) } - self.ws_cmt() ; - let idx = self.parse_clause(var_map, & hash_map, instance, true) ? ; + /// Parses a negated exists. + /// + /// Returns + /// + /// - `None` if nothing was parsed ; + /// - `Some(None)` if a clause was parsed but it was not actually added + /// (*e.g.* redundant) ; + /// - `Some(idx)` if a clause was parsed and added, and it has index `idx`. + fn nexists(&mut self, instance: &mut Instance) -> Res> { + if !self.word_opt(keywords::op::not_) { + return Ok(None); + } + self.ws_cmt(); + let outter_bind_count = self.let_bindings(&VarMap::new(), &BTreeMap::new(), instance)?; - self.ws_cmt() ; - self.tag(")") ? ; + self.ws_cmt(); + self.tag("(")?; - self.ws_cmt() ; - self.close_let_bindings(outter_bind_count) ? ; + self.ws_cmt(); + self.word(keywords::exists)?; - for _ in 0..closing_parens { - self.ws_cmt() ; - self.tag(")") ? - } + let (mut var_map, mut hash_map, mut parse_args, mut closing_parens) = + (VarMap::with_capacity(11), BTreeMap::new(), true, 0); + + while parse_args { + self.ws_cmt(); + self.args(&mut var_map, &mut hash_map)?; + + self.ws_cmt(); + parse_args = if let Some(pos) = self.tag_opt_pos("(") { + self.ws_cmt(); + if self.tag_opt(keywords::exists) { + closing_parens += 1; + true + } else { + self.backtrack_to(pos); + false + } + } else { + false + } + } - Ok( Some(idx) ) - } + self.ws_cmt(); + let idx = self.parse_clause(var_map, &hash_map, instance, true)?; + self.ws_cmt(); + self.tag(")")?; - fn parse_clause( - & mut self, - var_map: VarInfos, - map: & BTreeMap<& 's str, VarIdx>, - instance: & mut Instance, - negated: bool, - ) -> Res< ClauseRes > { - profile! { self tick "parsing", "clause" } - self.ws_cmt() ; + self.ws_cmt(); + self.close_let_bindings(outter_bind_count)?; - let start_pos = self.pos() ; - let mut ptterms = self.parse_ptterms( - & var_map, & map, instance - ) ? ; - if ! ptterms.typ().is_bool() { - err_chain! { - "while parsing clause terms" - => self.error( - start_pos, format!( - "expected expression of type Bool, got {}", ptterms.typ() - ) - ) - } - } - if negated { - ptterms = PTTerms::not(ptterms) ? + for _ in 0..closing_parens { + self.ws_cmt(); + self.tag(")")? + } + + Ok(Some(idx)) } - let (mut at_least_one, idx) = ( - false, instance.next_clause_index() - ) ; + fn parse_clause( + &mut self, + var_map: VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + instance: &mut Instance, + negated: bool, + ) -> Res { + profile! { self tick "parsing", "clause" } + self.ws_cmt(); + + let start_pos = self.pos(); + let mut ptterms = self.parse_ptterms(&var_map, &map, instance)?; + if !ptterms.typ().is_bool() { + err_chain! { + "while parsing clause terms" + => self.error( + start_pos, format!( + "expected expression of type Bool, got {}", ptterms.typ() + ) + ) + } + } + if negated { + ptterms = PTTerms::not(ptterms)? + } - let mut clauses = ptterms.into_clauses()?.into_iter() ; + let (mut at_least_one, idx) = (false, instance.next_clause_index()); - if let Some((last_lhs, last_rhs)) = clauses.next() { + let mut clauses = ptterms.into_clauses()?.into_iter(); - for (lhs, rhs) in clauses { - if self.add_clause(instance, var_map.clone(), lhs, rhs) ? { - at_least_one = true + if let Some((last_lhs, last_rhs)) = clauses.next() { + for (lhs, rhs) in clauses { + if self.add_clause(instance, var_map.clone(), lhs, rhs)? { + at_least_one = true + } + } + if self.add_clause(instance, var_map, last_lhs, last_rhs)? { + at_least_one = true + } } - } - if self.add_clause(instance, var_map, last_lhs, last_rhs) ? { - at_least_one = true - } - } - profile! { self mark "parsing", "clause" } + profile! { self mark "parsing", "clause" } - if at_least_one { - Ok( ClauseRes::Added(idx) ) - } else { - Ok( ClauseRes::Skipped ) + if at_least_one { + Ok(ClauseRes::Added(idx)) + } else { + Ok(ClauseRes::Skipped) + } } - } - /// Adds a clause to an instance. - fn add_clause( - & self, instance: & mut Instance, - var_map: VarInfos, lhs: Vec, rhs: TTerm - ) -> Res { + /// Adds a clause to an instance. + fn add_clause( + &self, + instance: &mut Instance, + var_map: VarInfos, + lhs: Vec, + rhs: TTerm, + ) -> Res { + let mut nu_lhs = Vec::with_capacity(lhs.len()); + let mut lhs_is_false = false; + for lhs in lhs { + if !lhs.is_true() { + if lhs.is_false() { + lhs_is_false = true; + break; + } else { + nu_lhs.push(lhs) + } + } + } + let rhs = match rhs { + TTerm::P { pred, args } => Some((pred, args)), + TTerm::T(t) => { + if t.bool() != Some(false) { + nu_lhs.push(TTerm::T(term::not(t))) + } + None + } + }; - let mut nu_lhs = Vec::with_capacity( lhs.len() ) ; - let mut lhs_is_false = false ; - for lhs in lhs { - if ! lhs.is_true() { - if lhs.is_false() { - lhs_is_false = true ; - break + if !lhs_is_false { + profile! { self tick "parsing", "add clause" } + let maybe_index = instance.push_new_clause(var_map, nu_lhs, rhs, "parsing")?; + profile! { self mark "parsing", "add clause" } + Ok(maybe_index.is_some()) } else { - nu_lhs.push(lhs) + Ok(false) } - } } - let rhs = match rhs { - TTerm::P { pred, args } => Some((pred, args)), - TTerm::T(t) => { - if t.bool() != Some(false) { - nu_lhs.push( TTerm::T( term::not(t) ) ) + + /// Parses an assert. + fn assert(&mut self, instance: &mut Instance) -> Res { + if !self.word_opt(keywords::cmd::assert) { + return Ok(false); } - None - }, - } ; - if ! lhs_is_false { - profile! { self tick "parsing", "add clause" } - let maybe_index = instance.push_new_clause( - var_map, nu_lhs, rhs, "parsing" - ) ? ; - profile! { self mark "parsing", "add clause" } - Ok(maybe_index.is_some()) - } else { - Ok(false) - } - } + profile! { self tick "parsing", "assert" } + self.ws_cmt(); - /// Parses an assert. - fn assert(& mut self, instance: & mut Instance) -> Res { - if ! self.word_opt(keywords::cmd::assert) { - return Ok(false) - } + let start_pos = self.pos(); + let tagged = if self.tag_opt("(") { + self.ws_cmt(); + if self.tag_opt("!") { + self.ws_cmt(); + true + } else { + self.backtrack_to(start_pos); + false + } + } else { + false + }; - profile! { self tick "parsing", "assert" } + let bind_count = self.let_bindings(&VarMap::new(), &BTreeMap::new(), instance)?; - self.ws_cmt() ; + let idx = if self.tag_opt("(") { + self.ws_cmt(); - let start_pos = self.pos() ; - let tagged = if self.tag_opt("(") { - self.ws_cmt() ; - if self.tag_opt("!") { - self.ws_cmt() ; - true - } else { - self.backtrack_to(start_pos) ; - false - } - } else { - false - } ; - - let bind_count = self.let_bindings( - & VarMap::new(), & BTreeMap::new(), instance - ) ? ; - - let idx = if self.tag_opt("(") { - self.ws_cmt() ; - - let idx = if let Some(idx) = self.forall(instance) ? { - idx - } else if let Some(idx) = self.nexists(instance) ? { - idx - } else { - bail!( - self.error_here("expected forall or negated exists") - ) - } ; - - self.ws_cmt() ; - self.tag(")") ? ; - idx - } else if self.tag_opt("true") { - ClauseRes::Skipped - } else if self.tag_opt("false") { - instance.set_unsat() ; - ClauseRes::Skipped - } else { - bail!( - self.error_here("expected negation, qualifier, `true` or `false`") - ) - } ; - - self.ws_cmt() ; - self.close_let_bindings(bind_count) ? ; - - if tagged { - self.ws_cmt() ; - self.word(":named").chain_err( - || "unexpected tag" - ) ? ; - self.ws_cmt() ; - let (_, ident) = self.ident().chain_err( - || "expected identifier after `:named` tag" - ) ? ; - if let Some(idx) = idx.into_option() { - instance.set_old_clause_name(idx, ident.into()) ? - } - self.ws_cmt() ; - self.tag(")") ? ; - } - - profile! { self mark "parsing", "assert" } - - Ok(true) - } - - /// Parses a check-sat. - fn check_sat(& mut self) -> bool { - self.word_opt(keywords::cmd::check_sat) - } - - /// Parses a get-model. - fn get_model(& mut self) -> bool { - self.word_opt(keywords::cmd::get_model) - } - - /// Parses a get-unsat-core. - fn get_unsat_core(& mut self) -> bool { - self.word_opt(keywords::cmd::get_unsat_core) - } - - /// Parses a get-proof. - fn get_proof(& mut self) -> bool { - self.word_opt(keywords::cmd::get_proof) - } - - /// Parses an exit command. - fn exit(& mut self) -> bool { - self.word_opt(keywords::cmd::exit) - } - - /// Parses an reset command. - fn reset(& mut self) -> bool { - self.word_opt(keywords::cmd::reset) - } - - /// Parses items, returns true if it found a check-sat. - pub fn parse( - mut self, instance: & mut Instance - ) -> Res { - self.ws_cmt() ; - let mut res = Parsed::Eof ; - self.cxt.term_stack.clear() ; - - while self.has_next() { - self.ws_cmt() ; - self.tag_err( - "(", format!( - "expected `{}` opening top-level item", - conf.emph("(") - ) - ) ? ; - self.ws_cmt() ; - - let start_pos = self.pos() ; - - res = if self.set_info() ? { - Parsed::Items - } else if let Some((key, val)) = self.set_option() ? { - instance.set_option(key, val).chain_err( - || { - self.backtrack_to(start_pos) ; - self.error_here("in this set-option") - } - ) ? ; - Parsed::Items - } else if self.set_logic() ? - || self.pred_dec(instance) ? - || self.define_fun(instance) ? - || self.define_funs_rec(instance) ? - || self.assert(instance) ? - || self.dtyp_dec_item() ? - || self.dtyp_decs_item() ? { - Parsed::Items - } else if self.check_sat() { - Parsed::CheckSat - } else if self.get_model() { - Parsed::GetModel - } else if self.get_unsat_core() { - Parsed::GetUnsatCore - } else if self.get_proof() { - Parsed::GetProof - } else if self.exit() { - Parsed::Exit - } else if self.reset() { - Parsed::Reset - } else if let Some(blah) = self.echo() ? { - println!("{}", blah) ; - Parsed::Items - } else { - bail!( - self.error_here("expected top-level item") - ) - } ; - - self.ws_cmt() ; - self.tag(")") ? ; - self.ws_cmt() ; - - debug_assert!( self.cxt.term_stack.is_empty() ) ; - debug_assert!( self.cxt.mem.is_empty() ) ; - - if res != Parsed::Items { - return Ok(res) - } - } - - debug_assert!( self.cxt.term_stack.is_empty() ) ; - debug_assert!( self.cxt.mem.is_empty() ) ; - - Ok(res) - } -} + let idx = if let Some(idx) = self.forall(instance)? { + idx + } else if let Some(idx) = self.nexists(instance)? { + idx + } else { + bail!(self.error_here("expected forall or negated exists")) + }; + + self.ws_cmt(); + self.tag(")")?; + idx + } else if self.tag_opt("true") { + ClauseRes::Skipped + } else if self.tag_opt("false") { + instance.set_unsat(); + ClauseRes::Skipped + } else { + bail!(self.error_here("expected negation, qualifier, `true` or `false`")) + }; + + self.ws_cmt(); + self.close_let_bindings(bind_count)?; + + if tagged { + self.ws_cmt(); + self.word(":named").chain_err(|| "unexpected tag")?; + self.ws_cmt(); + let (_, ident) = self + .ident() + .chain_err(|| "expected identifier after `:named` tag")?; + if let Some(idx) = idx.into_option() { + instance.set_old_clause_name(idx, ident.into())? + } + self.ws_cmt(); + self.tag(")")?; + } + + profile! { self mark "parsing", "assert" } + + Ok(true) + } + + /// Parses a check-sat. + fn check_sat(&mut self) -> bool { + self.word_opt(keywords::cmd::check_sat) + } + + /// Parses a get-model. + fn get_model(&mut self) -> bool { + self.word_opt(keywords::cmd::get_model) + } + + /// Parses a get-unsat-core. + fn get_unsat_core(&mut self) -> bool { + self.word_opt(keywords::cmd::get_unsat_core) + } + + /// Parses a get-proof. + fn get_proof(&mut self) -> bool { + self.word_opt(keywords::cmd::get_proof) + } + + /// Parses an exit command. + fn exit(&mut self) -> bool { + self.word_opt(keywords::cmd::exit) + } + + /// Parses an reset command. + fn reset(&mut self) -> bool { + self.word_opt(keywords::cmd::reset) + } + + /// Parses items, returns true if it found a check-sat. + pub fn parse(mut self, instance: &mut Instance) -> Res { + self.ws_cmt(); + let mut res = Parsed::Eof; + self.cxt.term_stack.clear(); + + while self.has_next() { + self.ws_cmt(); + self.tag_err( + "(", + format!("expected `{}` opening top-level item", conf.emph("(")), + )?; + self.ws_cmt(); + + let start_pos = self.pos(); + + res = if self.set_info()? { + Parsed::Items + } else if let Some((key, val)) = self.set_option()? { + instance.set_option(key, val).chain_err(|| { + self.backtrack_to(start_pos); + self.error_here("in this set-option") + })?; + Parsed::Items + } else if self.set_logic()? + || self.pred_dec(instance)? + || self.define_fun(instance)? + || self.define_funs_rec(instance)? + || self.assert(instance)? + || self.dtyp_dec_item()? + || self.dtyp_decs_item()? + { + Parsed::Items + } else if self.check_sat() { + Parsed::CheckSat + } else if self.get_model() { + Parsed::GetModel + } else if self.get_unsat_core() { + Parsed::GetUnsatCore + } else if self.get_proof() { + Parsed::GetProof + } else if self.exit() { + Parsed::Exit + } else if self.reset() { + Parsed::Reset + } else if let Some(blah) = self.echo()? { + println!("{}", blah); + Parsed::Items + } else { + bail!(self.error_here("expected top-level item")) + }; + self.ws_cmt(); + self.tag(")")?; + self.ws_cmt(); + debug_assert!(self.cxt.term_stack.is_empty()); + debug_assert!(self.cxt.mem.is_empty()); + if res != Parsed::Items { + return Ok(res); + } + } + + debug_assert!(self.cxt.term_stack.is_empty()); + debug_assert!(self.cxt.mem.is_empty()); + + Ok(res) + } +} diff --git a/src/parse/ptterms.rs b/src/parse/ptterms.rs index 513989bb..4c89996a 100644 --- a/src/parse/ptterms.rs +++ b/src/parse/ptterms.rs @@ -1,483 +1,452 @@ //! Terms built by the parser before constructing the actual terms. -use common::* ; +use common::*; /// Boolean combination of top terms used by the parser. #[derive(Clone)] pub enum PTTerms { - And( Vec ), - Or( Vec ), - NTTerm( TTerm ), - TTerm( TTerm ), + And(Vec), + Or(Vec), + NTTerm(TTerm), + TTerm(TTerm), } impl PTTerms { - /// Type of the top terms. - pub fn typ(& self) -> Typ { - match * self { - PTTerms::And(_) | - PTTerms::Or(_) | - PTTerms::NTTerm(_) => typ::bool(), - PTTerms::TTerm(ref tterm) => tterm.typ(), - } - } - - /// True if the top term is true. - pub fn is_true(& self) -> bool { - if let PTTerms::TTerm(ref tterm) = * self { - tterm.is_true() - } else { - false - } - } - /// True if the top term is false. - pub fn is_false(& self) -> bool { - if let PTTerms::TTerm(ref tterm) = * self { - tterm.is_false() - } else { - false - } - } - - /// The top term true. - pub fn tru() -> Self { - PTTerms::TTerm( TTerm::tru() ) - } - /// The top term false. - pub fn fls() -> Self { - PTTerms::TTerm( TTerm::fls() ) - } - - /// Total substitution over top terms. - /// - /// # TODO - /// - /// - eliminate recursion - pub fn subst_total( - & self, subst: & VarMap - ) -> Res { - let res = match self { - PTTerms::Or(ref kids) => { - let mut nu_kids = Vec::with_capacity( kids.len() ) ; - for kid in kids { - nu_kids.push( kid.subst_total(subst) ? ) - } - PTTerms::or(nu_kids) - }, - PTTerms::And(ref kids) => { - let mut nu_kids = Vec::with_capacity( kids.len() ) ; - for kid in kids { - nu_kids.push( kid.subst_total(subst) ? ) - } - PTTerms::and(nu_kids) - }, - - PTTerms::NTTerm(ref tterm) => { - if let Some(term) = tterm.term() { - if let Some(var) = term.var_idx() { - return Ok( PTTerms::not(subst[var].clone()) ? ) - } - } - PTTerms::NTTerm( tterm.subst_total(subst) ? ) - }, - - PTTerms::TTerm(ref tterm) => { - if let Some(term) = tterm.term() { - if let Some(var) = term.var_idx() { - return Ok( subst[var].clone() ) - } + /// Type of the top terms. + pub fn typ(&self) -> Typ { + match *self { + PTTerms::And(_) | PTTerms::Or(_) | PTTerms::NTTerm(_) => typ::bool(), + PTTerms::TTerm(ref tterm) => tterm.typ(), } - PTTerms::TTerm( tterm.subst_total(subst) ? ) - }, - } ; - Ok(res) - } - - pub fn and(mut tterms: Vec) -> Self { - use std::iter::Extend ; - debug_assert!( ! tterms.is_empty() ) ; - let mut cnt = 0 ; - while cnt < tterms.len() { - if tterms[cnt].is_true() { - tterms.swap_remove(cnt) ; - } else if tterms[cnt].is_false() { - return PTTerms::fls() - } else if let PTTerms::And(_) = tterms[cnt] { - let and = tterms.swap_remove(cnt) ; - if let PTTerms::And(tts) = and { - tterms.extend(tts) - } else { - unreachable!() - } - } else { - cnt += 1 - } } - match tterms.len() { - 0 => PTTerms::tru(), - 1 => tterms.pop().unwrap(), - _ => PTTerms::And(tterms), + /// True if the top term is true. + pub fn is_true(&self) -> bool { + if let PTTerms::TTerm(ref tterm) = *self { + tterm.is_true() + } else { + false + } } - } - - pub fn or(mut tterms: Vec) -> Self { - use std::iter::Extend ; - debug_assert!( ! tterms.is_empty() ) ; - let mut cnt = 0 ; - - while cnt < tterms.len() { - if tterms[cnt].is_false() { - tterms.swap_remove(cnt) ; - } else if tterms[cnt].is_true() { - return PTTerms::tru() - } else if let PTTerms::Or(_) = tterms[cnt] { - let or = tterms.swap_remove(cnt) ; - if let PTTerms::Or(tts) = or { - tterms.extend(tts) + /// True if the top term is false. + pub fn is_false(&self) -> bool { + if let PTTerms::TTerm(ref tterm) = *self { + tterm.is_false() } else { - unreachable!() + false } - } else { - cnt += 1 - } } - match tterms.len() { - 0 => PTTerms::fls(), - 1 => tterms.pop().unwrap(), - _ => PTTerms::Or(tterms), + /// The top term true. + pub fn tru() -> Self { + PTTerms::TTerm(TTerm::tru()) } - } - - pub fn not(mut tterms: PTTerms) -> Res { - enum Frame { - And( Vec, Vec ), - Or( Vec, Vec ), + /// The top term false. + pub fn fls() -> Self { + PTTerms::TTerm(TTerm::fls()) } - let mut stack: Vec = vec![] ; - 'go_down: loop { + /// Total substitution over top terms. + /// + /// # TODO + /// + /// - eliminate recursion + pub fn subst_total(&self, subst: &VarMap) -> Res { + let res = match self { + PTTerms::Or(ref kids) => { + let mut nu_kids = Vec::with_capacity(kids.len()); + for kid in kids { + nu_kids.push(kid.subst_total(subst)?) + } + PTTerms::or(nu_kids) + } + PTTerms::And(ref kids) => { + let mut nu_kids = Vec::with_capacity(kids.len()); + for kid in kids { + nu_kids.push(kid.subst_total(subst)?) + } + PTTerms::and(nu_kids) + } - tterms = match tterms { - PTTerms::And(mut args) => if let Some(first) = args.pop() { - tterms = first ; - args.reverse() ; - let done = Vec::with_capacity( args.len() + 1 ) ; - stack.push( Frame::Or(args, done) ) ; - continue 'go_down - } else { - bail!("nullary conjunction") - }, - - PTTerms::Or(mut args) => if let Some(first) = args.pop() { - tterms = first ; - args.reverse() ; - let done = Vec::with_capacity( args.len() + 1 ) ; - stack.push( Frame::And(args, done) ) ; - continue 'go_down - } else { - bail!("nullary disjunction") - }, + PTTerms::NTTerm(ref tterm) => { + if let Some(term) = tterm.term() { + if let Some(var) = term.var_idx() { + return Ok(PTTerms::not(subst[var].clone())?); + } + } + PTTerms::NTTerm(tterm.subst_total(subst)?) + } - PTTerms::NTTerm(tt) => PTTerms::TTerm(tt), + PTTerms::TTerm(ref tterm) => { + if let Some(term) = tterm.term() { + if let Some(var) = term.var_idx() { + return Ok(subst[var].clone()); + } + } + PTTerms::TTerm(tterm.subst_total(subst)?) + } + }; + Ok(res) + } - PTTerms::TTerm(tt) => if tt.is_true() { - PTTerms::tterm( TTerm::fls() ) - } else if tt.is_false() { - PTTerms::tterm( TTerm::tru() ) - } else { - PTTerms::NTTerm(tt) - }, - - } ; - - 'go_up: loop { - match stack.pop() { - Some( Frame::And(mut to_do, mut done) ) => { - done.push(tterms) ; - if let Some(tt) = to_do.pop() { - stack.push( Frame::And(to_do, done) ) ; - tterms = tt ; - continue 'go_down + pub fn and(mut tterms: Vec) -> Self { + use std::iter::Extend; + debug_assert!(!tterms.is_empty()); + let mut cnt = 0; + while cnt < tterms.len() { + if tterms[cnt].is_true() { + tterms.swap_remove(cnt); + } else if tterms[cnt].is_false() { + return PTTerms::fls(); + } else if let PTTerms::And(_) = tterms[cnt] { + let and = tterms.swap_remove(cnt); + if let PTTerms::And(tts) = and { + tterms.extend(tts) + } else { + unreachable!() + } } else { - tterms = Self::and(done) ; - continue 'go_up + cnt += 1 } - }, - Some( Frame::Or(mut to_do, mut done) ) => { - done.push(tterms) ; - if let Some(tt) = to_do.pop() { - stack.push( Frame::Or(to_do, done) ) ; - tterms = tt ; - continue 'go_down + } + + match tterms.len() { + 0 => PTTerms::tru(), + 1 => tterms.pop().unwrap(), + _ => PTTerms::And(tterms), + } + } + + pub fn or(mut tterms: Vec) -> Self { + use std::iter::Extend; + debug_assert!(!tterms.is_empty()); + let mut cnt = 0; + + while cnt < tterms.len() { + if tterms[cnt].is_false() { + tterms.swap_remove(cnt); + } else if tterms[cnt].is_true() { + return PTTerms::tru(); + } else if let PTTerms::Or(_) = tterms[cnt] { + let or = tterms.swap_remove(cnt); + if let PTTerms::Or(tts) = or { + tterms.extend(tts) + } else { + unreachable!() + } } else { - tterms = Self::or(done) ; - continue 'go_up + cnt += 1 } - }, - None => break 'go_down Ok(tterms) } - } - } - } - - pub fn tterm(tterm: TTerm) -> Self { - PTTerms::TTerm( tterm ) - } - - /// True if `PTTerms` does not contain a non-negated predicate. - pub fn is_legal_lhs(& self) -> bool { - let mut to_do = Vec::with_capacity(37) ; - to_do.push(self) ; - - while let Some(ptterm) = to_do.pop() { - match * ptterm { - PTTerms::And(ref args) => for arg in args { - to_do.push(arg) - }, - PTTerms::Or(ref args) => for arg in args { - to_do.push(arg) - }, - PTTerms::NTTerm(_) => (), - PTTerms::TTerm(ref term) => if term.pred().is_some() { - return false - }, - } + match tterms.len() { + 0 => PTTerms::fls(), + 1 => tterms.pop().unwrap(), + _ => PTTerms::Or(tterms), + } } - true - } - - pub fn into_clauses(self) -> Res< Vec< (Vec, TTerm) > > { - match self { - PTTerms::TTerm(tterm) => Ok( - vec![ (vec![], tterm) ] - ), - PTTerms::NTTerm(tterm) => Ok( - vec![ (vec![ tterm ], TTerm::fls()) ] - ), - - PTTerms::Or(ptterms) => { - let mut lhs = Vec::with_capacity( ptterms.len() ) ; - let mut multipliers = Vec::with_capacity(3) ; - let mut rhs = None ; - - for ptt in ptterms { - match ptt { - PTTerms::TTerm(tt) => if let TTerm::T(term) = tt { - lhs.push( TTerm::T( term::not(term) ) ) - } else if rhs.is_none() { - rhs = Some( vec![ tt ] ) - } else { - bail!("ill-formed horn clause (or, 1)") - }, - - PTTerms::NTTerm(tt) => lhs.push(tt), - - PTTerms::And(ptts) => { - let mut tts = Vec::with_capacity( ptts.len() ) ; - let mut positive_preds = None ; - for ptt in ptts { - match ptt { - PTTerms::NTTerm(tterm) => tts.push(tterm), - PTTerms::TTerm(tterm) => match tterm { - TTerm::T(term) => tts.push( TTerm::T(term::not(term)) ), - tterm => { - let positive_preds = positive_preds.get_or_insert_with( - || Vec::with_capacity(7) - ) ; - positive_preds.push( tterm ) - }, - }, - ptt => if let Some(term) = ptt.to_term() ? { - tts.push( TTerm::T( term::not(term) ) ) - } else { - bail!("ill-formed horn clause (or, 2)") - }, + pub fn not(mut tterms: PTTerms) -> Res { + enum Frame { + And(Vec, Vec), + Or(Vec, Vec), + } + let mut stack: Vec = vec![]; + + 'go_down: loop { + tterms = match tterms { + PTTerms::And(mut args) => if let Some(first) = args.pop() { + tterms = first; + args.reverse(); + let done = Vec::with_capacity(args.len() + 1); + stack.push(Frame::Or(args, done)); + continue 'go_down; + } else { + bail!("nullary conjunction") + }, + + PTTerms::Or(mut args) => if let Some(first) = args.pop() { + tterms = first; + args.reverse(); + let done = Vec::with_capacity(args.len() + 1); + stack.push(Frame::And(args, done)); + continue 'go_down; + } else { + bail!("nullary disjunction") + }, + + PTTerms::NTTerm(tt) => PTTerms::TTerm(tt), + + PTTerms::TTerm(tt) => if tt.is_true() { + PTTerms::tterm(TTerm::fls()) + } else if tt.is_false() { + PTTerms::tterm(TTerm::tru()) + } else { + PTTerms::NTTerm(tt) + }, + }; + + 'go_up: loop { + match stack.pop() { + Some(Frame::And(mut to_do, mut done)) => { + done.push(tterms); + if let Some(tt) = to_do.pop() { + stack.push(Frame::And(to_do, done)); + tterms = tt; + continue 'go_down; + } else { + tterms = Self::and(done); + continue 'go_up; + } + } + Some(Frame::Or(mut to_do, mut done)) => { + done.push(tterms); + if let Some(tt) = to_do.pop() { + stack.push(Frame::Or(to_do, done)); + tterms = tt; + continue 'go_down; + } else { + tterms = Self::or(done); + continue 'go_up; + } + } + None => break 'go_down Ok(tterms), } - } - if let Some(pos_preds) = positive_preds { - tts.extend( pos_preds ) ; - rhs = Some( tts ) - } else { - multipliers.push(tts) - } - }, - - _ => bail!("ecountered normalization issue (or, 3)"), - } + } } + } - let nu_lhs = if multipliers.len() <= 2 { - - let mut nu_lhs = Vec::with_capacity( - multipliers.len() * 2 - ) ; - nu_lhs.push(lhs) ; - let mut tmp_lhs = Vec::with_capacity(nu_lhs.len()) ; - for mut vec in multipliers { - if let Some(last) = vec.pop() { - tmp_lhs.clear() ; - for tterm in vec { - for lhs in & nu_lhs { - let mut lhs = lhs.clone() ; - lhs.push( tterm.clone() ) ; - tmp_lhs.push( lhs ) - } - } - for mut lhs in nu_lhs.drain(0..) { - lhs.push(last.clone()) ; - tmp_lhs.push( lhs ) - } - ::std::mem::swap(& mut nu_lhs, & mut tmp_lhs) + pub fn tterm(tterm: TTerm) -> Self { + PTTerms::TTerm(tterm) + } + + /// True if `PTTerms` does not contain a non-negated predicate. + pub fn is_legal_lhs(&self) -> bool { + let mut to_do = Vec::with_capacity(37); + to_do.push(self); + + while let Some(ptterm) = to_do.pop() { + match *ptterm { + PTTerms::And(ref args) => for arg in args { + to_do.push(arg) + }, + PTTerms::Or(ref args) => for arg in args { + to_do.push(arg) + }, + PTTerms::NTTerm(_) => (), + PTTerms::TTerm(ref term) => if term.pred().is_some() { + return false; + }, } - } + } - nu_lhs + true + } - } else { + pub fn into_clauses(self) -> Res, TTerm)>> { + match self { + PTTerms::TTerm(tterm) => Ok(vec![(vec![], tterm)]), + PTTerms::NTTerm(tterm) => Ok(vec![(vec![tterm], TTerm::fls())]), + + PTTerms::Or(ptterms) => { + let mut lhs = Vec::with_capacity(ptterms.len()); + let mut multipliers = Vec::with_capacity(3); + let mut rhs = None; + + for ptt in ptterms { + match ptt { + PTTerms::TTerm(tt) => if let TTerm::T(term) = tt { + lhs.push(TTerm::T(term::not(term))) + } else if rhs.is_none() { + rhs = Some(vec![tt]) + } else { + bail!("ill-formed horn clause (or, 1)") + }, + + PTTerms::NTTerm(tt) => lhs.push(tt), + + PTTerms::And(ptts) => { + let mut tts = Vec::with_capacity(ptts.len()); + let mut positive_preds = None; + for ptt in ptts { + match ptt { + PTTerms::NTTerm(tterm) => tts.push(tterm), + PTTerms::TTerm(tterm) => match tterm { + TTerm::T(term) => tts.push(TTerm::T(term::not(term))), + tterm => { + let positive_preds = positive_preds + .get_or_insert_with(|| Vec::with_capacity(7)); + positive_preds.push(tterm) + } + }, + ptt => if let Some(term) = ptt.to_term()? { + tts.push(TTerm::T(term::not(term))) + } else { + bail!("ill-formed horn clause (or, 2)") + }, + } + } + if let Some(pos_preds) = positive_preds { + tts.extend(pos_preds); + rhs = Some(tts) + } else { + multipliers.push(tts) + } + } + + _ => bail!("ecountered normalization issue (or, 3)"), + } + } - for disj in multipliers { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - for tterm in disj { - if let TTerm::T(term) = tterm { - nu_disj.push(term) - } else { - bail!("error during clause conversion") - } - } - lhs.push( - TTerm::T( term::or(nu_disj) ) - ) - } - vec![ lhs ] - - } ; - - if let Some(rhs) = rhs { - let mut res = Vec::with_capacity( rhs.len() ) ; - let mut rhs = rhs.into_iter() ; - if let Some(last) = rhs.next() { - for rhs in rhs { - for lhs in & nu_lhs { - res.push( (lhs.clone(), rhs.clone()) ) - } + let nu_lhs = if multipliers.len() <= 2 { + let mut nu_lhs = Vec::with_capacity(multipliers.len() * 2); + nu_lhs.push(lhs); + let mut tmp_lhs = Vec::with_capacity(nu_lhs.len()); + for mut vec in multipliers { + if let Some(last) = vec.pop() { + tmp_lhs.clear(); + for tterm in vec { + for lhs in &nu_lhs { + let mut lhs = lhs.clone(); + lhs.push(tterm.clone()); + tmp_lhs.push(lhs) + } + } + for mut lhs in nu_lhs.drain(0..) { + lhs.push(last.clone()); + tmp_lhs.push(lhs) + } + ::std::mem::swap(&mut nu_lhs, &mut tmp_lhs) + } + } + + nu_lhs + } else { + for disj in multipliers { + let mut nu_disj = Vec::with_capacity(disj.len()); + for tterm in disj { + if let TTerm::T(term) = tterm { + nu_disj.push(term) + } else { + bail!("error during clause conversion") + } + } + lhs.push(TTerm::T(term::or(nu_disj))) + } + vec![lhs] + }; + + if let Some(rhs) = rhs { + let mut res = Vec::with_capacity(rhs.len()); + let mut rhs = rhs.into_iter(); + if let Some(last) = rhs.next() { + for rhs in rhs { + for lhs in &nu_lhs { + res.push((lhs.clone(), rhs.clone())) + } + } + for lhs in nu_lhs { + res.push((lhs, last.clone())) + } + } + Ok(res) + } else { + Ok(nu_lhs.into_iter().map(|lhs| (lhs, TTerm::fls())).collect()) + } } - for lhs in nu_lhs { - res.push((lhs, last.clone())) + + PTTerms::And(ppterms) => { + let mut clauses = Vec::with_capacity(ppterms.len()); + for ppt in ppterms { + clauses.extend(ppt.into_clauses()?) + // ^^^^^^^^^^^^^^^^ + // This is a recursive call, but there can be no other rec call + // inside it because + // + // - there can be no `PTTerms::And` inside a `PTTerms::And` by + // construction + // - this is the only place `into_clauses` calls itself. + } + Ok(clauses) } - } - Ok(res) - } else { - Ok( - nu_lhs.into_iter().map( - |lhs| (lhs, TTerm::fls()) - ).collect() - ) } - }, - - PTTerms::And(ppterms) => { - let mut clauses = Vec::with_capacity(ppterms.len()) ; - for ppt in ppterms { - clauses.extend( ppt.into_clauses() ? ) - // ^^^^^^^^^^^^^^^^ - // This is a recursive call, but there can be no other rec call - // inside it because - // - // - there can be no `PTTerms::And` inside a `PTTerms::And` by - // construction - // - this is the only place `into_clauses` calls itself. - } - Ok(clauses) - }, - } - } - - /// Transforms a parser's combination of top terms into a term, if possible. - pub fn to_term(& self) -> Res> { - let mut stack = Vec::with_capacity(17) ; - - let mut ptterm = self ; - - 'go_down: loop { - - let mut term = match * ptterm { - PTTerms::And(ref args) => { - let mut args = args.iter() ; - if let Some(head) = args.next() { - stack.push((Op::And, args, vec![])) ; - ptterm = head ; - continue 'go_down - } else { - bail!("illegal nullary conjunction") - } - }, - PTTerms::Or(ref args) => { - let mut args = args.iter() ; - if let Some(head) = args.next() { - stack.push((Op::Or, args, vec![])) ; - ptterm = head ; - continue 'go_down - } else { - bail!("illegal nullary conjunction") - } - }, - PTTerms::TTerm(ref tterm) => if let Some(term) = tterm.term() { - term.clone() - } else { - return Ok(None) - }, - PTTerms::NTTerm(ref tterm) => if let Some(term) = tterm.term() { - term::not( term.clone() ) - } else { - return Ok(None) - }, - } ; - - 'go_up: loop { - if let Some((op, mut to_do, mut done)) = stack.pop() { - done.push(term) ; - if let Some(next) = to_do.next() { - stack.push( (op, to_do, done) ) ; - ptterm = next ; - continue 'go_down - } else { - term = term::app(op, done) ; - continue 'go_up - } - } else { - break 'go_down Ok( Some(term) ) - } - } - } - } - - pub fn print(& self, pref: & str) { - match * self { - PTTerms::And(ref args) => { - println!("{}(and", pref) ; - for arg in args { - arg.print(& format!("{} ", pref)) + /// Transforms a parser's combination of top terms into a term, if possible. + pub fn to_term(&self) -> Res> { + let mut stack = Vec::with_capacity(17); + + let mut ptterm = self; + + 'go_down: loop { + let mut term = match *ptterm { + PTTerms::And(ref args) => { + let mut args = args.iter(); + if let Some(head) = args.next() { + stack.push((Op::And, args, vec![])); + ptterm = head; + continue 'go_down; + } else { + bail!("illegal nullary conjunction") + } + } + PTTerms::Or(ref args) => { + let mut args = args.iter(); + if let Some(head) = args.next() { + stack.push((Op::Or, args, vec![])); + ptterm = head; + continue 'go_down; + } else { + bail!("illegal nullary conjunction") + } + } + PTTerms::TTerm(ref tterm) => if let Some(term) = tterm.term() { + term.clone() + } else { + return Ok(None); + }, + PTTerms::NTTerm(ref tterm) => if let Some(term) = tterm.term() { + term::not(term.clone()) + } else { + return Ok(None); + }, + }; + + 'go_up: loop { + if let Some((op, mut to_do, mut done)) = stack.pop() { + done.push(term); + if let Some(next) = to_do.next() { + stack.push((op, to_do, done)); + ptterm = next; + continue 'go_down; + } else { + term = term::app(op, done); + continue 'go_up; + } + } else { + break 'go_down Ok(Some(term)); + } + } } - println!("{})", pref) - }, - PTTerms::Or(ref args) => { - println!("{}(or", pref) ; - for arg in args { - arg.print(& format!("{} ", pref)) + } + + pub fn print(&self, pref: &str) { + match *self { + PTTerms::And(ref args) => { + println!("{}(and", pref); + for arg in args { + arg.print(&format!("{} ", pref)) + } + println!("{})", pref) + } + PTTerms::Or(ref args) => { + println!("{}(or", pref); + for arg in args { + arg.print(&format!("{} ", pref)) + } + println!("{})", pref) + } + PTTerms::NTTerm(ref arg) => println!("{}(not {})", pref, arg), + PTTerms::TTerm(ref tt) => println!("{}{}", pref, tt), } - println!("{})", pref) - }, - PTTerms::NTTerm(ref arg) => println!( - "{}(not {})", pref, arg - ), - PTTerms::TTerm(ref tt) => { - println!("{}{}", pref, tt) - }, } - } -} \ No newline at end of file +} diff --git a/src/split.rs b/src/split.rs index bc4ba70f..bc2459c8 100644 --- a/src/split.rs +++ b/src/split.rs @@ -2,11 +2,8 @@ //! //! Used to reason separately on each positive/negative clause. -use common::* ; -use unsat_core::UnsatRes ; - - - +use common::*; +use unsat_core::UnsatRes; /// Splits the instance if asked to do so, and solves it. /// @@ -18,359 +15,309 @@ use unsat_core::UnsatRes ; /// /// Assumes the instance is **already pre-processed**. pub fn work( - real_instance: & Arc, _profiler: & Profiler -) -> Res< Option< Either > > { - let mut model = ConjCandidates::new() ; - - let mut splitter = Splitter::new( - real_instance.clone() - ) ; - - 'split_loop: while let Some(preproc_res) = { - if_not_bench! { - if let Some((clause, handled, total)) = splitter.info() { - log! { conf.stats || conf.split_step, || @info - "\n{}{}{}{}{} Splitting on negative clause #{} ({} of {})", - conf.emph("|"), - conf.happy("="), - conf.sad("="), - conf.happy("="), - conf.emph("|"), - clause, handled + 1, total + real_instance: &Arc, + _profiler: &Profiler, +) -> Res>> { + let mut model = ConjCandidates::new(); + + let mut splitter = Splitter::new(real_instance.clone()); + + 'split_loop: while let Some(preproc_res) = { + if_not_bench! { + if let Some((clause, handled, total)) = splitter.info() { + log! { conf.stats || conf.split_step, || @info + "\n{}{}{}{}{} Splitting on negative clause #{} ({} of {})", + conf.emph("|"), + conf.happy("="), + conf.sad("="), + conf.happy("="), + conf.emph("|"), + clause, handled + 1, total + } + if conf.split_step { + pause("to start sub-preprocessing", _profiler) ; + } + } } - if conf.split_step { - pause("to start sub-preprocessing", _profiler) ; + splitter.next_instance(&_profiler) + }? { + if let Some(prof) = splitter.profiler() { + print_stats("sub-preproc", prof) } - } - } - splitter.next_instance(& _profiler) - } ? { - if let Some(prof) = splitter.profiler() { - print_stats("sub-preproc", prof) - } - profile! { |_profiler| "sub-system(s)" => add 1 } - - let mut instance = match preproc_res { - Either::Left(instance) => instance, - Either::Right( - MaybeModel::Unsat - ) => unsat!{ - "by preprocessing" - }, - Either::Right( - MaybeModel::Model(this_model) - ) => { - log_info! { "sat by preproc\n\n" } - add_submodel( - & real_instance, & mut model, this_model - ) ; - - continue 'split_loop - }, - } ; - - match run_on(_profiler, instance, & model) ? { - - Some( Either::Left(this_model) ) => add_submodel( - & real_instance, & mut model, this_model - ), - - Some( Either::Right(reason) ) => return Ok( - Some( Either::Right(reason) ) - ), - - None => (), - } + profile! { |_profiler| "sub-system(s)" => add 1 } - } + let mut instance = match preproc_res { + Either::Left(instance) => instance, + Either::Right(MaybeModel::Unsat) => unsat!{ + "by preprocessing" + }, + Either::Right(MaybeModel::Model(this_model)) => { + log_info! { "sat by preproc\n\n" } + add_submodel(&real_instance, &mut model, this_model); - if conf.infer { - Ok( Some( Either::Left(model) ) ) - } else { - Ok(None) - } -} + continue 'split_loop; + } + }; + match run_on(_profiler, instance, &model)? { + Some(Either::Left(this_model)) => add_submodel(&real_instance, &mut model, this_model), + Some(Either::Right(reason)) => return Ok(Some(Either::Right(reason))), -/// Runs on a pre-processed instance. -pub fn run_on( - _profiler: & Profiler, mut instance: Arc, - model: & ConjCandidates -) -> Res< Option< Either > > { - if ! conf.infer { - if conf.split_step { - pause("to continue", _profiler) ; + None => (), + } + } + + if conf.infer { + Ok(Some(Either::Left(model))) } else { - log_info! { "Skipping learning..." } + Ok(None) } +} - return Ok(None) +/// Runs on a pre-processed instance. +pub fn run_on( + _profiler: &Profiler, + mut instance: Arc, + model: &ConjCandidates, +) -> Res>> { + if !conf.infer { + if conf.split_step { + pause("to continue", _profiler); + } else { + log_info! { "Skipping learning..." } + } - } else if conf.split_step { - pause("to start solving", _profiler) ; - } else { - log_info! { "Starting learning..." } - } + return Ok(None); + } else if conf.split_step { + pause("to start solving", _profiler); + } else { + log_info! { "Starting learning..." } + } - let res = profile!( + let res = profile!( |_profiler| wrap { run_teacher(instance.clone(), & model) } "solving" - ) ? ; - - match res { + )?; - TeachRes::Model(candidates) => { - log_info! { "sat\n\n" } - let mut this_model = instance.model_of(candidates) ? ; - if let Some(instance) = Arc::get_mut(& mut instance) { - instance.simplify_pred_defs(& mut this_model) ? - } + match res { + TeachRes::Model(candidates) => { + log_info! { "sat\n\n" } + let mut this_model = instance.model_of(candidates)?; + if let Some(instance) = Arc::get_mut(&mut instance) { + instance.simplify_pred_defs(&mut this_model)? + } - Ok( - Some( Either::Left(this_model) ) - ) - }, - - TeachRes::Unsat(reason) => Ok( - Some( Either::Right(reason) ) - ), + Ok(Some(Either::Left(this_model))) + } - } + TeachRes::Unsat(reason) => Ok(Some(Either::Right(reason))), + } } - - /// Adds a model for a subinstance to a partial model. -pub fn add_submodel( - instance: & Arc, model: & mut ConjCandidates, submodel: Model -) { - for (pred, tterms) in submodel { - if ! instance.is_known(pred) { - let conj = model.entry(pred).or_insert_with( - || vec![] - ) ; - match tterms.bool() { - Some(true) => continue, - Some(false) => conj.clear(), - None => (), - } - - if ! conj.iter().any( - |tts| tts == & tterms || tts.bool() == Some(false) - ) { - conj.push(tterms) - } +pub fn add_submodel(instance: &Arc, model: &mut ConjCandidates, submodel: Model) { + for (pred, tterms) in submodel { + if !instance.is_known(pred) { + let conj = model.entry(pred).or_insert_with(|| vec![]); + match tterms.bool() { + Some(true) => continue, + Some(false) => conj.clear(), + None => (), + } + + if !conj + .iter() + .any(|tts| tts == &tterms || tts.bool() == Some(false)) + { + conj.push(tterms) + } + } } - } } - - /// Runs the teacher on an instance. -pub fn run_teacher( - instance: Arc, - model: & ConjCandidates, -) -> Res< TeachRes > { - let teacher_profiler = Profiler::new() ; - let solve_res = ::teacher::start_class( - instance, model, & teacher_profiler - ) ; - print_stats("teacher", teacher_profiler) ; - solve_res +pub fn run_teacher(instance: Arc, model: &ConjCandidates) -> Res { + let teacher_profiler = Profiler::new(); + let solve_res = ::teacher::start_class(instance, model, &teacher_profiler); + print_stats("teacher", teacher_profiler); + solve_res } - - - /// Creates new instances by splitting positive/negative clauses. pub struct Splitter { - /// The instance we're working on. - instance: Arc, - /// Clauses to look at separately. - /// - /// Indices refer to `self.instance`. - /// - /// `Right(once)` means that this splitting is inactive, and `next_instance` - /// will return `self.instance` if `! once` and `None` otherwise. - clauses: Either, bool>, - /// Total number of clauses considered. - clause_count: usize, - /// Negative clauses for which we already have a solution. - prev_clauses: ClsSet, - /// Profiler. - _profiler: Option, + /// The instance we're working on. + instance: Arc, + /// Clauses to look at separately. + /// + /// Indices refer to `self.instance`. + /// + /// `Right(once)` means that this splitting is inactive, and `next_instance` + /// will return `self.instance` if `! once` and `None` otherwise. + clauses: Either, bool>, + /// Total number of clauses considered. + clause_count: usize, + /// Negative clauses for which we already have a solution. + prev_clauses: ClsSet, + /// Profiler. + _profiler: Option, } impl Splitter { - - /// Constructor. - pub fn new(instance: Arc) -> Self { - let (clauses, clause_count) = if conf.split - && instance.neg_clauses().len() > 1 { - // We want the predicates that appear in the most lhs last (since - // we're popping). - let mut clauses: Vec<_> = instance.neg_clauses().iter().map( - |c| ( - * c, - if conf.preproc.split_sort { - instance[* c].lhs_preds().iter().fold( - 0, | - mut sum, (pred, _) - | { - - for clause in instance.clauses_of(* pred).0 { - if instance[* clause].rhs().is_some() { - sum += 1 - } + /// Constructor. + pub fn new(instance: Arc) -> Self { + let (clauses, clause_count) = if conf.split && instance.neg_clauses().len() > 1 { + // We want the predicates that appear in the most lhs last (since + // we're popping). + let mut clauses: Vec<_> = instance + .neg_clauses() + .iter() + .map(|c| { + ( + *c, + if conf.preproc.split_sort { + instance[*c] + .lhs_preds() + .iter() + .fold(0, |mut sum, (pred, _)| { + for clause in instance.clauses_of(*pred).0 { + if instance[*clause].rhs().is_some() { + sum += 1 + } + } + + for clause in instance.clauses_of(*pred).1 { + if instance[*clause].lhs_preds().is_empty() { + // Positive clauses are bad. + sum = 0; + break; + } else { + // sum -= ::std::cmp::min(sum, 1) + } + } + + sum + }) + } else { + -(**c as isize) + }, + ) + }).collect(); + + clauses.sort_unstable_by(|&(c_1, count_1), &(c_2, count_2)| { + if instance[c_1].is_strict_neg() && !instance[c_2].is_strict_neg() { + ::std::cmp::Ordering::Greater + } else if !instance[c_1].is_strict_neg() && instance[c_2].is_strict_neg() { + ::std::cmp::Ordering::Less + } else if instance[c_1].from_unrolling && !instance[c_2].from_unrolling { + ::std::cmp::Ordering::Greater + } else if !instance[c_1].from_unrolling && instance[c_2].from_unrolling { + ::std::cmp::Ordering::Less + } else { + count_1.cmp(&count_2) } - - for clause in instance.clauses_of(* pred).1 { - if instance[* clause].lhs_preds().is_empty() { - // Positive clauses are bad. - sum = 0 ; - break - } else { - // sum -= ::std::cmp::min(sum, 1) - } - } - - sum - } - ) - } else { - - (* * c as isize) - } - ) - ).collect() ; - - clauses.sort_unstable_by( - |& (c_1, count_1), & (c_2, count_2)| { - if instance[c_1].is_strict_neg() - && ! instance[c_2].is_strict_neg() { - ::std::cmp::Ordering::Greater - } else if ! instance[c_1].is_strict_neg() - && instance[c_2].is_strict_neg() { - ::std::cmp::Ordering::Less - } else if instance[c_1].from_unrolling - && ! instance[c_2].from_unrolling { - ::std::cmp::Ordering::Greater - } else if ! instance[c_1].from_unrolling - && instance[c_2].from_unrolling { - ::std::cmp::Ordering::Less - } else { - count_1.cmp(& count_2) - } + }); + + // if_verb! { + // if conf.preproc.split_sort { + // log_verb! { + // "sorted clauses:" + // } + // for & (clause, count) in clauses.iter() { + // log_verb! { "#{} ({})", clause, count } + // log_debug! { + // "{}", instance[clause].to_string_info(instance.preds()).unwrap() + // } + // } + // } + // } + + let clauses: Vec<_> = clauses + .into_iter() + .map( + |(c, _)| c, // .filter_map( + // |(c,_)| if instance[c].from_unrolling { + // Some(c) + // } else { + // Some(c) + // } + ).collect(); + + let len = clauses.len(); + if len <= 1 { + (Either::Right(false), len) + } else { + (Either::Left(clauses), len) + } + } else { + (Either::Right(false), 1) + }; + + Splitter { + instance, + clauses, + clause_count, + prev_clauses: ClsSet::new(), + _profiler: None, } - ) ; - - // if_verb! { - // if conf.preproc.split_sort { - // log_verb! { - // "sorted clauses:" - // } - // for & (clause, count) in clauses.iter() { - // log_verb! { "#{} ({})", clause, count } - // log_debug! { - // "{}", instance[clause].to_string_info(instance.preds()).unwrap() - // } - // } - // } - // } - - let clauses: Vec<_> = clauses.into_iter() - .map(|(c,_)| c - // .filter_map( - // |(c,_)| if instance[c].from_unrolling { - // Some(c) - // } else { - // Some(c) - // } - ).collect() ; - - let len = clauses.len() ; - if len <= 1 { - (Either::Right(false), len) - } else { - (Either::Left(clauses), len) - } - } else { - (Either::Right(false), 1) - } ; - - Splitter { - instance, clauses, clause_count, - prev_clauses: ClsSet::new(), _profiler: None, } - } - - - - - - + /// Retrieves the profiler. + pub fn profiler(&mut self) -> Option { + let mut res = None; + ::std::mem::swap(&mut res, &mut self._profiler); + res + } - /// Retrieves the profiler. - pub fn profiler(& mut self) -> Option { - let mut res = None ; - ::std::mem::swap(& mut res, & mut self._profiler) ; - res - } - - /// Returns the next clause to split on, the number of clauses already - /// treated and the total number of clauses to handle if active. - pub fn info(& self) -> Option<(ClsIdx, usize, usize)> { - match self.clauses { - Either::Left(ref clauses) => { - if let Some(clause) = clauses.last() { - let total = self.clause_count ; - let count = total - clauses.len() ; - Some((* clause, count, total)) - } else { - None + /// Returns the next clause to split on, the number of clauses already + /// treated and the total number of clauses to handle if active. + pub fn info(&self) -> Option<(ClsIdx, usize, usize)> { + match self.clauses { + Either::Left(ref clauses) => { + if let Some(clause) = clauses.last() { + let total = self.clause_count; + let count = total - clauses.len(); + Some((*clause, count, total)) + } else { + None + } + } + Either::Right(_) => None, } - }, - Either::Right(_) => None, } - } - - /// Returns the next instance to work on. - pub fn next_instance(& mut self, _prof: & Profiler) -> Res< - Option< Either, MaybeModel> > - > { - match self.clauses { - Either::Left(ref mut clauses) => if let Some(clause) = clauses.pop() { - let profiler = Profiler::new() ; - let preproc_res = profile! ( + + /// Returns the next instance to work on. + pub fn next_instance( + &mut self, + _prof: &Profiler, + ) -> Res, MaybeModel>>> { + match self.clauses { + Either::Left(ref mut clauses) => if let Some(clause) = clauses.pop() { + let profiler = Profiler::new(); + let preproc_res = profile! ( |_prof| wrap { preproc( self.instance.as_ref(), clause, & self.prev_clauses, & profiler ) } "sub-preproc" - ) ? ; - self.prev_clauses.insert(clause) ; - self._profiler = Some(profiler) ; - Ok( - Some( preproc_res.map_left(Arc::new) ) - ) - } else { - Ok(None) - }, - Either::Right(ref mut once) => if * once { - Ok(None) - } else { - * once = true ; - Ok( Some( Either::Left(self.instance.clone()) ) ) - } + )?; + self.prev_clauses.insert(clause); + self._profiler = Some(profiler); + Ok(Some(preproc_res.map_left(Arc::new))) + } else { + Ok(None) + }, + Either::Right(ref mut once) => if *once { + Ok(None) + } else { + *once = true; + Ok(Some(Either::Left(self.instance.clone()))) + }, + } } - } - } - - /// Applies pre-processing to a modified instance. /// /// Generates the instance obtained by removing all positive (if `pos`, @@ -379,21 +326,20 @@ impl Splitter { /// /// Fails in debug if the clause is not negative. fn preproc( - instance: & Instance, clause: ClsIdx, - prev_clauses: & ClsSet, profiler: & Profiler -) -> Res< Either>> { - - debug_assert! { - instance[clause].rhs().is_none() - } - - let instance = ::instance::preproc::work_on_split( - instance, clause, prev_clauses, profiler - ) ? ; - - if let Some(maybe_model) = instance.is_trivial_model() ? { - Ok( Either::Right(maybe_model) ) - } else { - Ok( Either::Left(instance) ) - } -} \ No newline at end of file + instance: &Instance, + clause: ClsIdx, + prev_clauses: &ClsSet, + profiler: &Profiler, +) -> Res>> { + debug_assert! { + instance[clause].rhs().is_none() + } + + let instance = ::instance::preproc::work_on_split(instance, clause, prev_clauses, profiler)?; + + if let Some(maybe_model) = instance.is_trivial_model()? { + Ok(Either::Right(maybe_model)) + } else { + Ok(Either::Left(instance)) + } +} diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index e8d7d9f5..12c68129 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -1,472 +1,435 @@ //! Handles example propagation. -use rsmt2::print::Expr2Smt ; - -use common::* ; -use data::{ Data, Sample } ; - - +use common::*; +use data::{Data, Sample}; /// Result of trying to force a sample positive/negative. pub enum ForceRes { - /// Failure. - None, - /// Sample was classified as positive. - Pos { sample: Sample, clause: ClsIdx }, - /// Sample was classified as negative. - Neg { sample: Sample, clause: ClsIdx }, + /// Failure. + None, + /// Sample was classified as positive. + Pos { sample: Sample, clause: ClsIdx }, + /// Sample was classified as negative. + Neg { sample: Sample, clause: ClsIdx }, } - - /// Propagates examples, tries to break implication constraints. pub struct Assistant { - /// Core, to communicate with the teacher. - // core: & 'a MsgCore, - /// Solver. - solver: Solver<()>, - /// Instance. - instance: Arc, - /// Positive constraints. - pos: PrdHMap< ClsSet >, - /// Negative constraints. - neg: PrdHMap< ClsSet >, - /// Profiler. - _profiler: Profiler, - /// True if we're using ADTs. - using_adts: bool, + /// Core, to communicate with the teacher. + // core: & 'a MsgCore, + /// Solver. + solver: Solver<()>, + /// Instance. + instance: Arc, + /// Positive constraints. + pos: PrdHMap, + /// Negative constraints. + neg: PrdHMap, + /// Profiler. + _profiler: Profiler, + /// True if we're using ADTs. + using_adts: bool, } impl Assistant { - - /// Constructor. - pub fn new( - instance: Arc, // core: & 'a MsgCore - ) -> Res { - let solver = conf.solver.spawn( - "assistant", (), & instance - ) ? ; - let _profiler = Profiler::new() ; - - let mut pos = PrdHMap::with_capacity( instance.preds().len() ) ; - let mut neg = PrdHMap::with_capacity( instance.preds().len() ) ; - - let mut pos_clauses = ClsSet::new() ; - let mut neg_clauses = ClsSet::new() ; - - let using_adts = dtyp::get_all().iter().next().is_some() ; - - macro_rules! add_clauses { - ($pred:expr) => ({ - if ! pos_clauses.is_empty() { - let mut clause_set = ClsSet::new() ; - ::std::mem::swap( & mut pos_clauses, & mut clause_set ) ; - let prev = pos.insert($pred, clause_set) ; - debug_assert! { prev.is_none() } + /// Constructor. + pub fn new(instance: Arc // core: & 'a MsgCore + ) -> Res { + let solver = conf.solver.spawn("assistant", (), &instance)?; + let _profiler = Profiler::new(); + + let mut pos = PrdHMap::with_capacity(instance.preds().len()); + let mut neg = PrdHMap::with_capacity(instance.preds().len()); + + let mut pos_clauses = ClsSet::new(); + let mut neg_clauses = ClsSet::new(); + + let using_adts = dtyp::get_all().iter().next().is_some(); + + macro_rules! add_clauses { + ($pred:expr) => {{ + if !pos_clauses.is_empty() { + let mut clause_set = ClsSet::new(); + ::std::mem::swap(&mut pos_clauses, &mut clause_set); + let prev = pos.insert($pred, clause_set); + debug_assert! { prev.is_none() } + } + if !neg_clauses.is_empty() { + let mut clause_set = ClsSet::new(); + ::std::mem::swap(&mut neg_clauses, &mut clause_set); + let prev = neg.insert($pred, clause_set); + debug_assert! { prev.is_none() } + } + }}; } - if ! neg_clauses.is_empty() { - let mut clause_set = ClsSet::new() ; - ::std::mem::swap( & mut neg_clauses, & mut clause_set ) ; - let prev = neg.insert($pred, clause_set) ; - debug_assert! { prev.is_none() } - } - }) ; - } - - for pred in instance.pred_indices() { - debug_assert! { pos_clauses.is_empty() } - debug_assert! { neg_clauses.is_empty() } - for clause in instance.clauses_of(pred).1 { - let clause = * clause ; - if instance[clause].lhs_preds().is_empty() { - let is_new = pos_clauses.insert(clause) ; - debug_assert! { is_new } - } - } - - for clause in instance.clauses_of(pred).0 { - let clause = * clause ; - if instance[clause].rhs().is_none() - && instance[clause].lhs_pred_apps_len() == 1 { - let is_new = neg_clauses.insert(clause) ; - debug_assert! { is_new } - } - } + for pred in instance.pred_indices() { + debug_assert! { pos_clauses.is_empty() } + debug_assert! { neg_clauses.is_empty() } - add_clauses!(pred) - } + for clause in instance.clauses_of(pred).1 { + let clause = *clause; + if instance[clause].lhs_preds().is_empty() { + let is_new = pos_clauses.insert(clause); + debug_assert! { is_new } + } + } - Ok( - Assistant { - // core, - solver, instance, pos, neg, _profiler, using_adts - } - ) - } - - /// Destroys the assistant. - pub fn finalize(mut self) -> Res { - self.solver.kill().chain_err( - || "While killing solver" - ) ? ; - Ok(self._profiler) - } - - /// Breaks implications. - pub fn break_implications( - & mut self, data: & mut Data - ) -> Res<()> { - if data.constraints.is_empty() { return Ok(()) } - - let (mut pos, mut neg) = ( Vec::new(), Vec::new() ) ; - // msg! { debug self.core => "breaking implications..." } - profile! { self "constraints received" => add data.constraints.len() } - - 'all_constraints: for cstr in CstrRange::zero_to( - data.constraints.len() - ) { - - // Can happen because of simplifications when propagating. - if cstr > data.constraints.len() { - break - } - if data.constraints[cstr].is_tautology() { - continue - } - - // debug! { - // " {}", data.constraints[cstr].string_do( - // self.instance.preds(), |s| s.to_string() - // ).unwrap() - // } - - let mut trivial = false ; - let mut rhs_false = false ; - let mut lhs_unknown = 0 ; - macro_rules! move_on { - (if trivial) => ( if trivial { move_on!() } ) ; - () => ({ - if trivial - || lhs_unknown == 0 - || rhs_false && lhs_unknown == 1 { - profile! { self "constraints broken" => add 1 } - } - // Discard the constraint, regardless of what will happen. - profile! { self tick "data" } - data.tautologize(cstr) ? ; - for (Sample { pred, args }, clause) in pos.drain(0..) { - data.add_pos(clause, pred, args) ; - } - for (Sample { pred, args }, clause) in neg.drain(0..) { - data.add_neg(clause, pred, args) ; - } - data.propagate() ? ; - profile! { self mark "data" } - continue 'all_constraints - }) ; - } - - if let Some( - & Sample { pred, ref args } - ) = data.constraints[cstr].rhs() { - match self.try_force(data, pred, args) ? { - ForceRes::None => (), - ForceRes::Pos { sample, clause } => { - pos.push( (sample, clause) ) ; - // Constraint is trivial, move on. - trivial = true - }, - ForceRes::Neg { sample, clause } => { - rhs_false = true ; - neg.push( (sample, clause) ) - }, - } - } - - // move_on!(if trivial) ; - - if let Some(lhs) = data.constraints[cstr].lhs() { - for (pred, samples) in lhs { - let mut lhs_trivial = true ; - for sample in samples { - match self.try_force(data, * pred, sample) ? { - ForceRes::None => { - lhs_unknown += 1 ; - lhs_trivial = false - }, - ForceRes::Pos { sample, clause } => pos.push( - (sample, clause) - ), - ForceRes::Neg { sample, clause } => { - neg.push( (sample, clause) ) ; - trivial = true ; - // Constraint is trivial, move on. - // break 'lhs - }, + for clause in instance.clauses_of(pred).0 { + let clause = *clause; + if instance[clause].rhs().is_none() && instance[clause].lhs_pred_apps_len() == 1 { + let is_new = neg_clauses.insert(clause); + debug_assert! { is_new } + } } - } - trivial = trivial || lhs_trivial - } - } else { - bail!("Illegal constraint") - } - move_on!() + add_clauses!(pred) + } + Ok(Assistant { + // core, + solver, + instance, + pos, + neg, + _profiler, + using_adts, + }) } - let (_pos_count, _neg_count) = data.pos_neg_count() ; - if ! data.pos.is_empty() { - profile! { self "positive examples generated" => add _pos_count } - } - if ! data.neg.is_empty() { - profile! { self "negative examples generated" => add _neg_count } + /// Destroys the assistant. + pub fn finalize(mut self) -> Res { + self.solver.kill().chain_err(|| "While killing solver")?; + Ok(self._profiler) } - Ok(()) - } - - /// Checks if a sample can be forced to anything. - /// - /// If it can't, return None. If it can, returns - /// - /// - `ForceRes::Pos` of a sample which, when forced positive, will force the - /// input sample to be classified positive. - /// - `ForceRes::Neg` of a sample which, when forced negative, will force the - /// input sample to be classified negative. - pub fn try_force( - & mut self, _data: & Data, pred: PrdIdx, vals: & VarVals - ) -> Res { - self.solver.comment_args( - format_args!("working on sample ({} {})", self.instance[pred], vals) - ) ? ; - - if let Some(clauses) = self.pos.get(& pred) { - - self.solver.comment("working on positive clauses") ? ; - - for clause_idx in clauses { - let clause_idx = * clause_idx ; - let clause = & self.instance[clause_idx] ; - if let Some((p, args)) = clause.rhs() { - debug_assert_eq! { pred, p } - debug_assert! { clause.lhs_preds().is_empty() } - - if self.using_adts { - smt::reset(& mut self.solver, & self.instance) ? - } else { - self.solver.push(1) ? - } - - clause.declare(& mut self.solver) ? ; - self.solver.assert( - & ConjWrap::new( clause.lhs_terms() ) - ) ? ; - - self.solver.assert( & ArgValEq::new(args, vals) ) ? ; - let sat = profile! { - self wrap { - self.solver.check_sat() ? - } "smt" - } ; - - if self.using_adts { - smt::reset(& mut self.solver, & self.instance) ? - } else { - self.solver.pop(1) ? - } - - if sat { - // msg! { debug self => " forcing positive" } - return Ok( - ForceRes::Pos { - sample: Sample { pred, args: vals.clone() }, - clause: clause_idx, - } - ) - } - } else { - bail!("inconsistent instance state") + /// Breaks implications. + pub fn break_implications(&mut self, data: &mut Data) -> Res<()> { + if data.constraints.is_empty() { + return Ok(()); } - } - } + let (mut pos, mut neg) = (Vec::new(), Vec::new()); + // msg! { debug self.core => "breaking implications..." } + profile! { self "constraints received" => add data.constraints.len() } + + 'all_constraints: for cstr in CstrRange::zero_to(data.constraints.len()) { + // Can happen because of simplifications when propagating. + if cstr > data.constraints.len() { + break; + } + if data.constraints[cstr].is_tautology() { + continue; + } - if let Some(clauses) = self.neg.get(& pred) { + // debug! { + // " {}", data.constraints[cstr].string_do( + // self.instance.preds(), |s| s.to_string() + // ).unwrap() + // } + + let mut trivial = false; + let mut rhs_false = false; + let mut lhs_unknown = 0; + macro_rules! move_on { + (if trivial) => { + if trivial { + move_on!() + } + }; + () => {{ + if trivial || lhs_unknown == 0 || rhs_false && lhs_unknown == 1 { + profile! { self "constraints broken" => add 1 } + } + // Discard the constraint, regardless of what will happen. + profile! { self tick "data" } + data.tautologize(cstr)?; + for (Sample { pred, args }, clause) in pos.drain(0..) { + data.add_pos(clause, pred, args); + } + for (Sample { pred, args }, clause) in neg.drain(0..) { + data.add_neg(clause, pred, args); + } + data.propagate()?; + profile! { self mark "data" } + continue 'all_constraints; + }}; + } - self.solver.comment("working on negative clauses") ? ; + if let Some(&Sample { pred, ref args }) = data.constraints[cstr].rhs() { + match self.try_force(data, pred, args)? { + ForceRes::None => (), + ForceRes::Pos { sample, clause } => { + pos.push((sample, clause)); + // Constraint is trivial, move on. + trivial = true + } + ForceRes::Neg { sample, clause } => { + rhs_false = true; + neg.push((sample, clause)) + } + } + } - for clause_idx in clauses { - let clause_idx = * clause_idx ; - let clause = & self.instance[clause_idx] ; - if let Some(argss) = clause.lhs_preds().get(& pred) { - let args = { - let mut argss = argss.iter() ; - if let Some(args) = argss.next() { - debug_assert! { argss.next().is_none() } - args + // move_on!(if trivial) ; + + if let Some(lhs) = data.constraints[cstr].lhs() { + for (pred, samples) in lhs { + let mut lhs_trivial = true; + for sample in samples { + match self.try_force(data, *pred, sample)? { + ForceRes::None => { + lhs_unknown += 1; + lhs_trivial = false + } + ForceRes::Pos { sample, clause } => pos.push((sample, clause)), + ForceRes::Neg { sample, clause } => { + neg.push((sample, clause)); + trivial = true; + // Constraint is trivial, move on. + // break 'lhs + } + } + } + trivial = trivial || lhs_trivial + } } else { - bail!("inconsistent instance state") + bail!("Illegal constraint") } - } ; - - if self.using_adts { - smt::reset(& mut self.solver, & self.instance) ? - } else { - self.solver.push(1) ? - } - - clause.declare(& mut self.solver) ? ; - self.solver.assert( - & ConjWrap::new( clause.lhs_terms() ) - ) ? ; - self.solver.assert( & ArgValEq::new(args, vals) ) ? ; - let sat = profile! { - self wrap { - self.solver.check_sat() ? - } "smt" - } ; - - if self.using_adts { - smt::reset(& mut self.solver, & self.instance) ? - } else { - self.solver.pop(1) ? - } - - if sat { - // msg! { debug self => " forcing negative" } - return Ok( - ForceRes::Neg { - sample: Sample { pred, args: vals.clone() }, - clause: clause_idx, - } - ) - } - } else { - bail!("inconsistent instance state") + + move_on!() + } + + let (_pos_count, _neg_count) = data.pos_neg_count(); + if !data.pos.is_empty() { + profile! { self "positive examples generated" => add _pos_count } + } + if !data.neg.is_empty() { + profile! { self "negative examples generated" => add _neg_count } } - } + Ok(()) } - Ok(ForceRes::None) - } + /// Checks if a sample can be forced to anything. + /// + /// If it can't, return None. If it can, returns + /// + /// - `ForceRes::Pos` of a sample which, when forced positive, will force the + /// input sample to be classified positive. + /// - `ForceRes::Neg` of a sample which, when forced negative, will force the + /// input sample to be classified negative. + pub fn try_force(&mut self, _data: &Data, pred: PrdIdx, vals: &VarVals) -> Res { + self.solver.comment_args(format_args!( + "working on sample ({} {})", + self.instance[pred], vals + ))?; + + if let Some(clauses) = self.pos.get(&pred) { + self.solver.comment("working on positive clauses")?; + + for clause_idx in clauses { + let clause_idx = *clause_idx; + let clause = &self.instance[clause_idx]; + if let Some((p, args)) = clause.rhs() { + debug_assert_eq! { pred, p } + debug_assert! { clause.lhs_preds().is_empty() } + + if self.using_adts { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.push(1)? + } + + clause.declare(&mut self.solver)?; + self.solver.assert(&ConjWrap::new(clause.lhs_terms()))?; + + self.solver.assert(&ArgValEq::new(args, vals))?; + let sat = profile! { + self wrap { + self.solver.check_sat() ? + } "smt" + }; + + if self.using_adts { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.pop(1)? + } + + if sat { + // msg! { debug self => " forcing positive" } + return Ok(ForceRes::Pos { + sample: Sample { + pred, + args: vals.clone(), + }, + clause: clause_idx, + }); + } + } else { + bail!("inconsistent instance state") + } + } + } + + if let Some(clauses) = self.neg.get(&pred) { + self.solver.comment("working on negative clauses")?; + + for clause_idx in clauses { + let clause_idx = *clause_idx; + let clause = &self.instance[clause_idx]; + if let Some(argss) = clause.lhs_preds().get(&pred) { + let args = { + let mut argss = argss.iter(); + if let Some(args) = argss.next() { + debug_assert! { argss.next().is_none() } + args + } else { + bail!("inconsistent instance state") + } + }; + + if self.using_adts { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.push(1)? + } + + clause.declare(&mut self.solver)?; + self.solver.assert(&ConjWrap::new(clause.lhs_terms()))?; + self.solver.assert(&ArgValEq::new(args, vals))?; + let sat = profile! { + self wrap { + self.solver.check_sat() ? + } "smt" + }; + + if self.using_adts { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.pop(1)? + } + + if sat { + // msg! { debug self => " forcing negative" } + return Ok(ForceRes::Neg { + sample: Sample { + pred, + args: vals.clone(), + }, + clause: clause_idx, + }); + } + } else { + bail!("inconsistent instance state") + } + } + } + Ok(ForceRes::None) + } } /// Wrapper around a conjunction for smt printing. struct ConjWrap<'a> { - /// Conjunction. - terms: & 'a TermSet, + /// Conjunction. + terms: &'a TermSet, } impl<'a> ConjWrap<'a> { - /// Constructor. - pub fn new(terms: & 'a TermSet) -> Self { - ConjWrap { terms } - } + /// Constructor. + pub fn new(terms: &'a TermSet) -> Self { + ConjWrap { terms } + } } -impl<'a> ::rsmt2::print::Expr2Smt<()> for ConjWrap<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> SmtRes<()> { - if self.terms.is_empty() { - write!(w, "true") ? - } else { - write!(w, "(and") ? ; - for term in self.terms { - write!(w, " ") ? ; - term.write(w, |w, var| var.default_write(w)) ? ; - } - write!(w, ")") ? +impl<'a> Expr2Smt<()> for ConjWrap<'a> { + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { + if self.terms.is_empty() { + write!(w, "true")? + } else { + write!(w, "(and")?; + for term in self.terms { + write!(w, " ")?; + term.write(w, |w, var| var.default_write(w))?; + } + write!(w, ")")? + } + Ok(()) } - Ok(()) - } } - /// Wrapper around some arguments and some values. /// /// Used to assert `(= arg[i] val[i])`. pub struct ArgValEq<'a> { - /// Arguments. - args: & 'a VarTerms, - /// Values. - vals: & 'a VarVals, + /// Arguments. + args: &'a VarTerms, + /// Values. + vals: &'a VarVals, } impl<'a> ArgValEq<'a> { - /// Constructor. - pub fn new(args: & 'a VarTerms, vals: & 'a VarVals) -> Self { - debug_assert_eq! { args.len(), vals.len() } - ArgValEq { args, vals } - } + /// Constructor. + pub fn new(args: &'a VarTerms, vals: &'a VarVals) -> Self { + debug_assert_eq! { args.len(), vals.len() } + ArgValEq { args, vals } + } } impl<'a> Expr2Smt<()> for ArgValEq<'a> { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> ::rsmt2::SmtRes<()> - where Writer: Write { - write!(w, "(and") ? ; - let mut unknown = 0 ; - - for (arg, val) in self.args.iter().zip( self.vals.iter() ) { - if ! val.is_known() { - unknown += 1 ; - continue - } - - match val.get() { - val::RVal::B(b) => { - write!(w, " ") ? ; - if ! b { - write!(w, "(not ") ? - } - arg.write( - w, |w, v| w.write_all( v.default_str().as_bytes() ) - ) ? ; - if ! b { - write!(w, ")") ? - } - }, - - val::RVal::I(ref i) => { - write!(w, " (= ") ? ; - arg.write( - w, |w, v| w.write_all( v.default_str().as_bytes() ) - ) ? ; - write!(w, " ") ? ; - int_to_smt!(w, i) ? ; - write!(w, ")") ? - }, - - val::RVal::R(ref r) => { - write!(w, " (= ") ? ; - arg.write( - w, |w, v| w.write_all( v.default_str().as_bytes() ) - ) ? ; - write!(w, " ") ? ; - rat_to_smt!(w, r) ? ; - write!(w, ")") ? - }, - - val::RVal::Array { .. } | - val::RVal::DTypNew { .. } => { - write!(w, " (= ") ? ; - arg.write( - w, |w, v| w.write_all( v.default_str().as_bytes() ) - ) ? ; - write!(w, " {})", val) ? - }, - - val::RVal::N(_) => (), - } - } + fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> ::rsmt2::SmtRes<()> + where + Writer: Write, + { + write!(w, "(and")?; + let mut unknown = 0; + + for (arg, val) in self.args.iter().zip(self.vals.iter()) { + if !val.is_known() { + unknown += 1; + continue; + } - if unknown == self.args.len() { - write!(w, " true") ? - } - write!(w, ")") ? ; - Ok(()) - } + match val.get() { + val::RVal::B(b) => { + write!(w, " ")?; + if !b { + write!(w, "(not ")? + } + arg.write(w, |w, v| w.write_all(v.default_str().as_bytes()))?; + if !b { + write!(w, ")")? + } + } + + val::RVal::I(ref i) => { + write!(w, " (= ")?; + arg.write(w, |w, v| w.write_all(v.default_str().as_bytes()))?; + write!(w, " ")?; + int_to_smt!(w, i)?; + write!(w, ")")? + } + + val::RVal::R(ref r) => { + write!(w, " (= ")?; + arg.write(w, |w, v| w.write_all(v.default_str().as_bytes()))?; + write!(w, " ")?; + rat_to_smt!(w, r)?; + write!(w, ")")? + } + + val::RVal::Array { .. } | val::RVal::DTypNew { .. } => { + write!(w, " (= ")?; + arg.write(w, |w, v| w.write_all(v.default_str().as_bytes()))?; + write!(w, " {})", val)? + } + + val::RVal::N(_) => (), + } + } + if unknown == self.args.len() { + write!(w, " true")? + } + write!(w, ")")?; + Ok(()) + } } diff --git a/src/teacher/cex_bias.rs b/src/teacher/cex_bias.rs index f67ee784..51785bce 100644 --- a/src/teacher/cex_bias.rs +++ b/src/teacher/cex_bias.rs @@ -1,377 +1,360 @@ //! Handles counterexample bias for the teacher. -use common::{ - *, smt::DisjArgs -} ; -use data::Data ; +use common::{smt::DisjArgs, *}; +use data::Data; use var_to::{ - terms::{ VarTermsMap, VarTermsSet }, - vals::VarValsSet, -} ; - + terms::{VarTermsMap, VarTermsSet}, + vals::VarValsSet, +}; /// Solver. -type Slvr = Solver< smt::FullParser > ; +type Slvr = Solver; /// #[derive(Default, Debug)] pub struct CexBias { - /// Activation literals for the predicate applications in the lhs of a - /// clause. - lhs_actlits: PrdHMap< VarTermsMap >, - /// LHS predicate applications that can't be forced positive. - lhs_non_pos: PrdHMap, + /// Activation literals for the predicate applications in the lhs of a + /// clause. + lhs_actlits: PrdHMap>, + /// LHS predicate applications that can't be forced positive. + lhs_non_pos: PrdHMap, } impl CexBias { - /// Constructor. - pub fn new() -> Self { - CexBias { - lhs_actlits: PrdHMap::new(), - lhs_non_pos: PrdHMap::new(), - } - } - - - - - - /// Generates activation literals forcing a bias in the clause. - /// - /// Assumes all variables are already declared. - pub fn apply( - & mut self, _profiler: & Profiler, solver: & mut Slvr, - clause: ClsIdx, instance: & Instance, data: & Data, - bias_only: bool, - ) -> Res< Vec<(Actlit, Bias)> > { - if ! conf.teacher.bias_cexs { - return Ok( vec![] ) - } - - macro_rules! clause { - () => ( instance[clause] ) ; + /// Constructor. + pub fn new() -> Self { + CexBias { + lhs_actlits: PrdHMap::new(), + lhs_non_pos: PrdHMap::new(), + } } - self.lhs_actlits.clear() ; - self.lhs_non_pos.clear() ; - - self.get_lhs_actlits(solver, clause, instance, data) ? ; + /// Generates activation literals forcing a bias in the clause. + /// + /// Assumes all variables are already declared. + pub fn apply( + &mut self, + _profiler: &Profiler, + solver: &mut Slvr, + clause: ClsIdx, + instance: &Instance, + data: &Data, + bias_only: bool, + ) -> Res> { + if !conf.teacher.bias_cexs { + return Ok(vec![]); + } + macro_rules! clause { + () => { + instance[clause] + }; + } - let rhs_actlit = self.get_rhs_actlit(solver, clause, instance, data) ? ; + self.lhs_actlits.clear(); + self.lhs_non_pos.clear(); + self.get_lhs_actlits(solver, clause, instance, data)?; - // Let's do this. - let mut actlits = vec![] ; + let rhs_actlit = self.get_rhs_actlit(solver, clause, instance, data)?; + // Let's do this. + let mut actlits = vec![]; - // If there's any positive samples at all, we can do something for the rhs - // (if any). - if ! self.lhs_actlits.is_empty() && clause!().rhs().is_some() && ( + // If there's any positive samples at all, we can do something for the rhs + // (if any). + if !self.lhs_actlits.is_empty() + && clause!().rhs().is_some() + && ( // Skip if we're only generating biased check-sat and there are // applications without any positive data. ! bias_only || self.lhs_non_pos.is_empty() ) { - self.bias_left( - _profiler, solver, & mut actlits, bias_only - ) ? - } - - - // If there's no rhs or it has negative samples we can do something for the - // lhs. - if clause!().rhs().is_none() || rhs_actlit.is_some() { - - if self.lhs_non_pos.is_empty() { - - self.exhaustive_bias_right( - _profiler, solver, & mut actlits, instance, & rhs_actlit - ) ? - - } else if ! bias_only || self.lhs_non_pos.iter().fold( - // Skip if only generating biased checksat and there's more than one - // application without positive data. - 0, |acc, (_, argss)| acc + argss.len() - ) == 1 { - - self.partial_bias_right( - _profiler, solver, & mut actlits, & rhs_actlit - ) ? + self.bias_left(_profiler, solver, &mut actlits, bias_only)? + } - } + // If there's no rhs or it has negative samples we can do something for the + // lhs. + if clause!().rhs().is_none() || rhs_actlit.is_some() { + if self.lhs_non_pos.is_empty() { + self.exhaustive_bias_right(_profiler, solver, &mut actlits, instance, &rhs_actlit)? + } else if !bias_only + || self.lhs_non_pos.iter().fold( + // Skip if only generating biased checksat and there's more than one + // application without positive data. + 0, + |acc, (_, argss)| acc + argss.len(), + ) == 1 + { + self.partial_bias_right(_profiler, solver, &mut actlits, &rhs_actlit)? + } + } + Ok(actlits) } + /// Generates an actlit that forces the RHS of a clause to be a negative + /// sample. + /// + /// Returns `None` if either + /// - there's no RHS, + /// - there's no negative data for the RHS, + /// - the negative data for the RHS can't be activated. + fn get_rhs_actlit( + &mut self, + solver: &mut Slvr, + clause: ClsIdx, + instance: &Instance, + data: &Data, + ) -> Res> { + log! { @4 "working on rhs" } + + let res = if let Some((pred, args)) = instance[clause].rhs() { + log! { @5 "-> {}", instance[pred] } + if data.neg[pred].is_empty() { + // No negative data... + log! { @5 " no negative data" } + None + } else { + log! { @5 " has negative data" } + // Negative data, generate an actlit for that. + solver.comment(&format!( + "activating negative samples for {}", + instance[pred] + ))?; + + if let Some(actlit) = get_actlit(solver, args, &data.neg[pred])? { + Some(actlit) + } else { + None + } + } + } else { + log! { @5 "-> None" } + None + }; - Ok(actlits) - } - - - /// Generates an actlit that forces the RHS of a clause to be a negative - /// sample. - /// - /// Returns `None` if either - /// - there's no RHS, - /// - there's no negative data for the RHS, - /// - the negative data for the RHS can't be activated. - fn get_rhs_actlit( - & mut self, solver: & mut Slvr, - clause: ClsIdx, instance: & Instance, data: & Data - ) -> Res< Option > { - log! { @4 "working on rhs" } - - let res = if let Some((pred, args)) = instance[clause].rhs() { - log! { @5 "-> {}", instance[pred] } - if data.neg[pred].is_empty() { - // No negative data... - log! { @5 " no negative data" } - None - } else { - log! { @5 " has negative data" } - // Negative data, generate an actlit for that. - solver.comment( - & format!( - "activating negative samples for {}", instance[pred] - ) - ) ? ; - - if let Some(actlit) = get_actlit(solver, args, & data.neg[pred]) ? { - Some(actlit) - } else { None } - } - } else { - log! { @5 "-> None" } - None - } ; - - Ok(res) - } + Ok(res) + } + /// + fn get_lhs_actlits( + &mut self, + solver: &mut Slvr, + clause: ClsIdx, + instance: &Instance, + data: &Data, + ) -> Res<()> { + macro_rules! clause { + () => { + instance[clause] + }; + } + self.lhs_actlits.clear(); + self.lhs_non_pos.clear(); - /// - fn get_lhs_actlits( - & mut self, solver: & mut Slvr, - clause: ClsIdx, instance: & Instance, data: & Data - ) -> Res<()> { - macro_rules! clause { - () => (instance[clause]) ; - } + log! { @4 "creating lhs actlits" } - self.lhs_actlits.clear() ; - self.lhs_non_pos.clear() ; + let mut cant_pos_argss = VarTermsSet::new(); - log! { @4 "creating lhs actlits" } + // Create actlits for lhs applications when possible. + for (pred, argss) in clause!().lhs_preds() { + let pred = *pred; + log! { @5 "for {} ({})", instance[pred], argss.len() } - let mut cant_pos_argss = VarTermsSet::new() ; + if data.pos[pred].is_empty() { + log! { @5 " no positive data" } + self.lhs_non_pos.insert(pred, argss.clone()); + continue; + } - // Create actlits for lhs applications when possible. - for (pred, argss) in clause!().lhs_preds() { - let pred = * pred ; - log! { @5 "for {} ({})", instance[pred], argss.len() } + solver.comment(&format!( + "activating positive samples for {} ({} applications)", + instance[pred], + argss.len() + ))?; - if data.pos[pred].is_empty() { - log! { @5 " no positive data" } - self.lhs_non_pos.insert(pred, argss.clone()) ; - continue - } + let mut argss_map = VarTermsMap::with_capacity(argss.len()); - solver.comment( - & format!( - "activating positive samples for {} ({} applications)", - instance[pred], argss.len() - ) - ) ? ; + debug_assert! { cant_pos_argss.is_empty() } - let mut argss_map = VarTermsMap::with_capacity( argss.len() ) ; + for args in argss { + log! { @6 "generating actlit for {}", args } - debug_assert! { cant_pos_argss.is_empty() } + if let Some(actlit) = get_actlit(solver, args, &data.pos[pred])? { + let prev = argss_map.insert(args.clone(), actlit); + debug_assert! { prev.is_none() } + } else { + let is_new = cant_pos_argss.insert(args.clone()); + debug_assert! { is_new } + } + } - for args in argss { - log! { @6 "generating actlit for {}", args } + if !cant_pos_argss.is_empty() { + let prev = self.lhs_non_pos.insert(pred, cant_pos_argss.drain().into()); + debug_assert! { prev.is_none() } + } - if let Some(actlit) = get_actlit( - solver, args, & data.pos[pred] - ) ? { - let prev = argss_map.insert( args.clone(), actlit ) ; - debug_assert! { prev.is_none() } - } else { - let is_new = cant_pos_argss.insert( args.clone() ) ; - debug_assert! { is_new } + if !argss_map.is_empty() { + let prev = self.lhs_actlits.insert(pred, argss_map); + debug_assert! { prev.is_none() } + } } - } - - if ! cant_pos_argss.is_empty() { - let prev = self.lhs_non_pos.insert( - pred, cant_pos_argss.drain().into() - ) ; - debug_assert! { prev.is_none() } - } - - if ! argss_map.is_empty() { - let prev = self.lhs_actlits.insert(pred, argss_map) ; - debug_assert! { prev.is_none() } - } - } - - Ok(()) - } - - /// Generates a bias-left actlit. - fn bias_left( - & mut self, _profiler: & Profiler, solver: & mut Slvr, - actlits: & mut Vec< (Actlit, Bias) >, bias_only: bool - ) -> Res<()> { - solver.comment("activating all lhs positive samples") ? ; - let actlit = solver.get_actlit() ? ; - for (_, map) in & self.lhs_actlits { - for (_, pos_actlit) in map { - solver.assert_act(& actlit, pos_actlit) ? - } - } - let bias = if self.lhs_non_pos.is_empty() { - log! { @4 "total bias left" } - profile! { |_profiler| "bias: total left" => add 1 } - // If all lhs predicates have positive sample then this actlit yields a - // positive example for the rhs. - Bias::Lft - } else { - log! { @4 "partial bias left" } - profile! { |_profiler| "bias: partial left" => add 1 } - // Otherwise this will generate a constraint. - Bias::Non - } ; - - if ! bias.is_none() || ! bias_only { - actlits.push( (actlit, bias) ) - } else { - solver.de_actlit(actlit) ? + Ok(()) } - Ok(()) - } - - - /// Generates a partial bias-right actlit. - fn partial_bias_right( - & mut self, _profiler: & Profiler, solver: & mut Slvr, - actlits: & mut Vec< (Actlit, Bias) >, - rhs_actlit: & Option - ) -> Res<()> { - // There's some lhs applications with no negative data. Activate - // everything we can. - let this_actlit = solver.get_actlit() ? ; + /// Generates a bias-left actlit. + fn bias_left( + &mut self, + _profiler: &Profiler, + solver: &mut Slvr, + actlits: &mut Vec<(Actlit, Bias)>, + bias_only: bool, + ) -> Res<()> { + solver.comment("activating all lhs positive samples")?; + let actlit = solver.get_actlit()?; + for (_, map) in &self.lhs_actlits { + for (_, pos_actlit) in map { + solver.assert_act(&actlit, pos_actlit)? + } + } - // Force rhs false if any. - if let Some(rhs_actlit) = rhs_actlit.as_ref() { - solver.assert_act(& this_actlit, rhs_actlit) ? - } + let bias = if self.lhs_non_pos.is_empty() { + log! { @4 "total bias left" } + profile! { |_profiler| "bias: total left" => add 1 } + // If all lhs predicates have positive sample then this actlit yields a + // positive example for the rhs. + Bias::Lft + } else { + log! { @4 "partial bias left" } + profile! { |_profiler| "bias: partial left" => add 1 } + // Otherwise this will generate a constraint. + Bias::Non + }; + + if !bias.is_none() || !bias_only { + actlits.push((actlit, bias)) + } else { + solver.de_actlit(actlit)? + } - // Activate everything we can. - for (_, map) in & self.lhs_actlits { - for (_, actlit) in map { - solver.assert_act(& this_actlit, actlit) ? - } + Ok(()) } - // Now the question is what kind of bias this corresponds to. - - let mut iter = self.lhs_non_pos.drain() ; - let (this_pred, argss) = iter.next().expect( - "next on non-empty iterator cannot yield none" - ) ; - - let mut argss_iter = argss.into_iter() ; - let this_args = argss_iter.next().expect( - "empty arguments for predicate are illegal in clauses" - ) ; - - // If we have a single predicate application with no negative samples - // we can generate an actlit for this one. - let bias = if iter.next().is_none() && argss_iter.next().is_none() { - log! { @4 "singular bias right" } - profile! { |_profiler| "bias: singular right" => add 1 } - Bias::NuRgt(this_pred, this_args.clone()) - } else { - // Otherwise we can just generate a negative constraint that's more - // constrained. - log! { @4 "partial bias right" } - profile! { |_profiler| "bias: partial right " => add 1 } - Bias::Non - } ; + /// Generates a partial bias-right actlit. + fn partial_bias_right( + &mut self, + _profiler: &Profiler, + solver: &mut Slvr, + actlits: &mut Vec<(Actlit, Bias)>, + rhs_actlit: &Option, + ) -> Res<()> { + // There's some lhs applications with no negative data. Activate + // everything we can. + let this_actlit = solver.get_actlit()?; - actlits.push( (this_actlit, bias) ) ; + // Force rhs false if any. + if let Some(rhs_actlit) = rhs_actlit.as_ref() { + solver.assert_act(&this_actlit, rhs_actlit)? + } - Ok(()) - } + // Activate everything we can. + for (_, map) in &self.lhs_actlits { + for (_, actlit) in map { + solver.assert_act(&this_actlit, actlit)? + } + } + // Now the question is what kind of bias this corresponds to. + let mut iter = self.lhs_non_pos.drain(); + let (this_pred, argss) = iter + .next() + .expect("next on non-empty iterator cannot yield none"); - /// Generates exhaustive bias-right actlits. - fn exhaustive_bias_right( - & mut self, _profiler: & Profiler, solver: & mut Slvr, - actlits: & mut Vec< (Actlit, Bias) >, - instance: & Instance, rhs_actlit: & Option - ) -> Res<()> { + let mut argss_iter = argss.into_iter(); + let this_args = argss_iter + .next() + .expect("empty arguments for predicate are illegal in clauses"); - log! { @4 "exhaustive bias right" } - // We can force all positive examples for all lhs applications but - // one in turn to try to generate negative examples. - for (this_pred, map) in & self.lhs_actlits { - for (this_args, _) in map { + // If we have a single predicate application with no negative samples + // we can generate an actlit for this one. + let bias = if iter.next().is_none() && argss_iter.next().is_none() { + log! { @4 "singular bias right" } + profile! { |_profiler| "bias: singular right" => add 1 } + Bias::NuRgt(this_pred, this_args.clone()) + } else { + // Otherwise we can just generate a negative constraint that's more + // constrained. + log! { @4 "partial bias right" } + profile! { |_profiler| "bias: partial right " => add 1 } + Bias::Non + }; - solver.comment( - & format!( - "actlit forcing everything but ({} {})", - instance[* this_pred], this_args - ) - ) ? ; - let this_actlit = solver.get_actlit() ? ; + actlits.push((this_actlit, bias)); - // Force rhs false if any. - if let Some(rhs_actlit) = rhs_actlit.as_ref() { - solver.assert_act(& this_actlit, rhs_actlit) ? - } + Ok(()) + } - // Activate everything else than this particular application - for (pred, map) in & self.lhs_actlits { - for (args, actlit) in map { - if pred == this_pred && args == this_args { - continue + /// Generates exhaustive bias-right actlits. + fn exhaustive_bias_right( + &mut self, + _profiler: &Profiler, + solver: &mut Slvr, + actlits: &mut Vec<(Actlit, Bias)>, + instance: &Instance, + rhs_actlit: &Option, + ) -> Res<()> { + log! { @4 "exhaustive bias right" } + // We can force all positive examples for all lhs applications but + // one in turn to try to generate negative examples. + for (this_pred, map) in &self.lhs_actlits { + for (this_args, _) in map { + solver.comment(&format!( + "actlit forcing everything but ({} {})", + instance[*this_pred], this_args + ))?; + let this_actlit = solver.get_actlit()?; + + // Force rhs false if any. + if let Some(rhs_actlit) = rhs_actlit.as_ref() { + solver.assert_act(&this_actlit, rhs_actlit)? + } + + // Activate everything else than this particular application + for (pred, map) in &self.lhs_actlits { + for (args, actlit) in map { + if pred == this_pred && args == this_args { + continue; + } + solver.assert_act(&this_actlit, actlit)? + } + } + + profile! { |_profiler| "bias: exhaustive right" => add 1 } + + actlits.push((this_actlit, Bias::NuRgt(*this_pred, this_args.clone()))) } - solver.assert_act(& this_actlit, actlit) ? - } } - profile! { |_profiler| "bias: exhaustive right" => add 1 } - - actlits.push( - (this_actlit, Bias::NuRgt(* this_pred, this_args.clone())) - ) - } + Ok(()) } - - Ok(()) - } - } - /// -fn get_actlit( - solver: & mut Slvr, args: & VarTerms, data: & VarValsSet -) -> Res< Option > { - let actlit = solver.get_actlit() ? ; - let disjunction = DisjArgs::new(args, data) ? ; - solver.assert_act(& actlit, & disjunction) ? ; - - if solver.check_sat_act( Some(& actlit) ) ? { - log! { @6 " sat, keeping actlit" } - Ok( Some(actlit) ) - } else { - log! { @6 " unsat, discarding actlit" } - solver.de_actlit(actlit) ? ; - Ok( None ) - } -} \ No newline at end of file +fn get_actlit(solver: &mut Slvr, args: &VarTerms, data: &VarValsSet) -> Res> { + let actlit = solver.get_actlit()?; + let disjunction = DisjArgs::new(args, data)?; + solver.assert_act(&actlit, &disjunction)?; + + if solver.check_sat_act(Some(&actlit))? { + log! { @6 " sat, keeping actlit" } + Ok(Some(actlit)) + } else { + log! { @6 " unsat, discarding actlit" } + solver.de_actlit(actlit)?; + Ok(None) + } +} diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index bf9f013d..2651b42d 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -3,1198 +3,1091 @@ //! [teach]: fn.teach.html //! (Teacher's teach function) -use std::time::Duration ; +use std::time::Duration; use common::{ - *, - msg::*, - smt::{ SmtTerm, FullParser as Parser }, -} ; -use unsat_core::UnsatRes ; -use data::Data ; + msg::*, + smt::{FullParser as Parser, SmtTerm}, + *, +}; +use data::Data; +use unsat_core::UnsatRes; -pub mod assistant ; -mod cex_bias ; -use self::assistant::Assistant ; - -pub use self::cex_bias::CexBias ; +pub mod assistant; +mod cex_bias; +use self::assistant::Assistant; +pub use self::cex_bias::CexBias; /// Starts the teaching process. /// /// The partial model stores conjunction of top terms for some of the top /// terms, and is expressed in terms of the predicates' original signatures. pub fn start_class( - instance: Arc, - partial_model: & ConjCandidates, - profiler: & Profiler -) -> Res< TeachRes > { - log! { @debug - "starting the learning process" ; - " launching solver kid..." - } - let mut teacher = Teacher::new(instance, profiler, partial_model) ? ; - - let res = match teach(& mut teacher) { - Ok(res) => Ok(res), - - Err(e) => match e.kind() { - ErrorKind::Unsat => { - warn! { - "legacy unsat (by error) result triggered\n\ - unsat core will not be available\n\ - please consider contacting the developer" - } - let core = teacher.unsat_core() ; - Ok( TeachRes::Unsat(core) ) - }, - _ => { - if let Err(tmo) = conf.check_timeout() { - Err(tmo) - } else { - Err(e) - } - }, - }, - } ; + instance: Arc, + partial_model: &ConjCandidates, + profiler: &Profiler, +) -> Res { + log! { @debug + "starting the learning process" ; + " launching solver kid..." + } + let mut teacher = Teacher::new(instance, profiler, partial_model)?; + + let res = match teach(&mut teacher) { + Ok(res) => Ok(res), + + Err(e) => match e.kind() { + ErrorKind::Unsat => { + warn! { + "legacy unsat (by error) result triggered\n\ + unsat core will not be available\n\ + please consider contacting the developer" + } + let core = teacher.unsat_core(); + Ok(TeachRes::Unsat(core)) + } + _ => { + if let Err(tmo) = conf.check_timeout() { + Err(tmo) + } else { + Err(e) + } + } + }, + }; - teacher.finalize() ? ; - res + teacher.finalize()?; + res } - - /// Teaching to the learners. -pub fn teach(teacher: & mut Teacher) -> Res { - - log_debug!{ "spawning ice learner(s)..." } - if conf.ice.pure_synth { - teacher.add_learner( ::learning::ice::Launcher, false ) ? ; - } - teacher.add_learner( ::learning::ice::Launcher, true ) ? ; - - if let Some(res) = teacher.init() ? { - return Ok(res) - } - - // Index of the learner the teacher is currently working for. - // - // None at the beginning (broadcast). - let mut learner: Option = None ; - - loop { - - log_verb! { - "all learning data:\n{}", teacher.data.string_do( - & (), |s| s.to_string() - ) ? +pub fn teach(teacher: &mut Teacher) -> Res { + log_debug!{ "spawning ice learner(s)..." } + if conf.ice.pure_synth { + teacher.add_learner(::learning::ice::Launcher, false)?; } + teacher.add_learner(::learning::ice::Launcher, true)?; - if let Some(idx) = learner { - if conf.teacher.step { - pause( - & format!( - "to send data to {}... (--step on)", - & conf.emph(& teacher.learners[idx].1) - ), & teacher._profiler - ) ; - } - let _ = teacher.send(idx) ? ; - () - } else { - if conf.teacher.step { - pause( - "to broadcast data... (--step on)", - & teacher._profiler - ) ; - } - let one_alive = teacher.broadcast() ; - if ! one_alive { - unknown!("all learners are dead") - } + if let Some(res) = teacher.init()? { + return Ok(res); } - // if teacher.data.is_unsat().is_some() { - // return Ok(Either::Right(teacher.unsat_core())) - // } - - match teacher.get_candidates(false) ? { + // Index of the learner the teacher is currently working for. + // + // None at the beginning (broadcast). + let mut learner: Option = None; - // Unsat result, done. - Either::Right(unsat) => { - return Ok( TeachRes::Unsat(unsat) ) - }, - - // Got a candidate. - Either::Left( ( idx, candidates) ) => { - learner = Some(idx) ; - if let Some(res) = teacher.handle_candidates(candidates, idx) ? { - return Ok(res) + loop { + log_verb! { + "all learning data:\n{}", teacher.data.string_do( + & (), |s| s.to_string() + ) ? } - }, - } - } -} + if let Some(idx) = learner { + if conf.teacher.step { + pause( + &format!( + "to send data to {}... (--step on)", + &conf.emph(&teacher.learners[idx].1) + ), + &teacher._profiler, + ); + } + let _ = teacher.send(idx)?; + () + } else { + if conf.teacher.step { + pause("to broadcast data... (--step on)", &teacher._profiler); + } + let one_alive = teacher.broadcast(); + if !one_alive { + unknown!("all learners are dead") + } + } + // if teacher.data.is_unsat().is_some() { + // return Ok(Either::Right(teacher.unsat_core())) + // } + match teacher.get_candidates(false)? { + // Unsat result, done. + Either::Right(unsat) => return Ok(TeachRes::Unsat(unsat)), + // Got a candidate. + Either::Left((idx, candidates)) => { + learner = Some(idx); + if let Some(res) = teacher.handle_candidates(candidates, idx)? { + return Ok(res); + } + } + } + } +} /// The teacher, stores a solver. pub struct Teacher<'a> { - /// The solver. - pub solver: Solver, - - /// The (shared) instance. - pub instance: Arc, - /// Learning data. - pub data: Data, - - /// Receiver. - pub from_learners: Receiver, - /// Sender used by learners. Becomes `None` when the learning process starts. - pub to_teacher: Option< Sender >, - - /// Learners sender and description. - /// - /// Stores the channel to the learner, its name (for log), and a flag - /// indicating whether the data has changed since this learner's last - /// candidates. - pub learners: LrnMap<( Option< Sender >, String, bool )>, - /// Assistant for implication constraint breaking. - /// - /// The boolean flag indicates whether the assistant was sent some stuff. - // pub assistant: Option< (Sender, bool) >, - pub assistant: Option, - /// Profiler. - pub _profiler: & 'a Profiler, - - /// Predicates that are true in the current candidate. - tru_preds: PrdSet, - /// Predicates that are true in the current candidate. - fls_preds: PrdSet, - /// Clauses that are trivially verified in the current candidate. - clauses_to_ignore: ClsSet, - - - /// Helper for cex bias. - bias: CexBias, - - /// Partial candidate, only really contains stuff when in split mode. - /// - /// Used to further constrain the learner's candidates using previous - /// results, when in split mode. - partial_model: PrdHMap, - /// Map from - /// Number of guesses. - count: usize, - - /// True if some recursive functions are defined. - using_rec_funs: bool, - /// Forces to restart the solver after each check. - restart_on_cex: bool, + /// The solver. + pub solver: Solver, + + /// The (shared) instance. + pub instance: Arc, + /// Learning data. + pub data: Data, + + /// Receiver. + pub from_learners: Receiver, + /// Sender used by learners. Becomes `None` when the learning process starts. + pub to_teacher: Option>, + + /// Learners sender and description. + /// + /// Stores the channel to the learner, its name (for log), and a flag + /// indicating whether the data has changed since this learner's last + /// candidates. + pub learners: LrnMap<(Option>, String, bool)>, + /// Assistant for implication constraint breaking. + /// + /// The boolean flag indicates whether the assistant was sent some stuff. + // pub assistant: Option< (Sender, bool) >, + pub assistant: Option, + /// Profiler. + pub _profiler: &'a Profiler, + + /// Predicates that are true in the current candidate. + tru_preds: PrdSet, + /// Predicates that are true in the current candidate. + fls_preds: PrdSet, + /// Clauses that are trivially verified in the current candidate. + clauses_to_ignore: ClsSet, + + /// Helper for cex bias. + bias: CexBias, + + /// Partial candidate, only really contains stuff when in split mode. + /// + /// Used to further constrain the learner's candidates using previous + /// results, when in split mode. + partial_model: PrdHMap, + /// Map from + /// Number of guesses. + count: usize, + + /// True if some recursive functions are defined. + using_rec_funs: bool, + /// Forces to restart the solver after each check. + restart_on_cex: bool, } impl<'a> Teacher<'a> { - - /// Constructor. - pub fn new( - instance: Arc, profiler: & 'a Profiler, - partial_model: & 'a ConjCandidates - ) -> Res { - let solver = conf.solver.spawn( - "teacher", Parser, & instance - ) ? ; - - // let partial_model = PrdHMap::new() ; - let partial_model = partial_model.iter().filter_map( - |(pred, tterms_vec)| { - let subst = instance.map_from_original_sig_of(* pred) ; - let conj: Vec<_> = tterms_vec.iter().filter_map( - |tterms| if let Some(term) = tterms.to_term() { - term.subst_total(& subst).map( - |(term, _)| term + /// Constructor. + pub fn new( + instance: Arc, + profiler: &'a Profiler, + partial_model: &'a ConjCandidates, + ) -> Res { + let solver = conf.solver.spawn("teacher", Parser, &instance)?; + + // let partial_model = PrdHMap::new() ; + let partial_model = partial_model + .iter() + .filter_map(|(pred, tterms_vec)| { + let subst = instance.map_from_original_sig_of(*pred); + let conj: Vec<_> = tterms_vec + .iter() + .filter_map(|tterms| { + if let Some(term) = tterms.to_term() { + term.subst_total(&subst).map(|(term, _)| term) + } else { + None + } + }).collect(); + + if conj.is_empty() { + None + } else { + profile! { + |profiler| "partial model reuse" => add 1 + } + Some((*pred, term::and(conj))) + } + }).collect(); + + let learners = LrnMap::with_capacity(2); + let (to_teacher, from_learners) = Msg::channel(); + let data = Data::new(instance.clone()); + + let assistant = if conf.teacher.assistant { + Some( + Assistant::new(instance.clone()) + .chain_err(|| "while spawning assistant".to_string())?, ) - } else { - None - } - ).collect() ; - - if conj.is_empty() { - None } else { - profile! { - |profiler| "partial model reuse" => add 1 - } - Some( (* pred, term::and(conj)) ) - } - } - ).collect() ; - - let learners = LrnMap::with_capacity( 2 ) ; - let (to_teacher, from_learners) = Msg::channel() ; - let data = Data::new( instance.clone() ) ; - - let assistant = if conf.teacher.assistant { - Some( - Assistant::new(instance.clone()).chain_err( - || "while spawning assistant".to_string() - ) ? - ) - } else { - None - } ; - - let mut using_rec_funs = false ; - - fun::iter( - |_| { using_rec_funs = true ; Ok(()) } - ) ? ; - - let restart_on_cex = - conf.teacher.restart_on_cex || - ! dtyp::get_all().is_empty() || - using_rec_funs - ; - - Ok( - Teacher { - solver, instance, data, from_learners, - to_teacher: Some(to_teacher), learners, assistant, - _profiler: profiler, partial_model, count: 0, - tru_preds: PrdSet::new(), fls_preds: PrdSet::new(), - clauses_to_ignore: ClsSet::new(), - bias: CexBias::new(), - using_rec_funs, restart_on_cex - } - ) - } - - /// Model from some candidates. - fn model_of_candidates( - & self, mut cands: Candidates - ) -> Candidates { - for (pred, cand) in cands.index_iter_mut() { - if let Some(cand) = cand.as_mut() { - if let Some(other) = self.instance.get_str(pred) { - * cand = term::and( - vec![ cand.clone(), other.clone() ] - ) - } - } - } - cands - } - - - /// Completes some candidates with partial-model and partial-defs. - fn complete_candidates( - & self, mut cands: Candidates - ) -> Candidates { - for (pred, cand) in cands.index_iter_mut() { - if let Some(cand) = cand.as_mut() { - let mut others = None ; - if let Some(other) = self.instance.get_str(pred) { - others.get_or_insert_with(Vec::new).push( other.clone() ) - } - if let Some(other) = self.partial_model.get(& pred) { - others.get_or_insert_with(Vec::new).push( other.clone() ) - } - if let Some(mut others) = others { - others.push( cand.clone() ) ; - * cand = term::and(others) - } - } - } - cands - } - - - /// Runs the initial check and registers the data. - pub fn init(& mut self) -> Res< Option > { - log_debug!{ "performing initial check..." } - let (cexs, cands) = self.initial_check() ? ; - if cexs.is_empty() { - log_debug!{ "solved by initial candidate..." } - return Ok( - Some( - TeachRes::Model( self.model_of_candidates(cands) ) - ) - ) - } - - log_debug!{ "generating data from initial cex..." } - let nu_stuff = self.instance.cexs_to_data(& mut self.data, cexs ) ? ; - if ! nu_stuff { - bail! { "translation of initial cexs to data generated no new data" } - } - self.run_assistant() ? ; - - Ok(None) - } - - - - /// Runs the assistant (if any) on the current data. - pub fn run_assistant(& mut self) -> Res<()> { - if let Some(assistant) = self.assistant.as_mut() { - profile! { self tick "assistant" } - if let Some(mut data) = self.data.clone_new_constraints() ? { - assistant.break_implications(& mut data) ? ; - let (_nu_pos, _nu_neg) = self.data.merge_samples(data) ? ; - profile! { self "assistant pos useful" => add _nu_pos } - profile! { self "assistant neg useful" => add _nu_neg } - } - profile! { self mark "assistant" } + None + }; + + let mut using_rec_funs = false; + + fun::iter(|_| { + using_rec_funs = true; + Ok(()) + })?; + + let restart_on_cex = + conf.teacher.restart_on_cex || !dtyp::get_all().is_empty() || using_rec_funs; + + Ok(Teacher { + solver, + instance, + data, + from_learners, + to_teacher: Some(to_teacher), + learners, + assistant, + _profiler: profiler, + partial_model, + count: 0, + tru_preds: PrdSet::new(), + fls_preds: PrdSet::new(), + clauses_to_ignore: ClsSet::new(), + bias: CexBias::new(), + using_rec_funs, + restart_on_cex, + }) } - Ok(()) - } - - - - /// Finalizes the run. - pub fn finalize(mut self) -> Res<()> { - for set in self.data.pos.iter() { - for sample in set { - if sample.is_partial() { - profile! { self "partial pos samples" => add 1 } - } - } - } - for set in self.data.neg.iter() { - for sample in set { - if sample.is_partial() { - profile! { self "partial neg samples" => add 1 } + /// Model from some candidates. + fn model_of_candidates(&self, mut cands: Candidates) -> Candidates { + for (pred, cand) in cands.index_iter_mut() { + if let Some(cand) = cand.as_mut() { + if let Some(other) = self.instance.get_str(pred) { + *cand = term::and(vec![cand.clone(), other.clone()]) + } + } } - } - } - self.solver.kill().chain_err( - || "While killing solver" - ) ? ; - for & mut (ref mut sender, _, _) in self.learners.iter_mut() { - if let Some(sender) = sender.as_ref() { - let _ = sender.send( FromTeacher::Exit ) ; - () - } - * sender = None - } - - if let Some(assistant) = self.assistant { - let profiler = assistant.finalize() ? ; - self._profiler.add_sub("assistant", profiler) - } - self.assistant = None ; - log_debug! { "draining messages" } - while let Ok(_) = self.get_candidates(true) {} - - if conf.stats { - self._profiler.add_sub( - "data", self.data.destroy() - ) - } - Ok(()) - } - - - - - /// Adds a new learner. - pub fn add_learner(& mut self, learner: L, mine: bool) -> Res<()> - where L: Learner + 'static { - if let Some(to_teacher) = self.to_teacher.clone() { - let index = self.learners.next_index() ; - let name = learner.description(mine) ; - let instance = self.instance.clone() ; - let data = self.data.clone() ; - let (to_learner, learner_recv) = FromTeacher::channel() ; - ::std::thread::Builder::new().name( name.clone() ).spawn( - move || learner.run( - MsgCore::new_learner( - index, to_teacher.clone(), learner_recv - ), - instance, data, mine - ) - ).chain_err( - || format!("while spawning learner `{}`", conf.emph(& name)) - ) ? ; - self.learners.push( ( Some(to_learner), name, false ) ) ; - Ok(()) - } else { - bail!("trying to add learner after teacher's finalization") + cands } - } - - - - /// Broadcasts data to the learners. Returns `true` if there's no more - /// learner left. - /// - /// Only used for the data from the first check. - pub fn broadcast(& self) -> bool { - profile!{ self tick "sending" } - let mut one_alive = false ; - log_verb! { "broadcasting..." } - for & (ref sender, ref name, _) in self.learners.iter() { - if let Some(sender) = sender.as_ref() { - if sender.send( - FromTeacher::Data( - Box::new( self.data.clone() ) - ) - ).is_err() { - warn!( "learner `{}` is dead...", name ) - } else { - one_alive = true + /// Completes some candidates with partial-model and partial-defs. + fn complete_candidates(&self, mut cands: Candidates) -> Candidates { + for (pred, cand) in cands.index_iter_mut() { + if let Some(cand) = cand.as_mut() { + let mut others = None; + if let Some(other) = self.instance.get_str(pred) { + others.get_or_insert_with(Vec::new).push(other.clone()) + } + if let Some(other) = self.partial_model.get(&pred) { + others.get_or_insert_with(Vec::new).push(other.clone()) + } + if let Some(mut others) = others { + others.push(cand.clone()); + *cand = term::and(others) + } + } } - } + cands } - log_verb! { "done broadcasting..." } - profile! { self mark "sending" } - one_alive - } - - + /// Runs the initial check and registers the data. + pub fn init(&mut self) -> Res> { + log_debug!{ "performing initial check..." } + let (cexs, cands) = self.initial_check()?; + if cexs.is_empty() { + log_debug!{ "solved by initial candidate..." } + return Ok(Some(TeachRes::Model(self.model_of_candidates(cands)))); + } + log_debug!{ "generating data from initial cex..." } + let nu_stuff = self.instance.cexs_to_data(&mut self.data, cexs)?; + if !nu_stuff { + bail! { "translation of initial cexs to data generated no new data" } + } + self.run_assistant()?; - /// Sends data to a specific learner. - pub fn send(& self, learner: LrnIdx) -> Res { - profile! { self tick "sending" } - let (ref sender, ref name, _) = self.learners[learner] ; - let alive = if let Some(sender) = sender.as_ref() { - sender.send( - FromTeacher::Data( - Box::new( self.data.clone() ) - ) - ).is_ok() - } else { - false - } ; - if ! alive { - warn!( "learner `{}` is dead...", name ) ; + Ok(None) } - profile! { self mark "sending" } - - Ok(alive) - } - - /// Receives a message with a timeout. - fn receive_msg_tmo( - & mut self, drain: bool, timeout: Duration - ) -> Res { - macro_rules! all_dead { - () => ( unknown!("all learners are dead") ) ; + /// Runs the assistant (if any) on the current data. + pub fn run_assistant(&mut self) -> Res<()> { + if let Some(assistant) = self.assistant.as_mut() { + profile! { self tick "assistant" } + if let Some(mut data) = self.data.clone_new_constraints()? { + assistant.break_implications(&mut data)?; + let (_nu_pos, _nu_neg) = self.data.merge_samples(data)?; + profile! { self "assistant pos useful" => add _nu_pos } + profile! { self "assistant neg useful" => add _nu_neg } + } + profile! { self mark "assistant" } + } + Ok(()) } - let msg = if ! drain { - match profile! { - self wrap { - self.from_learners.recv_timeout(timeout) - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - conf.check_timeout() ? ; - all_dead!() - }, - } - } else { - match profile! { - self wrap { - self.from_learners.recv() - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - all_dead!() - }, - } - } ; - Ok(msg) - } - + /// Finalizes the run. + pub fn finalize(mut self) -> Res<()> { + for set in self.data.pos.iter() { + for sample in set { + if sample.is_partial() { + profile! { self "partial pos samples" => add 1 } + } + } + } + for set in self.data.neg.iter() { + for sample in set { + if sample.is_partial() { + profile! { self "partial neg samples" => add 1 } + } + } + } + self.solver.kill().chain_err(|| "While killing solver")?; + for &mut (ref mut sender, _, _) in self.learners.iter_mut() { + if let Some(sender) = sender.as_ref() { + let _ = sender.send(FromTeacher::Exit); + () + } + *sender = None + } - /// Receive a message. - fn receive_msg( - & mut self, drain: bool - ) -> Res<(Id, MsgKind)> { - macro_rules! all_dead { - () => ( unknown!("all learners are dead") ) ; - } + if let Some(assistant) = self.assistant { + let profiler = assistant.finalize()?; + self._profiler.add_sub("assistant", profiler) + } + self.assistant = None; + log_debug! { "draining messages" } + while let Ok(_) = self.get_candidates(true) {} - if ! drain && self.learners.iter().all( - |& (ref channel, _, _)| channel.is_none() - ) { - all_dead!() + if conf.stats { + self._profiler.add_sub("data", self.data.destroy()) + } + Ok(()) } - profile!{ self tick "waiting" } - let Msg { id, msg } = if let Some(timeout) = conf.until_timeout() { - self.receive_msg_tmo(drain, timeout) ? - } else { - match profile! { - self wrap { - self.from_learners.recv() - } "waiting" - } { - Ok(msg) => msg, - Err(_) => { - profile!{ self mark "waiting" } - all_dead!() - }, - } - } ; - - Ok((id, msg)) - } - - - - /// Handles some candidates. - /// - /// - checks for counterexamples - /// - turns the cexs into learning data - /// - runs the assistant - /// - propagates the learning data - pub fn handle_candidates( - & mut self, candidates: Candidates, idx: LrnIdx - ) -> Res< Option > { - if_log!{ @1 - log! { conf.teacher.step, || @1 - "\nCurrent candidate(s) from {} learner:", - conf.emph( & self.learners[idx].1 ) - } - for _pred in self.instance.preds() { - if let Some(term) = candidates[_pred.idx].as_ref() { - log!( @1 - "{}:", conf.emph(& _pred.name) ; - " {}", term - ) + /// Adds a new learner. + pub fn add_learner(&mut self, learner: L, mine: bool) -> Res<()> + where + L: Learner + 'static, + { + if let Some(to_teacher) = self.to_teacher.clone() { + let index = self.learners.next_index(); + let name = learner.description(mine); + let instance = self.instance.clone(); + let data = self.data.clone(); + let (to_learner, learner_recv) = FromTeacher::channel(); + ::std::thread::Builder::new() + .name(name.clone()) + .spawn(move || { + learner.run( + MsgCore::new_learner(index, to_teacher.clone(), learner_recv), + instance, + data, + mine, + ) + }).chain_err(|| format!("while spawning learner `{}`", conf.emph(&name)))?; + self.learners.push((Some(to_learner), name, false)); + Ok(()) + } else { + bail!("trying to add learner after teacher's finalization") } - } - log! { @1 "" } } - if conf.teacher.step { - pause( - "to look for counterexamples... (--step on)", - & self._profiler - ) ; + /// Broadcasts data to the learners. Returns `true` if there's no more + /// learner left. + /// + /// Only used for the data from the first check. + pub fn broadcast(&self) -> bool { + profile!{ self tick "sending" } + let mut one_alive = false; + log_verb! { "broadcasting..." } + for &(ref sender, ref name, _) in self.learners.iter() { + if let Some(sender) = sender.as_ref() { + if sender + .send(FromTeacher::Data(Box::new(self.data.clone()))) + .is_err() + { + warn!("learner `{}` is dead...", name) + } else { + one_alive = true + } + } + } + log_verb! { "done broadcasting..." } + profile! { self mark "sending" } + one_alive } - let cexs = profile! { - self wrap { self.get_cexs(& candidates) } "cexs" - } ? ; + /// Sends data to a specific learner. + pub fn send(&self, learner: LrnIdx) -> Res { + profile! { self tick "sending" } + let (ref sender, ref name, _) = self.learners[learner]; + let alive = if let Some(sender) = sender.as_ref() { + sender + .send(FromTeacher::Data(Box::new(self.data.clone()))) + .is_ok() + } else { + false + }; + if !alive { + warn!("learner `{}` is dead...", name); + } + profile! { self mark "sending" } - if cexs.is_empty() { - return Ok( - Some( - TeachRes::Model( self.model_of_candidates(candidates) ) - ) - ) + Ok(alive) } - profile!{ self tick "data" } - profile!{ self tick "data", "registration" } - let res = self.instance.cexs_to_data( - & mut self.data, cexs - ) ; - profile!{ self mark "data", "registration" } - profile!{ self mark "data" } - - self.run_assistant() ? ; - - match res { - Ok(true) => { - // New data. - for ( - index, & mut (_, _, ref mut changed) - ) in self.learners.index_iter_mut() { - * changed = index != idx + /// Receives a message with a timeout. + fn receive_msg_tmo(&mut self, drain: bool, timeout: Duration) -> Res { + macro_rules! all_dead { + () => { + unknown!("all learners are dead") + }; } - }, - Ok(false) => if self.learners[idx].2 { - // Something has changed since the last candidate of this learner. - // The fact that the current candidate generated no new data is not - // a problem. - () - } else { - bail! { - "translation of cexs to data for {} generated no new data", - conf.emph( & self.learners[idx].1 ) - } - }, - Err(e) => { - if e.is_unsat() { - return Ok( - Some( - TeachRes::Unsat( self.unsat_core() ) - ) - ) + let msg = if !drain { + match profile! { + self wrap { + self.from_learners.recv_timeout(timeout) + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + conf.check_timeout()?; + all_dead!() + } + } } else { - bail!(e) - } - }, + match profile! { + self wrap { + self.from_learners.recv() + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + all_dead!() + } + } + }; + Ok(msg) } - profile!{ self tick "data" } - profile!{ self tick "data", "propagation" } - self.data.propagate() ? ; - profile!{ self mark "data", "propagation" } - profile!{ self mark "data" } - - Ok(None) - } - - - + /// Receive a message. + fn receive_msg(&mut self, drain: bool) -> Res<(Id, MsgKind)> { + macro_rules! all_dead { + () => { + unknown!("all learners are dead") + }; + } - /// Waits for some candidates. - /// - /// Returns `None` when there are no more kids. Otherwise, the second - /// element of the pair is `None` if a learner concluded `unsat`, and - /// `Some` of the candidates otherwise. - /// - /// If true, `drain` forces to ignore timeouts. Useful when finalizing. - pub fn get_candidates( - & mut self, drain: bool - ) -> Res< Either<(LrnIdx, Candidates), UnsatRes> > { + if !drain && self + .learners + .iter() + .all(|&(ref channel, _, _)| channel.is_none()) + { + all_dead!() + } - loop { - let (id, msg) = self.receive_msg(drain) ? ; + profile!{ self tick "waiting" } + let Msg { id, msg } = if let Some(timeout) = conf.until_timeout() { + self.receive_msg_tmo(drain, timeout)? + } else { + match profile! { + self wrap { + self.from_learners.recv() + } "waiting" + } { + Ok(msg) => msg, + Err(_) => { + profile!{ self mark "waiting" } + all_dead!() + } + } + }; - match msg { + Ok((id, msg)) + } - MsgKind::Cands(cands) => { - profile!{ self "candidates" => add 1 } - if let Id::Learner(idx) = id { - return Ok( - Either::Left( - ( idx, self.complete_candidates(cands) ) - ) - ) - } else { - bail!("received candidates from {}", id) + /// Handles some candidates. + /// + /// - checks for counterexamples + /// - turns the cexs into learning data + /// - runs the assistant + /// - propagates the learning data + pub fn handle_candidates( + &mut self, + candidates: Candidates, + idx: LrnIdx, + ) -> Res> { + if_log!{ @1 + log! { conf.teacher.step, || @1 + "\nCurrent candidate(s) from {} learner:", + conf.emph( & self.learners[idx].1 ) } - }, - - MsgKind::Samples(samples) => { - let (_pos, _neg) = samples.pos_neg_count() ; - profile! { self "assistant pos " => add _pos } - profile! { self "assistant neg " => add _neg } - let (pos, neg) = self.data.merge_samples(* samples) ? ; - profile! { self "assistant pos useful" => add pos } - profile! { self "assistant neg useful" => add neg } - - if pos + neg != 0 { - for & mut (_, _, ref mut changed) in & mut self.learners { - * changed = true + for _pred in self.instance.preds() { + if let Some(term) = candidates[_pred.idx].as_ref() { + log!( @1 + "{}:", conf.emph(& _pred.name) ; + " {}", term + ) } } - }, + log! { @1 "" } + } - MsgKind::Msg(_s) => { - let id = match id { - Id::Learner(idx) => conf.emph( & self.learners[idx].1 ), - Id::Assistant => conf.emph( "assistant" ), - } ; - println!(";") ; - for _line in _s.lines() { - println!("; {} | {}", id, _line) - } - }, + if conf.teacher.step { + pause( + "to look for counterexamples... (--step on)", + &self._profiler, + ); + } - MsgKind::Err(ref e) if e.is_unknown() => { - if_not_bench! { - let id = match id { - Id::Learner(idx) => conf.emph( & self.learners[idx].1 ), - Id::Assistant => conf.emph( "assistant" ), - } ; - log! { @verb "received `{}` from {}", conf.bad("unknown"), id } - } + let cexs = profile! { + self wrap { self.get_cexs(& candidates) } "cexs" + }?; - // Are we unsat? - if self.data.is_unsat().is_some() { - return Ok(Either::Right(self.unsat_core())) - } - }, + if cexs.is_empty() { + return Ok(Some(TeachRes::Model(self.model_of_candidates(candidates)))); + } - MsgKind::Err(e) => { - let id = match id { - Id::Learner(idx) => conf.emph( & self.learners[idx].1 ), - Id::Assistant => conf.emph( "assistant" ), - } ; - let err: Res<()> = Err(e) ; - let err: Res<()> = err.chain_err( - || format!( - "from {} learner", id - ) - ) ; - print_err( & err.unwrap_err() ) - }, + profile!{ self tick "data" } + profile!{ self tick "data", "registration" } + let res = self.instance.cexs_to_data(&mut self.data, cexs); + profile!{ self mark "data", "registration" } + profile!{ self mark "data" } - MsgKind::Stats(profiler) => { - let id = match id { - Id::Learner(idx) => { - self.learners[idx].0 = None ; - self.learners[idx].1.clone() + self.run_assistant()?; + + match res { + Ok(true) => { + // New data. + for (index, &mut (_, _, ref mut changed)) in self.learners.index_iter_mut() { + *changed = index != idx + } + } + Ok(false) => if self.learners[idx].2 { + // Something has changed since the last candidate of this learner. + // The fact that the current candidate generated no new data is not + // a problem. + () + } else { + bail! { + "translation of cexs to data for {} generated no new data", + conf.emph( & self.learners[idx].1 ) + } }, - Id::Assistant => "assistant".into(), - } ; - if conf.stats { - self._profiler.add_other(id, profiler) - } - }, + Err(e) => { + if e.is_unsat() { + return Ok(Some(TeachRes::Unsat(self.unsat_core()))); + } else { + bail!(e) + } + } + } - MsgKind::Unsat => if self.data.is_unsat().is_some() { - return Ok(Either::Right(self.unsat_core())) - }, + profile!{ self tick "data" } + profile!{ self tick "data", "propagation" } + self.data.propagate()?; + profile!{ self mark "data", "propagation" } + profile!{ self mark "data" } - } + Ok(None) + } + /// Waits for some candidates. + /// + /// Returns `None` when there are no more kids. Otherwise, the second + /// element of the pair is `None` if a learner concluded `unsat`, and + /// `Some` of the candidates otherwise. + /// + /// If true, `drain` forces to ignore timeouts. Useful when finalizing. + pub fn get_candidates(&mut self, drain: bool) -> Res> { + loop { + let (id, msg) = self.receive_msg(drain)?; + + match msg { + MsgKind::Cands(cands) => { + profile!{ self "candidates" => add 1 } + if let Id::Learner(idx) = id { + return Ok(Either::Left((idx, self.complete_candidates(cands)))); + } else { + bail!("received candidates from {}", id) + } + } + + MsgKind::Samples(samples) => { + let (_pos, _neg) = samples.pos_neg_count(); + profile! { self "assistant pos " => add _pos } + profile! { self "assistant neg " => add _neg } + let (pos, neg) = self.data.merge_samples(*samples)?; + profile! { self "assistant pos useful" => add pos } + profile! { self "assistant neg useful" => add neg } + + if pos + neg != 0 { + for &mut (_, _, ref mut changed) in &mut self.learners { + *changed = true + } + } + } + + MsgKind::Msg(_s) => { + let id = match id { + Id::Learner(idx) => conf.emph(&self.learners[idx].1), + Id::Assistant => conf.emph("assistant"), + }; + println!(";"); + for _line in _s.lines() { + println!("; {} | {}", id, _line) + } + } + + MsgKind::Err(ref e) if e.is_unknown() => { + if_not_bench! { + let id = match id { + Id::Learner(idx) => conf.emph( & self.learners[idx].1 ), + Id::Assistant => conf.emph( "assistant" ), + } ; + log! { @verb "received `{}` from {}", conf.bad("unknown"), id } + } + + // Are we unsat? + if self.data.is_unsat().is_some() { + return Ok(Either::Right(self.unsat_core())); + } + } + + MsgKind::Err(e) => { + let id = match id { + Id::Learner(idx) => conf.emph(&self.learners[idx].1), + Id::Assistant => conf.emph("assistant"), + }; + let err: Res<()> = Err(e); + let err: Res<()> = err.chain_err(|| format!("from {} learner", id)); + print_err(&err.unwrap_err()) + } + + MsgKind::Stats(profiler) => { + let id = match id { + Id::Learner(idx) => { + self.learners[idx].0 = None; + self.learners[idx].1.clone() + } + Id::Assistant => "assistant".into(), + }; + if conf.stats { + self._profiler.add_other(id, profiler) + } + } + + MsgKind::Unsat => if self.data.is_unsat().is_some() { + return Ok(Either::Right(self.unsat_core())); + }, + } + } } - } - - /// Retrieves the unsat core, if any. - pub fn unsat_core(& mut self) -> UnsatRes { - // UnsatRes::new( self.data.sample_graph() ) - UnsatRes::new( None ) - } - - /// Initial check, where all candidates are `true`. - /// - /// Drops the copy of the `Sender` end of the channel used to communicate - /// with the teacher (`self.to_teacher`). This entails that attempting to - /// receive messages will automatically fail if all learners are dead. - pub fn initial_check( - & mut self - ) -> Res< (Cexs, Candidates) > { - // Drop `to_teacher` sender so that we know when all kids are dead. - self.to_teacher = None ; - - let mut cands = PrdMap::with_capacity( self.instance.preds().len() ) ; - for pred in self.instance.pred_indices() { - if self.instance.forced_terms_of(pred).is_some() { - cands.push( None ) - - // } else if let Some(dnf) = partial_candidate.get(& pred) { - // let mut cand_dnf = vec![] ; - // for conj in dnf { - // let mut cand_conj = vec![] ; - // for tterms in conj { - // if let Some(term) = tterms.to_term() { - // term.subst() - // cand_conj.push(term) - // } else { - // cand_conj.clear() ; - // cand_dnf.clear() ; - // cands.push( Some(term::tru()) ) ; - // continue 'all_preds - // } - // } - // cand_dnf.push( term::and(cand_conj) ) - // } - // cands.push( Some( term::or(cand_dnf) ) ) - - } else { - cands.push( Some(term::tru()) ) - } + + /// Retrieves the unsat core, if any. + pub fn unsat_core(&mut self) -> UnsatRes { + // UnsatRes::new( self.data.sample_graph() ) + UnsatRes::new(None) } - let cands = self.complete_candidates(cands) ; + /// Initial check, where all candidates are `true`. + /// + /// Drops the copy of the `Sender` end of the channel used to communicate + /// with the teacher (`self.to_teacher`). This entails that attempting to + /// receive messages will automatically fail if all learners are dead. + pub fn initial_check(&mut self) -> Res<(Cexs, Candidates)> { + // Drop `to_teacher` sender so that we know when all kids are dead. + self.to_teacher = None; + + let mut cands = PrdMap::with_capacity(self.instance.preds().len()); + for pred in self.instance.pred_indices() { + if self.instance.forced_terms_of(pred).is_some() { + cands.push(None) + + // } else if let Some(dnf) = partial_candidate.get(& pred) { + // let mut cand_dnf = vec![] ; + // for conj in dnf { + // let mut cand_conj = vec![] ; + // for tterms in conj { + // if let Some(term) = tterms.to_term() { + // term.subst() + // cand_conj.push(term) + // } else { + // cand_conj.clear() ; + // cand_dnf.clear() ; + // cands.push( Some(term::tru()) ) ; + // continue 'all_preds + // } + // } + // cand_dnf.push( term::and(cand_conj) ) + // } + // cands.push( Some( term::or(cand_dnf) ) ) + } else { + cands.push(Some(term::tru())) + } + } + + let cands = self.complete_candidates(cands); - if_verb! { - log_verb! { " initial candidates:" } - for (pred, cand) in cands.index_iter() { - if let Some(cand) = cand.as_ref() { - log_verb! { - " {}: {}", self.instance[pred], cand + if_verb! { + log_verb! { " initial candidates:" } + for (pred, cand) in cands.index_iter() { + if let Some(cand) = cand.as_ref() { + log_verb! { + " {}: {}", self.instance[pred], cand + } + } } } - } + + self.get_cexs(&cands).map(|res| (res, cands)) } - self.get_cexs(& cands).map(|res| (res, cands)) - } - - - /// Defines the predicates given some candidates. - /// - /// Only defines predicates that are neither trivially true or false. - pub fn define_preds(& mut self, cands: & Candidates) -> Res<()> { - for (pred, cand) in cands.index_iter() { - if let Some(ref term) = * cand { - match term.bool() { - None => { - let pred = & self.instance[pred] ; - let sig: Vec<_> = pred.sig.index_iter().map( - |(var, typ)| (var, typ.get()) - ).collect() ; - self.solver.define_fun( - & pred.name, & sig, typ::bool().get(), & SmtTerm::new(& term) - ) ? - }, - Some(_) => (), + /// Defines the predicates given some candidates. + /// + /// Only defines predicates that are neither trivially true or false. + pub fn define_preds(&mut self, cands: &Candidates) -> Res<()> { + for (pred, cand) in cands.index_iter() { + if let Some(ref term) = *cand { + match term.bool() { + None => { + let pred = &self.instance[pred]; + let sig: Vec<_> = pred + .sig + .index_iter() + .map(|(var, typ)| (var, typ.get())) + .collect(); + self.solver.define_fun( + &pred.name, + &sig, + typ::bool().get(), + &SmtTerm::new(&term), + )? + } + Some(_) => (), + } + } } - } + Ok(()) } - Ok(()) - } - - - /// Registers predicates that are trivially true/false in a candidate. - /// - /// Returns the clauses that are trivially true given the candidate. - /// - /// Clears and then populates `self.clauses_to_ignore`, `self.tru_preds` and - /// `self.fls_preds`. - pub fn register_trivial(& mut self, cands: & Candidates) { - self.tru_preds.clear() ; - self.fls_preds.clear() ; - self.clauses_to_ignore.clear() ; - - for (pred, cand) in cands.index_iter() { - if let Some(ref term) = * cand { - - match term.bool() { - Some(true) => { - let _ = self.tru_preds.insert(pred) ; - self.clauses_to_ignore.extend( - self.instance.clauses_of(pred).1 - ) - }, - Some(false) => { - let _ = self.fls_preds.insert(pred) ; - self.clauses_to_ignore.extend( - self.instance.clauses_of(pred).0 - ) - }, - None => (), + + /// Registers predicates that are trivially true/false in a candidate. + /// + /// Returns the clauses that are trivially true given the candidate. + /// + /// Clears and then populates `self.clauses_to_ignore`, `self.tru_preds` and + /// `self.fls_preds`. + pub fn register_trivial(&mut self, cands: &Candidates) { + self.tru_preds.clear(); + self.fls_preds.clear(); + self.clauses_to_ignore.clear(); + + for (pred, cand) in cands.index_iter() { + if let Some(ref term) = *cand { + match term.bool() { + Some(true) => { + let _ = self.tru_preds.insert(pred); + self.clauses_to_ignore + .extend(self.instance.clauses_of(pred).1) + } + Some(false) => { + let _ = self.fls_preds.insert(pred); + self.clauses_to_ignore + .extend(self.instance.clauses_of(pred).0) + } + None => (), + } + } } - } } - } + /// Looks for falsifiable clauses given some candidates. + pub fn get_cexs(&mut self, cands: &Candidates) -> Res { + self.count += 1; - /// Looks for falsifiable clauses given some candidates. - pub fn get_cexs(& mut self, cands: & Candidates) -> Res< Cexs > { - self.count += 1 ; + self.register_trivial(cands); - self.register_trivial(cands) ; + let mut map = ClsHMap::with_capacity(self.instance.clauses().len()); - let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ; + if !self.restart_on_cex { + self.solver.push(1)?; + self.define_preds(cands)? + } - if ! self.restart_on_cex { - self.solver.push(1) ? ; - self.define_preds(cands) ? - } + let instance = self.instance.clone(); - let instance = self.instance.clone() ; + // True if we got some positive or negative samples. + // let mut got_pos_neg_samples = false ; - // True if we got some positive or negative samples. - // let mut got_pos_neg_samples = false ; + log! { @verb + "looking for counterexamples in positive clauses ({})...", + instance.pos_clauses().len() + } + for clause in instance.pos_clauses() { + self.get_cexs_of_clause(cands, *clause, &mut map, false)? + } - log! { @verb - "looking for counterexamples in positive clauses ({})...", - instance.pos_clauses().len() - } - for clause in instance.pos_clauses() { - self.get_cexs_of_clause( - cands, * clause, & mut map, false - ) ? - } + log! { @verb + "looking for counterexamples in strict negative clauses ({})...", + instance.strict_neg_clauses().len() + } + for clause in instance.strict_neg_clauses() { + self.get_cexs_of_clause(cands, *clause, &mut map, false)? + } - log! { @verb - "looking for counterexamples in strict negative clauses ({})...", - instance.strict_neg_clauses().len() - } - for clause in instance.strict_neg_clauses() { - self.get_cexs_of_clause( - cands, * clause, & mut map, false - ) ? - } + // got_pos_neg_samples = ! map.is_empty() ; - // got_pos_neg_samples = ! map.is_empty() ; - - if map.is_empty() - || ! conf.teacher.max_bias { - log! { @verb - "looking for counterexamples in non-strict negative clauses ({})...", - instance.non_strict_neg_clauses().len() - } - for clause in instance.non_strict_neg_clauses() { - self.get_cexs_of_clause( - cands, * clause, & mut map, true - ) ? - } - } + if map.is_empty() || !conf.teacher.max_bias { + log! { @verb + "looking for counterexamples in non-strict negative clauses ({})...", + instance.non_strict_neg_clauses().len() + } + for clause in instance.non_strict_neg_clauses() { + self.get_cexs_of_clause(cands, *clause, &mut map, true)? + } + } - if map.is_empty() - || ! conf.teacher.max_bias { - log_verb! { - "looking for counterexamples in implication clauses ({})...", - instance.imp_clauses().len() - } - - for clause in instance.imp_clauses() { - self.get_cexs_of_clause( - cands, * clause, & mut map, true - ) ? - } - } + if map.is_empty() || !conf.teacher.max_bias { + log_verb! { + "looking for counterexamples in implication clauses ({})...", + instance.imp_clauses().len() + } - log! { @debug - "extracted {} cexs", map.iter().fold( - 0, |acc, (_, cexs)| acc + cexs.len() - ) - } + for clause in instance.imp_clauses() { + self.get_cexs_of_clause(cands, *clause, &mut map, true)? + } + } - // if got_pos_neg_samples && conf.teacher.max_bias { - // map.retain( - // |_, cexs| { - // cexs.retain( |(_, bias)| ! bias.is_none() ) ; - // ! cexs.is_empty() - // } - // ) - // } - - if self.count % 100 == 0 - || self.restart_on_cex { - smt::reset(& mut self.solver, & self.instance) ? ; - } else { - self.solver.pop(1) ? - } + log! { @debug + "extracted {} cexs", map.iter().fold( + 0, |acc, (_, cexs)| acc + cexs.len() + ) + } - Ok(map) - } + // if got_pos_neg_samples && conf.teacher.max_bias { + // map.retain( + // |_, cexs| { + // cexs.retain( |(_, bias)| ! bias.is_none() ) ; + // ! cexs.is_empty() + // } + // ) + // } + + if self.count % 100 == 0 || self.restart_on_cex { + smt::reset(&mut self.solver, &self.instance)?; + } else { + self.solver.pop(1)? + } + Ok(map) + } + /// Retrieves counterexamples for a clause. + pub fn get_cexs_of_clause( + &mut self, + cands: &Candidates, + clause: ClsIdx, + map: &mut ClsHMap>, + bias: bool, + ) -> Res<()> { + if !self.clauses_to_ignore.contains(&clause) { + if self.restart_on_cex { + self.define_preds(cands)? + } else { + self.solver.push(1)? + } - /// Retrieves counterexamples for a clause. - pub fn get_cexs_of_clause( - & mut self, cands: & Candidates, - clause: ClsIdx, map: & mut ClsHMap>, bias: bool, - ) -> Res<()> { - if ! self.clauses_to_ignore.contains(& clause) { - if self.restart_on_cex { - self.define_preds(cands) ? - } else { - self.solver.push(1) ? - } + let cexs = self.get_cex(clause, bias, conf.teacher.max_bias)?; - let cexs = self.get_cex(clause, bias, conf.teacher.max_bias) ? ; + if self.restart_on_cex { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.pop(1)? + } - if self.restart_on_cex { - smt::reset(& mut self.solver, & self.instance) ? - } else { - self.solver.pop(1) ? - } + if !cexs.is_empty() { + let prev = map.insert(clause, cexs); + debug_assert_eq!(prev, None) + } + } - if ! cexs.is_empty() { - let prev = map.insert(clause, cexs) ; - debug_assert_eq!(prev, None) - } + Ok(()) } - Ok(()) - } - - - - /// Retrieves a counterexample given some bias. - fn get_bias_cex(& mut self, clause: ClsIdx, bias: & Bias) -> Res { - profile! { - self wrap { self.get_bias_cex_inner(clause, bias) } "cexs", "model" + /// Retrieves a counterexample given some bias. + fn get_bias_cex(&mut self, clause: ClsIdx, bias: &Bias) -> Res { + profile! { + self wrap { self.get_bias_cex_inner(clause, bias) } "cexs", "model" + } } - } - fn get_bias_cex_inner(& mut self, clause: ClsIdx, bias: & Bias) -> Res { - let model = self.solver.get_model() ? ; - let model = Parser.fix_model(model) ? ; - Cex::of_model( - self.instance[clause].vars(), model, - ! bias.is_none() && conf.teacher.partial - ) - } - - - /// Check-sats given an optional bias. - fn check_sat_cex( - & mut self, clause: ClsIdx, bias: Option<(Actlit, Bias)> - ) -> Res< Option<(Cex, Bias)> > { - if let Some((actlit, bias)) = bias { - - log! { @debug - " checksat with bias {}", bias.to_string(& self.instance) - } - self.solver.comment( - & format!("checksat with bias {}", bias.to_string(& self.instance)) - ) ? ; - profile!{ self tick "cexs", "biased check-sat" } - let sat = { - self.solver.check_sat_act( Some(& actlit) ) ? - } ; - - if sat { - log! { @debug " sat, getting cex" } - profile!{ self mark "cexs", "biased check-sat" } - let cex = self.get_bias_cex(clause, & bias) ? ; - log! { @debug " {}", cex } - self.solver.de_actlit(actlit) ? ; - Ok( - Some((cex, bias)) + fn get_bias_cex_inner(&mut self, clause: ClsIdx, bias: &Bias) -> Res { + let model = self.solver.get_model()?; + let model = Parser.fix_model(model)?; + Cex::of_model( + self.instance[clause].vars(), + model, + !bias.is_none() && conf.teacher.partial, ) - } else { - Ok(None) - } - - } else { - - log! { @debug " checksat" } - let (sat, actlit) = profile! { - self wrap { - - if self.using_rec_funs { - self.solver.get_actlit().and_then( - |actlit| { - let sat = self.solver.check_sat_act( Some(& actlit) ) ? ; - Ok( (sat, Some(actlit)) ) - } - ) - } else { - self.solver.check_sat().map( - |sat| (sat, None) - ) - } + } - } "cexs", "check-sat" - } ? ; - - let res = if sat { - log! { @debug " sat, getting cex" } - let bias = if self.instance[clause].is_positive() { - Bias::Lft - } else if self.instance[clause].is_strict_neg() { - let ( - pred, args - ) = self.instance[clause].lhs_preds().iter().next().map( - |(pred, argss)| ( - * pred, argss.iter().next().unwrap().clone() - ) - ).unwrap() ; - Bias::NuRgt(pred, args) + /// Check-sats given an optional bias. + fn check_sat_cex( + &mut self, + clause: ClsIdx, + bias: Option<(Actlit, Bias)>, + ) -> Res> { + if let Some((actlit, bias)) = bias { + log! { @debug + " checksat with bias {}", bias.to_string(& self.instance) + } + self.solver.comment(&format!( + "checksat with bias {}", + bias.to_string(&self.instance) + ))?; + profile!{ self tick "cexs", "biased check-sat" } + let sat = { self.solver.check_sat_act(Some(&actlit))? }; + + if sat { + log! { @debug " sat, getting cex" } + profile!{ self mark "cexs", "biased check-sat" } + let cex = self.get_bias_cex(clause, &bias)?; + log! { @debug " {}", cex } + self.solver.de_actlit(actlit)?; + Ok(Some((cex, bias))) + } else { + Ok(None) + } } else { - Bias::Non - } ; - let cex = self.get_bias_cex(clause, & bias) ? ; - log! { @debug " {}", cex } - Ok( - Some((cex, bias)) - ) - - } else { - Ok(None) - } ; - - if let Some(actlit) = actlit { - self.solver.de_actlit(actlit) ? - } - - res + log! { @debug " checksat" } + let (sat, actlit) = profile! { + self wrap { + + if self.using_rec_funs { + self.solver.get_actlit().and_then( + |actlit| { + let sat = self.solver.check_sat_act( Some(& actlit) ) ? ; + Ok( (sat, Some(actlit)) ) + } + ) + } else { + self.solver.check_sat().map( + |sat| (sat, None) + ) + } + + } "cexs", "check-sat" + }?; + + let res = if sat { + log! { @debug " sat, getting cex" } + let bias = if self.instance[clause].is_positive() { + Bias::Lft + } else if self.instance[clause].is_strict_neg() { + let (pred, args) = self.instance[clause] + .lhs_preds() + .iter() + .next() + .map(|(pred, argss)| (*pred, argss.iter().next().unwrap().clone())) + .unwrap(); + Bias::NuRgt(pred, args) + } else { + Bias::Non + }; + let cex = self.get_bias_cex(clause, &bias)?; + log! { @debug " {}", cex } + Ok(Some((cex, bias))) + } else { + Ok(None) + }; + + if let Some(actlit) = actlit { + self.solver.de_actlit(actlit)? + } + res + } } - } - + /// Checks if a clause is falsifiable and returns a model if it is. + pub fn get_cex(&mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool) -> Res> { + let mut cexs = vec![]; - /// Checks if a clause is falsifiable and returns a model if it is. - pub fn get_cex( - & mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool - ) -> Res< Vec > { - let mut cexs = vec![] ; + log! { @debug "working on clause #{}", clause_idx } - log! { @debug "working on clause #{}", clause_idx } - - // Macro to avoid borrowing `self.instance`. - macro_rules! clause { - () => ( & self.instance[clause_idx] ) ; - } - - if conf.solver.log { - self.solver.comment_args( - format_args!( - "\n\nClause # {}: {}", - clause_idx, - clause!().to_string_info(& self.instance.preds()).unwrap() - ) - ) ? - } + // Macro to avoid borrowing `self.instance`. + macro_rules! clause { + () => { + &self.instance[clause_idx] + }; + } - profile!{ self tick "cexs", "prep" } - clause!().declare(& mut self.solver) ? ; - self.solver.assert_with( - clause!(), & ( - false, & self.tru_preds, & self.fls_preds, self.instance.preds() - ) - ) ? ; - profile!{ self mark "cexs", "prep" } - - macro_rules! get_cex { - - () => ( // Normal check, no actlit. - if let Some(cex) = self.check_sat_cex( - clause_idx, None - ) ? { - cexs.push(cex) + if conf.solver.log { + self.solver.comment_args(format_args!( + "\n\nClause # {}: {}", + clause_idx, + clause!().to_string_info(&self.instance.preds()).unwrap() + ))? } - ) ; - ($actlit:expr ; $bias:expr) => ( - if let Some(cex) = self.check_sat_cex( - clause_idx, Some(($actlit, $bias)) - ) ? { - cexs.push(cex) + profile!{ self tick "cexs", "prep" } + clause!().declare(&mut self.solver)?; + self.solver.assert_with( + clause!(), + &( + false, + &self.tru_preds, + &self.fls_preds, + self.instance.preds(), + ), + )?; + profile!{ self mark "cexs", "prep" } + + macro_rules! get_cex { + () => { + // Normal check, no actlit. + if let Some(cex) = self.check_sat_cex(clause_idx, None)? { + cexs.push(cex) + } + }; + + ($actlit:expr ; $bias:expr) => { + if let Some(cex) = self.check_sat_cex(clause_idx, Some(($actlit, $bias)))? { + cexs.push(cex) + } + }; } - ) ; - } + get_cex!(); + + if bias { + let unbiased_cex = cexs.pop(); + + // Don't try bias examples if instance is unsat. + if unbiased_cex.is_some() && !self.using_rec_funs { + log! { @3 "generating bias actlits" } + let biased = profile! { + self wrap { + self.bias.apply( + & self._profiler, & mut self.solver, + clause_idx, & self.instance, & self.data, + bias_only + ) + } "cexs", "bias generation" + }?; + log! { @3 "working on {} biased checks", biased.len() } + for (actlit, bias) in biased { + get_cex! { actlit ; bias } + } + } - get_cex!() ; - - if bias { - let unbiased_cex = cexs.pop() ; - - // Don't try bias examples if instance is unsat. - if unbiased_cex.is_some() - && ! self.using_rec_funs { - log! { @3 "generating bias actlits" } - let biased = profile! { - self wrap { - self.bias.apply( - & self._profiler, & mut self.solver, - clause_idx, & self.instance, & self.data, - bias_only - ) - } "cexs", "bias generation" - } ? ; - log! { @3 "working on {} biased checks", biased.len() } - for (actlit, bias) in biased { - get_cex! { actlit ; bias } + // Add the unbiased cex back if bias checks yielded nothing. + if !conf.teacher.max_bias || cexs.is_empty() { + if let Some(unbiased_cex) = unbiased_cex { + cexs.push(unbiased_cex) + } + } } - } - // Add the unbiased cex back if bias checks yielded nothing. - if ! conf.teacher.max_bias || cexs.is_empty() { - if let Some(unbiased_cex) = unbiased_cex { - cexs.push(unbiased_cex) - } - } + Ok(cexs) } - - Ok(cexs) - } - -} \ No newline at end of file +} diff --git a/src/term/eval.rs b/src/term/eval.rs index a090c359..dccaa9a2 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -1,368 +1,356 @@ //! Term evaluation using the term zipper. -use std::slice::Iter ; +use std::slice::Iter; -use common::* ; -use term::zip::* ; +use common::*; +use term::zip::*; /// Zipper frames for term evaluation. -pub type Frame<'a> = ZipFrame< 'a, Vec > ; +pub type Frame<'a> = ZipFrame<'a, Vec>; /// Zipper command for term evaluation. -pub type Cmd<'a> = ZipDo< 'a, Vec, Val > ; +pub type Cmd<'a> = ZipDo<'a, Vec, Val>; /// Zipper command (total case) for term evaluation. -pub type CmdT<'a> = ZipDoTotal< 'a, Val > ; +pub type CmdT<'a> = ZipDoTotal<'a, Val>; /// Term evaluation. -pub fn eval(term: & Term, model: & E) -> Res { - if let Some(val) = term.val() { - return Ok(val) - } else if let Some(idx) = term.var_idx() { - if idx < model.len() { - return Ok( model.get(idx).clone() ) - } else { - bail!("model is too short ({} / {})", * idx, model.len()) +pub fn eval(term: &Term, model: &E) -> Res { + if let Some(val) = term.val() { + return Ok(val); + } else if let Some(idx) = term.var_idx() { + if idx < model.len() { + return Ok(model.get(idx).clone()); + } else { + bail!("model is too short ({} / {})", *idx, model.len()) + } } - } - - - let mut fun_ref_count = 0 ; - - - let res = zip( - term, |zip_null| leaf(model, zip_null), - |op, typ, values| total(op, typ, values, & mut fun_ref_count), - partial - ) ; - - fun::decrease_ref_count(fun_ref_count) ; - // if let Ok(res) = res.as_ref() { - // if model.len() > 0 - // && ! res.is_known() { - // println!("eval {}", term) ; - // for v in 0 .. model.len() { - // println!(" v_{}: {}", v, model.get( v.into() )) - // } - // println!("= {}", res) ; - // pause(" blah", & Profiler::new()) ; - // } - // } - res -} + let mut fun_ref_count = 0; + + let res = zip( + term, + |zip_null| leaf(model, zip_null), + |op, typ, values| total(op, typ, values, &mut fun_ref_count), + partial, + ); + + fun::decrease_ref_count(fun_ref_count); + // if let Ok(res) = res.as_ref() { + // if model.len() > 0 + // && ! res.is_known() { + // println!("eval {}", term) ; + // for v in 0 .. model.len() { + // println!(" v_{}: {}", v, model.get( v.into() )) + // } + // println!("= {}", res) ; + // pause(" blah", & Profiler::new()) ; + // } + // } + res +} macro_rules! go { - (up $e:expr) => ( - return Ok( ZipDo::Upp { yielded: $e } ) - ) ; - (down $e:expr) => ( - return Ok( ZipDo::Dwn { nu_term: $e } ) - ) ; + (up $e:expr) => { + return Ok(ZipDo::Upp { yielded: $e }); + }; + (down $e:expr) => { + return Ok(ZipDo::Dwn { nu_term: $e }); + }; } - -fn leaf<'a, E: Evaluator>( - model: & E, zip_null: ZipNullary<'a>, -) -> Res { - match zip_null { - ZipNullary::Cst(val) => Ok( val.clone() ), - ZipNullary::Var(_, var) => if var < model.len() { - Ok( model.get(var).clone() ) - } else { - bail!("model is too short ({} / {})", * var, model.len()) - }, - } +fn leaf<'a, E: Evaluator>(model: &E, zip_null: ZipNullary<'a>) -> Res { + match zip_null { + ZipNullary::Cst(val) => Ok(val.clone()), + ZipNullary::Var(_, var) => if var < model.len() { + Ok(model.get(var).clone()) + } else { + bail!("model is too short ({} / {})", *var, model.len()) + }, + } } - fn total<'a>( - op: ZipOp<'a>, typ: & 'a Typ, mut values: Vec, - fun_ref_count: & mut usize -) -> Res< CmdT<'a> > { - let yielded = match op { - ZipOp::Op(op) => { - op.eval(values).chain_err( - || format!("while evaluating operator `{}`", op) - ) ? - }, - - ZipOp::New(name) => val::dtyp_new( - typ.clone(), name.clone(), values - ), - - ZipOp::Slc(name) => if values.len() == 1 { - let value = values.pop().unwrap() ; - if ! value.is_known() { - val::none( typ.clone() ) - } else if let Some( - (ty, constructor, values) - ) = value.dtyp_inspect() { - if let Some((dtyp, _)) = ty.dtyp_inspect() { - - if let Some(selectors) = dtyp.news.get(constructor) { - - let mut res = None ; - for ((selector, _), value) in selectors.iter().zip( - values.iter() - ) { - if selector == name { - res = Some( value.clone() ) - } + op: ZipOp<'a>, + typ: &'a Typ, + mut values: Vec, + fun_ref_count: &mut usize, +) -> Res> { + let yielded = match op { + ZipOp::Op(op) => op + .eval(values) + .chain_err(|| format!("while evaluating operator `{}`", op))?, + + ZipOp::New(name) => val::dtyp_new(typ.clone(), name.clone(), values), + + ZipOp::Slc(name) => if values.len() == 1 { + let value = values.pop().unwrap(); + if !value.is_known() { + val::none(typ.clone()) + } else if let Some((ty, constructor, values)) = value.dtyp_inspect() { + if let Some((dtyp, _)) = ty.dtyp_inspect() { + if let Some(selectors) = dtyp.news.get(constructor) { + let mut res = None; + for ((selector, _), value) in selectors.iter().zip(values.iter()) { + if selector == name { + res = Some(value.clone()) + } + } + + if let Some(res) = res { + res + } else { + val::none(typ.clone()) + } + } else { + let e: Error = format!( + "unknown selector `{}` for datatype {}", + conf.bad(constructor), + dtyp.name + ).into(); + bail!(e.chain_err(|| dtyp::constructors_as_error(&dtyp.name))) + } + } else { + bail!("inconsistent type {} for value {}", ty, value) + } + } else { + bail!( + "illegal application of selector `{}` of `{}` to `{}`", + conf.bad(&name), + typ, + value + ) } + } else { + bail!( + "expected one value for datatype selection, found {}", + values.len() + ) + }, - if let Some(res) = res { - res + ZipOp::Tst(name) => if values.len() == 1 { + let value = values.pop().unwrap(); + if !value.is_known() { + val::none(typ.clone()) + } else if let Some((_, constructor, _)) = value.dtyp_inspect() { + val::bool(constructor == name) } else { - val::none( typ.clone() ) + bail!( + "illegal application of tester `{}` to {}: {}", + conf.bad(&name), + value, + value.typ() + ) } - - } else { - let e: Error = format!( - "unknown selector `{}` for datatype {}", - conf.bad(constructor), dtyp.name - ).into() ; + } else { bail!( - e.chain_err( || dtyp::constructors_as_error(& dtyp.name) ) + "expected one value for datatype selection, found {}", + values.len() ) - } + }, + ZipOp::CArray => if values.len() == 1 { + let default = values.pop().unwrap(); + val::array(typ.clone(), default) } else { - bail!("inconsistent type {} for value {}", ty, value) - } - } else { - bail!( - "illegal application of selector `{}` of `{}` to `{}`", - conf.bad(& name), typ, value - ) - } - } else { - bail!( - "expected one value for datatype selection, found {}", values.len() - ) - }, - - ZipOp::Tst(name) => if values.len() == 1 { - let value = values.pop().unwrap() ; - if ! value.is_known() { - val::none( typ.clone() ) - } else if let Some( - (_, constructor, _) - ) = value.dtyp_inspect() { - val::bool( constructor == name ) - } else { - bail!( - "illegal application of tester `{}` to {}: {}", - conf.bad(& name), value, value.typ() - ) - } - } else { - bail!( - "expected one value for datatype selection, found {}", values.len() - ) - }, - - ZipOp::CArray => if values.len() == 1 { - let default = values.pop().unwrap() ; - val::array( typ.clone(), default ) - } else { - bail!( - "expected one value for constant array construction, found {}", - values.len() - ) - }, - - ZipOp::Fun(name) => { - let fun = if let Some(fun) = fun::get_as_ref(name) { - fun - } else { - bail!("cannot evaluate unknown function `{}`", conf.bad(name)) - } ; - * fun_ref_count += 1 ; - - if values.len() != fun.sig.len() { - bail!( - "illegal application of function `{}` to {} arguments (expected {})", - conf.bad(name), values.len(), fun.sig.len() - ) - } - - return Ok( - ZipDoTotal::Dwn { - nu_term: & fun.def, - nu_subst: Some( values.into() ), - } - ) - }, - } ; - - Ok( ZipDoTotal::Upp { yielded } ) -} - + bail!( + "expected one value for constant array construction, found {}", + values.len() + ) + }, + ZipOp::Fun(name) => { + let fun = if let Some(fun) = fun::get_as_ref(name) { + fun + } else { + bail!("cannot evaluate unknown function `{}`", conf.bad(name)) + }; + *fun_ref_count += 1; + + if values.len() != fun.sig.len() { + bail!( + "illegal application of function `{}` to {} arguments (expected {})", + conf.bad(name), + values.len(), + fun.sig.len() + ) + } + return Ok(ZipDoTotal::Dwn { + nu_term: &fun.def, + nu_subst: Some(values.into()), + }); + } + }; + Ok(ZipDoTotal::Upp { yielded }) +} fn partial<'a>( - Frame { thing, typ, lft_args, mut rgt_args }: Frame<'a> -) -> Res< Cmd<'a> > { - - match thing { - ZipOp::Op(op) => partial_op(op, typ, lft_args, rgt_args), - thing @ ZipOp::New(_) | - thing @ ZipOp::Fun(_) | - thing @ ZipOp::CArray | - thing @ ZipOp::Slc(_) | - thing @ ZipOp::Tst(_) => { - let nu_term = rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args` (eval::partial)" - ) ; - Ok( - ZipDo::Trm { - nu_term, frame: Frame { thing, typ, lft_args, rgt_args } + Frame { + thing, + typ, + lft_args, + mut rgt_args, + }: Frame<'a>, +) -> Res> { + match thing { + ZipOp::Op(op) => partial_op(op, typ, lft_args, rgt_args), + thing @ ZipOp::New(_) + | thing @ ZipOp::Fun(_) + | thing @ ZipOp::CArray + | thing @ ZipOp::Slc(_) + | thing @ ZipOp::Tst(_) => { + let nu_term = rgt_args + .next() + .expect("illegal call to `partial_op`: empty `rgt_args` (eval::partial)"); + Ok(ZipDo::Trm { + nu_term, + frame: Frame { + thing, + typ, + lft_args, + rgt_args, + }, + }) } - ) - }, - } - + } } +fn partial_op<'a>( + op: Op, + typ: &'a Typ, + mut lft_args: Vec, + mut rgt_args: Iter<'a, Term>, +) -> Res, Val>> { + // Since this is called each time a value is added to `lft_args`, we only + // need to check the last value in `lft_args`. + + match op { + Op::Ite => if lft_args.len() == 1 { + let cond = lft_args.pop().expect("pop failed on vector of length 1"); + + match cond + .to_bool() + .chain_err(|| "during `Ite` condition evaluation")? + { + Some(cond) => if let (Some(t), Some(e), None) = + (rgt_args.next(), rgt_args.next(), rgt_args.next()) + { + if cond { + go!(down t) + } else { + go!(down e) + } + } else { + bail!("illegal application of `Ite`") + }, + + None if !cond.is_known() => if let (Some(t), Some(e), None) = + (rgt_args.next(), rgt_args.next(), rgt_args.next()) + { + debug_assert_eq!(t.typ(), e.typ()); + + go!(up val::none(t.typ())) + } else { + bail!("illegal application of `Ite`") + }, + + None => (), + } + }, + Op::And => if let Some(last) = lft_args.pop() { + match last.to_bool()? { + // False, no need to evaluate the other arguments. + Some(false) => go!( up val::bool(false) ), + // True, just skip. + Some(true) => (), + // Unknown, push back and keep going. + None => lft_args.push(last), + } + }, - -fn partial_op<'a>( - op: Op, typ: & 'a Typ, mut lft_args: Vec, mut rgt_args: Iter<'a, Term> -) -> Res< ZipDo< 'a, Vec, Val > > { - // Since this is called each time a value is added to `lft_args`, we only - // need to check the last value in `lft_args`. - - match op { - - Op::Ite => if lft_args.len() == 1 { - let cond = lft_args.pop().expect("pop failed on vector of length 1") ; - - match cond.to_bool().chain_err( - || "during `Ite` condition evaluation" - ) ? { - Some(cond) => if let (Some(t), Some(e), None) = ( - rgt_args.next(), rgt_args.next(), rgt_args.next() - ) { - if cond { - go!(down t) - } else { - go!(down e) - } - } else { - bail!("illegal application of `Ite`") + Op::Or => if let Some(last) = lft_args.pop() { + match last.to_bool()? { + // True, no need to evaluate the other arguments. + Some(true) => go!( up val::bool(true) ), + // False, just skip. + Some(false) => (), + // Unknown, push back and keep going. + None => lft_args.push(last), + } }, - None if ! cond.is_known() => if let (Some(t), Some(e), None) = ( - rgt_args.next(), rgt_args.next(), rgt_args.next() - ) { - debug_assert_eq!( t.typ(), e.typ() ) ; + Op::Mul => if let Some(last) = lft_args.last() { + if last.is_zero() || !last.is_known() { + go!( up last.clone() ) + } + }, - go!(up val::none(t.typ())) - } else { - bail!("illegal application of `Ite`") + Op::Mod | Op::Rem => if let Some(last) = lft_args.last() { + debug_assert! { lft_args.len() == 1 } + if last.is_zero() || !last.is_known() { + go!( up last.clone() ) + } }, - None => (), - } - }, - - Op::And => if let Some(last) = lft_args.pop() { - match last.to_bool() ? { - // False, no need to evaluate the other arguments. - Some(false) => go!( up val::bool(false) ), - // True, just skip. - Some(true) => (), - // Unknown, push back and keep going. - None => lft_args.push(last), - } - }, - - Op::Or => if let Some(last) = lft_args.pop() { - match last.to_bool() ? { - // True, no need to evaluate the other arguments. - Some(true) => go!( up val::bool(true) ), - // False, just skip. - Some(false) => (), - // Unknown, push back and keep going. - None => lft_args.push(last), - } - }, - - Op::Mul => if let Some(last) = lft_args.last() { - if last.is_zero() || ! last.is_known() { - go!( up last.clone() ) - } - }, - - Op::Mod | Op::Rem => if let Some(last) = lft_args.last() { - debug_assert! { lft_args.len() == 1 } - if last.is_zero() || ! last.is_known() { - go!( up last.clone() ) - } - }, - - Op::Impl => if let Some(last) = lft_args.last() { - debug_assert! { lft_args.len() == 1 } - if last.is_false() { - go!( up val::bool(true) ) - } - }, - - Op::Distinct => if let Some(last) = lft_args.last() { - if last.is_known() { - for other in lft_args.iter().take(lft_args.len() - 1) { - if last == other { - go!( up val::bool(false) ) - } - } - } - }, - - Op::Add | Op::Sub | - Op::CMul | Op::IDiv | Op::Div | - Op::Gt | Op::Ge | Op::Le | Op::Lt | - Op::Eql - => if let Some(last) = lft_args.last() { - if ! last.is_known() { - return Ok( - ZipDo::Upp { yielded: val::none( typ.clone() ) } - ) - } - }, - - Op::Store | Op::Select => (), - - Op::Not | - Op::ToInt | - Op::ToReal => bail!( - "partial application of unary operator ({}) makes no sense", op - ), - - } - - // Normal exit. - if let Some(nu_term) = rgt_args.next() { - Ok( - ZipDo::Trm { - nu_term, frame: Frame { - thing: ZipOp::Op(op), typ, lft_args, rgt_args - } - } - ) - } else { - println!("{}", op) ; - for arg in & lft_args { - println!(" {}", arg) - } - panic!( - "illegal call to `partial_op`: empty `rgt_args` (partial_op)" - ) - } -} + Op::Impl => if let Some(last) = lft_args.last() { + debug_assert! { lft_args.len() == 1 } + if last.is_false() { + go!( up val::bool(true) ) + } + }, + Op::Distinct => if let Some(last) = lft_args.last() { + if last.is_known() { + for other in lft_args.iter().take(lft_args.len() - 1) { + if last == other { + go!( up val::bool(false) ) + } + } + } + }, + Op::Add + | Op::Sub + | Op::CMul + | Op::IDiv + | Op::Div + | Op::Gt + | Op::Ge + | Op::Le + | Op::Lt + | Op::Eql => if let Some(last) = lft_args.last() { + if !last.is_known() { + return Ok(ZipDo::Upp { + yielded: val::none(typ.clone()), + }); + } + }, + Op::Store | Op::Select => (), + Op::Not | Op::ToInt | Op::ToReal => bail!( + "partial application of unary operator ({}) makes no sense", + op + ), + } + // Normal exit. + if let Some(nu_term) = rgt_args.next() { + Ok(ZipDo::Trm { + nu_term, + frame: Frame { + thing: ZipOp::Op(op), + typ, + lft_args, + rgt_args, + }, + }) + } else { + println!("{}", op); + for arg in &lft_args { + println!(" {}", arg) + } + panic!("illegal call to `partial_op`: empty `rgt_args` (partial_op)") + } +} diff --git a/src/term/factory.rs b/src/term/factory.rs index 8e511d83..245978fc 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -1,9 +1,9 @@ //! Term creation functions. -use hashconsing::HashConsign ; +use hashconsing::HashConsign; -use common::* ; -use term::{ RTerm, Term, Op } ; +use common::*; +use term::{Op, RTerm, Term}; new_consign! { /// Term factory. @@ -17,194 +17,204 @@ lazy_static! { ) ; } - /// Scans a term to extract the variables that appear in it. -fn scan_vars(t: & Term) -> VarSet { - let mut to_do = vec![ t ] ; - let mut set = VarSet::with_capacity(11) ; - - while let Some(term) = to_do.pop() { - match term.get() { - RTerm::Var(_, i) => { - let _ = set.insert(* i) ; () - }, - RTerm::Cst(_) => (), - - RTerm::App { args, .. } | - RTerm::Fun { args, .. } | - RTerm::DTypNew { args, .. } => for arg in args { - to_do.push(arg) - }, - - RTerm::CArray { term, .. } | - RTerm::DTypSlc { term, .. } | - RTerm::DTypTst { term, .. } => to_do.push(term), +fn scan_vars(t: &Term) -> VarSet { + let mut to_do = vec![t]; + let mut set = VarSet::with_capacity(11); + + while let Some(term) = to_do.pop() { + match term.get() { + RTerm::Var(_, i) => { + let _ = set.insert(*i); + () + } + RTerm::Cst(_) => (), + + RTerm::App { args, .. } | RTerm::Fun { args, .. } | RTerm::DTypNew { args, .. } => { + for arg in args { + to_do.push(arg) + } + } + + RTerm::CArray { term, .. } + | RTerm::DTypSlc { term, .. } + | RTerm::DTypTst { term, .. } => to_do.push(term), + } } - } - set.shrink_to_fit() ; - set + set.shrink_to_fit(); + set } /// Variables appearing in a term (cached). #[inline] -pub fn vars(t: & Term) -> VarSet { - if let Some(vars) = var_cache.read().expect( - "variable cache is corrupted..." - ).get(t) { - return vars.clone() - } - var_cache.write().expect( - "variable cache is corrupted..." - ).entry( t.clone() ).or_insert_with( - || scan_vars(t) - ).clone() +pub fn vars(t: &Term) -> VarSet { + if let Some(vars) = var_cache + .read() + .expect("variable cache is corrupted...") + .get(t) + { + return vars.clone(); + } + var_cache + .write() + .expect("variable cache is corrupted...") + .entry(t.clone()) + .or_insert_with(|| scan_vars(t)) + .clone() } /// Map over the variables appearing in a term (cached). #[inline] -pub fn map_vars(t: & Term, mut f: F) -where F: FnMut(VarIdx) { - if let Some(vars) = var_cache.read().expect( - "variable cache is corrupted..." - ).get(t) { - for var in vars { - f(* var) +pub fn map_vars(t: &Term, mut f: F) +where + F: FnMut(VarIdx), +{ + if let Some(vars) = var_cache + .read() + .expect("variable cache is corrupted...") + .get(t) + { + for var in vars { + f(*var) + } + return (); } - return () - } - - let vars = scan_vars(t) ; - for var in & vars { - f(* var) - } - var_cache.write().expect( - "variable cache is corrupted..." - ).entry( t.clone() ).or_insert_with( - || vars - ) ; - () + + let vars = scan_vars(t); + for var in &vars { + f(*var) + } + var_cache + .write() + .expect("variable cache is corrupted...") + .entry(t.clone()) + .or_insert_with(|| vars); + () } /// Creates a term. #[inline] pub fn term(t: RTerm) -> Term { - factory.mk(t) + factory.mk(t) } /// Creates a variable. #[inline] pub fn var>(v: V, typ: Typ) -> Term { - factory.mk( RTerm::Var(typ, v.into()) ) + factory.mk(RTerm::Var(typ, v.into())) } /// Creates an integer variable. #[inline] pub fn int_var>(v: V) -> Term { - factory.mk( RTerm::Var(typ::int(), v.into()) ) + factory.mk(RTerm::Var(typ::int(), v.into())) } /// Creates a real variable. #[inline] pub fn real_var>(v: V) -> Term { - factory.mk( RTerm::Var(typ::real(), v.into()) ) + factory.mk(RTerm::Var(typ::real(), v.into())) } /// Creates a boolean variable. #[inline] pub fn bool_var>(v: V) -> Term { - factory.mk( RTerm::Var(typ::bool(), v.into()) ) + factory.mk(RTerm::Var(typ::bool(), v.into())) } /// Creates a constant. #[inline] pub fn cst>(val: V) -> Term { - let val = val.into() ; - if ! val.is_known() { - panic!("trying to construct a constant term from a non-value {}", val) - } - factory.mk( RTerm::Cst( val ) ) + let val = val.into(); + if !val.is_known() { + panic!( + "trying to construct a constant term from a non-value {}", + val + ) + } + factory.mk(RTerm::Cst(val)) } /// Creates an integer constant. #[inline] pub fn int>(i: I) -> Term { - let i = i.into() ; - factory.mk( RTerm::Cst( val::int(i) ) ) + let i = i.into(); + factory.mk(RTerm::Cst(val::int(i))) } /// Creates a real constant. #[inline] pub fn real>(r: R) -> Term { - let r = r.into() ; - factory.mk( RTerm::Cst( val::real(r) ) ) + let r = r.into(); + factory.mk(RTerm::Cst(val::real(r))) } /// Creates a real constant from a float. #[inline] pub fn real_of_float(f: f64) -> Term { - real( rat_of_float(f) ) + real(rat_of_float(f)) } /// Creates the constant `0`. #[inline] pub fn int_zero() -> Term { - int( Int::zero() ) + int(Int::zero()) } /// Creates the constant `1`. #[inline] pub fn int_one() -> Term { - int( Int::one() ) + int(Int::one()) } /// Creates the constant `0`. #[inline] pub fn real_zero() -> Term { - real( Rat::zero() ) + real(Rat::zero()) } /// Creates the constant `1`. #[inline] pub fn real_one() -> Term { - real( Rat::one() ) + real(Rat::one()) } /// Creates a boolean. #[inline] pub fn bool(b: bool) -> Term { - factory.mk( RTerm::Cst( val::bool(b) ) ) + factory.mk(RTerm::Cst(val::bool(b))) } /// Creates the constant `true`. #[inline] pub fn tru() -> Term { - bool(true) + bool(true) } /// Creates the constant `false`. #[inline] pub fn fls() -> Term { - bool(false) + bool(false) } /// If-then-else. #[inline] pub fn ite(c: Term, t: Term, e: Term) -> Term { - app(Op::Ite, vec![c, t, e]) + app(Op::Ite, vec![c, t, e]) } /// Implication. #[inline] pub fn implies(lhs: Term, rhs: Term) -> Term { - app(Op::Impl, vec![lhs, rhs]) + app(Op::Impl, vec![lhs, rhs]) } /// Negates a term. #[inline] pub fn not(term: Term) -> Term { - app(Op::Not, vec![term]) + app(Op::Not, vec![term]) } /// Disjunction. #[inline] pub fn or(terms: Vec) -> Term { - app(Op::Or, terms) + app(Op::Or, terms) } /// Conjunction. #[inline] pub fn and(terms: Vec) -> Term { - app(Op::And, terms) + app(Op::And, terms) } /// Constant array. @@ -212,24 +222,22 @@ pub fn and(terms: Vec) -> Term { /// The type is the type of **the indices** of the array. #[inline] pub fn cst_array(typ: Typ, default: Term) -> Term { - if let Some(val) = default.val() { - factory.mk( - RTerm::Cst( val::array(typ, val) ) - ) - } else { - factory.mk( RTerm::CArray { typ, term: default } ) - } + if let Some(val) = default.val() { + factory.mk(RTerm::Cst(val::array(typ, val))) + } else { + factory.mk(RTerm::CArray { typ, term: default }) + } } /// Store operation in an array. #[inline] pub fn store(array: Term, idx: Term, val: Term) -> Term { - app( Op::Store, vec![ array, idx, val ] ) + app(Op::Store, vec![array, idx, val]) } /// Select operation for an array. #[inline] pub fn select(array: Term, idx: Term) -> Term { - app( Op::Select, vec![ array, idx ] ) + app(Op::Select, vec![array, idx]) } /// Function application. @@ -241,25 +249,23 @@ pub fn select(array: Term, idx: Term) -> Term { /// - if the arguments are illegal #[inline] pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { - if let Err(e) = fun::dec_do( - & name, |fun| { - debug_assert_eq! { typ, fun.typ } - if args.len() != fun.sig.len() { - panic!("illegal application of function {}", conf.bad(& name)) - } - for (info, arg) in fun.sig.iter().zip( args.iter_mut() ) { - if let Some(nu_arg) = arg.force_dtyp( info.typ.clone() ) { - * arg = nu_arg + if let Err(e) = fun::dec_do(&name, |fun| { + debug_assert_eq! { typ, fun.typ } + if args.len() != fun.sig.len() { + panic!("illegal application of function {}", conf.bad(&name)) + } + for (info, arg) in fun.sig.iter().zip(args.iter_mut()) { + if let Some(nu_arg) = arg.force_dtyp(info.typ.clone()) { + *arg = nu_arg + } } - } - Ok(()) + Ok(()) + }) { + print_err(&e); + panic!("illegal function application") } - ) { - print_err(& e) ; - panic!("illegal function application") - } - factory.mk( RTerm::Fun { typ, name, args } ) + factory.mk(RTerm::Fun { typ, name, args }) } /// Creates an operator application. @@ -269,7 +275,7 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn app(op: Op, mut args: Vec) -> Term { - let typ = expect!( + let typ = expect!( op.type_check(& mut args) => |e| let res: Res<()> = Err( "Fatal internal type checking error, \ @@ -300,21 +306,20 @@ pub fn app(op: Op, mut args: Vec) -> Term { ), term::TypError::Msg(blah) => res.chain_err(|| blah) }.unwrap_err() - ) ; + ); - normalize(op, args, typ.clone()) + normalize(op, args, typ.clone()) } /// Creates a constant term. pub fn val(val: Val) -> Term { - factory.mk( RTerm::Cst(val) ) + factory.mk(RTerm::Cst(val)) } - /// Creates a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { - let rterm = term::simplify::dtyp_new(typ, name, args) ; - factory.mk(rterm) + let rterm = term::simplify::dtyp_new(typ, name, args); + factory.mk(rterm) } /// Creates a new datatype selector. @@ -323,10 +328,10 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { /// /// - treat constants better pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { - match term::simplify::dtyp_slc(typ, name, term) { - Either::Left(rterm) => factory.mk(rterm), - Either::Right(term) => term, - } + match term::simplify::dtyp_slc(typ, name, term) { + Either::Left(rterm) => factory.mk(rterm), + Either::Right(term) => term, + } } /// Creates a new datatype tester. @@ -335,13 +340,13 @@ pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { /// /// - treat constants better pub fn dtyp_tst(name: String, term: Term) -> Term { - let (rterm, positive) = term::simplify::dtyp_tst(name, term) ; - let res = factory.mk( rterm ) ; - if ! positive { - not(res) - } else { - res - } + let (rterm, positive) = term::simplify::dtyp_tst(name, term); + let res = factory.mk(rterm); + if !positive { + not(res) + } else { + res + } } /// Creates an operator application. @@ -352,122 +357,119 @@ pub fn dtyp_tst(name: String, term: Term) -> Term { /// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn try_app(op: Op, mut args: Vec) -> Result { - let typ = op.type_check(& mut args) ? ; - Ok( normalize(op, args, typ) ) + let typ = op.type_check(&mut args)?; + Ok(normalize(op, args, typ)) } /// Creates a less than or equal to. #[inline] pub fn le(lhs: Term, rhs: Term) -> Term { - app(Op::Le, vec![lhs, rhs]) + app(Op::Le, vec![lhs, rhs]) } /// Creates a less than. #[inline] pub fn lt(lhs: Term, rhs: Term) -> Term { - app(Op::Lt, vec![lhs, rhs]) + app(Op::Lt, vec![lhs, rhs]) } /// Creates a greater than. #[inline] pub fn gt(lhs: Term, rhs: Term) -> Term { - app(Op::Gt, vec![lhs, rhs]) + app(Op::Gt, vec![lhs, rhs]) } /// Creates a greater than or equal to. #[inline] pub fn ge(lhs: Term, rhs: Term) -> Term { - app(Op::Ge, vec![lhs, rhs]) + app(Op::Ge, vec![lhs, rhs]) } /// Creates an equality. #[inline] pub fn eq(lhs: Term, rhs: Term) -> Term { - app(Op::Eql, vec![lhs, rhs]) + app(Op::Eql, vec![lhs, rhs]) } /// Creates a distinct application. #[inline] pub fn distinct(terms: Vec) -> Term { - app(Op::Distinct, terms) + app(Op::Distinct, terms) } /// Creates a sum. #[inline] pub fn add(kids: Vec) -> Term { - app(Op::Add, kids) + app(Op::Add, kids) } /// Creates a sum, binary version. #[inline] pub fn add2(kid_1: Term, kid_2: Term) -> Term { - app(Op::Add, vec![kid_1, kid_2]) + app(Op::Add, vec![kid_1, kid_2]) } /// Creates a subtraction. #[inline] pub fn sub(kids: Vec) -> Term { - app(Op::Sub, kids) + app(Op::Sub, kids) } /// Creates a subtraction, binary version. #[inline] pub fn sub2(kid_1: Term, kid_2: Term) -> Term { - app(Op::Sub, vec![kid_1, kid_2]) + app(Op::Sub, vec![kid_1, kid_2]) } /// Creates a unary minus. #[inline] pub fn u_minus(kid: Term) -> Term { - app(Op::Sub, vec![kid]) + app(Op::Sub, vec![kid]) } /// Creates a multiplication. #[inline] pub fn mul(kids: Vec) -> Term { - app(Op::Mul, kids) + app(Op::Mul, kids) } /// Creates a multiplication by a constant. #[inline] pub fn cmul(cst: V, term: Term) -> Term -where V: Into { - app( - Op::CMul, vec![ - cst.into().to_term().expect( - "illegal constant passed to CMul constructor" - ), - term - ] - ) +where + V: Into, +{ + app( + Op::CMul, + vec![ + cst.into() + .to_term() + .expect("illegal constant passed to CMul constructor"), + term, + ], + ) } /// Creates an integer division. #[inline] pub fn idiv(kids: Vec) -> Term { - app(Op::IDiv, kids) + app(Op::IDiv, kids) } /// Creates a division. #[inline] pub fn div(kids: Vec) -> Term { - app(Op::Div, kids) + app(Op::Div, kids) } /// Creates a modulo application. #[inline] pub fn modulo(a: Term, b: Term) -> Term { - app(Op::Mod, vec![a, b]) + app(Op::Mod, vec![a, b]) } /// Creates a conversion from `Int` to `Real`. #[inline] pub fn to_real(int: Term) -> Term { - app(Op::ToReal, vec![int]) + app(Op::ToReal, vec![int]) } /// Creates a conversion from `Real` to `Int`. #[inline] pub fn to_int(real: Term) -> Term { - app(Op::ToInt, vec![real]) + app(Op::ToInt, vec![real]) } - - - - - - /// Simplifies operator applications. /// /// This function is currently not strongly-normalizing. @@ -479,7 +481,7 @@ pub fn to_int(real: Term) -> Term { /// /// let tru = term::tru() ; /// let fls = term::fls() ; -/// +/// /// let var_1 = term::bool_var(7) ; /// let n_var_1 = term::not( var_1.clone() ) ; /// let var_2 = term::bool_var(2) ; @@ -569,138 +571,125 @@ pub fn to_int(real: Term) -> Term { /// assert_eq!( fls, term::gt(int_1.clone(), int_2.clone()) ) ; /// assert_eq!( tru, term::lt(int_1.clone(), int_2.clone()) ) ; /// ``` -fn normalize( - op: Op, args: Vec, typ: Typ -) -> Term { - - // Contains stack frames composed of - // - // - the operator `op` to apply, - // - a vector of operators to apply to some arguments before applying `op` - // - the arguments to apply `op`, basically storing the results of the - // applications from the second element - // - // It is important that the second, `to_do`, element of the frames is in - // **reverse order**. This is because its elements will be `pop`ped and - // `push`ed on the third element. - let mut stack = vec![ (typ, op, vec![], args) ] ; - - 'go_down: while let Some((typ, op, mut to_do, mut args)) = stack.pop() { - - 'do_stuff: loop { - - match to_do.pop() { - Some( NormRes::Term(term) ) => { - args.push(term) ; - continue 'do_stuff - }, - - Some( NormRes::App(nu_typ, nu_op, mut nu_to_do) ) => { - stack.push( (typ, op, to_do, args) ) ; - let nu_args = Vec::with_capacity( nu_to_do.len() ) ; - nu_to_do.reverse() ; - stack.push( (nu_typ, nu_op, nu_to_do, nu_args) ) ; - continue 'go_down - }, - - None => match normalize_app(op, args, typ) { - // Going down... - NormRes::App(typ, op, mut to_do) => { - let args = Vec::with_capacity( to_do.len() ) ; - to_do.reverse() ; - stack.push( (typ, op, to_do, args) ) ; - continue 'go_down - }, - // Going up... - NormRes::Term(term) => if let Some( - & mut ( _, _, _, ref mut args ) - ) = stack.last_mut() { - args.push( term ) ; - continue 'go_down - } else { - return term - }, - }, - } - +fn normalize(op: Op, args: Vec, typ: Typ) -> Term { + // Contains stack frames composed of + // + // - the operator `op` to apply, + // - a vector of operators to apply to some arguments before applying `op` + // - the arguments to apply `op`, basically storing the results of the + // applications from the second element + // + // It is important that the second, `to_do`, element of the frames is in + // **reverse order**. This is because its elements will be `pop`ped and + // `push`ed on the third element. + let mut stack = vec![(typ, op, vec![], args)]; + + 'go_down: while let Some((typ, op, mut to_do, mut args)) = stack.pop() { + 'do_stuff: loop { + match to_do.pop() { + Some(NormRes::Term(term)) => { + args.push(term); + continue 'do_stuff; + } + + Some(NormRes::App(nu_typ, nu_op, mut nu_to_do)) => { + stack.push((typ, op, to_do, args)); + let nu_args = Vec::with_capacity(nu_to_do.len()); + nu_to_do.reverse(); + stack.push((nu_typ, nu_op, nu_to_do, nu_args)); + continue 'go_down; + } + + None => match normalize_app(op, args, typ) { + // Going down... + NormRes::App(typ, op, mut to_do) => { + let args = Vec::with_capacity(to_do.len()); + to_do.reverse(); + stack.push((typ, op, to_do, args)); + continue 'go_down; + } + // Going up... + NormRes::Term(term) => { + if let Some(&mut (_, _, _, ref mut args)) = stack.last_mut() { + args.push(term); + continue 'go_down; + } else { + return term; + } + } + }, + } + } } - } - - unreachable!() + unreachable!() } - - /// Normalization result. pub enum NormRes { - /// Just a term. - Term(Term), - /// More stuff to do. - App(Typ, Op, Vec), + /// Just a term. + Term(Term), + /// More stuff to do. + App(Typ, Op, Vec), } - - /// Normalizes an operation application. fn normalize_app(mut op: Op, mut args: Vec, typ: Typ) -> NormRes { - use term::simplify ; - - // println!() ; - // print!("({}", op) ; - // for arg in & args { - // print!(" {}", arg) - // } - // println!(")") ; - - let maybe_res = match op { - - // Polymorphic operators. - Op::Eql => simplify::eql(& mut args), - Op::Ite => simplify::ite(& mut args), - Op::Distinct => simplify::distinct(& mut args), - - // Boolean operators. - Op::And => simplify::and(& mut args), - Op::Or => simplify::or(& mut args), - Op::Not => simplify::not(& mut args), - Op::Impl => simplify::implies(& mut args), - - // Relations over arithmetics. - Op::Ge | Op::Gt => simplify::gt_ge(& mut op, & mut args), - Op::Le | Op::Lt => simplify::lt_le(& mut op, & mut args), - - // Operations over arithmetics. - Op::Div => simplify::div(& mut args), - Op::Mod => simplify::modulo(& mut args), - Op::Rem => simplify::rem(& mut args), - Op::Sub => simplify::sub(& mut args), - Op::Add => simplify::add(& mut args), - Op::IDiv => simplify::idiv(& mut args), - Op::CMul => simplify::cmul(& mut args), - Op::Mul => simplify::mul(& mut args), - - // Casting operations. - Op::ToInt => simplify::to_int(& mut args), - Op::ToReal => simplify::to_real(& mut args), - - // Array operations. - Op::Store => simplify::store(& mut args), - Op::Select => simplify::select(& mut args), - - } ; - - // print!("... ") ; - - if let Some(result) = maybe_res { - // println!("simplified") ; - result - } else { + use term::simplify; + + // println!() ; // print!("({}", op) ; // for arg in & args { // print!(" {}", arg) // } // println!(")") ; - NormRes::Term( factory.mk( RTerm::App { typ, op, args } ) ) - } + + let maybe_res = match op { + // Polymorphic operators. + Op::Eql => simplify::eql(&mut args), + Op::Ite => simplify::ite(&mut args), + Op::Distinct => simplify::distinct(&mut args), + + // Boolean operators. + Op::And => simplify::and(&mut args), + Op::Or => simplify::or(&mut args), + Op::Not => simplify::not(&mut args), + Op::Impl => simplify::implies(&mut args), + + // Relations over arithmetics. + Op::Ge | Op::Gt => simplify::gt_ge(&mut op, &mut args), + Op::Le | Op::Lt => simplify::lt_le(&mut op, &mut args), + + // Operations over arithmetics. + Op::Div => simplify::div(&mut args), + Op::Mod => simplify::modulo(&mut args), + Op::Rem => simplify::rem(&mut args), + Op::Sub => simplify::sub(&mut args), + Op::Add => simplify::add(&mut args), + Op::IDiv => simplify::idiv(&mut args), + Op::CMul => simplify::cmul(&mut args), + Op::Mul => simplify::mul(&mut args), + + // Casting operations. + Op::ToInt => simplify::to_int(&mut args), + Op::ToReal => simplify::to_real(&mut args), + + // Array operations. + Op::Store => simplify::store(&mut args), + Op::Select => simplify::select(&mut args), + }; + + // print!("... ") ; + + if let Some(result) = maybe_res { + // println!("simplified") ; + result + } else { + // print!("({}", op) ; + // for arg in & args { + // print!(" {}", arg) + // } + // println!(")") ; + NormRes::Term(factory.mk(RTerm::App { typ, op, args })) + } } diff --git a/src/term/leaf_iter.rs b/src/term/leaf_iter.rs index 38686db4..59435194 100644 --- a/src/term/leaf_iter.rs +++ b/src/term/leaf_iter.rs @@ -1,93 +1,86 @@ //! Iterator over all the leaves in a term. -use std::slice::Iter ; +use std::slice::Iter; -use common::* ; +use common::*; /// Iterator over all the leaves in a term. pub struct LeafIter<'a> { - /// Stack of subterms to look at. - stack: Vec< Iter<'a, Term> >, - /// Original starting term if this is the first iteration. None otherwise. - term: Option<& 'a RTerm>, + /// Stack of subterms to look at. + stack: Vec>, + /// Original starting term if this is the first iteration. None otherwise. + term: Option<&'a RTerm>, } impl<'a> LeafIter<'a> { - /// Constructor. - pub fn of_rterm(term: & 'a RTerm) -> Self { - LeafIter { stack: Vec::with_capacity(11), term: Some(term) } - } + /// Constructor. + pub fn of_rterm(term: &'a RTerm) -> Self { + LeafIter { + stack: Vec::with_capacity(11), + term: Some(term), + } + } } impl<'a> Iterator for LeafIter<'a> { - type Item = Either< (& 'a Typ, VarIdx), & 'a Val > ; - - fn next(& mut self) -> Option { - - 'find_next: loop { + type Item = Either<(&'a Typ, VarIdx), &'a Val>; - let mut current = if let Some(term) = ::std::mem::replace( - & mut self.term, None - ) { - // First iteration. - term - } else { - loop { - // Something in the stack? - if let Some(mut iter) = self.stack.last_mut() { - // Is there something in `iter`? - if let Some(term) = iter.next() { - // Use that - break term.get() + fn next(&mut self) -> Option { + 'find_next: loop { + let mut current = if let Some(term) = ::std::mem::replace(&mut self.term, None) { + // First iteration. + term } else { - // Keep going, stack will be popped below. + loop { + // Something in the stack? + if let Some(mut iter) = self.stack.last_mut() { + // Is there something in `iter`? + if let Some(term) = iter.next() { + // Use that + break term.get(); + } else { + // Keep going, stack will be popped below. + } + } else { + return None; + } + + // Only reachable if + // - stack is not empty, and + // there was nothing in the last element of the stack. + let iter = self.stack.pop(); + debug_assert_eq! { + iter.map( + |mut iter| iter.next().map( + |_| "iter non-empty" + ).unwrap_or("ok") + ).unwrap_or("stack was empty") , "ok" + } + } + }; + + use term::RTerm::*; + + 'go_down: loop { + let next = match *current { + Var(ref typ, var) => Either::Left((typ, var)), + Cst(ref val) => Either::Right(val), + + DTypSlc { ref term, .. } + | DTypTst { ref term, .. } + | CArray { ref term, .. } => { + current = term.get(); + continue 'go_down; + } + + App { ref args, .. } | Fun { ref args, .. } | DTypNew { ref args, .. } => { + self.stack.push(args.iter()); + continue 'find_next; + } + }; + + return Some(next); } - } else { - return None - } - - // Only reachable if - // - stack is not empty, and - // there was nothing in the last element of the stack. - let iter = self.stack.pop() ; - debug_assert_eq! { - iter.map( - |mut iter| iter.next().map( - |_| "iter non-empty" - ).unwrap_or("ok") - ).unwrap_or("stack was empty") , "ok" - } } - } ; - - use term::RTerm::* ; - - 'go_down: loop { - - let next = match * current { - Var(ref typ, var) => Either::Left( (typ, var) ), - Cst(ref val) => Either::Right(val), - - DTypSlc { ref term, .. } | - DTypTst { ref term, .. } | - CArray { ref term, .. } => { - current = term.get() ; - continue 'go_down - }, - - App { ref args, .. } | - Fun { ref args, .. } | - DTypNew { ref args, .. } => { - self.stack.push( args.iter() ) ; - continue 'find_next - }, - } ; - - return Some(next) - - } - } - - } -} \ No newline at end of file +} diff --git a/src/term/mod.rs b/src/term/mod.rs index cb723fb0..0fdbddc0 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -44,7 +44,7 @@ //! ) //! ) ; //! # println!("{}", some_term) ; -//! +//! //! // A `Term` dereferences to an `RTerm`: //! match * some_term { //! RTerm::App { ref typ, op: Op::Eql, ref args } => { @@ -56,1248 +56,1286 @@ //! } //! ``` -use hashconsing::* ; +use hashconsing::*; -use common::* ; +use common::*; #[macro_use] -mod op ; -mod factory ; -mod tterms ; -pub mod simplify ; -pub mod typ ; -mod zip ; -mod eval ; -mod leaf_iter ; - -pub use self::op::* ; -pub use self::factory::* ; -pub use self::tterms::* ; -pub use self::typ::Typ ; -pub use self::leaf_iter::LeafIter ; +mod op; +mod eval; +mod factory; +mod leaf_iter; +pub mod simplify; +mod tterms; +pub mod typ; +mod zip; + +pub use self::factory::*; +pub use self::leaf_iter::LeafIter; +pub use self::op::*; +pub use self::tterms::*; +pub use self::typ::Typ; #[cfg(test)] -mod test ; - - +mod test; /// Hash consed term. -pub type Term = HConsed ; - - +pub type Term = HConsed; /// A real term. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RTerm { - /// A clause variable. - Var(Typ, VarIdx), - /// A constant. - Cst(Val), - - /// A **constant** array. - /// - /// The type is the type of **the indices** of the arrays. - CArray { - /// Type of **the indices** (not the array). - typ: Typ, - /// Default term of the array. - term: Term - }, - - /// An operator application. - App { - /// Type of the application. - typ: Typ, - /// The operator. - op: Op, - /// The arguments. - args: Vec, - }, - - /// A datatype constructor application. - DTypNew { - /// Type of the application. - typ: Typ, - /// Name of the constructor. - name: String, - /// Arguments of the constructor. - args: Vec, - }, - - /// A datatype selector application. - DTypSlc { - /// Type of the application. - typ: Typ, - /// Name of the selector. - name: String, - /// Argument of the selector. - term: Term, - }, - - /// A datatype tester application. - DTypTst { - /// Type of the term (always bool). - typ: Typ, - /// Name of the tester. - name: String, - /// Argument of the selector. - term: Term, - }, - - /// A function application. - Fun { - /// Type of this term. - typ: Typ, - /// Function being applied. - name: String, - /// Arguments of the function application. - args: Vec, - } -} + /// A clause variable. + Var(Typ, VarIdx), + /// A constant. + Cst(Val), + + /// A **constant** array. + /// + /// The type is the type of **the indices** of the arrays. + CArray { + /// Type of **the indices** (not the array). + typ: Typ, + /// Default term of the array. + term: Term, + }, + /// An operator application. + App { + /// Type of the application. + typ: Typ, + /// The operator. + op: Op, + /// The arguments. + args: Vec, + }, + + /// A datatype constructor application. + DTypNew { + /// Type of the application. + typ: Typ, + /// Name of the constructor. + name: String, + /// Arguments of the constructor. + args: Vec, + }, + /// A datatype selector application. + DTypSlc { + /// Type of the application. + typ: Typ, + /// Name of the selector. + name: String, + /// Argument of the selector. + term: Term, + }, -impl RTerm { - /// The operator and the kids of a term. - pub fn app_inspect(& self) -> Option< (Op, & Vec) > { - if let RTerm::App { op, ref args, .. } = * self { - Some((op, args)) - } else { None } - } + /// A datatype tester application. + DTypTst { + /// Type of the term (always bool). + typ: Typ, + /// Name of the tester. + name: String, + /// Argument of the selector. + term: Term, + }, - /// Returns the kids of an ite. - pub fn ite_inspect(& self) -> Option<(& Term, & Term, & Term)> { - if let RTerm::App { op: Op::Ite, ref args, .. } = * self { - debug_assert_eq! { args.len(), 3 } - Some( (& args[0], & args[1], & args[2]) ) - } else { None } - } + /// A function application. + Fun { + /// Type of this term. + typ: Typ, + /// Function being applied. + name: String, + /// Arguments of the function application. + args: Vec, + }, +} - /// Inspects a function application. - pub fn fun_inspect(& self) -> Option<(& String, & Vec)> { - if let RTerm::Fun { ref name, ref args, .. } = * self { - Some((name, args)) - } else { None } - } +impl RTerm { + /// The operator and the kids of a term. + pub fn app_inspect(&self) -> Option<(Op, &Vec)> { + if let RTerm::App { op, ref args, .. } = *self { + Some((op, args)) + } else { + None + } + } - /// Returns the kid of a negation. - pub fn neg_inspect(& self) -> Option<& Term> { - if let RTerm::App { op: Op::Not, ref args, .. } = * self { - debug_assert_eq! { args.len(), 1 } - Some(& args[0]) - } else { None } - } + /// Returns the kids of an ite. + pub fn ite_inspect(&self) -> Option<(&Term, &Term, &Term)> { + if let RTerm::App { + op: Op::Ite, + ref args, + .. + } = *self + { + debug_assert_eq! { args.len(), 3 } + Some((&args[0], &args[1], &args[2])) + } else { + None + } + } - /// Returns the kids of conjunctions. - pub fn conj_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::And, ref args, .. } = * self { - Some(args) - } else { None } - } - /// Returns the kids of disjunctions. - pub fn disj_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::Or, ref args, .. } = * self { - Some(args) - } else { None } - } - /// Returns the kids of equalities. - pub fn eq_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::Eql, ref args, .. } = * self { - Some(args) - } else { None } - } + /// Inspects a function application. + pub fn fun_inspect(&self) -> Option<(&String, &Vec)> { + if let RTerm::Fun { + ref name, ref args, .. + } = *self + { + Some((name, args)) + } else { + None + } + } - /// Returns the kids of additions. - pub fn add_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::Add, ref args, .. } = * self { - Some(args) - } else { None } - } - /// Returns the kids of subtractions. - pub fn sub_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::Sub, ref args, .. } = * self { - Some(args) - } else { None } - } - /// Returns the kids of multiplications. - pub fn mul_inspect(& self) -> Option<& Vec> { - if let RTerm::App { op: Op::Mul, ref args, .. } = * self { - Some(args) - } else { None } - } - /// Returns the kids of a constant multiplication. - pub fn cmul_inspect(& self) -> Option<(Val, & Term)> { - if let RTerm::App { op: Op::CMul, ref args, .. } = * self { - if args.len() == 2 { - if let Some(val) = args[0].val() { - return Some((val, & args[1])) + /// Returns the kid of a negation. + pub fn neg_inspect(&self) -> Option<&Term> { + if let RTerm::App { + op: Op::Not, + ref args, + .. + } = *self + { + debug_assert_eq! { args.len(), 1 } + Some(&args[0]) + } else { + None } - } - panic!("illegal c_mul application: {}", self) - } else { None } - } + } - /// Returns the kids of a datatype tester. - pub fn dtyp_tst_inspect(& self) -> Option<(& str, & Term)> { - if let RTerm::DTypTst { name, term, .. } = self { - Some((name, term)) - } else { - None + /// Returns the kids of conjunctions. + pub fn conj_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::And, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + /// Returns the kids of disjunctions. + pub fn disj_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Or, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + /// Returns the kids of equalities. + pub fn eq_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Eql, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } } - } - /// Returns the kids of a datatype constructor. - pub fn dtyp_new_inspect(& self) -> Option<(& Typ, & str, & [ Term ])> { - if let RTerm::DTypNew { typ, name, args } = self { - Some((typ, name, args)) - } else { - None + /// Returns the kids of additions. + pub fn add_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Add, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + /// Returns the kids of subtractions. + pub fn sub_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Sub, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + /// Returns the kids of multiplications. + pub fn mul_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Mul, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + /// Returns the kids of a constant multiplication. + pub fn cmul_inspect(&self) -> Option<(Val, &Term)> { + if let RTerm::App { + op: Op::CMul, + ref args, + .. + } = *self + { + if args.len() == 2 { + if let Some(val) = args[0].val() { + return Some((val, &args[1])); + } + } + panic!("illegal c_mul application: {}", self) + } else { + None + } } - } - /// Iterator over over all the leafs of a term. - pub fn leaf_iter(& self) -> LeafIter { - LeafIter::of_rterm(self) - } + /// Returns the kids of a datatype tester. + pub fn dtyp_tst_inspect(&self) -> Option<(&str, &Term)> { + if let RTerm::DTypTst { name, term, .. } = self { + Some((name, term)) + } else { + None + } + } - /// Iterates over all the subterms of a term, including itself. - pub fn iter(& self, mut f: F) { - let mut stack = vec![self] ; + /// Returns the kids of a datatype constructor. + pub fn dtyp_new_inspect(&self) -> Option<(&Typ, &str, &[Term])> { + if let RTerm::DTypNew { typ, name, args } = self { + Some((typ, name, args)) + } else { + None + } + } - while let Some(term) = stack.pop() { - f(term) ; - match term { - RTerm::DTypNew { args, .. } | - RTerm::Fun { args, .. } | - RTerm::App { args, .. } => for term in args { - stack.push(term) - }, + /// Iterator over over all the leafs of a term. + pub fn leaf_iter(&self) -> LeafIter { + LeafIter::of_rterm(self) + } - RTerm::CArray { term, .. } | - RTerm::DTypSlc { term, .. } | - RTerm::DTypTst { term, .. } => stack.push( term.get() ), + /// Iterates over all the subterms of a term, including itself. + pub fn iter(&self, mut f: F) + where + F: FnMut(&RTerm), + { + use RTerm::*; + let mut stack = vec![self]; + + while let Some(term) = stack.pop() { + f(term); + match term { + App { args, .. } | DTypNew { args, .. } | Fun { args, .. } => { + stack.extend(args.iter().map(|term| term.get())) + } + CArray { term, .. } | DTypSlc { term, .. } | DTypTst { term, .. } => { + stack.push(term.get()) + } - RTerm::Var(_, _) | - RTerm::Cst(_) => (), - } + Var(_, _) | Cst(_) => (), + } + } } - } - /// Type of the term. - pub fn typ(& self) -> Typ { - match self { + /// Type of the term. + pub fn typ(&self) -> Typ { + match self { + RTerm::CArray { typ, term } => typ::array(typ.clone(), term.typ()), - RTerm::CArray { typ, term } => typ::array( - typ.clone(), term.typ() - ), + RTerm::Cst(val) => val.typ(), - RTerm::Cst(val) => val.typ(), - - RTerm::Var(typ, _) | - RTerm::App { typ, .. } | - RTerm::Fun { typ, .. } | - RTerm::DTypSlc { typ, .. } | - RTerm::DTypTst { typ, .. } | - RTerm::DTypNew { typ, .. } => typ.clone(), + RTerm::Var(typ, _) + | RTerm::App { typ, .. } + | RTerm::Fun { typ, .. } + | RTerm::DTypSlc { typ, .. } + | RTerm::DTypTst { typ, .. } + | RTerm::DTypNew { typ, .. } => typ.clone(), + } } - } - /// True if the term is zero (integer or real). - pub fn is_zero(& self) -> bool { - match ( self.int(), self.real() ) { - (Some(i), _) => i.is_zero(), - (_, Some(r)) => r.is_zero(), - _ => false, + /// True if the term is zero (integer or real). + pub fn is_zero(&self) -> bool { + match (self.int(), self.real()) { + (Some(i), _) => i.is_zero(), + (_, Some(r)) => r.is_zero(), + _ => false, + } } - } - /// True if the term is one (integer or real). - pub fn is_one(& self) -> bool { - use num::One ; - match ( self.int(), self.real() ) { - (Some(i), _) => i == Int::one(), - (_, Some(r)) => r == Rat::one(), - _ => false, + /// True if the term is one (integer or real). + pub fn is_one(&self) -> bool { + use num::One; + match (self.int(), self.real()) { + (Some(i), _) => i == Int::one(), + (_, Some(r)) => r == Rat::one(), + _ => false, + } } - } - - /// Write a real term using a special function to write variables. - pub fn write( - & self, w: & mut W, write_var: WriteVar - ) -> IoRes<()> - where W: Write, WriteVar: Fn(& mut W, VarIdx) -> IoRes<()> { - let mut stack = vec![ - (vec![self], "", "") - // ^^^^^^^^^| ^^| ^^~~~ termination string (written once vector's empty) - // | |~~~~~~ prefix string (written before next element) - // |~~~~~~~~~~~ elements to write - ] ; - - while let Some( (mut to_do, sep, end) ) = stack.pop() { - use self::RTerm::* ; - - if let Some(this_term) = to_do.pop() { - stack.push( (to_do, sep, end) ) ; - write!(w, "{}", sep) ? ; - - match this_term { - - Var(_, v) => write_var(w, * v) ?, - - Cst(val) => write!(w, "{}", val) ?, - - CArray { term, .. } => { - write!( - w, "(({} {} {})", - keywords::op::as_, keywords::op::const_, this_term.typ() - ) ? ; - stack.push( (vec![term], " ", ")") ) - }, - - App { op, args, .. } => { - write!(w, "({}", op) ? ; - stack.push( - (args.iter().rev().map(|t| t.get()).collect(), " ", ")") - ) - }, - - DTypSlc { name, term, .. } => { - write!(w, "({}", name) ? ; - stack.push( (vec![term], " ", ")") ) - }, - - DTypTst { name, term, .. } => { - write!( - w, "({}-{}", keywords::op::is_, name - ) ? ; - stack.push( (vec![term], " ", ")") ) - }, - - DTypNew { name, args, .. } => if args.is_empty() { - write!(w, "{}", name) ? - } else { - write!(w, "({}", name) ? ; - stack.push( - (args.iter().rev().map(|t| t.get()).collect(), " ", ")") - ) - }, - - Fun { name, args, .. } => if args.is_empty() { - write!(w, "{}", name) ? - } else { - write!(w, "({}", name) ? ; - stack.push( - (args.iter().rev().map(|t| t.get()).collect(), " ", ")") - ) - }, + /// Write a real term using a special function to write variables. + pub fn write(&self, w: &mut W, write_var: WriteVar) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + // Stores triplets of + // - the elements to write, + // - the prefix string, written before next element + // - the suffix, written once there's no more elements to write + let mut stack = vec![(vec![self], "", "")]; + + while let Some((mut to_do, sep, end)) = stack.pop() { + use self::RTerm::*; + + if let Some(this_term) = to_do.pop() { + stack.push((to_do, sep, end)); + write!(w, "{}", sep)?; + + match this_term { + Var(_, v) => write_var(w, *v)?, + + Cst(val) => write!(w, "{}", val)?, + + CArray { term, .. } => { + write!( + w, + "(({} {} {})", + keywords::op::as_, + keywords::op::const_, + this_term.typ() + )?; + stack.push((vec![term], " ", ")")) + } + + App { op, args, .. } => { + write!(w, "({}", op)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + } + + DTypSlc { name, term, .. } => { + write!(w, "({}", name)?; + stack.push((vec![term], " ", ")")) + } + + DTypTst { name, term, .. } => { + write!(w, "({}-{}", keywords::op::is_, name)?; + stack.push((vec![term], " ", ")")) + } + + DTypNew { name, args, .. } => if args.is_empty() { + write!(w, "{}", name)? + } else { + write!(w, "({}", name)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + }, + + Fun { name, args, .. } => if args.is_empty() { + write!(w, "{}", name)? + } else { + write!(w, "({}", name)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + }, + } + } else { + w.write_all(end.as_bytes())? + } } - } else { - w.write_all( end.as_bytes() ) ? - } + Ok(()) } - Ok(()) - } - - /// True if atom `self` implies atom `other` syntactically. - /// - /// Returns - /// - /// - `None` if no conclusion was reached, - /// - `Some(Greater)` if `lhs => rhs`, - /// - `Some(Less)` if `lhs <= rhs`, - /// - `Some(Equal)` if `lhs` and `rhs` are equivalent. - /// - /// So *greater* really means *more generic*. - /// - /// See [the module's function][atom implies] for more details and examples. - /// - /// [atom implies]: fn.atom_implies.html (atom_implies module-level function) - pub fn conj_simpl(& self, other: & Self) -> simplify::SimplRes { - simplify::conj_simpl(& self, & other) - } + /// True if atom `self` implies atom `other` syntactically. + /// + /// Returns + /// + /// - `None` if no conclusion was reached, + /// - `Some(Greater)` if `lhs => rhs`, + /// - `Some(Less)` if `lhs <= rhs`, + /// - `Some(Equal)` if `lhs` and `rhs` are equivalent. + /// + /// So *greater* really means *more generic*. + /// + /// See [the module's function][atom implies] for more details and examples. + /// + /// [atom implies]: fn.atom_implies.html (atom_implies module-level function) + pub fn conj_simpl(&self, other: &Self) -> simplify::SimplRes { + simplify::conj_simpl(&self, &other) + } - /// Term evaluation (int). - pub fn int_eval( - & self, model: & E - ) -> Res< Option > { - self.eval(model)?.to_int() - } + /// Term evaluation (int). + pub fn int_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_int() + } - /// Term evaluation (real). - pub fn real_eval( - & self, model: & E - ) -> Res< Option > { - self.eval(model)?.to_real() - } + /// Term evaluation (real). + pub fn real_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_real() + } - /// Term evaluation (bool). - pub fn bool_eval( - & self, model: & E - ) -> Res< Option > { - self.eval(model)?.to_bool() - } + /// Term evaluation (bool). + pub fn bool_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_bool() + } - /// True if the term has no variables and evaluates to true. - /// - /// # Examples - /// - /// ```rust - /// use hoice::term ; - /// use hoice::term::Op ; - /// - /// let term = term::tru() ; - /// println!("true") ; - /// assert!( term.is_true() ) ; - /// let term = term::fls() ; - /// println!("false") ; - /// assert!( ! term.is_true() ) ; - /// let term = term::eq( - /// term::int(7), term::int_var(1) - /// ) ; - /// println!("7 = v_1") ; - /// assert!( ! term.is_true() ) ; - /// let term = term::eq( - /// term::int(9), term::int(9) - /// ) ; - /// println!("9 = 9") ; - /// assert!( term.is_true() ) ; - /// let term = term::eq( - /// term::int(1), term::int(9) - /// ) ; - /// println!("1 = 9") ; - /// assert!( ! term.is_true() ) ; - /// let term = term::le( - /// term::app( - /// Op::Add, vec![ term::int(3), term::int(4) ] - /// ), term::int(9) - /// ) ; - /// println!("3 + 4 = 9") ; - /// assert!( term.is_true() ) ; - /// ``` - pub fn is_true(& self) -> bool { - match self.bool_eval( & () ) { - Ok(Some(b)) => b, - _ => false, + /// True if the term has no variables and evaluates to true. + /// + /// # Examples + /// + /// ```rust + /// use hoice::term ; + /// use hoice::term::Op ; + /// + /// let term = term::tru() ; + /// println!("true") ; + /// assert!( term.is_true() ) ; + /// let term = term::fls() ; + /// println!("false") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::eq( + /// term::int(7), term::int_var(1) + /// ) ; + /// println!("7 = v_1") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::eq( + /// term::int(9), term::int(9) + /// ) ; + /// println!("9 = 9") ; + /// assert!( term.is_true() ) ; + /// let term = term::eq( + /// term::int(1), term::int(9) + /// ) ; + /// println!("1 = 9") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::le( + /// term::app( + /// Op::Add, vec![ term::int(3), term::int(4) ] + /// ), term::int(9) + /// ) ; + /// println!("3 + 4 = 9") ; + /// assert!( term.is_true() ) ; + /// ``` + pub fn is_true(&self) -> bool { + match self.bool_eval(&()) { + Ok(Some(b)) => b, + _ => false, + } } - } - - /// True if the term has no variables and evaluates to true. - /// - /// # Examples - /// - /// ```rust - /// use hoice::term ; - /// use hoice::term::Op ; - /// - /// let term = term::tru() ; - /// println!("true") ; - /// assert!( ! term.is_false() ) ; - /// let term = term::fls() ; - /// println!("false") ; - /// assert!( term.is_false() ) ; - /// let term = term::eq( - /// term::int(7), term::int_var(1) - /// ) ; - /// println!("7 = v_1") ; - /// assert!( ! term.is_false() ) ; - /// let term = term::eq( - /// term::int(9), term::int(9) - /// ) ; - /// println!("9 = 9") ; - /// assert!( ! term.is_false() ) ; - /// let term = term::eq( - /// term::int(1), term::int(9) - /// ) ; - /// println!("1 = 9") ; - /// assert!( term.is_false() ) ; - /// let term = term::le( - /// term::int(9), term::app( - /// Op::Add, vec![ term::int(3), term::int(4) ] - /// ) - /// ) ; - /// println!("9 <= 3 + 4") ; - /// assert!( term.is_false() ) ; - /// ``` - pub fn is_false(& self) -> bool { - match self.bool_eval( & () ) { - Ok(Some(b)) => ! b, - _ => false, + + /// True if the term has no variables and evaluates to true. + /// + /// # Examples + /// + /// ```rust + /// use hoice::term ; + /// use hoice::term::Op ; + /// + /// let term = term::tru() ; + /// println!("true") ; + /// assert!( ! term.is_false() ) ; + /// let term = term::fls() ; + /// println!("false") ; + /// assert!( term.is_false() ) ; + /// let term = term::eq( + /// term::int(7), term::int_var(1) + /// ) ; + /// println!("7 = v_1") ; + /// assert!( ! term.is_false() ) ; + /// let term = term::eq( + /// term::int(9), term::int(9) + /// ) ; + /// println!("9 = 9") ; + /// assert!( ! term.is_false() ) ; + /// let term = term::eq( + /// term::int(1), term::int(9) + /// ) ; + /// println!("1 = 9") ; + /// assert!( term.is_false() ) ; + /// let term = term::le( + /// term::int(9), term::app( + /// Op::Add, vec![ term::int(3), term::int(4) ] + /// ) + /// ) ; + /// println!("9 <= 3 + 4") ; + /// assert!( term.is_false() ) ; + /// ``` + pub fn is_false(&self) -> bool { + match self.bool_eval(&()) { + Ok(Some(b)) => !b, + _ => false, + } } - } - /// Boolean a constant boolean term evaluates to. - pub fn bool(& self) -> Option { - match self.bool_eval( & () ) { - Ok(Some(b)) => Some(b), - _ => None + /// Boolean a constant boolean term evaluates to. + pub fn bool(&self) -> Option { + match self.bool_eval(&()) { + Ok(Some(b)) => Some(b), + _ => None, + } } - } - /// Evaluates a term with an empty model. - pub fn as_val(& self) -> Val { - if let Ok(res) = self.eval(& ()) { res } else { - val::none(self.typ().clone()) + /// Evaluates a term with an empty model. + pub fn as_val(&self) -> Val { + if let Ok(res) = self.eval(&()) { + res + } else { + val::none(self.typ().clone()) + } } - } - /// Integer a constant integer term evaluates to. - pub fn int(& self) -> Option { - if self.typ() != typ::int() { return None } - match self.int_eval( & () ) { - Ok(Some(i)) => Some(i), - _ => None + /// Integer a constant integer term evaluates to. + pub fn int(&self) -> Option { + if self.typ() != typ::int() { + return None; + } + match self.int_eval(&()) { + Ok(Some(i)) => Some(i), + _ => None, + } } - } - /// Integer a constant integer term evaluates to. - pub fn real(& self) -> Option { - match self.real_eval( & () ) { - Ok(Some(r)) => Some(r), - _ => None + /// Integer a constant integer term evaluates to. + pub fn real(&self) -> Option { + match self.real_eval(&()) { + Ok(Some(r)) => Some(r), + _ => None, + } } - } - /// Turns a constant term in a `Val`. - pub fn val(& self) -> Option { - match * self { - RTerm::Cst(ref val) => Some( val.clone() ), - _ => None, + /// Turns a constant term in a `Val`. + pub fn val(&self) -> Option { + match *self { + RTerm::Cst(ref val) => Some(val.clone()), + _ => None, + } } - } - /// Returns a constant arithmetic version of the term if any. - pub fn arith(& self) -> Option { - if let Some(i) = self.int() { - Some( term::int(i) ) - } else if let Some(r) = self.real() { - Some( term::real(r) ) - } else { - None + /// Returns a constant arithmetic version of the term if any. + pub fn arith(&self) -> Option { + if let Some(i) = self.int() { + Some(term::int(i)) + } else if let Some(r) = self.real() { + Some(term::real(r)) + } else { + None + } } - } - /// The kids of this term, if any. - pub fn kids(& self) -> Option<& [Term]> { - if let RTerm::App{ ref args, .. } = * self { - Some(args) - } else { - None + /// The kids of this term, if any. + pub fn kids(&self) -> Option<&[Term]> { + if let RTerm::App { ref args, .. } = *self { + Some(args) + } else { + None + } } - } - /// Forces the type of a datatype constructor. - pub fn force_dtyp(& self, nu_typ: Typ) -> Option { - match self { - RTerm::DTypNew { typ, name, args } => { - debug_assert! { nu_typ.is_compatible(typ) } - Some( - dtyp_new( nu_typ, name.clone(), args.clone() ) - ) - }, - RTerm::Cst(val) => val.force_dtyp(nu_typ).map(cst), - _ => None, + /// Forces the type of a datatype constructor. + pub fn force_dtyp(&self, nu_typ: Typ) -> Option { + match self { + RTerm::DTypNew { typ, name, args } => { + debug_assert! { nu_typ.is_compatible(typ) } + Some(dtyp_new(nu_typ, name.clone(), args.clone())) + } + RTerm::Cst(val) => val.force_dtyp(nu_typ).map(cst), + _ => None, + } } - } - /// Casts a term. - /// - /// Only legal if the term's type and the one provided are compatible. - /// - /// Returns - /// - /// - an error if the types are not compatible - /// - `None` if the cast didn't do anything - /// - the new term otherwise - pub fn cast(& self, to_typ: & Typ) -> Res< Option > { - let nu_typ = if let Some(typ) = self.typ().merge(to_typ) { - if to_typ == & typ { return Ok(None) } - typ - } else { - bail!( - "types {} and {} are incompatible", self.typ(), to_typ - ) - } ; - - enum Frame<'a> { - // Array frame. - Arr(Typ), - // Datatype constructor. - New { - typ: Typ, - name: String, - lft: Vec, - rgt: ::std::vec::IntoIter<(Typ, & 'a RTerm)>, - }, - } + /// Casts a term. + /// + /// Only legal if the term's type and the one provided are compatible. + /// + /// Returns + /// + /// - an error if the types are not compatible + /// - `None` if the cast didn't do anything + /// - the new term otherwise + pub fn cast(&self, to_typ: &Typ) -> Res> { + let nu_typ = if let Some(typ) = self.typ().merge(to_typ) { + if to_typ == &typ { + return Ok(None); + } + typ + } else { + bail!("types {} and {} are incompatible", self.typ(), to_typ) + }; + + enum Frame<'a> { + // Array frame. + Arr(Typ), + // Datatype constructor. + New { + typ: Typ, + name: String, + lft: Vec, + rgt: ::std::vec::IntoIter<(Typ, &'a RTerm)>, + }, + } - let mut stack = vec![] ; - let (mut nu_typ, mut curr) = (nu_typ, self) ; + let mut stack = vec![]; + let (mut nu_typ, mut curr) = (nu_typ, self); - 'go_down: loop { + 'go_down: loop { + let mut term = match curr { + RTerm::Var(_, idx) => term::var(*idx, nu_typ), - let mut term = match curr { + RTerm::Cst(val) => if let Ok(val) = val.cast(&nu_typ) { + factory::cst(val) + } else { + return Ok(None); + }, - RTerm::Var(_, idx) => term::var(* idx, nu_typ), + RTerm::App { op, args, .. } => term::app(*op, args.clone()), - RTerm::Cst(val) => if let Ok(val) = val.cast(& nu_typ) { - factory::cst(val) - } else { - return Ok(None) - }, - - RTerm::App { op, args, .. } => term::app( * op, args.clone() ), - - RTerm::CArray { typ, term } => { - let (src, tgt) = typ.array_inspect().unwrap() ; - stack.push( - Frame::Arr( src.clone() ) - ) ; - nu_typ = tgt.clone() ; - curr = term.get() ; - continue 'go_down - }, - - RTerm::DTypNew { typ, name, args } => { - let mut lft = vec![] ; - let mut next = None ; - let mut rgt = vec![] ; - - scoped! { - - let (_, nu_prms) = nu_typ.dtyp_inspect().unwrap() ; - let (_, prms) = typ.dtyp_inspect().unwrap() ; - debug_assert_eq! { args.len(), nu_prms.len() } - debug_assert_eq! { args.len(), prms.len() } - let mut all = nu_prms.iter().zip( - prms.iter() - ).zip( args.iter() ) ; - - while let Some(((nu, typ), arg)) = all.next() { - if nu == typ { - lft.push( arg.clone() ) - } else { - next = Some( - ( arg.get(), nu.clone() ) - ) - } - } + RTerm::CArray { typ, term } => { + let (src, tgt) = typ.array_inspect().unwrap(); + stack.push(Frame::Arr(src.clone())); + nu_typ = tgt.clone(); + curr = term.get(); + continue 'go_down; + } - for ((nu_typ, _), arg) in all { - rgt.push( (nu_typ.clone(), arg.get()) ) - } + RTerm::DTypNew { typ, name, args } => { + let mut lft = vec![]; + let mut next = None; + let mut rgt = vec![]; + + scoped! { + + let (_, nu_prms) = nu_typ.dtyp_inspect().unwrap() ; + let (_, prms) = typ.dtyp_inspect().unwrap() ; + debug_assert_eq! { args.len(), nu_prms.len() } + debug_assert_eq! { args.len(), prms.len() } + let mut all = nu_prms.iter().zip( + prms.iter() + ).zip( args.iter() ) ; + + while let Some(((nu, typ), arg)) = all.next() { + if nu == typ { + lft.push( arg.clone() ) + } else { + next = Some( + ( arg.get(), nu.clone() ) + ) + } + } + + for ((nu_typ, _), arg) in all { + rgt.push( (nu_typ.clone(), arg.get()) ) + } + + } + + if let Some((term, nu)) = next { + let frame = Frame::New { + typ: nu_typ, + name: name.clone(), + lft, + rgt: rgt.into_iter(), + }; + stack.push(frame); + nu_typ = nu; + curr = term; + + continue 'go_down; + } else { + term::dtyp_new(nu_typ, name.clone(), lft) + } + } - } - - if let Some((term, nu)) = next { - let frame = Frame::New { - typ: nu_typ, name: name.clone(), lft, rgt: rgt.into_iter() - } ; - stack.push(frame) ; - nu_typ = nu ; - curr = term ; - - continue 'go_down - } else { - term::dtyp_new(nu_typ, name.clone(), lft) - } - }, - - RTerm::DTypSlc { typ, name, term } => { - debug_assert_eq! { typ, & nu_typ } - term::dtyp_slc( typ.clone(), name.clone(), term.clone() ) - }, - - RTerm::DTypTst { name, term, typ } => { - debug_assert_eq! { typ, & nu_typ } - term::dtyp_tst( name.clone(), term.clone() ) - }, - - RTerm::Fun { typ, name, args } => { - debug_assert_eq! { typ, & nu_typ } - term::fun( typ.clone(), name.clone(), args.clone() ) - }, - - } ; - - 'go_up: loop { - - match stack.pop() { - None => return Ok( Some(term) ), - - Some( Frame::Arr(typ) ) => { - term = term::cst_array(typ, term) ; - continue 'go_up - }, - - Some( - Frame::New { typ, name, mut lft, mut rgt } - ) => { - lft.push(term) ; - - if let Some((ty, tm)) = rgt.next() { - nu_typ = ty ; - curr = tm ; - stack.push( Frame::New { typ, name, lft, rgt } ) ; - continue 'go_down - } else { - term = term::dtyp_new(typ, name, lft) ; - continue 'go_up - } - }, - } + RTerm::DTypSlc { typ, name, term } => { + debug_assert_eq! { typ, & nu_typ } + term::dtyp_slc(typ.clone(), name.clone(), term.clone()) + } - } + RTerm::DTypTst { name, term, typ } => { + debug_assert_eq! { typ, & nu_typ } + term::dtyp_tst(name.clone(), term.clone()) + } + RTerm::Fun { typ, name, args } => { + debug_assert_eq! { typ, & nu_typ } + term::fun(typ.clone(), name.clone(), args.clone()) + } + }; + + 'go_up: loop { + match stack.pop() { + None => return Ok(Some(term)), + + Some(Frame::Arr(typ)) => { + term = term::cst_array(typ, term); + continue 'go_up; + } + + Some(Frame::New { + typ, + name, + mut lft, + mut rgt, + }) => { + lft.push(term); + + if let Some((ty, tm)) = rgt.next() { + nu_typ = ty; + curr = tm; + stack.push(Frame::New { + typ, + name, + lft, + rgt, + }); + continue 'go_down; + } else { + term = term::dtyp_new(typ, name, lft); + continue 'go_up; + } + } + } + } + } } - } - - /// Checks whether the term is a relation. - pub fn is_relation(& self) -> bool { - match * self { - RTerm::App { op: Op::Eql, .. } | - RTerm::App { op: Op::Gt, .. } | - RTerm::App { op: Op::Ge, .. } | - RTerm::App { op: Op::Lt, .. } | - RTerm::App { op: Op::Le, .. } => true, - RTerm::App { op: Op::Not, ref args, .. } => args[0].is_relation(), - _ => false, + /// Checks whether the term is a relation. + pub fn is_relation(&self) -> bool { + match *self { + RTerm::App { op: Op::Eql, .. } + | RTerm::App { op: Op::Gt, .. } + | RTerm::App { op: Op::Ge, .. } + | RTerm::App { op: Op::Lt, .. } + | RTerm::App { op: Op::Le, .. } => true, + RTerm::App { + op: Op::Not, + ref args, + .. + } => args[0].is_relation(), + _ => false, + } } - } - /// Checks whether a term is an equality. - pub fn is_eq(& self) -> bool { - match * self { - RTerm::App { op: Op::Eql, .. } => true, - _ => false, + /// Checks whether a term is an equality. + pub fn is_eq(&self) -> bool { + match *self { + RTerm::App { op: Op::Eql, .. } => true, + _ => false, + } } - } - - /// Term evaluation. - /// - /// # TODO - /// - /// - remove recursive call for constant arrays - pub fn eval(& self, model: & E) -> Res { - eval::eval( & factory::term( self.clone() ), model ) - } + /// Term evaluation. + /// + /// # TODO + /// + /// - remove recursive call for constant arrays + pub fn eval(&self, model: &E) -> Res { + eval::eval(&factory::term(self.clone()), model) + } - /// If the term's an integer constant, returns the value. - pub fn int_val(& self) -> Option<& Int> { - if let RTerm::Cst(val) = self { - if let val::RVal::I(i) = val.get() { - return Some( i ) - } + /// If the term's an integer constant, returns the value. + pub fn int_val(&self) -> Option<&Int> { + if let RTerm::Cst(val) = self { + if let val::RVal::I(i) = val.get() { + return Some(i); + } + } + None } - None - } - /// If the term's a rational constant, returns the value. - pub fn real_val(& self) -> Option<& Rat> { - if let RTerm::Cst(val) = self { - if let val::RVal::R(r) = val.get() { - return Some( r ) - } + /// If the term's a rational constant, returns the value. + pub fn real_val(&self) -> Option<&Rat> { + if let RTerm::Cst(val) = self { + if let val::RVal::R(r) = val.get() { + return Some(r); + } + } + None } - None - } - /// The highest variable index appearing in the term. - pub fn highest_var(& self) -> Option { - let mut max = None ; - - for var_or_cst in self.leaf_iter() { - if let Either::Left((_, var_idx)) = var_or_cst { - max = Some( - ::std::cmp::max( - var_idx, max.unwrap_or_else(|| 0.into()) - ) - ) - } - } + /// The highest variable index appearing in the term. + pub fn highest_var(&self) -> Option { + let mut max = None; - max - } + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + max = Some(::std::cmp::max(var_idx, max.unwrap_or_else(|| 0.into()))) + } + } - /// Returns the variable index if the term is a variable. - pub fn var_idx(& self) -> Option { - match * self { - RTerm::Var(_, i) => Some(i), - _ => None, + max } - } - /// Return true if the term mentions at least one variable from `vars`. - pub fn mentions_one_of(& self, vars: & VarSet) -> bool { - for var_or_cst in self.leaf_iter() { - if let Either::Left((_, var_idx)) = var_or_cst { - if vars.contains(& var_idx) { - return true + /// Returns the variable index if the term is a variable. + pub fn var_idx(&self) -> Option { + match *self { + RTerm::Var(_, i) => Some(i), + _ => None, } - } } - false - } - /// If the term is a negation, returns what's below the negation. - pub fn rm_neg(& self) -> Option { - match * self { - RTerm::App { op: Op::Not, ref args, .. } => { - debug_assert_eq!( args.len(), 1 ) ; - Some( args[0].clone() ) - }, - _ => None, + /// Return true if the term mentions at least one variable from `vars`. + pub fn mentions_one_of(&self, vars: &VarSet) -> bool { + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + if vars.contains(&var_idx) { + return true; + } + } + } + false } - } - - - /// Turns a real term in a hashconsed one. - #[inline] - pub fn to_hcons(& self) -> Term { - term( self.clone() ) - } - - - /// Returns true if the term mentions a function. - pub fn has_fun_apps(& self) -> bool { - use self::zip::* ; - - // Will be `Ok(())` if there's no function application, and `Err(())` - // otherwise. - let res = zip( - & self.to_hcons(), - - |_| Ok(()), - - |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(_) => Err(()), - _ => Ok( ZipDoTotal::Upp { yielded: () } ), - }, - - |frame| match frame { - ZipFrame { thing: ZipOp::Fun(_), .. } => Err(()), - mut frame => { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: - empty `rgt_args` (has_fun_app_or_adt)" - ) ; - Ok( ZipDo::Trm { nu_term, frame } ) - }, - } - ) ; - res.is_err() - } - - - - - /// Returns true if the term mentions a function or an ADT. - pub fn has_fun_app_or_adt(& self) -> bool { - use self::zip::* ; - - // Will be `Ok(())` if there's no function application, and `Err(())` - // otherwise. - let res = zip( - & self.to_hcons(), + /// If the term is a negation, returns what's below the negation. + pub fn rm_neg(&self) -> Option { + match *self { + RTerm::App { + op: Op::Not, + ref args, + .. + } => { + debug_assert_eq!(args.len(), 1); + Some(args[0].clone()) + } + _ => None, + } + } - |_| Ok(()), + /// Turns a real term in a hashconsed one. + #[inline] + pub fn to_hcons(&self) -> Term { + term(self.clone()) + } - |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(_) | - ZipOp::New(_) | - ZipOp::Slc(_) => Err(()), - _ => Ok( ZipDoTotal::Upp { yielded: () } ), - }, + /// Returns true if the term mentions a function. + pub fn has_fun_apps(&self) -> bool { + use self::zip::*; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) => Err(()), + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(_), + .. + } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); - |frame| match frame { - ZipFrame { thing: ZipOp::Fun(_), .. } | - ZipFrame { thing: ZipOp::New(_), .. } | - ZipFrame { thing: ZipOp::Slc(_), .. } => Err(()), - mut frame => { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: - empty `rgt_args` (has_fun_app_or_adt)" - ) ; - Ok( ZipDo::Trm { nu_term, frame } ) - }, - } - ) ; + res.is_err() + } - res.is_err() - } + /// Returns true if the term mentions a function or an ADT. + pub fn has_fun_app_or_adt(&self) -> bool { + use self::zip::*; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) | ZipOp::New(_) | ZipOp::Slc(_) => Err(()), + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(_), + .. + } + | ZipFrame { + thing: ZipOp::New(_), + .. + } + | ZipFrame { + thing: ZipOp::Slc(_), + .. + } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); + res.is_err() + } + /// Variable substitution. + /// + /// The `total` flag causes substitution to fail if a variable that's not in + /// `map`. + /// + /// The boolean returned is true if at least on substitution occured. + pub fn subst_custom>( + &self, + map: &Map, + total: bool, + ) -> Option<(Term, bool)> { + use self::zip::*; + let mut changed = false; + + let res = zip( + &self.to_hcons(), + |zip_null| match zip_null { + ZipNullary::Cst(val) => Ok(cst(val.clone())), + ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { + debug_assert_eq! { typ, & term.typ() } + changed = true; + Ok(term) + } else if total { + Err(()) + } else { + Ok(term::var(var, typ.clone())) + }, + }, + |zip_op, typ, mut acc| { + let yielded = match zip_op { + ZipOp::Op(op) => term::app(op, acc), + ZipOp::New(name) => term::dtyp_new(typ.clone(), name.clone(), acc), + + ZipOp::Slc(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype selector {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_slc(typ.clone(), name.clone(), kid) + } else { + panic!( + "illegal application of datatype selector {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::Tst(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype tester {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_tst(name.clone(), kid) + } else { + panic!( + "illegal application of datatype tester {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::CArray => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal constant array application to {} arguments", + acc.len() + 1 + ) + } + term::cst_array(typ.clone(), kid) + } else { + panic!("illegal constant array application to 0 arguments") + }, + ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), + }; + + Ok(ZipDoTotal::Upp { yielded }) + }, + |mut frame| { + let nu_term = frame + .rgt_args + .next() + .expect("illegal call to `partial_op`: empty `rgt_args` (subst_custom)"); + Ok(ZipDo::Trm { nu_term, frame }) + }, + ); - /// Variable substitution. - /// - /// The `total` flag causes substitution to fail if a variable that's not in - /// `map`. - /// - /// The boolean returned is true if at least on substitution occured. - pub fn subst_custom>( - & self, map: & Map, total: bool - ) -> Option<(Term, bool)> { - use self::zip::* ; - let mut changed = false ; - - let res = zip( - & self.to_hcons(), - - |zip_null| match zip_null { - ZipNullary::Cst(val) => Ok( - cst( val.clone() ) - ), - ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { - debug_assert_eq! { typ, & term.typ() } - changed = true ; - Ok(term) - } else if total { - Err(()) + if let Ok(term) = res { + Some((term, changed)) } else { - Ok( - term::var( var, typ.clone() ) - ) + None } - }, - - |zip_op, typ, mut acc| { - let yielded = match zip_op { - ZipOp::Op(op) => term::app(op, acc), - ZipOp::New(name) => term::dtyp_new( - typ.clone(), name.clone(), acc - ), - - ZipOp::Slc(name) => if let Some(kid) = acc.pop() { - if ! acc.is_empty() { - panic!( - "illegal application of datatype selector {} to {} arguments", - conf.bad(name), acc.len() + 1 - ) - } - term::dtyp_slc(typ.clone(), name.clone(), kid) - } else { - panic!( - "illegal application of datatype selector {} to 0 arguments", - conf.bad(name) - ) - }, - - ZipOp::Tst(name) => if let Some(kid) = acc.pop() { - if ! acc.is_empty() { - panic!( - "illegal application of datatype tester {} to {} arguments", - conf.bad(name), acc.len() + 1 - ) - } - term::dtyp_tst(name.clone(), kid) - } else { - panic!( - "illegal application of datatype tester {} to 0 arguments", - conf.bad(name) - ) - }, - - ZipOp::CArray => if let Some(kid) = acc.pop() { - if ! acc.is_empty() { - panic!( - "illegal constant array application to {} arguments", - acc.len() + 1 - ) - } - term::cst_array(typ.clone(), kid) - } else { - panic!("illegal constant array application to 0 arguments") - }, - ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), - } ; - - Ok( ZipDoTotal::Upp { yielded } ) - }, - - |mut frame| { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: empty `rgt_args` (subst_custom)" - ) ; - Ok( ZipDo::Trm { nu_term, frame } ) - }, - ) ; - - if let Ok(term) = res { - Some( (term, changed) ) - } else { - None } - } - /// Variable substitution. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occured. - /// - /// Used for substitutions in the same clause / predicate scope. - #[inline] - pub fn subst>( - & self, map: & Map - ) -> (Term, bool) { - self.subst_custom(map, false).expect("total substitution can't fail") - } - - /// Fixed-point (partial) variable substitution. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occured. - pub fn subst_fp>( - & self, map: & Map - ) -> (Term, bool) { - let (mut term, mut changed) = self.subst(map) ; - while changed { - let (new_term, new_changed) = term.subst(map) ; - term = new_term ; - changed = new_changed + /// Variable substitution. + /// + /// Returns the new term and a boolean indicating whether any substitution + /// occured. + /// + /// Used for substitutions in the same clause / predicate scope. + #[inline] + pub fn subst>(&self, map: &Map) -> (Term, bool) { + self.subst_custom(map, false) + .expect("total substitution can't fail") } - (term, changed) - } - /// Total variable substition, returns `None` if there was a variable in the - /// term that was not in the map. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occsured. - /// - /// Used for substitutions between different same clause / predicate scopes. - pub fn subst_total>( - & self, map: & Map - ) -> Option< (Term, bool) > { - self.subst_custom(map, true) - } + /// Fixed-point (partial) variable substitution. + /// + /// Returns the new term and a boolean indicating whether any substitution + /// occured. + pub fn subst_fp>(&self, map: &Map) -> (Term, bool) { + let (mut term, mut changed) = self.subst(map); + while changed { + let (new_term, new_changed) = term.subst(map); + term = new_term; + changed = new_changed + } + (term, changed) + } + /// Total variable substition, returns `None` if there was a variable in the + /// term that was not in the map. + /// + /// Returns the new term and a boolean indicating whether any substitution + /// occsured. + /// + /// Used for substitutions between different same clause / predicate scopes. + pub fn subst_total>(&self, map: &Map) -> Option<(Term, bool)> { + self.subst_custom(map, true) + } - /// Tries to turn a term into a substitution. - /// - /// Works only on equalities. - /// - /// # Examples - /// - /// ```rust - /// use hoice::term ; - /// - /// let bv0 = term::bool_var(0) ; - /// let bv1 = term::bool_var(1) ; - /// let bv2 = term::bool_var(2) ; - /// let rhs = term::or(vec![bv1, bv2]) ; - /// let term = term::eq(bv0, rhs.clone()) ; - /// debug_assert_eq! { term.as_subst(), Some((0.into(), rhs)) } - /// ``` - pub fn as_subst(& self) -> Option<(VarIdx, Term)> { - if let Some(kids) = self.eq_inspect() { - debug_assert_eq! { kids.len(), 2 } - let (lhs, rhs) = (& kids[0], & kids[1]) ; - - if let Some(var_idx) = lhs.var_idx() { - return Some((var_idx, rhs.clone())) - } else if let Some(var_idx) = rhs.var_idx() { - return Some((var_idx, lhs.clone())) - } - - if lhs.typ().is_arith() { - debug_assert! { rhs.is_zero() } - - let lhs = if let Some((_, term)) = lhs.cmul_inspect() { - term - } else { lhs } ; - - let mut add = vec![] ; - let mut var = None ; - let mut negated = false ; - - if let Some(kids) = lhs.add_inspect() { - for kid in kids { - if var.is_some() { - add.push(kid.clone()) ; - continue + /// Tries to turn a term into a substitution. + /// + /// Works only on equalities. + /// + /// # Examples + /// + /// ```rust + /// use hoice::term ; + /// + /// let bv0 = term::bool_var(0) ; + /// let bv1 = term::bool_var(1) ; + /// let bv2 = term::bool_var(2) ; + /// let rhs = term::or(vec![bv1, bv2]) ; + /// let term = term::eq(bv0, rhs.clone()) ; + /// debug_assert_eq! { term.as_subst(), Some((0.into(), rhs)) } + /// ``` + pub fn as_subst(&self) -> Option<(VarIdx, Term)> { + if let Some(kids) = self.eq_inspect() { + debug_assert_eq! { kids.len(), 2 } + let (lhs, rhs) = (&kids[0], &kids[1]); + + if let Some(var_idx) = lhs.var_idx() { + return Some((var_idx, rhs.clone())); + } else if let Some(var_idx) = rhs.var_idx() { + return Some((var_idx, lhs.clone())); } - if let Some(var_index) = kid.var_idx() { - debug_assert! { var.is_none() } - var = Some(var_index) ; - continue - } else if let Some((val, term)) = kid.cmul_inspect() { - if let Some(var_index) = term.var_idx() { - if val.is_one() { - var = Some(var_index) ; - continue - } else if val.is_minus_one() { - var = Some(var_index) ; - negated = true ; - continue + + if lhs.typ().is_arith() { + debug_assert! { rhs.is_zero() } + + let lhs = if let Some((_, term)) = lhs.cmul_inspect() { + term + } else { + lhs + }; + + let mut add = vec![]; + let mut var = None; + let mut negated = false; + + if let Some(kids) = lhs.add_inspect() { + for kid in kids { + if var.is_some() { + add.push(kid.clone()); + continue; + } + if let Some(var_index) = kid.var_idx() { + debug_assert! { var.is_none() } + var = Some(var_index); + continue; + } else if let Some((val, term)) = kid.cmul_inspect() { + if let Some(var_index) = term.var_idx() { + if val.is_one() { + var = Some(var_index); + continue; + } else if val.is_minus_one() { + var = Some(var_index); + negated = true; + continue; + } + } + } + add.push(kid.clone()) + } + + if let Some(var) = var { + let mut sum = term::add(add); + if !negated { + sum = term::u_minus(sum) + } + Some((var, sum)) + } else { + None + } + } else { + None } - } + } else { + None } - add.push(kid.clone()) - } - - if let Some(var) = var { - let mut sum = term::add(add) ; - if ! negated { sum = term::u_minus(sum) } - Some((var, sum)) - } else { - None - } } else { - None + None } - } else { - None - } - - } else { - None } - } - + /// Attempts to invert a term from a variable. + pub fn invert_var(&self, var: VarIdx, typ: Typ) -> Option<(VarIdx, Term)> { + self.invert(term::var(var, typ)) + } - /// Attempts to invert a term from a variable. - pub fn invert_var(& self, var: VarIdx, typ: Typ) -> Option<(VarIdx, Term)> { - self.invert( term::var(var, typ) ) - } - - /// Attempts to invert a term. - /// - /// More precisely, if the term only mentions one variable `v`, attempts to - /// find a `f` that's a solution of `var = term <=> v = f(var)`. - /// - /// Currently, only works when `v` appears exactly once. That is, it will - /// fail on `var = 3.v + 7.v` for instance. (This would be fine if - /// normalization handled this kind cases though.) - /// - /// Also, only works when all operators are binary (expect for unary minus). - /// - /// # Examples - /// - /// ```rust - /// use hoice::term ; - /// - /// let term = term::u_minus( term::int_var(0) ) ; - /// println!("{}", term) ; - /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( (0.into(), term::u_minus( term::int_var(1) ) ) ) - /// } - /// let term = term::sub( vec![ term::int_var(0), term::int(7) ] ) ; - /// println!("{}", term) ; - /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( (0.into(), term::add( vec![ term::int_var(1), term::int(7) ] ) ) ) - /// } - /// let term = term::add( vec![ term::int(7), term::int_var(0) ] ) ; - /// println!("{}", term) ; - /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( - /// (0.into(), term::sub( vec![ term::int_var(1), term::int(7) ] ) ) - /// ) - /// } - /// ``` - pub fn invert(& self, term: Term) -> Option<(VarIdx, Term)> { - let mut solution = term ; - let mut term = self ; - - loop { - // println!("inverting {}", term) ; - match * term { - - RTerm::App { op, ref args, .. } => { - let (po, symmetric) = match op { - Op::Add => (Op::Sub, true), - Op::Sub => { - if args.len() == 1 { - solution = term::u_minus( solution ) ; - term = & args[0] ; - continue - } else if args.len() == 2 { - if args[0].val().is_some() { - solution = term::sub( - vec![ args[0].clone(), solution ] - ) ; - term = & args[1] ; - continue - } else if args[1].val().is_some() { - solution = term::add( - vec![ args[1].clone(), solution ] - ) ; - term = & args[0] ; - continue - } - } - return None - }, - Op::IDiv => return None, - Op::CMul => { - if args.len() == 2 { - if let Some(val) = args[0].val() { - if val.minus().expect( - "illegal c_mul application found in `invert`" - ).is_one() { - solution = term::u_minus(solution) ; - term = & args[1] ; - continue - } else { - return None - } + /// Attempts to invert a term. + /// + /// More precisely, if the term only mentions one variable `v`, attempts to + /// find a `f` that's a solution of `var = term <=> v = f(var)`. + /// + /// Currently, only works when `v` appears exactly once. That is, it will + /// fail on `var = 3.v + 7.v` for instance. (This would be fine if + /// normalization handled this kind cases though.) + /// + /// Also, only works when all operators are binary (expect for unary minus). + /// + /// # Examples + /// + /// ```rust + /// use hoice::term ; + /// + /// let term = term::u_minus( term::int_var(0) ) ; + /// println!("{}", term) ; + /// assert_eq!{ + /// term.invert( term::int_var(1) ), + /// Some( (0.into(), term::u_minus( term::int_var(1) ) ) ) + /// } + /// let term = term::sub( vec![ term::int_var(0), term::int(7) ] ) ; + /// println!("{}", term) ; + /// assert_eq!{ + /// term.invert( term::int_var(1) ), + /// Some( (0.into(), term::add( vec![ term::int_var(1), term::int(7) ] ) ) ) + /// } + /// let term = term::add( vec![ term::int(7), term::int_var(0) ] ) ; + /// println!("{}", term) ; + /// assert_eq!{ + /// term.invert( term::int_var(1) ), + /// Some( + /// (0.into(), term::sub( vec![ term::int_var(1), term::int(7) ] ) ) + /// ) + /// } + /// ``` + pub fn invert(&self, term: Term) -> Option<(VarIdx, Term)> { + let mut solution = term; + let mut term = self; + + loop { + // println!("inverting {}", term) ; + match *term { + RTerm::App { op, ref args, .. } => { + let (po, symmetric) = match op { + Op::Add => (Op::Sub, true), + Op::Sub => { + if args.len() == 1 { + solution = term::u_minus(solution); + term = &args[0]; + continue; + } else if args.len() == 2 { + if args[0].val().is_some() { + solution = term::sub(vec![args[0].clone(), solution]); + term = &args[1]; + continue; + } else if args[1].val().is_some() { + solution = term::add(vec![args[1].clone(), solution]); + term = &args[0]; + continue; + } + } + return None; + } + Op::IDiv => return None, + Op::CMul => { + if args.len() == 2 { + if let Some(val) = args[0].val() { + if val + .minus() + .expect("illegal c_mul application found in `invert`") + .is_one() + { + solution = term::u_minus(solution); + term = &args[1]; + continue; + } else { + return None; + } + } + } + + panic!("illegal c_mul application found in `invert`") + } + // Op::Div => (Op::Mul, false), + // Op::Mul => (Op::Div, true), + Op::ToReal => { + solution = term::to_int(solution); + term = &args[0]; + continue; + } + Op::ToInt => { + solution = term::to_real(solution); + term = &args[0]; + continue; + } + _ => return None, + }; + if args.len() != 2 { + return None; + } + + if let Some(arith) = args[0].arith() { + if symmetric { + solution = term::app(po, vec![solution, arith]) + } else { + solution = term::app(op, vec![arith, solution]) + } + term = &args[1] + } else if let Some(arith) = args[1].arith() { + solution = term::app(po, vec![solution, arith]); + term = &args[0] + } else { + return None; + } } - } - panic!("illegal c_mul application found in `invert`") - }, - // Op::Div => (Op::Mul, false), - // Op::Mul => (Op::Div, true), - Op::ToReal => { - solution = term::to_int(solution) ; - term = & args[0] ; - continue - }, - Op::ToInt => { - solution = term::to_real(solution) ; - term = & args[0] ; - continue - }, - _ => return None, - } ; - if args.len() != 2 { return None } + RTerm::Var(_, v) => return Some((v, solution)), - if let Some(arith) = args[0].arith() { - if symmetric { - solution = term::app( po, vec![ solution, arith ] ) - } else { - solution = term::app( op, vec![ arith, solution ] ) + RTerm::Cst(_) + | RTerm::Fun { .. } + | RTerm::CArray { .. } + | RTerm::DTypNew { .. } + | RTerm::DTypSlc { .. } + | RTerm::DTypTst { .. } => return None, } - term = & args[1] - } else if let Some(arith) = args[1].arith() { - solution = term::app( po, vec![ solution, arith ] ) ; - term = & args[0] - } else { - return None - } - }, - - RTerm::Var(_, v) => return Some((v, solution)), - - RTerm::Cst ( _ ) | - RTerm::Fun { .. } | - RTerm::CArray { .. } | - RTerm::DTypNew { .. } | - RTerm::DTypSlc { .. } | - RTerm::DTypTst { .. } => return None, - - } + } } - } - } - impl_fmt!{ RTerm(self, fmt) { let mut buf = Vec::with_capacity(250) ; @@ -1311,15 +1349,15 @@ impl_fmt!{ } } impl<'a> PebcakFmt<'a> for RTerm { - type Info = & 'a VarMap< ::instance::info::VarInfo > ; - fn pebcak_err(& self) -> ErrorKind { - "during term pebcak formatting".into() - } - fn pebcak_io_fmt( - & self, w: & mut W, vars: & 'a VarMap< ::instance::info::VarInfo > - ) -> IoRes<()> { - self.write( - w, |w, var| w.write_all( vars[var].as_bytes() ) - ) - } + type Info = &'a VarMap<::instance::info::VarInfo>; + fn pebcak_err(&self) -> ErrorKind { + "during term pebcak formatting".into() + } + fn pebcak_io_fmt( + &self, + w: &mut W, + vars: &'a VarMap<::instance::info::VarInfo>, + ) -> IoRes<()> { + self.write(w, |w, var| w.write_all(vars[var].as_bytes())) + } } diff --git a/src/term/op.rs b/src/term/op.rs index f598f468..70fb38e1 100644 --- a/src/term/op.rs +++ b/src/term/op.rs @@ -1,112 +1,105 @@ //! Operators. -use common::* ; - - +use common::*; /// Operators. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum Op { - /// Addition. - Add, - /// Subtraction. - Sub, - /// Multiplication. - Mul, - /// Multiplication by a constant. - /// - /// Its arguments should always be [ constant, term ]. - CMul, - /// Integer division. - IDiv, - /// Division. - Div, - /// Remainder. - Rem, - /// Modulo. - Mod, - - /// Greater than. - Gt, - /// Greater than or equal to. - Ge, - /// Less than or equal to. - Le, - /// Less than. - Lt, - - /// Implication. - Impl, - /// Equal to. - Eql, - /// Negation. - Not, - /// Conjunction. - And, - /// Disjunction. - Or, - - /// If-then-else. - Ite, - /// Distinct. - Distinct, - - /// Conversion from `Int` to `Real`. - ToInt, - /// Conversion from `Real` to `Int`. - ToReal, - - /// Updater for arrays. - Store, - /// Accessor for arrays. - Select, + /// Addition. + Add, + /// Subtraction. + Sub, + /// Multiplication. + Mul, + /// Multiplication by a constant. + /// + /// Its arguments should always be [ constant, term ]. + CMul, + /// Integer division. + IDiv, + /// Division. + Div, + /// Remainder. + Rem, + /// Modulo. + Mod, + + /// Greater than. + Gt, + /// Greater than or equal to. + Ge, + /// Less than or equal to. + Le, + /// Less than. + Lt, + + /// Implication. + Impl, + /// Equal to. + Eql, + /// Negation. + Not, + /// Conjunction. + And, + /// Disjunction. + Or, + + /// If-then-else. + Ite, + /// Distinct. + Distinct, + + /// Conversion from `Int` to `Real`. + ToInt, + /// Conversion from `Real` to `Int`. + ToReal, + + /// Updater for arrays. + Store, + /// Accessor for arrays. + Select, } - - impl Op { - /// String representation. - pub fn as_str(self) -> & 'static str { - use self::Op::* ; - use keywords::op::* ; - match self { - Add => add_, - Sub => sub_, - Mul => mul_, - CMul => mul_, - IDiv => idiv_, - Div => div_, - Rem => rem_, - Mod => mod_, - Gt => gt_, - Ge => ge_, - Le => le_, - Lt => lt_, - Eql => eq_, - Not => not_, - And => and_, - Or => or_, - Impl => impl_, - Ite => ite_, - ToInt => to_int_, - ToReal => to_real_, - Store => store_, - Select => select_, - Distinct => distinct_, + /// String representation. + pub fn as_str(self) -> &'static str { + use self::Op::*; + use keywords::op::*; + match self { + Add => add_, + Sub => sub_, + Mul => mul_, + CMul => mul_, + IDiv => idiv_, + Div => div_, + Rem => rem_, + Mod => mod_, + Gt => gt_, + Ge => ge_, + Le => le_, + Lt => lt_, + Eql => eq_, + Not => not_, + And => and_, + Or => or_, + Impl => impl_, + Ite => ite_, + ToInt => to_int_, + ToReal => to_real_, + Store => store_, + Select => select_, + Distinct => distinct_, + } } - } + /// Type checking. + /// + /// If there is an error, returns the type the spurious argument should have + /// (if it is known) and the one found. + pub fn type_check(self, args: &mut [Term]) -> Result { + use Op::*; - /// Type checking. - /// - /// If there is an error, returns the type the spurious argument should have - /// (if it is known) and the one found. - pub fn type_check( - self, args: & mut [ Term ] - ) -> Result { - use Op::* ; - - macro_rules! err { + macro_rules! err { (lft $($lft:tt)*) => ( return Err( term::TypError::typ($($lft)*) ) ) ; @@ -122,146 +115,143 @@ impl Op { ) ; } - macro_rules! arity_check { - ( [ $min:tt, . ] => $e:expr ) => ( - if args.len() < $min { - err!(rgt - "illegal application of `{}` to {} arguments (> {})", - self, args.len(), $min - ) - } else { - $e + macro_rules! arity_check { + ( [ $min:tt, . ] => $e:expr ) => { + if args.len() < $min { + err!(rgt + "illegal application of `{}` to {} arguments (> {})", + self, args.len(), $min + ) + } else { + $e + } + }; + + ( [ $min:tt, $max:tt ] => $e:expr ) => { + if args.len() > $max { + err!(rgt + "illegal application of `{}` to {} arguments (> {})", + self, args.len(), $max + ) + } else { + arity_check!( [ $min, .] => $e ) + } + }; } - ) ; - ( [ $min:tt, $max:tt ] => $e:expr ) => ( - if args.len() > $max { - err!(rgt - "illegal application of `{}` to {} arguments (> {})", - self, args.len(), $max - ) - } else { - arity_check!( [ $min, .] => $e ) + macro_rules! all_same { + (arith) => {{ + let mut args_iter = args.iter_mut().enumerate(); + if let Some((index, fst)) = args_iter.next() { + let typ = fst.typ(); + if !typ.is_arith() { + err!(lft None, typ, index) + } + for (index, next) in args_iter { + if typ != next.typ() { + err!(lft Some(typ), next.typ(), index) + } + } + typ + } else { + err!(nullary) + } + }}; + + (bool) => {{ + let mut args_iter = args.iter_mut().enumerate(); + for (index, fst) in args_iter { + let typ = fst.typ(); + if typ != typ::bool() { + err!(lft Some(typ::bool()), typ, index) + } + } + typ::bool() + }}; + + () => { + all_same!( sub args args.iter_mut().enumerate() ) + }; + + (sub args $args:expr) => {{ + let mut args_iter = $args; + if let Some((_, fst)) = args_iter.next() { + // println!("all same") ; + let mut typ = fst.typ(); + // println!(" {} | {}", typ, fst) ; + for (index, next) in args_iter { + let next_typ = next.typ(); + // println!(" {} | {}", next_typ, next) ; + if let Some(merged_typ) = typ.merge(&next_typ) { + // println!(" nu_typ: {}", merged_typ) ; + if let Some(nu) = fst.force_dtyp(merged_typ.clone()) { + typ = merged_typ.clone(); + *fst = nu; + // println!(" -> {}", fst) + // println!("{}: {}", fst, fst.typ()) ; + } + if let Some(nu) = next.force_dtyp(merged_typ) { + // println!(" -> {} {}", nu, nu.typ()) ; + *next = nu; + // println!(" {} {}", next, next.typ()) + // println!("{}: {}", next, next.typ()) ; + } + } else { + err!(lft Some(typ), next.typ(), index) + } + } + typ + } else { + err!(nullary) + } + }}; + + ($typ:expr) => {{ + let mut args_iter = args.iter_mut().enumerate(); + for (index, fst) in args_iter { + if fst.typ() != $typ { + err!(lft Some($typ), fst.typ(), index) + } + } + $typ + }}; } - ) ; - } - macro_rules! all_same { - (arith) => ({ - let mut args_iter = args.iter_mut().enumerate() ; - if let Some((index, fst)) = args_iter.next() { - let typ = fst.typ() ; - if ! typ.is_arith() { - err!(lft None, typ, index) - } - for (index, next) in args_iter { - if typ != next.typ() { - err!(lft Some(typ), next.typ(), index) - } - } - typ - } else { - err!(nullary) - } - }) ; - - (bool) => ({ - let mut args_iter = args.iter_mut().enumerate() ; - for (index, fst) in args_iter { - let typ = fst.typ() ; - if typ != typ::bool() { - err!(lft Some(typ::bool()), typ, index) - } - } - typ::bool() - }) ; - - () => ( - all_same!( sub args args.iter_mut().enumerate() ) - ) ; - - (sub args $args:expr) => ({ - let mut args_iter = $args ; - if let Some((_, fst)) = args_iter.next() { - // println!("all same") ; - let mut typ = fst.typ() ; - // println!(" {} | {}", typ, fst) ; - for (index, next) in args_iter { - let next_typ = next.typ() ; - // println!(" {} | {}", next_typ, next) ; - if let Some(merged_typ) = typ.merge( & next_typ ) { - // println!(" nu_typ: {}", merged_typ) ; - if let Some(nu) = fst.force_dtyp( merged_typ.clone() ) { - typ = merged_typ.clone() ; - * fst = nu ; - // println!(" -> {}", fst) - // println!("{}: {}", fst, fst.typ()) ; - } - if let Some(nu) = next.force_dtyp( merged_typ ) { - // println!(" -> {} {}", nu, nu.typ()) ; - * next = nu ; - // println!(" {} {}", next, next.typ()) - // println!("{}: {}", next, next.typ()) ; - } - } else { - err!(lft Some(typ), next.typ(), index) - } - } - typ - } else { - err!(nullary) - } - }) ; - - ($typ:expr) => ({ - let mut args_iter = args.iter_mut().enumerate() ; - for (index, fst) in args_iter { - if fst.typ() != $typ { - err!(lft Some($typ), fst.typ(), index) - } - } - $typ - }) ; - } - - let res = match self { - Add | Sub | Mul | Div | CMul => { - all_same!(arith) - }, - IDiv | Rem | Mod => arity_check!( + let res = match self { + Add | Sub | Mul | Div | CMul => all_same!(arith), + IDiv | Rem | Mod => arity_check!( [ 2, 2 ] => { all_same!(typ::int()) } ), - Gt | Ge | Le | Lt => arity_check!( + Gt | Ge | Le | Lt => arity_check!( [ 2, 2 ] => { all_same!(arith) ; typ::bool() } ), - Distinct | - Eql => { - all_same!() ; - typ::bool() - }, - Not => arity_check!( + Distinct | Eql => { + all_same!(); + typ::bool() + } + Not => arity_check!( [ 1, 1 ] => all_same!(typ::bool()) ), - And | Or | Impl => all_same!(bool), - ToInt => arity_check!( + And | Or | Impl => all_same!(bool), + ToInt => arity_check!( [ 1, 1 ] => { all_same!(typ::real()) ; typ::int() } ), - ToReal => arity_check!( + ToReal => arity_check!( [ 1, 1 ] => { all_same!(typ::int()) ; typ::real() } ), - Ite => arity_check!( + Ite => arity_check!( [ 3, 3 ] => { let mut args_iter = args.iter_mut().enumerate() ; @@ -276,7 +266,7 @@ impl Op { } ), - Store => arity_check!( + Store => arity_check!( [ 3, 3 ] => if let Some((src, tgt)) = args[0].typ().array_inspect() { if args[1].typ() != * src { err!( @@ -294,7 +284,7 @@ impl Op { } ), - Select => arity_check!( + Select => arity_check!( [ 2, 2 ] => if let Some((src, tgt)) = args[0].typ().array_inspect() { if args[1].typ() != * src { err!( @@ -307,54 +297,53 @@ impl Op { err!(lft None, args[0].typ(), 0) } ), - } ; - Ok(res) - } - - - /// Evaluation. - pub fn eval(self, args: Vec) -> Res { - use term::Op::* ; - if args.is_empty() { - bail!("evaluating operator on 0 elements") + }; + Ok(res) } - match self { - // Arithmetic operators. - Add => eval::add(args), - Sub => eval::sub(args), - CMul | Mul => eval::mul(args), - Div => eval::div(args), - IDiv => eval::idiv(args), - Rem => eval::rem(args), - Mod => eval::modulo(args), - - // Relations over arithmetic. - Gt => eval::gt(args), - Ge => eval::ge(args), - Le => eval::le(args), - Lt => eval::lt(args), - - // Polymorphic operators. - Distinct => eval::distinct(args), - Eql => eval::eql(args), - Ite => eval::ite(args), - - // Boolean operators. - Not => eval::not(args), - And => eval::and(args), - Or => eval::or(args), - Impl => eval::implies(args), - - // Cast operators. - ToInt => eval::to_int(args), - ToReal => eval::to_real(args), - - // Array operators. - Store => eval::store(args), - Select => eval::select(args), + /// Evaluation. + pub fn eval(self, args: Vec) -> Res { + use term::Op::*; + if args.is_empty() { + bail!("evaluating operator on 0 elements") + } + + match self { + // Arithmetic operators. + Add => eval::add(args), + Sub => eval::sub(args), + CMul | Mul => eval::mul(args), + Div => eval::div(args), + IDiv => eval::idiv(args), + Rem => eval::rem(args), + Mod => eval::modulo(args), + + // Relations over arithmetic. + Gt => eval::gt(args), + Ge => eval::ge(args), + Le => eval::le(args), + Lt => eval::lt(args), + + // Polymorphic operators. + Distinct => eval::distinct(args), + Eql => eval::eql(args), + Ite => eval::ite(args), + + // Boolean operators. + Not => eval::not(args), + And => eval::and(args), + Or => eval::or(args), + Impl => eval::implies(args), + + // Cast operators. + ToInt => eval::to_int(args), + ToReal => eval::to_real(args), + + // Array operators. + Store => eval::store(args), + Select => eval::select(args), + } } - } } impl_fmt!{ Op(self, fmt) { @@ -362,58 +351,54 @@ impl_fmt!{ } } - - - /// Evaluation-related stuff. mod eval { - use common::* ; - - /// Applies an operation on arithmetic arguments. - macro_rules! arith_app { - (relation $op:tt $str:tt => $args:expr) => ({ - let mut args = $args.into_iter() ; - if let ( - Some(fst), Some(mut pre) - ) = (args.next(), args.next()) { - - let mut res = fst.get().$op( & pre ) ? ; - - for arg in args { - res = res.and( & pre.get().$op( & arg ) ? ) ? ; - pre = arg - } - Ok(res) - } else { - bail!("`{}` applied to 0 or 1 argument(s)") - } - }) ; - ($op:tt $str:tt => $args:expr) => ({ - let mut args = $args.into_iter() ; - if let Some(mut acc) = args.next() { - for arg in args { - acc = acc.$op(& arg) ? - } - Ok(acc) - } else { - bail!("`{}` applied to zero arguments", $str) - } - }) ; - } + use common::*; + + /// Applies an operation on arithmetic arguments. + macro_rules! arith_app { + (relation $op:tt $str:tt => $args:expr) => {{ + let mut args = $args.into_iter(); + if let (Some(fst), Some(mut pre)) = (args.next(), args.next()) { + let mut res = fst.get().$op(&pre)?; + + for arg in args { + res = res.and(&pre.get().$op(&arg)?)?; + pre = arg + } + Ok(res) + } else { + bail!("`{}` applied to 0 or 1 argument(s)") + } + }}; + ($op:tt $str:tt => $args:expr) => {{ + let mut args = $args.into_iter(); + if let Some(mut acc) = args.next() { + for arg in args { + acc = acc.$op(&arg)? + } + Ok(acc) + } else { + bail!("`{}` applied to zero arguments", $str) + } + }}; + } - /// Arity check. - macro_rules! arity { - ($op:expr => $args:expr, $len:expr) => ( - if $args.len() != $len { - bail!( - "illegal application of `{}` to {} arguments", $op, $args.len() - ) - } - ) ; - } + /// Arity check. + macro_rules! arity { + ($op:expr => $args:expr, $len:expr) => { + if $args.len() != $len { + bail!( + "illegal application of `{}` to {} arguments", + $op, + $args.len() + ) + } + }; + } - /// Creates an evaluation function. - macro_rules! eval_fun { + /// Creates an evaluation function. + macro_rules! eval_fun { ( $(fn $name:ident($args:pat) $body:expr);* $(;)* ) => ( $( pub fn $name($args: Vec) -> Res { $body } @@ -421,186 +406,177 @@ mod eval { ) ; } - // Arithmetic operators. - - eval_fun! { - // Addition. - fn add(args) arith_app!(add "+" => args) ; - - // Subtraction. - fn sub(mut args) if args.len() == 1 { - args.pop().unwrap().minus() - } else { - arith_app!(sub "-" => args) - } ; - - // Multiplication - fn mul(args) arith_app!(mul "*" => args) ; - - // Division. - fn div(args) arith_app!(div "/" => args) ; - - // Integer division. - fn idiv(args) { - arity!("div" => args, 2) ; - args[0].idiv(& args[1]) - } ; - - // Remainder. - fn rem(args) { - arity!("rem" => args, 2) ; - args[0].rem(& args[1]) - } ; - - // Modulo. - fn modulo(args) { - arity!("mod" => args, 2) ; - args[0].modulo(& args[1]) - } ; - } + // Arithmetic operators. - // Relations over arithmetic. - eval_fun! { - // Greater than. - fn gt(args) arith_app!(relation g_t ">" => args) ; - // Greater than or equal to. - fn ge(args) arith_app!(relation g_e ">=" => args) ; - // Less than. - fn lt(args) arith_app!(relation l_t "<" => args) ; - // Less than or equal to. - fn le(args) arith_app!(relation l_e "<=" => args) ; - } + eval_fun! { + // Addition. + fn add(args) arith_app!(add "+" => args) ; + + // Subtraction. + fn sub(mut args) if args.len() == 1 { + args.pop().unwrap().minus() + } else { + arith_app!(sub "-" => args) + } ; + + // Multiplication + fn mul(args) arith_app!(mul "*" => args) ; + + // Division. + fn div(args) arith_app!(div "/" => args) ; + + // Integer division. + fn idiv(args) { + arity!("div" => args, 2) ; + args[0].idiv(& args[1]) + } ; + + // Remainder. + fn rem(args) { + arity!("rem" => args, 2) ; + args[0].rem(& args[1]) + } ; + + // Modulo. + fn modulo(args) { + arity!("mod" => args, 2) ; + args[0].modulo(& args[1]) + } ; + } - // Polymorphic operators. - eval_fun! { - // Distinct. - fn distinct(mut args) { - while let Some(arg) = args.pop() { - for other in & args { - if let Some(equal) = arg.eql(& other).to_bool() ? { - if equal { - return Ok( val::bool(false) ) + // Relations over arithmetic. + eval_fun! { + // Greater than. + fn gt(args) arith_app!(relation g_t ">" => args) ; + // Greater than or equal to. + fn ge(args) arith_app!(relation g_e ">=" => args) ; + // Less than. + fn lt(args) arith_app!(relation l_t "<" => args) ; + // Less than or equal to. + fn le(args) arith_app!(relation l_e "<=" => args) ; + } + + // Polymorphic operators. + eval_fun! { + // Distinct. + fn distinct(mut args) { + while let Some(arg) = args.pop() { + for other in & args { + if let Some(equal) = arg.eql(& other).to_bool() ? { + if equal { + return Ok( val::bool(false) ) + } + } else { + return Ok( val::none( typ::bool() ) ) } - } else { - return Ok( val::none( typ::bool() ) ) } } - } - Ok( val::bool(true) ) - } ; - - // Equal. - fn eql(mut args) { - if let Some(fst) = args.pop() { - for other in & args { - if let Some(equal) = fst.eql(other).to_bool() ? { - if ! equal { - return Ok( val::bool(false) ) + Ok( val::bool(true) ) + } ; + + // Equal. + fn eql(mut args) { + if let Some(fst) = args.pop() { + for other in & args { + if let Some(equal) = fst.eql(other).to_bool() ? { + if ! equal { + return Ok( val::bool(false) ) + } + } else { + return Ok( val::none( typ::bool() ) ) } - } else { - return Ok( val::none( typ::bool() ) ) } } + Ok( val::bool(true) ) + } ; + + // If-then-else. + fn ite(mut args) { + arity!("ite" => args, 3) ; + let (els, thn, cnd) = ( + args.pop().unwrap(), args.pop().unwrap(), args.pop().unwrap() + ) ; + cnd.ite(thn, els) } - Ok( val::bool(true) ) - } ; - - // If-then-else. - fn ite(mut args) { - arity!("ite" => args, 3) ; - let (els, thn, cnd) = ( - args.pop().unwrap(), args.pop().unwrap(), args.pop().unwrap() - ) ; - cnd.ite(thn, els) } - } - - // Boolean operators. - eval_fun! { - // Not. - fn not(args) { - arity!("not" => args, 1) ; - args[0].not() - } ; - - // Conjunction. - fn and(args) { - for arg in args { - match arg.to_bool() ? { - Some(true) => (), - Some(false) => return Ok( val::bool(false) ), - None => return Ok( val::none( typ::bool() ) ), - } - } - Ok( val::bool(true) ) - } ; - - // Disjunction. - fn or(args) { - let mut res = val::bool(false) ; - for arg in args { - match arg.to_bool() ? { - Some(true) => return Ok( val::bool(true) ), - Some(false) => (), - None => res = val::none( typ::bool() ), - } - } - Ok(res) - } ; - - // Implication. - fn implies(args) { - arity!("=>" => args, 2) ; - args[0].implies( & args[1] ) - } ; - } - - // Cast operators. - eval_fun! { - // To int. - fn to_int(args) { - arity!("to-int" => args, 1) ; - args[0].real_to_int() - } ; - - // To real. - fn to_real(args) { - arity!("to-real" => args, 1) ; - args[0].int_to_real() - } ; - } - - // Array operators. - eval_fun! { - // Store. - fn store(mut args) { - arity!("store" => args, 3) ; - let (val, idx, array) = ( - args.pop().unwrap(), - args.pop().unwrap(), - args.pop().unwrap(), - ) ; - Ok( array.store(idx, val) ) - } ; - - // Select. - fn select(mut args) { - arity!("select" => args, 2) ; - let (idx, array) = ( - args.pop().unwrap(), - args.pop().unwrap(), - ) ; - Ok( array.select(idx) ) - } ; - } - -} - - + // Boolean operators. + eval_fun! { + // Not. + fn not(args) { + arity!("not" => args, 1) ; + args[0].not() + } ; + // Conjunction. + fn and(args) { + for arg in args { + match arg.to_bool() ? { + Some(true) => (), + Some(false) => return Ok( val::bool(false) ), + None => return Ok( val::none( typ::bool() ) ), + } + } + Ok( val::bool(true) ) + } ; + // Disjunction. + fn or(args) { + let mut res = val::bool(false) ; + for arg in args { + match arg.to_bool() ? { + Some(true) => return Ok( val::bool(true) ), + Some(false) => (), + None => res = val::none( typ::bool() ), + } + } + Ok(res) + } ; + // Implication. + fn implies(args) { + arity!("=>" => args, 2) ; + args[0].implies( & args[1] ) + } ; + } + // Cast operators. + eval_fun! { + // To int. + fn to_int(args) { + arity!("to-int" => args, 1) ; + args[0].real_to_int() + } ; + + // To real. + fn to_real(args) { + arity!("to-real" => args, 1) ; + args[0].int_to_real() + } ; + } + // Array operators. + eval_fun! { + // Store. + fn store(mut args) { + arity!("store" => args, 3) ; + let (val, idx, array) = ( + args.pop().unwrap(), + args.pop().unwrap(), + args.pop().unwrap(), + ) ; + Ok( array.store(idx, val) ) + } ; + + // Select. + fn select(mut args) { + arity!("select" => args, 2) ; + let (idx, array) = ( + args.pop().unwrap(), + args.pop().unwrap(), + ) ; + Ok( array.select(idx) ) + } ; + } +} diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 3ccb8788..712d3383 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1,210 +1,191 @@ //! Term simplification. -pub use std::cmp::Ordering ; -use std::ops::Deref ; +pub use std::cmp::Ordering; +use std::ops::Deref; use term::factory::NormRes; -use common::* ; - - +use common::*; lazy_static! { - static ref simpl_solver: RwLock< - Option<::rsmt2::Solver<()>> - > = RwLock::new( - if conf.check_simpl { - Some( - conf.solver.spawn("check_simpl", (), & Instance::new()).unwrap() - ) - } else { - None - } - ) ; + static ref simpl_solver: RwLock>> = + RwLock::new(if conf.check_simpl { + Some( + conf.solver + .spawn("check_simpl", (), &Instance::new()) + .unwrap(), + ) + } else { + None + }); } - - /// Result of a simplification check between two terms. #[derive(PartialEq, Eq, Clone, Debug)] pub enum SimplRes { - /// Nothing. - None, - /// The two terms are directly related. - Cmp(Ordering), - /// The two terms are equivalent to another term. - Yields(Term), + /// Nothing. + None, + /// The two terms are directly related. + Cmp(Ordering), + /// The two terms are equivalent to another term. + Yields(Term), } impl SimplRes { - /// Inverts the result. - pub fn invert(self) -> Self { - use self::SimplRes::* ; - match self { - None => None, - Cmp(ord) => Cmp( ord.reverse() ), - Yields(term) => Yields(term), + /// Inverts the result. + pub fn invert(self) -> Self { + use self::SimplRes::*; + match self { + None => None, + Cmp(ord) => Cmp(ord.reverse()), + Yields(term) => Yields(term), + } + } + /// True if `self` is not `None`. + #[inline] + pub fn is_some(&self) -> bool { + self != &SimplRes::None + } + /// True if `self` is `None`. + #[inline] + pub fn is_none(&self) -> bool { + !self.is_some() } - } - /// True if `self` is not `None`. - #[inline] - pub fn is_some(& self) -> bool { - self != & SimplRes::None - } - /// True if `self` is `None`. - #[inline] - pub fn is_none(& self) -> bool { - ! self.is_some() - } } - /// Adds a term to a set understood as a conjunction. /// /// Returns `true` if the resulting set is false. -pub fn conj_term_insert( - term: Term, set: & mut TermSet -) -> bool { - - let mut next_term = Some(term) ; - let mut fls = false ; - let mut add_term ; - - macro_rules! helper { - (add term) => ( - next_term.is_none() && add_term - ) ; - - (is false) => (fls) ; - (term dropped) => (next_term.is_none() && ! add_term) ; - (is true) => (! fls && helper!(term dropped)) ; - - (drop term) => ({ - next_term = None ; - add_term = false - }) ; - - (set false) => ({ - fls = true ; - helper!(drop term) - }) ; - - (set true) => ({ - debug_assert! { ! fls } - helper!(drop term) - }) ; - - (update $old_term:expr, to $term:expr) => ({ - $old_term = $term.clone() ; - next_term = Some($term) ; - }) ; - } - - while let Some(mut term) = ::std::mem::replace( - & mut next_term, None - ) { - add_term = true ; - - - set.retain( - |set_term| { - if helper!(is false) { - return false - } - if helper!(is true) { - return true - } +pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { + let mut next_term = Some(term); + let mut fls = false; + let mut add_term; + + macro_rules! helper { + (add term) => { + next_term.is_none() && add_term + }; + + (is false) => { + fls + }; + (term dropped) => { + next_term.is_none() && !add_term + }; + (is true) => { + !fls && helper!(term dropped) + }; + + (drop term) => {{ + next_term = None; + add_term = false + }}; + + (set false) => {{ + fls = true; + helper!(drop term) + }}; + + (set true) => {{ + debug_assert! { ! fls } + helper!(drop term) + }}; + + (update $old_term:expr, to $term:expr) => {{ + $old_term = $term.clone(); + next_term = Some($term); + }}; + } - use std::cmp::Ordering::* ; - match conj_simpl(& term, set_term) { - SimplRes::None => true, + while let Some(mut term) = ::std::mem::replace(&mut next_term, None) { + add_term = true; - SimplRes::Cmp(Less) | - SimplRes::Cmp(Equal) => { - helper!(drop term) ; - true - }, + set.retain(|set_term| { + if helper!(is false) { + return false; + } + if helper!(is true) { + return true; + } - SimplRes::Cmp(Greater) => { - false - }, + use std::cmp::Ordering::*; + match conj_simpl(&term, set_term) { + SimplRes::None => true, + + SimplRes::Cmp(Less) | SimplRes::Cmp(Equal) => { + helper!(drop term); + true + } + + SimplRes::Cmp(Greater) => false, + + SimplRes::Yields(nu_term) => match nu_term.bool() { + Some(true) => { + helper!(set true); + false + } + Some(false) => { + helper!(set false); + false + } + None => { + helper!(update term, to nu_term); + false + } + }, + } + }); - SimplRes::Yields(nu_term) => match nu_term.bool() { - Some(true) => { - helper!(set true) ; - false - }, - Some(false) => { - helper!(set false) ; - false - }, - None => { - helper!(update term, to nu_term) ; - false - }, - }, + if helper!(add term) { + let is_new = set.insert(term); + debug_assert! { is_new } } - } - ) ; - - if helper!(add term) { - let is_new = set.insert(term) ; - debug_assert! { is_new } } - } - if fls { - set.clear() ; - set.insert( term::fls() ) ; - } - fls + if fls { + set.clear(); + set.insert(term::fls()); + } + fls } - - /// Simplifies a conjunction. -pub fn conj_vec_simpl(terms: & mut Vec) { - let mut res = Vec::with_capacity( terms.len() ) ; +pub fn conj_vec_simpl(terms: &mut Vec) { + let mut res = Vec::with_capacity(terms.len()); - 'add_terms: while let Some(term) = terms.pop() { + 'add_terms: while let Some(term) = terms.pop() { + let mut cnt = 0; - let mut cnt = 0 ; + while cnt < res.len() { + use self::SimplRes::*; + use std::cmp::Ordering::*; - while cnt < res.len() { - use self::SimplRes::* ; - use std::cmp::Ordering::* ; + match conj_simpl(&term, &res[cnt]) { + None => cnt += 1, - match conj_simpl(& term, & res[cnt]) { - None => cnt += 1, + Cmp(Less) | Cmp(Equal) => continue 'add_terms, - Cmp(Less) | - Cmp(Equal) => continue 'add_terms, + Cmp(Greater) => { + res.swap_remove(cnt); + } - Cmp(Greater) => { - res.swap_remove(cnt) ; - }, + Yields(term) => { + res.swap_remove(cnt); + terms.push(term); + continue 'add_terms; + } + } + } - Yields(term) => { - res.swap_remove(cnt) ; - terms.push(term) ; - continue 'add_terms - }, - } + res.push(term) } - res.push(term) + res.shrink_to_fit(); + res.sort_unstable(); + res.dedup(); - } - - res.shrink_to_fit() ; - res.sort_unstable() ; - res.dedup() ; - - ::std::mem::swap(terms, & mut res) + ::std::mem::swap(terms, &mut res) } - - - /// Checks the conjunction of two terms for simplification. /// /// Returns @@ -321,306 +302,283 @@ pub fn conj_vec_simpl(terms: & mut Vec) { /// # println!("=> {}\n\n", rhs) ; /// debug_assert_eq! { conj_simpl(& lhs, & rhs), None } /// ``` -pub fn conj_simpl(lhs: & T1, rhs: & T2) -> SimplRes -where T1: Deref, T2: Deref { +pub fn conj_simpl(lhs: &T1, rhs: &T2) -> SimplRes +where + T1: Deref, + T2: Deref, +{ + if conf.term_simpl == 0 { + return SimplRes::None; + } - if conf.term_simpl == 0 { - return SimplRes::None - } + let res = conj_simpl_2(lhs, rhs); + + if res.is_some() { + if let Some(solver) = simpl_solver + .write() + .expect("could not retrieve term simplification solver") + .as_mut() + { + solver + .push(1) + .expect("error on `push` during term simplification"); + let mut vars = VarSet::new(); + lhs.iter(|term| { + if let Some(idx) = term.var_idx() { + let is_new = vars.insert(idx); + if is_new { + solver + .declare_const(&format!("{}", term), term.typ().get()) + .expect("error on `declare_const` during term simplification (lhs)") + } + } + }); + rhs.iter(|term| { + if let Some(idx) = term.var_idx() { + let is_new = vars.insert(idx); + if is_new { + solver + .declare_const(&format!("{}", term), term.typ().get()) + .expect("error on `declare_const` during term simplification (rhs)") + } + } + }); + + use std::cmp::Ordering::*; + let check = match res { + SimplRes::Cmp(Equal) => format!("(= {} {})", lhs.deref(), rhs.deref()), + SimplRes::Cmp(Less) => format!("(=> {} {})", rhs.deref(), lhs.deref()), + SimplRes::Cmp(Greater) => format!("(=> {} {})", lhs.deref(), rhs.deref()), + SimplRes::Yields(ref term) => { + format!("(= (and {} {}) {})", lhs.deref(), rhs.deref(), term) + } + SimplRes::None => unreachable!(), + }; + + solver + .assert(&format!("(not {})", check)) + .expect("error on `assert` during term simplification"); + + if solver + .check_sat() + .expect("could not retrieve check-sat result in conjunction simplification") + { + log! { @0 + " " ; + "{}", lhs.deref() ; + "{}", rhs.deref() ; + "{:?}", res ; + " " + } + panic! { "simplification failure" } + } - let res = conj_simpl_2(lhs, rhs) ; + solver + .pop(1) + .expect("error on `pop` during term simplification") + } + } - if res.is_some() { + match res { + SimplRes::Yields(_) if conf.term_simpl <= 1 => SimplRes::None, + res => res, + } +} - if let Some(solver) = simpl_solver.write().expect( - "could not retrieve term simplification solver" - ).as_mut() { - solver.push(1).expect( - "error on `push` during term simplification" - ) ; - let mut vars = VarSet::new() ; - lhs.iter( - |term| if let Some(idx) = term.var_idx() { - let is_new = vars.insert(idx) ; - if is_new { - solver.declare_const( - & format!("{}", term), term.typ().get() - ).expect( - "error on `declare_const` during term simplification (lhs)" - ) - } +pub fn conj_simpl_2(lhs: &T1, rhs: &T2) -> SimplRes +where + T1: Deref, + T2: Deref, +{ + use std::cmp::Ordering::*; + + if let Some(args) = lhs.disj_inspect() { + let mut greater_count = 0; + let mut yields = vec![]; + for lhs in args { + match int_conj_simpl(lhs, rhs, true) { + SimplRes::Cmp(Equal) | SimplRes::Cmp(Less) => return SimplRes::Cmp(Less), + SimplRes::Cmp(Greater) => greater_count += 1, + + SimplRes::Yields(term) => yields.push(term), + SimplRes::None => (), + } } - ) ; - rhs.iter( - |term| if let Some(idx) = term.var_idx() { - let is_new = vars.insert(idx) ; - if is_new { - solver.declare_const( - & format!("{}", term), term.typ().get() - ).expect( - "error on `declare_const` during term simplification (rhs)" - ) - } + if yields.len() == args.len() { + return SimplRes::Yields(term::or(yields)); + } else if greater_count == args.len() { + return SimplRes::Cmp(Greater); } - ) ; - - use std::cmp::Ordering::* ; - let check = match res { - SimplRes::Cmp(Equal) => format!( - "(= {} {})", lhs.deref(), rhs.deref() - ), - SimplRes::Cmp(Less) => format!( - "(=> {} {})", rhs.deref(), lhs.deref() - ), - SimplRes::Cmp(Greater) => format!( - "(=> {} {})", lhs.deref(), rhs.deref() - ), - SimplRes::Yields(ref term) => format!( - "(= (and {} {}) {})", lhs.deref(), rhs.deref(), term - ), - SimplRes::None => unreachable!(), - } ; - - solver.assert(& format!("(not {})", check)).expect( - "error on `assert` during term simplification" - ) ; - - if solver.check_sat().expect( - "could not retrieve check-sat result in conjunction simplification" - ) { - log! { @0 - " " ; - "{}", lhs.deref() ; - "{}", rhs.deref() ; - "{:?}", res ; - " " + } else if let Some(args) = rhs.disj_inspect() { + let mut less_count = 0; + let mut yields = vec![]; + for rhs in args { + match int_conj_simpl(lhs, rhs, true) { + SimplRes::Cmp(Equal) | SimplRes::Cmp(Greater) => return SimplRes::Cmp(Greater), + SimplRes::Cmp(Less) => less_count += 1, + + SimplRes::Yields(term) => yields.push(term), + SimplRes::None => (), + } + } + if yields.len() == args.len() { + return SimplRes::Yields(term::or(yields)); + } else if less_count == args.len() { + return SimplRes::Cmp(Greater); } - panic! { "simplification failure" } - } - - solver.pop(1).expect( - "error on `pop` during term simplification" - ) } - } - match res { - SimplRes::Yields(_) if conf.term_simpl <= 1 => SimplRes::None, - res => res, - } + int_conj_simpl(lhs, rhs, true) } - -pub fn conj_simpl_2(lhs: & T1, rhs: & T2) -> SimplRes -where T1: Deref, T2: Deref { - use std::cmp::Ordering::* ; - - if let Some(args) = lhs.disj_inspect() { - let mut greater_count = 0 ; - let mut yields = vec![] ; - for lhs in args { - match int_conj_simpl(lhs, rhs, true) { - SimplRes::Cmp(Equal) | - SimplRes::Cmp(Less) => return SimplRes::Cmp(Less), - SimplRes::Cmp(Greater) => greater_count += 1, - - SimplRes::Yields(term) => yields.push(term), - SimplRes::None => (), - } - } - if yields.len() == args.len() { - return SimplRes::Yields( term::or(yields) ) - } else if greater_count == args.len() { - return SimplRes::Cmp(Greater) - } - } else if let Some(args) = rhs.disj_inspect() { - let mut less_count = 0 ; - let mut yields = vec![] ; - for rhs in args { - match int_conj_simpl(lhs, rhs, true) { - SimplRes::Cmp(Equal) | - SimplRes::Cmp(Greater) => return SimplRes::Cmp(Greater), - SimplRes::Cmp(Less) => less_count += 1, - - SimplRes::Yields(term) => yields.push(term), - SimplRes::None => (), - } - } - if yields.len() == args.len() { - return SimplRes::Yields( term::or(yields) ) - } else if less_count == args.len() { - return SimplRes::Cmp(Greater) +fn int_conj_simpl(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes +where + T1: Deref, + T2: Deref, +{ + use std::cmp::Ordering::*; + // Input boolean is true (false) for `lhs` => `rhs` (reversed). + macro_rules! ord_of_bool { + ($b:expr) => { + if $b { + SimplRes::Cmp(Greater) + } else { + SimplRes::Cmp(Less) + } + }; } - } - - int_conj_simpl(lhs, rhs, true) -} - - -fn int_conj_simpl(lhs: & T1, rhs: & T2, conj: bool) -> SimplRes -where T1: Deref, T2: Deref { - use std::cmp::Ordering::* ; - // Input boolean is true (false) for `lhs` => `rhs` (reversed). - macro_rules! ord_of_bool { - ($b:expr) => ( - if $b { - SimplRes::Cmp(Greater) - } else { - SimplRes::Cmp(Less) - } - ) ; - } - - let (lhs, rhs) = ( lhs.deref(), rhs.deref() ) ; - // A term implies itself. - if lhs == rhs { - return SimplRes::Cmp(Equal) - } - - match ( lhs.bool(), rhs.bool() ) { - // True can only imply true. - (Some(true), rhs) => return ord_of_bool!( - rhs.unwrap_or(false) - ), - // False implies anything. - (Some(false), _) => return ord_of_bool!(true), - // False can only be implied by false. - (lhs, Some(false)) => return ord_of_bool!( - ! lhs.unwrap_or(true) - ), - // True is implied by anything. - (_, Some(true)) => return ord_of_bool!(true), - // Otherwise we don't know (yet). - (None, None) => (), - } + let (lhs, rhs) = (lhs.deref(), rhs.deref()); - // Only legal atoms are `vars >= cst` and `vars > cst`. - let ( - lhs_op, lhs_vars, lhs_cst - ) = if let Some((op, args)) = lhs.app_inspect() { - if ! args[0].typ().is_arith() { - return SimplRes::None - } - ( - op, - & args[0], - args[1].val().expect( - "error during value unwrapping in term simplification (lhs)" - ), - ) - } else { - return SimplRes::None - } ; - let ( - rhs_op, rhs_vars, rhs_cst - ) = if let Some((op, args)) = rhs.app_inspect() { - if ! args[0].typ().is_arith() { - return SimplRes::None + // A term implies itself. + if lhs == rhs { + return SimplRes::Cmp(Equal); } - ( - op, - & args[0], - args[1].val().expect( - "error during value unwrapping in term simplification (rhs)" - ), - ) - } else { - return SimplRes::None - } ; - if lhs_vars == rhs_vars { - - match (lhs_op, rhs_op) { - (Op::Gt, Op::Gt) | - (Op::Ge, Op::Ge) => return SimplRes::Cmp( - lhs_cst.get().compare(& rhs_cst).expect( - "error during comparison unwrapping in term simplification (same op)" - ) - ), + match (lhs.bool(), rhs.bool()) { + // True can only imply true. + (Some(true), rhs) => return ord_of_bool!(rhs.unwrap_or(false)), + // False implies anything. + (Some(false), _) => return ord_of_bool!(true), + // False can only be implied by false. + (lhs, Some(false)) => return ord_of_bool!(!lhs.unwrap_or(true)), + // True is implied by anything. + (_, Some(true)) => return ord_of_bool!(true), + // Otherwise we don't know (yet). + (None, None) => (), + } - (Op::Gt, Op::Ge) | - (Op::Ge, Op::Gt) => if lhs_cst == rhs_cst { - if lhs_op == Op::Gt { - return SimplRes::Cmp(Greater) - } else { - return SimplRes::Cmp(Less) + // Only legal atoms are `vars >= cst` and `vars > cst`. + let (lhs_op, lhs_vars, lhs_cst) = if let Some((op, args)) = lhs.app_inspect() { + if !args[0].typ().is_arith() { + return SimplRes::None; } - } else { - return SimplRes::Cmp( - lhs_cst.get().compare(& rhs_cst).expect( - "error during comparison unwrapping in term simplification (diff op)" + ( + op, + &args[0], + args[1] + .val() + .expect("error during value unwrapping in term simplification (lhs)"), ) + } else { + return SimplRes::None; + }; + let (rhs_op, rhs_vars, rhs_cst) = if let Some((op, args)) = rhs.app_inspect() { + if !args[0].typ().is_arith() { + return SimplRes::None; + } + ( + op, + &args[0], + args[1] + .val() + .expect("error during value unwrapping in term simplification (rhs)"), ) - }, + } else { + return SimplRes::None; + }; + + if lhs_vars == rhs_vars { + match (lhs_op, rhs_op) { + (Op::Gt, Op::Gt) | (Op::Ge, Op::Ge) => { + return SimplRes::Cmp( + lhs_cst.get().compare(&rhs_cst).expect( + "error during comparison unwrapping in term simplification (same op)", + ), + ) + } - (Op::Eql, Op::Ge) | - (Op::Eql, Op::Gt) => match lhs_cst.get().compare(& rhs_cst) { - Some(Less) => return SimplRes::Yields( term::fls() ), - Some(Equal) if rhs_op == Op::Gt => if conj { - return SimplRes::Yields( term::fls() ) - }, - Some(Equal) | - Some(Greater) => return SimplRes::Cmp(Greater), - None => unreachable!(), - }, + (Op::Gt, Op::Ge) | (Op::Ge, Op::Gt) => { + if lhs_cst == rhs_cst { + if lhs_op == Op::Gt { + return SimplRes::Cmp(Greater); + } else { + return SimplRes::Cmp(Less); + } + } else { + return SimplRes::Cmp(lhs_cst.get().compare(&rhs_cst).expect( + "error during comparison unwrapping in term simplification (diff op)", + )); + } + } - (Op::Ge, Op::Eql) | - (Op::Gt, Op::Eql) => match rhs_cst.get().compare(& lhs_cst) { - Some(Less) => return SimplRes::Yields( term::fls() ), - Some(Equal) if lhs_op == Op::Gt => return SimplRes::Yields( - term::fls() - ), - Some(Equal) | - Some(Greater) => return SimplRes::Cmp(Less), - None => unreachable!(), - }, + (Op::Eql, Op::Ge) | (Op::Eql, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { + Some(Less) => return SimplRes::Yields(term::fls()), + Some(Equal) if rhs_op == Op::Gt => if conj { + return SimplRes::Yields(term::fls()); + }, + Some(Equal) | Some(Greater) => return SimplRes::Cmp(Greater), + None => unreachable!(), + }, - (Op::Eql, Op::Eql) => if rhs_cst.equal(& lhs_cst) { - return SimplRes::Cmp(Greater) - } else { - return SimplRes::Yields( term::fls() ) - }, + (Op::Ge, Op::Eql) | (Op::Gt, Op::Eql) => match rhs_cst.get().compare(&lhs_cst) { + Some(Less) => return SimplRes::Yields(term::fls()), + Some(Equal) if lhs_op == Op::Gt => return SimplRes::Yields(term::fls()), + Some(Equal) | Some(Greater) => return SimplRes::Cmp(Less), + None => unreachable!(), + }, - _ => (), - } + (Op::Eql, Op::Eql) => if rhs_cst.equal(&lhs_cst) { + return SimplRes::Cmp(Greater); + } else { + return SimplRes::Yields(term::fls()); + }, - } else if lhs_op == Op::Ge && rhs_op == Op::Ge - && lhs_cst == rhs_cst.minus().expect( - "error during rhs inversion in term simplification" - ) - && rhs_vars == & term::u_minus( lhs_vars.clone() ) { - - if conj { - return SimplRes::Yields( - term::eq( - lhs_vars.clone(), lhs_cst.to_term().expect( - "error during lhs cst to term conversion in term simplification" - ) - ) - ) - } else { - return SimplRes::Yields( - term::tru() - ) + _ => (), + } + } else if lhs_op == Op::Ge + && rhs_op == Op::Ge + && lhs_cst == rhs_cst + .minus() + .expect("error during rhs inversion in term simplification") + && rhs_vars == &term::u_minus(lhs_vars.clone()) + { + if conj { + return SimplRes::Yields(term::eq( + lhs_vars.clone(), + lhs_cst + .to_term() + .expect("error during lhs cst to term conversion in term simplification"), + )); + } else { + return SimplRes::Yields(term::tru()); + } } - } - SimplRes::None + SimplRes::None } - - - /// Fails if the number of arguments is wrong. macro_rules! arity { - ($op:expr => $args:expr, $len:expr) => ( - if $args.len() != $len { - panic!( - "illegal application of `{}` to {} arguments", $op, $args.len() - ) - } - ) ; + ($op:expr => $args:expr, $len:expr) => { + if $args.len() != $len { + panic!( + "illegal application of `{}` to {} arguments", + $op, + $args.len() + ) + } + }; } macro_rules! simpl_fun { ( $(fn $name:ident($args:pat) $body:expr);* $(;)* ) => ( @@ -639,7 +597,6 @@ macro_rules! simpl_fun { ) ; } - // Polymorphic operations. simpl_fun! { @@ -862,7 +819,6 @@ simpl_fun! { } } - // Boolean operations. simpl_fun! { @@ -871,7 +827,7 @@ simpl_fun! { args.dedup() ; let mut cnt = 0 ; - + while cnt < args.len() { if let Some(b) = args[cnt].bool() { if b { @@ -918,7 +874,7 @@ simpl_fun! { args.dedup() ; let mut cnt = 0 ; - + while cnt < args.len() { if let Some(b) = args[cnt].bool() { @@ -1042,7 +998,6 @@ simpl_fun! { } } - // Relation operators over arithmetic terms. simpl_fun! { @@ -1166,7 +1121,6 @@ simpl_fun! { } ; } - // Arithmetic operations. simpl_fun! { @@ -1668,7 +1622,6 @@ simpl_fun! { } - // Casting operations. simpl_fun! { @@ -1704,7 +1657,6 @@ simpl_fun! { } - // Array operations. simpl_fun! { @@ -1752,187 +1704,173 @@ simpl_fun! { } - - /// Tries to create a constant datatype constructor. -fn cst_dtyp_new( - typ: Typ, name: String, args: Vec -) -> Either)> { - if args.is_empty() { - return Either::Left( - val::dtyp_new( typ, name, vec![] ) - ) - } +fn cst_dtyp_new(typ: Typ, name: String, args: Vec) -> Either)> { + if args.is_empty() { + return Either::Left(val::dtyp_new(typ, name, vec![])); + } - let mut nu_args = None ; + let mut nu_args = None; - for arg in & args { - if let Some(val) = arg.val() { - nu_args.get_or_insert_with( - || Vec::with_capacity( args.len() ) - ).push(val) - } else { - nu_args = None ; - break + for arg in &args { + if let Some(val) = arg.val() { + nu_args + .get_or_insert_with(|| Vec::with_capacity(args.len())) + .push(val) + } else { + nu_args = None; + break; + } } - } - if let Some(args) = nu_args { - Either::Left( val::dtyp_new(typ, name, args) ) - } else { - Either::Right( (typ, name, args) ) - } + if let Some(args) = nu_args { + Either::Left(val::dtyp_new(typ, name, args)) + } else { + Either::Right((typ, name, args)) + } } - /// Simplifies a datatype constructor. pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> RTerm { - let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { - Either::Left(val) => { - return RTerm::Cst(val) - }, - Either::Right(stuff) => stuff, - } ; - - if let Some((dtyp, prms)) = typ.dtyp_inspect() { - if let Some(fargs) = dtyp.news.get(& name) { - if args.len() != fargs.len() { - panic!( - "constructor `{}` for `{}` expects {} arguments, found {}", - conf.emph(& name), conf.emph(& dtyp.name), - fargs.len(), args.len() - ) - } + let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { + Either::Left(val) => return RTerm::Cst(val), + Either::Right(stuff) => stuff, + }; + + if let Some((dtyp, prms)) = typ.dtyp_inspect() { + if let Some(fargs) = dtyp.news.get(&name) { + if args.len() != fargs.len() { + panic!( + "constructor `{}` for `{}` expects {} arguments, found {}", + conf.emph(&name), + conf.emph(&dtyp.name), + fargs.len(), + args.len() + ) + } - for (arg, param) in args.iter_mut().zip( fargs.iter() ) { - let typ = param.1.to_type(prms).unwrap_or_else( - |_| panic!("ill-formed datatype constructor: {}", typ) - ) ; - if let Some(typ) = typ.merge( & arg.typ() ) { - if let Some(nu_arg) = arg.force_dtyp(typ) { - * arg = nu_arg - } + for (arg, param) in args.iter_mut().zip(fargs.iter()) { + let typ = param + .1 + .to_type(prms) + .unwrap_or_else(|_| panic!("ill-formed datatype constructor: {}", typ)); + if let Some(typ) = typ.merge(&arg.typ()) { + if let Some(nu_arg) = arg.force_dtyp(typ) { + *arg = nu_arg + } + } + } + } else { + panic!( + "datatype `{}` has no constructor named `{}`", + conf.emph(&dtyp.name), + conf.bad(&name) + ) } - } } else { - panic!( - "datatype `{}` has no constructor named `{}`", - conf.emph(& dtyp.name), conf.bad(& name) - ) + panic!("ill-typed datatype constructor: {}", typ) } - } else { - panic!("ill-typed datatype constructor: {}", typ) - } - let mut vals = if args.is_empty() { - Some(vec![]) - } else { - None - } ; + let mut vals = if args.is_empty() { Some(vec![]) } else { None }; - for arg in & args { - if let Some(val) = arg.val() { - vals.get_or_insert_with( - || Vec::with_capacity( args.len() ) - ).push(val) - } else { - vals = None ; - break + for arg in &args { + if let Some(val) = arg.val() { + vals.get_or_insert_with(|| Vec::with_capacity(args.len())) + .push(val) + } else { + vals = None; + break; + } } - } - if let Some(vals) = vals { - debug_assert_eq! { vals.len(), args.len() } - RTerm::Cst( val::dtyp_new(typ, name, vals) ) - } else { - debug_assert!( ! args.is_empty() ) ; - RTerm::DTypNew { typ, name, args } - } + if let Some(vals) = vals { + debug_assert_eq! { vals.len(), args.len() } + RTerm::Cst(val::dtyp_new(typ, name, vals)) + } else { + debug_assert!(!args.is_empty()); + RTerm::DTypNew { typ, name, args } + } } - - /// Simplifies a datatype selector. pub fn dtyp_slc(typ: Typ, field: String, term: Term) -> Either { - if let Some(val) = term.val() { - if let Some(res) = val.dtyp_slc(& field) { - return Either::Left( RTerm::Cst(res) ) + if let Some(val) = term.val() { + if let Some(res) = val.dtyp_slc(&field) { + return Either::Left(RTerm::Cst(res)); + } } - } - if let Some((typ, constructor, args)) = term.dtyp_new_inspect() { - if let Some((dtyp, _)) = typ.dtyp_inspect() { - if let Some(params) = dtyp.news.get(constructor) { - debug_assert_eq! { args.len(), params.len() } - for ((fld, _), arg) in params.iter().zip( args.iter() ) { - if fld == & field { - return Either::Right( arg.clone() ) - } + if let Some((typ, constructor, args)) = term.dtyp_new_inspect() { + if let Some((dtyp, _)) = typ.dtyp_inspect() { + if let Some(params) = dtyp.news.get(constructor) { + debug_assert_eq! { args.len(), params.len() } + for ((fld, _), arg) in params.iter().zip(args.iter()) { + if fld == &field { + return Either::Right(arg.clone()); + } + } + } else { + panic!("inconsistent internal datatype term") + } + } else { + panic!("inconsistent application of datatype selector") } - } else { - panic!("inconsistent internal datatype term") - } - } else { - panic!("inconsistent application of datatype selector") } - } - Either::Left( - RTerm::DTypSlc { typ, name: field, term } - ) + Either::Left(RTerm::DTypSlc { + typ, + name: field, + term, + }) } - /// Simplifies a datatype tester. /// /// The boolean flag returned indicates the polarity of the result. That is, if /// it is `false` then the term should be negated. pub fn dtyp_tst(constructor: String, term: Term) -> (RTerm, bool) { - if let Some(val) = term.val() { - if let val::RVal::DTypNew { name, .. } = val.get() { - return ( - RTerm::Cst( val::bool(& constructor == name) ), - true - ) - } else { - panic!("illegal datatype tester (ill-typed)") + if let Some(val) = term.val() { + if let val::RVal::DTypNew { name, .. } = val.get() { + return (RTerm::Cst(val::bool(&constructor == name)), true); + } else { + panic!("illegal datatype tester (ill-typed)") + } + } else if let Some((_, name, _)) = term.dtyp_new_inspect() { + return (RTerm::Cst(val::bool(constructor == name)), true); } - } else if let Some((_, name, _)) = term.dtyp_new_inspect() { - return ( - RTerm::Cst( val::bool(constructor == name) ), - true - ) - } - // The following tries to find a constructor that's more complex than the - // current one. The reason is that so far, it seems to work better that way. - if let Some(dtyp) = dtyp::of_constructor(& constructor) { - if let Some(args) = dtyp.news.get(& constructor) { - - if args.is_empty() { - if let Some(constructor) = dtyp.rec_constructor() { - return ( - RTerm::DTypTst { - typ: typ::bool(), name: constructor.into(), term - }, - false - ) + // The following tries to find a constructor that's more complex than the + // current one. The reason is that so far, it seems to work better that way. + if let Some(dtyp) = dtyp::of_constructor(&constructor) { + if let Some(args) = dtyp.news.get(&constructor) { + if args.is_empty() { + if let Some(constructor) = dtyp.rec_constructor() { + return ( + RTerm::DTypTst { + typ: typ::bool(), + name: constructor.into(), + term, + }, + false, + ); + } + } + } else { + panic!("inconsistent maps for datatypes") } - } - } else { - panic!("inconsistent maps for datatypes") + panic!( + "trying to construct a tester for unknown constructor {}", + constructor + ) } - } else { - panic!( - "trying to construct a tester for unknown constructor {}", constructor - ) - } - ( - RTerm::DTypTst { - typ: typ::bool(), name: constructor, term - }, - true - ) + ( + RTerm::DTypTst { + typ: typ::bool(), + name: constructor, + term, + }, + true, + ) } diff --git a/src/term/test.rs b/src/term/test.rs index 3e4c606e..18245e52 100644 --- a/src/term/test.rs +++ b/src/term/test.rs @@ -1,417 +1,413 @@ //! Tests for the term structure. -use common::* ; -use term::Op ; -use term::int ; +use common::*; +use term::int; +use term::Op; #[test] fn cst_add() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let sum = term::app( Op::Add, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let sum = term::app(Op::Add, vec![c_1, c_2]); + let model = model!(); + assert_eval!( int model => sum, 10 ) } #[test] fn cst_sub_1() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let sub = term::app( Op::Sub, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let sub = term::app(Op::Sub, vec![c_1, c_2]); + let model = model!(); + assert_eval!( int model => sub, 4 ) } #[test] fn cst_sub_2() { - let c_1 = int(7) ; - let sub = term::app( Op::Sub, vec![ c_1 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let sub = term::app(Op::Sub, vec![c_1]); + let model = model!(); + assert_eval!( int model => sub, (-7) ) } #[test] fn cst_mul() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let mul = term::app( Op::Mul, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let mul = term::app(Op::Mul, vec![c_1, c_2]); + let model = model!(); + assert_eval!( int model => mul, 21 ) } #[test] fn cst_div() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let div = term::app( Op::IDiv, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let div = term::app(Op::IDiv, vec![c_1, c_2]); + let model = model!(); + assert_eval!( int model => div, 2 ) } #[test] fn cst_mod() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let m0d = term::app( Op::Mod, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let m0d = term::app(Op::Mod, vec![c_1, c_2]); + let model = model!(); + assert_eval!( int model => m0d, 1 ) } #[test] fn cst_gt_1() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let gt = term::app( Op::Gt, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let gt = term::app(Op::Gt, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => gt ) } #[test] fn cst_gt_2() { - let c_1 = int(7) ; - let c_2 = int(7) ; - let gt = term::app( Op::Gt, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(7); + let gt = term::app(Op::Gt, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => gt ) } #[test] fn cst_ge_1() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let ge = term::app( Op::Ge, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let ge = term::app(Op::Ge, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => ge ) } #[test] fn cst_ge_2() { - let c_1 = int(7) ; - let c_2 = int(7) ; - let ge = term::app( Op::Ge, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(7); + let ge = term::app(Op::Ge, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => ge ) } #[test] fn cst_le_1() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let le = term::app( Op::Le, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let le = term::app(Op::Le, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => le ) } #[test] fn cst_le_2() { - let c_1 = int(7) ; - let c_2 = int(7) ; - let le = term::app( Op::Le, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(7); + let le = term::app(Op::Le, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => le ) } #[test] fn cst_lt_1() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let lt = term::app( Op::Lt, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let lt = term::app(Op::Lt, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => lt ) } #[test] fn cst_lt_2() { - let c_1 = int(7) ; - let c_2 = int(7) ; - let lt = term::app( Op::Lt, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(7); + let lt = term::app(Op::Lt, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => lt ) } #[test] fn cst_eq_1() { - let c_1 = int(7) ; - let c_2 = int(7) ; - let eq = term::app( Op::Eql, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(7); + let eq = term::app(Op::Eql, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => eq ) } #[test] fn cst_eq_2() { - let c_1 = int(7) ; - let c_2 = int(3) ; - let eq = term::app( Op::Eql, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = int(7); + let c_2 = int(3); + let eq = term::app(Op::Eql, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => eq ) } #[test] fn cst_eq_3() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let eq = term::app( Op::Eql, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let eq = term::app(Op::Eql, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => eq ) } #[test] fn cst_eq_4() { - let c_1 = term::fls() ; - let c_2 = term::tru() ; - let eq = term::app( Op::Eql, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::tru(); + let eq = term::app(Op::Eql, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => eq ) } #[test] fn cst_impl_1() { - let c_1 = term::fls() ; - let c_2 = term::fls() ; - let imp = term::app( Op::Impl, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::fls(); + let imp = term::app(Op::Impl, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => imp ) } #[test] fn cst_impl_2() { - let c_1 = term::tru() ; - let c_2 = term::fls() ; - let imp = term::app( Op::Impl, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::fls(); + let imp = term::app(Op::Impl, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool not model => imp ) } #[test] fn cst_impl_3() { - let c_1 = term::fls() ; - let c_2 = term::tru() ; - let imp = term::app( Op::Impl, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::tru(); + let imp = term::app(Op::Impl, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => imp ) } #[test] fn cst_impl_4() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let imp = term::app( Op::Impl, vec![ c_1, c_2 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let imp = term::app(Op::Impl, vec![c_1, c_2]); + let model = model!(); + assert_eval!( bool model => imp ) } #[test] fn cst_not_1() { - let c_1 = term::fls() ; - let not = term::app( Op::Not, vec![ c_1 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let not = term::app(Op::Not, vec![c_1]); + let model = model!(); + assert_eval!( bool model => not ) } #[test] fn cst_not_2() { - let c_1 = term::tru() ; - let not = term::app( Op::Not, vec![ c_1 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let not = term::app(Op::Not, vec![c_1]); + let model = model!(); + assert_eval!( bool not model => not ) } #[test] fn cst_and_1() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let c_3 = term::tru() ; - let c_4 = term::tru() ; - let and = term::app( Op::And, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let c_3 = term::tru(); + let c_4 = term::tru(); + let and = term::app(Op::And, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool model => and ) } #[test] fn cst_and_2() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let c_3 = term::fls() ; - let c_4 = term::tru() ; - let and = term::app( Op::And, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let c_3 = term::fls(); + let c_4 = term::tru(); + let and = term::app(Op::And, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool not model => and ) } #[test] fn cst_and_3() { - let c_1 = term::fls() ; - let c_2 = term::tru() ; - let c_3 = term::tru() ; - let c_4 = term::tru() ; - let and = term::app( Op::And, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::tru(); + let c_3 = term::tru(); + let c_4 = term::tru(); + let and = term::app(Op::And, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool not model => and ) } #[test] fn cst_and_4() { - let c_1 = term::tru() ; - let c_2 = term::fls() ; - let c_3 = term::fls() ; - let c_4 = term::tru() ; - let and = term::app( Op::And, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::fls(); + let c_3 = term::fls(); + let c_4 = term::tru(); + let and = term::app(Op::And, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool not model => and ) } #[test] fn cst_or_1() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let c_3 = term::tru() ; - let c_4 = term::tru() ; - let or = term::app( Op::Or, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let c_3 = term::tru(); + let c_4 = term::tru(); + let or = term::app(Op::Or, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool model => or ) } #[test] fn cst_or_2() { - let c_1 = term::tru() ; - let c_2 = term::tru() ; - let c_3 = term::fls() ; - let c_4 = term::tru() ; - let or = term::app( Op::Or, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::tru(); + let c_3 = term::fls(); + let c_4 = term::tru(); + let or = term::app(Op::Or, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool model => or ) } #[test] fn cst_or_3() { - let c_1 = term::fls() ; - let c_2 = term::tru() ; - let c_3 = term::tru() ; - let c_4 = term::tru() ; - let or = term::app( Op::Or, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::tru(); + let c_3 = term::tru(); + let c_4 = term::tru(); + let or = term::app(Op::Or, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool model => or ) } #[test] fn cst_or_4() { - let c_1 = term::tru() ; - let c_2 = term::fls() ; - let c_3 = term::fls() ; - let c_4 = term::tru() ; - let or = term::app( Op::Or, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::tru(); + let c_2 = term::fls(); + let c_3 = term::fls(); + let c_4 = term::tru(); + let or = term::app(Op::Or, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool model => or ) } #[test] fn cst_or_5() { - let c_1 = term::fls() ; - let c_2 = term::fls() ; - let c_3 = term::fls() ; - let c_4 = term::fls() ; - let or = term::app( Op::Or, vec![ c_1, c_2, c_3, c_4 ] ) ; - let model = model!() ; - assert_eval!( + let c_1 = term::fls(); + let c_2 = term::fls(); + let c_3 = term::fls(); + let c_4 = term::fls(); + let or = term::app(Op::Or, vec![c_1, c_2, c_3, c_4]); + let model = model!(); + assert_eval!( bool not model => or ) } #[test] fn add_real_1() { - let v_1 = term::var( 0, typ::real() ) ; - let add = term::add( vec![ term::real_of_float(-2.0), v_1.clone() ] ) ; - let model = model!( val::real( rat_of_float(1.0) ) ) ; - assert_eval!( real model => add, - 1.0 ) ; + let v_1 = term::var(0, typ::real()); + let add = term::add(vec![term::real_of_float(-2.0), v_1.clone()]); + let model = model!(val::real(rat_of_float(1.0))); + assert_eval!( real model => add, - 1.0 ); } #[test] fn ite_1() { - let v_1 = term::var( 0, typ::real() ) ; - let ite = term::ite( - term::eq( - term::add( vec![ term::real_of_float(-2.0), v_1.clone() ] ), - term::real_of_float(0.0) - ), - term::real_of_float(1.0), - term::add( - vec![ term::real_of_float(1.0), v_1.clone() ] - ) - ) ; - let model = model!( val::real( rat_of_float(1.0) ) ) ; - println!("ite_1") ; - println!("model:") ; - model.print() ; - assert_eval!( real model => ite, 2.0 ) ; + let v_1 = term::var(0, typ::real()); + let ite = term::ite( + term::eq( + term::add(vec![term::real_of_float(-2.0), v_1.clone()]), + term::real_of_float(0.0), + ), + term::real_of_float(1.0), + term::add(vec![term::real_of_float(1.0), v_1.clone()]), + ); + let model = model!(val::real(rat_of_float(1.0))); + println!("ite_1"); + println!("model:"); + model.print(); + assert_eval!( real model => ite, 2.0 ); } - - // The lazy evaluation tests rely on the order in which the terms are created. // This is not the case outside of these tests obviously. But here the goal is // to have the last term being illegal, usually a variable that's not defined @@ -423,83 +419,72 @@ fn ite_1() { // different index each time, ideally a high one to avoid clashes with other // tests. - - - #[test] fn lazy_1() { - let v_0 = term::real_var(0) ; - let t_1 = term::ge( v_0, term::real( rat_of_float(7.0) ) ) ; - let v_1 = term::real_var(1000) ; - let t_2 = term::ge( v_1, term::real( rat_of_float(0.0) ) ) ; + let v_0 = term::real_var(0); + let t_1 = term::ge(v_0, term::real(rat_of_float(7.0))); + let v_1 = term::real_var(1000); + let t_2 = term::ge(v_1, term::real(rat_of_float(0.0))); - let conj = term::and( vec![ t_1, t_2 ] ) ; + let conj = term::and(vec![t_1, t_2]); - let model = model!( val::real( rat_of_float(1.0) ) ) ; + let model = model!(val::real(rat_of_float(1.0))); - // This evaluation should not work: `v_1` is not defined in the model, but - // because evaluation is lazy and `t_1` is false with this model, it goes - // through and the conjunction evaluates to false. + // This evaluation should not work: `v_1` is not defined in the model, but + // because evaluation is lazy and `t_1` is false with this model, it goes + // through and the conjunction evaluates to false. - assert_eval!( bool not model => conj ) + assert_eval!( bool not model => conj ) } - #[test] fn lazy_2() { - let v_0 = term::real_var(0) ; - let t_1 = term::ge( v_0, term::real( rat_of_float(0.0) ) ) ; - let v_1 = term::real_var(1001) ; - let t_2 = term::ge( v_1, term::real( rat_of_float(0.0) ) ) ; + let v_0 = term::real_var(0); + let t_1 = term::ge(v_0, term::real(rat_of_float(0.0))); + let v_1 = term::real_var(1001); + let t_2 = term::ge(v_1, term::real(rat_of_float(0.0))); - let disj = term::or( vec![ t_1, t_2 ] ) ; + let disj = term::or(vec![t_1, t_2]); - let model = model!( val::real( rat_of_float(1.0) ) ) ; + let model = model!(val::real(rat_of_float(1.0))); - // This evaluation should not work: `v_1` is not defined in the model, but - // because evaluation is lazy and `t_1` is false with this model, it goes - // through and the conjunction evaluates to false. + // This evaluation should not work: `v_1` is not defined in the model, but + // because evaluation is lazy and `t_1` is false with this model, it goes + // through and the conjunction evaluates to false. - assert_eval!( bool model => disj ) + assert_eval!( bool model => disj ) } - #[test] fn lazy_3() { - let v_0 = term::real_var(0) ; - let t_1 = term::ge( - v_0.clone(), term::real( rat_of_float(7.0) ) - ) ; - let v_1 = term::real_var(1002) ; - let ite = term::ite(t_1, v_1, v_0) ; + let v_0 = term::real_var(0); + let t_1 = term::ge(v_0.clone(), term::real(rat_of_float(7.0))); + let v_1 = term::real_var(1002); + let ite = term::ite(t_1, v_1, v_0); - let model = model!( val::real( rat_of_float(1.0) ) ) ; + let model = model!(val::real(rat_of_float(1.0))); - assert_eval!( real model => ite, 1.0 ) + assert_eval!( real model => ite, 1.0 ) } - #[test] fn lazy_4() { - let v_0 = term::real_var(0) ; - let t_1 = term::u_minus( v_0.clone() ) ; - let v_1 = term::real_var(1003) ; - let distinct = term::distinct( vec![t_1, v_0, v_1] ) ; + let v_0 = term::real_var(0); + let t_1 = term::u_minus(v_0.clone()); + let v_1 = term::real_var(1003); + let distinct = term::distinct(vec![t_1, v_0, v_1]); - let model = model!( val::real( rat_of_float(0.0) ) ) ; + let model = model!(val::real(rat_of_float(0.0))); - assert_eval!( bool not model => distinct ) + assert_eval!( bool not model => distinct ) } - - // #[test] // fn models() { // let v_1 = term::bool_var(0) ; // let v_2 = term::int_var(1) ; // let v_3 = term::int_var(2) ; - // let model_1 = model!( true, 2, 3 ) ; // let model_2 = model!( true, 7, 0 ) ; @@ -532,8 +517,6 @@ fn lazy_4() { // () // } - - // #[test] // fn partial_eval() { // let v_1 = term::bool_var(0) ; @@ -609,12 +592,8 @@ fn lazy_4() { // assert_eval!{ int model => term, 0 } // } - - - // use term::{ tru, fls } ; - // macro_rules! eq { // ($(> $lhs:expr, $rhs:expr);* $(;)*) => ({ // $({ @@ -842,7 +821,6 @@ fn lazy_4() { // // } // } - // macro_rules! parser { // ( // vars { @@ -891,7 +869,6 @@ fn lazy_4() { // // ($($tt:tt)*) => ($($tt)*) ; // } - // #[test] // fn bug_find() { // use term::Typ ; @@ -995,8 +972,3 @@ fn lazy_4() { // } // } // } - - - - - diff --git a/src/term/tterms.rs b/src/term/tterms.rs index cb1ee11a..3d0c0cc9 100644 --- a/src/term/tterms.rs +++ b/src/term/tterms.rs @@ -1,196 +1,210 @@ //! Top-term-related types. -use common::* ; -use var_to::terms::VarTermsSet ; +use common::*; +use var_to::terms::VarTermsSet; -use term::* ; +use term::*; /// Top term, as they appear in clauses. #[derive(Clone, PartialEq, Eq, Hash)] pub enum TTerm { - /// A predicate application. - P { - /// Predicate applied. - pred: PrdIdx, - /// The arguments. - args: VarTerms, - }, - /// Just a term. - T(Term), + /// A predicate application. + P { + /// Predicate applied. + pred: PrdIdx, + /// The arguments. + args: VarTerms, + }, + /// Just a term. + T(Term), } impl TTerm { - /// The false top term. - pub fn fls() -> Self { - TTerm::T( term::fls() ) - } - /// The true top term. - pub fn tru() -> Self { - TTerm::T( term::tru() ) - } + /// The false top term. + pub fn fls() -> Self { + TTerm::T(term::fls()) + } + /// The true top term. + pub fn tru() -> Self { + TTerm::T(term::tru()) + } - /// Type of the top term. - /// - /// Should always be bool, except during parsing. - pub fn typ(& self) -> Typ { - match * self { - TTerm::P { .. } => typ::bool(), - TTerm::T(ref term) => term.typ(), + /// Type of the top term. + /// + /// Should always be bool, except during parsing. + pub fn typ(&self) -> Typ { + match *self { + TTerm::P { .. } => typ::bool(), + TTerm::T(ref term) => term.typ(), + } } - } - /// True if the top term is a term with no variables and evaluates to true. - pub fn is_true(& self) -> bool { - self.bool() == Some(true) - } - /// True if the top term is a term with no variables and evaluates to false. - pub fn is_false(& self) -> bool { - self.bool() == Some(false) - } - /// Boolean corresponding to the top term if it's a bool constant. - pub fn bool(& self) -> Option { - if let TTerm::T(t) = self { - t.bool() - } else { None } - } - /// Boolean corresponding to the top term if it's an integer constant. - pub fn int(& self) -> Option { - if let TTerm::T(t) = self { - t.int() - } else { None } - } - /// The operator and the kids of a top term, if it's an operator application. - pub fn app_inspect(& self) -> Option< (Op, & Vec) > { - if let TTerm::T(t) = self { - t.app_inspect() - } else { None } - } - /// If the top term is simply a term, returns that term. - #[inline] - pub fn term(& self) -> Option<& Term> { - if let TTerm::T(ref t) = * self { - Some(t) - } else { None } - } + /// True if the top term is a term with no variables and evaluates to true. + pub fn is_true(&self) -> bool { + self.bool() == Some(true) + } + /// True if the top term is a term with no variables and evaluates to false. + pub fn is_false(&self) -> bool { + self.bool() == Some(false) + } + /// Boolean corresponding to the top term if it's a bool constant. + pub fn bool(&self) -> Option { + if let TTerm::T(t) = self { + t.bool() + } else { + None + } + } + /// Boolean corresponding to the top term if it's an integer constant. + pub fn int(&self) -> Option { + if let TTerm::T(t) = self { + t.int() + } else { + None + } + } + /// The operator and the kids of a top term, if it's an operator application. + pub fn app_inspect(&self) -> Option<(Op, &Vec)> { + if let TTerm::T(t) = self { + t.app_inspect() + } else { + None + } + } + /// If the top term is simply a term, returns that term. + #[inline] + pub fn term(&self) -> Option<&Term> { + if let TTerm::T(ref t) = *self { + Some(t) + } else { + None + } + } - /// The predicate a top term is an application of, if any. - pub fn pred(& self) -> Option { - if let TTerm::P { pred, .. } = * self { - Some(pred) - } else { None } - } + /// The predicate a top term is an application of, if any. + pub fn pred(&self) -> Option { + if let TTerm::P { pred, .. } = *self { + Some(pred) + } else { + None + } + } - /// The arguments of a top term if it's a predicate application. - pub fn args(& self) -> Option<& VarTerms> { - if let TTerm::P { ref args, .. } = * self { - Some(args) - } else { None } - } + /// The arguments of a top term if it's a predicate application. + pub fn args(&self) -> Option<&VarTerms> { + if let TTerm::P { ref args, .. } = *self { + Some(args) + } else { + None + } + } - /// Applies some treatment if the top term is a predicate application. - pub fn pred_app_fold(& mut self, init: T, f: F) -> T - where F: Fn(T, PrdIdx, & mut VarTerms) -> T { - if let TTerm::P { pred, ref mut args } = * self { - f(init, pred, args) - } else { - init + /// Applies some treatment if the top term is a predicate application. + pub fn pred_app_fold(&mut self, init: T, f: F) -> T + where + F: Fn(T, PrdIdx, &mut VarTerms) -> T, + { + if let TTerm::P { pred, ref mut args } = *self { + f(init, pred, args) + } else { + init + } } - } - /// Variables appearing in a top term. - pub fn vars(& self) -> VarSet { - match * self { - TTerm::P { ref args, .. } => { - let mut vars = VarSet::with_capacity(17) ; - for term in args.iter() { - vars.extend( term::vars(term) ) + /// Variables appearing in a top term. + pub fn vars(&self) -> VarSet { + match *self { + TTerm::P { ref args, .. } => { + let mut vars = VarSet::with_capacity(17); + for term in args.iter() { + vars.extend(term::vars(term)) + } + vars + } + TTerm::T(ref term) => term::vars(term), } - vars - }, - TTerm::T(ref term) => term::vars(term), } - } - /// In-place variable substitution for top terms. - /// - /// Used for substitutions in the same clause / predicate scope. - pub fn subst>( - & mut self, map: & Map - ) -> bool { - match * self { - TTerm::T(ref mut term) => { - let (t, b) = term.subst(map) ; - * term = t ; - b - }, - TTerm::P { ref mut args, .. } => { - let mut changed = false ; - let mut nu_args = VarMap::with_capacity( args.len() ) ; - for arg in args.iter() { - let (t, b) = arg.subst(map) ; - nu_args.push(t) ; - changed = changed || b + /// In-place variable substitution for top terms. + /// + /// Used for substitutions in the same clause / predicate scope. + pub fn subst>(&mut self, map: &Map) -> bool { + match *self { + TTerm::T(ref mut term) => { + let (t, b) = term.subst(map); + *term = t; + b + } + TTerm::P { ref mut args, .. } => { + let mut changed = false; + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let (t, b) = arg.subst(map); + nu_args.push(t); + changed = changed || b + } + *args = nu_args.into(); + changed + } } - * args = nu_args.into() ; - changed - }, } - } - /// Total variable substitution for top terms. - /// - /// Used for substitutions in different clause / predicate scope. - pub fn subst_total>( - & self, map: & Map - ) -> Res { - match * self { - TTerm::P { pred, ref args } => { - let mut new_args = VarMap::with_capacity( args.len() ) ; - for term in args.iter() { - if let Some((term, _)) = term.subst_total(map) { - new_args.push(term) - } else { - bail!("total substitution failed (predicate)") - } + /// Total variable substitution for top terms. + /// + /// Used for substitutions in different clause / predicate scope. + pub fn subst_total>(&self, map: &Map) -> Res { + match *self { + TTerm::P { pred, ref args } => { + let mut new_args = VarMap::with_capacity(args.len()); + for term in args.iter() { + if let Some((term, _)) = term.subst_total(map) { + new_args.push(term) + } else { + bail!("total substitution failed (predicate)") + } + } + Ok(TTerm::P { + pred, + args: new_args.into(), + }) + } + TTerm::T(ref term) => if let Some((term, _)) = term.subst_total(map) { + Ok(TTerm::T(term)) + } else { + bail!("total substitution failed (term)") + }, } - Ok( TTerm::P { pred, args: new_args.into() } ) - }, - TTerm::T(ref term) => if let Some((term, _)) = term.subst_total(map) { - Ok( TTerm::T(term) ) - } else { - bail!("total substitution failed (term)") - }, } - } - /// Writes a top term using special functions for writing predicates and - /// variables. - pub fn write( - & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - use self::TTerm::* ; - match * self { - P { pred, ref args } => write_prd(w, pred, args), - T(ref t) => t.write(w, write_var), + /// Writes a top term using special functions for writing predicates and + /// variables. + pub fn write( + &self, + w: &mut W, + write_var: WriteVar, + write_prd: WritePrd, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + use self::TTerm::*; + match *self { + P { pred, ref args } => write_prd(w, pred, args), + T(ref t) => t.write(w, write_var), + } } - } - /// Writes a top term smt2 style using a special function for writing - /// predicates. - pub fn write_smt2( - & self, w: & mut W, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - self.write( - w, |w, var| var.default_write(w), write_prd - ) - } + /// Writes a top term smt2 style using a special function for writing + /// predicates. + pub fn write_smt2(&self, w: &mut W, write_prd: WritePrd) -> IoRes<()> + where + W: Write, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + self.write(w, |w, var| var.default_write(w), write_prd) + } } impl_fmt!{ TTerm(self, fmt) { @@ -207,1149 +221,1223 @@ impl_fmt!{ } } - - - - - - /// A *set* of top terms. /// /// Actually contains a set of `Term`s and a map from predicates to their /// arguments. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct TTermSet { - /// Set of terms. - terms: TermSet, - /// Predicate applications. - preds: PrdHMap< VarTermsSet >, + /// Set of terms. + terms: TermSet, + /// Predicate applications. + preds: PrdHMap, } impl TTermSet { - /// Creates a new top term set with some capacity - #[inline] - pub fn with_capacity(capa: usize) -> Self { - TTermSet { - terms: TermSet::with_capacity(capa), - preds: PrdHMap::with_capacity(capa), + /// Creates a new top term set with some capacity + #[inline] + pub fn with_capacity(capa: usize) -> Self { + TTermSet { + terms: TermSet::with_capacity(capa), + preds: PrdHMap::with_capacity(capa), + } } - } - /// Creates a new top term set with some capacities - #[inline] - pub fn with_capacities(term_capa: usize, pred_capa: usize) -> Self { - TTermSet { - terms: TermSet::with_capacity(term_capa), - preds: PrdHMap::with_capacity(pred_capa), + /// Creates a new top term set with some capacities + #[inline] + pub fn with_capacities(term_capa: usize, pred_capa: usize) -> Self { + TTermSet { + terms: TermSet::with_capacity(term_capa), + preds: PrdHMap::with_capacity(pred_capa), + } } - } - - /// Destroys the set. - #[inline] - pub fn destroy(self) -> (TermSet, PrdHMap) { - (self.terms, self.preds) - } - - /// Reserves some space. - #[inline] - pub fn reserve(& mut self, terms: usize, preds: usize) { - self.terms.reserve(terms) ; - self.preds.reserve(preds) - } - - /// Creates an empty top term set. - #[inline] - pub fn new() -> Self { Self::with_capacity(5) } - /// Creates a top term set from a set of terms. - #[inline] - pub fn of_terms(terms: TermSet, pred_capa: usize) -> Self { - TTermSet { - terms, preds: PrdHMap::with_capacity(pred_capa), + /// Destroys the set. + #[inline] + pub fn destroy(self) -> (TermSet, PrdHMap) { + (self.terms, self.preds) } - } - /// True iff the set is empty. - #[inline] - pub fn is_empty(& self) -> bool { - self.terms.is_empty() && self.preds.is_empty() - } - - /// Number of elements. - #[inline] - pub fn len(& self) -> usize { - let mut len = self.terms.len() ; - for (_, argss) in & self.preds { - len += argss.len() + /// Reserves some space. + #[inline] + pub fn reserve(&mut self, terms: usize, preds: usize) { + self.terms.reserve(terms); + self.preds.reserve(preds) } - len - } - /// Terms. - #[inline] - pub fn terms(& self) -> & TermSet { - & self.terms - } - /// Predicate applications. - #[inline] - pub fn preds(& self) -> & PrdHMap< VarTermsSet > { - & self.preds - } - - /// Terms (mutable version). - #[inline] - pub fn terms_mut(& mut self) -> & mut TermSet { - & mut self.terms - } - /// Predicate applications (mutable version). - #[inline] - pub fn preds_mut(& mut self) -> & mut PrdHMap< VarTermsSet > { - & mut self.preds - } - - /// True if `self` is a subset of `that`. - #[inline] - pub fn is_subset_of(& self, that: & Self) -> bool { - // `terms` subset? - if ! self.terms.is_subset(& that.terms) { return false } - // All predicates in `that` also appear in `self`? - for (pred, _) in & that.preds { - if ! self.preds.contains_key(pred) { - return false - } - } - // All applications in `self` also in `that`? - for (pred, self_set) in & self.preds { - if let Some(that_set) = that.preds.get(pred) { - if ! self_set.is_subset(that_set) { return false } - } else { - return false - } - } - true - } - - /// Variable substitution. - pub fn subst>(& self, map: & Map) -> Self { - let mut terms = TermSet::with_capacity(self.terms.len()) ; - for term in self.terms() { - let (term, _) = term.subst(map) ; - terms.insert(term) ; + /// Creates an empty top term set. + #[inline] + pub fn new() -> Self { + Self::with_capacity(5) } - let mut preds = PrdHMap::with_capacity( self.preds.len() ) ; - for (pred, argss) in self.preds.iter() { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss { - let args = var_to::terms::new( args.subst(map) ) ; - nu_argss.insert(args) ; - } - preds.insert(* pred, nu_argss) ; + /// Creates a top term set from a set of terms. + #[inline] + pub fn of_terms(terms: TermSet, pred_capa: usize) -> Self { + TTermSet { + terms, + preds: PrdHMap::with_capacity(pred_capa), + } } - TTermSet { terms, preds } - } - - /// Inserts a predicate application. - #[inline] - pub fn insert_pred_app(& mut self, pred: PrdIdx, args: VarTerms) -> bool { - self.preds.entry(pred).or_insert_with(VarTermsSet::new).insert(args) - } - /// Inserts some predicate applications. - pub fn insert_pred_apps( - & mut self, pred: PrdIdx, argss: TArgss - ) - where - Iter: Iterator + ExactSizeIterator, - TArgss: IntoIterator { - let argss = argss.into_iter() ; - if argss.len() == 0 { return () } - self.preds.entry(pred).or_insert_with(VarTermsSet::new).extend( argss ) - } - - /// Inserts a term. - #[inline] - pub fn insert_term(& mut self, term: Term) -> bool { - self.terms.insert(term) - } - /// Inserts some terms. - #[inline] - pub fn insert_terms(& mut self, terms: Terms) - where - Iter: Iterator + ExactSizeIterator, - Terms: IntoIterator { - let terms = terms.into_iter() ; - self.terms.reserve( terms.len() ) ; - for term in terms { self.terms.insert(term) ; () } - } - - /// Inserts a top term. - pub fn insert_tterm(& mut self, tterm: TTerm) -> bool { - match tterm { - TTerm::T(term) => self.insert_term(term), - TTerm::P { pred, args } => self.insert_pred_app(pred, args) + /// True iff the set is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.terms.is_empty() && self.preds.is_empty() } - } - /// Constructor from some top terms. - pub fn of_tterms(tterms: TTs) -> Self - where - Iter: Iterator + ExactSizeIterator, - TTs: IntoIterator { - let tterms = tterms.into_iter() ; - let mut slf = Self::with_capacity( tterms.len() ) ; - for tterm in tterms { - slf.insert_tterm(tterm) ; () - } - slf - } + /// Number of elements. + #[inline] + pub fn len(&self) -> usize { + let mut len = self.terms.len(); + for (_, argss) in &self.preds { + len += argss.len() + } + len + } - /// Constructor from a single term. - pub fn of_term(term: Term) -> Self { - let mut slf = Self::new() ; - slf.insert_term(term) ; - slf - } + /// Terms. + #[inline] + pub fn terms(&self) -> &TermSet { + &self.terms + } + /// Predicate applications. + #[inline] + pub fn preds(&self) -> &PrdHMap { + &self.preds + } - /// Puts the variables appearing in the top terms in some set. - pub fn vars(& self, set: & mut VarSet) { - for term in & self.terms { - set.extend( term::vars(term) ) + /// Terms (mutable version). + #[inline] + pub fn terms_mut(&mut self) -> &mut TermSet { + &mut self.terms } - for (_, argss) in & self.preds { - for args in argss { - for arg in args.iter() { - set.extend( term::vars(arg) ) - } - } + /// Predicate applications (mutable version). + #[inline] + pub fn preds_mut(&mut self) -> &mut PrdHMap { + &mut self.preds } - } - /// Removes some arguments from the predicate applications. - pub fn remove_vars(& mut self, to_keep: & PrdHMap) { - remove_vars_from_pred_apps( - & mut self.preds, to_keep - ) - } + /// True if `self` is a subset of `that`. + #[inline] + pub fn is_subset_of(&self, that: &Self) -> bool { + // `terms` subset? + if !self.terms.is_subset(&that.terms) { + return false; + } + // All predicates in `that` also appear in `self`? + for (pred, _) in &that.preds { + if !self.preds.contains_key(pred) { + return false; + } + } + // All applications in `self` also in `that`? + for (pred, self_set) in &self.preds { + if let Some(that_set) = that.preds.get(pred) { + if !self_set.is_subset(that_set) { + return false; + } + } else { + return false; + } + } + true + } + /// Variable substitution. + pub fn subst>(&self, map: &Map) -> Self { + let mut terms = TermSet::with_capacity(self.terms.len()); + for term in self.terms() { + let (term, _) = term.subst(map); + terms.insert(term); + } - /// Writes all top terms with some separator. - pub fn write( - & self, w: & mut W, sep: & str, write_var: WriteVar, write_pred: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - // Don't print the separator the first time. - let mut separate = false ; - macro_rules! write_sep { - () => ( - if separate { - write!(w, "{}", sep) ? - } else { - separate = true + let mut preds = PrdHMap::with_capacity(self.preds.len()); + for (pred, argss) in self.preds.iter() { + let mut nu_argss = VarTermsSet::with_capacity(argss.len()); + for args in argss { + let args = var_to::terms::new(args.subst(map)); + nu_argss.insert(args); + } + preds.insert(*pred, nu_argss); } - ) ; - } - for term in & self.terms { - write_sep!() ; - term.write(w, & write_var) ? + TTermSet { terms, preds } } - for (pred, argss) in & self.preds { - for args in argss { - write_sep!() ; - write_pred(w, * pred, args) ? - } + /// Inserts a predicate application. + #[inline] + pub fn insert_pred_app(&mut self, pred: PrdIdx, args: VarTerms) -> bool { + self.preds + .entry(pred) + .or_insert_with(VarTermsSet::new) + .insert(args) } - - Ok(()) - } -} -impl ::std::cmp::PartialOrd for TTermSet { - fn partial_cmp(& self, other: & Self) -> Option<::std::cmp::Ordering> { - use ::std::cmp::Ordering::* ; - let mut le = true ; - let mut ge = true ; - macro_rules! check_none { - () => ( if ! le && ! ge { return None } ) + /// Inserts some predicate applications. + pub fn insert_pred_apps(&mut self, pred: PrdIdx, argss: TArgss) + where + Iter: Iterator + ExactSizeIterator, + TArgss: IntoIterator, + { + let argss = argss.into_iter(); + if argss.len() == 0 { + return (); + } + self.preds + .entry(pred) + .or_insert_with(VarTermsSet::new) + .extend(argss) } - for term in & self.terms { - if ! other.terms.contains(term) { le = false ; break } + /// Inserts a term. + #[inline] + pub fn insert_term(&mut self, term: Term) -> bool { + self.terms.insert(term) } - for term in & other.terms { - if ! self.terms.contains(term) { ge = false ; break } + /// Inserts some terms. + #[inline] + pub fn insert_terms(&mut self, terms: Terms) + where + Iter: Iterator + ExactSizeIterator, + Terms: IntoIterator, + { + let terms = terms.into_iter(); + self.terms.reserve(terms.len()); + for term in terms { + self.terms.insert(term); + () + } } - check_none! {} - // Part of what happens in this loop is explained below. - for (pred, argss) in & self.preds { - check_none! {} - if let Some(ass) = other.preds.get(pred) { - if ! argss.is_subset(ass) { - le = false + /// Inserts a top term. + pub fn insert_tterm(&mut self, tterm: TTerm) -> bool { + match tterm { + TTerm::T(term) => self.insert_term(term), + TTerm::P { pred, args } => self.insert_pred_app(pred, args), } - if ! ass.is_subset(argss) { - ge = false - } - } else { - le = false - } - } - - // At this point we checked all predicate applications in `self`. We won't - // touch `le` anymore. - // - // The only way `ge` can change is if it is true and `other` has some - // predicates that don't appear in `self`. That's because in the previous - // loop, whenever a predicate from `self` also appears in `other`, we - // checked whether the other set of arguments is a subset of the self set - // of arguments and set `ge` to false if it's not the case. - - if ge { - for pred in other.preds.keys() { - if ! self.preds.contains_key(pred) { - ge = false ; - break - } - } } - match (le, ge) { - (true, true) => Some(Equal), - (true, false) => Some(Less), - (false, true) => Some(Greater), - (false, false) => None, + /// Constructor from some top terms. + pub fn of_tterms(tterms: TTs) -> Self + where + Iter: Iterator + ExactSizeIterator, + TTs: IntoIterator, + { + let tterms = tterms.into_iter(); + let mut slf = Self::with_capacity(tterms.len()); + for tterm in tterms { + slf.insert_tterm(tterm); + () + } + slf } - } -} -/// Removes some arguments from some predicate applications. -fn remove_vars_from_pred_apps( - apps: & mut PrdHMap< VarTermsSet >, to_keep: & PrdHMap -) { - for (pred, argss) in apps.iter_mut() { - let vars_to_keep = if let Some(vars) = to_keep.get(pred) { - vars - } else { - continue - } ; - let mut old_argss = VarTermsSet::with_capacity( argss.len() ) ; - ::std::mem::swap( & mut old_argss, argss ) ; - for args in old_argss { - argss.insert( args.remove( vars_to_keep ) ) ; + /// Constructor from a single term. + pub fn of_term(term: Term) -> Self { + let mut slf = Self::new(); + slf.insert_term(term); + slf } - } -} - - -/// A formula composed of top terms. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TTerms { - /// True. - True, - /// False. - False, - /// Conjunction. - Conj { - quant: Option, - tterms: TTermSet, - }, - /// Disjunction. - Disj { - quant: Option, - tterms: TTermSet, - neg_preds: PrdHMap< VarTermsSet >, - }, - /// Almost a DNF: a disjunction of conjunctions of `TTerm`s. - Dnf { - disj: Vec< (Option, TTermSet) > - }, -} -impl TTerms { - // pub fn inspect(& self) { - // match * self { - // TTerms::True => println!("true"), - // TTerms::False => println!("false"), - // TTerms::Conj { ref quant, ref tterms } => println!( - // "conj, {} ({})", tterms.len(), - // if let Some(q) = quant.as_ref() { - // format!("{}", q.len()) - // } else { "none".into() } - // ), - // TTerms::Disj { ref quant, ref tterms, ref neg_preds } => println!( - // "conj, {}, {}, {}", tterms.len(), neg_preds.len(), - // if let Some(q) = quant.as_ref() { - // format!("{}", q.len()) - // } else { "none".into() } - // ), - // TTerms::Dnf { ref disj } => println!( - // "dnf, {}", disj.len() - // ), - // } - // } - - /// True. - #[inline] - pub fn tru() -> Self { TTerms::True } - /// False. - #[inline] - pub fn fls() -> Self { TTerms::False } - /// Constructor from a boolean. - #[inline] - pub fn of_bool(b: bool) -> Self { - if b { Self::tru() } else { Self::fls() } - } - /// Attempts to transform some terms in a term. - pub fn to_term(& self) -> Option { - match * self { - TTerms::True => Some( term::tru() ), - TTerms::False => Some( term::fls() ), - - TTerms::Conj { ref quant, .. } if quant.is_some() => None, - TTerms::Conj { ref tterms, .. } if ! tterms.preds.is_empty() => None, - TTerms::Conj { ref tterms, .. } => Some( - term::and( - tterms.terms().iter().cloned().collect() - ) - ), - - TTerms::Disj { ref quant, .. } if quant.is_some() => None, - TTerms::Disj { - ref tterms, ref neg_preds, .. - } if ! tterms.preds.is_empty() || ! neg_preds.is_empty() => None, - TTerms::Disj { ref tterms, .. } => Some( - term::or( - tterms.terms().iter().cloned().collect() - ) - ), - - TTerms::Dnf { ref disj } => { - let mut disj_terms = Vec::with_capacity( disj.len() ) ; - for & (ref quant, ref conj) in disj { - if quant.is_some() - || ! conj.preds.is_empty() { return None } - disj_terms.push( - term::and( - conj.terms().iter().cloned().collect() - ) - ) + /// Puts the variables appearing in the top terms in some set. + pub fn vars(&self, set: &mut VarSet) { + for term in &self.terms { + set.extend(term::vars(term)) + } + for (_, argss) in &self.preds { + for args in argss { + for arg in args.iter() { + set.extend(term::vars(arg)) + } + } } - Some( term::or(disj_terms) ) - }, - } - } - - /// Boolean value of some top terms. - #[inline] - pub fn bool(& self) -> Option { - match * self { - TTerms::True => Some(true), - TTerms::False => Some(false), - _ => None, } - } - /// Removes some arguments from the predicate applications. - pub fn remove_vars(& mut self, to_keep: & PrdHMap< VarSet >) { - match * self { - TTerms::True | TTerms::False => (), - TTerms::Conj { ref mut tterms, .. } => tterms.remove_vars(to_keep), - TTerms::Disj { - ref mut tterms, ref mut neg_preds, .. - } => { - tterms.remove_vars(to_keep) ; - remove_vars_from_pred_apps(neg_preds, to_keep) - }, - TTerms::Dnf { ref mut disj } => for & mut (_, ref mut tterms) in disj { - tterms.remove_vars(to_keep) - }, + /// Removes some arguments from the predicate applications. + pub fn remove_vars(&mut self, to_keep: &PrdHMap) { + remove_vars_from_pred_apps(&mut self.preds, to_keep) } - } - /// Variable substitution. - pub fn subst>(& self, map: & Map) -> Self { - match * self { - TTerms::True => TTerms::True, - TTerms::False => TTerms::False, - - TTerms::Conj { ref quant, ref tterms } => { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) - } else { - true - } + /// Writes all top terms with some separator. + pub fn write( + &self, + w: &mut W, + sep: &str, + write_var: WriteVar, + write_pred: WritePrd, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + // Don't print the separator the first time. + let mut separate = false; + macro_rules! write_sep { + () => { + if separate { + write!(w, "{}", sep)? + } else { + separate = true + } + }; } - TTerms::Conj { quant: quant.clone(), tterms: tterms.subst(map) } - }, - TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) - } else { - true - } + for term in &self.terms { + write_sep!(); + term.write(w, &write_var)? } - let mut preds = PrdHMap::with_capacity( neg_preds.len() ) ; - for (pred, argss) in neg_preds.iter() { - let mut nu_argss = VarTermsSet::with_capacity( argss.len() ) ; - for args in argss { - let args = var_to::terms::new( args.subst(map) ) ; - nu_argss.insert(args) ; - } - preds.insert(* pred, nu_argss) ; + for (pred, argss) in &self.preds { + for args in argss { + write_sep!(); + write_pred(w, *pred, args)? + } } - TTerms::Disj { - quant: quant.clone(), - tterms: tterms.subst(map), - neg_preds: preds, + Ok(()) + } +} +impl ::std::cmp::PartialOrd for TTermSet { + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + use std::cmp::Ordering::*; + let mut le = true; + let mut ge = true; + macro_rules! check_none { + () => { + if !le && !ge { + return None; + } + }; } - }, - TTerms::Dnf { ref disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - for & (ref quant, ref tterms) in disj { - debug_assert! { - if let Some(quant) = quant.as_ref() { - quant.vars().keys().all( - |v| map.var_get(* v).is_none() - ) + for term in &self.terms { + if !other.terms.contains(term) { + le = false; + break; + } + } + for term in &other.terms { + if !self.terms.contains(term) { + ge = false; + break; + } + } + check_none!{} + + // Part of what happens in this loop is explained below. + for (pred, argss) in &self.preds { + check_none!{} + if let Some(ass) = other.preds.get(pred) { + if !argss.is_subset(ass) { + le = false + } + if !ass.is_subset(argss) { + ge = false + } } else { - true + le = false } - } - nu_disj.push( - (quant.clone(), tterms.subst(map)) - ) } - TTerms::Dnf { disj: nu_disj } - }, - } - } - - /// Constructor for a single term. - pub fn of_term(quant: Option, term: Term) -> Self { - Self::conj( quant, TTermSet::of_term(term) ) - } - - /// Constructs a conjuction. - pub fn conj(quant: Option, tterms: TTermSet) -> Self { - TTerms::Conj { quant, tterms }.simplify() - } - - /// Constructs a disjuction. - pub fn disj( - quant: Option, tterms: TTermSet, neg_preds: PrdHMap - ) -> Self { - TTerms::Disj { quant, tterms, neg_preds }.simplify() - } - /// Constructs a disjunction from a positive application and some negated top - /// terms. - /// - /// This special format is exactly the one used by preprocessing. - pub fn disj_of_pos_neg( - quant: Option, pos: Option<(PrdIdx, VarTerms)>, neg: TTermSet - ) -> Self { - let TTermSet { terms, preds: neg_preds } = neg ; - let mut tterms = TTermSet::with_capacities(terms.len(), 1) ; - for term in terms { - tterms.insert_term( not(term) ) ; - } - if let Some((pred, args)) = pos { - tterms.insert_pred_app(pred, args) ; - } - TTerms::Disj { - quant, tterms, neg_preds - } - } - /// Constructs a DNF. - pub fn dnf(disj: Vec< (Option, TTermSet) >) -> Self { - TTerms::Dnf{ disj }.simplify() - } - - /// Predicates appearing in the top terms. - pub fn preds(& self) -> PrdSet { - let mut res = PrdSet::new() ; - match * self { - TTerms::True | TTerms::False => (), - - TTerms::Conj { ref tterms, .. } => for pred in tterms.preds.keys() { - res.insert(* pred) ; - }, - TTerms::Disj { ref tterms, ref neg_preds, .. } => { - for pred in tterms.preds.keys() { - res.insert(* pred) ; - } - for pred in neg_preds.keys() { - res.insert(* pred) ; + // At this point we checked all predicate applications in `self`. We won't + // touch `le` anymore. + // + // The only way `ge` can change is if it is true and `other` has some + // predicates that don't appear in `self`. That's because in the previous + // loop, whenever a predicate from `self` also appears in `other`, we + // checked whether the other set of arguments is a subset of the self set + // of arguments and set `ge` to false if it's not the case. + + if ge { + for pred in other.preds.keys() { + if !self.preds.contains_key(pred) { + ge = false; + break; + } + } } - }, - TTerms::Dnf { ref disj } => for & (_, ref tterms) in disj { - for pred in tterms.preds.keys() { - res.insert(* pred) ; + match (le, ge) { + (true, true) => Some(Equal), + (true, false) => Some(Less), + (false, true) => Some(Greater), + (false, false) => None, } - }, } +} - res - } - - /// Constructs the disjunction of `self` and `conj` (a conjunction). - /// - /// Does not call `simplify` if it creates a dnf. - /// - /// # Error if - /// - /// - called on a `Disj` - pub fn or(self, conj: (Option, TTermSet)) -> Res { - match self { - TTerms::True => Ok(self), - TTerms::False => Ok( - TTerms::conj(conj.0, conj.1).simplify() - ), - TTerms::Conj { quant, tterms } => { - if tterms.is_subset_of(& conj.1) { - // conj => self? - Ok( TTerms::Conj { quant, tterms } ) - } else if conj.1.is_subset_of(& tterms) { - // self => conj? - Ok( - TTerms::Conj{ quant: conj.0, tterms: conj.1 }.simplify() - ) +/// Removes some arguments from some predicate applications. +fn remove_vars_from_pred_apps(apps: &mut PrdHMap, to_keep: &PrdHMap) { + for (pred, argss) in apps.iter_mut() { + let vars_to_keep = if let Some(vars) = to_keep.get(pred) { + vars } else { - Ok( - TTerms::dnf( - vec![ (quant, tterms), conj ] - ) - ) + continue; + }; + let mut old_argss = VarTermsSet::with_capacity(argss.len()); + ::std::mem::swap(&mut old_argss, argss); + for args in old_argss { + argss.insert(args.remove(vars_to_keep)); } - }, - TTerms::Disj { .. } => bail!( - "TTerms: trying to call `or` on a disjunction" - ), - TTerms::Dnf { disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - let mut ignore_conj = false ; - for (quant, tterms) in disj { - use std::cmp::Ordering::* ; - match tterms.partial_cmp( & conj.1 ) { - // conj => tterms - // don't need `conj` - Some(Less) => ignore_conj = true, - // tterms => conj - // skip `tterms` - Some(Greater) => continue, - // tterms = conj - // skip `conj` - Some(Equal) => ignore_conj = true, - - None => (), - } - nu_disj.push( (quant, tterms) ) - } - - if ! ignore_conj { nu_disj.push(conj) } - - // Note that if we ignored a `tterms_1` because `tterms_1 => conj`, and - // then we saw a `tterms_2` s.t. `conj => tterms_2` so we ignored - // `conj` as well, there is no problem because implication is - // transitive. + } +} - Ok( TTerms::Dnf { disj: nu_disj } ) - }, +/// A formula composed of top terms. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TTerms { + /// True. + True, + /// False. + False, + /// Conjunction. + Conj { + quant: Option, + tterms: TTermSet, + }, + /// Disjunction. + Disj { + quant: Option, + tterms: TTermSet, + neg_preds: PrdHMap, + }, + /// Almost a DNF: a disjunction of conjunctions of `TTerm`s. + Dnf { + disj: Vec<(Option, TTermSet)>, + }, +} +impl TTerms { + // pub fn inspect(& self) { + // match * self { + // TTerms::True => println!("true"), + // TTerms::False => println!("false"), + // TTerms::Conj { ref quant, ref tterms } => println!( + // "conj, {} ({})", tterms.len(), + // if let Some(q) = quant.as_ref() { + // format!("{}", q.len()) + // } else { "none".into() } + // ), + // TTerms::Disj { ref quant, ref tterms, ref neg_preds } => println!( + // "conj, {}, {}, {}", tterms.len(), neg_preds.len(), + // if let Some(q) = quant.as_ref() { + // format!("{}", q.len()) + // } else { "none".into() } + // ), + // TTerms::Dnf { ref disj } => println!( + // "dnf, {}", disj.len() + // ), + // } + // } + + /// True. + #[inline] + pub fn tru() -> Self { + TTerms::True + } + /// False. + #[inline] + pub fn fls() -> Self { + TTerms::False + } + /// Constructor from a boolean. + #[inline] + pub fn of_bool(b: bool) -> Self { + if b { + Self::tru() + } else { + Self::fls() + } } - } - /// Simplifies a formula of top terms. - /// - /// # TODO - /// - /// - factor code inside this function, `Conj` and `Disj` are almost the - /// same, DRY - /// - /// # Warning - /// - /// This function recurses on a `Conj` in the `Dnf` case. At the time of - /// writing, this recursive call is guaranteed not to cause more recursive - /// calls. - /// - /// Be careful to maintain, or (ideally) improve, this invariant. - pub fn simplify(self) -> Self { - - match self { - - TTerms::True | TTerms::False => self, - - TTerms::Conj { quant, mut tterms } => { - tterms.preds.retain( - |_, argss| ! argss.is_empty() - ) ; - - let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; - // Used to inline conjunctions. - let mut swap = TermSet::new() ; - ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; - - 'inline_conjs: loop { - - 'inspect_conj_terms: for term in old_terms.drain() { - - // Is the term a conjunction? - if let Some(kids) = term.conj_inspect() { - for kid in kids { - swap.insert( kid.clone() ) ; - () - } - continue 'inspect_conj_terms - } + /// Attempts to transform some terms in a term. + pub fn to_term(&self) -> Option { + match *self { + TTerms::True => Some(term::tru()), + TTerms::False => Some(term::fls()), - // Term trivial? - match term.bool() { - Some(true) => continue 'inspect_conj_terms, - Some(false) => return TTerms::fls(), - None => (), + TTerms::Conj { ref quant, .. } if quant.is_some() => None, + TTerms::Conj { ref tterms, .. } if !tterms.preds.is_empty() => None, + TTerms::Conj { ref tterms, .. } => { + Some(term::and(tterms.terms().iter().cloned().collect())) } - // Do we also have its negation? - if tterms.terms.contains( & not( term.clone() ) ) { - return TTerms::fls() + TTerms::Disj { ref quant, .. } if quant.is_some() => None, + TTerms::Disj { + ref tterms, + ref neg_preds, + .. + } + if !tterms.preds.is_empty() || !neg_preds.is_empty() => + { + None + } + TTerms::Disj { ref tterms, .. } => { + Some(term::or(tterms.terms().iter().cloned().collect())) } - // Okay, move on. - tterms.terms.insert(term) ; - () - } - - // Keep going if `swap` is not empty. - if ! swap.is_empty() { - ::std::mem::swap( & mut old_terms, & mut swap ) ; - continue 'inline_conjs - } else { - break 'inline_conjs - } - + TTerms::Dnf { ref disj } => { + let mut disj_terms = Vec::with_capacity(disj.len()); + for &(ref quant, ref conj) in disj { + if quant.is_some() || !conj.preds.is_empty() { + return None; + } + disj_terms.push(term::and(conj.terms().iter().cloned().collect())) + } + Some(term::or(disj_terms)) + } } + } - // Only keep active quantified variables. - let quant = quant.and_then( - |quant| { - let mut active = VarSet::with_capacity( - quant.vars().len() * 2 - ) ; - tterms.vars(& mut active) ; - quant.filter(|var| active.contains(var)) - } - ) ; - - if tterms.is_empty() { - TTerms::tru() - } else { - TTerms::Conj { quant, tterms } + /// Boolean value of some top terms. + #[inline] + pub fn bool(&self) -> Option { + match *self { + TTerms::True => Some(true), + TTerms::False => Some(false), + _ => None, } - }, + } - TTerms::Disj { - quant, mut tterms, mut neg_preds - } => { - tterms.preds.retain( - |_, argss| ! argss.is_empty() - ) ; - neg_preds.retain( - |_, argss| ! argss.is_empty() - ) ; - - // Do we have a predicate application and its negation? - for (pred, argss) in & tterms.preds { - if let Some(neg_argss) = neg_preds.get(pred) { - for args in argss { - if neg_argss.contains(args) { return TTerms::tru() } + /// Removes some arguments from the predicate applications. + pub fn remove_vars(&mut self, to_keep: &PrdHMap) { + match *self { + TTerms::True | TTerms::False => (), + TTerms::Conj { ref mut tterms, .. } => tterms.remove_vars(to_keep), + TTerms::Disj { + ref mut tterms, + ref mut neg_preds, + .. + } => { + tterms.remove_vars(to_keep); + remove_vars_from_pred_apps(neg_preds, to_keep) } - } + TTerms::Dnf { ref mut disj } => for &mut (_, ref mut tterms) in disj { + tterms.remove_vars(to_keep) + }, } + } - let mut old_terms = TermSet::with_capacity( tterms.terms.len() ) ; - // Used to inline disjunctions. - let mut swap = TermSet::new() ; - ::std::mem::swap( & mut old_terms, & mut tterms.terms ) ; - - 'inline_disj: loop { + /// Variable substitution. + pub fn subst>(&self, map: &Map) -> Self { + match *self { + TTerms::True => TTerms::True, + TTerms::False => TTerms::False, + + TTerms::Conj { + ref quant, + ref tterms, + } => { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } + TTerms::Conj { + quant: quant.clone(), + tterms: tterms.subst(map), + } + } - 'inspect_disj_terms: for term in old_terms.drain() { + TTerms::Disj { + ref quant, + ref tterms, + ref neg_preds, + } => { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } - // Is the term a disjunction? - if let Some(kids) = term.disj_inspect() { - for kid in kids { - swap.insert( kid.clone() ) ; - () - } - continue 'inspect_disj_terms - } + let mut preds = PrdHMap::with_capacity(neg_preds.len()); + for (pred, argss) in neg_preds.iter() { + let mut nu_argss = VarTermsSet::with_capacity(argss.len()); + for args in argss { + let args = var_to::terms::new(args.subst(map)); + nu_argss.insert(args); + } + preds.insert(*pred, nu_argss); + } - // Term trivial? - match term.bool() { - Some(true) => return TTerms::tru(), - Some(false) => continue 'inspect_disj_terms, - None => (), + TTerms::Disj { + quant: quant.clone(), + tterms: tterms.subst(map), + neg_preds: preds, + } } - // Do we also have its negation? - if tterms.terms.contains( & not( term.clone() ) ) { - return TTerms::tru() + TTerms::Dnf { ref disj } => { + let mut nu_disj = Vec::with_capacity(disj.len()); + for &(ref quant, ref tterms) in disj { + debug_assert! { + if let Some(quant) = quant.as_ref() { + quant.vars().keys().all( + |v| map.var_get(* v).is_none() + ) + } else { + true + } + } + nu_disj.push((quant.clone(), tterms.subst(map))) + } + TTerms::Dnf { disj: nu_disj } } + } + } - // Okay, move on. - tterms.terms.insert(term) ; - () - } + /// Constructor for a single term. + pub fn of_term(quant: Option, term: Term) -> Self { + Self::conj(quant, TTermSet::of_term(term)) + } - // Keep going if `swap` is not empty. - if ! swap.is_empty() { - ::std::mem::swap( & mut old_terms, & mut swap ) ; - continue 'inline_disj - } else { - break 'inline_disj - } + /// Constructs a conjuction. + pub fn conj(quant: Option, tterms: TTermSet) -> Self { + TTerms::Conj { quant, tterms }.simplify() + } + /// Constructs a disjuction. + pub fn disj(quant: Option, tterms: TTermSet, neg_preds: PrdHMap) -> Self { + TTerms::Disj { + quant, + tterms, + neg_preds, + }.simplify() + } + /// Constructs a disjunction from a positive application and some negated top + /// terms. + /// + /// This special format is exactly the one used by preprocessing. + pub fn disj_of_pos_neg( + quant: Option, + pos: Option<(PrdIdx, VarTerms)>, + neg: TTermSet, + ) -> Self { + let TTermSet { + terms, + preds: neg_preds, + } = neg; + let mut tterms = TTermSet::with_capacities(terms.len(), 1); + for term in terms { + tterms.insert_term(not(term)); + } + if let Some((pred, args)) = pos { + tterms.insert_pred_app(pred, args); + } + TTerms::Disj { + quant, + tterms, + neg_preds, } + } + + /// Constructs a DNF. + pub fn dnf(disj: Vec<(Option, TTermSet)>) -> Self { + TTerms::Dnf { disj }.simplify() + } + + /// Predicates appearing in the top terms. + pub fn preds(&self) -> PrdSet { + let mut res = PrdSet::new(); + match *self { + TTerms::True | TTerms::False => (), - // Only keep active quantified variables. - let quant = quant.and_then( - |quant| { - let mut active = VarSet::with_capacity( - quant.vars().len() * 2 - ) ; - tterms.vars(& mut active) ; - for (_, argss) in & neg_preds { - for args in argss { - for arg in args.iter() { - active.extend( term::vars(arg) ) + TTerms::Conj { ref tterms, .. } => for pred in tterms.preds.keys() { + res.insert(*pred); + }, + TTerms::Disj { + ref tterms, + ref neg_preds, + .. + } => { + for pred in tterms.preds.keys() { + res.insert(*pred); + } + for pred in neg_preds.keys() { + res.insert(*pred); } - } } - quant.filter(|var| active.contains(var)) - } - ) ; - - if tterms.is_empty() && neg_preds.is_empty() { - TTerms::fls() - } else { - TTerms::Disj { quant, tterms, neg_preds } - } - }, - TTerms::Dnf { disj } => { - // We're cheating a bit here. Simplifying the disjuncts is done by - // constructing the corresponding `TTerms::Conj` and simplifying them. - // While this causes a recursive call, it's fine because it is - // guaranteed to be the only one. - // - // Unless something changes later in `Conj`'s simplification... - let mut nu_disj: Vec<(_, TTermSet)> = Vec::with_capacity( - disj.len() - ) ; - 'simplify_disjuncts: for (quant, tterms) in disj { - match ( TTerms::Conj { quant, tterms } ).simplify() { - TTerms::True => return TTerms::True, - TTerms::False => (), - TTerms::Conj { quant, tterms } => { - // Check with other disjuncts. - let mut cnt = 0 ; - while cnt < nu_disj.len() { - use std::cmp::Ordering::* ; - match tterms.partial_cmp(& nu_disj[cnt].1) { - None => cnt += 1, - // other disjunct => this disjunct - Some(Less) => { nu_disj.swap_remove(cnt) ; () }, - // other disjunct = this disjunct - Some(Equal) => continue 'simplify_disjuncts, - // this disjunct => other disjunct - Some(Greater) => continue 'simplify_disjuncts, + TTerms::Dnf { ref disj } => for &(_, ref tterms) in disj { + for pred in tterms.preds.keys() { + res.insert(*pred); } - } - nu_disj.push( (quant, tterms) ) }, - TTerms::Disj { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a disjunction, unreachable" - ), - TTerms::Dnf { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a DNF, unreachable" - ), - } - } - match nu_disj.len() { - 0 => TTerms::fls(), - 1 => if let Some((quant, tterms)) = nu_disj.pop() { - TTerms::Conj { quant, tterms } - } else { - unreachable!() - }, - _ => TTerms::Dnf { disj: nu_disj } } - }, - } - } - - /// Simplifies some top terms given some definitions for the predicates. - pub fn simplify_pred_apps( - self, model: ModelRef, pred_terms: & PrdMap< Option > - ) -> Self { - macro_rules! if_defined { - ($pred:ident then |$def:ident| $stuff:expr) => ( - if let Some($def) = pred_terms[* $pred].as_ref() { - $stuff - } else { - for (ref idx, ref $def) in model { - if idx == $pred { $stuff } - } - } - ) + res } - match self { - TTerms::True => TTerms::True, - TTerms::False => TTerms::False, + /// Constructs the disjunction of `self` and `conj` (a conjunction). + /// + /// Does not call `simplify` if it creates a dnf. + /// + /// # Error if + /// + /// - called on a `Disj` + pub fn or(self, conj: (Option, TTermSet)) -> Res { + match self { + TTerms::True => Ok(self), + TTerms::False => Ok(TTerms::conj(conj.0, conj.1).simplify()), + TTerms::Conj { quant, tterms } => { + if tterms.is_subset_of(&conj.1) { + // conj => self? + Ok(TTerms::Conj { quant, tterms }) + } else if conj.1.is_subset_of(&tterms) { + // self => conj? + Ok(TTerms::Conj { + quant: conj.0, + tterms: conj.1, + }.simplify()) + } else { + Ok(TTerms::dnf(vec![(quant, tterms), conj])) + } + } + TTerms::Disj { .. } => bail!("TTerms: trying to call `or` on a disjunction"), + TTerms::Dnf { disj } => { + let mut nu_disj = Vec::with_capacity(disj.len()); + let mut ignore_conj = false; + for (quant, tterms) in disj { + use std::cmp::Ordering::*; + match tterms.partial_cmp(&conj.1) { + // conj => tterms + // don't need `conj` + Some(Less) => ignore_conj = true, + // tterms => conj + // skip `tterms` + Some(Greater) => continue, + // tterms = conj + // skip `conj` + Some(Equal) => ignore_conj = true, + + None => (), + } + nu_disj.push((quant, tterms)) + } + + if !ignore_conj { + nu_disj.push(conj) + } - TTerms::Conj { quant, mut tterms } => { - let mut to_rm = PrdSet::new() ; + // Note that if we ignored a `tterms_1` because `tterms_1 => conj`, and + // then we saw a `tterms_2` s.t. `conj => tterms_2` so we ignored + // `conj` as well, there is no problem because implication is + // transitive. - for pred in tterms.preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(true) => { to_rm.insert(* pred) ; () }, - Some(false) => return TTerms::fls(), - None => (), + Ok(TTerms::Dnf { disj: nu_disj }) } - } } + } - for pred in to_rm { - let value = tterms.preds.remove(& pred) ; - debug_assert!( value.is_some() ) - } + /// Simplifies a formula of top terms. + /// + /// # TODO + /// + /// - factor code inside this function, `Conj` and `Disj` are almost the + /// same, DRY + /// + /// # Warning + /// + /// This function recurses on a `Conj` in the `Dnf` case. At the time of + /// writing, this recursive call is guaranteed not to cause more recursive + /// calls. + /// + /// Be careful to maintain, or (ideally) improve, this invariant. + pub fn simplify(self) -> Self { + match self { + TTerms::True | TTerms::False => self, + + TTerms::Conj { quant, mut tterms } => { + tterms.preds.retain(|_, argss| !argss.is_empty()); + + let mut old_terms = TermSet::with_capacity(tterms.terms.len()); + // Used to inline conjunctions. + let mut swap = TermSet::new(); + ::std::mem::swap(&mut old_terms, &mut tterms.terms); + + 'inline_conjs: loop { + 'inspect_conj_terms: for term in old_terms.drain() { + // Is the term a conjunction? + if let Some(kids) = term.conj_inspect() { + for kid in kids { + swap.insert(kid.clone()); + () + } + continue 'inspect_conj_terms; + } + + // Term trivial? + match term.bool() { + Some(true) => continue 'inspect_conj_terms, + Some(false) => return TTerms::fls(), + None => (), + } + + // Do we also have its negation? + if tterms.terms.contains(¬(term.clone())) { + return TTerms::fls(); + } + + // Okay, move on. + tterms.terms.insert(term); + () + } + + // Keep going if `swap` is not empty. + if !swap.is_empty() { + ::std::mem::swap(&mut old_terms, &mut swap); + continue 'inline_conjs; + } else { + break 'inline_conjs; + } + } - TTerms::Conj { quant, tterms }.simplify() - }, + // Only keep active quantified variables. + let quant = quant.and_then(|quant| { + let mut active = VarSet::with_capacity(quant.vars().len() * 2); + tterms.vars(&mut active); + quant.filter(|var| active.contains(var)) + }); + + if tterms.is_empty() { + TTerms::tru() + } else { + TTerms::Conj { quant, tterms } + } + } - TTerms::Disj { quant, mut tterms, mut neg_preds } => { - let mut to_rm = PrdSet::new() ; + TTerms::Disj { + quant, + mut tterms, + mut neg_preds, + } => { + tterms.preds.retain(|_, argss| !argss.is_empty()); + neg_preds.retain(|_, argss| !argss.is_empty()); + + // Do we have a predicate application and its negation? + for (pred, argss) in &tterms.preds { + if let Some(neg_argss) = neg_preds.get(pred) { + for args in argss { + if neg_argss.contains(args) { + return TTerms::tru(); + } + } + } + } - for pred in tterms.preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(false) => { to_rm.insert(* pred) ; () }, - Some(true) => return TTerms::tru(), - None => (), - } - } - } + let mut old_terms = TermSet::with_capacity(tterms.terms.len()); + // Used to inline disjunctions. + let mut swap = TermSet::new(); + ::std::mem::swap(&mut old_terms, &mut tterms.terms); + + 'inline_disj: loop { + 'inspect_disj_terms: for term in old_terms.drain() { + // Is the term a disjunction? + if let Some(kids) = term.disj_inspect() { + for kid in kids { + swap.insert(kid.clone()); + () + } + continue 'inspect_disj_terms; + } + + // Term trivial? + match term.bool() { + Some(true) => return TTerms::tru(), + Some(false) => continue 'inspect_disj_terms, + None => (), + } + + // Do we also have its negation? + if tterms.terms.contains(¬(term.clone())) { + return TTerms::tru(); + } + + // Okay, move on. + tterms.terms.insert(term); + () + } + + // Keep going if `swap` is not empty. + if !swap.is_empty() { + ::std::mem::swap(&mut old_terms, &mut swap); + continue 'inline_disj; + } else { + break 'inline_disj; + } + } - for pred in to_rm.drain() { - let value = tterms.preds.remove(& pred) ; - debug_assert!( value.is_some() ) - } + // Only keep active quantified variables. + let quant = quant.and_then(|quant| { + let mut active = VarSet::with_capacity(quant.vars().len() * 2); + tterms.vars(&mut active); + for (_, argss) in &neg_preds { + for args in argss { + for arg in args.iter() { + active.extend(term::vars(arg)) + } + } + } + quant.filter(|var| active.contains(var)) + }); + + if tterms.is_empty() && neg_preds.is_empty() { + TTerms::fls() + } else { + TTerms::Disj { + quant, + tterms, + neg_preds, + } + } + } - for pred in neg_preds.keys() { - if_defined! { - pred then |def| match def.bool() { - Some(true) => { to_rm.insert(* pred) ; () }, - Some(false) => return TTerms::tru(), - None => (), + TTerms::Dnf { disj } => { + // We're cheating a bit here. Simplifying the disjuncts is done by + // constructing the corresponding `TTerms::Conj` and simplifying them. + // While this causes a recursive call, it's fine because it is + // guaranteed to be the only one. + // + // Unless something changes later in `Conj`'s simplification... + let mut nu_disj: Vec<(_, TTermSet)> = Vec::with_capacity(disj.len()); + 'simplify_disjuncts: for (quant, tterms) in disj { + match (TTerms::Conj { quant, tterms }).simplify() { + TTerms::True => return TTerms::True, + TTerms::False => (), + TTerms::Conj { quant, tterms } => { + // Check with other disjuncts. + let mut cnt = 0; + while cnt < nu_disj.len() { + use std::cmp::Ordering::*; + match tterms.partial_cmp(&nu_disj[cnt].1) { + None => cnt += 1, + // other disjunct => this disjunct + Some(Less) => { + nu_disj.swap_remove(cnt); + () + } + // other disjunct = this disjunct + Some(Equal) => continue 'simplify_disjuncts, + // this disjunct => other disjunct + Some(Greater) => continue 'simplify_disjuncts, + } + } + nu_disj.push((quant, tterms)) + } + TTerms::Disj { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a disjunction, unreachable" + ), + TTerms::Dnf { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a DNF, unreachable" + ), + } + } + match nu_disj.len() { + 0 => TTerms::fls(), + 1 => if let Some((quant, tterms)) = nu_disj.pop() { + TTerms::Conj { quant, tterms } + } else { + unreachable!() + }, + _ => TTerms::Dnf { disj: nu_disj }, + } } - } } + } - for pred in to_rm.drain() { - let value = neg_preds.remove(& pred) ; - debug_assert!( value.is_some() ) + /// Simplifies some top terms given some definitions for the predicates. + pub fn simplify_pred_apps(self, model: ModelRef, pred_terms: &PrdMap>) -> Self { + macro_rules! if_defined { + ($pred:ident then |$def:ident| $stuff:expr) => { + if let Some($def) = pred_terms[*$pred].as_ref() { + $stuff + } else { + for (ref idx, ref $def) in model { + if idx == $pred { + $stuff + } + } + } + }; } - TTerms::Disj { quant, tterms, neg_preds }.simplify() - }, + match self { + TTerms::True => TTerms::True, + TTerms::False => TTerms::False, + + TTerms::Conj { quant, mut tterms } => { + let mut to_rm = PrdSet::new(); + + for pred in tterms.preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(true) => { to_rm.insert(* pred) ; () }, + Some(false) => return TTerms::fls(), + None => (), + } + } + } - TTerms::Dnf { disj } => { - let mut nu_disj = Vec::with_capacity( disj.len() ) ; - - for (quant, tterms) in disj { - match ( - TTerms::Conj { quant, tterms } - ).simplify_pred_apps(model, pred_terms) { - TTerms::True => return TTerms::tru(), - TTerms::False => (), - - TTerms::Conj { quant, tterms } => nu_disj.push( - (quant, tterms) - ), - - TTerms::Disj { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a disjunction, unreachable" - ), - TTerms::Dnf { .. } => panic!( - "simplification of a conjunct in a TTerms DNF \ - yielded a DNF, unreachable" - ), - } - } + for pred in to_rm { + let value = tterms.preds.remove(&pred); + debug_assert!(value.is_some()) + } - TTerms::Dnf{ disj: nu_disj }.simplify() - }, - } - } + TTerms::Conj { quant, tterms }.simplify() + } + TTerms::Disj { + quant, + mut tterms, + mut neg_preds, + } => { + let mut to_rm = PrdSet::new(); + + for pred in tterms.preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(false) => { to_rm.insert(* pred) ; () }, + Some(true) => return TTerms::tru(), + None => (), + } + } + } - /// Writes some top terms using special functions for writing predicates and - /// variables. - pub fn write( - & self, w: & mut W, write_var: WriteVar, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(& mut W, VarIdx) -> IoRes<()>, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { + for pred in to_rm.drain() { + let value = tterms.preds.remove(&pred); + debug_assert!(value.is_some()) + } - macro_rules! write_conj { - ($quant:expr, $tterms:expr) => ({ - let close_quant = if let Some(quant) = $quant.as_ref() { - write!(w, "(") ? ; - quant.write(w, & write_var) ? ; - write!(w, " ") ? ; - true - } else { false } ; + for pred in neg_preds.keys() { + if_defined! { + pred then |def| match def.bool() { + Some(true) => { to_rm.insert(* pred) ; () }, + Some(false) => return TTerms::tru(), + None => (), + } + } + } - let close_and = if $tterms.len() > 1 { - write!(w, "(and ") ? ; - true - } else { false } ; + for pred in to_rm.drain() { + let value = neg_preds.remove(&pred); + debug_assert!(value.is_some()) + } - $tterms.write(w, " ", & write_var, & write_prd) ? ; + TTerms::Disj { + quant, + tterms, + neg_preds, + }.simplify() + } - if close_and { write!(w, ")") ? } + TTerms::Dnf { disj } => { + let mut nu_disj = Vec::with_capacity(disj.len()); + + for (quant, tterms) in disj { + match (TTerms::Conj { quant, tterms }).simplify_pred_apps(model, pred_terms) { + TTerms::True => return TTerms::tru(), + TTerms::False => (), + + TTerms::Conj { quant, tterms } => nu_disj.push((quant, tterms)), + + TTerms::Disj { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a disjunction, unreachable" + ), + TTerms::Dnf { .. } => panic!( + "simplification of a conjunct in a TTerms DNF \ + yielded a DNF, unreachable" + ), + } + } - if close_quant { write!(w, ")") } else { Ok(()) } - }) ; + TTerms::Dnf { disj: nu_disj }.simplify() + } + } } - match * self { - TTerms::True => return write!(w, "true"), - TTerms::False => return write!(w, "false"), - - TTerms::Conj { ref quant, ref tterms } => write_conj!(quant, tterms), - - TTerms::Disj { ref quant, ref tterms, ref neg_preds } => { - let close_quant = if let Some(quant) = quant.as_ref() { - write!(w, "(") ? ; - quant.write(w, & write_var) ? ; - write!(w, " ") ? ; - true - } else { false } ; + /// Writes some top terms using special functions for writing predicates and + /// variables. + pub fn write( + &self, + w: &mut W, + write_var: WriteVar, + write_prd: WritePrd, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + macro_rules! write_conj { + ($quant:expr, $tterms:expr) => {{ + let close_quant = if let Some(quant) = $quant.as_ref() { + write!(w, "(")?; + quant.write(w, &write_var)?; + write!(w, " ")?; + true + } else { + false + }; + + let close_and = if $tterms.len() > 1 { + write!(w, "(and ")?; + true + } else { + false + }; + + $tterms.write(w, " ", &write_var, &write_prd)?; + + if close_and { + write!(w, ")")? + } - let close_or = if tterms.len() + neg_preds.len() > 1 { - write!(w, "(or ") ? ; - true - } else { false } ; + if close_quant { + write!(w, ")") + } else { + Ok(()) + } + }}; + } - tterms.write(w, " ", & write_var, & write_prd) ? ; + match *self { + TTerms::True => return write!(w, "true"), + TTerms::False => return write!(w, "false"), + + TTerms::Conj { + ref quant, + ref tterms, + } => write_conj!(quant, tterms), + + TTerms::Disj { + ref quant, + ref tterms, + ref neg_preds, + } => { + let close_quant = if let Some(quant) = quant.as_ref() { + write!(w, "(")?; + quant.write(w, &write_var)?; + write!(w, " ")?; + true + } else { + false + }; + + let close_or = if tterms.len() + neg_preds.len() > 1 { + write!(w, "(or ")?; + true + } else { + false + }; + + tterms.write(w, " ", &write_var, &write_prd)?; + + let mut sep = !tterms.is_empty() && !neg_preds.is_empty(); + + for (pred, argss) in neg_preds { + for args in argss { + if sep { + write!(w, " ")? + } else { + sep = true + } + write!(w, "(not ")?; + write_prd(w, *pred, args)?; + write!(w, ")")? + } + } - let mut sep = ! tterms.is_empty() && ! neg_preds.is_empty() ; + if close_or { + write!(w, ")")? + } - for (pred, argss) in neg_preds { - for args in argss { - if sep { - write!(w, " ") ? - } else { - sep = true + if close_quant { + write!(w, ")") + } else { + Ok(()) + } } - write!(w, "(not ") ? ; - write_prd(w, * pred, args) ? ; - write!(w, ")") ? - } - } - - if close_or { write!(w, ")") ? } - if close_quant { write!(w, ")") } else { Ok(()) } - }, - - TTerms::Dnf { ref disj } => { - let close_or = if disj.len() > 1 { - write!(w, "(or") ? ; - true - } else { false } ; + TTerms::Dnf { ref disj } => { + let close_or = if disj.len() > 1 { + write!(w, "(or")?; + true + } else { + false + }; + + for &(ref quant, ref tterms) in disj { + write!(w, " ")?; + write_conj!(quant, tterms)? + } - for & (ref quant, ref tterms) in disj { - write!(w, " ") ? ; - write_conj!(quant, tterms) ? + if close_or { + write!(w, ")") + } else { + Ok(()) + } + } } - - if close_or { write!(w, ")") } else { Ok(()) } - }, } - } - /// Writes some top terms smt2 style using a special function for writing - /// predicates. - /// - /// Equivalent to `write` with variable default printing. - pub fn write_smt2( - & self, w: & mut W, write_prd: WritePrd - ) -> IoRes<()> - where - W: Write, - WritePrd: Fn(& mut W, PrdIdx, & VarTerms) -> IoRes<()> { - self.write( - w, |w, var| var.default_write(w), write_prd - ) - } + /// Writes some top terms smt2 style using a special function for writing + /// predicates. + /// + /// Equivalent to `write` with variable default printing. + pub fn write_smt2(&self, w: &mut W, write_prd: WritePrd) -> IoRes<()> + where + W: Write, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + { + self.write(w, |w, var| var.default_write(w), write_prd) + } } -impl<'a, 'b> ::rsmt2::print::Expr2Smt< - & 'b (& 'a PrdSet, & 'a PrdSet, & 'a PrdMap< ::instance::info::PrdInfo >) -> for TTerms { - fn expr_to_smt2( - & self, w: & mut Writer, info: & 'b ( - & 'a PrdSet, & 'a PrdSet, & 'a PrdMap<::instance::info::PrdInfo> - ) - ) -> SmtRes<()> { - let (true_preds, false_preds, pred_info) = * info ; - self.write_smt2( - w, |w, pred, args| { - if true_preds.contains(& pred) { - write!(w, "true") - } else if false_preds.contains(& pred) { - write!(w, "false") - } else if args.is_empty() { - write!(w, "{}", pred_info[pred]) - } else { - write!(w, "({}", pred_info[pred]) ? ; - for arg in args.iter() { - write!(w, " ") ? ; - arg.write(w, |w, var| var.default_write(w)) ? - } - write!(w, ")") - } - } - ) ? ; - Ok(()) - } +impl<'a, 'b> + Expr2Smt<&'b ( + &'a PrdSet, + &'a PrdSet, + &'a PrdMap<::instance::info::PrdInfo>, + )> for TTerms +{ + fn expr_to_smt2( + &self, + w: &mut Writer, + info: &'b ( + &'a PrdSet, + &'a PrdSet, + &'a PrdMap<::instance::info::PrdInfo>, + ), + ) -> SmtRes<()> { + let (true_preds, false_preds, pred_info) = *info; + self.write_smt2(w, |w, pred, args| { + if true_preds.contains(&pred) { + write!(w, "true") + } else if false_preds.contains(&pred) { + write!(w, "false") + } else if args.is_empty() { + write!(w, "{}", pred_info[pred]) + } else { + write!(w, "({}", pred_info[pred])?; + for arg in args.iter() { + write!(w, " ")?; + arg.write(w, |w, var| var.default_write(w))? + } + write!(w, ")") + } + })?; + Ok(()) + } } - - /// Existential or universal quantifier. /// /// Always use the constructors to avoid falsifying the invariant. @@ -1359,112 +1447,129 @@ impl<'a, 'b> ::rsmt2::print::Expr2Smt< /// The variable partial maps are never empty. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Quant { - /// Exists. - Exists( VarHMap ), - /// Forall. - Forall( VarHMap ), + /// Exists. + Exists(VarHMap), + /// Forall. + Forall(VarHMap), } impl Quant { - /// Creates an existential qualifier. - pub fn exists(map: VarHMap) -> Option { - if map.is_empty() { None } else { Some( Quant::Exists(map) ) } - } - /// Creates an existential qualifier. - pub fn forall(map: VarHMap) -> Option { - if map.is_empty() { None } else { Some( Quant::Forall(map) ) } - } + /// Creates an existential qualifier. + pub fn exists(map: VarHMap) -> Option { + if map.is_empty() { + None + } else { + Some(Quant::Exists(map)) + } + } + /// Creates an existential qualifier. + pub fn forall(map: VarHMap) -> Option { + if map.is_empty() { + None + } else { + Some(Quant::Forall(map)) + } + } - /// Quantified variables. - pub fn vars(& self) -> & VarHMap { - match * self { - Quant::Exists(ref vars) => vars, - Quant::Forall(ref vars) => vars, + /// Quantified variables. + pub fn vars(&self) -> &VarHMap { + match *self { + Quant::Exists(ref vars) => vars, + Quant::Forall(ref vars) => vars, + } } - } - /// Quantified variables (mutable version). - pub fn vars_mut(& mut self) -> & mut VarHMap { - match * self { - Quant::Exists(ref mut vars) => vars, - Quant::Forall(ref mut vars) => vars, + /// Quantified variables (mutable version). + pub fn vars_mut(&mut self) -> &mut VarHMap { + match *self { + Quant::Exists(ref mut vars) => vars, + Quant::Forall(ref mut vars) => vars, + } } - } - /// Number of quantified variables. - pub fn len(& self) -> usize { - let map = match * self { - Quant::Exists(ref map) => map, - Quant::Forall(ref map) => map, - } ; - debug_assert! { ! map.is_empty() } - map.len() - } + /// Number of quantified variables. + pub fn len(&self) -> usize { + let map = match *self { + Quant::Exists(ref map) => map, + Quant::Forall(ref map) => map, + }; + debug_assert! { ! map.is_empty() } + map.len() + } - /// True if there are no quantified variables. - pub fn is_empty(& self) -> bool { self.len() == 0 } - - /// Filters some quantified variables. - /// - /// Keeps all quantified variables such that `f(var)`. - /// - /// Returns `None` if the mapping ends up being empty. - pub fn filter(mut self, f: F) -> Option - where F: Fn(& VarIdx) -> bool { - self.vars_mut().retain( |var, _| f(var) ) ; - if self.vars().is_empty() { None } else { Some(self) } - } + /// True if there are no quantified variables. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } - /// Writes the quantier and its quantified variables. - pub fn write( - & self, w: & mut W, write_var: WVar - ) -> IoRes<()> - where W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { - debug_assert!( ! self.vars().is_empty() ) ; - - let qvars = match * self { - Quant::Exists(ref qvars) => { - write!(w, "exists ") ? ; - qvars - }, - Quant::Forall(ref qvars) => { - write!(w, "forall ") ? ; - qvars - }, - } ; + /// Filters some quantified variables. + /// + /// Keeps all quantified variables such that `f(var)`. + /// + /// Returns `None` if the mapping ends up being empty. + pub fn filter(mut self, f: F) -> Option + where + F: Fn(&VarIdx) -> bool, + { + self.vars_mut().retain(|var, _| f(var)); + if self.vars().is_empty() { + None + } else { + Some(self) + } + } - write!(w, "(") ? ; - for (var, typ) in qvars { - write!(w, " (") ? ; - write_var(w, * var) ? ; - write!(w, " {})", typ) ? ; + /// Writes the quantier and its quantified variables. + pub fn write(&self, w: &mut W, write_var: WVar) -> IoRes<()> + where + W: Write, + WVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + debug_assert!(!self.vars().is_empty()); + + let qvars = match *self { + Quant::Exists(ref qvars) => { + write!(w, "exists ")?; + qvars + } + Quant::Forall(ref qvars) => { + write!(w, "forall ")?; + qvars + } + }; + + write!(w, "(")?; + for (var, typ) in qvars { + write!(w, " (")?; + write_var(w, *var)?; + write!(w, " {})", typ)?; + } + write!(w, " )") } - write!(w, " )") - } - /// Writes the opening part of the quantifier as a line. - /// - /// Basically `"{}( ( )\n", prefix`. - pub fn write_pref( - & self, w: & mut W, pref: & str, write_var: WVar - ) -> IoRes<()> - where - W: Write, WVar: Fn(& mut W, VarIdx) -> IoRes<()> { - w.write_all( pref.as_bytes() ) ? ; - let map = match * self { - Quant::Exists(ref map) => { - write!(w, "(exists (") ? ; - map - }, - Quant::Forall(ref map) => { - write!(w, "(forall (") ? ; - map - }, - } ; - for (var, typ) in map { - write!(w, " (") ? ; - write_var(w, * var) ? ; - write!(w, " {})", typ) ? + /// Writes the opening part of the quantifier as a line. + /// + /// Basically `"{}( ( )\n", prefix`. + pub fn write_pref(&self, w: &mut W, pref: &str, write_var: WVar) -> IoRes<()> + where + W: Write, + WVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + w.write_all(pref.as_bytes())?; + let map = match *self { + Quant::Exists(ref map) => { + write!(w, "(exists (")?; + map + } + Quant::Forall(ref map) => { + write!(w, "(forall (")?; + map + } + }; + for (var, typ) in map { + write!(w, " (")?; + write_var(w, *var)?; + write!(w, " {})", typ)? + } + writeln!(w, " )") } - writeln!(w, " )") - } -} \ No newline at end of file +} diff --git a/src/term/typ.rs b/src/term/typ.rs index b456cd20..ed158f79 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -1,44 +1,42 @@ //! Everything type-related. -use hashconsing::{ HashConsign, HConsed } ; +use hashconsing::{HConsed, HashConsign}; -use common::* ; -use dtyp::TPrmMap ; +use common::*; +use dtyp::TPrmMap; new_consign! { /// Type factory. let factory = consign(conf.instance.term_capa) for RTyp ; } - /// Generates the `Int` type. pub fn int() -> Typ { - factory.mk(RTyp::Int) + factory.mk(RTyp::Int) } /// Generates the `Real` type. pub fn real() -> Typ { - factory.mk(RTyp::Real) + factory.mk(RTyp::Real) } /// Generates the `Bool` type. pub fn bool() -> Typ { - factory.mk(RTyp::Bool) + factory.mk(RTyp::Bool) } /// Generates an Array type. pub fn array(src: Typ, tgt: Typ) -> Typ { - factory.mk(RTyp::Array { src, tgt }) + factory.mk(RTyp::Array { src, tgt }) } /// Generates an unknown type. pub fn unk() -> Typ { - factory.mk(RTyp::Unk) + factory.mk(RTyp::Unk) } /// Generates a datatype. pub fn dtyp(dtyp: dtyp::DTyp, prms: TPrmMap) -> Typ { - factory.mk( RTyp::DTyp { dtyp, prms } ) + factory.mk(RTyp::DTyp { dtyp, prms }) } /// A hash-consed type. -pub type Typ = HConsed ; - +pub type Typ = HConsed; /// Types. /// @@ -51,410 +49,412 @@ pub type Typ = HConsed ; /// "(Array Int (Array Int Int))" /// } /// ``` -#[ - derive( - Debug, Clone, - PartialEq, Eq, Hash, - PartialOrd, Ord - ) -] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum RTyp { - /// Unknown. - Unk, - /// Integers. - Int, - /// Reals. - Real, - /// Booleans. - Bool, - - /// Arrays. - Array { - /// Type of indices. - src: Typ, - /// Type of values. - tgt: Typ, - }, - - /// Datatype. - DTyp { - /// Original datatype. - dtyp: DTyp, - /// Type parameters. - prms: TPrmMap, - }, + /// Unknown. + Unk, + /// Integers. + Int, + /// Reals. + Real, + /// Booleans. + Bool, + + /// Arrays. + Array { + /// Type of indices. + src: Typ, + /// Type of values. + tgt: Typ, + }, + + /// Datatype. + DTyp { + /// Original datatype. + dtyp: DTyp, + /// Type parameters. + prms: TPrmMap, + }, } impl RTyp { - /// True if the type is bool. - pub fn is_bool(& self) -> bool { - * self == RTyp::Bool - } - /// True if the type is integer. - pub fn is_int(& self) -> bool { - * self == RTyp::Int - } - /// True if the type is real. - pub fn is_real(& self) -> bool { - * self == RTyp::Real - } - - /// True if the type is arithmetic. - pub fn is_arith(& self) -> bool { - match * self { - RTyp::Int | RTyp::Real => true, - _ => false, + /// True if the type is bool. + pub fn is_bool(&self) -> bool { + *self == RTyp::Bool } - } - - /// True if the type is an array. - pub fn is_array(& self) -> bool { - match * self { - RTyp::Array { .. } => true, - _ => false, + /// True if the type is integer. + pub fn is_int(&self) -> bool { + *self == RTyp::Int } - } - - /// True if the type is a datatype. - pub fn is_dtyp(& self) -> bool { - match * self { - RTyp::DTyp { .. } => true, - _ => false, + /// True if the type is real. + pub fn is_real(&self) -> bool { + *self == RTyp::Real } - } - /// Inspects an array type. - pub fn array_inspect(& self) -> Option<(& Typ, & Typ)> { - if let RTyp::Array { src, tgt } = self { - Some( (src, tgt) ) - } else { - None + /// True if the type is arithmetic. + pub fn is_arith(&self) -> bool { + match *self { + RTyp::Int | RTyp::Real => true, + _ => false, + } } - } - /// True if the type is unknown. - pub fn is_unk(& self) -> bool { - RTyp::Unk == * self - } + /// True if the type is an array. + pub fn is_array(&self) -> bool { + match *self { + RTyp::Array { .. } => true, + _ => false, + } + } - /// True if the type mentions an unknown type. - pub fn has_unk(& self) -> bool { - let mut stack = vec![ self ] ; - while let Some(curr) = stack.pop() { - match curr { - RTyp::Unk => return true, - RTyp::Array { src, tgt } => { - stack.push( src.get() ) ; - stack.push( tgt.get() ) - }, - RTyp::DTyp { prms, .. } => for typ in prms { - stack.push( typ.get() ) - }, - RTyp::Int | - RTyp::Real | - RTyp::Bool => (), - } + /// True if the type is a datatype. + pub fn is_dtyp(&self) -> bool { + match *self { + RTyp::DTyp { .. } => true, + _ => false, + } } - false - } - /// Returns the selectors of a constructor. - /// - /// Only legal on a datatype. - pub fn selectors_of(& self, constructor: & str) -> Res<& dtyp::CArgs> { - if let Some((dtyp, _)) = self.dtyp_inspect() { - dtyp.selectors_of(constructor) - } else { - bail!("cannot retrieve dtyp selectors on non-dtyp sort {}", self) + /// Inspects an array type. + pub fn array_inspect(&self) -> Option<(&Typ, &Typ)> { + if let RTyp::Array { src, tgt } = self { + Some((src, tgt)) + } else { + None + } } - } - /// Inspects a datatype type. - pub fn dtyp_inspect(& self) -> Option<(& DTyp, & TPrmMap)> { - if let RTyp::DTyp { dtyp, prms } = self { - Some( (dtyp, prms) ) - } else { - None + /// True if the type is unknown. + pub fn is_unk(&self) -> bool { + RTyp::Unk == *self } - } - /// Checks a type is legal. - pub fn check(& self) -> Res<()> { - match self { - RTyp::DTyp { dtyp, prms } => if dtyp.prms.len() != prms.len() { - bail!( - "datatype {} expects {} parameters, found {}", - conf.emph(& dtyp.name), dtyp.prms.len(), prms.len() - ) - }, - RTyp::Unk | RTyp::Array { .. } | - RTyp::Int | RTyp::Real | RTyp::Bool => (), + /// True if the type mentions an unknown type. + pub fn has_unk(&self) -> bool { + let mut stack = vec![self]; + while let Some(curr) = stack.pop() { + match curr { + RTyp::Unk => return true, + RTyp::Array { src, tgt } => { + stack.push(src.get()); + stack.push(tgt.get()) + } + RTyp::DTyp { prms, .. } => for typ in prms { + stack.push(typ.get()) + }, + RTyp::Int | RTyp::Real | RTyp::Bool => (), + } + } + false } - Ok(()) - } - /// True if the types are compatible. - /// - /// Two types are compatible if they're the same except for unknown subtypes. - /// - /// # Examples - /// - /// ```rust - /// use hoice::common::* ; - /// - /// let t_1 = typ::array( typ::int(), typ::unk() ) ; - /// let t_2 = typ::array( typ::unk(), typ::real() ) ; - /// debug_assert! { t_1.is_compatible(& t_2) } - /// ``` - pub fn is_compatible(& self, other: & Typ) -> bool { - if self == other.get() { return true } - - let mut stack = vec![ (self, other.get()) ] ; - while let Some((t_1, t_2)) = stack.pop() { - - match (t_1, t_2) { - (RTyp::Int, RTyp::Int) => (), - (RTyp::Real, RTyp::Real) => (), - (RTyp::Bool, RTyp::Bool) => (), - - (RTyp::Unk, _) | (_, RTyp::Unk) => (), - - ( - RTyp::Array { src: src_1, tgt: tgt_1 }, - RTyp::Array { src: src_2, tgt: tgt_2 }, - ) => { - stack.push( (src_1.get(), src_2.get()) ) ; - stack.push( (tgt_1.get(), tgt_2.get()) ) ; - }, - - ( - RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, - RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, - ) => if dtyp_1.name == dtyp_2.name { - for (t_1, t_2) in prms_1.iter().zip( prms_2.iter() ) { - stack.push( (t_1, t_2) ) - } + /// Returns the selectors of a constructor. + /// + /// Only legal on a datatype. + pub fn selectors_of(&self, constructor: &str) -> Res<&dtyp::CArgs> { + if let Some((dtyp, _)) = self.dtyp_inspect() { + dtyp.selectors_of(constructor) } else { - return false - }, - - (RTyp::Int, _) | - (RTyp::Real, _) | - (RTyp::Bool, _) | - (RTyp::Array { .. }, _) | - (RTyp::DTyp { .. }, _) => return false, - } - + bail!("cannot retrieve dtyp selectors on non-dtyp sort {}", self) + } } - true - } - - /// Merges two types. - /// - /// Basically removes unknown types when possible. Returns `None` if the - /// types are not compatible. - pub fn merge(& self, typ: & Typ) -> Option { - use std::slice::Iter ; - use std::iter::Zip ; - - enum Frame<'a, 'b> { - ArrayLft(& 'a Typ, & 'a Typ), - ArrayRgt(Typ), - DTyp( - dtyp::DTyp, dtyp::TPrmMap, Zip< Iter<'b, Typ>, Iter<'b, Typ> > - ), + /// Inspects a datatype type. + pub fn dtyp_inspect(&self) -> Option<(&DTyp, &TPrmMap)> { + if let RTyp::DTyp { dtyp, prms } = self { + Some((dtyp, prms)) + } else { + None + } } - let slf = factory.mk( self.clone() ) ; - - let (mut lft, mut rgt) = ( & slf, typ ) ; - let mut stack = vec![] ; - 'go_down: loop { - - let mut typ = match ( lft.get(), rgt.get() ) { - (RTyp::Unk, _) => rgt.clone(), - (_, RTyp::Unk) => lft.clone(), - - ( - RTyp::Array { src: src_1, tgt: tgt_1 }, - RTyp::Array { src: src_2, tgt: tgt_2 }, - ) => { - lft = src_1 ; - rgt = src_2 ; - stack.push( Frame::ArrayLft(tgt_1, tgt_2) ) ; - - continue 'go_down + /// Checks a type is legal. + pub fn check(&self) -> Res<()> { + match self { + RTyp::DTyp { dtyp, prms } => if dtyp.prms.len() != prms.len() { + bail!( + "datatype {} expects {} parameters, found {}", + conf.emph(&dtyp.name), + dtyp.prms.len(), + prms.len() + ) + }, + RTyp::Unk | RTyp::Array { .. } | RTyp::Int | RTyp::Real | RTyp::Bool => (), } + Ok(()) + } - ( - RTyp::DTyp { dtyp: dtyp_1, prms: prms_1 }, - RTyp::DTyp { dtyp: dtyp_2, prms: prms_2 }, - ) => if dtyp_1.name == dtyp_2.name && prms_1.len() == prms_2.len() { - debug_assert_eq! { prms_1.len(), prms_2.len() } - - let mut prms = prms_1.iter().zip( prms_2.iter() ) ; - - if let Some((l, r)) = prms.next() { - lft = l ; - rgt = r ; - - stack.push( - Frame::DTyp( - dtyp_1.clone(), Vec::with_capacity( prms_1.len() ).into(), prms - ) - ) ; - - continue 'go_down - } else { - lft.clone() - } - } else { - return None - }, - - (RTyp::Int, _) | - (RTyp::Real, _) | - (RTyp::Bool, _) | - (RTyp::Array { .. }, _) | - (RTyp::DTyp { .. }, _) => if lft == rgt { - lft.clone() - } else { - return None - }, - - } ; - - 'go_up: loop { - match stack.pop() { - - None => return Some(typ), - - Some( Frame::ArrayLft(l, r) ) => { - stack.push( Frame::ArrayRgt(typ) ) ; - lft = l ; - rgt = r ; - continue 'go_down - }, + /// True if the types are compatible. + /// + /// Two types are compatible if they're the same except for unknown subtypes. + /// + /// # Examples + /// + /// ```rust + /// use hoice::common::* ; + /// + /// let t_1 = typ::array( typ::int(), typ::unk() ) ; + /// let t_2 = typ::array( typ::unk(), typ::real() ) ; + /// debug_assert! { t_1.is_compatible(& t_2) } + /// ``` + pub fn is_compatible(&self, other: &Typ) -> bool { + if self == other.get() { + return true; + } - Some( Frame::ArrayRgt(src) ) => { - typ = array(src, typ) ; - continue 'go_up - } - - Some( - Frame::DTyp(dt, mut params, mut zip) - ) => { - params.push(typ) ; - if let Some((l, r)) = zip.next() { - stack.push( Frame::DTyp(dt, params, zip) ) ; - lft = l ; - rgt = r ; - continue 'go_down - } else { - typ = dtyp(dt, params) ; - continue 'go_up + let mut stack = vec![(self, other.get())]; + while let Some((t_1, t_2)) = stack.pop() { + match (t_1, t_2) { + (RTyp::Int, RTyp::Int) => (), + (RTyp::Real, RTyp::Real) => (), + (RTyp::Bool, RTyp::Bool) => (), + + (RTyp::Unk, _) | (_, RTyp::Unk) => (), + + ( + RTyp::Array { + src: src_1, + tgt: tgt_1, + }, + RTyp::Array { + src: src_2, + tgt: tgt_2, + }, + ) => { + stack.push((src_1.get(), src_2.get())); + stack.push((tgt_1.get(), tgt_2.get())); + } + + ( + RTyp::DTyp { + dtyp: dtyp_1, + prms: prms_1, + }, + RTyp::DTyp { + dtyp: dtyp_2, + prms: prms_2, + }, + ) => if dtyp_1.name == dtyp_2.name { + for (t_1, t_2) in prms_1.iter().zip(prms_2.iter()) { + stack.push((t_1, t_2)) + } + } else { + return false; + }, + + (RTyp::Int, _) + | (RTyp::Real, _) + | (RTyp::Bool, _) + | (RTyp::Array { .. }, _) + | (RTyp::DTyp { .. }, _) => return false, } - }, - } - } + true } - } - /// Default value of a type. - /// - /// Fails if the type is unknown. - pub fn default_val(& self) -> Val { - let typ = factory.mk( self.clone() ) ; - let mut current = typ ; - let mut stack = vec![] ; - - 'go_down: loop { - - let mut val = match current.clone().get() { - RTyp::Real => val::real( Rat::zero() ), - RTyp::Int => val::int( Int::zero() ), - RTyp::Bool => val::bool( true ), - RTyp::Array { ref src, ref tgt } => val::array( - src.clone(), tgt.default_val() - ), - RTyp::DTyp { dtyp, prms } => { - let mut args = vec![] ; - - for (_, arg_typ) in dtyp.news.get(& dtyp.default).expect( - "inconsistent datatype factory/map state" - ) { - let arg_typ = arg_typ.to_type(prms).unwrap_or_else( - |_| panic!("illegal type {}", current) - ) ; - args.push(arg_typ) - } - - let mut args = args.into_iter() ; - - if let Some(next) = args.next() { - stack.push( - ( current.clone(), dtyp.default.clone(), vec![], args ) - ) ; - current = next ; - continue 'go_down - } else { - val::dtyp_new( - current.clone(), dtyp.default.clone(), vec![] - ) - } - }, - RTyp::Unk => panic!("unknown type has no default value"), - } ; - - - 'go_up: loop { - match stack.pop() { - None => return val, - - Some( - (typ, default, mut args, mut prms) - ) => { - args.push(val) ; - if let Some(prm) = prms.next() { - stack.push( (typ, default, args, prms) ) ; - current = prm ; - continue 'go_down - } else { - val = val::dtyp_new( typ.clone(), default, args ) ; - continue 'go_up + /// Merges two types. + /// + /// Basically removes unknown types when possible. Returns `None` if the + /// types are not compatible. + pub fn merge(&self, typ: &Typ) -> Option { + use std::iter::Zip; + use std::slice::Iter; + + enum Frame<'a, 'b> { + ArrayLft(&'a Typ, &'a Typ), + ArrayRgt(Typ), + DTyp( + dtyp::DTyp, + dtyp::TPrmMap, + Zip, Iter<'b, Typ>>, + ), + } + let slf = factory.mk(self.clone()); + + let (mut lft, mut rgt) = (&slf, typ); + let mut stack = vec![]; + + 'go_down: loop { + let mut typ = match (lft.get(), rgt.get()) { + (RTyp::Unk, _) => rgt.clone(), + (_, RTyp::Unk) => lft.clone(), + + ( + RTyp::Array { + src: src_1, + tgt: tgt_1, + }, + RTyp::Array { + src: src_2, + tgt: tgt_2, + }, + ) => { + lft = src_1; + rgt = src_2; + stack.push(Frame::ArrayLft(tgt_1, tgt_2)); + + continue 'go_down; + } + + ( + RTyp::DTyp { + dtyp: dtyp_1, + prms: prms_1, + }, + RTyp::DTyp { + dtyp: dtyp_2, + prms: prms_2, + }, + ) => if dtyp_1.name == dtyp_2.name && prms_1.len() == prms_2.len() { + debug_assert_eq! { prms_1.len(), prms_2.len() } + + let mut prms = prms_1.iter().zip(prms_2.iter()); + + if let Some((l, r)) = prms.next() { + lft = l; + rgt = r; + + stack.push(Frame::DTyp( + dtyp_1.clone(), + Vec::with_capacity(prms_1.len()).into(), + prms, + )); + + continue 'go_down; + } else { + lft.clone() + } + } else { + return None; + }, + + (RTyp::Int, _) + | (RTyp::Real, _) + | (RTyp::Bool, _) + | (RTyp::Array { .. }, _) + | (RTyp::DTyp { .. }, _) => if lft == rgt { + lft.clone() + } else { + return None; + }, + }; + + 'go_up: loop { + match stack.pop() { + None => return Some(typ), + + Some(Frame::ArrayLft(l, r)) => { + stack.push(Frame::ArrayRgt(typ)); + lft = l; + rgt = r; + continue 'go_down; + } + + Some(Frame::ArrayRgt(src)) => { + typ = array(src, typ); + continue 'go_up; + } + + Some(Frame::DTyp(dt, mut params, mut zip)) => { + params.push(typ); + if let Some((l, r)) = zip.next() { + stack.push(Frame::DTyp(dt, params, zip)); + lft = l; + rgt = r; + continue 'go_down; + } else { + typ = dtyp(dt, params); + continue 'go_up; + } + } + } } - }, } - } + } + /// Default value of a type. + /// + /// Fails if the type is unknown. + pub fn default_val(&self) -> Val { + let typ = factory.mk(self.clone()); + let mut current = typ; + let mut stack = vec![]; + + 'go_down: loop { + let mut val = match current.clone().get() { + RTyp::Real => val::real(Rat::zero()), + RTyp::Int => val::int(Int::zero()), + RTyp::Bool => val::bool(true), + RTyp::Array { ref src, ref tgt } => val::array(src.clone(), tgt.default_val()), + RTyp::DTyp { dtyp, prms } => { + let mut args = vec![]; + + for (_, arg_typ) in dtyp + .news + .get(&dtyp.default) + .expect("inconsistent datatype factory/map state") + { + let arg_typ = arg_typ + .to_type(prms) + .unwrap_or_else(|_| panic!("illegal type {}", current)); + args.push(arg_typ) + } + + let mut args = args.into_iter(); + + if let Some(next) = args.next() { + stack.push((current.clone(), dtyp.default.clone(), vec![], args)); + current = next; + continue 'go_down; + } else { + val::dtyp_new(current.clone(), dtyp.default.clone(), vec![]) + } + } + RTyp::Unk => panic!("unknown type has no default value"), + }; + + 'go_up: loop { + match stack.pop() { + None => return val, + + Some((typ, default, mut args, mut prms)) => { + args.push(val); + if let Some(prm) = prms.next() { + stack.push((typ, default, args, prms)); + current = prm; + continue 'go_down; + } else { + val = val::dtyp_new(typ.clone(), default, args); + continue 'go_up; + } + } + } + } + } } - } - /// Default term of a type. - /// - /// Fails if the type is unknown. - pub fn default_term(& self) -> Term { - match * self { - RTyp::Real => term::real( Rat::zero() ), - RTyp::Int => term::int( Int::zero() ), - RTyp::Bool => term::bool( true ), - RTyp::Array { ref src, ref tgt } => term::cst_array( - src.clone(), tgt.default_term() - ), - RTyp::DTyp { .. } => unimplemented!(), - RTyp::Unk => panic!("unknown type has no default term"), + /// Default term of a type. + /// + /// Fails if the type is unknown. + pub fn default_term(&self) -> Term { + match *self { + RTyp::Real => term::real(Rat::zero()), + RTyp::Int => term::int(Int::zero()), + RTyp::Bool => term::bool(true), + RTyp::Array { ref src, ref tgt } => term::cst_array(src.clone(), tgt.default_term()), + RTyp::DTyp { .. } => unimplemented!(), + RTyp::Unk => panic!("unknown type has no default term"), + } } - } } impl ::rsmt2::print::Sort2Smt for RTyp { - fn sort_to_smt2( - & self, w: &mut Writer - ) -> SmtRes<()> where Writer: Write { - write!(w, "{}", self) ? ; - Ok(()) - } + fn sort_to_smt2(&self, w: &mut Writer) -> SmtRes<()> + where + Writer: Write, + { + write!(w, "{}", self)?; + Ok(()) + } } impl_fmt!{ @@ -497,4 +497,4 @@ impl_fmt!{ Ok(()) } -} \ No newline at end of file +} diff --git a/src/term/zip.rs b/src/term/zip.rs index d2a7c79f..1c5afe6e 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -4,17 +4,10 @@ //! //! - explain -use std::fmt ; -use std::slice::Iter ; - -use common::* ; - - - - - - +use std::fmt; +use std::slice::Iter; +use common::*; /// The direction of the next step. /// @@ -22,378 +15,408 @@ use common::* ; /// telling it which way it will go next. #[derive(Clone, Debug)] pub enum ZipDo<'a, Acc, Yield> { - /// Remember a frame and inspect a term. - Trm { - /// The new term to work on. - nu_term: & 'a Term, - /// The frame (application) inside of which we are. - frame: ZipFrame<'a, Acc>, - }, - - /// Go down. Means "skip the current application and go directly to this". - /// - /// Example: when evaluating an `(ite c t e)` application: given the value of - /// `c`, when want to go down into `t` or `e` directly, not evaluate both. - Dwn { - /// The term we're going down in. - nu_term: & 'a Term, - }, - - /// Go up. Means "here is the result, skip everything else at this level". - /// - /// Example: when evaluating a `(* a0 a1 ...)` application. If `a0` evaluates - /// to `0` then we can go up directly with `0`. - Upp { - /// The result to propagate upwards. - yielded: Yield, - }, + /// Remember a frame and inspect a term. + Trm { + /// The new term to work on. + nu_term: &'a Term, + /// The frame (application) inside of which we are. + frame: ZipFrame<'a, Acc>, + }, + + /// Go down. Means "skip the current application and go directly to this". + /// + /// Example: when evaluating an `(ite c t e)` application: given the value of + /// `c`, when want to go down into `t` or `e` directly, not evaluate both. + Dwn { + /// The term we're going down in. + nu_term: &'a Term, + }, + + /// Go up. Means "here is the result, skip everything else at this level". + /// + /// Example: when evaluating a `(* a0 a1 ...)` application. If `a0` evaluates + /// to `0` then we can go up directly with `0`. + Upp { + /// The result to propagate upwards. + yielded: Yield, + }, } - - /// The direction of the next step, total version. /// /// This is what the user produces when zipping up an application and all of /// its arguments. pub enum ZipDoTotal<'a, Yield> { - /// Going down. - /// - /// The only use case here is function application. - Dwn { - /// Term we're going down in. - nu_term: & 'a Term, - /// An optional substitution. + /// Going down. /// - /// The only case where this is useful currently is when evaluating a - /// function application. - nu_subst: Option< VarMap >, - }, - - /// Go up. Result. - Upp { - /// The result to propagate upwards. - yielded: Yield, - }, + /// The only use case here is function application. + Dwn { + /// Term we're going down in. + nu_term: &'a Term, + /// An optional substitution. + /// + /// The only case where this is useful currently is when evaluating a + /// function application. + nu_subst: Option>, + }, + + /// Go up. Result. + Upp { + /// The result to propagate upwards. + yielded: Yield, + }, } - /// The operators manipulated by the zipper. #[derive(Clone, Copy, Debug)] pub enum ZipOp<'a> { - /// An operator. - Op(Op), - /// A datatype constructor. - New(& 'a String), - /// A function application. - Fun(& 'a String), - /// A constant array construction. - CArray, - /// A datatype selection. - Slc(& 'a String), - /// A datatype tester. - Tst(& 'a String), + /// An operator. + Op(Op), + /// A datatype constructor. + New(&'a String), + /// A function application. + Fun(&'a String), + /// A constant array construction. + CArray, + /// A datatype selection. + Slc(&'a String), + /// A datatype tester. + Tst(&'a String), } impl<'a> ::std::fmt::Display for ZipOp<'a> { - fn fmt(& self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - ZipOp::Op(inner) => write!(fmt, "Op({})", inner), - ZipOp::New(inner) => write!(fmt, "New({})", inner), - ZipOp::Fun(inner) => write!(fmt, "Fun({})", inner), - ZipOp::Slc(inner) => write!(fmt, "Slc({})", inner), - ZipOp::Tst(inner) => write!(fmt, "Tst({})", inner), - ZipOp::CArray => write!(fmt, "array"), + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match self { + ZipOp::Op(inner) => write!(fmt, "Op({})", inner), + ZipOp::New(inner) => write!(fmt, "New({})", inner), + ZipOp::Fun(inner) => write!(fmt, "Fun({})", inner), + ZipOp::Slc(inner) => write!(fmt, "Slc({})", inner), + ZipOp::Tst(inner) => write!(fmt, "Tst({})", inner), + ZipOp::CArray => write!(fmt, "array"), + } } - } } - // Nullary things the zipper can manipulate. #[derive(Clone, Copy, Debug)] pub enum ZipNullary<'a> { - /// A constant. - Cst(& 'a Val), - /// A variable. - Var(& 'a Typ, VarIdx), + /// A constant. + Cst(&'a Val), + /// A variable. + Var(&'a Typ, VarIdx), } impl<'a> fmt::Display for ZipNullary<'a> { - fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result { - match self { - ZipNullary::Cst(val) => write!(fmt, "{}", val), - ZipNullary::Var(typ, var) => write!(fmt, "v_{}<{}>", var ,typ), + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + ZipNullary::Cst(val) => write!(fmt, "{}", val), + ZipNullary::Var(typ, var) => write!(fmt, "v_{}<{}>", var, typ), + } } - } } - /// A frame in the zipper. /// /// This is both what the zipper manipulates and what the user's function will /// take as input. #[derive(Clone, Debug)] pub struct ZipFrame<'a, Acc> { - /// The thing being applied. - pub thing: ZipOp<'a>, - /// The type of the application. - pub typ: & 'a Typ, - /// The arguments that have already been handled. - pub lft_args: Acc, - /// The arguments that have not already been handled. - pub rgt_args: Iter<'a, Term>, + /// The thing being applied. + pub thing: ZipOp<'a>, + /// The type of the application. + pub typ: &'a Typ, + /// The arguments that have already been handled. + pub lft_args: Acc, + /// The arguments that have not already been handled. + pub rgt_args: Iter<'a, Term>, } - - /// Accumulator trait. /// /// The goal is to let the zipper know how to construct an empty accumulator. pub trait Accumulator { - /// Creates an empty accumulator. - fn new_empty(usize) -> Self ; - /// Pushes a new element in the accumulator. - fn push(& mut self, Elem) ; + /// Creates an empty accumulator. + fn new_empty(usize) -> Self; + /// Pushes a new element in the accumulator. + fn push(&mut self, Elem); } impl Accumulator for Vec { - fn new_empty(hint: usize) -> Self { Vec::with_capacity(hint) } - fn push(& mut self, elem: T) { self.push(elem) } + fn new_empty(hint: usize) -> Self { + Vec::with_capacity(hint) + } + fn push(&mut self, elem: T) { + self.push(elem) + } } impl Accumulator<()> for () { - fn new_empty(_: usize) -> Self {} - fn push(& mut self, _: ()) {} + fn new_empty(_: usize) -> Self {} + fn push(&mut self, _: ()) {} } - - /// Zip function. pub fn zip( - term: & Term, mut nul_do: NulF, mut app_do: AppF, mut partial: Partial + term: &Term, + mut nul_do: NulF, + mut app_do: AppF, + mut partial: Partial, ) -> Result where -Acc: Accumulator, Yield: Clone, - -NulF: for<'a> FnMut( - ZipNullary<'a> -) -> Result< Yield, E >, - -AppF: for<'a> FnMut( - ZipOp<'a>, & 'a Typ, Acc -) -> Result< ZipDoTotal<'a, Yield>, E >, - -Partial: for<'a> FnMut( - ZipFrame<'a, Acc> -) -> Result< ZipDo<'a, Acc, Yield>, E > { - // Empty vector of terms, useful when handling unary operators. - let empty: Vec = Vec::with_capacity(0) ; - - // Current term we're going down in. - let mut term = term ; - - // Stack of `ZipFrame`. - let mut stack: Vec<_> = Vec::with_capacity(7) ; - // The current substitution, if any. - let mut subst: Option< VarMap > = None ; - - // macro_rules! stack_print { - // () => ({ - // println!("stack {{") ; - // for (ZipFrame { thing, rgt_args, .. }, _) in & stack { - // println!(" {} > {}", thing, rgt_args.len()) - // } - // println!("}}") - // }) ; - // } - - 'inspect_term: loop { - // stack_print!() ; - - let result = match * term.get() { - - RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { - ZipDoTotal::Upp { - yielded: subst[var_idx].clone(), - } - } else { - ZipDoTotal::Upp { - yielded: nul_do( ZipNullary::Var(typ, var_idx) ) ?, - } - }, - - RTerm::Cst(ref cst) => ZipDoTotal::Upp { - yielded: nul_do( ZipNullary::Cst(cst) ) ?, - }, - - RTerm::CArray { ref typ, term: ref nu_term } => { - let frame = ZipFrame { - thing: ZipOp::CArray, typ, - lft_args: Acc::new_empty(1), - rgt_args: empty.iter(), - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - }, - - RTerm::App { op, ref typ, ref args } => { - let mut rgt_args = args.iter() ; - let op = ZipOp::Op(op) ; - let lft_args = Acc::new_empty( args.len() ) ; - - if let Some(nu_term) = rgt_args.next() { - let frame = ZipFrame { - thing: op, typ, lft_args, rgt_args, - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - - } else { - app_do(op, typ, lft_args) ? - } - }, - - RTerm::DTypNew { ref typ, ref name, ref args } => { - let mut rgt_args = args.iter() ; - let op = ZipOp::New(name) ; - let lft_args = Acc::new_empty( args.len() ) ; - - if let Some(nu_term) = rgt_args.next() { - let frame = ZipFrame { - thing: op, typ, lft_args, rgt_args, - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - - } else { - app_do(op, typ, lft_args) ? - } - }, - - RTerm::DTypSlc { ref typ, ref name, term: ref nu_term } => { - let mut rgt_args = empty.iter() ; - let op = ZipOp::Slc(name) ; - let lft_args = Acc::new_empty(1) ; - - let frame = ZipFrame { - thing: op, typ, lft_args, rgt_args, - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - }, - - RTerm::DTypTst { ref typ, ref name, term: ref nu_term } => { - let mut rgt_args = empty.iter() ; - let op = ZipOp::Tst(name) ; - let lft_args = Acc::new_empty(1) ; - - let frame = ZipFrame { - thing: op, typ, lft_args, rgt_args, - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - }, - - RTerm::Fun { ref typ, ref name, ref args } => { - let mut rgt_args = args.iter() ; - let op = ZipOp::Fun(name) ; - let lft_args = Acc::new_empty( args.len() ) ; - - if let Some(nu_term) = rgt_args.next() { - let frame = ZipFrame { - thing: op, typ, lft_args, rgt_args, - } ; - stack.push( (frame, subst.clone()) ) ; - term = nu_term ; - - continue 'inspect_term - - } else { - app_do(op, typ, lft_args) ? - } - }, - - } ; - - let mut result = match result { - ZipDoTotal::Dwn { nu_term, nu_subst } => { - if nu_subst.is_some() { - subst = nu_subst - } - term = nu_term ; - - continue 'inspect_term - }, - - ZipDoTotal::Upp { yielded } => yielded - } ; - - 'inspect_do_res: loop { - // stack_print!() ; - - match stack.pop() { - - // Done, we're at top level. - None => return Ok(result), + Acc: Accumulator, + Yield: Clone, + + NulF: for<'a> FnMut(ZipNullary<'a>) -> Result, + + AppF: for<'a> FnMut(ZipOp<'a>, &'a Typ, Acc) -> Result, E>, + + Partial: for<'a> FnMut(ZipFrame<'a, Acc>) -> Result, E>, +{ + // Empty vector of terms, useful when handling unary operators. + let empty: Vec = Vec::with_capacity(0); + + // Current term we're going down in. + let mut term = term; + + // Stack of `ZipFrame`. + let mut stack: Vec<_> = Vec::with_capacity(7); + // The current substitution, if any. + let mut subst: Option> = None; + + // macro_rules! stack_print { + // () => ({ + // println!("stack {{") ; + // for (ZipFrame { thing, rgt_args, .. }, _) in & stack { + // println!(" {} > {}", thing, rgt_args.len()) + // } + // println!("}}") + // }) ; + // } + + 'inspect_term: loop { + // stack_print!() ; + + let result = match *term.get() { + RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { + ZipDoTotal::Upp { + yielded: subst[var_idx].clone(), + } + } else { + ZipDoTotal::Upp { + yielded: nul_do(ZipNullary::Var(typ, var_idx))?, + } + }, + + RTerm::Cst(ref cst) => ZipDoTotal::Upp { + yielded: nul_do(ZipNullary::Cst(cst))?, + }, + + RTerm::CArray { + ref typ, + term: ref nu_term, + } => { + let frame = ZipFrame { + thing: ZipOp::CArray, + typ, + lft_args: Acc::new_empty(1), + rgt_args: empty.iter(), + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } - // Work on the next frame. - Some( - (ZipFrame { thing, typ, mut lft_args, rgt_args }, old_subst) - ) => { - subst = old_subst ; + RTerm::App { + op, + ref typ, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::Op(op); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? + } + } - // Update left args. - lft_args.push( result ) ; + RTerm::DTypNew { + ref typ, + ref name, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::New(name); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? + } + } - if rgt_args.len() == 0 { + RTerm::DTypSlc { + ref typ, + ref name, + term: ref nu_term, + } => { + let mut rgt_args = empty.iter(); + let op = ZipOp::Slc(name); + let lft_args = Acc::new_empty(1); + + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } - match app_do(thing, typ, lft_args) ? { - ZipDoTotal::Upp { yielded } => { - result = yielded ; - continue 'inspect_do_res - }, + RTerm::DTypTst { + ref typ, + ref name, + term: ref nu_term, + } => { + let mut rgt_args = empty.iter(); + let op = ZipOp::Tst(name); + let lft_args = Acc::new_empty(1); + + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } - ZipDoTotal::Dwn { nu_term, nu_subst} => { - if nu_subst.is_some() { - subst = nu_subst + RTerm::Fun { + ref typ, + ref name, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::Fun(name); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? } - term = nu_term ; - continue 'inspect_term - }, } + }; - } else { - - match partial( - ZipFrame { thing, typ, lft_args, rgt_args } - ) ? { - - ZipDo::Trm { nu_term, frame } => { - term = nu_term ; - stack.push( (frame, subst.clone()) ) ; - continue 'inspect_term - }, + let mut result = match result { + ZipDoTotal::Dwn { nu_term, nu_subst } => { + if nu_subst.is_some() { + subst = nu_subst + } + term = nu_term; - ZipDo::Dwn { nu_term } => { - term = nu_term ; - continue 'inspect_term - }, + continue 'inspect_term; + } - ZipDo::Upp { yielded } => { - result = yielded ; - continue 'inspect_do_res - }, + ZipDoTotal::Upp { yielded } => yielded, + }; + + 'inspect_do_res: loop { + // stack_print!() ; + + match stack.pop() { + // Done, we're at top level. + None => return Ok(result), + + // Work on the next frame. + Some(( + ZipFrame { + thing, + typ, + mut lft_args, + rgt_args, + }, + old_subst, + )) => { + subst = old_subst; + + // Update left args. + lft_args.push(result); + + if rgt_args.len() == 0 { + match app_do(thing, typ, lft_args)? { + ZipDoTotal::Upp { yielded } => { + result = yielded; + continue 'inspect_do_res; + } + + ZipDoTotal::Dwn { nu_term, nu_subst } => { + if nu_subst.is_some() { + subst = nu_subst + } + term = nu_term; + continue 'inspect_term; + } + } + } else { + match partial(ZipFrame { + thing, + typ, + lft_args, + rgt_args, + })? { + ZipDo::Trm { nu_term, frame } => { + term = nu_term; + stack.push((frame, subst.clone())); + continue 'inspect_term; + } + + ZipDo::Dwn { nu_term } => { + term = nu_term; + continue 'inspect_term; + } + + ZipDo::Upp { yielded } => { + result = yielded; + continue 'inspect_do_res; + } + } + } + } } - } - }, - } + } } - } } - - diff --git a/src/unsat_core/mod.rs b/src/unsat_core/mod.rs index ec2929fd..a3d7ffa8 100644 --- a/src/unsat_core/mod.rs +++ b/src/unsat_core/mod.rs @@ -2,117 +2,116 @@ //! //! Currently inactive. -use common::* ; +use common::*; -pub mod sample_graph ; +pub mod sample_graph; -pub use self::sample_graph::SampleGraph ; -use self::sample_graph::UnsatProof ; +pub use self::sample_graph::SampleGraph; +use self::sample_graph::UnsatProof; /// An unsat result. pub enum UnsatRes { - /// Unsat cores were not active. - None, - /// A sample dependency graph: raw result from teacher. - Graph(SampleGraph), - /// A proof, obtained from a graph. - Proof(UnsatProof), - /// An unsat result from a single clause. - Clause(ClsIdx), + /// Unsat cores were not active. + None, + /// A sample dependency graph: raw result from teacher. + Graph(SampleGraph), + /// A proof, obtained from a graph. + Proof(UnsatProof), + /// An unsat result from a single clause. + Clause(ClsIdx), } impl UnsatRes { - /// Constructor. - pub fn new(graph: Option) -> Self { - if let Some(graph) = graph { - UnsatRes::Graph(graph) - } else { - UnsatRes::None + /// Constructor. + pub fn new(graph: Option) -> Self { + if let Some(graph) = graph { + UnsatRes::Graph(graph) + } else { + UnsatRes::None + } } - } - /// True if none. - pub fn is_none(& self) -> bool { - match self { UnsatRes::None => true, _ => false } - } - - /// Retrieves the unsat core. - fn get_core(& mut self, instance: & Instance) -> Res { - let (nu_self, res) = match self { - UnsatRes::None => bail!( - "cannot produce unsat cores without `{}`", - conf.emph("(set-option :produce-unsat-cores true)") - ), - UnsatRes::Graph(graph) => { - let proof = graph.get_proof(instance) ? ; - let core = proof.core() ; + /// True if none. + pub fn is_none(&self) -> bool { + match self { + UnsatRes::None => true, + _ => false, + } + } - ( UnsatRes::Proof(proof), core ) - }, - UnsatRes::Proof(proof) => return Ok( proof.core() ), - UnsatRes::Clause(clause) => { - let mut set = ClsSet::new() ; - set.insert(* clause) ; - return Ok(set) - }, - } ; + /// Retrieves the unsat core. + fn get_core(&mut self, instance: &Instance) -> Res { + let (nu_self, res) = match self { + UnsatRes::None => bail!( + "cannot produce unsat cores without `{}`", + conf.emph("(set-option :produce-unsat-cores true)") + ), + UnsatRes::Graph(graph) => { + let proof = graph.get_proof(instance)?; + let core = proof.core(); - * self = nu_self ; + (UnsatRes::Proof(proof), core) + } + UnsatRes::Proof(proof) => return Ok(proof.core()), + UnsatRes::Clause(clause) => { + let mut set = ClsSet::new(); + set.insert(*clause); + return Ok(set); + } + }; - Ok(res) - } + *self = nu_self; - /// Writes the unsat core. - pub fn write_core( - & mut self, w: & mut W, instance: & Instance - ) -> Res<()> { - let core = self.get_core(& instance) ? ; - if ! instance.unsat_cores() { - bail!( - "cannot produce unsat cores without `{}`", - conf.emph("(set-option :produce-unsat-cores true)") - ) + Ok(res) } - write!(w, "(") ? ; - for clause in core { - if let Some(name) = instance.name_of_old_clause(clause) { - write!(w, " {}", name) ? - } + + /// Writes the unsat core. + pub fn write_core(&mut self, w: &mut W, instance: &Instance) -> Res<()> { + let core = self.get_core(&instance)?; + if !instance.unsat_cores() { + bail!( + "cannot produce unsat cores without `{}`", + conf.emph("(set-option :produce-unsat-cores true)") + ) + } + write!(w, "(")?; + for clause in core { + if let Some(name) = instance.name_of_old_clause(clause) { + write!(w, " {}", name)? + } + } + writeln!(w, " )")?; + Ok(()) } - writeln!(w, " )") ? ; - Ok(()) - } - /// Writes an unsat proof. - pub fn write_proof( - & mut self, w: & mut W, instance: & Instance - ) -> Res<()> { - let err = || ErrorKind::from( - format!( - "cannot produce proof without `{}`", - conf.emph("(set-option :produce-proofs true)") - ) - ) ; - let nu_self = match self { - _ if ! instance.proofs() => bail!( err() ), - UnsatRes::None => bail!( err() ), - UnsatRes::Graph(graph) => { - let proof = graph.get_proof(instance) ? ; - proof.write(w, instance) ? ; + /// Writes an unsat proof. + pub fn write_proof(&mut self, w: &mut W, instance: &Instance) -> Res<()> { + let err = || { + ErrorKind::from(format!( + "cannot produce proof without `{}`", + conf.emph("(set-option :produce-proofs true)") + )) + }; + let nu_self = match self { + _ if !instance.proofs() => bail!(err()), + UnsatRes::None => bail!(err()), + UnsatRes::Graph(graph) => { + let proof = graph.get_proof(instance)?; + proof.write(w, instance)?; - UnsatRes::Proof(proof) - }, - UnsatRes::Proof(proof) => { - proof.write(w, instance) ? ; - return Ok(()) - }, - UnsatRes::Clause(_) => { - writeln!(w, "( () () () )") ? ; - return Ok(()) - }, - } ; + UnsatRes::Proof(proof) + } + UnsatRes::Proof(proof) => { + proof.write(w, instance)?; + return Ok(()); + } + UnsatRes::Clause(_) => { + writeln!(w, "( () () () )")?; + return Ok(()); + } + }; - * self = nu_self ; + *self = nu_self; - Ok(()) - } + Ok(()) + } } diff --git a/src/unsat_core/sample_graph.rs b/src/unsat_core/sample_graph.rs index 146d9ccf..fae02a5a 100644 --- a/src/unsat_core/sample_graph.rs +++ b/src/unsat_core/sample_graph.rs @@ -2,46 +2,48 @@ #![allow(dead_code)] -use std::borrow::Borrow ; +use std::borrow::Borrow; use common::{ - *, - // smt::FullParser as Parser, - // var_to::vals::{ VarValsMap, VarValsSet }, - var_to::vals::VarValsMap, -} ; - -use unsat_core::* ; + // smt::FullParser as Parser, + // var_to::vals::{ VarValsMap, VarValsSet }, + var_to::vals::VarValsMap, + *, +}; +use unsat_core::*; /// Maps term arguments to concrete ones. -pub type TArgMap = HConMap ; +pub type TArgMap = HConMap; /// Maps term arguments to Origins. -pub type OTArgMap = HConMap> ; +pub type OTArgMap = HConMap>; /// An optional rhs. -pub type Rhs = Option<(PrdIdx, VarTerms, VarVals)> ; - +pub type Rhs = Option<(PrdIdx, VarTerms, VarVals)>; /// The origin of a sample is a clause and some samples for activating the rhs. -type Origin = (ClsIdx, PrdHMap< TArgMap >) ; - - +type Origin = (ClsIdx, PrdHMap); /// Polarity of a sample: positive or negative. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Polarity { - /// Positive? - pos: bool + /// Positive? + pos: bool, } impl Polarity { - /// Positive constructor. - pub fn pos() -> Self { Polarity { pos: true } } - /// Negative constructor. - pub fn neg() -> Self { Polarity { pos: false } } + /// Positive constructor. + pub fn pos() -> Self { + Polarity { pos: true } + } + /// Negative constructor. + pub fn neg() -> Self { + Polarity { pos: false } + } - /// True if the polarity is positive. - pub fn is_pos(self) -> bool { self.pos } + /// True if the polarity is positive. + pub fn is_pos(self) -> bool { + self.pos + } } impl_fmt! { Polarity(self, fmt) { @@ -49,238 +51,239 @@ impl_fmt! { } } - - - /// Known samples: positive or negative. #[derive(Default)] pub struct KnownSamples { - /// Positive samples. - pos: PrdHMap< VarValsMap<(VarTerms, Origin)> >, - /// Negative samples. - neg: PrdHMap< VarValsMap<(Rhs, Origin)> >, + /// Positive samples. + pos: PrdHMap>, + /// Negative samples. + neg: PrdHMap>, } impl KnownSamples { - /// Constructor. - pub fn new() -> Self { - KnownSamples { - pos: PrdHMap::new(), - neg: PrdHMap::new(), - } - } - - /// Writes itself. - pub fn write(& self, w: & mut W, pref: & str) -> Res<()> { - writeln!(w, "{}pos {{", pref) ? ; - for (pred, map) in & self.pos { - for (args, & (_, (clause, ref lhs))) in map { - write!(w, "{} ({} {}) <=#{}=", pref, pred, args, clause) ? ; - for (pred, argss) in lhs { - for (_, args) in argss { - write!(w, " ({} {})", pred, args) ? - } + /// Constructor. + pub fn new() -> Self { + KnownSamples { + pos: PrdHMap::new(), + neg: PrdHMap::new(), } - writeln!(w) ? - } } - writeln!(w, "{}}}", pref) ? ; - writeln!(w, "{}neg {{", pref) ? ; - for (pred, map) in & self.neg { - for (args, & (ref rhs, (clause, ref lhs))) in map { - write!(w, "{} ({} {}) from ", pref, pred, args) ? ; - if let Some((pred, _, ref args)) = rhs { - write!(w, "({} {})", pred, args) ? - } else { - write!(w, "false") ? + + /// Writes itself. + pub fn write(&self, w: &mut W, pref: &str) -> Res<()> { + writeln!(w, "{}pos {{", pref)?; + for (pred, map) in &self.pos { + for (args, &(_, (clause, ref lhs))) in map { + write!(w, "{} ({} {}) <=#{}=", pref, pred, args, clause)?; + for (pred, argss) in lhs { + for (_, args) in argss { + write!(w, " ({} {})", pred, args)? + } + } + writeln!(w)? + } } - write!(w, " <=#{}=", clause) ? ; - for (pred, argss) in lhs { - for (_, args) in argss { - write!(w, " ({} {})", pred, args) ? - } + writeln!(w, "{}}}", pref)?; + writeln!(w, "{}neg {{", pref)?; + for (pred, map) in &self.neg { + for (args, &(ref rhs, (clause, ref lhs))) in map { + write!(w, "{} ({} {}) from ", pref, pred, args)?; + if let Some((pred, _, ref args)) = rhs { + write!(w, "({} {})", pred, args)? + } else { + write!(w, "false")? + } + write!(w, " <=#{}=", clause)?; + for (pred, argss) in lhs { + for (_, args) in argss { + write!(w, " ({} {})", pred, args)? + } + } + writeln!(w)? + } } - writeln!(w) ? - } + writeln!(w, "{}}}", pref)?; + Ok(()) } - writeln!(w, "{}}}", pref) ? ; - Ok(()) - } - - /// Number of positive samples of a predicate. - pub fn pos_len>(& self, pred: P) -> usize { - self.pos.get( pred.borrow() ).map( - |args| args.len() - ).unwrap_or(0) - } - /// Number of negative samples of a predicate. - pub fn neg_len>(& self, pred: P) -> usize { - self.neg.get( pred.borrow() ).map( - |args| args.len() - ).unwrap_or(0) - } - - /// Gets the origin of a positive sample. - pub fn get_pos>( - & self, pred: P, args: & VarVals - ) -> Option<& (VarTerms, Origin)> { - self.pos.get( pred.borrow() ).and_then( - |map| map.get(args) - ) - } - /// True if the sample is known to be positive. - pub fn is_pos>( - & self, pred: P, args: & VarVals - ) -> bool { - self.get_pos(pred, args).is_some() - } - - /// Gets the origin of a negative sample. - pub fn get_neg>( - & self, pred: P, args: & VarVals - ) -> Option<& (Rhs, Origin)> { - self.neg.get( pred.borrow() ).and_then( - |map| map.get(args) - ) - } - /// True if the sample is known to be negative. - pub fn is_neg>( - & self, pred: P, args: & VarVals - ) -> bool { - self.get_neg(pred, args).is_some() - } - /// Gets the orign of a sample. - pub fn get>( - & self, polarity: Polarity, pred: P, args: & VarVals - ) -> Option<(Rhs, Origin)> { - if polarity.is_pos() { - self.get_pos(pred.borrow(), args).map( - |& (ref fargs, ref origin)| ( - Some((* pred.borrow(), fargs.clone(), args.clone())), origin.clone() - ) - ) - } else { - self.get_neg(pred, args).map( - |(rhs, origin)| (rhs.clone(), origin.clone()) - ) + /// Number of positive samples of a predicate. + pub fn pos_len>(&self, pred: P) -> usize { + self.pos + .get(pred.borrow()) + .map(|args| args.len()) + .unwrap_or(0) + } + /// Number of negative samples of a predicate. + pub fn neg_len>(&self, pred: P) -> usize { + self.neg + .get(pred.borrow()) + .map(|args| args.len()) + .unwrap_or(0) } - } - - /// Registers a positive sample. - /// - /// Returns a sample related to this one but registered negative if any. - /// - /// If the sample is already registered as positive, overwrites the old - /// origin. - pub fn add_pos( - & mut self, pred: PrdIdx, fargs: VarTerms, args: VarVals, origin: Origin - ) -> Option { - let res = self.neg.get(& pred).and_then( - |map| map.keys().find( - |other| other.is_related_to(& args) - ) - ).cloned() ; - - self.pos.entry(pred).or_insert_with( - VarValsMap::new - ).insert(args, (fargs, origin)) ; - - res - } - /// Registers a negative sample. - /// - /// Returns a sample related to this one but registered positive if any. - /// - /// If the sample is already registered as negative, overwrites the old - /// origin. - pub fn add_neg( - & mut self, pred: PrdIdx, args: VarVals, - rhs: Rhs, origin: Origin - ) -> Option { - let res = self.pos.get(& pred).and_then( - |map| map.keys().find( - |other| other.is_related_to(& args) - ) - ).cloned() ; - - self.neg.entry(pred).or_insert_with( - VarValsMap::new - ).insert(args, (rhs, origin)) ; - - res - } + /// Gets the origin of a positive sample. + pub fn get_pos>( + &self, + pred: P, + args: &VarVals, + ) -> Option<&(VarTerms, Origin)> { + self.pos.get(pred.borrow()).and_then(|map| map.get(args)) + } + /// True if the sample is known to be positive. + pub fn is_pos>(&self, pred: P, args: &VarVals) -> bool { + self.get_pos(pred, args).is_some() + } + /// Gets the origin of a negative sample. + pub fn get_neg>(&self, pred: P, args: &VarVals) -> Option<&(Rhs, Origin)> { + self.neg.get(pred.borrow()).and_then(|map| map.get(args)) + } + /// True if the sample is known to be negative. + pub fn is_neg>(&self, pred: P, args: &VarVals) -> bool { + self.get_neg(pred, args).is_some() + } - pub fn update( - & mut self, rhs: Option<(PrdIdx, & VarTerms, & VarVals)>, - lhs: & PrdHMap, - clause: ClsIdx, - ) -> Either< - Option, (PrdIdx, VarVals, VarVals) - > { - macro_rules! done { - (trivial) => ({ - return Either::Left(None) - }) ; - (changed) => ({ - return Either::Left( Some(true) ) - }) ; - (not changed) => ({ - return Either::Left( Some(false) ) - }) ; - (adding pos $pred:expr, $fargs:expr, $args:expr, $origin:expr) => ({ - if let Some(neg) = self.add_pos( - $pred, $fargs.clone(), $args.clone(), $origin.clone() - ) { - done!(contradiction $pred, $args.clone(), neg.clone()) + /// Gets the orign of a sample. + pub fn get>( + &self, + polarity: Polarity, + pred: P, + args: &VarVals, + ) -> Option<(Rhs, Origin)> { + if polarity.is_pos() { + self.get_pos(pred.borrow(), args) + .map(|&(ref fargs, ref origin)| { + ( + Some((*pred.borrow(), fargs.clone(), args.clone())), + origin.clone(), + ) + }) } else { - done!(changed) + self.get_neg(pred, args) + .map(|(rhs, origin)| (rhs.clone(), origin.clone())) } - }) ; - (adding neg $pred:expr, $args:expr, $rhs:expr, $origin:expr) => ({ - if let Some(pos) = self.add_neg( - $pred, $args.clone(), $rhs, $origin.clone() - ) { - done!(contradiction $pred, pos.clone(), $args.clone()) - } else { - done!(changed) - } - }) ; - (contradiction $pred:expr, $pos:expr, $neg:expr) => ({ - return Either::Right( - ($pred, $pos.clone(), $neg.clone()) - ) - }) ; } - let (mut lhs_pos, mut lhs_neg, mut lhs_unk) = (vec![], vec![], vec![]) ; + /// Registers a positive sample. + /// + /// Returns a sample related to this one but registered negative if any. + /// + /// If the sample is already registered as positive, overwrites the old + /// origin. + pub fn add_pos( + &mut self, + pred: PrdIdx, + fargs: VarTerms, + args: VarVals, + origin: Origin, + ) -> Option { + let res = self + .neg + .get(&pred) + .and_then(|map| map.keys().find(|other| other.is_related_to(&args))) + .cloned(); + + self.pos + .entry(pred) + .or_insert_with(VarValsMap::new) + .insert(args, (fargs, origin)); + + res + } - let rhs_true = rhs.map( - |(pred, fargs, args)| ( - pred, fargs, args, if self.is_pos(pred, args) { - Some(true) - } else if self.is_neg(pred, args) { - Some(false) - } else { - None + /// Registers a negative sample. + /// + /// Returns a sample related to this one but registered positive if any. + /// + /// If the sample is already registered as negative, overwrites the old + /// origin. + pub fn add_neg( + &mut self, + pred: PrdIdx, + args: VarVals, + rhs: Rhs, + origin: Origin, + ) -> Option { + let res = self + .pos + .get(&pred) + .and_then(|map| map.keys().find(|other| other.is_related_to(&args))) + .cloned(); + + self.neg + .entry(pred) + .or_insert_with(VarValsMap::new) + .insert(args, (rhs, origin)); + + res + } + + pub fn update( + &mut self, + rhs: Option<(PrdIdx, &VarTerms, &VarVals)>, + lhs: &PrdHMap, + clause: ClsIdx, + ) -> Either, (PrdIdx, VarVals, VarVals)> { + macro_rules! done { + (trivial) => {{ + return Either::Left(None); + }}; + (changed) => {{ + return Either::Left(Some(true)); + }}; + (not changed) => {{ + return Either::Left(Some(false)); + }}; + (adding pos $pred:expr, $fargs:expr, $args:expr, $origin:expr) => {{ + if let Some(neg) = + self.add_pos($pred, $fargs.clone(), $args.clone(), $origin.clone()) + { + done!(contradiction $pred, $args.clone(), neg.clone()) + } else { + done!(changed) + } + }}; + (adding neg $pred:expr, $args:expr, $rhs:expr, $origin:expr) => {{ + if let Some(pos) = self.add_neg($pred, $args.clone(), $rhs, $origin.clone()) { + done!(contradiction $pred, pos.clone(), $args.clone()) + } else { + done!(changed) + } + }}; + (contradiction $pred:expr, $pos:expr, $neg:expr) => {{ + return Either::Right(($pred, $pos.clone(), $neg.clone())); + }}; } - ) - ) ; - - for (pred, argss) in lhs { - for (_, args) in argss { - if self.is_pos(pred, args) { - lhs_pos.push((pred, args)) - } else if self.is_neg(pred, args) { - lhs_neg.push((pred, args)) - } else { - lhs_unk.push((pred, args)) + + let (mut lhs_pos, mut lhs_neg, mut lhs_unk) = (vec![], vec![], vec![]); + + let rhs_true = rhs.map(|(pred, fargs, args)| { + ( + pred, + fargs, + args, + if self.is_pos(pred, args) { + Some(true) + } else if self.is_neg(pred, args) { + Some(false) + } else { + None + }, + ) + }); + + for (pred, argss) in lhs { + for (_, args) in argss { + if self.is_pos(pred, args) { + lhs_pos.push((pred, args)) + } else if self.is_neg(pred, args) { + lhs_neg.push((pred, args)) + } else { + lhs_unk.push((pred, args)) + } + } } - } - } - match ( + match ( lhs_pos.is_empty(), lhs_neg.is_empty(), lhs_unk.len(), rhs_true ) { @@ -351,254 +354,254 @@ impl KnownSamples { } } - } - - + } } - - - /// A frame of a trace (explanation). pub struct TraceFrame { - /// Clause this result comes from. - pub clause: ClsIdx, - /// Values for the variables of the clauses that yield this frame. - pub values: VarHMap, - /// Positiveness flag. - pub polarity: Polarity, - /// Predicate. - pub pred: PrdIdx, - /// Arguments. - pub args: VarVals, - /// Optional rhs. - pub rhs: Rhs, - /// Antecedents. - pub lhs: PrdHMap, + /// Clause this result comes from. + pub clause: ClsIdx, + /// Values for the variables of the clauses that yield this frame. + pub values: VarHMap, + /// Positiveness flag. + pub polarity: Polarity, + /// Predicate. + pub pred: PrdIdx, + /// Arguments. + pub args: VarVals, + /// Optional rhs. + pub rhs: Rhs, + /// Antecedents. + pub lhs: PrdHMap, } impl TraceFrame { + /// Constructor. + pub fn new( + clause: ClsIdx, + values: VarHMap, + polarity: Polarity, + pred: PrdIdx, + args: VarVals, + rhs: Rhs, + lhs: PrdHMap, + ) -> Self { + TraceFrame { + clause, + values, + polarity, + pred, + args, + rhs, + lhs, + } + } - /// Constructor. - pub fn new( - clause: ClsIdx, - values: VarHMap, - polarity: Polarity, - pred: PrdIdx, - args: VarVals, - rhs: Rhs, - lhs: PrdHMap, - ) -> Self { - TraceFrame { clause, values, polarity, pred, args, rhs, lhs } - } - - /// Writes itself as a derivation. - pub fn write_as_derivation( - & self, w: & mut W, pref: & str, instance: & Instance - ) -> Res<()> { - let clause = & instance[self.clause] ; + /// Writes itself as a derivation. + pub fn write_as_derivation( + &self, + w: &mut W, + pref: &str, + instance: &Instance, + ) -> Res<()> { + let clause = &instance[self.clause]; - let original_clause_index = clause.from() ; + let original_clause_index = clause.from(); - let original_clause_name = if let Some(name) = instance.name_of_old_clause( - original_clause_index - ) { - name - } else { - return Ok(()) - } ; - - writeln!(w, "{}({}", pref, original_clause_name) ? ; - - writeln!(w, "{} ( ; Values for the clause's variables:", pref) ? ; - for (var, val) in & self.values { - writeln!( - w, "{} (define-fun {} {} {})", - pref, clause.vars[* var], clause.vars[* var].typ, val - ) ? ; - } - writeln!(w, "{} )", pref) ? ; - - writeln!( - w, "{} (=> ; Concrete values for applications (non-ordered):", pref - ) ? ; - if self.lhs.is_empty() { - writeln!(w, "{} true", pref) ? - } else { - write!(w, "{} (and", pref) ? ; - for (pred, argss) in & self.lhs { - for (_, args) in argss { - write!(w, " ({} {})", instance[* pred], args) ? + let original_clause_name = + if let Some(name) = instance.name_of_old_clause(original_clause_index) { + name + } else { + return Ok(()); + }; + + writeln!(w, "{}({}", pref, original_clause_name)?; + + writeln!(w, "{} ( ; Values for the clause's variables:", pref)?; + for (var, val) in &self.values { + writeln!( + w, + "{} (define-fun {} {} {})", + pref, clause.vars[*var], clause.vars[*var].typ, val + )?; } - } - writeln!(w, ")") ? - } - if let Some((pred, _, ref args)) = self.rhs { - writeln!(w, "{} ({} {})", pref, instance[pred], args) ? - } else { - writeln!(w, "{} false", pref) ? - } - writeln!(w, "{} )", pref) ? ; - - writeln!(w, "{} ; Yields:", pref) ? ; - write!(w, "{} ", pref) ? ; - let closing = if ! self.polarity.is_pos() { - write!(w, "(not ") ? ; - ")" - } else { - "" - } ; - writeln!(w, "({} {}){}", instance[self.pred], self.args, closing) ? ; - - writeln!(w, "{})", pref) ? ; - Ok(()) - } + writeln!(w, "{} )", pref)?; + + writeln!( + w, + "{} (=> ; Concrete values for applications (non-ordered):", + pref + )?; + if self.lhs.is_empty() { + writeln!(w, "{} true", pref)? + } else { + write!(w, "{} (and", pref)?; + for (pred, argss) in &self.lhs { + for (_, args) in argss { + write!(w, " ({} {})", instance[*pred], args)? + } + } + writeln!(w, ")")? + } + if let Some((pred, _, ref args)) = self.rhs { + writeln!(w, "{} ({} {})", pref, instance[pred], args)? + } else { + writeln!(w, "{} false", pref)? + } + writeln!(w, "{} )", pref)?; - /// Writes itself. - pub fn write( - & self, w: & mut W, pref: & str, instance: & Instance - ) -> Res<()> { - writeln!( - w, "{}{} ({} {}) {{", - pref, if self.polarity.is_pos() { "+" } else { "-" }, - instance[self.pred], self.args - ) ? ; - - if let Some(& (pred, _, ref args)) = self.rhs.as_ref() { - write!(w, "{} ({} {})", pref, instance[pred], args) ? - } else { - write!(w, "{} false", pref) ? + writeln!(w, "{} ; Yields:", pref)?; + write!(w, "{} ", pref)?; + let closing = if !self.polarity.is_pos() { + write!(w, "(not ")?; + ")" + } else { + "" + }; + writeln!(w, "({} {}){}", instance[self.pred], self.args, closing)?; + + writeln!(w, "{})", pref)?; + Ok(()) } - write!(w, " <=#{}=", self.clause) ? ; + /// Writes itself. + pub fn write(&self, w: &mut W, pref: &str, instance: &Instance) -> Res<()> { + writeln!( + w, + "{}{} ({} {}) {{", + pref, + if self.polarity.is_pos() { "+" } else { "-" }, + instance[self.pred], + self.args + )?; + + if let Some(&(pred, _, ref args)) = self.rhs.as_ref() { + write!(w, "{} ({} {})", pref, instance[pred], args)? + } else { + write!(w, "{} false", pref)? + } - for (& pred, argss) in & self.lhs { - for (_, args) in argss { - write!(w, " ({} {})", instance[pred], args) ? - } - } - writeln!(w) ? ; - let clause = & instance[self.clause] ; - write!(w, "{} with", pref) ? ; - for (var, val) in & self.values { - write!(w, " {}: {},", clause.vars[* var], val) ? - } - writeln!(w) ? ; + write!(w, " <=#{}=", self.clause)?; - writeln!(w, "{}}}", pref) ? ; - Ok(()) - } + for (&pred, argss) in &self.lhs { + for (_, args) in argss { + write!(w, " ({} {})", instance[pred], args)? + } + } + writeln!(w)?; + let clause = &instance[self.clause]; + write!(w, "{} with", pref)?; + for (var, val) in &self.values { + write!(w, " {}: {},", clause.vars[*var], val)? + } + writeln!(w)?; + writeln!(w, "{}}}", pref)?; + Ok(()) + } } /// A trace: an explanation for the value of the last sample. pub struct Trace { - /// Frames of the trace. - pub frames: Vec + /// Frames of the trace. + pub frames: Vec, } impl Trace { - /// Constructor. - pub fn new(frames: Vec) -> Self { - Trace { frames } - } - - /// Writes itself. - pub fn write( - & self, w: & mut W, pref: & str, instance: & Instance - ) -> Res<()> { - for frame in & self.frames { - frame.write(w, & format!("{} ", pref), instance) ? + /// Constructor. + pub fn new(frames: Vec) -> Self { + Trace { frames } } - Ok(()) - } - /// Writes itself as a derivation. - pub fn write_as_derivation( - & self, w: & mut W, pref: & str, instance: & Instance - ) -> Res<()> { - for frame in & self.frames { - frame.write_as_derivation(w, pref, instance) ? + /// Writes itself. + pub fn write(&self, w: &mut W, pref: &str, instance: &Instance) -> Res<()> { + for frame in &self.frames { + frame.write(w, &format!("{} ", pref), instance)? + } + Ok(()) } - Ok(()) - } - /// Returns all clauses mentioned in this trace. - pub fn all_clauses(& self, set: & mut ClsSet) { - for frame in & self.frames { - set.insert(frame.clause) ; + /// Writes itself as a derivation. + pub fn write_as_derivation( + &self, + w: &mut W, + pref: &str, + instance: &Instance, + ) -> Res<()> { + for frame in &self.frames { + frame.write_as_derivation(w, pref, instance)? + } + Ok(()) } - } + /// Returns all clauses mentioned in this trace. + pub fn all_clauses(&self, set: &mut ClsSet) { + for frame in &self.frames { + set.insert(frame.clause); + } + } } - /// A proof: an explanation of an unsat result. pub struct UnsatProof { - /// Predicate. - pred: PrdIdx, - /// Positive sample. - pos: VarVals, - /// Negative sample. - neg: VarVals, - /// Derivation for `pos`. - pos_trace: Trace, - /// Derivation for `neg`. - neg_trace: Trace, + /// Predicate. + pred: PrdIdx, + /// Positive sample. + pos: VarVals, + /// Negative sample. + neg: VarVals, + /// Derivation for `pos`. + pos_trace: Trace, + /// Derivation for `neg`. + neg_trace: Trace, } impl UnsatProof { - /// Retrieves the unsat core. - pub fn core(& self) -> ClsSet { - let mut res = ClsSet::new() ; - self.pos_trace.all_clauses(& mut res) ; - self.neg_trace.all_clauses(& mut res) ; - res - } - - - /// Writes itself. - pub fn write( - & self, w: & mut W, instance: & Instance - ) -> Res<()> { + /// Retrieves the unsat core. + pub fn core(&self) -> ClsSet { + let mut res = ClsSet::new(); + self.pos_trace.all_clauses(&mut res); + self.neg_trace.all_clauses(&mut res); + res + } - writeln!(w, "(") ? ; + /// Writes itself. + pub fn write(&self, w: &mut W, instance: &Instance) -> Res<()> { + writeln!(w, "(")?; - writeln!(w, " ; Contradiction:") ? ; - writeln!(w, - " ( and ({} {}) (not ({} {})) )", - instance[self.pred], self.pos, instance[self.pred], self.neg - ) ? ; + writeln!(w, " ; Contradiction:")?; + writeln!( + w, + " ( and ({} {}) (not ({} {})) )", + instance[self.pred], self.pos, instance[self.pred], self.neg + )?; - writeln!(w) ? ; - writeln!( - w, " ( ; Derivation for ({} {}):", instance[self.pred], self.pos - ) ? ; + writeln!(w)?; + writeln!( + w, + " ( ; Derivation for ({} {}):", + instance[self.pred], self.pos + )?; - self.pos_trace.write_as_derivation( - w, " ", instance - ) ? ; + self.pos_trace.write_as_derivation(w, " ", instance)?; - writeln!(w, " )") ? ; + writeln!(w, " )")?; - writeln!(w) ? ; - writeln!( - w, " ( ; Derivation for (not ({} {})):", instance[self.pred], self.neg - ) ? ; + writeln!(w)?; + writeln!( + w, + " ( ; Derivation for (not ({} {})):", + instance[self.pred], self.neg + )?; - self.neg_trace.write_as_derivation( - w, " ", instance - ) ? ; + self.neg_trace.write_as_derivation(w, " ", instance)?; - writeln!(w, " )") ? ; + writeln!(w, " )")?; - write!(w, ")") ? ; + write!(w, ")")?; - Ok(()) - } + Ok(()) + } } - - - /** Stores the graph of dependencies between samples. This is maintained by the teacher when unsat core production is active and is @@ -608,137 +611,130 @@ false at the same time. */ #[derive(Clone, Debug, Default)] pub struct SampleGraph { - /// Maps samples to the clause and the samples for this clause they come - /// from. - graph: PrdHMap< VarValsMap< OTArgMap > >, - /// Negative samples. - neg: Vec, + /// Maps samples to the clause and the samples for this clause they come + /// from. + graph: PrdHMap>, + /// Negative samples. + neg: Vec, } impl SampleGraph { - /// Creates a new sample graph. - pub fn new() -> Self { - SampleGraph { graph: PrdHMap::new(), neg: vec![] } - } - - - /// Merges two graphs. - pub fn merge(& mut self, other : Self) { - let SampleGraph { graph, mut neg } = other ; - - for (pred, map) in graph { - let pred_target = self.graph.entry(pred).or_insert_with( - || VarValsMap::with_capacity(map.len()) - ) ; - for (args, origins) in map { - let target = pred_target.entry(args).or_insert_with( - || OTArgMap::with_capacity(origins.len()) - ) ; - for (fargs, origins) in origins { - let target = target.entry(fargs).or_insert_with( - || Vec::with_capacity( origins.len() ) - ) ; - for origin in origins { - if ! target.contains(& origin) { - target.push(origin) - } - } + /// Creates a new sample graph. + pub fn new() -> Self { + SampleGraph { + graph: PrdHMap::new(), + neg: vec![], } - } } - neg.retain( - |origin| self.neg.iter().all(|o| o != origin) - ) ; - for origin in neg { self.neg.push(origin) } - } - + /// Merges two graphs. + pub fn merge(&mut self, other: Self) { + let SampleGraph { graph, mut neg } = other; + + for (pred, map) in graph { + let pred_target = self + .graph + .entry(pred) + .or_insert_with(|| VarValsMap::with_capacity(map.len())); + for (args, origins) in map { + let target = pred_target + .entry(args) + .or_insert_with(|| OTArgMap::with_capacity(origins.len())); + for (fargs, origins) in origins { + let target = target + .entry(fargs) + .or_insert_with(|| Vec::with_capacity(origins.len())); + for origin in origins { + if !target.contains(&origin) { + target.push(origin) + } + } + } + } + } - /// Adds traceability for a sample. - pub fn add( - & mut self, prd: PrdIdx, fargs: VarTerms, args: VarVals, - cls: ClsIdx, samples: PrdHMap, - ) { - if_log! { @3 - log! { @3 - "adding origin for ({} {})", prd, args ; - "from clause #{} {{", cls - } - for (pred, samples) in & samples { - log! { @3 " from predicate {}:", pred } - for (_, sample) in samples { - log! { @3 " {}", sample } + neg.retain(|origin| self.neg.iter().all(|o| o != origin)); + for origin in neg { + self.neg.push(origin) } - } - log! { @3 "}}" } } - self.graph.entry(prd).or_insert_with( - VarValsMap::new - ).entry(args).or_insert_with( - OTArgMap::new - ).entry(fargs).or_insert_with( - || vec![] - ).push( (cls, samples) ) - } - - /// Adds traceability for a negative sample. - pub fn add_neg( - & mut self, cls: ClsIdx, samples: PrdHMap - ) { - self.neg.push( (cls, samples) ) - } - - - - - - /// Searches for a contradiction in the graph. - /// - /// If a contradiction is detected, returns - /// - /// - the related positive and negative samples - /// - known samples (pos/neg) - fn find_contradiction(& mut self) -> Option< - (PrdIdx, VarVals, VarVals, KnownSamples) - > { - - // Known samples (positive/negative) and their relevant origin. - let mut known = KnownSamples::new() ; - // Fixed point flag. - let mut fixed_point = false ; - - // Stuff to remove from non-negative constraints. - let mut to_rm_1 = vec![] ; - - let mut res = None ; - - macro_rules! cleanup { - () => ({ - for (pred, args) in to_rm_1.drain(0..) { - let rmed = self.graph.get_mut(& pred).map( - |arg_map| arg_map.remove(& args) - ) ; - debug_assert! { rmed.is_some() } + /// Adds traceability for a sample. + pub fn add( + &mut self, + prd: PrdIdx, + fargs: VarTerms, + args: VarVals, + cls: ClsIdx, + samples: PrdHMap, + ) { + if_log! { @3 + log! { @3 + "adding origin for ({} {})", prd, args ; + "from clause #{} {{", cls + } + for (pred, samples) in & samples { + log! { @3 " from predicate {}:", pred } + for (_, sample) in samples { + log! { @3 " {}", sample } + } + } + log! { @3 "}}" } } - self.graph.retain( - |_, arg_map| ! arg_map.is_empty() - ) - }) ; + self.graph + .entry(prd) + .or_insert_with(VarValsMap::new) + .entry(args) + .or_insert_with(OTArgMap::new) + .entry(fargs) + .or_insert_with(|| vec![]) + .push((cls, samples)) } - // Saturate the propagation mechanism. - 'saturate: while ! fixed_point { - fixed_point = true ; - - for (pred, arg_map) in & self.graph { + /// Adds traceability for a negative sample. + pub fn add_neg(&mut self, cls: ClsIdx, samples: PrdHMap) { + self.neg.push((cls, samples)) + } - for (args, origins) in arg_map { + /// Searches for a contradiction in the graph. + /// + /// If a contradiction is detected, returns + /// + /// - the related positive and negative samples + /// - known samples (pos/neg) + fn find_contradiction(&mut self) -> Option<(PrdIdx, VarVals, VarVals, KnownSamples)> { + // Known samples (positive/negative) and their relevant origin. + let mut known = KnownSamples::new(); + // Fixed point flag. + let mut fixed_point = false; + + // Stuff to remove from non-negative constraints. + let mut to_rm_1 = vec![]; + + let mut res = None; + + macro_rules! cleanup { + () => {{ + for (pred, args) in to_rm_1.drain(0..) { + let rmed = self + .graph + .get_mut(&pred) + .map(|arg_map| arg_map.remove(&args)); + debug_assert! { rmed.is_some() } + } + self.graph.retain(|_, arg_map| !arg_map.is_empty()) + }}; + } - for (fargs, origins) in origins { + // Saturate the propagation mechanism. + 'saturate: while !fixed_point { + fixed_point = true; - for & (clause, ref lhs) in origins { - match known.update( + for (pred, arg_map) in &self.graph { + for (args, origins) in arg_map { + for (fargs, origins) in origins { + for &(clause, ref lhs) in origins { + match known.update( Some((* pred, fargs, args)), lhs, clause ) { // Used. @@ -761,17 +757,14 @@ impl SampleGraph { break 'saturate }, } + } + } + } } - } - - } - - } - let mut neg_cnt = 0 ; - while neg_cnt < self.neg.len() { - - match known.update( + let mut neg_cnt = 0; + while neg_cnt < self.neg.len() { + match known.update( None, & self.neg[neg_cnt].1, self.neg[neg_cnt].0 ) { // Used. @@ -796,247 +789,232 @@ impl SampleGraph { break 'saturate }, } - } + } - cleanup!() + cleanup!() + } + res.map(|(pred, pos, neg)| (pred, pos, neg, known)) } - res.map( - |(pred, pos, neg)| (pred, pos, neg, known) - ) - } - - - + // /// Traces the origin of a sample. + // fn trace<'a>( + // & 'a self, + // pred: PrdIdx, args: & VarVals, + // polarity: Polarity, + // known: & KnownSamples, + // solver: & mut Solver, + // instance: & Instance, + // ) -> Res { + + // // Samples for which we already have an explanation for. + // let mut explained = PrdHMap::::new() ; + + // // Checks whether a sample is explained or inserts a sample in explained + // // samples. + // macro_rules! explained { + // // Checks whether a sample is already explained. + // (contains $pred:expr, $args:expr) => ( + // explained.get(& $pred).map( + // |set| set.contains(& $args) + // ).unwrap_or(false) + // ) ; + + // // Adds a sample as explained. + // (insert $pred:expr, $args:expr) => ({ + // let is_new = explained.entry($pred).or_insert_with( + // VarValsSet::new + // ).insert($args) ; + // debug_assert! { is_new } + // }) ; + // } + // // Result: full trace of explanation. + // let mut res = vec![] ; + + // // Stores the samples we need to explain. + // let mut to_explain = vec![ (polarity, pred, args.clone()) ] ; + + // // Explain all samples. + // 'all_samples: while let Some( + // (polarity, pred, args) + // ) = to_explain.pop() { + + // // Already explained? + // if explained!(contains pred, & args) { + // continue 'all_samples + // } + + // let (rhs, (clause, lhs)) = if let Some(origin) = known.get( + // polarity, pred, & args + // ) { + // origin + // } else { + // bail!("unable to explain why sample ({} {}) is positive", pred, args) + // } ; + + // // Take care of the antecedents. + // for (pred, argss) in lhs.iter() { + // for (_, args) in argss { + // to_explain.push( + // (Polarity::pos(), * pred, args.clone()) + // ) + // } + // } + + // let mut map = VarHMap::new() ; + + // { + // use common::smt::{ SmtConj, EqConj } ; + + // solver.comment( + // & format!("Working on clause #{}", clause) + // ) ? ; + // let clause = & instance[clause] ; + + // solver.push(1) ? ; + + // clause.declare(solver) ? ; + + // let conj = SmtConj::new(clause.lhs_terms()) ; + + // solver.assert(& conj) ? ; + + // debug_assert_eq! { clause.lhs_preds().len(), lhs.len() } + + // debug_assert! { + // lhs.iter().all( + // |(pred, argss)| clause.lhs_preds().iter().any( + // |(p, a)| p == pred && argss.len() == a.len() && argss.iter().all( + // |(args, _)| a.iter().any( + // |a| a == args + // ) + // ) + // ) + // ) + // } + + // for argss in lhs.values() { + // for (fargs, sample) in argss { + // let eq_conj = EqConj::new(fargs, sample) ; + // solver.assert(& eq_conj) ? + // } + // } + + // if let Some((pred, ref fargs, ref args)) = rhs { + // debug_assert! { + // if let Some((p, fa)) = clause.rhs() { + // pred == p && fa == fargs + // } else { + // false + // } + // } + // let eq_conj = EqConj::new(fargs, args) ; + // solver.assert(& eq_conj) ? + // } + + // if ! solver.check_sat() ? { + // bail!("error retrieving unsat core, trace is not feasible") + // } + + // let model = solver.get_model() ? ; + // let model = Parser.fix_model(model) ? ; + + // solver.pop(1) ? ; + + // for (var, _, val) in model { + // let prev = map.insert(var, val) ; + // debug_assert_eq! { prev, None } + // } + + // } + + // // Append to result. + // res.push( + // TraceFrame::new( + // clause, map, polarity, pred, args.clone(), rhs, lhs + // ) + // ) ; + + // // Remember we explained this sample. + // explained! { insert pred, args } - // /// Traces the origin of a sample. - // fn trace<'a>( - // & 'a self, - // pred: PrdIdx, args: & VarVals, - // polarity: Polarity, - // known: & KnownSamples, - // solver: & mut Solver, - // instance: & Instance, - // ) -> Res { - - // // Samples for which we already have an explanation for. - // let mut explained = PrdHMap::::new() ; - - // // Checks whether a sample is explained or inserts a sample in explained - // // samples. - // macro_rules! explained { - // // Checks whether a sample is already explained. - // (contains $pred:expr, $args:expr) => ( - // explained.get(& $pred).map( - // |set| set.contains(& $args) - // ).unwrap_or(false) - // ) ; - - // // Adds a sample as explained. - // (insert $pred:expr, $args:expr) => ({ - // let is_new = explained.entry($pred).or_insert_with( - // VarValsSet::new - // ).insert($args) ; - // debug_assert! { is_new } - // }) ; - // } - - // // Result: full trace of explanation. - // let mut res = vec![] ; - - // // Stores the samples we need to explain. - // let mut to_explain = vec![ (polarity, pred, args.clone()) ] ; - - // // Explain all samples. - // 'all_samples: while let Some( - // (polarity, pred, args) - // ) = to_explain.pop() { - - // // Already explained? - // if explained!(contains pred, & args) { - // continue 'all_samples - // } - - // let (rhs, (clause, lhs)) = if let Some(origin) = known.get( - // polarity, pred, & args - // ) { - // origin - // } else { - // bail!("unable to explain why sample ({} {}) is positive", pred, args) - // } ; - - // // Take care of the antecedents. - // for (pred, argss) in lhs.iter() { - // for (_, args) in argss { - // to_explain.push( - // (Polarity::pos(), * pred, args.clone()) - // ) - // } - // } - - // let mut map = VarHMap::new() ; - - // { - // use common::smt::{ SmtConj, EqConj } ; - - // solver.comment( - // & format!("Working on clause #{}", clause) - // ) ? ; - // let clause = & instance[clause] ; - - // solver.push(1) ? ; - - // clause.declare(solver) ? ; - - // let conj = SmtConj::new(clause.lhs_terms()) ; - - // solver.assert(& conj) ? ; - - // debug_assert_eq! { clause.lhs_preds().len(), lhs.len() } - - // debug_assert! { - // lhs.iter().all( - // |(pred, argss)| clause.lhs_preds().iter().any( - // |(p, a)| p == pred && argss.len() == a.len() && argss.iter().all( - // |(args, _)| a.iter().any( - // |a| a == args - // ) - // ) - // ) - // ) - // } - - // for argss in lhs.values() { - // for (fargs, sample) in argss { - // let eq_conj = EqConj::new(fargs, sample) ; - // solver.assert(& eq_conj) ? - // } - // } - - // if let Some((pred, ref fargs, ref args)) = rhs { - // debug_assert! { - // if let Some((p, fa)) = clause.rhs() { - // pred == p && fa == fargs - // } else { - // false - // } - // } - // let eq_conj = EqConj::new(fargs, args) ; - // solver.assert(& eq_conj) ? - // } - - // if ! solver.check_sat() ? { - // bail!("error retrieving unsat core, trace is not feasible") - // } - - // let model = solver.get_model() ? ; - // let model = Parser.fix_model(model) ? ; - - // solver.pop(1) ? ; - - // for (var, _, val) in model { - // let prev = map.insert(var, val) ; - // debug_assert_eq! { prev, None } - // } - - // } - - // // Append to result. - // res.push( - // TraceFrame::new( - // clause, map, polarity, pred, args.clone(), rhs, lhs - // ) - // ) ; - - // // Remember we explained this sample. - // explained! { insert pred, args } - - // } - - // res.reverse() ; - - // Ok( Trace::new(res) ) - // } - - - - - /// Extracts a proof for unsat. - pub fn get_proof(& mut self, _instance: & Instance) -> Res { - bail!("unimplemented") ; - // let ( - // pred, pos, neg, known - // ) = if let Some(contradiction) = self.find_contradiction() { - // contradiction - // } else { - // bail!("could not retrieve unsat result") - // } ; - - // let mut solver = conf.solver.spawn( - // "core_extraction", Parser, & instance - // ) ? ; - // let pos_trace = self.trace( - // pred, & pos, Polarity::pos(), & known, & mut solver, - // instance - // ) ? ; - // let neg_trace = self.trace( - // pred, & neg, Polarity::neg(), & known, & mut solver, - // instance - // ) ? ; - - // Ok( - // UnsatProof { - // pred, pos, neg, pos_trace, neg_trace // } - // ) - } - + // res.reverse() ; + + // Ok( Trace::new(res) ) + // } + + /// Extracts a proof for unsat. + pub fn get_proof(&mut self, _instance: &Instance) -> Res { + bail!("unimplemented"); + // let ( + // pred, pos, neg, known + // ) = if let Some(contradiction) = self.find_contradiction() { + // contradiction + // } else { + // bail!("could not retrieve unsat result") + // } ; + + // let mut solver = conf.solver.spawn( + // "core_extraction", Parser, & instance + // ) ? ; + // let pos_trace = self.trace( + // pred, & pos, Polarity::pos(), & known, & mut solver, + // instance + // ) ? ; + // let neg_trace = self.trace( + // pred, & neg, Polarity::neg(), & known, & mut solver, + // instance + // ) ? ; + + // Ok( + // UnsatProof { + // pred, pos, neg, pos_trace, neg_trace + // } + // ) + } - /// Writes the sample graph with a prefix. - pub fn write( - & self, w: & mut W, pref: & str, instance: & Instance - ) -> IoRes<()> { - writeln!(w, "{}sample graph {{", pref) ? ; + /// Writes the sample graph with a prefix. + pub fn write(&self, w: &mut W, pref: &str, instance: &Instance) -> IoRes<()> { + writeln!(w, "{}sample graph {{", pref)?; + + for (pred, map) in &self.graph { + writeln!(w, "{} {}:", pref, instance[*pred])?; + for (args, origins) in map { + for origins in origins.values() { + for &(clause, ref apps) in origins { + write!(w, "{} {} <=#{}=", pref, args, clause)?; + if apps.is_empty() { + write!(w, " true")? + } else { + for (pred, argss) in apps { + for args in argss.values() { + write!(w, " ({} {})", instance[*pred], args)? + } + } + } + writeln!(w)? + } + } + } + } - for (pred, map) in & self.graph { - writeln!(w, "{} {}:", pref, instance[* pred]) ? ; - for (args, origins) in map { - for origins in origins.values() { - for & (clause, ref apps) in origins { - write!(w, "{} {} <=#{}=", pref, args, clause) ? ; + for (clause, ref apps) in &self.neg { + write!(w, "{} false <=#{}=", pref, clause)?; if apps.is_empty() { - write!(w, " true") ? + write!(w, " true")? } else { - for (pred, argss) in apps { - for args in argss.values() { - write!(w, " ({} {})", instance[* pred], args) ? + for (pred, argss) in apps { + for args in argss.values() { + write!(w, " ({} {})", instance[*pred], args)? + } } - } } - writeln!(w) ? - } + writeln!(w)? } - } - } - for (clause, ref apps) in & self.neg { - write!(w, "{} false <=#{}=", pref, clause) ? ; - if apps.is_empty() { - write!(w, " true") ? - } else { - for (pred, argss) in apps { - for args in argss.values() { - write!(w, " ({} {})", instance[* pred], args) ? - } - } - } - writeln!(w) ? + writeln!(w, "{}}}", pref) } - - writeln!(w, "{}}}", pref) - } - -} \ No newline at end of file +} diff --git a/src/val/mod.rs b/src/val/mod.rs index ccc70335..76a30ef9 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -6,9 +6,9 @@ //! - `Val::I` from `Int`, `usize`, `isize`, `u32`, `i32`, `u64`, `i64` //! - `Val::N` from `()` -use hashconsing::HConsed ; +use hashconsing::HConsed; -use common::* ; +use common::*; new_consign! { /// Value factory. @@ -16,163 +16,159 @@ new_consign! { } /// A hash-consed type. -pub type Val = HConsed ; - +pub type Val = HConsed; /// Creates a value. pub fn mk>(val: V) -> Val { - let val = val.into() ; - // if val.typ().has_unk() { - // panic!( - // "trying to create a value with a (partially) unknown type: {} ({})", - // val, val.typ() - // ) - // } - factory.mk(val) + let val = val.into(); + // if val.typ().has_unk() { + // panic!( + // "trying to create a value with a (partially) unknown type: {} ({})", + // val, val.typ() + // ) + // } + factory.mk(val) } /// Creates a boolean value. pub fn bool(b: bool) -> Val { - mk( RVal::B(b) ) + mk(RVal::B(b)) } /// Creates an array with a default value. /// /// - `idx_typ` is the type of **the indices** of the array. -pub fn array>( - idx_typ: Typ, default: Tgt -) -> Val { - let default = default.into() ; - let default = if ! default.is_known() { - default.typ().default_val() - } else { - default - } ; - mk( - RVal::Array { idx_typ, default, vals: Vec::new() } - ) +pub fn array>(idx_typ: Typ, default: Tgt) -> Val { + let default = default.into(); + let default = if !default.is_known() { + default.typ().default_val() + } else { + default + }; + mk(RVal::Array { + idx_typ, + default, + vals: Vec::new(), + }) } /// Constructs an array from a function definition. -pub fn array_of_fun(idx_typ: Typ, term: & Term) -> Res { - let mut vals: Vec<(Val, Val)> = vec![] ; - - let mut current = term ; - - loop { - debug_assert_eq! { term.typ(), current.typ() } +pub fn array_of_fun(idx_typ: Typ, term: &Term) -> Res { + let mut vals: Vec<(Val, Val)> = vec![]; - if let Some(val) = current.val() { - debug_assert! { - vals.iter().all( - |(c, v)| c.typ() == idx_typ && v.typ() == val.typ() - ) - } - return Ok( - mk( RVal::Array { idx_typ, default: val, vals } ) - ) - } else if let Some((c, t, e)) = current.ite_inspect() { - - if let Some(args) = c.eq_inspect() { - debug_assert_eq! { args.len(), 2 } - - let cond = if let Some(var) = args[0].var_idx() { - debug_assert_eq! { * var, 0 } - args[1].clone() - } else if let Some(var) = args[1].var_idx() { - debug_assert_eq! { * var, 0 } - args[0].clone() - } else if let Some( - (var, term) - ) = if term::vars(& args[1]).is_empty() { - args[0].invert( args[1].clone() ) - } else if term::vars(& args[0]).is_empty() { - args[1].invert( args[0].clone() ) - } else { - break - } { - debug_assert_eq! { * var, 0 } - term - } else { - break - } ; + let mut current = term; - let cond = if let Some(val) = cond.val() { - val - } else { - break - } ; + loop { + debug_assert_eq! { term.typ(), current.typ() } - let val = if let Some(val) = t.val() { - val + if let Some(val) = current.val() { + debug_assert! { + vals.iter().all( + |(c, v)| c.typ() == idx_typ && v.typ() == val.typ() + ) + } + return Ok(mk(RVal::Array { + idx_typ, + default: val, + vals, + })); + } else if let Some((c, t, e)) = current.ite_inspect() { + if let Some(args) = c.eq_inspect() { + debug_assert_eq! { args.len(), 2 } + + let cond = if let Some(var) = args[0].var_idx() { + debug_assert_eq! { * var, 0 } + args[1].clone() + } else if let Some(var) = args[1].var_idx() { + debug_assert_eq! { * var, 0 } + args[0].clone() + } else if let Some((var, term)) = if term::vars(&args[1]).is_empty() { + args[0].invert(args[1].clone()) + } else if term::vars(&args[0]).is_empty() { + args[1].invert(args[0].clone()) + } else { + break; + } { + debug_assert_eq! { * var, 0 } + term + } else { + break; + }; + + let cond = if let Some(val) = cond.val() { + val + } else { + break; + }; + + let val = if let Some(val) = t.val() { val } else { break }; + + current = e; + vals.push((cond, val)) + } else { + break; + } } else { - break - } ; - - current = e ; - vals.push((cond, val)) - - } else { - break - } - } else { - break + break; + } } - } - bail!("cannot extract array from term {}", term) + bail!("cannot extract array from term {}", term) } /// Creates an integer value. pub fn int>(i: I) -> Val { - mk( - RVal::I( i.into() ) - ) + mk(RVal::I(i.into())) } /// Creates a rational value. pub fn real>(r: R) -> Val { - mk( - RVal::R( r.into() ) - ) + mk(RVal::R(r.into())) } /// Creates a non-value for a type. pub fn none(typ: Typ) -> Val { - mk( RVal::N(typ) ) + mk(RVal::N(typ)) } /// Creates a new datatype value. pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { - { - let (dtyp, typ_args) = typ.dtyp_inspect().unwrap_or_else( - || panic!("illegal application of constructor {} of type {}", name, typ) - ) ; - let cargs = dtyp.news.get(& name).unwrap_or_else( - || panic!( - "unknown constructor {} for datatype {}", conf.bad(& name), dtyp.name - ) - ) ; - if args.len() != cargs.len() { - panic!( - "illegal application of constructor {} for datatype {} to {} \ - arguments, expected {}", - conf.bad(& name), dtyp.name, args.len(), cargs.len() - ) - } + { + let (dtyp, typ_args) = typ.dtyp_inspect().unwrap_or_else(|| { + panic!( + "illegal application of constructor {} of type {}", + name, typ + ) + }); + let cargs = dtyp.news.get(&name).unwrap_or_else(|| { + panic!( + "unknown constructor {} for datatype {}", + conf.bad(&name), + dtyp.name + ) + }); + if args.len() != cargs.len() { + panic!( + "illegal application of constructor {} for datatype {} to {} \ + arguments, expected {}", + conf.bad(&name), + dtyp.name, + args.len(), + cargs.len() + ) + } - for (count, (_ , ptyp)) in cargs.iter().enumerate() { - let typ = ptyp.to_type(typ_args).unwrap_or_else( - |_| panic!("illegal datatype {}", typ) - ) ; - if let Some(nu) = args[count].typ().merge(& typ) { - if let Some(nu) = args[count].force_dtyp(nu) { - args[count] = nu + for (count, (_, ptyp)) in cargs.iter().enumerate() { + let typ = ptyp + .to_type(typ_args) + .unwrap_or_else(|_| panic!("illegal datatype {}", typ)); + if let Some(nu) = args[count].typ().merge(&typ) { + if let Some(nu) = args[count].force_dtyp(nu) { + args[count] = nu + } + } } - } } - } - mk( RVal::DTypNew { typ, name, args } ) + mk(RVal::DTypNew { typ, name, args }) } - /// Values. /// /// Not that the `PartialEq` implementation is syntactic equality. In @@ -187,62 +183,60 @@ pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { /// [equal]: #method.equal (equal method) #[derive(PartialEq, Eq, Debug, Clone, Hash)] pub enum RVal { - /// No value. - N(Typ), - /// Boolean value. - B(bool), - /// Integer value. - I(Int), - /// Real value (actually a rational). - R(Rat), - - /// Datatype constructor. - DTypNew { - /// Type of the value. - typ: Typ, - /// Constructor. - name: String, - /// Arguments. - args: Vec, - }, - - /// An array is a total function. - /// - /// The `vals` field encodes a sequence of if-then-else's. - Array { - /// The type of **the indices** of the array. - /// - /// The actual type of the array is `array(idx_typ, default.typ())`. - /// - /// # Examples + /// No value. + N(Typ), + /// Boolean value. + B(bool), + /// Integer value. + I(Int), + /// Real value (actually a rational). + R(Rat), + + /// Datatype constructor. + DTypNew { + /// Type of the value. + typ: Typ, + /// Constructor. + name: String, + /// Arguments. + args: Vec, + }, + + /// An array is a total function. /// - /// ``` - /// use hoice::common::* ; - /// let array = val::array( - /// typ::int(), val::real( Rat::new(9.into(), 7.into()) ) - /// ) ; - /// assert_eq! { array.typ(), typ::array( typ::int(), typ::real() ) } - /// ``` - idx_typ: Typ, - /// Default target value. - default: Val, - /// The values of the array. - vals: Vec<(Val, Val)> - }, + /// The `vals` field encodes a sequence of if-then-else's. + Array { + /// The type of **the indices** of the array. + /// + /// The actual type of the array is `array(idx_typ, default.typ())`. + /// + /// # Examples + /// + /// ``` + /// use hoice::common::* ; + /// let array = val::array( + /// typ::int(), val::real( Rat::new(9.into(), 7.into()) ) + /// ) ; + /// assert_eq! { array.typ(), typ::array( typ::int(), typ::real() ) } + /// ``` + idx_typ: Typ, + /// Default target value. + default: Val, + /// The values of the array. + vals: Vec<(Val, Val)>, + }, } - - impl Into for RVal { - fn into(self) -> Val { - factory.mk(self) - } + fn into(self) -> Val { + factory.mk(self) + } } /// Applies a binary operation on two compatible values. /// /// The first token specifies the mode: -/// +/// /// - `arith`: only works if /// - the two values are the same arithmetic kind of `Val`, or /// - both are `Val::N`, or @@ -333,925 +327,860 @@ macro_rules! bin_op { /// `Val`, or both are `RVal::N`, or one is arithmetic and another one is /// `RVal::N`. macro_rules! arith_bin_rel { - ($lft:expr, $op:tt, $rgt:expr) => ({ - use num::One ; - match $lft { - - RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), - - RVal::I(ref lft) => match $rgt { - RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), - RVal::I(ref rgt) => Ok( bool(lft.$op(rgt)) ), - RVal::R(ref rgt) => Ok( - bool( Rat::new(lft.clone(), Int::one()).$op(rgt) ) - ), - ref rgt => bail!( - "expected arith values, found {} and {}", lft, rgt - ), - }, + ($lft:expr, $op:tt, $rgt:expr) => {{ + use num::One; + match $lft { + RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), + + RVal::I(ref lft) => match $rgt { + RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), + RVal::I(ref rgt) => Ok(bool(lft.$op(rgt))), + RVal::R(ref rgt) => Ok(bool(Rat::new(lft.clone(), Int::one()).$op(rgt))), + ref rgt => bail!("expected arith values, found {} and {}", lft, rgt), + }, - RVal::R(ref lft) => match $rgt { - RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), - RVal::R(ref rgt) => Ok( bool(lft.$op(rgt)) ), - RVal::I(ref rgt) => Ok( - bool( lft.$op( & Rat::new(rgt.clone(), Int::one()) ) ) - ), - ref rgt => bail!( - "expected arith values, found {} and {}", lft, rgt - ), - }, + RVal::R(ref lft) => match $rgt { + RVal::N(ref typ) if typ.is_arith() => Ok(none(typ::bool())), + RVal::R(ref rgt) => Ok(bool(lft.$op(rgt))), + RVal::I(ref rgt) => Ok(bool(lft.$op(&Rat::new(rgt.clone(), Int::one())))), + ref rgt => bail!("expected arith values, found {} and {}", lft, rgt), + }, - ref lft => bail!( - "expected arith values, found {} and {}", lft, $rgt - ), - } - }) ; + ref lft => bail!("expected arith values, found {} and {}", lft, $rgt), + } + }}; } /// Operations legal for all values. impl RVal { - /// Returns true iff the value is not `RVal::N`. - #[inline] - pub fn is_known(& self) -> bool { - match * self { - RVal::N(_) => false, - RVal::Array { ref default, ref vals, .. } => { - default.is_known() && vals.iter().all( - |(cond, val)| cond.is_known() && val.is_known() - ) - }, - _ => true, + /// Returns true iff the value is not `RVal::N`. + #[inline] + pub fn is_known(&self) -> bool { + match *self { + RVal::N(_) => false, + RVal::Array { + ref default, + ref vals, + .. + } => { + default.is_known() && vals + .iter() + .all(|(cond, val)| cond.is_known() && val.is_known()) + } + _ => true, + } } - } - /// Inspects a datatype value. - pub fn dtyp_inspect(& self) -> Option<(& Typ, & str, & [Val])> { - if let RVal::DTypNew { typ, name, args } = self { - Some((typ, name, args)) - } else { - None + /// Inspects a datatype value. + pub fn dtyp_inspect(&self) -> Option<(&Typ, &str, &[Val])> { + if let RVal::DTypNew { typ, name, args } = self { + Some((typ, name, args)) + } else { + None + } } - } - /// Returns the type of the value. - pub fn typ(& self) -> Typ { - use self::RVal::* ; - match * self { - B(_) => typ::bool(), - I(_) => typ::int(), - R(_) => typ::real(), - Array { ref idx_typ, ref default, .. } => typ::array( - idx_typ.clone(), default.typ() - ), - DTypNew{ ref typ, .. } => typ.clone(), - N(ref typ) => typ.clone() + /// Returns the type of the value. + pub fn typ(&self) -> Typ { + use self::RVal::*; + match *self { + B(_) => typ::bool(), + I(_) => typ::int(), + R(_) => typ::real(), + Array { + ref idx_typ, + ref default, + .. + } => typ::array(idx_typ.clone(), default.typ()), + DTypNew { ref typ, .. } => typ.clone(), + N(ref typ) => typ.clone(), + } } - } - /// Forces the type of a datatype constructor. - pub fn force_dtyp(& self, nu_typ: Typ) -> Option { - if let RVal::DTypNew { typ, name, args } = self { - debug_assert! { nu_typ.is_compatible(typ) } - Some( - dtyp_new( nu_typ, name.clone(), args.clone() ) - ) - } else { - None + /// Forces the type of a datatype constructor. + pub fn force_dtyp(&self, nu_typ: Typ) -> Option { + if let RVal::DTypNew { typ, name, args } = self { + debug_assert! { nu_typ.is_compatible(typ) } + Some(dtyp_new(nu_typ, name.clone(), args.clone())) + } else { + None + } } - } - /// Attempts to cast a value. - pub fn cast(& self, typ: & Typ) -> Res { - use num::One ; - use term::typ::RTyp ; - if & self.typ() == typ { - return Ok( mk(self.clone()) ) - } + /// Attempts to cast a value. + pub fn cast(&self, typ: &Typ) -> Res { + use num::One; + use term::typ::RTyp; + if &self.typ() == typ { + return Ok(mk(self.clone())); + } - match (self, typ.get()) { - (& RVal::I(ref i), & RTyp::Int) => Ok( int(i.clone()) ), - (& RVal::I(ref num), & RTyp::Real) => Ok( - real( Rat::new(num.clone(), Int::one()) ) - ), + match (self, typ.get()) { + (&RVal::I(ref i), &RTyp::Int) => Ok(int(i.clone())), + (&RVal::I(ref num), &RTyp::Real) => Ok(real(Rat::new(num.clone(), Int::one()))), - (& RVal::R(ref r), & RTyp::Real) => Ok( real(r.clone()) ), + (&RVal::R(ref r), &RTyp::Real) => Ok(real(r.clone())), - (& RVal::B(b), & RTyp::Bool) => Ok( - bool(b) - ), + (&RVal::B(b), &RTyp::Bool) => Ok(bool(b)), - // This is a bit lax as it allows to cast a non-value of any type to a - // non-value of any other type... - (& RVal::N(_), _) => Ok(none(typ.clone())), - - ( - & RVal::DTypNew { typ: ref vtyp, ref name, ref args }, _ - ) => if let Some(typ) = vtyp.merge(typ) { - Ok( dtyp_new(typ, name.clone(), args.clone()) ) - } else { - bail!( - "Cannot cast value {} to type {}", self, typ - ) - }, + // This is a bit lax as it allows to cast a non-value of any type to a + // non-value of any other type... + (&RVal::N(_), _) => Ok(none(typ.clone())), - (val, typ) => bail!( - "Cannot cast value {} to type {}", val, typ - ), + ( + &RVal::DTypNew { + typ: ref vtyp, + ref name, + ref args, + }, + _, + ) => if let Some(typ) = vtyp.merge(typ) { + Ok(dtyp_new(typ, name.clone(), args.clone())) + } else { + bail!("Cannot cast value {} to type {}", self, typ) + }, + + (val, typ) => bail!("Cannot cast value {} to type {}", val, typ), + } } - } - /// Checks if two values are the semantically equal. - /// - /// Different from partial eq! Here, `N` is not equal to `N`. - pub fn equal(& self, other: & Self) -> bool { - if self.is_known() && other.is_known() { - self == other - } else { - false + /// Checks if two values are the semantically equal. + /// + /// Different from partial eq! Here, `N` is not equal to `N`. + pub fn equal(&self, other: &Self) -> bool { + if self.is_known() && other.is_known() { + self == other + } else { + false + } } - } - /// Checks if two values are the semantically equal. - /// - /// Different from partial eq! Here, `N` is not equal to `N`. - pub fn equal_int(& self, other: & Int) -> bool { - if self.is_known() { - if let RVal::I(ref i) = * self { - i == other - } else { - false - } - } else { - false + /// Checks if two values are the semantically equal. + /// + /// Different from partial eq! Here, `N` is not equal to `N`. + pub fn equal_int(&self, other: &Int) -> bool { + if self.is_known() { + if let RVal::I(ref i) = *self { + i == other + } else { + false + } + } else { + false + } } - } - /// Checks if two values are the semantically equal. - /// - /// Different from partial eq! Here, `N` is not equal to `N`. - pub fn equal_real(& self, other: & Rat) -> bool { - if self.is_known() { - if let RVal::R(ref r) = * self { - r == other - } else { - false - } - } else { - false + /// Checks if two values are the semantically equal. + /// + /// Different from partial eq! Here, `N` is not equal to `N`. + pub fn equal_real(&self, other: &Rat) -> bool { + if self.is_known() { + if let RVal::R(ref r) = *self { + r == other + } else { + false + } + } else { + false + } } - } - /// Checks if two values are the semantically equal. - /// - /// Different from partial eq! Here, `N` is not equal to `N`. - pub fn equal_val(& self, other: & Val) -> bool { - if self.is_known() && other.get().is_known() { - self == other.get() - } else { - false + /// Checks if two values are the semantically equal. + /// + /// Different from partial eq! Here, `N` is not equal to `N`. + pub fn equal_val(&self, other: &Val) -> bool { + if self.is_known() && other.get().is_known() { + self == other.get() + } else { + false + } } - } - /// Extracts a boolean value. - pub fn to_bool(& self) -> Res> { - match self { - RVal::B(b) => Ok( Some(* b) ), - RVal::N(ref typ) if typ.is_bool() => Ok(None), - RVal::N(ref typ) => bail!( - "expected boolean value, got non-value of type {}", typ - ), - _ => bail!( - "expected boolean value, found value of type {}", self.typ() - ), + /// Extracts a boolean value. + pub fn to_bool(&self) -> Res> { + match self { + RVal::B(b) => Ok(Some(*b)), + RVal::N(ref typ) if typ.is_bool() => Ok(None), + RVal::N(ref typ) => bail!("expected boolean value, got non-value of type {}", typ), + _ => bail!("expected boolean value, found value of type {}", self.typ()), + } } - } - /// Extracts an integer value. - pub fn to_int(& self) -> Res> { - use num::One ; - match self { - RVal::I(ref i) => Ok( Some(i.clone()) ), - RVal::R(ref r) => if r.denom().abs() == Int::one() { - Ok( - Some( - if r.denom().is_negative() { - - r.numer() + /// Extracts an integer value. + pub fn to_int(&self) -> Res> { + use num::One; + match self { + RVal::I(ref i) => Ok(Some(i.clone())), + RVal::R(ref r) => if r.denom().abs() == Int::one() { + Ok(Some(if r.denom().is_negative() { + -r.numer() + } else { + r.numer().clone() + })) } else { - r.numer().clone() - } - ) - ) - } else { - bail!("expected integer value, found rational {}", r) - }, - RVal::N(ref typ) if typ == & typ::int() => Ok(None), - RVal::N(ref typ) => bail!( - "expected integer value, got no-value of type {}", typ - ), - _ => bail!( - "expected integer value, found value of type {}", self.typ() - ), + bail!("expected integer value, found rational {}", r) + }, + RVal::N(ref typ) if typ == &typ::int() => Ok(None), + RVal::N(ref typ) => bail!("expected integer value, got no-value of type {}", typ), + _ => bail!("expected integer value, found value of type {}", self.typ()), + } } - } - /// Extracts a real value. - pub fn to_real(& self) -> Res> { - use num::One ; - match self { - RVal::R(ref r) => Ok( Some(r.clone()) ), - RVal::I(ref i) => Ok( Some( Rat::new(i.clone(), Int::one()) ) ), - RVal::N(ref typ) if typ == & typ::real() => Ok(None), - RVal::N(ref typ) => bail!( - "expected real value, got no-value of type {}", typ - ), - _ => bail!( - "expected rational value, found value of type {}", self.typ() - ), + /// Extracts a real value. + pub fn to_real(&self) -> Res> { + use num::One; + match self { + RVal::R(ref r) => Ok(Some(r.clone())), + RVal::I(ref i) => Ok(Some(Rat::new(i.clone(), Int::one()))), + RVal::N(ref typ) if typ == &typ::real() => Ok(None), + RVal::N(ref typ) => bail!("expected real value, got no-value of type {}", typ), + _ => bail!( + "expected rational value, found value of type {}", + self.typ() + ), + } } - } - /// True if the two values have the same type. - pub fn same_type(& self, other: & Self) -> bool { - self.typ() == other.typ() - } + /// True if the two values have the same type. + pub fn same_type(&self, other: &Self) -> bool { + self.typ() == other.typ() + } - /// Transforms a value into a term. - pub fn to_term(& self) -> Option<::term::Term> { - match self { - RVal::I(ref i) => Some( ::term::int(i.clone()) ), - RVal::R(ref r) => Some( ::term::real(r.clone()) ), - RVal::B(b) => Some( ::term::bool(* b) ), - RVal::N(_) => None, - RVal::Array { ref idx_typ, ref default, ref vals } => { - let default = default.to_term().expect( - "default of array cannot be non-value" - ) ; - let mut res = term::cst_array(idx_typ.clone(), default) ; - for (idx, val) in vals { - let (idx, val) = ( - idx.to_term().expect("index of arrays cannot be non-value"), - val.to_term().expect("value of arrays cannot be non-value"), - ) ; - res = term::store(res, idx, val) ; - } - Some(res) - }, - RVal::DTypNew { ref name, ref typ, ref args } => { - let mut t_args = Vec::with_capacity( args.len() ) ; - for arg in args { - if let Some(t_arg) = arg.to_term() { - t_args.push(t_arg) - } else { - return None - } + /// Transforms a value into a term. + pub fn to_term(&self) -> Option<::term::Term> { + match self { + RVal::I(ref i) => Some(::term::int(i.clone())), + RVal::R(ref r) => Some(::term::real(r.clone())), + RVal::B(b) => Some(::term::bool(*b)), + RVal::N(_) => None, + RVal::Array { + ref idx_typ, + ref default, + ref vals, + } => { + let default = default + .to_term() + .expect("default of array cannot be non-value"); + let mut res = term::cst_array(idx_typ.clone(), default); + for (idx, val) in vals { + let (idx, val) = ( + idx.to_term().expect("index of arrays cannot be non-value"), + val.to_term().expect("value of arrays cannot be non-value"), + ); + res = term::store(res, idx, val); + } + Some(res) + } + RVal::DTypNew { + ref name, + ref typ, + ref args, + } => { + let mut t_args = Vec::with_capacity(args.len()); + for arg in args { + if let Some(t_arg) = arg.to_term() { + t_args.push(t_arg) + } else { + return None; + } + } + Some(term::dtyp_new(typ.clone(), name.clone(), t_args)) + } } - Some( - term::dtyp_new( typ.clone(), name.clone(), t_args ) - ) - }, } - } - /// Equal operator. - pub fn eql(& self, other: & Val) -> Val { - if ! self.is_known() || ! other.is_known() { - none(typ::bool()) - } else { - bool(self == other.get()) + /// Equal operator. + pub fn eql(&self, other: &Val) -> Val { + if !self.is_known() || !other.is_known() { + none(typ::bool()) + } else { + bool(self == other.get()) + } } - } - /// Ite operator. - pub fn ite(& self, thn: Val, els: Val) -> Res { - if thn == els { - return Ok(thn) - } + /// Ite operator. + pub fn ite(&self, thn: Val, els: Val) -> Res { + if thn == els { + return Ok(thn); + } - debug_assert_eq! { thn.typ(), els.typ() } + debug_assert_eq! { thn.typ(), els.typ() } - let res = match self.to_bool() ? { - Some(true) => thn, - Some(false) => els, - None => none( thn.typ() ), - } ; + let res = match self.to_bool()? { + Some(true) => thn, + Some(false) => els, + None => none(thn.typ()), + }; - Ok(res) - } + Ok(res) + } - /// Compares two values. - pub fn compare(& self, other: & Self) -> Option<::std::cmp::Ordering> { - match (self, other) { - (RVal::I(ref l), RVal::I(ref r)) => Some( l.cmp(r) ), - (RVal::R(ref l), RVal::R(ref r)) => Some( l.cmp(r) ), - (RVal::B(ref l), RVal::B(ref r)) => Some( l.cmp(r) ), - _ => None, + /// Compares two values. + pub fn compare(&self, other: &Self) -> Option<::std::cmp::Ordering> { + match (self, other) { + (RVal::I(ref l), RVal::I(ref r)) => Some(l.cmp(r)), + (RVal::R(ref l), RVal::R(ref r)) => Some(l.cmp(r)), + (RVal::B(ref l), RVal::B(ref r)) => Some(l.cmp(r)), + _ => None, + } } - } } - /// Arithmetic operations. impl RVal { - /// Checks if the value is zero (integer or rational). - pub fn is_zero(& self) -> bool { - use num::Zero ; - match self { - RVal::I(ref i) => i.is_zero(), - RVal::R(ref r) => r.is_zero(), - _ => false, + /// Checks if the value is zero (integer or rational). + pub fn is_zero(&self) -> bool { + use num::Zero; + match self { + RVal::I(ref i) => i.is_zero(), + RVal::R(ref r) => r.is_zero(), + _ => false, + } } - } - /// Checks if the value is one (integer or rational). - pub fn is_one(& self) -> bool { - use num::One ; - match self { - RVal::I(ref i) => i == & Int::one(), - RVal::R(ref r) => r == & Rat::one(), - _ => false, + /// Checks if the value is one (integer or rational). + pub fn is_one(&self) -> bool { + use num::One; + match self { + RVal::I(ref i) => i == &Int::one(), + RVal::R(ref r) => r == &Rat::one(), + _ => false, + } } - } - /// Checks if the value is minus one (integer or rational). - pub fn is_minus_one(& self) -> bool { - use num::One ; - match self { - RVal::I(ref i) => i == & - Int::one(), - RVal::R(ref r) => r == & - Rat::one(), - _ => false, + /// Checks if the value is minus one (integer or rational). + pub fn is_minus_one(&self) -> bool { + use num::One; + match self { + RVal::I(ref i) => i == &-Int::one(), + RVal::R(ref r) => r == &-Rat::one(), + _ => false, + } } - } - - /// Adds two values. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(35), val::mk(7)) ; - /// let res = lft.add(& rgt).unwrap() ; - /// # println!("{} + {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(42) } - /// lft = val::none( typ::int() ) ; - /// let res = lft.add(& rgt).unwrap() ; - /// # println!("{} + {} = {}", lft, rgt, res) ; - /// assert!{ ! res.is_known() } - /// rgt = val::mk(false) ; - /// let res = lft.add(& rgt) ; - /// # println!("{} + {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn add(& self, other: & Val) -> Res { - bin_op! { arith * self, +, * other.get() } - } - /// Subtracts two values. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(49), val::mk(7)) ; - /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; - /// assert_eq!{ lft.sub(& rgt).unwrap(), val::mk(42) } - /// lft = val::none( typ::int() ) ; - /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; - /// assert_eq!{ lft.sub(& rgt).unwrap(), val::none( typ::int() ) } - /// rgt = val::mk(false) ; - /// # println!("{} - {} = {:?}", lft, rgt, lft.sub(& rgt)) ; - /// assert!{ lft.sub(& rgt).is_err() } - /// ``` - pub fn sub(& self, other: & Val) -> Res { - bin_op! { arith * self, -, * other.get() } - } - /// Multiplies two values. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(6), val::mk(7)) ; - /// let res = lft.mul(& rgt).unwrap() ; - /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(42) } - /// lft = val::none(typ::int()) ; - /// let res = lft.mul(& rgt).unwrap() ; - /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, lft } - /// rgt = val::mk(0) ; - /// let res = lft.mul(& rgt).unwrap() ; - /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(0) } - /// rgt = val::mk((0, 1)) ; - /// let res = lft.mul(& rgt).unwrap() ; - /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk((0, 1)) } - /// lft = val::mk(7) ; - /// let res = lft.mul(& rgt).unwrap() ; - /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk((0, 1)) } - /// lft = val::mk(false) ; - /// let res = lft.mul(& rgt) ; - /// # println!("{} * {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn mul(& self, other: & Val) -> Res { - use num::Zero ; - match self { - - RVal::N(ref typ) if typ.is_int() => match other.get() { - RVal::I(ref i) if i.is_zero() => Ok( int(0) ), - RVal::R(ref r) if r.is_zero() => Ok( mk((0, 1)) ), - RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), - RVal::I(_) => Ok(none(typ.clone())), - RVal::R(_) => Ok(none(typ::real())), - ref rgt => bail!( - "expected arith values, found {} and {}", "_", rgt - ), - }, - - RVal::N(ref typ) if typ.is_real() => match other.get() { - RVal::I(ref i) if i.is_zero() => Ok( mk((0, 1)) ), - RVal::R(ref r) if r.is_zero() => Ok( mk((0, 1)) ), - RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), - RVal::I(_) => Ok(none(typ.clone())), - RVal::R(_) => Ok(none(typ::real())), - ref rgt => bail!( - "expected arith values, found {} and {}", "_", rgt - ), - }, - - RVal::I(ref lft) => match other.get() { - RVal::N(ref typ) if typ.is_arith() => match ( - typ.is_int(), lft.is_zero() - ) { - (_, false) => Ok( none(typ.clone()) ), - (true, true) => Ok( int(0) ), - (false, true) => Ok( real( Rat::new(0.into(), 0.into())) ), - }, - RVal::I(ref rgt) => Ok( int(lft * rgt) ), - RVal::R(ref rgt) => Ok( - real( Rat::new(lft * rgt.numer(), rgt.denom().clone()) ) - ), - ref rgt => bail!( - "expected arith values, found {} and {}", lft, rgt - ), - } - RVal::R(ref lft) => match other.to_real() ? { - None => if lft.is_zero() { - Ok( real( Rat::new(0.into(), 1.into()) ) ) - } else { - Ok( none(typ::real()) ) - }, - Some(rgt) => Ok( real(lft * rgt) ), - } - ref lft => bail!( - "expected arith values, found {} and {}", lft, other - ), + /// Adds two values. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(35), val::mk(7)) ; + /// let res = lft.add(& rgt).unwrap() ; + /// # println!("{} + {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(42) } + /// lft = val::none( typ::int() ) ; + /// let res = lft.add(& rgt).unwrap() ; + /// # println!("{} + {} = {}", lft, rgt, res) ; + /// assert!{ ! res.is_known() } + /// rgt = val::mk(false) ; + /// let res = lft.add(& rgt) ; + /// # println!("{} + {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn add(&self, other: &Val) -> Res { + bin_op! { arith * self, +, * other.get() } } - } + /// Subtracts two values. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(49), val::mk(7)) ; + /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; + /// assert_eq!{ lft.sub(& rgt).unwrap(), val::mk(42) } + /// lft = val::none( typ::int() ) ; + /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; + /// assert_eq!{ lft.sub(& rgt).unwrap(), val::none( typ::int() ) } + /// rgt = val::mk(false) ; + /// # println!("{} - {} = {:?}", lft, rgt, lft.sub(& rgt)) ; + /// assert!{ lft.sub(& rgt).is_err() } + /// ``` + pub fn sub(&self, other: &Val) -> Res { + bin_op! { arith * self, -, * other.get() } + } + /// Multiplies two values. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(6), val::mk(7)) ; + /// let res = lft.mul(& rgt).unwrap() ; + /// # println!("{} * {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(42) } + /// lft = val::none(typ::int()) ; + /// let res = lft.mul(& rgt).unwrap() ; + /// # println!("{} * {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, lft } + /// rgt = val::mk(0) ; + /// let res = lft.mul(& rgt).unwrap() ; + /// # println!("{} * {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(0) } + /// rgt = val::mk((0, 1)) ; + /// let res = lft.mul(& rgt).unwrap() ; + /// # println!("{} * {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk((0, 1)) } + /// lft = val::mk(7) ; + /// let res = lft.mul(& rgt).unwrap() ; + /// # println!("{} * {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk((0, 1)) } + /// lft = val::mk(false) ; + /// let res = lft.mul(& rgt) ; + /// # println!("{} * {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn mul(&self, other: &Val) -> Res { + use num::Zero; + match self { + RVal::N(ref typ) if typ.is_int() => match other.get() { + RVal::I(ref i) if i.is_zero() => Ok(int(0)), + RVal::R(ref r) if r.is_zero() => Ok(mk((0, 1))), + RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), + RVal::I(_) => Ok(none(typ.clone())), + RVal::R(_) => Ok(none(typ::real())), + ref rgt => bail!("expected arith values, found {} and {}", "_", rgt), + }, - /// Division. - pub fn div(& self, other: & Val) -> Res { - let res = if self.is_zero() { - real( Rat::new(0.into(), 1.into()) ) - } else { + RVal::N(ref typ) if typ.is_real() => match other.get() { + RVal::I(ref i) if i.is_zero() => Ok(mk((0, 1))), + RVal::R(ref r) if r.is_zero() => Ok(mk((0, 1))), + RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), + RVal::I(_) => Ok(none(typ.clone())), + RVal::R(_) => Ok(none(typ::real())), + ref rgt => bail!("expected arith values, found {} and {}", "_", rgt), + }, - match self { - - RVal::N(_) => none( typ::real() ), - - RVal::I(ref num) => match other.get() { - RVal::N(_) => none( typ::real() ), - RVal::I(ref den) => real( - Rat::new( num.clone(), den.clone() ) - ), - RVal::R(ref den) => real( - Rat::new( num * den.denom(), den.numer().clone() ) - ), - ref den => bail!( - "expected arith values, found {} and {}", num, den - ), - }, + RVal::I(ref lft) => match other.get() { + RVal::N(ref typ) if typ.is_arith() => match (typ.is_int(), lft.is_zero()) { + (_, false) => Ok(none(typ.clone())), + (true, true) => Ok(int(0)), + (false, true) => Ok(real(Rat::new(0.into(), 0.into()))), + }, + RVal::I(ref rgt) => Ok(int(lft * rgt)), + RVal::R(ref rgt) => Ok(real(Rat::new(lft * rgt.numer(), rgt.denom().clone()))), + ref rgt => bail!("expected arith values, found {} and {}", lft, rgt), + }, - RVal::R(ref num) => match other.get() { - RVal::N(_) => none( typ::real() ), - RVal::I(ref den) => real( num / den ), - RVal::R(ref den) => real( - Rat::new( num.numer() * den.denom(), num.denom() * den.numer() ) - ), - ref den => bail!( - "expected arith values, found {} and {}", num, den - ), + RVal::R(ref lft) => match other.to_real()? { + None => if lft.is_zero() { + Ok(real(Rat::new(0.into(), 1.into()))) + } else { + Ok(none(typ::real())) + }, + Some(rgt) => Ok(real(lft * rgt)), + }, + ref lft => bail!("expected arith values, found {} and {}", lft, other), } - ref num => bail!( - "expected arith values, found {} and {}", num, other - ), - } - } ; + } - Ok(res) - } + /// Division. + pub fn div(&self, other: &Val) -> Res { + let res = if self.is_zero() { + real(Rat::new(0.into(), 1.into())) + } else { + match self { + RVal::N(_) => none(typ::real()), + + RVal::I(ref num) => match other.get() { + RVal::N(_) => none(typ::real()), + RVal::I(ref den) => real(Rat::new(num.clone(), den.clone())), + RVal::R(ref den) => real(Rat::new(num * den.denom(), den.numer().clone())), + ref den => bail!("expected arith values, found {} and {}", num, den), + }, + + RVal::R(ref num) => match other.get() { + RVal::N(_) => none(typ::real()), + RVal::I(ref den) => real(num / den), + RVal::R(ref den) => real(Rat::new( + num.numer() * den.denom(), + num.denom() * den.numer(), + )), + ref den => bail!("expected arith values, found {} and {}", num, den), + }, + ref num => bail!("expected arith values, found {} and {}", num, other), + } + }; - /// Integer division. - pub fn idiv(& self, other: & Val) -> Res { - let num = try_val!( int self ) ; - let den = try_val!( int other ) ; - if den.is_zero() { - bail!("division by zero, aborting...") + Ok(res) } - let mut res = & num / & den ; - use num::Signed ; - if num.is_negative() ^ den.is_negative() - && den.clone() * & res != num { - res -= 1 - } - Ok( val::int(res) ) - } - /// Unary minus. - pub fn minus(& self) -> Res { - match self { - RVal::I(ref i) => Ok( int(- i) ), - RVal::R(ref r) => Ok( real(- r) ), - RVal::N(ref typ) if typ.is_arith() => Ok( none(typ.clone()) ), - _ => bail!( - "expected arith value, found {} ({})", self, self.typ() - ), + /// Integer division. + pub fn idiv(&self, other: &Val) -> Res { + let num = try_val!( int self ); + let den = try_val!( int other ); + if den.is_zero() { + bail!("division by zero, aborting...") + } + let mut res = &num / &den; + use num::Signed; + if num.is_negative() ^ den.is_negative() && den.clone() * &res != num { + res -= 1 + } + Ok(val::int(res)) } - } - - - /// Remainder. - pub fn rem(& self, other: & Val) -> Res { - use num::Integer ; - let b = try_val!(int self) ; - let res = if b.is_one() { - val::int(0) - } else { - let a = try_val!(int other) ; - val::int( a.div_rem(& b).1 ) - } ; - Ok(res) - } + /// Unary minus. + pub fn minus(&self) -> Res { + match self { + RVal::I(ref i) => Ok(int(-i)), + RVal::R(ref r) => Ok(real(-r)), + RVal::N(ref typ) if typ.is_arith() => Ok(none(typ.clone())), + _ => bail!("expected arith value, found {} ({})", self, self.typ()), + } + } - /// Modulo. - pub fn modulo(& self, other: & Val) -> Res { - use num::{ Integer, Signed } ; - let b = try_val!(int other) ; - let res = if b.is_one() { - val::int(0) - } else { - let a = try_val!(int self) ; - let res = a.mod_floor(& b) ; - let res = if res.is_negative() { - b.abs() - res.abs() - } else { - res - } ; - val::int(res) - } ; - Ok(res) - } + /// Remainder. + pub fn rem(&self, other: &Val) -> Res { + use num::Integer; + let b = try_val!(int self); + let res = if b.is_one() { + val::int(0) + } else { + let a = try_val!(int other); + val::int(a.div_rem(&b).1) + }; + Ok(res) + } + /// Modulo. + pub fn modulo(&self, other: &Val) -> Res { + use num::{Integer, Signed}; + let b = try_val!(int other); + let res = if b.is_one() { + val::int(0) + } else { + let a = try_val!(int self); + let res = a.mod_floor(&b); + let res = if res.is_negative() { + b.abs() - res.abs() + } else { + res + }; + val::int(res) + }; + Ok(res) + } - /// Greater than. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; - /// let res = lft.g_t(& rgt).unwrap() ; - /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// lft = val::none( typ::int() ) ; - /// let res = lft.g_t(& rgt).unwrap() ; - /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(103) ; - /// let res = lft.g_t(& rgt).unwrap() ; - /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((103,1)) ; - /// let res = lft.g_t(& rgt).unwrap() ; - /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; - /// let res = lft.g_t(& rgt).unwrap() ; - /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; - /// let res = lft.g_t(& rgt) ; - /// # println!("{} > {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn g_t(& self, other: & Val) -> Res { - #[allow(unused_parens)] - arith_bin_rel! { * self, gt, * other.get() } - } + /// Greater than. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; + /// let res = lft.g_t(& rgt).unwrap() ; + /// # println!("{} > {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// lft = val::none( typ::int() ) ; + /// let res = lft.g_t(& rgt).unwrap() ; + /// # println!("{} > {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(103) ; + /// let res = lft.g_t(& rgt).unwrap() ; + /// # println!("{} > {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// lft = val::mk((103,1)) ; + /// let res = lft.g_t(& rgt).unwrap() ; + /// # println!("{} > {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk((42,1)) ; + /// let res = lft.g_t(& rgt).unwrap() ; + /// # println!("{} > {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(false) ; + /// let res = lft.g_t(& rgt) ; + /// # println!("{} > {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn g_t(&self, other: &Val) -> Res { + #[allow(unused_parens)] + arith_bin_rel! { * self, gt, * other.get() } + } - /// Greater than or equal to. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; - /// let res = lft.g_e(& rgt).unwrap() ; - /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// lft = val::none( typ::int() ) ; - /// let res = lft.g_e(& rgt).unwrap() ; - /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(42) ; - /// let res = lft.g_e(& rgt).unwrap() ; - /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((42,1)) ; - /// let res = lft.g_e(& rgt).unwrap() ; - /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; - /// let res = lft.g_e(& rgt).unwrap() ; - /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; - /// let res = lft.g_e(& rgt) ; - /// # println!("{} >= {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn g_e(& self, other: & Val) -> Res { - arith_bin_rel! { * self, ge, * other.get() } - } + /// Greater than or equal to. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; + /// let res = lft.g_e(& rgt).unwrap() ; + /// # println!("{} >= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// lft = val::none( typ::int() ) ; + /// let res = lft.g_e(& rgt).unwrap() ; + /// # println!("{} >= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(42) ; + /// let res = lft.g_e(& rgt).unwrap() ; + /// # println!("{} >= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// lft = val::mk((42,1)) ; + /// let res = lft.g_e(& rgt).unwrap() ; + /// # println!("{} >= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk((42,1)) ; + /// let res = lft.g_e(& rgt).unwrap() ; + /// # println!("{} >= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(false) ; + /// let res = lft.g_e(& rgt) ; + /// # println!("{} >= {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn g_e(&self, other: &Val) -> Res { + arith_bin_rel! { * self, ge, * other.get() } + } - /// Less than or equal to. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; - /// let res = lft.l_e(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// lft = val::none( typ::int() ) ; - /// let res = lft.l_e(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(3) ; - /// let res = lft.l_e(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((3,1)) ; - /// let res = lft.l_e(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((3,1)) ; - /// let res = lft.l_e(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; - /// let res = lft.l_e(& rgt) ; - /// # println!("{} <= {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - /// Less than or equal to. - pub fn l_e(& self, other: & Val) -> Res { - arith_bin_rel! { * self, le, * other.get() } - } + /// Less than or equal to. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; + /// let res = lft.l_e(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// lft = val::none( typ::int() ) ; + /// let res = lft.l_e(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(3) ; + /// let res = lft.l_e(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// lft = val::mk((3,1)) ; + /// let res = lft.l_e(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk((3,1)) ; + /// let res = lft.l_e(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(false) ; + /// let res = lft.l_e(& rgt) ; + /// # println!("{} <= {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + /// Less than or equal to. + pub fn l_e(&self, other: &Val) -> Res { + arith_bin_rel! { * self, le, * other.get() } + } - /// Less than. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; - /// let res = lft.l_t(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// lft = val::none( typ::int() ) ; - /// let res = lft.l_t(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(2) ; - /// let res = lft.l_t(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((2,1)) ; - /// let res = lft.l_t(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; - /// let res = lft.l_t(& rgt).unwrap() ; - /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; - /// let res = lft.l_t(& rgt) ; - /// # println!("{} <= {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn l_t(& self, other: & Val) -> Res { - arith_bin_rel! { * self, lt, * other.get() } - } + /// Less than. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; + /// let res = lft.l_t(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// lft = val::none( typ::int() ) ; + /// let res = lft.l_t(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(2) ; + /// let res = lft.l_t(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// lft = val::mk((2,1)) ; + /// let res = lft.l_t(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk((42,1)) ; + /// let res = lft.l_t(& rgt).unwrap() ; + /// # println!("{} <= {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(false) ; + /// let res = lft.l_t(& rgt) ; + /// # println!("{} <= {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn l_t(&self, other: &Val) -> Res { + arith_bin_rel! { * self, lt, * other.get() } + } - /// Real to int conversion. - pub fn real_to_int(& self) -> Res { - let res = if let Some(rat) = self.to_real() ? { - let res = rat.denom() / rat.denom() ; - if rat.denom().is_negative() ^ rat.numer().is_negative() { - val::int(- res) - } else { - val::int(res) - } - } else { - val::none( typ::int() ) - } ; - Ok(res) - } + /// Real to int conversion. + pub fn real_to_int(&self) -> Res { + let res = if let Some(rat) = self.to_real()? { + let res = rat.denom() / rat.denom(); + if rat.denom().is_negative() ^ rat.numer().is_negative() { + val::int(-res) + } else { + val::int(res) + } + } else { + val::none(typ::int()) + }; + Ok(res) + } - /// Int to real conversion. - pub fn int_to_real(& self) -> Res { - let res = if let Some(int) = self.to_int() ? { - val::real( - Rat::new( int, 1.into() ) - ) - } else { - val::none( typ::real() ) - } ; - Ok(res) - } + /// Int to real conversion. + pub fn int_to_real(&self) -> Res { + let res = if let Some(int) = self.to_int()? { + val::real(Rat::new(int, 1.into())) + } else { + val::none(typ::real()) + }; + Ok(res) + } } - - /// Operations over booleans. impl RVal { + /// Conjunction. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(true), val::mk(false)) ; + /// let res = lft.and(& rgt).unwrap() ; + /// # println!("{} && {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// lft = val::none( typ::bool() ) ; + /// let res = lft.and(& rgt).unwrap() ; + /// # println!("{} && {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// rgt = val::mk(true) ; + /// let res = lft.and(& rgt).unwrap() ; + /// # println!("{} && {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(true) ; + /// let res = lft.and(& rgt).unwrap() ; + /// # println!("{} && {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(7) ; + /// let res = lft.and(& rgt) ; + /// # println!("{} && {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn and(&self, other: &Val) -> Res { + match (self, other.get()) { + (RVal::B(false), _) | (_, RVal::B(false)) => Ok(bool(false)), + (RVal::B(b_1), RVal::B(b_2)) => Ok(bool(*b_1 && *b_2)), - /// Conjunction. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(true), val::mk(false)) ; - /// let res = lft.and(& rgt).unwrap() ; - /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// lft = val::none( typ::bool() ) ; - /// let res = lft.and(& rgt).unwrap() ; - /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// rgt = val::mk(true) ; - /// let res = lft.and(& rgt).unwrap() ; - /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(true) ; - /// let res = lft.and(& rgt).unwrap() ; - /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(7) ; - /// let res = lft.and(& rgt) ; - /// # println!("{} && {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn and(& self, other: & Val) -> Res { - match (self, other.get()) { - (RVal::B(false), _) | - (_, RVal::B(false)) => Ok(bool(false)), - (RVal::B(b_1), RVal::B(b_2)) => Ok( - bool(* b_1 && * b_2) - ), - - (RVal::N(_), RVal::B(_)) | - (RVal::B(_), RVal::N(_)) | - (RVal::N(_), RVal::N(_)) => Ok(none(typ::bool())), + (RVal::N(_), RVal::B(_)) | (RVal::B(_), RVal::N(_)) | (RVal::N(_), RVal::N(_)) => { + Ok(none(typ::bool())) + } - (lft, rgt) => bail!( - "expected boolean values, found values of type {} and {}", - lft.typ(), rgt.typ() - ) + (lft, rgt) => bail!( + "expected boolean values, found values of type {} and {}", + lft.typ(), + rgt.typ() + ), + } } - } - /// Disjunction. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(false), val::mk(true)) ; - /// let res = lft.or(& rgt).unwrap() ; - /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::none( typ::bool() ) ; - /// let res = lft.or(& rgt).unwrap() ; - /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; - /// let res = lft.or(& rgt).unwrap() ; - /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(false) ; - /// let res = lft.or(& rgt).unwrap() ; - /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// rgt = val::mk(7) ; - /// let res = lft.or(& rgt) ; - /// # println!("{} || {} = {:?}", lft, rgt, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn or(& self, other: & Val) -> Res { - match (self, other.get()) { - (RVal::B(true), _) | - (_, RVal::B(true)) => Ok(bool(true)), - (RVal::B(b_1), RVal::B(b_2)) => Ok( - bool(* b_1 || * b_2) - ), + /// Disjunction. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let (mut lft, mut rgt) = (val::mk(false), val::mk(true)) ; + /// let res = lft.or(& rgt).unwrap() ; + /// # println!("{} || {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// lft = val::none( typ::bool() ) ; + /// let res = lft.or(& rgt).unwrap() ; + /// # println!("{} || {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(true) } + /// rgt = val::mk(false) ; + /// let res = lft.or(& rgt).unwrap() ; + /// # println!("{} || {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::none( typ::bool() ) } + /// lft = val::mk(false) ; + /// let res = lft.or(& rgt).unwrap() ; + /// # println!("{} || {} = {}", lft, rgt, res) ; + /// assert_eq!{ res, val::mk(false) } + /// rgt = val::mk(7) ; + /// let res = lft.or(& rgt) ; + /// # println!("{} || {} = {:?}", lft, rgt, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn or(&self, other: &Val) -> Res { + match (self, other.get()) { + (RVal::B(true), _) | (_, RVal::B(true)) => Ok(bool(true)), + (RVal::B(b_1), RVal::B(b_2)) => Ok(bool(*b_1 || *b_2)), - (RVal::N(_), RVal::B(_)) | - (RVal::B(_), RVal::N(_)) | - (RVal::N(_), RVal::N(_)) => Ok(none(typ::bool())), + (RVal::N(_), RVal::B(_)) | (RVal::B(_), RVal::N(_)) | (RVal::N(_), RVal::N(_)) => { + Ok(none(typ::bool())) + } - (lft, rgt) => bail!( - "expected boolean values, found values of type {} and {}", - lft.typ(), rgt.typ() - ) + (lft, rgt) => bail!( + "expected boolean values, found values of type {} and {}", + lft.typ(), + rgt.typ() + ), + } } - } - /// Negation. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val ; - /// let mut buru = val::mk(true) ; - /// let res = buru.not().unwrap() ; - /// # println!("not {} = {}", buru, res) ; - /// assert_eq!{ res, val::mk(false) } - /// buru = val::mk(false) ; - /// let res = buru.not().unwrap() ; - /// # println!("not {} = {}", buru, res) ; - /// assert_eq!{ res, val::mk(true) } - /// buru = val::none( typ::bool() ) ; - /// let res = buru.not().unwrap() ; - /// # println!("not {} = {}", buru, res) ; - /// assert_eq!{ res, buru } - /// buru = val::mk(7) ; - /// let res = buru.not() ; - /// # println!("not {} = {:?}", buru, res) ; - /// assert!{ res.is_err() } - /// ``` - pub fn not(& self) -> Res { - match self { - RVal::B(b) => Ok( bool(! b) ), - RVal::N(ref typ) if typ.is_bool() => Ok(none(typ.clone())), - RVal::N(ref typ) => bail!( - "expected boolean value, found non-value of type {}", typ - ), - _ => bail!( - "expected boolean value, found value of type {}", self.typ() - ), + /// Negation. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val ; + /// let mut buru = val::mk(true) ; + /// let res = buru.not().unwrap() ; + /// # println!("not {} = {}", buru, res) ; + /// assert_eq!{ res, val::mk(false) } + /// buru = val::mk(false) ; + /// let res = buru.not().unwrap() ; + /// # println!("not {} = {}", buru, res) ; + /// assert_eq!{ res, val::mk(true) } + /// buru = val::none( typ::bool() ) ; + /// let res = buru.not().unwrap() ; + /// # println!("not {} = {}", buru, res) ; + /// assert_eq!{ res, buru } + /// buru = val::mk(7) ; + /// let res = buru.not() ; + /// # println!("not {} = {:?}", buru, res) ; + /// assert!{ res.is_err() } + /// ``` + pub fn not(&self) -> Res { + match self { + RVal::B(b) => Ok(bool(!b)), + RVal::N(ref typ) if typ.is_bool() => Ok(none(typ.clone())), + RVal::N(ref typ) => bail!("expected boolean value, found non-value of type {}", typ), + _ => bail!("expected boolean value, found value of type {}", self.typ()), + } } - } - /// Implication. - pub fn implies(& self, other: & Val) -> Res { - let res = match ( self.to_bool() ?, other.to_bool() ? ) { - ( Some(false), _ ) | - ( _, Some(true) ) => bool(true), - ( None, _ ) | - ( _, None ) => none( typ::bool() ), - _ => bool(false), - } ; - Ok(res) - } + /// Implication. + pub fn implies(&self, other: &Val) -> Res { + let res = match (self.to_bool()?, other.to_bool()?) { + (Some(false), _) | (_, Some(true)) => bool(true), + (None, _) | (_, None) => none(typ::bool()), + _ => bool(false), + }; + Ok(res) + } - /// True iff the value is true. - pub fn is_true(& self) -> bool { - self == & RVal::B(true) - } + /// True iff the value is true. + pub fn is_true(&self) -> bool { + self == &RVal::B(true) + } - /// True iff the value is false. - pub fn is_false(& self) -> bool { - self == & RVal::B(false) - } + /// True iff the value is false. + pub fn is_false(&self) -> bool { + self == &RVal::B(false) + } } - - /// Operations over arrays. /// /// # Examples @@ -1285,220 +1214,234 @@ impl RVal { /// assert_eq! { array.select( none(typ::int()) ), none(typ::int()) } /// ``` impl RVal { - /// Store over arrays, creates a `RVal`. - /// - /// Does not actually create a `Val`. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val::* ; - /// - /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(0)) ; - /// assert_eq! { - /// & format!("{}", arr), "((as const (Array Int Int)) 0)" - /// } - /// - /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(1)) ; - /// assert_eq! { - /// & format!("{}", arr), "(store ((as const (Array Int Int)) 0) 7 1)" - /// } - /// ``` - pub fn raw_store>(& self, idx: V, val: V) -> Self { - let (idx, val) = ( idx.into(), val.into() ) ; - let idx = if ! idx.is_known() { - idx.typ().default_val() - } else { - idx - } ; - let val = if ! val.is_known() { - val.typ().default_val() - } else { - val - } ; - match self { - RVal::Array { ref idx_typ, ref default, ref vals } => { - debug_assert_eq! { idx_typ, & idx.typ() } - debug_assert_eq! { default.typ(), val.typ() } - debug_assert! { idx.is_known() } - debug_assert! { val.is_known() } - - let mut nu_vals: Vec<_> = vals.iter().filter_map( - |(i, v)| if i != & idx { - Some( (i.clone(), v.clone()) ) - } else { - None - } - ).collect() ; - - let (idx_typ, default) = ( - idx_typ.clone(), default.clone() - ) ; - if default != val { - nu_vals.push( (idx, val) ) - } - - nu_vals.sort_unstable() ; + /// Store over arrays, creates a `RVal`. + /// + /// Does not actually create a `Val`. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val::* ; + /// + /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(0)) ; + /// assert_eq! { + /// & format!("{}", arr), "((as const (Array Int Int)) 0)" + /// } + /// + /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(1)) ; + /// assert_eq! { + /// & format!("{}", arr), "(store ((as const (Array Int Int)) 0) 7 1)" + /// } + /// ``` + pub fn raw_store>(&self, idx: V, val: V) -> Self { + let (idx, val) = (idx.into(), val.into()); + let idx = if !idx.is_known() { + idx.typ().default_val() + } else { + idx + }; + let val = if !val.is_known() { + val.typ().default_val() + } else { + val + }; + match self { + RVal::Array { + ref idx_typ, + ref default, + ref vals, + } => { + debug_assert_eq! { idx_typ, & idx.typ() } + debug_assert_eq! { default.typ(), val.typ() } + debug_assert! { idx.is_known() } + debug_assert! { val.is_known() } + + let mut nu_vals: Vec<_> = vals + .iter() + .filter_map(|(i, v)| { + if i != &idx { + Some((i.clone(), v.clone())) + } else { + None + } + }).collect(); + + let (idx_typ, default) = (idx_typ.clone(), default.clone()); + if default != val { + nu_vals.push((idx, val)) + } + + nu_vals.sort_unstable(); + + return RVal::Array { + idx_typ, + default, + vals: nu_vals, + }; + } - return RVal::Array { idx_typ, default, vals: nu_vals } - }, + RVal::N(ref typ) => if let Some((i, v)) = typ.array_inspect() { + debug_assert_eq! { & idx.typ(), i } + debug_assert_eq! { & val.typ(), v } + let vals = vec![(idx, val)]; + return RVal::Array { + idx_typ: i.clone(), + default: v.default_val(), + vals, + }; + } else { + () + }, - RVal::N(ref typ) => if let Some((i, v)) = typ.array_inspect() { - debug_assert_eq! { & idx.typ(), i } - debug_assert_eq! { & val.typ(), v } - let vals = vec![ (idx, val) ] ; - return RVal::Array { - idx_typ: i.clone(), default: v.default_val(), vals + _ => (), } - } else { - () - }, - _ => (), + panic!( + "trying to store value {}: {} at {}: {} in non-array value {}: {}", + idx, + idx.typ(), + val, + val.typ(), + self, + self.typ() + ) } - panic!( - "trying to store value {}: {} at {}: {} in non-array value {}: {}", - idx, idx.typ(), val, val.typ(), self, self.typ() - ) - } - - /// Store over arrays. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val::* ; - /// - /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(0)) ; - /// assert_eq! { - /// & format!("{}", arr), "((as const (Array Int Int)) 0)" - /// } - /// - /// let arr = array( typ::int(), int(0) ).store(int(7), int(1)) ; - /// assert_eq! { - /// & format!("{}", arr), "(store ((as const (Array Int Int)) 0) 7 1)" - /// } - /// ``` - pub fn store>(& self, idx: V, val: V) -> Val { - factory.mk( self.raw_store(idx, val) ) - } - - /// Select over arrays. - /// - /// # Examples - /// - /// ``` - /// use hoice::term::typ ; - /// use hoice::val::* ; - /// - /// let array = array( typ::int(), int(0) ).store(int(7), int(1)) ; - /// assert_eq! { - /// & format!("{}", array), "(store ((as const (Array Int Int)) 0) 7 1)" - /// } - /// assert_eq! { array.select( int(7) ), int(1) } - /// assert_eq! { array.select( int(5) ), int(0) } - /// assert_eq! { array.select( int(0) ), int(0) } - /// assert_eq! { array.select( none(typ::int()) ), none(typ::int()) } - /// ``` - pub fn select>(& self, idx: V) -> Val { - let idx = idx.into() ; - match self { - RVal::Array { ref idx_typ, ref default, ref vals } => { - debug_assert_eq! { idx_typ, & idx.typ() } - - // If the index is a non-value... - if ! idx.is_known() { - // and the array is constant, return that value. - if vals.is_empty() { - return default.clone() - } else { - return none( default.typ().clone() ) - } - } + /// Store over arrays. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val::* ; + /// + /// let arr: RVal = array( typ::int(), int(0) ).raw_store(int(7), int(0)) ; + /// assert_eq! { + /// & format!("{}", arr), "((as const (Array Int Int)) 0)" + /// } + /// + /// let arr = array( typ::int(), int(0) ).store(int(7), int(1)) ; + /// assert_eq! { + /// & format!("{}", arr), "(store ((as const (Array Int Int)) 0) 7 1)" + /// } + /// ``` + pub fn store>(&self, idx: V, val: V) -> Val { + factory.mk(self.raw_store(idx, val)) + } - for (cond, val) in vals { - match Op::Eql.eval(vec![idx.clone(), cond.clone()]).and_then( - |res| res.to_bool() - ) { - Ok( Some(true) ) => return val.clone(), - Ok( Some(false) ) => (), - Ok(None) => panic!( - "non-value array condition (= {} {})", idx, cond - ), - Err(e) => { - print_err(& e) ; - panic!( - "while evaluating array condition (= {} {})", idx, cond - ) + /// Select over arrays. + /// + /// # Examples + /// + /// ``` + /// use hoice::term::typ ; + /// use hoice::val::* ; + /// + /// let array = array( typ::int(), int(0) ).store(int(7), int(1)) ; + /// assert_eq! { + /// & format!("{}", array), "(store ((as const (Array Int Int)) 0) 7 1)" + /// } + /// assert_eq! { array.select( int(7) ), int(1) } + /// assert_eq! { array.select( int(5) ), int(0) } + /// assert_eq! { array.select( int(0) ), int(0) } + /// assert_eq! { array.select( none(typ::int()) ), none(typ::int()) } + /// ``` + pub fn select>(&self, idx: V) -> Val { + let idx = idx.into(); + match self { + RVal::Array { + ref idx_typ, + ref default, + ref vals, + } => { + debug_assert_eq! { idx_typ, & idx.typ() } + + // If the index is a non-value... + if !idx.is_known() { + // and the array is constant, return that value. + if vals.is_empty() { + return default.clone(); + } else { + return none(default.typ().clone()); + } + } + + for (cond, val) in vals { + match Op::Eql + .eval(vec![idx.clone(), cond.clone()]) + .and_then(|res| res.to_bool()) + { + Ok(Some(true)) => return val.clone(), + Ok(Some(false)) => (), + Ok(None) => panic!("non-value array condition (= {} {})", idx, cond), + Err(e) => { + print_err(&e); + panic!("while evaluating array condition (= {} {})", idx, cond) + } + } + } + + return default.clone(); } - } - } - return default.clone() - }, - - RVal::N(ref typ) => if let Some((i, v)) = typ.array_inspect() { - debug_assert_eq! { i, & idx.typ() } - return none( v.clone() ) - } else { - () - }, + RVal::N(ref typ) => if let Some((i, v)) = typ.array_inspect() { + debug_assert_eq! { i, & idx.typ() } + return none(v.clone()); + } else { + () + }, - _ => (), + _ => (), + } + panic!( + "trying to select at {}: {} in non-array value {}: {}", + idx, + idx.typ(), + self, + self.typ() + ) } - panic!( - "trying to select at {}: {} in non-array value {}: {}", - idx, idx.typ(), self, self.typ() - ) - } - /// True if the value is composite (array or ADT). - pub fn is_composite(& self) -> bool { - match self { - RVal::Array { .. } | - RVal::DTypNew { .. } => true, - RVal::I(_) | RVal::R(_) | RVal::B(_) => false, - RVal::N(ref t) => t.is_dtyp() || t.is_array(), + /// True if the value is composite (array or ADT). + pub fn is_composite(&self) -> bool { + match self { + RVal::Array { .. } | RVal::DTypNew { .. } => true, + RVal::I(_) | RVal::R(_) | RVal::B(_) => false, + RVal::N(ref t) => t.is_dtyp() || t.is_array(), + } } - } } - - - /// Operation over datatype values. impl RVal { - /// Datatype selector. - pub fn dtyp_slc(& self, field: S) -> Option - where S: AsRef { - let field = field.as_ref() ; - if let Some((val_typ, constructor, args)) = self.dtyp_inspect() { - if let Some((dtyp, _)) = val_typ.dtyp_inspect() { - if let Some(params) = dtyp.news.get(constructor) { - debug_assert_eq! { params.len(), args.len() } - for ((name, _), arg) in params.iter().zip( args.iter() ) { - if name == field { - return Some( arg.clone() ) + /// Datatype selector. + pub fn dtyp_slc(&self, field: S) -> Option + where + S: AsRef, + { + let field = field.as_ref(); + if let Some((val_typ, constructor, args)) = self.dtyp_inspect() { + if let Some((dtyp, _)) = val_typ.dtyp_inspect() { + if let Some(params) = dtyp.news.get(constructor) { + debug_assert_eq! { params.len(), args.len() } + for ((name, _), arg) in params.iter().zip(args.iter()) { + if name == field { + return Some(arg.clone()); + } + } + } + } else { + panic!("inconsistent internal datatype term") } - } + } else { + panic!("inconsistent internal datatype selector term") } - } else { - panic!("inconsistent internal datatype term") - } - } else { - panic!("inconsistent internal datatype selector term") - } - None - } + None + } } - - - impl_fmt!{ RVal(self, fmt) { @@ -1563,67 +1506,71 @@ impl_fmt!{ } impl From for RVal { - fn from(b: bool) -> RVal { - RVal::B(b) - } + fn from(b: bool) -> RVal { + RVal::B(b) + } } impl From for RVal { - fn from(i: Int) -> RVal { - RVal::I(i) - } + fn from(i: Int) -> RVal { + RVal::I(i) + } } impl From for RVal { - fn from(r: Rat) -> RVal { - RVal::R(r) - } + fn from(r: Rat) -> RVal { + RVal::R(r) + } } impl From for RVal { - fn from(i: usize) -> RVal { - RVal::I( i.into() ) - } + fn from(i: usize) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(i: isize) -> RVal { - RVal::I( i.into() ) - } + fn from(i: isize) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(i: u32) -> RVal { - RVal::I( i.into() ) - } + fn from(i: u32) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(i: i32) -> RVal { - RVal::I( i.into() ) - } + fn from(i: i32) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(i: u64) -> RVal { - RVal::I( i.into() ) - } + fn from(i: u64) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(i: i64) -> RVal { - RVal::I( i.into() ) - } + fn from(i: i64) -> RVal { + RVal::I(i.into()) + } } impl From for RVal { - fn from(f: f64) -> RVal { - rat_of_float(f).into() - } + fn from(f: f64) -> RVal { + rat_of_float(f).into() + } } impl From<(Num, Den)> for RVal -where Num: Into, Den: Into { - fn from(rat: (Num, Den)) -> RVal { - RVal::R( Rat::new( rat.0.into(), rat.1.into() ) ) - } +where + Num: Into, + Den: Into, +{ + fn from(rat: (Num, Den)) -> RVal { + RVal::R(Rat::new(rat.0.into(), rat.1.into())) + } } -impl ::rsmt2::print::Expr2Smt<()> for RVal { - fn expr_to_smt2( - & self, w: & mut Writer, _: () - ) -> ::rsmt2::SmtRes<()> { - write!(w, "{}", self) ? ; - Ok(()) - } +impl Expr2Smt<()> for RVal { + fn expr_to_smt2(&self, w: &mut W, _: ()) -> ::rsmt2::SmtRes<()> + where + W: Write, + { + write!(w, "{}", self)?; + Ok(()) + } } diff --git a/src/var_to/mod.rs b/src/var_to/mod.rs index 970e33e8..0e5e832d 100644 --- a/src/var_to/mod.rs +++ b/src/var_to/mod.rs @@ -1,7 +1,7 @@ //! Hashconsed maps from variables to other things. -pub mod vals ; -pub mod terms ; +pub mod terms; +pub mod vals; -pub use self::vals::VarVals ; -pub use self::terms::VarTerms ; \ No newline at end of file +pub use self::terms::VarTerms; +pub use self::vals::VarVals; diff --git a/src/var_to/terms.rs b/src/var_to/terms.rs index c577305f..658b4967 100644 --- a/src/var_to/terms.rs +++ b/src/var_to/terms.rs @@ -1,9 +1,9 @@ /*! Hashconsed maps from variables to terms. */ -use hashconsing::{ HashConsign, HConsed } ; +use hashconsing::{HConsed, HashConsign}; -use common::* ; +use common::*; new_consign! { /// Term factory. @@ -11,13 +11,13 @@ new_consign! { } /// Hashconsed arguments for predicate applications. -pub type VarTerms = HConsed< VarMap > ; +pub type VarTerms = HConsed>; /// Set of hashconsed arguments for predicate applications. -pub type VarTermsSet = HConSet ; +pub type VarTermsSet = HConSet; /// Map from arguments for predicate applications to something. -pub type VarTermsMap = HConMap ; +pub type VarTermsMap = HConMap; /// Creates some new arguments. pub fn new(args: VarMap) -> VarTerms { - factory.mk(args) -} \ No newline at end of file + factory.mk(args) +} diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index f43567bf..ed21a6da 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -1,11 +1,11 @@ /*! Hashconsed maps from variables to terms. */ -use std::cmp::Ordering ; +use std::cmp::Ordering; -use hashconsing::{ HashConsign, HConsed } ; +use hashconsing::{HConsed, HashConsign}; -use common::* ; +use common::*; new_consign! { /// Term factory. @@ -14,31 +14,27 @@ new_consign! { /// Creates hashconsed arguments. pub fn new>(args: RA) -> VarVals { - factory.mk( args.into() ) + factory.mk(args.into()) } /// Creates hashconsed arguments, returns `true` if the arguments are actually /// new. pub fn new_is_new>(args: RA) -> (VarVals, bool) { - factory.mk_is_new( args.into() ) + factory.mk_is_new(args.into()) } /// Creates hashconsed arguments from iterators. -pub fn of, A: IntoIterator>( - args: A -) -> VarVals { - let mut map = VarMap::new() ; - for val in args { - map.push( val.into() ) - } - new(map) +pub fn of, A: IntoIterator>(args: A) -> VarVals { + let mut map = VarMap::new(); + for val in args { + map.push(val.into()) + } + new(map) } - - /// Mapping from variables to values, used for learning data. #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct RVarVals { - /// Internal map. - map: VarMap, + /// Internal map. + map: VarMap, } impl_fmt! { @@ -47,254 +43,250 @@ impl_fmt! { } } impl ::std::ops::Deref for RVarVals { - type Target = VarMap ; - fn deref(& self) -> & VarMap { & self.map } + type Target = VarMap; + fn deref(&self) -> &VarMap { + &self.map + } } impl ::std::ops::DerefMut for RVarVals { - fn deref_mut(& mut self) -> & mut VarMap { & mut self.map } + fn deref_mut(&mut self) -> &mut VarMap { + &mut self.map + } } -impl From< VarMap > for RVarVals { - fn from(map: VarMap) -> Self { - RVarVals { map } - } +impl From> for RVarVals { + fn from(map: VarMap) -> Self { + RVarVals { map } + } } impl Evaluator for RVarVals { - #[inline] - fn get(& self, var: VarIdx) -> & Val { - & self.map[var] - } - #[inline] - fn len(& self) -> usize { VarMap::len(& self.map) } - fn print(& self) { - println!("varvals@") ; - self.map.print() ; - } + #[inline] + fn get(&self, var: VarIdx) -> &Val { + &self.map[var] + } + #[inline] + fn len(&self) -> usize { + VarMap::len(&self.map) + } + fn print(&self) { + println!("varvals@"); + self.map.print(); + } } impl RVarVals { + /// Constructor with some capacity. + pub fn with_capacity(capa: usize) -> Self { + Self { + map: VarMap::with_capacity(capa), + } + } - /// Constructor with some capacity. - pub fn with_capacity(capa: usize) -> Self { - Self { map: VarMap::with_capacity(capa) } - } - - /// True if at least one value is `Val::N`. - pub fn is_partial(& self) -> bool { - for v in self.iter() { - if ! v.is_known() { return true } + /// True if at least one value is `Val::N`. + pub fn is_partial(&self) -> bool { + for v in self.iter() { + if !v.is_known() { + return true; + } + } + false } - false - } - /// True if the two args are semantically the same. - /// - /// "Semantically" here means that `Val::N` is not equal to itself. - pub fn equal(& self, other: & Self) -> bool { - for (v_1, v_2) in self.map.iter().zip( other.map.iter() ) { - if ! v_1.equal(v_2) { return false } + /// True if the two args are semantically the same. + /// + /// "Semantically" here means that `Val::N` is not equal to itself. + pub fn equal(&self, other: &Self) -> bool { + for (v_1, v_2) in self.map.iter().zip(other.map.iter()) { + if !v_1.equal(v_2) { + return false; + } + } + true } - true - } - /// Constructor from a model. - pub fn of_model( - info: & VarMap<::instance::info::VarInfo>, - model: Vec<(VarIdx, T, Val)>, - partial: bool, - ) -> Res { - let mut slf = RVarVals { - map: info.iter().map( - |info| if partial { - val::none(info.typ.clone()) - } else { - info.typ.default_val() + /// Constructor from a model. + pub fn of_model( + info: &VarMap<::instance::info::VarInfo>, + model: Vec<(VarIdx, T, Val)>, + partial: bool, + ) -> Res { + let mut slf = RVarVals { + map: info + .iter() + .map(|info| { + if partial { + val::none(info.typ.clone()) + } else { + info.typ.default_val() + } + }).collect(), + }; + for (var, _, val) in model { + slf[var] = val.cast(&info[var].typ)?; } - ).collect() - } ; - for (var, _, val) in model { - slf[var] = val.cast(& info[var].typ) ? ; + Ok(slf) } - Ok(slf) - } - /// Evaluates some arguments and yields the resulting `VarMap`. - pub fn apply_to( - & self, args: & VarMap<::term::Term> - ) -> ::errors::Res { - let mut res = Self::with_capacity( args.len() ) ; - for arg in args { - res.push( arg.eval(self) ? ) + /// Evaluates some arguments and yields the resulting `VarMap`. + pub fn apply_to(&self, args: &VarMap<::term::Term>) -> ::errors::Res { + let mut res = Self::with_capacity(args.len()); + for arg in args { + res.push(arg.eval(self)?) + } + Ok(res) } - Ok(res) - } } - - - /// Hash consed arguments. -pub type VarVals = HConsed ; +pub type VarVals = HConsed; /// A set of hashconsed arguments. -pub type VarValsSet = HConSet ; +pub type VarValsSet = HConSet; /// A map from hashconsed arguments to something. -pub type VarValsMap = HConMap ; - - +pub type VarValsMap = HConMap; /// Helper functions for `VarVals`. pub trait SubsumeExt { - /// Type of the sets we want to check for subsumption. - type Set ; - - /// Compares two arguments w.r.t. subsumption. - /// - /// Returns - /// - /// - `Some(Greater)` if `self` subsumes `other`, - /// - `Some(Equal)` if `self` is equal to `other`, - /// - `Some(Less)` if `other` subsumes `self`, - /// - `None` if `self` and `other` cannot be compared. - /// - /// Returns an error if `self` and `other` do not have the same length. - fn compare(& self, other: & Self) -> Option ; - - /// True if two samples are complementary. - /// - /// Two samples are complementary iff for all `i` - /// - /// - `v_i` is a non-value, or - /// - `v'_i` is a non-value, or - /// - `v_1 == v'_i`. - fn is_complementary(& self, other: & Self) -> bool ; - - /// Returns true if the two samples are related. - /// - /// Two samples are related if they're equal or one subsumes the other. - fn is_related_to(& self, other: & Self) -> bool { - self.compare(other).is_some() - } - - /// True iff `self` subsumes or is equal to `other`. - fn subsumes(& self, other: & Self) -> bool { - match self.compare(other) { - Some(Ordering::Greater) | Some(Ordering::Equal) => true, - _ => false, + /// Type of the sets we want to check for subsumption. + type Set; + + /// Compares two arguments w.r.t. subsumption. + /// + /// Returns + /// + /// - `Some(Greater)` if `self` subsumes `other`, + /// - `Some(Equal)` if `self` is equal to `other`, + /// - `Some(Less)` if `other` subsumes `self`, + /// - `None` if `self` and `other` cannot be compared. + /// + /// Returns an error if `self` and `other` do not have the same length. + fn compare(&self, other: &Self) -> Option; + + /// True if two samples are complementary. + /// + /// Two samples are complementary iff for all `i` + /// + /// - `v_i` is a non-value, or + /// - `v'_i` is a non-value, or + /// - `v_1 == v'_i`. + fn is_complementary(&self, other: &Self) -> bool; + + /// Returns true if the two samples are related. + /// + /// Two samples are related if they're equal or one subsumes the other. + fn is_related_to(&self, other: &Self) -> bool { + self.compare(other).is_some() } - } - /// True iff `other` subsumes or is equal to `self`. - fn subsumed(& self, other: & Self) -> bool { - other.subsumes(self) - } - /// Checks whether `self` is subsumed by anything in the set. - /// - /// Returns `(b, n)`: - /// - /// - `b` indicates whether `self` is subsumed - /// - `n` is the number of elements removed because they were subsumed - /// by `self` - /// - /// Generally speaking, it is expected that `n > 0 => ! b`. In particular, if - /// `self` is in the set the expected output is `(true, 0)`. - fn set_subsumed_rm(& self, set: & mut Self::Set) -> (bool, usize) ; + /// True iff `self` subsumes or is equal to `other`. + fn subsumes(&self, other: &Self) -> bool { + match self.compare(other) { + Some(Ordering::Greater) | Some(Ordering::Equal) => true, + _ => false, + } + } + /// True iff `other` subsumes or is equal to `self`. + fn subsumed(&self, other: &Self) -> bool { + other.subsumes(self) + } - /// Checks whether `self` is subsumed by anything in the set. - /// - /// Same as `set_subsumed_rm`, but does remove anything. - fn set_subsumed(& self, set: & Self::Set) -> bool ; + /// Checks whether `self` is subsumed by anything in the set. + /// + /// Returns `(b, n)`: + /// + /// - `b` indicates whether `self` is subsumed + /// - `n` is the number of elements removed because they were subsumed + /// by `self` + /// + /// Generally speaking, it is expected that `n > 0 => ! b`. In particular, if + /// `self` is in the set the expected output is `(true, 0)`. + fn set_subsumed_rm(&self, set: &mut Self::Set) -> (bool, usize); + + /// Checks whether `self` is subsumed by anything in the set. + /// + /// Same as `set_subsumed_rm`, but does remove anything. + fn set_subsumed(&self, set: &Self::Set) -> bool; } impl SubsumeExt for VarVals { - type Set = VarValsSet ; + type Set = VarValsSet; - fn is_complementary(& self, other: & Self) -> bool { - for (val, other_val) in self.iter().zip( other.iter() ) { - if val.is_known() && other_val.is_known() && val != other_val { - return false - } + fn is_complementary(&self, other: &Self) -> bool { + for (val, other_val) in self.iter().zip(other.iter()) { + if val.is_known() && other_val.is_known() && val != other_val { + return false; + } + } + true } - true - } - - fn compare(& self, other: & Self) -> Option { - debug_assert_eq! { self.len(), other.len() } - - if self == other { return Some(Ordering::Equal) } - if ! conf.teacher.partial { - None - } else { + fn compare(&self, other: &Self) -> Option { + debug_assert_eq! { self.len(), other.len() } - let (mut less, mut greater) = (true, true) ; - - for (val, other_val) in self.iter().zip( other.iter() ) { - greater = greater && ( - ! val.is_known() || val == other_val - ) ; - less = less && ( - ! other_val.is_known() || val == other_val - ) ; - } - - match (less, greater) { - (false, false) => None, - (true, false) => Some(Ordering::Less), - (false, true) => Some(Ordering::Greater), - (true, true) => fail_with!( - "Fatal error in sample hashconsing" - ), - } + if self == other { + return Some(Ordering::Equal); + } + if !conf.teacher.partial { + None + } else { + let (mut less, mut greater) = (true, true); + + for (val, other_val) in self.iter().zip(other.iter()) { + greater = greater && (!val.is_known() || val == other_val); + less = less && (!other_val.is_known() || val == other_val); + } + + match (less, greater) { + (false, false) => None, + (true, false) => Some(Ordering::Less), + (false, true) => Some(Ordering::Greater), + (true, true) => fail_with!("Fatal error in sample hashconsing"), + } + } } - } - fn set_subsumed(& self, set: & Self::Set) -> bool { - if ! conf.teacher.partial { - set.contains(self) - } else { - for elem in set.iter() { - if elem.subsumes(self) { - return true + fn set_subsumed(&self, set: &Self::Set) -> bool { + if !conf.teacher.partial { + set.contains(self) + } else { + for elem in set.iter() { + if elem.subsumes(self) { + return true; + } + } + false } - } - false } - } - fn set_subsumed_rm( - & self, set: & mut VarValsSet - ) -> (bool, usize) { - if ! conf.teacher.partial { - (set.contains(self), 0) - } else if ! self.is_partial() { - for elem in set.iter() { - if elem.subsumes(self) { - return { (true, 0) } - } - } - (false, 0) - } else { - let mut subsumed = false ; - let mut count = 0 ; - set.retain( - |other| match self.compare(other) { - Some(Ordering::Equal) => { - subsumed = true ; - true - }, - Some(Ordering::Greater) => { - count += 1 ; - false - }, - Some(Ordering::Less) => { - subsumed = true ; - true - }, - None => true, + fn set_subsumed_rm(&self, set: &mut VarValsSet) -> (bool, usize) { + if !conf.teacher.partial { + (set.contains(self), 0) + } else if !self.is_partial() { + for elem in set.iter() { + if elem.subsumes(self) { + return { (true, 0) }; + } + } + (false, 0) + } else { + let mut subsumed = false; + let mut count = 0; + set.retain(|other| match self.compare(other) { + Some(Ordering::Equal) => { + subsumed = true; + true + } + Some(Ordering::Greater) => { + count += 1; + false + } + Some(Ordering::Less) => { + subsumed = true; + true + } + None => true, + }); + (subsumed, count) } - ) ; - (subsumed, count) } - } -} \ No newline at end of file +} From eae38a4605e37c20e5fee615cccbe0e26f613529 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 5 Sep 2018 20:47:05 +0900 Subject: [PATCH 46/94] rustfmt --- src/parse/mod.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 79da938c..b4dde8d9 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -862,10 +862,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { // Parse closing paren. self.ws_cmt(); if !self.tag_opt(")") { - Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) - .chain_err(|| { - self.error(current_pos, "expected expected closing paren") - })? + let err: Error = + self.error(pos, "while parsing this array sort").into(); + Err(err).chain_err(|| { + self.error(current_pos, "expected expected closing paren") + })? } continue 'go_up; @@ -886,7 +887,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { if let Ok(dtyp) = dtyp::get(name) { typ = Some(typ::dtyp(dtyp, typs)) } else { - bail!(self.error(pos, format!("unknown sort `{}`", conf.bad(name)))) + let msg = format!("unknown sort `{}`", conf.bad(name)); + bail!(self.error(pos, msg)) } continue 'go_up; } else { @@ -1040,10 +1042,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { // Parse closing paren. self.ws_cmt(); if !self.tag_opt(")") { - Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) - .chain_err(|| { - self.error(current_pos, "expected expected closing paren") - })? + let err: Error = + self.error(pos, "while parsing this array sort").into(); + Err(err).chain_err(|| { + self.error(current_pos, "expected expected closing paren") + })? } continue 'go_up; @@ -2403,7 +2406,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.cxt.term_stack.push(frame); continue 'read_kids; } else if self.functions.get(id).is_some() || fun::get(id).is_some() { - let frame = TermFrame::new(FrameOp::Fun(id.into()), op_pos, bind_count); + let op = FrameOp::Fun(id.into()); + let frame = TermFrame::new(op, op_pos, bind_count); self.cxt.term_stack.push(frame); continue 'read_kids; } From e18eed374af297978c57b554a3c633b96083d616 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 6 Sep 2018 11:31:33 +0900 Subject: [PATCH 47/94] minor bugfix --- src/instance/instance/clause.rs | 8 +++++--- src/instance/instance/pre_instance.rs | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index aed4085d..7181a70e 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -189,11 +189,13 @@ impl Clause { /// Turns the clause into a negative one. #[inline] pub fn unset_rhs(&mut self) -> Option { - let mut old_rhs = None; - ::std::mem::swap(&mut self.rhs, &mut old_rhs); - if (old_rhs.is_some() && self.lhs_preds.is_empty()) || old_rhs.is_some() { + let old_rhs = ::std::mem::replace(&mut self.rhs, None); + if old_rhs.is_some() && self.lhs_preds.is_empty() { self.terms_changed = true } + if old_rhs.is_some() { + self.preds_changed = true + } old_rhs } diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index cfab3323..d56dfe91 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -841,11 +841,13 @@ impl<'a> PreInstance<'a> { self.instance .unlink_pred_rhs(pred, &mut self.clauses_to_simplify); for clause in &self.clauses_to_simplify { + let old_rhs = self.instance.clauses[*clause].unset_rhs(); debug_assert_eq! { - self.instance.clauses[* clause].rhs().map(|(p, _)| p), Some(pred) + old_rhs.map(|(p, _)| p), Some(pred) + } + debug_assert! { + self.instance.clauses[*clause].preds_changed() } - self.instance.clauses[*clause].unset_rhs(); - debug_assert! { self.instance.clauses[* clause].preds_changed() } } info += self.simplify_clauses()?; From 31d27094e84e77b4cfc333363c9aeafb3c92365f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 6 Sep 2018 16:51:11 +0900 Subject: [PATCH 48/94] assistant can now generalize samples (make them more partial) --- src/teacher/assistant.rs | 365 +++++++++++++++++++++------------------ 1 file changed, 194 insertions(+), 171 deletions(-) diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 12c68129..9edde3e0 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -13,6 +13,63 @@ pub enum ForceRes { Neg { sample: Sample, clause: ClsIdx }, } +/// Stores data from a positive / strict negative clause. +/// +/// # Examples +/// +/// If the clause is `forall x, y | x > 0 => p(x + 1, y)` then the data stored is +/// +/// | | | +/// |:----:|:-------------------------| +/// | conj | `x > 0` | +/// | args | `v_0 -> x + 1, v_1 -> y` | +/// | vars | `v_0` | +#[derive(Clone, Debug)] +pub struct ClauseData { + /// Index of the clause. + pub idx: ClsIdx, + /// Conjunction of lhs terms. + pub conj: Term, + /// Map from the clause's **only** predicate to the clause's variables. + pub args: VarTerms, + /// Variables of the predicate that are relevant w.r.t. `conj`. None if all are relevant. + pub vars: Option, + /// True if the clause is a positive one. + pub pos: bool, +} +impl ClauseData { + /// Constructor. + pub fn new(idx: ClsIdx, pos: bool, args: &VarTerms, lhs_terms: &TermSet) -> Self { + let conj = term::and(lhs_terms.iter().cloned().collect()); + let args = args.clone(); + let conj_vars = term::vars(&conj); + + let mut vars = VarSet::new(); + for (pred_var, arg) in args.index_iter() { + if let Some(var) = arg.var_idx() { + if !conj_vars.contains(&var) { + continue; + } + } + let is_new = vars.insert(pred_var); + debug_assert! { is_new } + } + let vars = if vars.len() == args.len() { + None + } else { + Some(vars) + }; + + ClauseData { + idx, + conj, + args, + vars, + pos, + } + } +} + /// Propagates examples, tries to break implication constraints. pub struct Assistant { /// Core, to communicate with the teacher. @@ -21,80 +78,88 @@ pub struct Assistant { solver: Solver<()>, /// Instance. instance: Arc, - /// Positive constraints. - pos: PrdHMap, - /// Negative constraints. - neg: PrdHMap, /// Profiler. _profiler: Profiler, /// True if we're using ADTs. using_adts: bool, + /// Maps predicates to their positive / strict negative clause data. + clauses: PrdHMap>, } impl Assistant { /// Constructor. - pub fn new(instance: Arc // core: & 'a MsgCore - ) -> Res { + pub fn new(instance: Arc) -> Res { let solver = conf.solver.spawn("assistant", (), &instance)?; let _profiler = Profiler::new(); - let mut pos = PrdHMap::with_capacity(instance.preds().len()); - let mut neg = PrdHMap::with_capacity(instance.preds().len()); + let using_adts = dtyp::get_all().iter().next().is_some(); - let mut pos_clauses = ClsSet::new(); - let mut neg_clauses = ClsSet::new(); + let clauses = PrdHMap::new(); - let using_adts = dtyp::get_all().iter().next().is_some(); + let mut res = Assistant { + // core, + solver, + instance, + _profiler, + using_adts, + clauses, + }; - macro_rules! add_clauses { - ($pred:expr) => {{ - if !pos_clauses.is_empty() { - let mut clause_set = ClsSet::new(); - ::std::mem::swap(&mut pos_clauses, &mut clause_set); - let prev = pos.insert($pred, clause_set); - debug_assert! { prev.is_none() } - } - if !neg_clauses.is_empty() { - let mut clause_set = ClsSet::new(); - ::std::mem::swap(&mut neg_clauses, &mut clause_set); - let prev = neg.insert($pred, clause_set); - debug_assert! { prev.is_none() } - } - }}; - } + res.register_clauses()?; + + Ok(res) + } - for pred in instance.pred_indices() { - debug_assert! { pos_clauses.is_empty() } - debug_assert! { neg_clauses.is_empty() } + /// Registers all positive / strict negative clauses. + fn register_clauses(&mut self) -> Res<()> { + let instance = self.instance.clone(); + for clause_idx in instance.pos_clauses() { + let clause = &instance[*clause_idx]; - for clause in instance.clauses_of(pred).1 { - let clause = *clause; - if instance[clause].lhs_preds().is_empty() { - let is_new = pos_clauses.insert(clause); - debug_assert! { is_new } - } + debug_assert! { clause.lhs_preds().is_empty() } + + if let Some((pred, args)) = clause.rhs() { + self.register(*clause_idx, pred, args, clause.lhs_terms(), true) + } else { + bail!("inconsistent positive clause set from instance") } + } - for clause in instance.clauses_of(pred).0 { - let clause = *clause; - if instance[clause].rhs().is_none() && instance[clause].lhs_pred_apps_len() == 1 { - let is_new = neg_clauses.insert(clause); - debug_assert! { is_new } + for clause_idx in instance.strict_neg_clauses() { + let clause = &instance[*clause_idx]; + + debug_assert! { clause.rhs().is_none() } + debug_assert! { clause.lhs_preds().len() == 1 } + + if let Some((pred, argss)) = clause.lhs_preds().iter().next() { + debug_assert! { argss.len() == 1 } + + if let Some(args) = argss.iter().next() { + self.register(*clause_idx, *pred, args, clause.lhs_terms(), false) + } else { + bail!("inconsistent clause state") } + } else { + bail!("inconsistent strict negative clause set from instance") } - - add_clauses!(pred) } - Ok(Assistant { - // core, - solver, - instance, - pos, - neg, - _profiler, - using_adts, - }) + Ok(()) + } + + /// Registers some clause data for a predicate. + /// + /// Boolean flag indicates whether the original clause is positive or not. + fn register( + &mut self, + idx: ClsIdx, + pred: PrdIdx, + args: &VarTerms, + lhs_terms: &TermSet, + pos: bool, + ) { + let data = ClauseData::new(idx, pos, args, lhs_terms); + self.clauses.entry(pred).or_insert_with(Vec::new).push(data) } /// Destroys the assistant. @@ -141,6 +206,7 @@ impl Assistant { if trivial || lhs_unknown == 0 || rhs_false && lhs_unknown == 1 { profile! { self "constraints broken" => add 1 } } + // Discard the constraint, regardless of what will happen. profile! { self tick "data" } data.tautologize(cstr)?; @@ -220,109 +286,89 @@ impl Assistant { /// - `ForceRes::Neg` of a sample which, when forced negative, will force the /// input sample to be classified negative. pub fn try_force(&mut self, _data: &Data, pred: PrdIdx, vals: &VarVals) -> Res { + let clause_data = if let Some(data) = self.clauses.get(&pred) { + data + } else { + return Ok(ForceRes::None); + }; + self.solver.comment_args(format_args!( "working on sample ({} {})", self.instance[pred], vals ))?; - if let Some(clauses) = self.pos.get(&pred) { - self.solver.comment("working on positive clauses")?; + macro_rules! solver { + (push) => { + if !self.using_adts { + self.solver.push(1)? + } + }; + (pop) => { + if self.using_adts { + smt::reset(&mut self.solver, &self.instance)? + } else { + self.solver.pop(1)? + } + }; + } - for clause_idx in clauses { - let clause_idx = *clause_idx; - let clause = &self.instance[clause_idx]; - if let Some((p, args)) = clause.rhs() { - debug_assert_eq! { pred, p } - debug_assert! { clause.lhs_preds().is_empty() } + for ClauseData { + idx, + conj, + args, + vars, + pos, + } in clause_data + { + self.solver.comment_args(format_args!( + "working on positive clauses with lhs {}", + conj + ))?; - if self.using_adts { - smt::reset(&mut self.solver, &self.instance)? - } else { - self.solver.push(1)? - } + let clause = *idx; - clause.declare(&mut self.solver)?; - self.solver.assert(&ConjWrap::new(clause.lhs_terms()))?; + solver!(push); - self.solver.assert(&ArgValEq::new(args, vals))?; - let sat = profile! { - self wrap { - self.solver.check_sat() ? - } "smt" - }; + self.instance[clause].declare(&mut self.solver)?; - if self.using_adts { - smt::reset(&mut self.solver, &self.instance)? - } else { - self.solver.pop(1)? - } + self.solver.assert(&smt::SmtTerm::new(conj))?; + self.solver + .assert(&ArgValEq::new(args, vals, vars.as_ref()))?; - if sat { - // msg! { debug self => " forcing positive" } - return Ok(ForceRes::Pos { - sample: Sample { - pred, - args: vals.clone(), - }, - clause: clause_idx, - }); - } - } else { - bail!("inconsistent instance state") - } - } - } + let sat = profile! { + self wrap { self.solver.check_sat() } "smt" + }?; + + solver!(pop); - if let Some(clauses) = self.neg.get(&pred) { - self.solver.comment("working on negative clauses")?; - - for clause_idx in clauses { - let clause_idx = *clause_idx; - let clause = &self.instance[clause_idx]; - if let Some(argss) = clause.lhs_preds().get(&pred) { - let args = { - let mut argss = argss.iter(); - if let Some(args) = argss.next() { - debug_assert! { argss.next().is_none() } - args + if sat { + let args = if let Some(vars) = vars { + let mut nu_vals = var_to::vals::RVarVals::with_capacity(vals.len()); + for (idx, val) in vals.index_iter() { + if vars.contains(&idx) { + nu_vals.push(val.clone()) } else { - bail!("inconsistent instance state") + nu_vals.push(val::none(val.typ())) } - }; - - if self.using_adts { - smt::reset(&mut self.solver, &self.instance)? - } else { - self.solver.push(1)? } + var_to::vals::new(nu_vals) + } else { + vals.clone() + }; - clause.declare(&mut self.solver)?; - self.solver.assert(&ConjWrap::new(clause.lhs_terms()))?; - self.solver.assert(&ArgValEq::new(args, vals))?; - let sat = profile! { - self wrap { - self.solver.check_sat() ? - } "smt" - }; - - if self.using_adts { - smt::reset(&mut self.solver, &self.instance)? - } else { - self.solver.pop(1)? - } + self.solver.comment_args(format_args!( + "success, yielding {} sample ({} {})", + if *pos { "positive" } else { "negative" }, + self.instance[pred], + args + ))?; - if sat { - // msg! { debug self => " forcing negative" } - return Ok(ForceRes::Neg { - sample: Sample { - pred, - args: vals.clone(), - }, - clause: clause_idx, - }); - } + let sample = Sample { pred, args }; + + if *pos { + return Ok(ForceRes::Pos { sample, clause }); } else { - bail!("inconsistent instance state") + return Ok(ForceRes::Neg { sample, clause }); } } } @@ -331,33 +377,6 @@ impl Assistant { } } -/// Wrapper around a conjunction for smt printing. -struct ConjWrap<'a> { - /// Conjunction. - terms: &'a TermSet, -} -impl<'a> ConjWrap<'a> { - /// Constructor. - pub fn new(terms: &'a TermSet) -> Self { - ConjWrap { terms } - } -} -impl<'a> Expr2Smt<()> for ConjWrap<'a> { - fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { - if self.terms.is_empty() { - write!(w, "true")? - } else { - write!(w, "(and")?; - for term in self.terms { - write!(w, " ")?; - term.write(w, |w, var| var.default_write(w))?; - } - write!(w, ")")? - } - Ok(()) - } -} - /// Wrapper around some arguments and some values. /// /// Used to assert `(= arg[i] val[i])`. @@ -366,12 +385,14 @@ pub struct ArgValEq<'a> { args: &'a VarTerms, /// Values. vals: &'a VarVals, + /// Only assert equalities for variables that are in this set. Assert all if none. + vars: Option<&'a VarSet>, } impl<'a> ArgValEq<'a> { /// Constructor. - pub fn new(args: &'a VarTerms, vals: &'a VarVals) -> Self { + pub fn new(args: &'a VarTerms, vals: &'a VarVals, vars: Option<&'a VarSet>) -> Self { debug_assert_eq! { args.len(), vals.len() } - ArgValEq { args, vals } + ArgValEq { args, vals, vars } } } impl<'a> Expr2Smt<()> for ArgValEq<'a> { @@ -380,11 +401,13 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { Writer: Write, { write!(w, "(and")?; - let mut unknown = 0; + let mut skipped = 0; - for (arg, val) in self.args.iter().zip(self.vals.iter()) { - if !val.is_known() { - unknown += 1; + for ((var, arg), val) in self.args.index_iter().zip(self.vals.iter()) { + // Skip if variable has no value, or the set of variables to assert does not contain + // it. + if !val.is_known() || self.vars.map(|set| !set.contains(&var)).unwrap_or(false) { + skipped += 1; continue; } @@ -426,7 +449,7 @@ impl<'a> Expr2Smt<()> for ArgValEq<'a> { } } - if unknown == self.args.len() { + if skipped == self.args.len() { write!(w, " true")? } write!(w, ")")?; From a89467310e28ddc94c97a13e818dafd7135422ae Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 6 Sep 2018 17:09:46 +0900 Subject: [PATCH 49/94] renice --- Cargo.lock | 1 + Cargo.toml | 1 + src/bin/main.rs | 10 ++++++++++ src/hoice.rs | 1 + 4 files changed, 13 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 514bcf9f..2bc0e5d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,7 @@ dependencies = [ "hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 5a1960ac..5c37279f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ panic = 'unwind' bench = [ ] [dependencies] +libc = "*" lazy_static = "*" clap = "*" hashconsing = "*" diff --git a/src/bin/main.rs b/src/bin/main.rs index 5dd6f337..d3e5d8fb 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,10 +1,20 @@ //! Entry point for the binary. extern crate hoice; +extern crate libc; use hoice::common::*; +/// Renices the +fn renice() { + let whole_group = ::libc::PRIO_PGRP; + let result = unsafe { ::libc::setpriority(whole_group, 0, 50) }; + debug_assert_eq!(result, 0) +} + +/// Entry point. fn main() { + renice(); // Work and report error if any. if let Err(errs) = ::hoice::work() { let errs = match *errs.kind() { diff --git a/src/hoice.rs b/src/hoice.rs index 5edf9abb..d5173abc 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -22,6 +22,7 @@ extern crate ansi_term as ansi; extern crate hashconsing; extern crate either; extern crate isatty; +extern crate libc; extern crate num; extern crate rand; extern crate rsmt2; From 9df2c2b88ecc3e60ac6366e557b0e087ae43fdfd Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 6 Sep 2018 17:58:34 +0900 Subject: [PATCH 50/94] no renice on windows --- src/bin/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index d3e5d8fb..8a237a81 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -5,7 +5,12 @@ extern crate libc; use hoice::common::*; -/// Renices the +/// Renices the process group. +#[cfg(windows)] +fn renice() {} + +/// Renices the process group. +#[cfg(not(windows))] fn renice() { let whole_group = ::libc::PRIO_PGRP; let result = unsafe { ::libc::setpriority(whole_group, 0, 50) }; From 8a1a1a01f4ff6b3f22c1a3bfe486269ac19c9268 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 6 Sep 2018 19:56:57 +0900 Subject: [PATCH 51/94] bug fix in assistant, no more renice, began improving fun_preds --- src/bin/main.rs | 24 +++---- src/instance/preproc/fun_preds.rs | 104 ++++++++++++++++-------------- src/teacher/assistant.rs | 62 ++++++++++++++++-- 3 files changed, 123 insertions(+), 67 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 8a237a81..02b954d2 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,25 +1,25 @@ //! Entry point for the binary. extern crate hoice; -extern crate libc; +// extern crate libc; use hoice::common::*; -/// Renices the process group. -#[cfg(windows)] -fn renice() {} +// /// Renices the process group. +// #[cfg(windows)] +// fn renice() {} -/// Renices the process group. -#[cfg(not(windows))] -fn renice() { - let whole_group = ::libc::PRIO_PGRP; - let result = unsafe { ::libc::setpriority(whole_group, 0, 50) }; - debug_assert_eq!(result, 0) -} +// /// Renices the process group. +// #[cfg(not(windows))] +// fn renice() { +// let whole_group = ::libc::PRIO_PGRP; +// let result = unsafe { ::libc::setpriority(whole_group, 0, 50) }; +// debug_assert_eq!(result, 0) +// } /// Entry point. fn main() { - renice(); + // renice(); // Work and report error if any. if let Err(errs) = ::hoice::work() { let errs = match *errs.kind() { diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index 69407140..cbfbcfd9 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -867,61 +867,67 @@ fn get_invariants( let mut invariants = TermSet::new(); - scoped! { - let solver = instance.solver() ; + { + let solver = instance.solver(); - for info in sig { - solver.declare_const(& info.idx, info) ? - } - - solver.declare_fun( name, sig, typ.get() ) ? ; - - use smt::{ SmtTerm, TermConj} ; - - for (candidate, (cubes, apps)) in candidates { - log! { @4 "checking candidate: {}", candidate } - let mut invariant = true ; - - for (cube, value, _) in definitions.iter() { - if_log! { @5 - log! { @5 "cube:" } - for term in cube { - log! { @5 " {}", term } - } - } - let actlit = solver.get_actlit() ? ; - for term in cube { - solver.assert_act( & actlit, & SmtTerm::new(term) ) ? - } - for app in apps.iter() { - let term = term::eq( app.clone(), value.clone() ) ; - solver.assert_act( & actlit, & SmtTerm::new(& term) ) ? - } - solver.assert_act_with( - & actlit, & TermConj::new( Some(& candidate) ), false - ) ? ; + for info in sig { + solver.declare_const(&info.idx, info)? + } - let sat = solver.check_sat_act( Some(& actlit) ) ? ; + solver.declare_fun(name, sig, typ.get())?; - if sat { - invariant = false ; - break - } - } + // use smt::{SmtTerm, TermConj}; - if invariant { - log! { @4 "invariant :)" } - let is_new = invariants.insert(candidate) ; - debug_assert! { is_new } - } else { - log! { @4 "not invariant :(" } - for cube in cubes { - definitions[cube].0.insert( candidate.clone() ) ; - } + for (candidate, _) in candidates { + invariants.insert(candidate); } - } - + // for (candidate, (cubes, apps)) in candidates { + // log! { @4 "checking candidate: {}", candidate } + // let mut invariant = true; + + // solver.comment_args(format_args!("checking candidate {}", candidate))?; + + // for (cube, value, _) in definitions.iter() { + // if_log! { @5 + // log! { @5 "cube:" } + // for term in cube { + // log! { @5 " {}", term } + // } + // } + // let actlit = solver.get_actlit()?; + // solver.comment("asserting cube")?; + // for term in cube { + // solver.assert_act(&actlit, &SmtTerm::new(term))? + // } + // solver.comment("forcing args")?; + // for app in apps.iter() { + // solver.comment_args(format_args!("{} = {}", app, value))?; + // let term = term::eq(app.clone(), value.clone()); + // solver.assert_act(&actlit, &SmtTerm::new(&term))? + // } + // solver.comment("forcing branche's return value")?; + // solver.assert_act_with(&actlit, &TermConj::new(Some(&candidate)), false)?; + + // let sat = solver.check_sat_act(Some(&actlit))?; + + // if sat { + // invariant = false; + // break; + // } + // } + + // if invariant { + // log! { @4 "invariant :)" } + // let is_new = invariants.insert(candidate); + // debug_assert! { is_new } + // } else { + // log! { @4 "not invariant :(" } + // for cube in cubes { + // definitions[cube].0.insert(candidate.clone()); + // } + // } + // } } instance.reset_solver()?; diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 9edde3e0..97373b33 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -42,24 +42,74 @@ impl ClauseData { pub fn new(idx: ClsIdx, pos: bool, args: &VarTerms, lhs_terms: &TermSet) -> Self { let conj = term::and(lhs_terms.iter().cloned().collect()); let args = args.clone(); - let conj_vars = term::vars(&conj); - + let mut conj_vars = term::vars(&conj); + + // println!("{} clause:", if pos { "positive" } else { "negative" }); + // println!(" conj: {}", conj); + // print!(" conj_vars:"); + // for var in &conj_vars { + // print!(" v_{},", var) + // } + // println!(); + // println!(" args: (_ {})", args); + + // Final predicate variables that are needed. let mut vars = VarSet::new(); + // When a clause variable is not considered necessary, it is added here as a mapping the + // predicate variable it corresponds to. + let mut var_map = VarHMap::new(); + for (pred_var, arg) in args.index_iter() { + // Argument's a variable? if let Some(var) = arg.var_idx() { - if !conj_vars.contains(&var) { - continue; + // Appears in the conjunction? + if conj_vars.contains(&var) { + // Then it is important. + let is_new = vars.insert(pred_var); + debug_assert! { is_new } + } else if let Some(prev) = var_map.insert(var, pred_var) { + // Does not appear in the conjunction: just inserted in the memory. If + // something was there, then both this pred variable and the one previously in + // the map must be kept. + let is_new = vars.insert(pred_var); + debug_assert! { is_new } + let is_new = vars.insert(prev); + debug_assert! { is_new } + let prev = var_map.remove(&var); + debug_assert! { prev.is_some() } + let is_new = conj_vars.insert(var); + debug_assert! { is_new } } + } else { + // Argument's not a variable. Go over its variables. + for var in term::vars(arg) { + conj_vars.insert(var); + if let Some(pred_var) = var_map.remove(&var) { + let is_new = vars.insert(pred_var); + debug_assert! { is_new } + } + } + let is_new = vars.insert(pred_var); + debug_assert! { is_new } } - let is_new = vars.insert(pred_var); - debug_assert! { is_new } } + let vars = if vars.len() == args.len() { None } else { Some(vars) }; + // print!(" vars:"); + // if let Some(vars) = vars.as_ref() { + // for var in vars { + // print!(" v_{},", var) + // } + // } else { + // print!(" none") + // } + // println!(); + ClauseData { idx, conj, From 1ff968d99b5e1c393e7dc2e526a18d50a7d700ca Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 7 Sep 2018 17:42:12 +0900 Subject: [PATCH 52/94] improved fun preds a lot --- Cargo.toml | 2 +- src/common/macros.rs | 22 + src/fun/mod.rs | 38 +- src/instance/preproc/fun_preds.rs | 1029 +++++++++++++++++------------ 4 files changed, 658 insertions(+), 433 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5c37279f..1b9c91dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ clap = "*" hashconsing = "*" error-chain = "*" ansi_term = "*" -rsmt2 = "^0.9.11" +rsmt2 = "*" # { path = "../rsmt2" } # "^0.9.11" num = "*" mylib = { git = "https://github.com/AdrienChampion/mylib" } either = "*" diff --git a/src/common/macros.rs b/src/common/macros.rs index ed2f88fd..202b20c0 100644 --- a/src/common/macros.rs +++ b/src/common/macros.rs @@ -136,6 +136,15 @@ macro_rules! log { } ) ; + ( @$flag:tt |=> $($tail:tt)* ) => ( + log! { > log!(|pref_of| $flag) => $($tail)* } + ) ; + ( @$flag:tt | $($tail:tt)* ) => ( + if log!(|cond_of| $flag) { + log! { > log!(|pref_of| $flag) => $($tail)* } + } + ) ; + ( @$flag:tt => $($tail:tt)* ) => ( log! { log!(|pref_of| $flag) => $($tail)* } ) ; @@ -145,6 +154,19 @@ macro_rules! log { } ) ; + ( > $pref:expr => $( $str:expr $(, $args:expr)* $(,)* );* ) => ({ + $( + println!("{}{}", $pref, format!($str $(, $args)*)) ; + )* + () + }) ; + ( > $( $str:expr $(, $args:expr)* $(,)* );* ) => ({ + $( + println!("; {}", format!($str $(, $args)*)) + )* + () + }) ; + ( $pref:expr => $( $str:expr $(, $args:expr)* $(,)* );* ) => ({ $( for line in format!($str $(, $args)*).lines() { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index a5359324..d7b9efe3 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -36,6 +36,7 @@ lazy_static! { /// Registers a function declaration. pub fn register_dec(fun: RFun) -> Res<()> { + println!("registering {}", fun.name); if let Ok(mut decs) = fun_decs.write() { let prev = decs.insert(fun.name.clone(), fun); if let Some(prev) = prev { @@ -49,14 +50,27 @@ pub fn register_dec(fun: RFun) -> Res<()> { /// Retrieves a function declaration. pub fn retrieve_dec(fun: &str) -> Res { + println!("retrieving {}", fun); if let Ok(mut decs) = fun_decs.write() { if let Some(dec) = decs.remove(fun) { Ok(dec) } else { - bail!( - "unable to retrieve function declaration for {}", + let mut s = format!( + "trying to retrieve declaration for unknown function {}\n", conf.bad(fun) - ) + ); + if decs.is_empty() { + s += "no function declaration present" + } else { + s += "function(s) declared:"; + for (name, _) in decs.iter() { + s += " "; + s += name; + s += "," + } + } + + bail!(s) } } else { bail!("unable to access function declarations") @@ -80,10 +94,22 @@ where if let Some(def) = defs.get(fun) { f(def) } else { - bail!( - "trying to retrieve declaration for unknown function {}", + let mut s = format!( + "trying to retrieve declaration for unknown function {}\n", conf.bad(fun) - ) + ); + if defs.is_empty() { + s += "no function declaration present" + } else { + s += "function(s) declared:"; + for (name, _) in defs.iter() { + s += " "; + s += name; + s += "," + } + } + + bail!(s) } } else { bail!("unable to retrieve function declarations") diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index cbfbcfd9..c4d19e37 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -1,114 +1,621 @@ //! Predicate-to-function reduction. +//! +//! This preprocessor attempts to reconstruct function definitions from Horn clauses. Functions are +//! reconstructed as multiple branches of an if-then-else. use common::*; use fun::RFun; -use instance::{info::VarInfo, instance::PreInstance, preproc::RedStrat}; +use instance::{info::VarInfo, instance::PreInstance, preproc::RedStrat, Clause}; + +/// A branch in the definition of a function. +#[derive(Clone, Debug)] +struct FunBranch { + /// Guard of the branch (condition). + guard: TermSet, + /// Value yielded by the branch. + value: Term, + /// Recursive calls. + calls: VarHMap, +} -pub struct FunPreds; +impl FunBranch { + /// Constructor. + pub fn new( + (pred, fun_name, fun_sig, fun_typ): (PrdIdx, &str, &VarInfos, &Typ), + rhs_args: &VarTerms, + lhs_terms: &TermSet, + lhs_preds: &PredApps, + invars: &mut TermSet, + ) -> Res> { + let fun_args_len = fun_sig.len(); + let (last, mut fresh) = if fun_args_len == rhs_args.len() { + (None, rhs_args.len().into()) + } else { + let last: VarIdx = (rhs_args.len() - 1).into(); + let mut fresh = last; + fresh.inc(); + (Some(last), fresh) + }; -impl FunPreds { - /// Finalizes a definition for a predicate. - /// - /// Returns the final definition and some invariants. - pub fn finalize_definition( - instance: &mut PreInstance, - name: &str, - sig: &VarInfos, - typ: &Typ, - mut definitions: Vec<(TermSet, Term, bool)>, - check: bool, - ) -> Res)>> { - instance.reset_solver()?; + if_log! { @4 + log! { @4 |=> + "building branch (keeping first {} argumentss) from", fun_args_len ; + " rhs_args: (p_{} {})", pred, rhs_args; + " fresh: v_{}", fresh; + " lhs_terms:" + } ; + for term in lhs_terms { + log! { @4 |=> " {}", term } + }; + log! { @4 |=> " lhs apps:" }; + for (pred, argss) in lhs_preds { + for args in argss { + log! { @4 |=> " (p_{} {})", pred, args } + } + } + } - let invs = get_invariants(instance, name, sig, typ, &mut definitions)?; + macro_rules! get_fresh { + () => {{ + let res = fresh; + fresh.inc(); + res + }}; + } - if_log! { @3 - if let Some(invs) = invs.as_ref() { - log! { @3 "found {} invariant(s)", invs.len() } - for inv in invs { - log! { @3 " {}", inv } + debug_assert! { fun_args_len == rhs_args.len() || fun_args_len == rhs_args.len() - 1 } + debug_assert! { lhs_preds.len() <= 1 } + debug_assert! { + if let Some((p, _)) = lhs_preds.iter().next() { + * p == pred + } else { + true } - } else { - log! { @3 "none found" } - } } - if check { - log! { @3 "checking conditions are mutually exclusive" } - let solver = instance.solver(); + let (mut guard, mut subst) = if let Some(res) = args_invert(rhs_args, fun_args_len)? { + res + } else { + log! { @3 "failed to invert rhs arguments {}", rhs_args } + return Ok(None); + }; - for info in sig { - solver.declare_const(&info.idx, info.typ.get())? + if_log! { @4 + log! { @4 |=> + "rhs inversion successful" ; + " substitution:" + } + for (var, term) in subst.iter() { + log! { @4 |=> " v_{} -> {}", var, term } } + log! { @4 |=> " cube:" } + for term in guard.iter() { + log! { @4 " {}", term } + } + } + + let mut lhs_argss: Vec<_> = lhs_preds + .get(&pred) + .map(|argss| argss.iter().collect()) + .unwrap_or_else(Vec::new); + + let mut nu_args = Vec::with_capacity(fun_args_len); + let mut value = None; + + // Stores the recursive calls. + let mut calls = VarHMap::new(); + + macro_rules! register_call { + ($call:expr) => {{ + // Check if we already have a binding for this call. + let mut var = None; + for (v, trm) in &calls { + if $call == *trm { + // Found a variable for this call. + var = Some(*v); + break; + } + } + if let Some(var) = var { + // Already have a binding. + var + } else { + // Create new binding. + let fresh = get_fresh!(); + let prev = calls.insert(fresh, $call); + debug_assert! { prev.is_none() } + fresh + } + }}; + } - solver.declare_fun(name, sig, typ.get())?; + while !lhs_argss.is_empty() { + let prev_len = lhs_argss.len(); + let mut failed: Res<_> = Ok(false); - let mut actlits = Vec::with_capacity(definitions.len()); + lhs_argss.retain(|args| { + for arg in &args[0..fun_args_len] { + if let Some((arg, _)) = arg.subst_total(&subst) { + nu_args.push(arg) + } else { + nu_args.clear(); + return true; + } + } + let nu_args = ::std::mem::replace(&mut nu_args, Vec::with_capacity(fun_args_len)); - for (cube, _, _) in &definitions { - use smt::TermConj; - let conj = TermConj::new(cube.iter()); - let actlit = solver.get_actlit()?; - solver.assert_act_with(&actlit, &conj, true)?; - actlits.push(actlit) - } + let fun_app = term::fun(fun_typ.clone(), fun_name.into(), nu_args); + let fun_app_var = register_call!(fun_app); + let fun_app = term::var(fun_app_var, fun_typ.clone()); - scoped! { - let mut actlits_iter = actlits.iter() ; + let okay = if let Some(last) = last.as_ref() { + let last = *last; + map_invert(&args[last], fun_app, &mut subst, &mut guard) + } else { + value.get_or_insert_with(Vec::new).push(fun_app); + Ok(true) + }; - while let Some(actlit) = actlits_iter.next() { - for other in actlits_iter.clone() { - let not_exclusive = solver.check_sat_act( - vec![ actlit, other ] - ) ? ; - if not_exclusive { - return Ok(None) - } + match okay { + Ok(true) => false, + Ok(false) => { + if let Ok(failed) = failed.as_mut() { + *failed = true + } + true + } + err => { + failed = err.chain_err(|| format!("while inverting {}", args)); + true + } } - } + }); + + if failed? { + log! { @3 | "failed" } + return Ok(None); + } else if lhs_argss.len() == prev_len { + // not making progress. + log! { @3 | "not making progress on lhs preds" } + return Ok(None); } + } - for actlit in actlits { - solver.de_actlit(actlit)? + if_log! { @4 + log! { @4 |=> + "lhs inversion successful" ; + " substitution:" + }; + for (var, term) in &subst { + log! { @4 |=> " v_{} -> {}", var, term } + }; + log! { @4 |=> " cube:" }; + for term in &guard { + log! { @4 |=> " {}", term } + }; + log! { @4 |=> " recursive calls:" }; + for (var, term) in & calls { + log! { @4 |=> " v_{} -> {}", var, term } } + } - log! { @3 "all branches are exclusive, checking they're exhaustive" } + for term in lhs_terms { + if let Some((term, _)) = term.subst_total(&subst) { + guard.insert(term); + } else { + log! { @3 | "total substitution failed on term {}", term } + return Ok(None); + } + } - for (cube, _, _) in &definitions { - use smt::TermConj; - let conj = TermConj::new(cube.iter()); - solver.assert_with(&conj, false)?; + if_log! { @4 + log! { @4 |=> + "lhs terms substitution successful"; + " cube:" } + for term in &guard { + log! { @4 |=> " {}", term } + }; + } - let not_exhaustive = solver.check_sat()?; + let mut invar_subst = VarHMap::with_capacity(1); - if not_exhaustive { - // warn!("fun_preds: branches are not exhaustive, moving on anyways") ; - log! { @3 "branches are not exhaustive, aborting" } - return Ok(None); + // Term elligible as candidate invariants are the ones that mention **exactly one** + // variable from `calls`. + guard.retain(|term| { + let term_vars = term::vars(&term); + let mut vars = vec![]; + + for var in term_vars { + if calls.get(&var).is_some() { + vars.push(var) + } + } + + if vars.len() == 1 { + let var = vars[0]; + invar_subst.insert(var, term::var(0, fun_typ.clone())); + let (invar, _) = term + .subst_total(&invar_subst) + .expect("total substitution cannot fail due to previous checks"); + invars.insert(invar); + false } else { - log! { @3 "branches are exhaustive, building definition" } + true } - } + }); - let mut def = None; + if_log! { @4 + log! { @4 |=> + "potential invariant extraction successful"; + " cube:" + } + for term in &guard { + log! { @4 |=> " {}", term } + }; + log! { @4 |=> " invariants:" }; + for invar in invars.iter() { + log! { @4 |=> " {}", invar } + }; + } - for (cube, value, _) in definitions.into_iter().rev() { - let nu_def = if let Some(def) = def { - term::ite(term::and(cube.into_iter().collect()), value, def) + let value = if let Some(last) = last.as_ref() { + if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { + res } else { - value + log! { @3 | "failed to retrieve value for {}", rhs_args[*last] } + return Ok(None); + } + } else if let Some(conj) = value { + term::and(conj) + } else { + term::tru() + }; + + log! { @4 | "value extraction successful: {}", value } + + Ok(Some(FunBranch { + guard, + value, + calls, + })) + } + + /// Propagates the substition `calls` to the whole branch. + fn propagate_calls(&mut self) { + if !self.calls.is_empty() { + self.guard = self + .guard + .iter() + .map(|term| term.subst(&self.calls).0) + .collect(); + self.value = self.value.subst(&self.calls).0; + self.calls.clear() + } + } +} + +/// A function definition. +#[derive(Clone, Debug)] +struct FunDef { + /// Predicate this function is for. + pred: PrdIdx, + /// Function name. + name: String, + /// Function signature. + sig: VarInfos, + /// Function type. + typ: Typ, + /// Branches of the definition. + branches: Vec, + /// Invariants. + /// + /// Invariants are all terms ranging over exactly one variable, the variable `v_0`. This + /// variable is a placeholder for the function call. + /// + /// # Examples + /// + /// If `fun(args) >= 0` is an invariant, then the set contains `v_0 >= 0`. + invars: TermSet, +} + +impl FunDef { + /// Creates an empty function definition. + pub fn new(pred: PrdIdx, name: String, sig: VarInfos, typ: Typ) -> Self { + FunDef { + pred, + name, + sig, + typ, + branches: Vec::new(), + invars: TermSet::new(), + } + } + + /// Registers a clause. + /// + /// Returns none if the registration failed and the reconstruction process must be aborted. + /// Invariants are not checked. + pub fn register_clause(mut self, clause: &Clause) -> Res> { + let rhs_args = if let Some((p, args)) = clause.rhs() { + debug_assert_eq! { p, self.pred } + args + } else { + bail!("FunDef::register_clause called with illegal clause") + }; + + let res = if let Some(branch) = FunBranch::new( + (self.pred, &self.name, &self.sig, &self.typ), + rhs_args, + clause.lhs_terms(), + clause.lhs_preds(), + &mut self.invars, + )? { + self.branches.push(branch); + Some(self) + } else { + None + }; + + Ok(res) + } + + /// Checks that invariants are actual invariants. + /// + /// Once the invariants are confirmed, all the substitutions defined by `calls` in the branches + /// are applied to the guard and the result. + pub fn check_invariants(&mut self, instance: &mut PreInstance) -> Res { + macro_rules! solver { + () => { + instance.solver() }; - def = Some(nu_def) } - if let Some(def) = def { - Ok(Some((def, invs))) + if !self.invars.is_empty() { + log! { @3 | "checking {} invariants...", self.invars.len() } + solver!().comment_args(format_args!( + "checking candidate invariants for function {}", + self.name + ))?; + } + + let mut subst = VarMap::with_capacity(1); + + for invariant in &self.invars { + solver!().comment_args(format_args!("checking candidate invariant {}", invariant))?; + + // Check invariant holds for each branch. + 'all_branches: for branch in &self.branches { + // Target invariant (negated). + let neg_objective = { + debug_assert! { subst.is_empty() } + subst.push(branch.value.clone()); + + let invariant = invariant + .subst_total(&subst) + .expect("cannot fail by construction") + .0; + + subst.clear(); + + match invariant.bool() { + Some(true) => continue 'all_branches, + Some(false) => return Ok(false), + None => term::not(invariant), + } + }; + + // Declare function's variables. + for info in &self.sig { + solver!().declare_const(&info.idx, info)? + } + + // Declare recursive call variables. + if !branch.calls.is_empty() { + solver!().comment("declaring vars for recursive calls")?; + for (var, _) in &branch.calls { + solver!().declare_const(var, self.typ.get())? + } + } + + solver!().comment("branch guard")?; + + for term in &branch.guard { + solver!().assert(&smt::SmtTerm::new(term))? + } + + if !branch.calls.is_empty() { + solver!().comment("recursion hypotheses")?; + + for (var, _) in &branch.calls { + debug_assert! { subst.is_empty() } + subst.push(term::var(*var, self.typ.clone())); + let invariant = invariant + .subst_total(&subst) + .expect("cannot fail by construction") + .0; + subst.clear(); + + solver!().assert(&smt::SmtTerm::new(&invariant))? + } + } + + solver!().comment("invariant to prove on output value")?; + solver!().assert(&smt::SmtTerm::new(&neg_objective))?; + + let sat = solver!().check_sat()?; + + if sat { + log! { @3 | " not an invariant: {}", invariant } + return Ok(false); + } + + instance.reset_solver()?; + } + + log! { @3 | " confirmed invariant: {}", invariant } + } + + for branch in &mut self.branches { + branch.propagate_calls() + } + + Ok(true) + } + + /// Checks that all branches are exclusive and exhaustive. + pub fn check_branches(&self, instance: &mut PreInstance) -> Res { + macro_rules! solver { + () => { + instance.solver() + }; + } + + for info in &self.sig { + solver!().declare_const(&info.idx, info.typ.get())? + } + + solver!().declare_fun(&self.name, &self.sig, self.typ.get())?; + + let mut actlits = Vec::with_capacity(self.branches.len()); + + for branch in &self.branches { + let conj = smt::TermConj::new(branch.guard.iter()); + let actlit = solver!().get_actlit()?; + solver!().assert_act_with(&actlit, &conj, true)?; + actlits.push(actlit) + } + + { + let mut actlits_iter = actlits.iter(); + while let Some(actlit) = actlits_iter.next() { + for other in actlits_iter.clone() { + let not_exclusive = solver!().check_sat_act(vec![actlit, other])?; + if not_exclusive { + log! { @3 "branches are not mutually exclusive" } + return Ok(false); + } + } + } + } + + for actlit in actlits { + solver!().de_actlit(actlit)? + } + + log! { @3 | "all branches are exclusive, checking they're exhaustive" } + + for branch in &self.branches { + let conj = smt::TermConj::new(branch.guard.iter()); + solver!().assert_with(&conj, false)?; + } + + let not_exhaustive = solver!().check_sat()?; + + if not_exhaustive { + log! { @3 | "branches are not exhaustive" } + return Ok(false); + } else { + log! { @3 | "branches are exhaustive" } + } + + Ok(true) + } + + /// Finalizes the function definition. + pub fn finalize(mut self, instance: &mut PreInstance) -> Res> { + let okay = self + .check_invariants(instance) + .chain_err(|| "while checking the invariants")?; + + if okay { + log!{ @3 | "all invariant(s) are fine" } } else { - bail!("empty definitions during finalization in fun_preds") + log!{ @3 | "failed to verify invariants" } + return Ok(None); + } + + let okay = self + .check_branches(instance) + .chain_err(|| "while checking branches")?; + + if !okay { + return Ok(None); + } + + // Everything okay, let's do this. + + self.branches.sort_by(|b_1, b_2| { + use std::cmp::Ordering::*; + match b_1.calls.len().cmp(&b_2.calls.len()) { + Equal => b_1.guard.len().cmp(&b_2.guard.len()), + res => res, + } + }); + + let mut invs = Vec::with_capacity(self.invars.len()); + if !self.invars.is_empty() { + let subst: VarMap<_> = vec![term::fun( + self.typ, + self.name.clone(), + self.sig + .into_iter() + .map(|info| term::var(info.idx, info.typ)) + .collect(), + )].into(); + + for inv in self.invars { + let inv = inv + .subst_total(&subst) + .expect("cannot fail by construction") + .0; + + invs.push(inv); + } + } + + // Build actual definition. + let mut def = None; + for branch in self.branches.into_iter().rev() { + let mut nu_def = if let Some(def) = def { + term::ite( + term::and(branch.guard.into_iter().collect()), + branch.value, + def, + ) + } else { + branch.value + }; + def = Some(nu_def) } + + let def = def.ok_or("empty definitions during finalization in fun_preds")?; + + // Finally creating the function. + let pred = self.pred; + + let mut dec = fun::retrieve_dec(&self.name)?; + dec.set_def(def); + dec.invariants.extend(invs); + + let fun = fun::mk(dec).chain_err(|| { + format!( + "while creating internal function for predicate {}", + conf.bad(&instance[pred].name) + ) + })?; + instance.add_companion_fun(pred, fun.clone()); + + // Restart the solver to make sure the function is defined for the next user. + instance.reset_solver()?; + + Ok(Some(fun)) } +} + +pub struct FunPreds; +impl FunPreds { /// Reduces a predicate to a function. pub fn reduce_pred( instance: &mut PreInstance, @@ -154,9 +661,15 @@ impl FunPreds { typ::bool() }; - let mut rfun = RFun::new(pred_fun_name.clone(), var_infos, pred_fun_typ.clone()); + let mut rfun = RFun::new( + pred_fun_name.clone(), + var_infos.clone(), + pred_fun_typ.clone(), + ); rfun.set_synthetic(pred); + fun::register_dec(rfun)?; + macro_rules! abort { ($($stuff:tt)*) => {{ let _ = fun::retrieve_dec(&pred_fun_name); @@ -164,243 +677,28 @@ impl FunPreds { }}; } - fun::register_dec(rfun)?; - - let mut definitions = vec![]; + let mut fun_def = FunDef::new(pred, pred_fun_name.clone(), var_infos, pred_fun_typ); for clause in instance.clauses_of(pred).1 { - to_rm.push(*clause); - - if_log! { @3 - log!(@3 "working on clause #{}", clause) ; - log!(@4 "lhs terms:") ; - for term in instance[* clause].lhs_terms() { - log!(@4 " {}", term) - } - log!(@4 "lhs preds:") ; - for (pred, argss) in instance[* clause].lhs_preds() { - for args in argss { - log!(@4 " ({} {})", instance[* pred], args) - } - } - } let clause = *clause; - let (_, rhs_args) = instance[clause].rhs().unwrap(); - log!(@4 "rhs args:\n ({} {})", instance[pred], rhs_args); - - let (mut cube, mut subst) = - if let Some((cube, subst)) = args_invert(rhs_args, args_len)? { - (cube, subst) - } else { - log!(@3 "failed to invert rhs arguments"); - abort!() - }; - - if_log! { @4 - log!(@4 "substitution:") ; - for (var, term) in subst.iter() { - log!(@4 " v_{} -> {}", var, term) - } - log!(@4 "cube:") ; - for term in cube.iter() { - log!(@4 " {}", term) - } - } - - let recursive = instance[clause].lhs_preds().contains_key(&pred); + to_rm.push(clause); - let mut lhs_argss: Vec<_> = instance[clause] - .lhs_preds() - .get(&pred) - .map(|argss| argss.iter().collect()) - .unwrap_or_else(Vec::new); + log! { @3 | "working on clause #{}", clause } - let mut nu_args = Vec::with_capacity(args_len); - - let mut value = None; - - while !lhs_argss.is_empty() { - let prev_len = lhs_argss.len(); - let mut failed: Res<_> = Ok(false); - - lhs_argss.retain(|args| { - for arg in &args[0..args_len] { - if let Some((arg, _)) = arg.subst_total(&subst) { - nu_args.push(arg) - } else { - nu_args.clear(); - return true; - } - } - let nu_args = ::std::mem::replace( - &mut nu_args, - Vec::with_capacity(instance[pred].sig.len() - 1), - ); - - let fun_app = term::fun(pred_fun_typ.clone(), pred_fun_name.clone(), nu_args); - - let okay = if let Some(last) = last.as_ref() { - let last = *last; - - map_invert(&args[last], fun_app, &mut subst, &mut cube) - } else { - value.get_or_insert_with(Vec::new).push(fun_app); - Ok(true) - }; - - match okay { - // Success, don't retain. - Ok(true) => false, - // Failure, retain. - Ok(false) => { - log!(@3 - "could not invert last argument ({:?}) of ({} {})", - last, instance[pred], args - ); - if let Ok(failed) = failed.as_mut() { - *failed = true - } - true - } - // Error, do whatever. - err => { - failed = err.chain_err(|| { - format!("while inverting ({} {})", instance[pred], args) - }); - true - } - } - }); - - if failed? { - log!(@3 "failed"); - abort!() - } else if lhs_argss.len() == prev_len { - // not making progress. - log!(@3 "not making progress on lhs preds"); - abort!() - } - } - - if_log! { @4 - log!(@4 "subst after lhs preds:") ; - for (var, term) in & subst { - log!(@4 " v_{} -> {}", var, term) - } - } - - for term in instance[clause].lhs_terms() { - if let Some((term, _)) = term.subst_total(&subst) { - cube.insert(term); - } else { - log!(@3 "total substitution on term {} failed", term); - abort!() - } - } - - if_log! { @4 - log!(@4 "cube:") ; - for term in & cube { - log!(@4 " {}", term) - } - } - - let res = if let Some(last) = last.as_ref() { - if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { - res - } else { - log!(@3 "failed to retrieve value, aborting"); - abort!() - } - } else if let Some(conj) = value { - term::and(conj) + fun_def = if let Some(new) = fun_def.register_clause(&instance[clause])? { + new } else { - term::tru() + log!{ @3 | "clause registration failed" } + abort!() }; - - log!(@4 "value: {}", res); - - definitions.push((cube, res, recursive)) - } - - definitions.sort_by(|(c_1, _, rec_1), (c_2, _, rec_2)| { - use std::cmp::Ordering::*; - if *rec_1 && !*rec_2 { - Less - } else if *rec_2 && !*rec_1 { - Greater - } else { - c_1.len().cmp(&c_2.len()) - } - }); - - if use_all_args { - let mut tru = TermSet::new(); - tru.insert(term::tru()); - definitions.push((tru, term::fls(), false)) - } - - log!(@3 "done working on {}", instance[pred]); - if_log! { @4 - for (cube, res, recursive) in & definitions { - log!(@4 "when {{") ; - for term in cube { - log!(@4 " {}", term) - } - log!(@4 "}} -{}> {}", if * recursive { "rec-" } else { "" }, res) - } } - // let mut def = pred_fun_typ.default_term() ; - - // for (cube, res) in definitions.into_iter().rev() { - // let cond = term::and( - // cube.into_iter().collect() - // ) ; - // def = term::ite( cond, res, def ) - // } - - let mut dec = fun::retrieve_dec(&pred_fun_name)?; - - let (def, invs) = if let Some(def) = FunPreds::finalize_definition( - instance, - &pred_fun_name, - &dec.sig, - &pred_fun_typ, - definitions, - !use_all_args, - )? { - def + let fun = if let Some(fun) = fun_def.finalize(instance)? { + fun } else { - log!(@3 "failed to finalize definition, aborting"); abort!() }; - if let Some(invs) = invs { - for inv in invs { - dec.invariants.insert(inv); - } - } - - instance.reset_solver()?; - - log!(@3 "definition:\n {}", def); - - log!(@4 "registering..."); - dec.set_def(def); - - let fun = fun::mk(dec).chain_err(|| { - format!( - "while creating internal function for predicate {}", - conf.bad(&instance[pred].name) - ) - })?; - - instance.add_companion_fun(pred, fun.clone()); - - // Restarting solver so that the function is declared. - instance.reset_solver()?; - info.clauses_rmed += to_rm.len(); instance.forget_clauses(&mut to_rm)?; @@ -555,25 +853,25 @@ pub fn args_invert(args: &VarTerms, args_len: usize) -> Res, -) -> Res> { - let mut candidates = TermMap::new(); - - log! { @3 "looking for invariants..." } - - for (idx, (cube, _, _)) in definitions.iter_mut().enumerate() { - cube.retain(|term| { - let mut applications = None; - - term.iter(|sub| { - if let Some((fun, _)) = sub.fun_inspect() { - if fun == name { - applications - .get_or_insert_with(TermSet::new) - .insert(sub.to_hcons()); - } - } - }); - - if let Some(applications) = applications { - candidates - .entry(term.clone()) - .or_insert_with(|| (Vec::new(), applications)) - .0 - .push(idx); - false - } else { - true - } - }) - } - - if candidates.is_empty() { - return Ok(None); - } - - let mut invariants = TermSet::new(); - - { - let solver = instance.solver(); - - for info in sig { - solver.declare_const(&info.idx, info)? - } - - solver.declare_fun(name, sig, typ.get())?; - - // use smt::{SmtTerm, TermConj}; - - for (candidate, _) in candidates { - invariants.insert(candidate); - } - - // for (candidate, (cubes, apps)) in candidates { - // log! { @4 "checking candidate: {}", candidate } - // let mut invariant = true; - - // solver.comment_args(format_args!("checking candidate {}", candidate))?; - - // for (cube, value, _) in definitions.iter() { - // if_log! { @5 - // log! { @5 "cube:" } - // for term in cube { - // log! { @5 " {}", term } - // } - // } - // let actlit = solver.get_actlit()?; - // solver.comment("asserting cube")?; - // for term in cube { - // solver.assert_act(&actlit, &SmtTerm::new(term))? - // } - // solver.comment("forcing args")?; - // for app in apps.iter() { - // solver.comment_args(format_args!("{} = {}", app, value))?; - // let term = term::eq(app.clone(), value.clone()); - // solver.assert_act(&actlit, &SmtTerm::new(&term))? - // } - // solver.comment("forcing branche's return value")?; - // solver.assert_act_with(&actlit, &TermConj::new(Some(&candidate)), false)?; - - // let sat = solver.check_sat_act(Some(&actlit))?; - - // if sat { - // invariant = false; - // break; - // } - // } - - // if invariant { - // log! { @4 "invariant :)" } - // let is_new = invariants.insert(candidate); - // debug_assert! { is_new } - // } else { - // log! { @4 "not invariant :(" } - // for cube in cubes { - // definitions[cube].0.insert(candidate.clone()); - // } - // } - // } - } - - instance.reset_solver()?; - - let res = if invariants.is_empty() { - None - } else { - Some(invariants) - }; - - Ok(res) -} - fn make_fun_name(other_name: &str) -> Res { let split: Vec<_> = other_name.split('|').collect(); let str = match split.len() { From 21e5ed16a38a752b0aecc78c24e674e1d1004046 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 10 Sep 2018 18:43:20 +0900 Subject: [PATCH 53/94] much improved fun preds, much improved strict neg clauses --- src/common/smt.rs | 57 +++-- src/dtyp/mod.rs | 14 +- src/fun/mod.rs | 2 - src/instance/instance/clause.rs | 116 +++++++++++ src/instance/instance/pre_instance.rs | 1 - src/instance/preproc/fun_preds.rs | 148 ++++++++----- src/instance/preproc/strict_neg_clauses.rs | 25 +++ src/instance/preproc/utils.rs | 14 +- src/teacher/assistant.rs | 21 +- src/teacher/mod.rs | 31 +-- src/term/eval.rs | 1 + src/term/mod.rs | 114 +++++++++- src/term/zip.rs | 229 +++++++++++---------- 13 files changed, 555 insertions(+), 218 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index 32c88b5b..e3800e43 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -60,6 +60,37 @@ pub fn preproc_reset

(solver: &mut Solver

) -> Res<()> { preproc_init(solver) } +/// Performs a check-sat. +/// +/// Tries to check-sat a solver with various strategies. Returns the result of the first tries that +/// does not return `unknown`. +/// +/// # First strategy +/// +/// Declares an actlit, and tries a `check-sat-assuming` with this actlit. +/// +/// # Second strategy +/// +/// Check-sat without any actlits. +pub fn multi_try_check_sat

(solver: &mut Solver

) -> Res { + if let Some(res) = actlit_check_sat(solver)? { + Ok(res) + } else { + let res = solver.check_sat()?; + Ok(res) + } +} + +/// Tries to check-sat a solver with an actlit. +/// +/// Does **not** deactivate the actlit once it's done. This is to allow `get-model` after the +/// check. +fn actlit_check_sat

(solver: &mut Solver

) -> Res> { + let actlit = solver.get_actlit()?; + let res = solver.check_sat_act_or_unk(Some(&actlit))?; + Ok(res) +} + /// SMT-prints a term using the default var writer. pub struct SmtTerm<'a> { /// The term. @@ -135,11 +166,7 @@ where } /// Checks if this conjunction is unsatisfiable. - fn is_unsat( - &self, - solver: &mut Solver, - actlit: Option<&Actlit>, - ) -> Res { + fn is_unsat(&self, solver: &mut Solver) -> Res { if self.terms.len() == 0 { return Ok(false); } @@ -151,7 +178,7 @@ where } } solver.assert(self)?; - let sat = solver.check_sat_act(actlit)?; + let sat = multi_try_check_sat(solver)?; Ok(!sat) } } @@ -827,18 +854,12 @@ impl ClauseTrivialExt for Solver { } } - let mut actlit = None; - let res = { let conj = SmtConj::new(lhs.iter(), &clause.vars); if clause.rhs().is_none() && clause.lhs_preds().is_empty() { - if conj.has_fun_apps { - actlit = Some(self.get_actlit()?) - } - // Either it is trivial, or falsifiable regardless of the predicates. - if conj.is_unsat(self, actlit.as_ref())? { + if conj.is_unsat(self)? { Ok(Some(true)) } else { Ok(None) @@ -858,19 +879,11 @@ impl ClauseTrivialExt for Solver { if lhs.is_empty() { Ok(Some(false)) } else { - if conj.has_fun_apps { - actlit = Some(self.get_actlit()?) - } - - conj.is_unsat(self, actlit.as_ref()).map(Some) + conj.is_unsat(self).map(Some) } } }; - if let Some(actlit) = actlit { - self.de_actlit(actlit)? - } - clause.lhs_terms_checked(); res diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 5f8bd9ea..aab79d11 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -338,13 +338,13 @@ pub fn one_or_more() -> Res { } /// Checks whether a datatype is reserved. -pub fn check_reserved(name: &str) -> Res<()> { - if reserved_dtyps.contains(name) { - bail!( - "attempting to redefine built-in datatype {}", - conf.bad(name) - ) - } +pub fn check_reserved(_name: &str) -> Res<()> { + // if reserved_dtyps.contains(name) { + // bail!( + // "attempting to redefine built-in datatype {}", + // conf.bad(name) + // ) + // } Ok(()) } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index d7b9efe3..1f84123e 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -36,7 +36,6 @@ lazy_static! { /// Registers a function declaration. pub fn register_dec(fun: RFun) -> Res<()> { - println!("registering {}", fun.name); if let Ok(mut decs) = fun_decs.write() { let prev = decs.insert(fun.name.clone(), fun); if let Some(prev) = prev { @@ -50,7 +49,6 @@ pub fn register_dec(fun: RFun) -> Res<()> { /// Retrieves a function declaration. pub fn retrieve_dec(fun: &str) -> Res { - println!("retrieving {}", fun); if let Ok(mut decs) = fun_decs.write() { if let Some(dec) = decs.remove(fun) { Ok(dec) diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 7181a70e..8a77580c 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -968,6 +968,122 @@ impl Clause { Ok(()) } + + /// Rewrites a clause for a specific predicate application. + /// + /// Only legal if the predicate application actually appears in the clause. + /// + /// - introduces a fresh variable `v_i` for each formal argument of the predicate + /// - replaces the application(s) `(p a_1 a_2 ...)` with `(p v_1 v_2 ...)` + /// - replaces all occurences of `a_i` by `v_i` for all `a_i`s + /// - if at least one variable appearing in `a_i` still appears in the clause, adds the term + /// `a_i = v_i` + pub fn rewrite_clause_for_app( + &self, + pred: PrdIdx, + args: &VarTerms, + idx: ClsIdx, + ) -> Res { + let mut clause = new( + self.vars.clone(), + vec![], + None, + "rewrite_clause_for_app", + idx, + ); + + // Maps arguments of the application to fresh clause variables. + let mut map = TermMap::with_capacity(args.len()); + // Maps the clause's original variables to the set of arguments they appear in, along with + // the corresponding fresh variable. + let mut var_map = VarHMap::new(); + + for arg in args.iter() { + let var = map + .entry(arg.clone()) + .or_insert_with(|| term::var(clause.vars.next_index(), arg.typ())) + .clone(); + let idx = var.var_idx().expect("variable by construction"); + + clause + .vars + .push(VarInfo::new(idx.default_str(), arg.typ(), idx)); + + for arg_var in term::vars(arg) { + var_map + .entry(arg_var) + .or_insert_with(Vec::new) + .push((arg, idx)) + } + } + + // True if we actually saw the predicate application in question. + let mut legal = false; + // Variables still appearing in the clause. + let mut vars = VarSet::new(); + + for term in &self.lhs_terms { + let term = term.term_subst(&map); + vars.extend(term::vars(&term).into_iter()); + clause.insert_term(term); + } + + for (p, p_argss) in &self.lhs_preds { + let p = *p; + let prev = clause + .lhs_preds + .insert(p, VarTermsSet::with_capacity(p_argss.len())); + debug_assert! { prev.is_none() } + for p_args in p_argss { + if p == pred && args == p_args { + legal = true + } + let mut nu_p_args = VarMap::with_capacity(p_args.len()); + for arg in p_args.iter() { + let arg = arg.term_subst(&map); + vars.extend(term::vars(&arg).into_iter()); + nu_p_args.push(arg) + } + let nu_p_args = var_to::terms::new(nu_p_args); + clause + .lhs_preds + .get_mut(&p) + .expect("was inserted right above") + .insert(nu_p_args); + } + } + + if let Some((p, p_args)) = self.rhs.as_ref() { + let mut nu_p_args = VarMap::with_capacity(p_args.len()); + for arg in p_args.iter() { + let arg = arg.term_subst(&map); + vars.extend(term::vars(&arg).into_iter()); + nu_p_args.push(arg) + } + let nu_p_args = var_to::terms::new(nu_p_args); + clause.rhs = Some((*p, nu_p_args)) + } + + for info in &mut clause.vars { + info.active = false + } + + for var in vars { + if let Some(equalities) = var_map.remove(&var) { + for (arg, idx) in equalities { + let var = term::var(idx, arg.typ()); + clause.lhs_terms.insert(term::eq(arg.clone(), var)); + } + } + clause.vars[var].active = true + } + + if !legal { + bail!("clause rewriting for application called on unknown application") + } else { + Ok(clause) + } + } } impl ::std::ops::Index for Clause { diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index d56dfe91..9a17b6c8 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -669,7 +669,6 @@ impl<'a> PreInstance<'a> { /// /// - the terms in the lhs are equivalent to `false`, or /// - the rhs is a predicate application contained in the lhs. - #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))] fn is_clause_trivial(&mut self, clause_idx: ClsIdx) -> Res { if self.reset_solver { smt::reset(&mut self.solver, &self.instance)?; diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index c4d19e37..e13739f8 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -25,7 +25,8 @@ impl FunBranch { rhs_args: &VarTerms, lhs_terms: &TermSet, lhs_preds: &PredApps, - invars: &mut TermSet, + invars: &mut TermMap>, + index: usize, ) -> Res> { let fun_args_len = fun_sig.len(); let (last, mut fresh) = if fun_args_len == rhs_args.len() { @@ -218,6 +219,21 @@ impl FunBranch { }; } + let value = if let Some(last) = last.as_ref() { + if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { + res + } else { + log! { @3 | "failed to retrieve value for {}", rhs_args[*last] } + return Ok(None); + } + } else if let Some(conj) = value { + term::and(conj) + } else { + term::tru() + }; + + log! { @4 | "value extraction successful: {}", value } + let mut invar_subst = VarHMap::with_capacity(1); // Term elligible as candidate invariants are the ones that mention **exactly one** @@ -238,7 +254,10 @@ impl FunBranch { let (invar, _) = term .subst_total(&invar_subst) .expect("total substitution cannot fail due to previous checks"); - invars.insert(invar); + invars + .entry(invar) + .or_insert_with(Vec::new) + .push((index, term.clone())); false } else { true @@ -254,26 +273,11 @@ impl FunBranch { log! { @4 |=> " {}", term } }; log! { @4 |=> " invariants:" }; - for invar in invars.iter() { + for invar in invars.keys() { log! { @4 |=> " {}", invar } }; } - let value = if let Some(last) = last.as_ref() { - if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { - res - } else { - log! { @3 | "failed to retrieve value for {}", rhs_args[*last] } - return Ok(None); - } - } else if let Some(conj) = value { - term::and(conj) - } else { - term::tru() - }; - - log! { @4 | "value extraction successful: {}", value } - Ok(Some(FunBranch { guard, value, @@ -308,6 +312,12 @@ struct FunDef { typ: Typ, /// Branches of the definition. branches: Vec, + /// Candidates invariants. + /// + /// Maps candidates to the index of the branch they come from, and the term they originate + /// from. If a candidate is not an invariant, it will be added back to the corresponding + /// branches. + candidate_invars: TermMap>, /// Invariants. /// /// Invariants are all terms ranging over exactly one variable, the variable `v_0`. This @@ -328,6 +338,7 @@ impl FunDef { sig, typ, branches: Vec::new(), + candidate_invars: TermMap::new(), invars: TermSet::new(), } } @@ -344,12 +355,15 @@ impl FunDef { bail!("FunDef::register_clause called with illegal clause") }; + let index = self.branches.len(); + let res = if let Some(branch) = FunBranch::new( (self.pred, &self.name, &self.sig, &self.typ), rhs_args, clause.lhs_terms(), clause.lhs_preds(), - &mut self.invars, + &mut self.candidate_invars, + index, )? { self.branches.push(branch); Some(self) @@ -364,15 +378,20 @@ impl FunDef { /// /// Once the invariants are confirmed, all the substitutions defined by `calls` in the branches /// are applied to the guard and the result. - pub fn check_invariants(&mut self, instance: &mut PreInstance) -> Res { + pub fn check_invariants(&mut self, instance: &mut PreInstance) -> Res<()> { macro_rules! solver { () => { instance.solver() }; } - if !self.invars.is_empty() { - log! { @3 | "checking {} invariants...", self.invars.len() } + if !self.candidate_invars.is_empty() { + log! { @3 | "checking {} invariants...", self.candidate_invars.len() } + if_log! { @4 + for invar in self.candidate_invars.keys() { + log! { @4 |=> "{}", invar } + } + } solver!().comment_args(format_args!( "checking candidate invariants for function {}", self.name @@ -381,15 +400,30 @@ impl FunDef { let mut subst = VarMap::with_capacity(1); - for invariant in &self.invars { + 'check_invariants: for (invariant, backtrack) in self.candidate_invars.drain() { solver!().comment_args(format_args!("checking candidate invariant {}", invariant))?; + macro_rules! backtrack { + () => {{ + for (index, term) in backtrack { + self.branches[index].guard.insert(term); + } + continue 'check_invariants; + }}; + } + // Check invariant holds for each branch. - 'all_branches: for branch in &self.branches { + 'all_branches: for branch_index in 0..self.branches.len() { + macro_rules! branch { + () => { + self.branches[branch_index] + }; + } + // Target invariant (negated). let neg_objective = { debug_assert! { subst.is_empty() } - subst.push(branch.value.clone()); + subst.push(branch!().value.clone()); let invariant = invariant .subst_total(&subst) @@ -400,7 +434,7 @@ impl FunDef { match invariant.bool() { Some(true) => continue 'all_branches, - Some(false) => return Ok(false), + Some(false) => backtrack!(), None => term::not(invariant), } }; @@ -411,23 +445,23 @@ impl FunDef { } // Declare recursive call variables. - if !branch.calls.is_empty() { + if !branch!().calls.is_empty() { solver!().comment("declaring vars for recursive calls")?; - for (var, _) in &branch.calls { + for (var, _) in &branch!().calls { solver!().declare_const(var, self.typ.get())? } } solver!().comment("branch guard")?; - for term in &branch.guard { + for term in &branch!().guard { solver!().assert(&smt::SmtTerm::new(term))? } - if !branch.calls.is_empty() { + if !branch!().calls.is_empty() { solver!().comment("recursion hypotheses")?; - for (var, _) in &branch.calls { + for (var, _) in &branch!().calls { debug_assert! { subst.is_empty() } subst.push(term::var(*var, self.typ.clone())); let invariant = invariant @@ -447,24 +481,33 @@ impl FunDef { if sat { log! { @3 | " not an invariant: {}", invariant } - return Ok(false); + backtrack!() } instance.reset_solver()?; } log! { @3 | " confirmed invariant: {}", invariant } + let is_new = self.invars.insert(invariant); + debug_assert! { is_new } } for branch in &mut self.branches { branch.propagate_calls() } - Ok(true) + Ok(()) } /// Checks that all branches are exclusive and exhaustive. pub fn check_branches(&self, instance: &mut PreInstance) -> Res { + let res = self.inner_check_branches(instance)?; + instance.reset_solver()?; + Ok(res) + } + + /// Checks that all branches are exclusive and exhaustive. + fn inner_check_branches(&self, instance: &mut PreInstance) -> Res { macro_rules! solver { () => { instance.solver() @@ -477,6 +520,11 @@ impl FunDef { solver!().declare_fun(&self.name, &self.sig, self.typ.get())?; + solver!().comment_args(format_args!( + "checking branches for {} are exclusive", + self.name + ))?; + let mut actlits = Vec::with_capacity(self.branches.len()); for branch in &self.branches { @@ -505,6 +553,11 @@ impl FunDef { log! { @3 | "all branches are exclusive, checking they're exhaustive" } + solver!().comment_args(format_args!( + "checking branches for {} are exhaustive", + self.name + ))?; + for branch in &self.branches { let conj = smt::TermConj::new(branch.guard.iter()); solver!().assert_with(&conj, false)?; @@ -524,15 +577,12 @@ impl FunDef { /// Finalizes the function definition. pub fn finalize(mut self, instance: &mut PreInstance) -> Res> { - let okay = self - .check_invariants(instance) + self.check_invariants(instance) .chain_err(|| "while checking the invariants")?; - if okay { - log!{ @3 | "all invariant(s) are fine" } - } else { - log!{ @3 | "failed to verify invariants" } - return Ok(None); + let invs = self.invars.len(); + if invs > 0 { + log! { @3 | "discovered {} invariant(s)", invs } } let okay = self @@ -816,12 +866,12 @@ impl RedStrat for FunPreds { info += red_info; break; } else { - // let res = FunPreds::reduce_pred(instance, pred, true) ? ; + // let res = FunPreds::reduce_pred(instance, pred, true)?; // // pause("to resume fun_preds", & Profiler::new()) ; // if let Some(red_info) = res { - // new_stuff = true ; - // info += red_info ; - // break + // new_stuff = true; + // info += red_info; + // break; // } () } @@ -853,15 +903,15 @@ pub fn args_invert(args: &VarTerms, args_len: usize) -> Res "subst:" } for (var, term) in & subst { - log! { @6 " {}: {}", var.default_str(), term } + log! { @6 |=> " {}: {}", var.default_str(), term } } - log! { @6 "cube:" } + log! { @6 |=> "cube:" } for term in & cube { - log! { @6 " {}", term } + log! { @6 |=> " {}", term } } } diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index ee28773b..a8a19265 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -61,6 +61,25 @@ impl RedStrat for StrictNeg { bail!("inconsistent instance state") } ; + + let clause = clause.rewrite_clause_for_app( + pred, args, 0.into() + ).chain_err( + || "during clause rewriting" + )?; + + let (pred, args) = if let Some( + (pred, argss) + ) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (* pred, args) + } else { + bail!("inconsistent instance state") + } + } else { + bail!("inconsistent instance state") + } ; + match extractor.terms_of_lhs_app( false, instance, clause.vars(), ( clause.lhs_terms(), clause.lhs_preds() ), @@ -105,6 +124,12 @@ impl RedStrat for StrictNeg { for (pred, terms_opt) in partial_defs { if let Some(mut terms) = terms_opt { + log! { @3 "success ({} term(s))", terms.len() } + if_log! { @4 + for term in & terms { + log! { @4 |=> "{}", term } + } + } if let Some(term) = instance.get_str(pred) { terms.push(term.clone()) } diff --git a/src/instance/preproc/utils.rs b/src/instance/preproc/utils.rs index d8359772..458f5b44 100644 --- a/src/instance/preproc/utils.rs +++ b/src/instance/preproc/utils.rs @@ -179,7 +179,7 @@ impl ExtractionCxt { Terms: IntoIterator, F: Fn(Term) -> Term, { - log! { @4 "terms_of_terms" } + log! { @5 | "terms_of_terms" } // Finds terms which variables are related to the ones from the predicate // applications. @@ -270,6 +270,8 @@ impl ExtractionCxt { ) -> Res> { debug_assert! { self.map.is_empty() } + log! { @5 | "terms of app to {}", args } + let mut app_vars = VarSet::with_capacity(instance[pred].sig.len()); let mut terms = TermSet::with_capacity(7); @@ -277,12 +279,15 @@ impl ExtractionCxt { let mut postponed = Vec::with_capacity(args.len()); for (index, arg) in args.index_iter() { + log! { @6 | "v_{} -> {}", index, arg } if let Some(var) = arg.var_idx() { + log! { @6 | " success" } let _ = app_vars.insert(var); if let Some(pre) = self.map.insert(var, term::var(index, arg.typ())) { terms.insert(term::eq(term::var(index, arg.typ()), pre)); } } else { + log! { @6 | " postponed" } match arg.as_val().to_term() { Some(trm) => { debug_assert_eq! { trm.typ(), arg.typ() } @@ -294,7 +299,11 @@ impl ExtractionCxt { } } + log! { @6 | "postponed" } + for (var, arg) in postponed { + log! { @7 "v_{} -> {}", var, arg } + if let Some((term, _)) = arg.subst_total(&self.map) { terms.insert(term::eq(term::var(var, arg.typ()), term)); } else if let Some((v, inverted)) = arg.invert_var(var, arg.typ()) { @@ -310,6 +319,7 @@ impl ExtractionCxt { true, |term| term::eq(term::var(var, term.typ()), term), )? { + log! { @6 | "failed to extract argument v_{}: {}", var, arg } return Ok(None); } } @@ -330,7 +340,7 @@ impl ExtractionCxt { (lhs_terms, lhs_preds): (&TermSet, &'a PredApps), (pred, args): (PrdIdx, &VarTerms), ) -> Res)>> { - log!{ @5 "extracting application's terms" } + log!{ @5 "terms of lhs part" } let (terms, mut app_vars) = if let Some(res) = self.terms_of_app(var_info, instance, pred, args)? { diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 97373b33..59f7e380 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -134,6 +134,9 @@ pub struct Assistant { using_adts: bool, /// Maps predicates to their positive / strict negative clause data. clauses: PrdHMap>, + + /// True if some recursive functions are defined. + using_rec_funs: bool, } impl Assistant { @@ -146,6 +149,13 @@ impl Assistant { let clauses = PrdHMap::new(); + let mut using_rec_funs = false; + + fun::iter(|_| { + using_rec_funs = true; + Ok(()) + })?; + let mut res = Assistant { // core, solver, @@ -153,6 +163,7 @@ impl Assistant { _profiler, using_adts, clauses, + using_rec_funs, }; res.register_clauses()?; @@ -386,7 +397,15 @@ impl Assistant { .assert(&ArgValEq::new(args, vals, vars.as_ref()))?; let sat = profile! { - self wrap { self.solver.check_sat() } "smt" + self wrap { + if self.using_rec_funs { + smt::multi_try_check_sat(& mut self.solver) + } else { + self.solver.check_sat().map_err( + |e| e.into() + ) + } + } "smt" }?; solver!(pop); diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 2651b42d..32d65a34 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -959,26 +959,21 @@ impl<'a> Teacher<'a> { } } else { log! { @debug " checksat" } - let (sat, actlit) = profile! { - self wrap { + let sat = profile! { + self wrap { - if self.using_rec_funs { - self.solver.get_actlit().and_then( - |actlit| { - let sat = self.solver.check_sat_act( Some(& actlit) ) ? ; - Ok( (sat, Some(actlit)) ) + if self.using_rec_funs { + smt::multi_try_check_sat(& mut self.solver) + } else { + self.solver.check_sat().map_err( + |e| e.into() + ) } - ) - } else { - self.solver.check_sat().map( - |sat| (sat, None) - ) - } - } "cexs", "check-sat" + } "cexs", "check-sat" }?; - let res = if sat { + if sat { log! { @debug " sat, getting cex" } let bias = if self.instance[clause].is_positive() { Bias::Lft @@ -998,13 +993,7 @@ impl<'a> Teacher<'a> { Ok(Some((cex, bias))) } else { Ok(None) - }; - - if let Some(actlit) = actlit { - self.solver.de_actlit(actlit)? } - - res } } diff --git a/src/term/eval.rs b/src/term/eval.rs index dccaa9a2..7be1275c 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -28,6 +28,7 @@ pub fn eval(term: &Term, model: &E) -> Res { let res = zip( term, + |_| Ok(None), |zip_null| leaf(model, zip_null), |op, typ, values| total(op, typ, values, &mut fun_ref_count), partial, diff --git a/src/term/mod.rs b/src/term/mod.rs index 0fdbddc0..f0cb2b49 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -319,6 +319,15 @@ impl RTerm { } } + /// Returns the kids of a datatype selector. + pub fn dtyp_slc_inspect(&self) -> Option<(&Typ, &str, &Term)> { + if let RTerm::DTypSlc { typ, name, term } = self { + Some((typ, name, term)) + } else { + None + } + } + /// Iterator over over all the leafs of a term. pub fn leaf_iter(&self) -> LeafIter { LeafIter::of_rterm(self) @@ -919,6 +928,13 @@ impl RTerm { // otherwise. let res = zip( &self.to_hcons(), + |term| { + if term.fun_inspect().is_some() { + Err(()) + } else { + Ok(None) + } + }, |_| Ok(()), |zip_op, _, _: ()| match zip_op { ZipOp::Fun(_) => Err(()), @@ -931,8 +947,8 @@ impl RTerm { } => Err(()), mut frame => { let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: - empty `rgt_args` (has_fun_app_or_adt)", + "illegal call to `partial_op`: \ + empty `rgt_args` (has_fun_app_or_adt)", ); Ok(ZipDo::Trm { nu_term, frame }) } @@ -950,6 +966,16 @@ impl RTerm { // otherwise. let res = zip( &self.to_hcons(), + |term| { + if term.fun_inspect().is_some() + || term.dtyp_new_inspect().is_some() + || term.dtyp_slc_inspect().is_some() + { + Err(()) + } else { + Ok(None) + } + }, |_| Ok(()), |zip_op, _, _: ()| match zip_op { ZipOp::Fun(_) | ZipOp::New(_) | ZipOp::Slc(_) => Err(()), @@ -981,6 +1007,89 @@ impl RTerm { res.is_err() } + /// TOP-DOWN term substitution. + pub fn term_subst(&self, map: &TermMap) -> Term { + self.top_down_map(|term| map.get(term).cloned()) + } + + /// TOP-DOWN map over terms. + pub fn top_down_map(&self, mut f: Fun) -> Term + where + Fun: for<'a> FnMut(&'a Term) -> Option, + { + use self::zip::*; + let res: Res = zip( + &self.to_hcons(), + |term| Ok(f(term)), + |zip_null| match zip_null { + ZipNullary::Cst(val) => Ok(cst(val.clone())), + ZipNullary::Var(typ, var) => Ok(term::var(var, typ.clone())), + }, + |zip_op, typ, mut acc| { + let yielded = match zip_op { + ZipOp::Op(op) => term::app(op, acc), + ZipOp::New(name) => term::dtyp_new(typ.clone(), name.clone(), acc), + + ZipOp::Slc(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype selector {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_slc(typ.clone(), name.clone(), kid) + } else { + panic!( + "illegal application of datatype selector {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::Tst(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype tester {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_tst(name.clone(), kid) + } else { + panic!( + "illegal application of datatype tester {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::CArray => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal constant array application to {} arguments", + acc.len() + 1 + ) + } + term::cst_array(typ.clone(), kid) + } else { + panic!("illegal constant array application to 0 arguments") + }, + ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), + }; + + Ok(ZipDoTotal::Upp { yielded }) + }, + |mut frame| { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: \ + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + }, + ); + + res.expect("top down map can never fail") + } + /// Variable substitution. /// /// The `total` flag causes substitution to fail if a variable that's not in @@ -997,6 +1106,7 @@ impl RTerm { let res = zip( &self.to_hcons(), + |_| Ok(None), |zip_null| match zip_null { ZipNullary::Cst(val) => Ok(cst(val.clone())), ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { diff --git a/src/term/zip.rs b/src/term/zip.rs index 1c5afe6e..fb154fa3 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -152,8 +152,9 @@ impl Accumulator<()> for () { } /// Zip function. -pub fn zip( +pub fn zip( term: &Term, + mut dwn_do: DwnF, mut nul_do: NulF, mut app_do: AppF, mut partial: Partial, @@ -162,6 +163,8 @@ where Acc: Accumulator, Yield: Clone, + DwnF: for<'a> FnMut(&'a Term) -> Result, E>, + NulF: for<'a> FnMut(ZipNullary<'a>) -> Result, AppF: for<'a> FnMut(ZipOp<'a>, &'a Typ, Acc) -> Result, E>, @@ -192,72 +195,99 @@ where 'inspect_term: loop { // stack_print!() ; - let result = match *term.get() { - RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { - ZipDoTotal::Upp { - yielded: subst[var_idx].clone(), - } - } else { - ZipDoTotal::Upp { - yielded: nul_do(ZipNullary::Var(typ, var_idx))?, - } - }, - - RTerm::Cst(ref cst) => ZipDoTotal::Upp { - yielded: nul_do(ZipNullary::Cst(cst))?, - }, - - RTerm::CArray { - ref typ, - term: ref nu_term, - } => { - let frame = ZipFrame { - thing: ZipOp::CArray, - typ, - lft_args: Acc::new_empty(1), - rgt_args: empty.iter(), - }; - stack.push((frame, subst.clone())); - term = nu_term; - - continue 'inspect_term; - } + let result = if let Some(yielded) = dwn_do(term)? { + ZipDoTotal::Upp { yielded } + } else { + match *term.get() { + RTerm::Var(ref typ, var_idx) => if let Some(subst) = subst.as_ref() { + ZipDoTotal::Upp { + yielded: subst[var_idx].clone(), + } + } else { + ZipDoTotal::Upp { + yielded: nul_do(ZipNullary::Var(typ, var_idx))?, + } + }, - RTerm::App { - op, - ref typ, - ref args, - } => { - let mut rgt_args = args.iter(); - let op = ZipOp::Op(op); - let lft_args = Acc::new_empty(args.len()); + RTerm::Cst(ref cst) => ZipDoTotal::Upp { + yielded: nul_do(ZipNullary::Cst(cst))?, + }, - if let Some(nu_term) = rgt_args.next() { + RTerm::CArray { + ref typ, + term: ref nu_term, + } => { let frame = ZipFrame { - thing: op, + thing: ZipOp::CArray, typ, - lft_args, - rgt_args, + lft_args: Acc::new_empty(1), + rgt_args: empty.iter(), }; stack.push((frame, subst.clone())); term = nu_term; continue 'inspect_term; - } else { - app_do(op, typ, lft_args)? } - } - RTerm::DTypNew { - ref typ, - ref name, - ref args, - } => { - let mut rgt_args = args.iter(); - let op = ZipOp::New(name); - let lft_args = Acc::new_empty(args.len()); + RTerm::App { + op, + ref typ, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::Op(op); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? + } + } + + RTerm::DTypNew { + ref typ, + ref name, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::New(name); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? + } + } + + RTerm::DTypSlc { + ref typ, + ref name, + term: ref nu_term, + } => { + let mut rgt_args = empty.iter(); + let op = ZipOp::Slc(name); + let lft_args = Acc::new_empty(1); - if let Some(nu_term) = rgt_args.next() { let frame = ZipFrame { thing: op, typ, @@ -268,63 +298,17 @@ where term = nu_term; continue 'inspect_term; - } else { - app_do(op, typ, lft_args)? } - } - - RTerm::DTypSlc { - ref typ, - ref name, - term: ref nu_term, - } => { - let mut rgt_args = empty.iter(); - let op = ZipOp::Slc(name); - let lft_args = Acc::new_empty(1); - - let frame = ZipFrame { - thing: op, - typ, - lft_args, - rgt_args, - }; - stack.push((frame, subst.clone())); - term = nu_term; - - continue 'inspect_term; - } - - RTerm::DTypTst { - ref typ, - ref name, - term: ref nu_term, - } => { - let mut rgt_args = empty.iter(); - let op = ZipOp::Tst(name); - let lft_args = Acc::new_empty(1); - - let frame = ZipFrame { - thing: op, - typ, - lft_args, - rgt_args, - }; - stack.push((frame, subst.clone())); - term = nu_term; - - continue 'inspect_term; - } - RTerm::Fun { - ref typ, - ref name, - ref args, - } => { - let mut rgt_args = args.iter(); - let op = ZipOp::Fun(name); - let lft_args = Acc::new_empty(args.len()); + RTerm::DTypTst { + ref typ, + ref name, + term: ref nu_term, + } => { + let mut rgt_args = empty.iter(); + let op = ZipOp::Tst(name); + let lft_args = Acc::new_empty(1); - if let Some(nu_term) = rgt_args.next() { let frame = ZipFrame { thing: op, typ, @@ -335,8 +319,31 @@ where term = nu_term; continue 'inspect_term; - } else { - app_do(op, typ, lft_args)? + } + + RTerm::Fun { + ref typ, + ref name, + ref args, + } => { + let mut rgt_args = args.iter(); + let op = ZipOp::Fun(name); + let lft_args = Acc::new_empty(args.len()); + + if let Some(nu_term) = rgt_args.next() { + let frame = ZipFrame { + thing: op, + typ, + lft_args, + rgt_args, + }; + stack.push((frame, subst.clone())); + term = nu_term; + + continue 'inspect_term; + } else { + app_do(op, typ, lft_args)? + } } } }; From 80cda522efc81f0aefee2b269a1d14f1377239f7 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 10 Sep 2018 20:15:19 +0900 Subject: [PATCH 54/94] atty --- Cargo.lock | 21 +++++---------------- Cargo.toml | 4 ++-- src/common/config.rs | 2 +- src/hoice.rs | 2 +- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bc0e5d6..8e4321ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ name = "backtrace-sys" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -114,11 +114,11 @@ name = "hoice" version = "1.5.0" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", @@ -127,16 +127,6 @@ dependencies = [ "rsmt2 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "isatty" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lazy_static" version = "1.1.0" @@ -153,7 +143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mylib" version = "0.1.0" -source = "git+https://github.com/AdrienChampion/mylib#c0d479af6df03d5c6f0f5599a3a34d7652103915" +source = "git+https://github.com/AdrienChampion/mylib#10d82f09c336a51a1f1199fa81b1eaa53e885508" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -326,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131" +"checksum cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "70f2a88c2e69ceee91c209d8ef25b81fc1a65f42c7f14dfd59d1fed189e514d1" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -335,7 +325,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c241e038569e815af4c1153387e239dcfe30ae8adf77b39a08f4ea0a829b0c" -"checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)" = "" diff --git a/Cargo.toml b/Cargo.toml index 1b9c91dc..a7da8d99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,9 +37,9 @@ clap = "*" hashconsing = "*" error-chain = "*" ansi_term = "*" -rsmt2 = "*" # { path = "../rsmt2" } # "^0.9.11" +rsmt2 = "^0.9.11" num = "*" mylib = { git = "https://github.com/AdrienChampion/mylib" } either = "*" rand = "*" -isatty = "*" \ No newline at end of file +atty = "*" \ No newline at end of file diff --git a/src/common/config.rs b/src/common/config.rs index 8b491cdc..14ed8553 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -1205,7 +1205,7 @@ impl Config { } // Colors. - let color = ::isatty::stdout_isatty() && bool_of_matches(&matches, "color"); + let color = ::atty::is(::atty::Stream::Stdout) && bool_of_matches(&matches, "color"); let styles = Styles::new(color); // Output directory. diff --git a/src/hoice.rs b/src/hoice.rs index d5173abc..9feb5133 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -20,8 +20,8 @@ extern crate clap; extern crate ansi_term as ansi; #[macro_use] extern crate hashconsing; +extern crate atty; extern crate either; -extern crate isatty; extern crate libc; extern crate num; extern crate rand; From c2b7554e14a268e75dc255fbe59e2c05fd2c9ae1 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 12 Sep 2018 14:53:34 +0900 Subject: [PATCH 55/94] improvements (?) on adts --- src/common/smt.rs | 144 ++++++++++++++++++++- src/hoice.rs | 2 +- src/instance/instance/clause.rs | 53 ++++---- src/instance/instance/mod.rs | 4 +- src/instance/preproc/fun_preds.rs | 35 ++++- src/instance/preproc/strict_neg_clauses.rs | 3 + src/teacher/mod.rs | 123 ++++++++++++++++-- 7 files changed, 314 insertions(+), 50 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index e3800e43..2b9ba32a 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -60,6 +60,32 @@ pub fn preproc_reset

(solver: &mut Solver

) -> Res<()> { preproc_init(solver) } +/// Performs a check-sat. +pub fn tmo_multi_try_check_sat( + solver: &mut Solver

, + tmo: ::std::time::Duration, + do_stuff: F, + // final_unbounded_check: bool, +) -> Res +where + F: FnOnce(&mut Solver

) -> Res<()>, +{ + solver.set_option(":timeout", &format!("{}000", tmo.as_secs()))?; + if let Some(res) = multi_try_check_sat_or_unk(solver)? { + return Ok(res); + } + do_stuff(solver)?; + // if !final_unbounded_check { + // multi_try_check_sat(solver) + // } else { + // if let Some(res) = multi_try_check_sat_or_unk(solver)? { + // return Ok(res); + // } + // solver.set_option(":timeout", "1000000000")?; + multi_try_check_sat(solver) + // } +} + /// Performs a check-sat. /// /// Tries to check-sat a solver with various strategies. Returns the result of the first tries that @@ -73,11 +99,17 @@ pub fn preproc_reset

(solver: &mut Solver

) -> Res<()> { /// /// Check-sat without any actlits. pub fn multi_try_check_sat

(solver: &mut Solver

) -> Res { - if let Some(res) = actlit_check_sat(solver)? { + if let Some(res) = solver.check_sat_or_unk()? { Ok(res) } else { - let res = solver.check_sat()?; - Ok(res) + Ok(actlit_check_sat(solver)?) + } +} +pub fn multi_try_check_sat_or_unk

(solver: &mut Solver

) -> Res> { + if let Some(res) = solver.check_sat_or_unk()? { + Ok(Some(res)) + } else { + Ok(actlit_check_sat_or_unk(solver)?) } } @@ -85,7 +117,12 @@ pub fn multi_try_check_sat

(solver: &mut Solver

) -> Res { /// /// Does **not** deactivate the actlit once it's done. This is to allow `get-model` after the /// check. -fn actlit_check_sat

(solver: &mut Solver

) -> Res> { +fn actlit_check_sat

(solver: &mut Solver

) -> Res { + let actlit = solver.get_actlit()?; + let res = solver.check_sat_act(Some(&actlit))?; + Ok(res) +} +fn actlit_check_sat_or_unk

(solver: &mut Solver

) -> Res> { let actlit = solver.get_actlit()?; let res = solver.check_sat_act_or_unk(Some(&actlit))?; Ok(res) @@ -109,6 +146,92 @@ impl<'a> Expr2Smt<()> for SmtTerm<'a> { } } +/// SMT-prints a predicate application using the default var writer. +pub struct SmtPredApp<'a> { + /// The predicate. + pub pred: PrdIdx, + /// Its arguments. + pub args: &'a VarTerms, +} +impl<'a> SmtPredApp<'a> { + /// Constructor. + pub fn new(pred: PrdIdx, args: &'a VarTerms) -> Self { + SmtPredApp { pred, args } + } +} +impl<'a, 'b> Expr2Smt<(&'b PrdInfos, bool)> for SmtPredApp<'a> { + fn expr_to_smt2( + &self, + w: &mut Writer, + (infos, pos): (&'b PrdInfos, bool), + ) -> SmtRes<()> { + if !pos { + write!(w, "(not ")? + } + if self.args.is_empty() { + write!(w, "{}", infos[self.pred])? + } else { + write!(w, "({}", infos[self.pred])?; + for arg in self.args.iter() { + write!(w, " ")?; + arg.write(w, |w, v| v.default_write(w))? + } + write!(w, ")")? + } + if !pos { + write!(w, ")")? + } + Ok(()) + } +} + +/// Smt-prints a clause negated, with its universal quantifier. +pub struct NegQClause<'a> { + /// The clause. + pub clause: &'a Clause, +} +impl<'a> NegQClause<'a> { + /// Constructor. + pub fn new(clause: &'a Clause) -> Self { + NegQClause { clause } + } +} +impl<'a> Expr2Smt<(&'a PrdSet, &'a PrdSet, &'a PrdInfos)> for NegQClause<'a> { + fn expr_to_smt2( + &self, + w: &mut Writer, + (true_preds, false_preds, others): (&'a PrdSet, &'a PrdSet, &'a PrdInfos), + ) -> SmtRes<()> { + writeln!(w, "(not")?; + self.clause.forall_write( + w, + |w, var_info| var_info.idx.default_write(w), + |w, prd, args| { + if true_preds.contains(&prd) { + write!(w, "true") + } else if false_preds.contains(&prd) { + write!(w, "false") + } else { + let pred = &others[prd].name; + if args.is_empty() { + write!(w, "{}", pred) + } else { + write!(w, "({}", pred)?; + for arg in args.iter() { + write!(w, " ")?; + arg.write(w, |w, var| var.default_write(w))? + } + write!(w, ")") + } + } + }, + 4, + )?; + write!(w, " )")?; + Ok(()) + } +} + /// Smt-prints a clause that has no predicate application. pub struct SmtSideClause<'a> { /// The clause. @@ -710,7 +833,18 @@ impl FullParser { impl<'a> IdentParser for FullParser { fn parse_ident(self, input: &'a str) -> SmtRes { - if input.len() >= 2 && &input[0..2] == "v_" { + if input.len() >= 2 && &input[0..2] == "v_" && { + let mut okay = true; + for c in input[2..].chars() { + if c.is_whitespace() { + break; + } else if !c.is_numeric() { + okay = false; + break; + } + } + okay + } { match usize::from_str(&input[2..]) { Ok(idx) => Ok(FPVar::Var(idx.into())), Err(e) => bail!("could not retrieve var index from `{}`: {}", input, e), diff --git a/src/hoice.rs b/src/hoice.rs index 9feb5133..bd1f1d41 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -251,7 +251,7 @@ pub fn read_and_work( None } Err(e) => { - println!("bail, {}", e.description()); + // println!("bail, {}", e.description()); bail!(e) } } diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 8a77580c..78bcbf36 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -996,9 +996,14 @@ impl Clause { let mut map = TermMap::with_capacity(args.len()); // Maps the clause's original variables to the set of arguments they appear in, along with // the corresponding fresh variable. - let mut var_map = VarHMap::new(); + // let mut var_map = VarHMap::new(); for arg in args.iter() { + // Already a variable? + if arg.var_idx().is_some() { + continue; + } + let var = map .entry(arg.clone()) .or_insert_with(|| term::var(clause.vars.next_index(), arg.typ())) @@ -1009,22 +1014,22 @@ impl Clause { .vars .push(VarInfo::new(idx.default_str(), arg.typ(), idx)); - for arg_var in term::vars(arg) { - var_map - .entry(arg_var) - .or_insert_with(Vec::new) - .push((arg, idx)) - } + // for arg_var in term::vars(arg) { + // var_map + // .entry(arg_var) + // .or_insert_with(Vec::new) + // .push((arg, idx)) + // } } // True if we actually saw the predicate application in question. let mut legal = false; // Variables still appearing in the clause. - let mut vars = VarSet::new(); + // let mut vars = VarSet::new(); for term in &self.lhs_terms { let term = term.term_subst(&map); - vars.extend(term::vars(&term).into_iter()); + // vars.extend(term::vars(&term).into_iter()); clause.insert_term(term); } @@ -1041,7 +1046,7 @@ impl Clause { let mut nu_p_args = VarMap::with_capacity(p_args.len()); for arg in p_args.iter() { let arg = arg.term_subst(&map); - vars.extend(term::vars(&arg).into_iter()); + // vars.extend(term::vars(&arg).into_iter()); nu_p_args.push(arg) } let nu_p_args = var_to::terms::new(nu_p_args); @@ -1057,27 +1062,31 @@ impl Clause { let mut nu_p_args = VarMap::with_capacity(p_args.len()); for arg in p_args.iter() { let arg = arg.term_subst(&map); - vars.extend(term::vars(&arg).into_iter()); + // vars.extend(term::vars(&arg).into_iter()); nu_p_args.push(arg) } let nu_p_args = var_to::terms::new(nu_p_args); clause.rhs = Some((*p, nu_p_args)) } - for info in &mut clause.vars { - info.active = false - } + // for info in &mut clause.vars { + // info.active = false + // } - for var in vars { - if let Some(equalities) = var_map.remove(&var) { - for (arg, idx) in equalities { - let var = term::var(idx, arg.typ()); - clause.lhs_terms.insert(term::eq(arg.clone(), var)); - } - } - clause.vars[var].active = true + for (term, var) in map { + clause.insert_term(term::eq(term, var)); } + // for var in vars { + // if let Some(equalities) = var_map.remove(&var) { + // for (arg, idx) in equalities { + // let var = term::var(idx, arg.typ()); + // clause.lhs_terms.insert(term::eq(arg.clone(), var)); + // } + // } + // clause.vars[var].active = true + // } + if !legal { bail!("clause rewriting for application called on unknown application") } else { diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 77d507c7..a54afad5 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -759,8 +759,8 @@ impl Instance { clauses.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); let mut prev = None; for clause in clauses.drain(0..) { - log_debug!{ - " forgetting {}", self[clause].to_string_info(& self.preds) ? + log!{ @6 + "forgetting {}", self[clause].to_string_info(& self.preds) ? } if prev == Some(clause) { continue; diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index e13739f8..d90260d4 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -479,12 +479,12 @@ impl FunDef { let sat = solver!().check_sat()?; + instance.reset_solver()?; + if sat { log! { @3 | " not an invariant: {}", invariant } backtrack!() } - - instance.reset_solver()?; } log! { @3 | " confirmed invariant: {}", invariant } @@ -577,6 +577,29 @@ impl FunDef { /// Finalizes the function definition. pub fn finalize(mut self, instance: &mut PreInstance) -> Res> { + if self.typ.is_arith() { + let (zero, one) = if self.typ.is_int() { + (term::int(0), term::int(1)) + } else { + ( + term::real((0.into(), 1.into())), + term::real((1.into(), 1.into())), + ) + }; + + let term = term::var(0, self.typ.clone()); + + let t = term::ge(term.clone(), zero.clone()); + self.candidate_invars.entry(t).or_insert_with(Vec::new); + let t = term::le(term.clone(), zero.clone()); + self.candidate_invars.entry(t).or_insert_with(Vec::new); + + let t = term::ge(term.clone(), one.clone()); + self.candidate_invars.entry(t).or_insert_with(Vec::new); + let t = term::le(term.clone(), term::u_minus(one.clone())); + self.candidate_invars.entry(t).or_insert_with(Vec::new); + } + self.check_invariants(instance) .chain_err(|| "while checking the invariants")?; @@ -790,10 +813,6 @@ impl RedStrat for FunPreds { while new_stuff { new_stuff = false; - if instance.active_pred_count() <= 1 { - return Ok(info); - } - let mut to_inline: Vec<_> = instance .preds() .iter() @@ -859,6 +878,10 @@ impl RedStrat for FunPreds { } while let Some(pred) = to_inline.pop() { + if instance.active_pred_count() <= 1 { + return Ok(info); + } + let res = FunPreds::reduce_pred(instance, pred, false)?; // pause("to resume fun_preds", & Profiler::new()) ; if let Some(red_info) = res { diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index a8a19265..0283c591 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -48,6 +48,7 @@ impl RedStrat for StrictNeg { for (clause_idx, clause) in strict_clauses { log! { @3 "working on clause #{}", clause_idx } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } let (pred, args) = if let Some( (pred, argss) @@ -67,6 +68,8 @@ impl RedStrat for StrictNeg { ).chain_err( || "during clause rewriting" )?; + log! { @3 "rewriting successful" } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } let (pred, args) = if let Some( (pred, argss) diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 32d65a34..e61a83e2 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -816,6 +816,23 @@ impl<'a> Teacher<'a> { let instance = self.instance.clone(); + let mut got_unknown = false; + + macro_rules! handle_clause_res { + ($e:expr) => { + match $e { + Ok(()) => Ok(()), + Err(e) => if e.is_unknown() { + smt::reset(&mut self.solver, &self.instance)?; + got_unknown = true; + Ok(()) + } else { + Err(e) + }, + } + }; + } + // True if we got some positive or negative samples. // let mut got_pos_neg_samples = false ; @@ -824,7 +841,7 @@ impl<'a> Teacher<'a> { instance.pos_clauses().len() } for clause in instance.pos_clauses() { - self.get_cexs_of_clause(cands, *clause, &mut map, false)? + handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, false))? } log! { @verb @@ -832,7 +849,7 @@ impl<'a> Teacher<'a> { instance.strict_neg_clauses().len() } for clause in instance.strict_neg_clauses() { - self.get_cexs_of_clause(cands, *clause, &mut map, false)? + handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, false))? } // got_pos_neg_samples = ! map.is_empty() ; @@ -843,7 +860,7 @@ impl<'a> Teacher<'a> { instance.non_strict_neg_clauses().len() } for clause in instance.non_strict_neg_clauses() { - self.get_cexs_of_clause(cands, *clause, &mut map, true)? + handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, true))? } } @@ -854,10 +871,14 @@ impl<'a> Teacher<'a> { } for clause in instance.imp_clauses() { - self.get_cexs_of_clause(cands, *clause, &mut map, true)? + handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, true))? } } + if map.is_empty() && got_unknown { + bail!(ErrorKind::SmtError(::rsmt2::errors::ErrorKind::Unknown)) + } + log! { @debug "extracted {} cexs", map.iter().fold( 0, |acc, (_, cexs)| acc + cexs.len() @@ -963,7 +984,35 @@ impl<'a> Teacher<'a> { self wrap { if self.using_rec_funs { - smt::multi_try_check_sat(& mut self.solver) + let solver = & mut self.solver; + let tru_preds = & self.tru_preds; + let fls_preds = & self.fls_preds; + let instance = & self.instance; + smt::tmo_multi_try_check_sat( + solver, + conf.until_timeout().map( + |time| time / 20 + ).unwrap_or_else( || Duration::new(1,0) ), + |solver| { + let clause = smt::NegQClause::new(& instance[clause]); + solver.assert_with( + & clause, + ( + tru_preds, + fls_preds, + instance.preds(), + ) + )?; + Ok(()) + }, + ) + // if res.as_ref().err().map( + // |e| e.is_unknown() + // ).unwrap_or(false) { + // smt::multi_try_check_sat(solver) + // } else { + // res + // } } else { self.solver.check_sat().map_err( |e| e.into() @@ -1020,15 +1069,61 @@ impl<'a> Teacher<'a> { profile!{ self tick "cexs", "prep" } clause!().declare(&mut self.solver)?; - self.solver.assert_with( - clause!(), - &( - false, - &self.tru_preds, - &self.fls_preds, - self.instance.preds(), - ), - )?; + + if self.using_rec_funs { + log! { @4 | "assert/check-sat lhs terms" } + for term in clause!().lhs_terms() { + log! { @5 "{}", term } + self.solver.assert(&smt::SmtTerm::new(term))?; + + let res = smt::multi_try_check_sat(&mut self.solver); + if let Ok(false) = res { + return Ok(vec![]); + } + } + + log! { @4 | "assert/check-sat lhs preds" } + for (pred, argss) in clause!().lhs_preds() { + if self.tru_preds.contains(pred) { + continue; + } + + for args in argss { + log! { @5 | "({} {})", self.instance[*pred], args } + self.solver.assert_with( + &smt::SmtPredApp::new(*pred, args), + (self.instance.preds(), true), + )?; + + let res = smt::multi_try_check_sat(&mut self.solver); + if let Ok(false) = res { + return Ok(vec![]); + } + } + } + + if let Some((pred, args)) = clause!().rhs() { + if !self.fls_preds.contains(&pred) { + log! { @4 | "assert/check-sat rhs pred" } + log! { @5 | "(not ({} {}))", self.instance[pred], args } + self.solver.assert_with( + &smt::SmtPredApp::new(pred, args), + (self.instance.preds(), false), + )? + } + } + } else { + self.solver.assert_with( + clause!(), + &( + false, + &self.tru_preds, + &self.fls_preds, + self.instance.preds(), + ), + )? + } + profile!{ self mark "cexs", "prep" } macro_rules! get_cex { From 95c8104648f0d3718589cd9bf6eed24ee192c5fc Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 13 Sep 2018 11:27:32 +0900 Subject: [PATCH 56/94] better gain pivots when using rec funs --- src/common/smt.rs | 20 ++++++++++---------- src/learning/ice/mod.rs | 17 +++++++++++++++-- src/teacher/mod.rs | 20 +++++++++++++++----- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index 2b9ba32a..82fd9a00 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -65,7 +65,7 @@ pub fn tmo_multi_try_check_sat( solver: &mut Solver

, tmo: ::std::time::Duration, do_stuff: F, - // final_unbounded_check: bool, + final_unbounded_check: bool, ) -> Res where F: FnOnce(&mut Solver

) -> Res<()>, @@ -75,15 +75,15 @@ where return Ok(res); } do_stuff(solver)?; - // if !final_unbounded_check { - // multi_try_check_sat(solver) - // } else { - // if let Some(res) = multi_try_check_sat_or_unk(solver)? { - // return Ok(res); - // } - // solver.set_option(":timeout", "1000000000")?; - multi_try_check_sat(solver) - // } + if !final_unbounded_check { + multi_try_check_sat(solver) + } else { + if let Some(res) = multi_try_check_sat_or_unk(solver)? { + return Ok(res); + } + solver.set_option(":timeout", "1000000000")?; + multi_try_check_sat(solver) + } } /// Performs a check-sat. diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index 0bde5503..d1925507 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -119,6 +119,19 @@ impl<'core> IceLearner<'core> { synth_sys.push(SynthSys::new(&instance[pred].sig)) } + let mut using_rec_funs = false; + + fun::iter(|_| { + using_rec_funs = true; + Ok(()) + })?; + + let (gain_pivot, gain_pivot_synth) = if using_rec_funs { + (0.9999f64, Some(0.4f64)) + } else { + (conf.ice.gain_pivot, conf.ice.gain_pivot_synth) + }; + use rand::SeedableRng; Ok(IceLearner { @@ -140,8 +153,8 @@ impl<'core> IceLearner<'core> { pre_skip_rng: { Rng::from_seed([245; 16]) }, luby: if mine { None } else { Some(LubyCount::new()) }, known_quals: TermSet::new(), - gain_pivot: conf.ice.gain_pivot, - gain_pivot_synth: conf.ice.gain_pivot_synth, + gain_pivot, + gain_pivot_synth, count: 0, }) } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index e61a83e2..132baefd 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -918,7 +918,7 @@ impl<'a> Teacher<'a> { self.solver.push(1)? } - let cexs = self.get_cex(clause, bias, conf.teacher.max_bias)?; + let cexs = self.get_cex(clause, bias, conf.teacher.max_bias, !map.is_empty())?; if self.restart_on_cex { smt::reset(&mut self.solver, &self.instance)? @@ -956,6 +956,7 @@ impl<'a> Teacher<'a> { &mut self, clause: ClsIdx, bias: Option<(Actlit, Bias)>, + just_try: bool, ) -> Res> { if let Some((actlit, bias)) = bias { log! { @debug @@ -992,7 +993,7 @@ impl<'a> Teacher<'a> { solver, conf.until_timeout().map( |time| time / 20 - ).unwrap_or_else( || Duration::new(1,0) ), + ).unwrap_or_else( || Duration::new(5,0) ), |solver| { let clause = smt::NegQClause::new(& instance[clause]); solver.assert_with( @@ -1005,6 +1006,7 @@ impl<'a> Teacher<'a> { )?; Ok(()) }, + ! just_try ) // if res.as_ref().err().map( // |e| e.is_unknown() @@ -1047,7 +1049,13 @@ impl<'a> Teacher<'a> { } /// Checks if a clause is falsifiable and returns a model if it is. - pub fn get_cex(&mut self, clause_idx: ClsIdx, bias: bool, bias_only: bool) -> Res> { + pub fn get_cex( + &mut self, + clause_idx: ClsIdx, + bias: bool, + bias_only: bool, + just_try: bool, + ) -> Res> { let mut cexs = vec![]; log! { @debug "working on clause #{}", clause_idx } @@ -1129,13 +1137,15 @@ impl<'a> Teacher<'a> { macro_rules! get_cex { () => { // Normal check, no actlit. - if let Some(cex) = self.check_sat_cex(clause_idx, None)? { + if let Some(cex) = self.check_sat_cex(clause_idx, None, just_try)? { cexs.push(cex) } }; ($actlit:expr ; $bias:expr) => { - if let Some(cex) = self.check_sat_cex(clause_idx, Some(($actlit, $bias)))? { + if let Some(cex) = + self.check_sat_cex(clause_idx, Some(($actlit, $bias)), just_try)? + { cexs.push(cex) } }; From a28a5a4cf574df3868780cbbed09b5603f312ab1 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 14 Sep 2018 12:43:13 +0900 Subject: [PATCH 57/94] let binding when printing, restricted function reconstruction --- Cargo.lock | 12 +- Cargo.toml | 2 +- src/common/smt.rs | 39 +++- src/common/wrappers.rs | 8 + src/instance/instance/clause.rs | 85 ++++++++- src/instance/instance/mod.rs | 10 +- src/instance/instance/pre_instance.rs | 1 + src/instance/preproc/fun_preds.rs | 110 ++++++----- src/instance/preproc/strict_neg_clauses.rs | 136 ++++++-------- src/teacher/mod.rs | 9 +- src/term/bindings.rs | 209 +++++++++++++++++++++ src/term/factory.rs | 6 +- src/term/mod.rs | 182 ++++++++++++++++-- src/term/simplify.rs | 26 +-- src/term/zip.rs | 6 + 15 files changed, 651 insertions(+), 190 deletions(-) create mode 100644 src/term/bindings.rs diff --git a/Cargo.lock b/Cargo.lock index 8e4321ec..f3ffe76b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ name = "backtrace-sys" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -44,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -104,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hashconsing" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/AdrienChampion/hashconsing#1cb71f67b72c0a0818bef3f948377af62cb1cd68" dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -118,7 +118,7 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)", @@ -316,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "70f2a88c2e69ceee91c209d8ef25b81fc1a65f42c7f14dfd59d1fed189e514d1" +"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -324,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum hashconsing 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c241e038569e815af4c1153387e239dcfe30ae8adf77b39a08f4ea0a829b0c" +"checksum hashconsing 0.10.1 (git+https://github.com/AdrienChampion/hashconsing)" = "" "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum mylib 0.1.0 (git+https://github.com/AdrienChampion/mylib)" = "" diff --git a/Cargo.toml b/Cargo.toml index a7da8d99..538a6b06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ bench = [ ] libc = "*" lazy_static = "*" clap = "*" -hashconsing = "*" +hashconsing = { git = "https://github.com/AdrienChampion/hashconsing" } error-chain = "*" ansi_term = "*" rsmt2 = "^0.9.11" diff --git a/src/common/smt.rs b/src/common/smt.rs index 82fd9a00..b7d623a1 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -206,7 +206,7 @@ impl<'a> Expr2Smt<(&'a PrdSet, &'a PrdSet, &'a PrdInfos)> for NegQClause<'a> { self.clause.forall_write( w, |w, var_info| var_info.idx.default_write(w), - |w, prd, args| { + |w, prd, args, bindings| { if true_preds.contains(&prd) { write!(w, "true") } else if false_preds.contains(&prd) { @@ -219,7 +219,7 @@ impl<'a> Expr2Smt<(&'a PrdSet, &'a PrdSet, &'a PrdInfos)> for NegQClause<'a> { write!(w, "({}", pred)?; for arg in args.iter() { write!(w, " ")?; - arg.write(w, |w, var| var.default_write(w))? + arg.write_with(w, |w, var| var.default_write(w), bindings)? } write!(w, ")") } @@ -248,7 +248,7 @@ impl<'a> Expr2Smt<()> for SmtSideClause<'a> { self.clause.forall_write( w, |w, var_info| var_info.idx.default_write(w), - |_, _, _| panic!("illegal side clause: found predicate application(s)"), + |_, _, _, _| panic!("illegal side clause: found predicate application(s)"), 2, )?; Ok(()) @@ -263,13 +263,19 @@ pub struct SmtConj<'a, Trms> { has_fun_apps: bool, /// Variable informations. infos: &'a VarInfos, + /// Let bindings. + bindings: Option<&'a term::Bindings>, } impl<'a, 'b, Trms> SmtConj<'b, Trms> where Trms: Iterator + ExactSizeIterator + Clone, { /// Constructor. - pub fn new(terms: IntoIter, infos: &'b VarInfos) -> Self + pub fn new( + terms: IntoIter, + infos: &'b VarInfos, + bindings: Option<&'b term::Bindings>, + ) -> Self where IntoIter: IntoIterator, { @@ -285,6 +291,7 @@ where terms, has_fun_apps, infos, + bindings, } } @@ -301,7 +308,14 @@ where } } solver.assert(self)?; - let sat = multi_try_check_sat(solver)?; + let sat = tmo_multi_try_check_sat( + solver, + conf.until_timeout() + .map(|time| time / 20) + .unwrap_or_else(|| ::std::time::Duration::new(1, 0)), + |_| Ok(()), + true, + )?; Ok(!sat) } } @@ -335,12 +349,18 @@ where if self.terms.len() == 0 { write!(w, "true")? } else { + if let Some(bindings) = self.bindings { + bindings.write_opening(w, VarIdx::write, "")? + } write!(w, "(and")?; for term in self.terms.clone() { write!(w, " ")?; - term.write(w, |w, var| var.default_write(w))?; + term.write_with(w, |w, var| var.default_write(w), self.bindings)?; + } + write!(w, ")")?; + if let Some(bindings) = self.bindings { + bindings.write_closing(w, "")? } - write!(w, ")")? } write!(w, "{}", suffix)?; Ok(()) @@ -989,7 +1009,10 @@ impl ClauseTrivialExt for Solver { } let res = { - let conj = SmtConj::new(lhs.iter(), &clause.vars); + let bindings = term::bindings::Builder::new() + .scan_terms(&lhs) + .build(clause.vars.next_index()); + let conj = SmtConj::new(lhs.iter(), &clause.vars, bindings.as_ref()); if clause.rhs().is_none() && clause.lhs_preds().is_empty() { // Either it is trivial, or falsifiable regardless of the predicates. diff --git a/src/common/wrappers.rs b/src/common/wrappers.rs index a034ced0..0a7fda2f 100644 --- a/src/common/wrappers.rs +++ b/src/common/wrappers.rs @@ -47,6 +47,14 @@ impl VarIdx { self.default_write(&mut s).unwrap(); ::std::str::from_utf8(&s).unwrap().into() } + + /// Default way to write variables: `v_`. + pub fn write(w: &mut W, var: Self) -> ::std::io::Result<()> + where + W: Write, + { + var.default_write(w) + } } impl Into for VarMap<::term::Term> { diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index 78bcbf36..d79b6b7d 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -85,6 +85,32 @@ impl Clause { self.shrink_vars() } + /// True if the clause features a function call. + pub fn has_fun_apps(&self) -> bool { + for term in self.lhs_terms() { + if term.has_fun_apps() { + return true; + } + } + for (_, argss) in self.lhs_preds() { + for args in argss { + for arg in args.iter() { + if arg.has_fun_apps() { + return true; + } + } + } + } + if let Some((_, args)) = self.rhs() { + for arg in args.iter() { + if arg.has_fun_apps() { + return true; + } + } + } + false + } + /// Inserts a top term in the lhs. /// /// Returns true if it was not there (`is_new`). @@ -839,7 +865,7 @@ impl Clause { where W: Write, WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, - WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms, Option<&term::Bindings>) -> IoRes<()>, { if info { writeln!( @@ -878,7 +904,7 @@ impl Clause { where W: Write, WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, - WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms, Option<&term::Bindings>) -> IoRes<()>, { write!( w, @@ -916,13 +942,27 @@ impl Clause { w: &mut W, write_var: WriteVar, write_prd: WritePrd, - indent: usize, + original_indent: usize, ) -> IoRes<()> where W: Write, WriteVar: Fn(&mut W, &VarInfo) -> IoRes<()>, - WritePrd: Fn(&mut W, PrdIdx, &VarTerms) -> IoRes<()>, + WritePrd: Fn(&mut W, PrdIdx, &VarTerms, Option<&term::Bindings>) -> IoRes<()>, { + let bindings = self.bindings(); + let bindings = bindings.as_ref(); + + let mut indent = original_indent; + + if let Some(bindings) = bindings { + indent += 2; + bindings.write_opening( + w, + |w, var| write_var(w, &self[var]), + &" ".repeat(original_indent), + )? + } + write!( w, "{nil: >indent$}(=>\n{nil: >indent$} (and\n{nil: >indent$} ", @@ -935,7 +975,7 @@ impl Clause { } else { for term in &self.lhs_terms { write!(w, " ")?; - term.write(w, |w, var| write_var(w, &self.vars[var]))? + term.write_with(w, |w, var| write_var(w, &self.vars[var]), bindings)? } } @@ -947,7 +987,7 @@ impl Clause { for (pred, argss) in &self.lhs_preds { for args in argss { write!(w, " ")?; - write_prd(w, *pred, args)? + write_prd(w, *pred, args, bindings)? } } } @@ -960,12 +1000,16 @@ impl Clause { )?; if let Some((pred, ref args)) = self.rhs { - write_prd(w, pred, args)? + write_prd(w, pred, args, bindings)? } else { write!(w, "false")? } writeln!(w, "\n{nil: >indent$})", nil = "", indent = indent)?; + if let Some(bindings) = bindings { + bindings.write_closing(w, &" ".repeat(original_indent))? + } + Ok(()) } @@ -1093,6 +1137,16 @@ impl Clause { Ok(clause) } } + + /// Retrieves or constructs the let-bindings for this clause. + /// + /// Vector is sorted by the depth of the terms in the map. For each map, all terms should have + /// the same depth. + pub fn bindings(&self) -> Option { + term::bindings::Builder::new() + .scan_clause(&self) + .build(self.vars.next_index()) + } } impl ::std::ops::Index for Clause { @@ -1114,6 +1168,13 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau ) -> SmtRes<()> { let (pos, ref true_preds, ref false_preds, ref prd_info) = *info; + let bindings = self.bindings(); + let bindings = bindings.as_ref(); + + if let Some(bindings) = bindings { + bindings.write_opening(writer, VarIdx::write, "")?; + } + if !pos { write!(writer, "(not ")? } @@ -1124,7 +1185,7 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau for term in &self.lhs_terms { write!(writer, " ")?; - term.write(writer, |w, var| var.default_write(w))? + term.write_with(writer, VarIdx::write, bindings)? } for (pred, argss) in &self.lhs_preds { @@ -1138,7 +1199,7 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau writer.write_all(prd_info[*pred].name.as_bytes())?; for arg in args.iter() { write!(writer, " ")?; - arg.write(writer, |w, var| var.default_write(w))? + arg.write_with(writer, VarIdx::write, bindings)? } write!(writer, ")")? } @@ -1161,7 +1222,7 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau write!(writer, "{}", prd_info[prd].name)?; for arg in args.iter() { write!(writer, " ")?; - arg.write(writer, |w, var| var.default_write(w))? + arg.write_with(writer, VarIdx::write, bindings)? } if !args.is_empty() { write!(writer, ")")? @@ -1179,6 +1240,10 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau write!(writer, ")")? } + if let Some(bindings) = bindings { + bindings.write_closing(writer, "")?; + } + Ok(()) } } diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index a54afad5..7167a1d1 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -1210,7 +1210,7 @@ impl Instance { side_clause.write( w, |w, var_info| write!(w, "{}", var_info.name), - |_, _, _| panic!("illegal side-clause: found predicate application(s)"), + |_, _, _, _| panic!("illegal side-clause: found predicate application(s)"), true, )?; writeln!(w)?; @@ -1253,14 +1253,14 @@ impl Instance { clause.write( w, |w, var_info| write!(w, "{}", var_info.name), - |w, p, args| { + |w, p, args, bindings| { if !args.is_empty() { write!(w, "(")? } w.write_all(self[p].name.as_bytes())?; for arg in args.iter() { write!(w, " ")?; - arg.write(w, |w, var| write!(w, "{}", clause.vars[var]))? + arg.write_with(w, |w, var| write!(w, "{}", clause.vars[var]), bindings)? } if !args.is_empty() { write!(w, ")") @@ -1702,12 +1702,12 @@ impl<'a> PebcakFmt<'a> for Clause { self.write( w, |w, var_info| write!(w, "{}", var_info.name), - |w, prd, args| { + |w, prd, args, bindings| { write!(w, "(")?; w.write_all(prds[prd].as_bytes())?; for arg in args.iter() { write!(w, " ")?; - arg.write(w, |w, var| write!(w, "{}", self.vars[var]))? + arg.write_with(w, |w, var| write!(w, "{}", self.vars[var]), bindings)? } write!(w, ")") }, diff --git a/src/instance/instance/pre_instance.rs b/src/instance/instance/pre_instance.rs index 9a17b6c8..e11b2284 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/instance/pre_instance.rs @@ -1954,6 +1954,7 @@ impl ClauseSimplifier { clause.deactivate(var)? } } + // println!("{}", clause.to_string_info(_preds).unwrap()) ; Ok(Some(changed)) } diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index d90260d4..f484bcd0 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -294,7 +294,7 @@ impl FunBranch { .map(|term| term.subst(&self.calls).0) .collect(); self.value = self.value.subst(&self.calls).0; - self.calls.clear() + // self.calls.clear() } } } @@ -650,12 +650,9 @@ impl FunDef { // Build actual definition. let mut def = None; for branch in self.branches.into_iter().rev() { + let cond = term::and(branch.guard.into_iter().collect()); let mut nu_def = if let Some(def) = def { - term::ite( - term::and(branch.guard.into_iter().collect()), - branch.value, - def, - ) + term::ite(cond, branch.value, def) } else { branch.value }; @@ -817,45 +814,61 @@ impl RedStrat for FunPreds { .preds() .iter() .filter_map(|info| { - let inline = { - let pred = info.idx; + let pred = info.idx; - // Predicate is still unknown. - ! instance.is_known(pred) + // for clause in instance.clauses_of(pred).1 { + // println!( + // "{}", + // instance[*clause].to_string_info(instance.preds()).unwrap() + // ); + // } - // When `pred` appears in the rhs of a clause, the lhs only mentions - // `pred`. - && instance.clauses_of(pred).1.iter().all( - |clause| instance[* clause].lhs_preds().iter().all( - |(p, _)| * p == pred - ) - ) - - // `pred` appears more than once in a clause where it's not alone. - // && instance.clauses_of(pred).0.iter().any( - // |clause| instance[* clause].lhs_preds().get( - // & pred - // ).unwrap().len() > 1 - // && ( - // instance[* clause].lhs_preds().len() > 1 - // || instance[* clause].rhs().map( - // |(p, _)| pred != * p - // ).unwrap_or(false) - // ) - // ) - - // `pred` has only one dtyp argument (ignoring the last argument) - && info.sig.len() > 1 - && info.sig[ 0 .. info.sig.len() - 1 ].iter().fold( - 0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc } - ) <= 1 - }; + // Predicate is still unknown. + if instance.is_known(pred) { + // println!(" known"); + return None; + } - if inline { - Some(info.idx) - } else { - None + // When `pred` appears in the rhs of a clause, the lhs only mentions + // `pred`. Also, lhs has no function application. + if !instance.clauses_of(pred).1.iter().all(|clause| { + !instance[*clause].has_fun_apps() && instance[*clause] + .lhs_preds() + .iter() + .all(|(p, _)| *p == pred) + }) { + // println!(" lhs problem"); + return None; } + + // `pred` appears more than once in a clause where it's not alone. + // && instance.clauses_of(pred).0.iter().any( + // |clause| instance[* clause].lhs_preds().get( + // & pred + // ).unwrap().len() > 1 + // && ( + // instance[* clause].lhs_preds().len() > 1 + // || instance[* clause].rhs().map( + // |(p, _)| pred != * p + // ).unwrap_or(false) + // ) + // ) + + // `pred` has only one dtyp argument (ignoring the last argument) + if info.sig.len() <= 1 + && info.sig[0..info.sig.len() - 1].iter().fold(0, |acc, typ| { + if typ.is_dtyp() { + acc + 1 + } else { + acc + } + }) > 1 + { + // println!(" more than one dtyp arg"); + return None; + } + + Some(info.idx) }).collect(); to_inline.sort_by(|p_1, p_2| { @@ -978,7 +991,9 @@ pub fn map_invert( while let Some((term, to_invert)) = stack.pop() { match to_invert { - RTerm::DTypNew { typ, name, args } => { + RTerm::DTypNew { + typ, name, args, .. + } => { let selectors = typ.selectors_of(name)?; debug_assert_eq! { args.len(), selectors.len() } @@ -1008,7 +1023,9 @@ pub fn map_invert( } // Constant array. - RTerm::CArray { term: inner, typ } => { + RTerm::CArray { + term: inner, typ, .. + } => { stack.push(( // Array is constant, select any value. term::select(term, typ.default_term()), @@ -1016,7 +1033,7 @@ pub fn map_invert( )) } - RTerm::App { typ, op, args } => { + RTerm::App { typ, op, args, .. } => { match op { Op::CMul => if args[0].val().is_some() { let nu_term = if typ.is_int() { @@ -1145,6 +1162,7 @@ pub fn map_invert( typ, name, term: inner, + .. } => if let Some((inner, _)) = inner.subst_total(&(&*subst, &nu_subst)) { nu_cube.push(term::eq( term, @@ -1162,7 +1180,9 @@ pub fn map_invert( return Ok(false); }, - RTerm::Fun { typ, name, args } => { + RTerm::Fun { + typ, name, args, .. + } => { let mut nu_args = Vec::with_capacity(args.len()); for arg in args { if let Some((arg, _)) = arg.subst_total(&(&*subst, &nu_subst)) { diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index 0283c591..b87ea811 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -41,83 +41,71 @@ impl RedStrat for StrictNeg { }}; } - scoped! { - let ( - extractor, instance, strict_clauses - ) = instance.strict_neg_clauses() ; - - for (clause_idx, clause) in strict_clauses { - log! { @3 "working on clause #{}", clause_idx } - log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } - - let (pred, args) = if let Some( - (pred, argss) - ) = clause.lhs_preds().iter().next() { - if let Some(args) = argss.iter().next() { - (* pred, args) - } else { - bail!("inconsistent instance state") - } - } else { - bail!("inconsistent instance state") - } ; - - - let clause = clause.rewrite_clause_for_app( - pred, args, 0.into() - ).chain_err( - || "during clause rewriting" - )?; - log! { @3 "rewriting successful" } - log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } - - let (pred, args) = if let Some( - (pred, argss) - ) = clause.lhs_preds().iter().next() { - if let Some(args) = argss.iter().next() { - (* pred, args) - } else { - bail!("inconsistent instance state") - } - } else { - bail!("inconsistent instance state") - } ; - - match extractor.terms_of_lhs_app( - false, instance, clause.vars(), - ( clause.lhs_terms(), clause.lhs_preds() ), - clause.rhs(), (pred, args) - ) ? { - ExtractRes::Trivial | - ExtractRes::SuccessTrue => clauses_to_rm.push( clause_idx ), - - ExtractRes::SuccessFalse => pdef!(pred => set false), - - ExtractRes::Success( (qvars, pred_app, tterms) ) => - if qvars.is_empty() { - if pred_app.is_some() - || ! tterms.preds().is_empty() { - bail!("inconsistent instance state") - } - - let terms = tterms.terms() ; - - if terms.is_empty() { - pdef!(pred => set false) + { + let (extractor, instance, strict_clauses) = instance.strict_neg_clauses(); + + for (idx, clause) in strict_clauses { + log! { @3 "working on clause #{}", idx } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } + + let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (*pred, args) + } else { + bail!("inconsistent instance state") + } + } else { + bail!("inconsistent instance state") + }; + + let clause = clause + .rewrite_clause_for_app(pred, args, 0.into()) + .chain_err(|| "during clause rewriting")?; + log! { @3 "rewriting successful" } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } + + let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (*pred, args) + } else { + bail!("inconsistent instance state") + } } else { - let term = term::or( - terms.iter().map( - |term| term::not( term.clone() ) - ).collect() - ) ; - pdef!(pred => add term) + bail!("inconsistent instance state") + }; + + match extractor.terms_of_lhs_app( + false, + instance, + clause.vars(), + (clause.lhs_terms(), clause.lhs_preds()), + clause.rhs(), + (pred, args), + )? { + ExtractRes::Trivial | ExtractRes::SuccessTrue => clauses_to_rm.push(idx), + + ExtractRes::SuccessFalse => pdef!(pred => set false), + + ExtractRes::Success((qvars, pred_app, tterms)) => if qvars.is_empty() { + if pred_app.is_some() || !tterms.preds().is_empty() { + bail!("inconsistent instance state") + } + + let terms = tterms.terms(); + + if terms.is_empty() { + pdef!(pred => set false) + } else { + let term = term::or( + terms.iter().map(|term| term::not(term.clone())).collect(), + ); + pdef!(pred => add term) + } + }, + + ExtractRes::Failed => (), } - }, - - ExtractRes::Failed => (), } - - } } if !clauses_to_rm.is_empty() { diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 132baefd..44eac5f1 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -1083,11 +1083,10 @@ impl<'a> Teacher<'a> { for term in clause!().lhs_terms() { log! { @5 "{}", term } self.solver.assert(&smt::SmtTerm::new(term))?; - - let res = smt::multi_try_check_sat(&mut self.solver); - if let Ok(false) = res { - return Ok(vec![]); - } + } + let res = smt::multi_try_check_sat(&mut self.solver); + if let Ok(false) = res { + return Ok(vec![]); } log! { @4 | "assert/check-sat lhs preds" } diff --git a/src/term/bindings.rs b/src/term/bindings.rs new file mode 100644 index 00000000..1d29f904 --- /dev/null +++ b/src/term/bindings.rs @@ -0,0 +1,209 @@ +//! Let bindings module. + +use common::*; +use instance::Clause; + +/// Stores let bindings. +/// +/// Stores a sequence of let bindings. Each element of the sequence is a map from terms to a +/// variable. Each term map is such that all terms have exactly the same depth. The sequence is +/// ordered by increasing term depth. +/// +/// This ensures that the sequence `[ map_i ]` is such that if `i < j`, no subterm of a term in +/// `map_i` can appear in `map_j`. +/// +/// `Bindings` are constructed using `BindingBuilder`s. +#[derive(Debug, Clone)] +pub struct Bindings { + first_fresh: VarIdx, + bindings: Vec>, +} +impl Bindings { + /// Bindings accessor. + pub fn bindings(&self) -> &[TermMap] { + &self.bindings + } + + /// True if the bindings are empty. + pub fn is_empty(&self) -> bool { + self.bindings.is_empty() + } + + /// Writes a variable in the default format. + pub fn write_var(w: &mut W, var: VarIdx) -> IoRes<()> + where + W: Write, + { + VarIdx::write(w, var) + } + + /// Writes the **opening** of the let-bindings. + pub fn write_opening( + &self, + w: &mut W, + write_var: WriteVar, + pref: &str, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + // let write_var = &mut write_var; + let first_fresh = self.first_fresh; + for (current, term_map) in self.bindings.iter().enumerate() { + writeln!(w, "{}(let", pref)?; + write!(w, "{} (", pref)?; + for (term, var) in term_map { + write!(w, " ({} ", var.default_str())?; + term.write_with_raw( + w, + |w, var| { + if var >= first_fresh { + write!(w, "{}", var.default_str()) + } else { + write_var(w, var) + } + }, + Some(&self.bindings[0..current]), + )?; + write!(w, ")")? + } + writeln!(w, " )")? + } + + Ok(()) + } + + /// Writes the **closing** part of the let-bindings. + pub fn write_closing(&self, w: &mut W, pref: &str) -> IoRes<()> + where + W: Write, + { + write!(w, "{}", pref)?; + for _ in &self.bindings { + write!(w, ")")? + } + writeln!(w)?; + Ok(()) + } +} + +/// Bindings builder. +/// +/// Aggregates information about how many times a (sub)term appears in the term gradually fed to +/// it. This builder uses information from term hashconsing: all terms that do not have more than 2 +/// references pointing to themselves are ignored. +/// +/// This implies that the builder assumes all the terms it will work on are already constructed: +/// the haconsing reference counts are relevant for all the terms the bindings are for. +#[derive(Default)] +pub struct Builder { + /// Maps term depth to a map from terms of that depth to the number of times they where + /// encountered in the terms seen so far. + depth_map: BTreeMap>, +} +impl Builder { + /// Constructor. + pub fn new() -> Self { + Builder { + depth_map: BTreeMap::new(), + } + } + + /// Builds bindings from the information it accumulated so far. + pub fn build(mut self, mut fresh: VarIdx) -> Option { + let first_fresh = fresh; + let mut empty = 0; + for term_map in self.depth_map.values_mut() { + term_map.retain(|_, count| *count > 2); + if term_map.is_empty() { + empty += 1 + } + } + + if empty == self.depth_map.len() { + return None; + } + + let mut bindings = Vec::with_capacity(self.depth_map.len() - empty); + + for (_depth, term_map) in self.depth_map { + if term_map.is_empty() { + continue; + } + + let mut nu_term_map = TermMap::new(); + for (term, _) in term_map { + debug_assert_eq! { term.depth(), _depth } + let prev = nu_term_map.insert(term, fresh); + debug_assert! { prev.is_none() } + fresh.inc() + } + + bindings.push(nu_term_map) + } + + Some(Bindings { + first_fresh, + bindings, + }) + } + + /// Scans a term to the builder. + pub fn scan_term(mut self, term: &Term) -> Self { + term.iter(|term| { + let term = term.to_hcons(); + if term.arc_count() > 1 && term.depth() > 1 { + let count = self + .depth_map + .entry(term.depth()) + .or_insert_with(TermMap::new) + .entry(term) + .or_insert(0); + *count += 1 + } + }); + self + } + + /// Scans some terms to the builder. + pub fn scan_terms<'a, Terms>(mut self, terms: Terms) -> Self + where + Terms: IntoIterator, + { + for term in terms { + self = self.scan_term(term) + } + self + } + + /// Scans some predicate applications to the builder. + pub fn scan_pred_apps(mut self, apps: &PredApps) -> Self { + for argss in apps.values() { + for args in argss { + self = self.scan_terms(args.iter()) + } + } + self + } + + /// Scans an optional predicate application to the builder. + pub fn scan_pred_app(mut self, app: Option<(PrdIdx, &VarTerms)>) -> Self { + if let Some((_, args)) = app.as_ref() { + self = self.scan_terms(args.iter()) + } + self + } + + /// Scans only the lhs terms of a clause. + pub fn scan_clause_lhs_terms(self, clause: &Clause) -> Self { + self.scan_terms(clause.lhs_terms()) + } + + /// Scans a clause. + pub fn scan_clause(self, clause: &Clause) -> Self { + self.scan_clause_lhs_terms(clause) + .scan_pred_apps(clause.lhs_preds()) + .scan_pred_app(clause.rhs()) + } +} diff --git a/src/term/factory.rs b/src/term/factory.rs index 245978fc..7b60b507 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -225,7 +225,7 @@ pub fn cst_array(typ: Typ, default: Term) -> Term { if let Some(val) = default.val() { factory.mk(RTerm::Cst(val::array(typ, val))) } else { - factory.mk(RTerm::CArray { typ, term: default }) + factory.mk(RTerm::new_carray(typ, default)) } } @@ -265,7 +265,7 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { panic!("illegal function application") } - factory.mk(RTerm::Fun { typ, name, args }) + factory.mk(RTerm::new_fun(typ, name, args)) } /// Creates an operator application. @@ -690,6 +690,6 @@ fn normalize_app(mut op: Op, mut args: Vec, typ: Typ) -> NormRes { // print!(" {}", arg) // } // println!(")") ; - NormRes::Term(factory.mk(RTerm::App { typ, op, args })) + NormRes::Term(factory.mk(RTerm::new_app(typ, op, args))) } } diff --git a/src/term/mod.rs b/src/term/mod.rs index f0cb2b49..16cb78b7 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -47,7 +47,7 @@ //! //! // A `Term` dereferences to an `RTerm`: //! match * some_term { -//! RTerm::App { ref typ, op: Op::Eql, ref args } => { +//! RTerm::App { ref typ, op: Op::Eql, ref args, .. } => { //! assert_eq!( typ, & typ::bool() ) ; //! assert_eq!( args.len(), 2 ) ; //! assert_eq!( format!("{}", some_term), "(= (+ (* (- 2) v_5) 11) 0)" ) @@ -62,6 +62,7 @@ use common::*; #[macro_use] mod op; +pub mod bindings; mod eval; mod factory; mod leaf_iter; @@ -70,6 +71,7 @@ mod tterms; pub mod typ; mod zip; +pub use self::bindings::Bindings; pub use self::factory::*; pub use self::leaf_iter::LeafIter; pub use self::op::*; @@ -94,6 +96,8 @@ pub enum RTerm { /// /// The type is the type of **the indices** of the arrays. CArray { + /// Depth of this term. + depth: usize, /// Type of **the indices** (not the array). typ: Typ, /// Default term of the array. @@ -102,6 +106,8 @@ pub enum RTerm { /// An operator application. App { + /// Depth of this term. + depth: usize, /// Type of the application. typ: Typ, /// The operator. @@ -112,6 +118,8 @@ pub enum RTerm { /// A datatype constructor application. DTypNew { + /// Depth of this term. + depth: usize, /// Type of the application. typ: Typ, /// Name of the constructor. @@ -122,6 +130,8 @@ pub enum RTerm { /// A datatype selector application. DTypSlc { + /// Depth of this term. + depth: usize, /// Type of the application. typ: Typ, /// Name of the selector. @@ -132,6 +142,8 @@ pub enum RTerm { /// A datatype tester application. DTypTst { + /// Depth of this term. + depth: usize, /// Type of the term (always bool). typ: Typ, /// Name of the tester. @@ -142,6 +154,8 @@ pub enum RTerm { /// A function application. Fun { + /// Depth of this term. + depth: usize, /// Type of this term. typ: Typ, /// Function being applied. @@ -152,6 +166,98 @@ pub enum RTerm { } impl RTerm { + /// Constructs a constant array. + pub fn new_carray(typ: Typ, term: Term) -> Self { + RTerm::CArray { + depth: term.depth() + 1, + typ, + term, + } + } + /// Constructs a datatype selector application. + pub fn new_dtyp_slc(typ: Typ, name: S, term: Term) -> Self + where + S: Into, + { + let name = name.into(); + RTerm::DTypSlc { + depth: term.depth() + 1, + typ, + name, + term, + } + } + /// Constructs a datatype tester application. + pub fn new_dtyp_tst(typ: Typ, name: S, term: Term) -> Self + where + S: Into, + { + let name = name.into(); + RTerm::DTypTst { + depth: term.depth() + 1, + typ, + name, + term, + } + } + /// Constructs a function application. + pub fn new_fun(typ: Typ, name: S, args: Vec) -> Self + where + S: Into, + { + let depth = args + .iter() + .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); + let name = name.into(); + RTerm::Fun { + depth, + typ, + name, + args, + } + } + /// Constructs an operator application. + pub fn new_app(typ: Typ, op: Op, args: Vec) -> Self { + let depth = args + .iter() + .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); + RTerm::App { + depth, + typ, + op, + args, + } + } + /// Constructs a datatype constructor application. + pub fn new_dtyp_new(typ: Typ, name: S, args: Vec) -> Self + where + S: Into, + { + let name = name.into(); + let depth = args + .iter() + .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); + RTerm::DTypNew { + depth, + typ, + name, + args, + } + } + + /// Size of a term: number of subterms. + pub fn depth(&self) -> usize { + match self { + RTerm::Var(_, _) | RTerm::Cst(_) => 1, + RTerm::CArray { depth, .. } + | RTerm::App { depth, .. } + | RTerm::DTypNew { depth, .. } + | RTerm::DTypSlc { depth, .. } + | RTerm::DTypTst { depth, .. } + | RTerm::Fun { depth, .. } => *depth, + } + } + /// The operator and the kids of a term. pub fn app_inspect(&self) -> Option<(Op, &Vec)> { if let RTerm::App { op, ref args, .. } = *self { @@ -312,7 +418,10 @@ impl RTerm { /// Returns the kids of a datatype constructor. pub fn dtyp_new_inspect(&self) -> Option<(&Typ, &str, &[Term])> { - if let RTerm::DTypNew { typ, name, args } = self { + if let RTerm::DTypNew { + typ, name, args, .. + } = self + { Some((typ, name, args)) } else { None @@ -321,7 +430,10 @@ impl RTerm { /// Returns the kids of a datatype selector. pub fn dtyp_slc_inspect(&self) -> Option<(&Typ, &str, &Term)> { - if let RTerm::DTypSlc { typ, name, term } = self { + if let RTerm::DTypSlc { + typ, name, term, .. + } = self + { Some((typ, name, term)) } else { None @@ -360,7 +472,7 @@ impl RTerm { /// Type of the term. pub fn typ(&self) -> Typ { match self { - RTerm::CArray { typ, term } => typ::array(typ.clone(), term.typ()), + RTerm::CArray { typ, term, .. } => typ::array(typ.clone(), term.typ()), RTerm::Cst(val) => val.typ(), @@ -394,6 +506,34 @@ impl RTerm { /// Write a real term using a special function to write variables. pub fn write(&self, w: &mut W, write_var: WriteVar) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + self.write_with(w, write_var, None) + } + + /// Write a real term using a special function to write variables. + pub fn write_with( + &self, + w: &mut W, + write_var: WriteVar, + bindings: Option<&bindings::Bindings>, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + self.write_with_raw(w, write_var, bindings.map(|b| b.bindings())) + } + + /// Write a real term using a special function to write variables. + fn write_with_raw( + &self, + w: &mut W, + write_var: WriteVar, + bindings: Option<&[TermMap]>, + ) -> IoRes<()> where W: Write, WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, @@ -404,13 +544,23 @@ impl RTerm { // - the suffix, written once there's no more elements to write let mut stack = vec![(vec![self], "", "")]; - while let Some((mut to_do, sep, end)) = stack.pop() { + 'work: while let Some((mut to_do, sep, end)) = stack.pop() { use self::RTerm::*; if let Some(this_term) = to_do.pop() { stack.push((to_do, sep, end)); write!(w, "{}", sep)?; + // Is term in the bindings? + if let Some(bindings) = bindings { + for map in bindings { + if let Some(var) = map.get(&this_term.to_hcons()) { + Bindings::write_var(w, *var)?; + continue 'work; + } + } + } + match this_term { Var(_, v) => write_var(w, *v)?, @@ -650,7 +800,9 @@ impl RTerm { /// Forces the type of a datatype constructor. pub fn force_dtyp(&self, nu_typ: Typ) -> Option { match self { - RTerm::DTypNew { typ, name, args } => { + RTerm::DTypNew { + typ, name, args, .. + } => { debug_assert! { nu_typ.is_compatible(typ) } Some(dtyp_new(nu_typ, name.clone(), args.clone())) } @@ -705,7 +857,7 @@ impl RTerm { RTerm::App { op, args, .. } => term::app(*op, args.clone()), - RTerm::CArray { typ, term } => { + RTerm::CArray { typ, term, .. } => { let (src, tgt) = typ.array_inspect().unwrap(); stack.push(Frame::Arr(src.clone())); nu_typ = tgt.clone(); @@ -713,7 +865,9 @@ impl RTerm { continue 'go_down; } - RTerm::DTypNew { typ, name, args } => { + RTerm::DTypNew { + typ, name, args, .. + } => { let mut lft = vec![]; let mut next = None; let mut rgt = vec![]; @@ -761,17 +915,23 @@ impl RTerm { } } - RTerm::DTypSlc { typ, name, term } => { + RTerm::DTypSlc { + typ, name, term, .. + } => { debug_assert_eq! { typ, & nu_typ } term::dtyp_slc(typ.clone(), name.clone(), term.clone()) } - RTerm::DTypTst { name, term, typ } => { + RTerm::DTypTst { + name, term, typ, .. + } => { debug_assert_eq! { typ, & nu_typ } term::dtyp_tst(name.clone(), term.clone()) } - RTerm::Fun { typ, name, args } => { + RTerm::Fun { + typ, name, args, .. + } => { debug_assert_eq! { typ, & nu_typ } term::fun(typ.clone(), name.clone(), args.clone()) } diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 712d3383..2aa0a63b 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1788,7 +1788,7 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> RTerm { RTerm::Cst(val::dtyp_new(typ, name, vals)) } else { debug_assert!(!args.is_empty()); - RTerm::DTypNew { typ, name, args } + RTerm::new_dtyp_new(typ, name, args) } } @@ -1817,11 +1817,7 @@ pub fn dtyp_slc(typ: Typ, field: String, term: Term) -> Either { } } - Either::Left(RTerm::DTypSlc { - typ, - name: field, - term, - }) + Either::Left(RTerm::new_dtyp_slc(typ, field, term)) } /// Simplifies a datatype tester. @@ -1845,14 +1841,7 @@ pub fn dtyp_tst(constructor: String, term: Term) -> (RTerm, bool) { if let Some(args) = dtyp.news.get(&constructor) { if args.is_empty() { if let Some(constructor) = dtyp.rec_constructor() { - return ( - RTerm::DTypTst { - typ: typ::bool(), - name: constructor.into(), - term, - }, - false, - ); + return (RTerm::new_dtyp_tst(typ::bool(), constructor, term), false); } } } else { @@ -1865,12 +1854,5 @@ pub fn dtyp_tst(constructor: String, term: Term) -> (RTerm, bool) { ) } - ( - RTerm::DTypTst { - typ: typ::bool(), - name: constructor, - term, - }, - true, - ) + (RTerm::new_dtyp_tst(typ::bool(), constructor, term), true) } diff --git a/src/term/zip.rs b/src/term/zip.rs index fb154fa3..37ecb97a 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -216,6 +216,7 @@ where RTerm::CArray { ref typ, term: ref nu_term, + .. } => { let frame = ZipFrame { thing: ZipOp::CArray, @@ -233,6 +234,7 @@ where op, ref typ, ref args, + .. } => { let mut rgt_args = args.iter(); let op = ZipOp::Op(op); @@ -258,6 +260,7 @@ where ref typ, ref name, ref args, + .. } => { let mut rgt_args = args.iter(); let op = ZipOp::New(name); @@ -283,6 +286,7 @@ where ref typ, ref name, term: ref nu_term, + .. } => { let mut rgt_args = empty.iter(); let op = ZipOp::Slc(name); @@ -304,6 +308,7 @@ where ref typ, ref name, term: ref nu_term, + .. } => { let mut rgt_args = empty.iter(); let op = ZipOp::Tst(name); @@ -325,6 +330,7 @@ where ref typ, ref name, ref args, + .. } => { let mut rgt_args = args.iter(); let op = ZipOp::Fun(name); From 3c3cbaf7a4a5431b1b706bf597c47b070c9f795e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 14 Sep 2018 16:38:54 +0900 Subject: [PATCH 58/94] modified function reconstruction --- src/common/smt.rs | 32 +++--- src/instance/instance/clause.rs | 26 +++++ src/instance/preproc/fun_preds.rs | 169 +++++++++++++++++------------- src/term/mod.rs | 59 ++++++++++- 4 files changed, 192 insertions(+), 94 deletions(-) diff --git a/src/common/smt.rs b/src/common/smt.rs index b7d623a1..4c07ae72 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -259,8 +259,6 @@ impl<'a> Expr2Smt<()> for SmtSideClause<'a> { pub struct SmtConj<'a, Trms> { /// Conjunction. terms: Trms, - /// True if the terms have function applications. - has_fun_apps: bool, /// Variable informations. infos: &'a VarInfos, /// Let bindings. @@ -280,16 +278,8 @@ where IntoIter: IntoIterator, { let terms = terms.into_iter(); - let mut has_fun_apps = false; - for term in terms.clone() { - if term.has_fun_apps() { - has_fun_apps = true; - break; - } - } SmtConj { terms, - has_fun_apps, infos, bindings, } @@ -300,32 +290,34 @@ where if self.terms.len() == 0 { return Ok(false); } - if !self.has_fun_apps { - for var in self.infos { - if var.active { - solver.declare_const(&var.idx, var.typ.get())? - } + for var in self.infos { + if var.active { + solver.declare_const(&var.idx, var.typ.get())? } } - solver.assert(self)?; + + solver.assert_with(self, false)?; let sat = tmo_multi_try_check_sat( solver, conf.until_timeout() .map(|time| time / 20) .unwrap_or_else(|| ::std::time::Duration::new(1, 0)), - |_| Ok(()), + |solver| { + solver.assert_with(self, true)?; + Ok(()) + }, true, )?; Ok(!sat) } } -impl<'a, 'b, Trms> Expr2Smt<()> for SmtConj<'b, Trms> +impl<'a, 'b, Trms> Expr2Smt for SmtConj<'b, Trms> where Trms: Iterator + ExactSizeIterator + Clone, { - fn expr_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { - let suffix = if self.has_fun_apps { + fn expr_to_smt2(&self, w: &mut Writer, quantified: bool) -> SmtRes<()> { + let suffix = if quantified { write!(w, "(exists (")?; let mut inactive = 0; for var in self.infos { diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index d79b6b7d..be71a737 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -85,6 +85,32 @@ impl Clause { self.shrink_vars() } + /// True if the clause features a recursive function call. + pub fn has_rec_fun_apps(&self) -> bool { + for term in self.lhs_terms() { + if term.has_rec_fun_apps() { + return true; + } + } + for (_, argss) in self.lhs_preds() { + for args in argss { + for arg in args.iter() { + if arg.has_rec_fun_apps() { + return true; + } + } + } + } + if let Some((_, args)) = self.rhs() { + for arg in args.iter() { + if arg.has_rec_fun_apps() { + return true; + } + } + } + false + } + /// True if the clause features a function call. pub fn has_fun_apps(&self) -> bool { for term in self.lhs_terms() { diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index f484bcd0..e0efe0f2 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -683,7 +683,9 @@ impl FunDef { } } -pub struct FunPreds; +pub struct FunPreds { + to_inline: Vec<(PrdIdx, bool)>, +} impl FunPreds { /// Reduces a predicate to a function. @@ -799,98 +801,119 @@ impl RedStrat for FunPreds { } fn new(_: &Instance) -> Self { - FunPreds + FunPreds { + to_inline: Vec::new(), + } } fn apply(&mut self, instance: &mut PreInstance) -> Res { let mut info = RedInfo::new(); - let mut new_stuff = true; while new_stuff { new_stuff = false; + self.to_inline.clear(); + + 'all_preds: for info in instance.preds() { + let pred = info.idx; + + // println!("{}", instance[pred]); + // for clause in instance.clauses_of(pred).1 { + // println!( + // "{}", + // instance[*clause].to_string_info(instance.preds()).unwrap() + // ); + // } + + // Predicate is still unknown. + if instance.is_known(pred) { + // println!(" known"); + continue 'all_preds; + } - let mut to_inline: Vec<_> = instance - .preds() - .iter() - .filter_map(|info| { - let pred = info.idx; - - // for clause in instance.clauses_of(pred).1 { - // println!( - // "{}", - // instance[*clause].to_string_info(instance.preds()).unwrap() - // ); - // } - - // Predicate is still unknown. - if instance.is_known(pred) { - // println!(" known"); - return None; + // When `pred` appears in the rhs of a clause, the lhs only mentions + // `pred`. + for clause in instance.clauses_of(pred).1 { + if instance[*clause] + .lhs_preds() + .iter() + .any(|(p, _)| *p != pred) + { + continue 'all_preds; } + } - // When `pred` appears in the rhs of a clause, the lhs only mentions - // `pred`. Also, lhs has no function application. - if !instance.clauses_of(pred).1.iter().all(|clause| { - !instance[*clause].has_fun_apps() && instance[*clause] - .lhs_preds() - .iter() - .all(|(p, _)| *p == pred) - }) { - // println!(" lhs problem"); - return None; + // // `pred` appears more than once in a clause where it's not alone. + // let potential_relation = instance.clauses_of(pred).0.iter().any(|clause| { + // instance[*clause].lhs_preds().get(&pred).unwrap().len() > 1 + // && (instance[*clause].lhs_preds().len() > 1 || instance[*clause] + // .rhs() + // .map(|(p, _)| pred != *p) + // .unwrap_or(false)) + // && instance[*clause].rhs().is_none() + // }); + + let mut has_fun_apps = false; + + // Clauses where `pred` is in the rhs do not have function calls. + // if !potential_relation { + for clause in instance.clauses_of(pred).1 { + if instance[*clause].has_fun_apps() { + has_fun_apps = true } - - // `pred` appears more than once in a clause where it's not alone. - // && instance.clauses_of(pred).0.iter().any( - // |clause| instance[* clause].lhs_preds().get( - // & pred - // ).unwrap().len() > 1 - // && ( - // instance[* clause].lhs_preds().len() > 1 - // || instance[* clause].rhs().map( - // |(p, _)| pred != * p - // ).unwrap_or(false) - // ) - // ) - - // `pred` has only one dtyp argument (ignoring the last argument) - if info.sig.len() <= 1 - && info.sig[0..info.sig.len() - 1].iter().fold(0, |acc, typ| { - if typ.is_dtyp() { - acc + 1 - } else { - acc - } - }) > 1 - { - // println!(" more than one dtyp arg"); - return None; + if instance[*clause].has_fun_apps() { + // println!(" recursive function"); + continue 'all_preds; } + } + // } - Some(info.idx) - }).collect(); - - to_inline.sort_by(|p_1, p_2| { - let adt_count_1 = - instance[*p_1] - .sig - .iter() - .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); - let adt_count_2 = - instance[*p_2] - .sig + // `pred` has only one dtyp argument (ignoring the last argument) + if info.sig.len() <= 1 + || info.sig[0..info.sig.len() - 1] .iter() - .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); + .filter(|typ| typ.is_dtyp()) + .count() + > 1 + { + // println!(" more than one dtyp arg"); + continue 'all_preds; + } - adt_count_1.cmp(&adt_count_2).reverse() - }); + self.to_inline.push((pred, has_fun_apps)) + } + + // Note that we're reverse-sorting because we're going to pop below. + self.to_inline + .sort_by(|(p_1, has_fun_1), (p_2, has_fun_2)| { + use std::cmp::Ordering::*; + if *has_fun_1 && !*has_fun_2 { + return Less; + } else if *has_fun_2 && !*has_fun_1 { + return Greater; + } + + let (sig_1, sig_2) = (&instance[*p_1].sig, &instance[*p_2].sig); + let adt_count_1 = + sig_1 + .iter() + .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); + let adt_count_2 = + sig_2 + .iter() + .fold(0, |acc, typ| if typ.is_dtyp() { acc + 1 } else { acc }); + + match adt_count_1.cmp(&adt_count_2).reverse() { + ::std::cmp::Ordering::Equal => sig_1.len().cmp(&sig_2.len()).reverse(), + res => res, + } + }); - if to_inline.is_empty() { + if self.to_inline.is_empty() { return Ok(info); } - while let Some(pred) = to_inline.pop() { + while let Some((pred, _)) = self.to_inline.pop() { if instance.active_pred_count() <= 1 { return Ok(info); } diff --git a/src/term/mod.rs b/src/term/mod.rs index 16cb78b7..a354120d 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1089,7 +1089,7 @@ impl RTerm { let res = zip( &self.to_hcons(), |term| { - if term.fun_inspect().is_some() { + if let Some((_, _)) = term.fun_inspect() { Err(()) } else { Ok(None) @@ -1118,6 +1118,63 @@ impl RTerm { res.is_err() } + /// Returns true if the term mentions a recursive function. + pub fn has_rec_fun_apps(&self) -> bool { + use self::zip::*; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |term| { + if let Some((name, _)) = term.fun_inspect() { + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() + { + Err(()) + } else { + Ok(None) + } + } else { + Ok(None) + } + }, + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(name) + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() => + { + Err(()) + } + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(name), + .. + } + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() => + { + Err(()) + } + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: \ + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); + + res.is_err() + } + /// Returns true if the term mentions a function or an ADT. pub fn has_fun_app_or_adt(&self) -> bool { use self::zip::*; From 70a05b6aa68bbb02c4e0354a9f15670db557ac9f Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 18 Sep 2018 13:50:27 +0900 Subject: [PATCH 59/94] bugfixes in preprocessing --- src/instance/instance/clause.rs | 80 ++++++++++++++-------- src/instance/instance/mod.rs | 4 ++ src/instance/preproc/strict_neg_clauses.rs | 2 + src/term/simplify.rs | 5 +- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/instance/instance/clause.rs b/src/instance/instance/clause.rs index be71a737..1530db00 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/instance/clause.rs @@ -1068,30 +1068,39 @@ impl Clause { // the corresponding fresh variable. // let mut var_map = VarHMap::new(); + // Maps constants to variables. This map is stored separately because we don't want to + // substitute constant terms. + let mut cst_map = TermMap::new(); + + // New arguments for the application. + let mut nu_args = VarMap::with_capacity(args.len()); + for arg in args.iter() { // Already a variable? if arg.var_idx().is_some() { + nu_args.push(arg.clone()); continue; } - let var = map - .entry(arg.clone()) - .or_insert_with(|| term::var(clause.vars.next_index(), arg.typ())) - .clone(); - let idx = var.var_idx().expect("variable by construction"); + let idx = clause.vars.next_index(); + + let var = if arg.val().is_none() { + &mut map + } else { + &mut cst_map + }.entry(arg.clone()) + .or_insert_with(|| term::var(clause.vars.next_index(), arg.typ())) + .clone(); + + nu_args.push(var.clone()); clause .vars .push(VarInfo::new(idx.default_str(), arg.typ(), idx)); - - // for arg_var in term::vars(arg) { - // var_map - // .entry(arg_var) - // .or_insert_with(Vec::new) - // .push((arg, idx)) - // } } + let nu_args = var_to::terms::new(nu_args); + // True if we actually saw the predicate application in question. let mut legal = false; // Variables still appearing in the clause. @@ -1111,8 +1120,34 @@ impl Clause { debug_assert! { prev.is_none() } for p_args in p_argss { if p == pred && args == p_args { - legal = true + legal = true; + clause + .lhs_preds + .get_mut(&p) + .expect("was inserted right above") + .insert(nu_args.clone()); + } else { + let mut nu_p_args = VarMap::with_capacity(p_args.len()); + for arg in p_args.iter() { + let arg = arg.term_subst(&map); + // vars.extend(term::vars(&arg).into_iter()); + nu_p_args.push(arg) + } + let nu_p_args = var_to::terms::new(nu_p_args); + clause + .lhs_preds + .get_mut(&p) + .expect("was inserted right above") + .insert(nu_p_args); } + } + } + + if let Some((p, p_args)) = self.rhs.as_ref() { + if *p == pred && args == p_args { + legal = true; + clause.rhs = Some((*p, nu_args)) + } else { let mut nu_p_args = VarMap::with_capacity(p_args.len()); for arg in p_args.iter() { let arg = arg.term_subst(&map); @@ -1120,30 +1155,15 @@ impl Clause { nu_p_args.push(arg) } let nu_p_args = var_to::terms::new(nu_p_args); - clause - .lhs_preds - .get_mut(&p) - .expect("was inserted right above") - .insert(nu_p_args); - } - } - - if let Some((p, p_args)) = self.rhs.as_ref() { - let mut nu_p_args = VarMap::with_capacity(p_args.len()); - for arg in p_args.iter() { - let arg = arg.term_subst(&map); - // vars.extend(term::vars(&arg).into_iter()); - nu_p_args.push(arg) + clause.rhs = Some((*p, nu_p_args)) } - let nu_p_args = var_to::terms::new(nu_p_args); - clause.rhs = Some((*p, nu_p_args)) } // for info in &mut clause.vars { // info.active = false // } - for (term, var) in map { + for (term, var) in map.into_iter().chain(cst_map.into_iter()) { clause.insert_term(term::eq(term, var)); } diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 7167a1d1..10f779fb 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -1221,6 +1221,10 @@ impl Instance { for (pred_idx, pred) in self.preds.index_iter() { if self.pred_terms[pred_idx].is_none() { + if let Some(term) = &self.pred_str[pred_idx] { + writeln!(w, "; Strengthening term:")?; + writeln!(w, "; {}", term)? + } write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; for typ in &pred.sig { write!(w, " {}", typ)? diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index b87ea811..c32511f7 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -58,6 +58,8 @@ impl RedStrat for StrictNeg { bail!("inconsistent instance state") }; + log! { @3 "rewriting clause" } + let clause = clause .rewrite_clause_for_app(pred, args, 0.into()) .chain_err(|| "during clause rewriting")?; diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 2aa0a63b..73f628bb 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1443,7 +1443,10 @@ simpl_fun! { let cst_val = if let Some(val) = cst.val() { val } else { - panic!("illegal `cmul` application to {} {}", cst, term) + return Some( + NormRes::App(typ.clone(), Op::Mul, vec![ NormRes::Term(cst), NormRes::Term(term) ]) + ) + // panic!("illegal `cmul` application to {} {}", cst, term) } ; if let Some(val) = term.val() { From 53b6e8d5b6354a06eed19f93179c97b75feb851d Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 18 Sep 2018 17:17:10 +0900 Subject: [PATCH 60/94] documentation for preproc techniques --- src/common/wrappers.rs | 40 +- src/hoice.rs | 3 +- src/instance/instance/mod.rs | 2 + src/instance/mod.rs | 4 +- src/instance/preproc/cfg_red.rs | 37 ++ src/instance/preproc/fun_preds.rs | 410 +++++++++++++-------- src/instance/preproc/mod.rs | 3 +- src/instance/preproc/strict_neg_clauses.rs | 214 +++++++---- src/parse/mod.rs | 75 +++- 9 files changed, 530 insertions(+), 258 deletions(-) diff --git a/src/common/wrappers.rs b/src/common/wrappers.rs index 0a7fda2f..a3fce8c0 100644 --- a/src/common/wrappers.rs +++ b/src/common/wrappers.rs @@ -9,29 +9,29 @@ use common::{var_to, SmtRes, VarIndexed, VarTerms}; use term::Term; wrap_usize!{ - #[doc = "Predicate indices."] - PrdIdx - #[doc = "Range over predicates."] - range: PrdRange - #[doc = "Set of predicates."] - set: PrdSet - #[doc = "Hash map from predicates to something."] - hash map: PrdHMap - #[doc = "Total map from predicates to something."] - map: PrdMap with iter: PrdMapIter + #[doc = "Predicate indices."] + PrdIdx + #[doc = "Range over predicates."] + range: PrdRange + #[doc = "Set of predicates."] + set: PrdSet + #[doc = "Hash map from predicates to something."] + hash map: PrdHMap + #[doc = "Total map from predicates to something."] + map: PrdMap with iter: PrdMapIter } wrap_usize!{ - #[doc = "Variable indices."] - VarIdx - #[doc = "Range over variables."] - range: VarRange - #[doc = "Set of variables."] - set: VarSet - #[doc = "Hash map from variables to something."] - hash map: VarHMap - #[doc = "Total map from variables to something."] - map: VarMap with iter: VarMapIter + #[doc = "Variable indices."] + VarIdx + #[doc = "Range over variables."] + range: VarRange + #[doc = "Set of variables."] + set: VarSet + #[doc = "Hash map from variables to something."] + hash map: VarHMap + #[doc = "Total map from variables to something."] + map: VarMap with iter: VarMapIter } impl VarIdx { /// Default way to write variables: `v_`. diff --git a/src/hoice.rs b/src/hoice.rs index bd1f1d41..1ac0d7a9 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -39,12 +39,13 @@ pub mod var_to; pub mod check; pub mod data; -pub mod instance; +mod instance; pub mod learning; pub mod parse; pub mod split; pub mod teacher; pub mod unsat_core; +pub use instance::preproc; use common::*; use instance::Instance; diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs index 10f779fb..61224be5 100644 --- a/src/instance/instance/mod.rs +++ b/src/instance/instance/mod.rs @@ -121,11 +121,13 @@ pub struct Instance { /// Can only be set by `(set-option :produce-proofs true)`. proofs: bool, } + impl Default for Instance { fn default() -> Self { Self::new() } } + impl Instance { /// Instance constructor. pub fn new() -> Instance { diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 0f83f7ab..4957b8e8 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -3,9 +3,9 @@ use common::*; pub mod info; +pub mod preproc; + #[cfg_attr(feature = "cargo-clippy", allow(module_inception))] mod instance; -pub mod preproc; - pub use self::instance::{Clause, Instance, PreInstance}; diff --git a/src/instance/preproc/cfg_red.rs b/src/instance/preproc/cfg_red.rs index 5a78fdc3..b5107f1c 100644 --- a/src/instance/preproc/cfg_red.rs +++ b/src/instance/preproc/cfg_red.rs @@ -1097,6 +1097,43 @@ impl Graph { } /// Detects cycles and keeps a minimal set of predicates to infer. +/// +/// Break the acyclic parts of the "control-flow graph" of the predicates. Writing `p_i, ..., p_k +/// -> p_n` when there's a clause with applications of `p_i, ..., p_k` in its LHS and `p_n` in its +/// RHS, say we have +/// +/// - `true -> p_0` +/// - `p_0 -> p_1` +/// - `p_1, p_2 -> p_1` +/// - `p_3 -> p_2` +/// - `p_1 -> p_3` +/// - `p_1 -> false` +/// +/// Graphically the dependencies between the predicates are +/// +/// ```bash +/// _ +/// | V +/// true ---> p_0 -----> p_1 ---> p_3 +/// / ^ | +/// / | V +/// false <-| |------ p_2 +/// ``` +/// +/// The really `p_0` could be re-written in terms of `p_1`. Likewise for `p_3`. Then, since `p_2` +/// can be defined using `p_3`, `p_2` can be re-written in terms of `p_1` too. +/// +/// This technique works by removing nodes in the CFG that break cycle. In particular, +/// self-referencing predicates like `p_1` above are removed right away. In the example above, this +/// breaks all the cycle, meaning the remaining predicates can be expressed in terms of the ones +/// removed. +/// +/// # Examples +/// +/// ```rust +/// // See this file for a non-trivial example. +/// ::std::fs::OpenOptions::new().read(true).open("rsc/sat/cfg_red.smt2").unwrap(); +/// ``` pub struct CfgRed { /// Internal counter for log files. cnt: usize, diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index e0efe0f2..3ed2dd0f 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -29,7 +29,7 @@ impl FunBranch { index: usize, ) -> Res> { let fun_args_len = fun_sig.len(); - let (last, mut fresh) = if fun_args_len == rhs_args.len() { + let (last, fresh) = if fun_args_len == rhs_args.len() { (None, rhs_args.len().into()) } else { let last: VarIdx = (rhs_args.len() - 1).into(); @@ -56,43 +56,36 @@ impl FunBranch { } } - macro_rules! get_fresh { - () => {{ - let res = fresh; - fresh.inc(); - res - }}; - } - debug_assert! { fun_args_len == rhs_args.len() || fun_args_len == rhs_args.len() - 1 } debug_assert! { lhs_preds.len() <= 1 } debug_assert! { - if let Some((p, _)) = lhs_preds.iter().next() { - * p == pred - } else { - true - } + lhs_preds.iter().next().map(|(p, _)| * p == pred).unwrap_or(true) } - let (mut guard, mut subst) = if let Some(res) = args_invert(rhs_args, fun_args_len)? { + let (guard, subst) = if let Some(res) = args_invert(rhs_args, fun_args_len)? { res } else { log! { @3 "failed to invert rhs arguments {}", rhs_args } return Ok(None); }; + let mut builder = BranchBuilder { + args_len: fun_sig.len(), + fun_typ, + fun_name, + last, + fresh, + guard, + subst, + calls: VarHMap::new(), + value: None, + index, + }; + if_log! { @4 - log! { @4 |=> - "rhs inversion successful" ; - " substitution:" - } - for (var, term) in subst.iter() { - log! { @4 |=> " v_{} -> {}", var, term } - } - log! { @4 |=> " cube:" } - for term in guard.iter() { - log! { @4 " {}", term } - } + log! { @4 |=> "rhs inversion successful" } + builder.log_subst(); + builder.log_guard(); } let mut lhs_argss: Vec<_> = lhs_preds @@ -100,17 +93,163 @@ impl FunBranch { .map(|argss| argss.iter().collect()) .unwrap_or_else(Vec::new); - let mut nu_args = Vec::with_capacity(fun_args_len); - let mut value = None; + let okay = builder.work_on_lhs(&mut lhs_argss)?; + if !okay { + return Ok(None); + } + + if_log! { @4 + log! { @4 |=> "lhs inversion successful" }; + builder.log_subst(); + builder.log_guard(); + builder.log_calls(); + } + + for term in lhs_terms { + if let Some((term, _)) = term.subst_total(&builder.subst) { + builder.guard.insert(term); + } else { + log! { @3 | "total substitution failed on term {}", term } + return Ok(None); + } + } + + if_log! { @4 + log! { @4 |=> "lhs terms substitution successful" } + builder.log_guard(); + } + + let value = if let Some(last) = builder.last { + if let Some((res, _)) = rhs_args[last].subst_total(&builder.subst) { + res + } else { + log! { @3 | "failed to retrieve value for {}", rhs_args[last] } + return Ok(None); + } + } else if let Some(conj) = ::std::mem::replace(&mut builder.value, None) { + term::and(conj) + } else { + term::tru() + }; + + log! { @4 | "value extraction successful: {}", value } + + builder.potential_invariants(invars); + + if_log! { @4 + log! { @4 |=> "potential invariant extraction successful" } + builder.log_guard(); + log! { @4 |=> " invariants:" }; + for invar in invars.keys() { + log! { @4 |=> " {}", invar } + }; + } + + Ok(Some(FunBranch { + guard: builder.guard, + value, + calls: builder.calls, + })) + } + + /// Propagates the substition `calls` to the whole branch. + fn propagate_calls(&mut self) { + if !self.calls.is_empty() { + self.guard = self + .guard + .iter() + .map(|term| term.subst(&self.calls).0) + .collect(); + self.value = self.value.subst(&self.calls).0; + // self.calls.clear() + } + } +} + +/// Helper for branch construction for a function. +struct BranchBuilder<'a> { + /// Number of arguments of the function. + args_len: usize, + /// Return type of the function. + fun_typ: &'a Typ, + /// Name of the function. + fun_name: &'a str, + /// The index of the last variable if we are not working on all the arguments. None otherwise. + /// + /// If none, then we are reconstructing the predicate itself. Otherwise, we are reconstructing + /// the function that yields the last argument of the predicate when it is true. + last: Option, + /// Index of the next fresh variable. + fresh: VarIdx, + /// Substitution from clause variables to the function's variables. + subst: VarHMap, + /// Guard of the branch. + guard: TermSet, + /// Recursive calls appearing in this branch. + calls: VarHMap, + /// Value yielded by the branch. + value: Option>, + /// Index of the current branch. + index: usize, +} +impl<'a> BranchBuilder<'a> { + /// Index of the next fresh variable. + /// + /// Increases the internal counter. + fn get_fresh(&mut self) -> VarIdx { + let res = self.fresh; + self.fresh.inc(); + res + } + + /// Retrieves potential invariants. + /// + /// A term from the guard of the branch is elligible as a candidate invariant if it mentions + /// **exactly one** variable from `builder.calls`. + fn potential_invariants(&mut self, invars: &mut TermMap>) { + let mut invar_subst = VarHMap::with_capacity(1); + let guard = &mut self.guard; + let calls = &mut self.calls; + let fun_typ = &self.fun_typ; + let index = self.index; + + guard.retain(|term| { + let term_vars = term::vars(&term); + let mut vars = vec![]; + + for var in term_vars { + if calls.get(&var).is_some() { + vars.push(var) + } + } + + if vars.len() == 1 { + let var = vars[0]; + invar_subst.insert(var, term::var(0, (*fun_typ).clone())); + let (invar, _) = term + .subst_total(&invar_subst) + .expect("total substitution cannot fail du to previous checks"); + invars + .entry(invar) + .or_insert_with(Vec::new) + .push((index, term.clone())); + false + } else { + true + } + }) + } - // Stores the recursive calls. - let mut calls = VarHMap::new(); + /// Handles the lhs predicate applications. + fn work_on_lhs(&mut self, lhs_argss: &mut Vec<&VarTerms>) -> Res { + let mut nu_args = Vec::with_capacity(self.args_len); + debug_assert! { self.value.is_none() } macro_rules! register_call { ($call:expr) => {{ // Check if we already have a binding for this call. let mut var = None; - for (v, trm) in &calls { + for (v, trm) in &self.calls { if $call == *trm { // Found a variable for this call. var = Some(*v); @@ -122,8 +261,8 @@ impl FunBranch { var } else { // Create new binding. - let fresh = get_fresh!(); - let prev = calls.insert(fresh, $call); + let fresh = self.get_fresh(); + let prev = self.calls.insert(fresh, $call); debug_assert! { prev.is_none() } fresh } @@ -135,25 +274,25 @@ impl FunBranch { let mut failed: Res<_> = Ok(false); lhs_argss.retain(|args| { - for arg in &args[0..fun_args_len] { - if let Some((arg, _)) = arg.subst_total(&subst) { + for arg in &args[0..self.args_len] { + if let Some((arg, _)) = arg.subst_total(&self.subst) { nu_args.push(arg) } else { nu_args.clear(); return true; } } - let nu_args = ::std::mem::replace(&mut nu_args, Vec::with_capacity(fun_args_len)); + let nu_args = ::std::mem::replace(&mut nu_args, Vec::with_capacity(self.args_len)); - let fun_app = term::fun(fun_typ.clone(), fun_name.into(), nu_args); + let fun_app = term::fun(self.fun_typ.clone(), self.fun_name.into(), nu_args); let fun_app_var = register_call!(fun_app); - let fun_app = term::var(fun_app_var, fun_typ.clone()); + let fun_app = term::var(fun_app_var, self.fun_typ.clone()); - let okay = if let Some(last) = last.as_ref() { + let okay = if let Some(last) = self.last.as_ref() { let last = *last; - map_invert(&args[last], fun_app, &mut subst, &mut guard) + map_invert(&args[last], fun_app, &mut self.subst, &mut self.guard) } else { - value.get_or_insert_with(Vec::new).push(fun_app); + self.value.get_or_insert_with(Vec::new).push(fun_app); Ok(true) }; @@ -174,127 +313,39 @@ impl FunBranch { if failed? { log! { @3 | "failed" } - return Ok(None); + return Ok(false); } else if lhs_argss.len() == prev_len { // not making progress. log! { @3 | "not making progress on lhs preds" } - return Ok(None); + return Ok(false); } } - if_log! { @4 - log! { @4 |=> - "lhs inversion successful" ; - " substitution:" - }; - for (var, term) in &subst { - log! { @4 |=> " v_{} -> {}", var, term } - }; - log! { @4 |=> " cube:" }; - for term in &guard { - log! { @4 |=> " {}", term } - }; - log! { @4 |=> " recursive calls:" }; - for (var, term) in & calls { - log! { @4 |=> " v_{} -> {}", var, term } - } - } + Ok(true) + } +} - for term in lhs_terms { - if let Some((term, _)) = term.subst_total(&subst) { - guard.insert(term); - } else { - log! { @3 | "total substitution failed on term {}", term } - return Ok(None); - } +/// Log functions. +impl<'a> BranchBuilder<'a> { + #[allow(dead_code)] + fn log_guard(&self) { + log! { @4 |=> " guard:" } + for term in &self.guard { + log! { @4 |=> " {}", term } } - - if_log! { @4 - log! { @4 |=> - "lhs terms substitution successful"; - " cube:" - } - for term in &guard { - log! { @4 |=> " {}", term } - }; - } - - let value = if let Some(last) = last.as_ref() { - if let Some((res, _)) = rhs_args[*last].subst_total(&subst) { - res - } else { - log! { @3 | "failed to retrieve value for {}", rhs_args[*last] } - return Ok(None); - } - } else if let Some(conj) = value { - term::and(conj) - } else { - term::tru() - }; - - log! { @4 | "value extraction successful: {}", value } - - let mut invar_subst = VarHMap::with_capacity(1); - - // Term elligible as candidate invariants are the ones that mention **exactly one** - // variable from `calls`. - guard.retain(|term| { - let term_vars = term::vars(&term); - let mut vars = vec![]; - - for var in term_vars { - if calls.get(&var).is_some() { - vars.push(var) - } - } - - if vars.len() == 1 { - let var = vars[0]; - invar_subst.insert(var, term::var(0, fun_typ.clone())); - let (invar, _) = term - .subst_total(&invar_subst) - .expect("total substitution cannot fail due to previous checks"); - invars - .entry(invar) - .or_insert_with(Vec::new) - .push((index, term.clone())); - false - } else { - true - } - }); - - if_log! { @4 - log! { @4 |=> - "potential invariant extraction successful"; - " cube:" - } - for term in &guard { - log! { @4 |=> " {}", term } - }; - log! { @4 |=> " invariants:" }; - for invar in invars.keys() { - log! { @4 |=> " {}", invar } - }; + } + #[allow(dead_code)] + fn log_calls(&self) { + log! { @4 |=> " rec calls:" } + for (var, term) in &self.calls { + log! { @4 |=> " v_{} -> {}", var, term } } - - Ok(Some(FunBranch { - guard, - value, - calls, - })) } - - /// Propagates the substition `calls` to the whole branch. - fn propagate_calls(&mut self) { - if !self.calls.is_empty() { - self.guard = self - .guard - .iter() - .map(|term| term.subst(&self.calls).0) - .collect(); - self.value = self.value.subst(&self.calls).0; - // self.calls.clear() + #[allow(dead_code)] + fn log_subst(&self) { + log! { @4 |=> " subst:" } + for (var, term) in &self.subst { + log! { @4 |=> " v_{} -> {}", var, term } } } } @@ -683,6 +734,57 @@ impl FunDef { } } +/// Function reconstruction from clauses over some predicates. +/// +/// The technique will attempt to reconstruct predicate `pred` of arity `ar` if +/// +/// - when it appears in the RHS of aclause, then the only predicate applications in the LHS of +/// this clause are applications of `pred`, and +/// - it is not the only undefined predicate left, and +/// - exactly *one* of its `ar - 1` first arguments is a datatype. +/// +/// # Examples +/// +/// ``` +/// # use hoice::{ common::PrdIdx, parse, preproc::{ PreInstance, RedStrat, FunPreds } }; +/// let mut instance = parse::instance(" +/// (declare-fun len_fun_preds_example ( (List Int) Int ) Bool) +/// (declare-fun unused ( Int ) Bool) +/// (assert +/// (forall ( (n Int) ) +/// (len_fun_preds_example nil 0) +/// ) +/// ) +/// (assert +/// (forall ( (t (List Int)) (h Int) (n Int) ) +/// (=> +/// (and +/// (len_fun_preds_example t n) +/// ) +/// (len_fun_preds_example (insert h t) (+ n 1)) +/// ) +/// ) +/// ) +/// "); +/// +/// let mut fun_preds = FunPreds::new(& instance); +/// let mut instance = PreInstance::new(& mut instance).unwrap(); +/// let info = fun_preds.apply(& mut instance).unwrap(); +/// debug_assert_eq! { info.preds, 1 } +/// +/// let pred: PrdIdx = 0.into(); +/// debug_assert_eq! { "len_fun_preds_example", & instance[pred].name } +/// +/// let funs = instance.get_companion_funs(pred).unwrap(); +/// assert_eq!( "len_fun_preds_example_hoice_reserved_fun", &funs[0].name); +/// assert_eq! { +/// "(ite \ +/// (not (is-insert v_0)) \ +/// 0 \ +/// (+ 1 (len_fun_preds_example_hoice_reserved_fun (tail v_0)))\ +/// )", & format!("{}", funs[0].def) +/// } +/// ``` pub struct FunPreds { to_inline: Vec<(PrdIdx, bool)>, } @@ -699,6 +801,7 @@ impl FunPreds { let mut to_rm = vec![]; log!(@2 "working on {}", conf.emph(& instance[pred].name)); + println!("working on {}", conf.emph(&instance[pred].name)); debug_assert! { to_rm.is_empty() } @@ -756,11 +859,13 @@ impl FunPreds { to_rm.push(clause); log! { @3 | "working on clause #{}", clause } + println! { "working on clause #{}", clause } fun_def = if let Some(new) = fun_def.register_clause(&instance[clause])? { new } else { log!{ @3 | "clause registration failed" } + println!{ "clause registration failed" } abort!() }; } @@ -786,7 +891,6 @@ impl FunPreds { fun_app }; - info.preds += 1; let mut tterm_set = TTermSet::new(); tterm_set.insert_term(def); info += instance.force_dnf_left(pred, vec![(Quantfed::new(), tterm_set)])?; diff --git a/src/instance/preproc/mod.rs b/src/instance/preproc/mod.rs index 9ec40b76..48f886fb 100644 --- a/src/instance/preproc/mod.rs +++ b/src/instance/preproc/mod.rs @@ -17,10 +17,11 @@ mod one_rhs; mod strict_neg_clauses; mod unroll; -use self::{ +pub use self::{ arg_red::ArgRed, bias_unroll::BiasedUnroll, cfg_red::CfgRed, fun_preds::FunPreds, one_lhs::OneLhs, one_rhs::OneRhs, strict_neg_clauses::StrictNeg, unroll::RUnroll, }; +pub use instance::PreInstance; /// Extension for a predicate. /// diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/instance/preproc/strict_neg_clauses.rs index c32511f7..0c523808 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/instance/preproc/strict_neg_clauses.rs @@ -1,4 +1,4 @@ -//! Strict negative clause. +//! Works on strict negative clause. use common::*; use instance::{ @@ -6,32 +6,70 @@ use instance::{ preproc::{utils::ExtractRes, RedStrat}, }; -pub struct StrictNeg; - -impl RedStrat for StrictNeg { - fn name(&self) -> &'static str { - "strict_neg" - } - - fn new(_: &Instance) -> Self { - StrictNeg - } - - fn apply(&mut self, instance: &mut PreInstance) -> Res { - let mut info = RedInfo::new(); +/// Works on strict negative clause. +/// +/// Extracts strengthening terms from strict negative clauses. +/// +/// # Examples +/// +/// ``` +/// # use hoice::{ common::PrdIdx, parse, preproc::{ PreInstance, RedStrat, StrictNeg } }; +/// let mut instance = parse::instance(" +/// (declare-fun pred ( Int Int Int Int ) Bool) +/// (assert +/// (forall ( (x2 Int) (x3 Int) ) +/// (=> +/// (and +/// (>= x2 1) (>= (+ x2 (* (- 1) x3)) 1) +/// (pred x2 0 (- 1) x3) +/// ) +/// false +/// ) +/// ) +/// ) +/// "); +/// +/// let mut strict_neg = StrictNeg::new(& instance); +/// let mut instance = PreInstance::new(& mut instance).unwrap(); +/// let info = strict_neg.apply(& mut instance).unwrap(); +/// debug_assert! { ! info.non_zero() } +/// +/// let pred: PrdIdx = 0.into(); +/// debug_assert_eq! { "pred", & instance[pred].name } +/// +/// let strengthening = instance.get_str(pred).unwrap(); +/// debug_assert_eq! { +/// "(or \ +/// (>= (* (- 1) v_0) 0) \ +/// (>= (+ v_3 (* (- 1) v_0)) 0) \ +/// (not (= (+ (- 1) (* (- 1) v_2)) 0)) \ +/// (not (= v_1 0))\ +/// )", & format!("{}", strengthening) +/// } +/// ``` +pub struct StrictNeg { + /// Stores partial definitions for the predicates. + partial_defs: PrdHMap>>, + /// Clauses to remove. + clauses_to_rm: Vec, +} - let mut partial_defs = PrdHMap::new(); - let mut clauses_to_rm = Vec::new(); +impl StrictNeg { + /// Builds the partial definitions by going through strict negative clauses. + fn build_partial_defs(&mut self, instance: &mut PreInstance) -> Res<()> { + debug_assert! { self.partial_defs.is_empty() } + debug_assert! { self.clauses_to_rm.is_empty() } macro_rules! pdef { ($pred:expr => set false) => {{ - partial_defs.remove(&$pred); - partial_defs.insert($pred, None); + self.partial_defs.remove(&$pred); + self.partial_defs.insert($pred, None); () }}; ($pred:expr => add $term:expr) => {{ - if let Some(vec) = partial_defs + if let Some(vec) = self + .partial_defs .entry($pred) .or_insert_with(|| Some(Vec::new())) .as_mut() @@ -41,81 +79,99 @@ impl RedStrat for StrictNeg { }}; } - { - let (extractor, instance, strict_clauses) = instance.strict_neg_clauses(); + let (extractor, instance, strict_clauses) = instance.strict_neg_clauses(); - for (idx, clause) in strict_clauses { - log! { @3 "working on clause #{}", idx } - log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } + for (idx, clause) in strict_clauses { + log! { @3 "working on clause #{}", idx } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } - let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { - if let Some(args) = argss.iter().next() { - (*pred, args) - } else { - bail!("inconsistent instance state") - } + let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (*pred, args) } else { bail!("inconsistent instance state") - }; + } + } else { + bail!("inconsistent instance state") + }; - log! { @3 "rewriting clause" } + log! { @3 "rewriting clause" } - let clause = clause - .rewrite_clause_for_app(pred, args, 0.into()) - .chain_err(|| "during clause rewriting")?; - log! { @3 "rewriting successful" } - log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } + let clause = clause + .rewrite_clause_for_app(pred, args, 0.into()) + .chain_err(|| "during clause rewriting")?; + log! { @3 "rewriting successful" } + log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } - let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { - if let Some(args) = argss.iter().next() { - (*pred, args) - } else { - bail!("inconsistent instance state") - } + let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { + if let Some(args) = argss.iter().next() { + (*pred, args) } else { bail!("inconsistent instance state") - }; - - match extractor.terms_of_lhs_app( - false, - instance, - clause.vars(), - (clause.lhs_terms(), clause.lhs_preds()), - clause.rhs(), - (pred, args), - )? { - ExtractRes::Trivial | ExtractRes::SuccessTrue => clauses_to_rm.push(idx), - - ExtractRes::SuccessFalse => pdef!(pred => set false), - - ExtractRes::Success((qvars, pred_app, tterms)) => if qvars.is_empty() { - if pred_app.is_some() || !tterms.preds().is_empty() { - bail!("inconsistent instance state") - } - - let terms = tterms.terms(); - - if terms.is_empty() { - pdef!(pred => set false) - } else { - let term = term::or( - terms.iter().map(|term| term::not(term.clone())).collect(), - ); - pdef!(pred => add term) - } - }, - - ExtractRes::Failed => (), } + } else { + bail!("inconsistent instance state") + }; + + match extractor.terms_of_lhs_app( + false, + instance, + clause.vars(), + (clause.lhs_terms(), clause.lhs_preds()), + clause.rhs(), + (pred, args), + )? { + ExtractRes::Trivial | ExtractRes::SuccessTrue => self.clauses_to_rm.push(idx), + + ExtractRes::SuccessFalse => pdef!(pred => set false), + + ExtractRes::Success((qvars, pred_app, tterms)) => if qvars.is_empty() { + if pred_app.is_some() || !tterms.preds().is_empty() { + bail!("inconsistent instance state") + } + + let terms = tterms.terms(); + + if terms.is_empty() { + pdef!(pred => set false) + } else { + let term = + term::or(terms.iter().map(|term| term::not(term.clone())).collect()); + pdef!(pred => add term) + } + }, + + ExtractRes::Failed => (), } } - if !clauses_to_rm.is_empty() { - info.clauses_rmed += clauses_to_rm.len(); - instance.forget_clauses(&mut clauses_to_rm)? + Ok(()) + } +} + +impl RedStrat for StrictNeg { + fn name(&self) -> &'static str { + "strict_neg" + } + + fn new(_: &Instance) -> Self { + StrictNeg { + partial_defs: PrdHMap::new(), + clauses_to_rm: Vec::new(), + } + } + + fn apply(&mut self, instance: &mut PreInstance) -> Res { + let mut info = RedInfo::new(); + + self.build_partial_defs(instance)?; + + if !self.clauses_to_rm.is_empty() { + info.clauses_rmed += self.clauses_to_rm.len(); + instance.forget_clauses(&mut self.clauses_to_rm)? } - for (pred, terms_opt) in partial_defs { + for (pred, terms_opt) in self.partial_defs.drain() { if let Some(mut terms) = terms_opt { log! { @3 "success ({} term(s))", terms.len() } if_log! { @4 diff --git a/src/parse/mod.rs b/src/parse/mod.rs index b4dde8d9..6ed0ea7a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -394,7 +394,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { line_count += 1; if char_pos < line.len() { pref = line[0..char_pos].to_string(); - token = line[char_pos..(char_pos + 1)].to_string(); + token = line[char_pos..=char_pos].to_string(); suff = line[(char_pos + 1)..line.len()].to_string(); break; } else if char_pos == line.len() { @@ -1506,7 +1506,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Parses some arguments `( ( ) ... )`. - fn args( + pub fn args( &mut self, var_map: &mut VarInfos, hash_map: &mut BTreeMap<&'s str, VarIdx>, @@ -3366,3 +3366,74 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok(res) } } + +/// If input expression is an error, prints it and panics. +macro_rules! print_err { + ($e:expr, $blah:expr) => { + match $e { + Ok(res) => res, + Err(e) => { + print_err(&e); + panic!("error {}", $blah) + } + } + }; +} + +/// Parses some variable information from an SMT 2 string. +/// +/// The info needs to be in SMT 2 format. For instance: `( (n Int) (r Real) )`. Used for +/// testing / documentation. +pub fn var_infos(s: &str) -> VarInfos { + let mut var_infos = VarInfos::new(); + let mut cxt = ParserCxt::new(); + let mut map = BTreeMap::new(); + + print_err!( + cxt.parser(s, 0, &Profiler::new()) + .args(&mut var_infos, &mut map), + "while parsing variable information" + ); + + var_infos +} + +/// Parses a term from an SMT 2 string. +/// +/// Used for testing / documentation. +pub fn term(s: &str, var_infos: &VarInfos, instance: &Instance) -> Term { + let mut map = BTreeMap::new(); + for info in var_infos { + map.insert(&info.name as &str, info.idx); + } + + let mut cxt = ParserCxt::new(); + + let term_opt = print_err!( + cxt.parser(s, 0, &Profiler::new()) + .term_opt(var_infos, &map, instance), + "while parsing term" + ); + + if let Some(term) = term_opt { + term + } else { + panic!("failed to parse term from `{}`", s) + } +} + +/// Parses an instance from an SMT 2 string. +/// +/// Stops at the end of the string or at the first non-declaration non-assert non-definition +/// item. Used for testing / documentation purposes. +pub fn instance(s: &str) -> Instance { + let mut instance = Instance::new(); + let mut cxt = ParserCxt::new(); + + print_err!( + cxt.parser(s, 0, &Profiler::new()).parse(&mut instance), + "while parsing instance" + ); + + instance +} From 9bd06b81c0320759d9d31c09b069949f1717100e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 18 Sep 2018 18:16:19 +0900 Subject: [PATCH 61/94] more documentation and tests for preproc techniques --- src/instance/preproc/arg_red.rs | 7 +++++ src/instance/preproc/fun_preds.rs | 3 ++- src/instance/preproc/one_lhs.rs | 44 +++++++++++++++++++++++++++++++ src/instance/preproc/one_rhs.rs | 42 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/instance/preproc/arg_red.rs b/src/instance/preproc/arg_red.rs index e6fe393f..5d9d7b8b 100644 --- a/src/instance/preproc/arg_red.rs +++ b/src/instance/preproc/arg_red.rs @@ -10,6 +10,13 @@ use instance::{instance::PreInstance, preproc::RedStrat, Clause}; /// /// [paper]: https://link.springer.com/chapter/10.1007%2F3-540-62718-9_6 /// (Redundant argument filtering of logic programs) +/// +/// # Examples +/// +/// ```rust +/// // See this file for a non-trivial example. +/// ::std::fs::OpenOptions::new().read(true).open("rsc/sat/arg_red.smt2").unwrap(); +/// ``` pub struct ArgRed { inner: ArgReductor, } diff --git a/src/instance/preproc/fun_preds.rs b/src/instance/preproc/fun_preds.rs index 3ed2dd0f..4564d41f 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/instance/preproc/fun_preds.rs @@ -770,7 +770,7 @@ impl FunDef { /// let mut fun_preds = FunPreds::new(& instance); /// let mut instance = PreInstance::new(& mut instance).unwrap(); /// let info = fun_preds.apply(& mut instance).unwrap(); -/// debug_assert_eq! { info.preds, 1 } +/// debug_assert_eq! { info.preds, 2 } // 2 because `unused` is simplified by propagation /// /// let pred: PrdIdx = 0.into(); /// debug_assert_eq! { "len_fun_preds_example", & instance[pred].name } @@ -891,6 +891,7 @@ impl FunPreds { fun_app }; + info.preds += 1; let mut tterm_set = TTermSet::new(); tterm_set.insert_term(def); info += instance.force_dnf_left(pred, vec![(Quantfed::new(), tterm_set)])?; diff --git a/src/instance/preproc/one_lhs.rs b/src/instance/preproc/one_lhs.rs index adb363c9..f1bdc5bb 100644 --- a/src/instance/preproc/one_lhs.rs +++ b/src/instance/preproc/one_lhs.rs @@ -27,6 +27,50 @@ use instance::{ /// If `lhs` or `(not rhs)` is unsat, then the clause is dropped and `p` is /// reduced to `true` since it does not appear as an antecedent anywhere /// anymore. +/// +/// # Examples +/// +/// ``` +/// # use hoice::{ common::{ PrdIdx, PrdHMap }, parse, preproc::{ PreInstance, RedStrat, OneLhs } }; +/// let mut instance = parse::instance(" +/// (declare-fun p_1 ( Int ) Bool) +/// (assert +/// (forall ( (n Int) ) +/// (=> +/// (p_1 n) +/// (> n 0) +/// ) +/// ) +/// ) +/// "); +/// +/// let mut one_lhs = OneLhs::new(& instance); +/// let mut instance = PreInstance::new(& mut instance).unwrap(); +/// let info = one_lhs.apply(& mut instance).unwrap(); +/// instance.finalize().unwrap(); +/// assert_eq! { info.preds, 1 } +/// +/// let pred: PrdIdx = 0.into(); +/// assert_eq! { "p_1", & instance[pred].name } +/// +/// let model = PrdHMap::new(); +/// let model = instance.extend_model(model).unwrap(); +/// let mut s: Vec = vec![]; +/// instance.write_model(& model, & mut s).unwrap(); +/// +/// assert_eq! { +/// "\ +/// (model +/// (define-fun p_1 +/// ( (v_0 Int) ) Bool +/// (>= v_0 1) +/// ) +/// ) +/// \ +/// ", +/// &String::from_utf8_lossy(&s) +/// } +/// ``` pub struct OneLhs; impl OneLhs { diff --git a/src/instance/preproc/one_rhs.rs b/src/instance/preproc/one_rhs.rs index 5fb13c44..b37c7be5 100644 --- a/src/instance/preproc/one_rhs.rs +++ b/src/instance/preproc/one_rhs.rs @@ -17,6 +17,48 @@ use instance::{ /// | `(v > 0) => (p 7 v')` | `(v_0 = 7)` | /// | `(v > 0) => (p v v )` | `(v_0 = v_1) and (v_0 > 0)` | /// | `(v > 0) and (v <= 0) => (p 7 v')` | `false` (by check-sat) | +/// +/// ``` +/// # use hoice::{ common::{ PrdIdx, PrdHMap }, parse, preproc::{ PreInstance, RedStrat, OneRhs } }; +/// let mut instance = parse::instance(" +/// (declare-fun p_1 ( Int ) Bool) +/// (assert +/// (forall ( (n Int) ) +/// (=> +/// (> n 0) +/// (p_1 n) +/// ) +/// ) +/// ) +/// "); +/// +/// let mut one_rhs = OneRhs::new(& instance); +/// let mut instance = PreInstance::new(& mut instance).unwrap(); +/// let info = one_rhs.apply(& mut instance).unwrap(); +/// instance.finalize().unwrap(); +/// assert_eq! { info.preds, 1 } +/// +/// let pred: PrdIdx = 0.into(); +/// assert_eq! { "p_1", & instance[pred].name } +/// +/// let model = PrdHMap::new(); +/// let model = instance.extend_model(model).unwrap(); +/// let mut s: Vec = vec![]; +/// instance.write_model(& model, & mut s).unwrap(); +/// +/// assert_eq! { +/// "\ +/// (model +/// (define-fun p_1 +/// ( (v_0 Int) ) Bool +/// (>= v_0 1) +/// ) +/// ) +/// \ +/// ", +/// &String::from_utf8_lossy(&s) +/// } +/// ``` pub struct OneRhs; impl OneRhs { From a0df5d094cad1ba7624c86b60ab0863782663dfb Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 19 Sep 2018 16:48:54 +0900 Subject: [PATCH 62/94] huge architecture simplification + a lot of doc + refactoring + tests --- src/common/mod.rs | 12 +- src/common/smt.rs | 10 +- src/data/constraint.rs | 4 +- src/data/sample.rs | 4 +- src/hoice.rs | 19 +- src/info.rs | 170 ++ src/instance/{instance => }/clause.rs | 6 +- src/instance/info.rs | 85 - src/instance/instance/mod.rs | 1828 ----------------- src/instance/mod.rs | 1808 +++++++++++++++- src/instance/{instance => }/pre_instance.rs | 158 +- src/parse/mod.rs | 2 +- src/{instance => }/preproc/arg_red.rs | 2 +- src/{instance => }/preproc/bias_unroll.rs | 171 +- src/{instance => }/preproc/cfg_red.rs | 177 +- src/{instance => }/preproc/fun_preds.rs | 464 +++-- src/{instance => }/preproc/mod.rs | 52 +- src/{instance => }/preproc/one_lhs.rs | 13 +- src/{instance => }/preproc/one_rhs.rs | 19 +- .../preproc/strict_neg_clauses.rs | 9 +- src/{instance => }/preproc/unroll.rs | 53 +- src/{instance => }/preproc/utils.rs | 4 +- src/split.rs | 2 +- src/teacher/mod.rs | 57 +- src/term/mod.rs | 70 +- src/term/tterms.rs | 18 +- src/var_to/vals.rs | 2 +- 27 files changed, 2701 insertions(+), 2518 deletions(-) create mode 100644 src/info.rs rename src/instance/{instance => }/clause.rs (99%) delete mode 100644 src/instance/info.rs delete mode 100644 src/instance/instance/mod.rs rename src/instance/{instance => }/pre_instance.rs (94%) rename src/{instance => }/preproc/arg_red.rs (99%) rename src/{instance => }/preproc/bias_unroll.rs (88%) rename src/{instance => }/preproc/cfg_red.rs (89%) rename src/{instance => }/preproc/fun_preds.rs (80%) rename src/{instance => }/preproc/mod.rs (91%) rename src/{instance => }/preproc/one_lhs.rs (96%) rename src/{instance => }/preproc/one_rhs.rs (93%) rename src/{instance => }/preproc/strict_neg_clauses.rs (97%) rename src/{instance => }/preproc/unroll.rs (87%) rename src/{instance => }/preproc/utils.rs (99%) diff --git a/src/common/mod.rs b/src/common/mod.rs index 555ad50f..f1c2d6e7 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -40,7 +40,7 @@ pub use var_to; pub use var_to::{vals::SubsumeExt, VarTerms, VarVals}; pub use common::consts::keywords; -pub use instance::Instance; +pub use instance::{Clause, Instance}; mod wrappers; @@ -238,10 +238,10 @@ impl PredAppsExt for PredApps { } } -/// Predicate informations. -pub type PrdInfos = PrdMap<::instance::info::PrdInfo>; -/// Variable informations. -pub type VarInfos = VarMap<::instance::info::VarInfo>; +/// Predicate information. +pub type Preds = PrdMap<::info::Pred>; +/// Variable information. +pub type VarInfos = VarMap<::info::VarInfo>; /// Maps predicates to optional terms. pub type Candidates = PrdMap>; @@ -355,7 +355,7 @@ pub trait Signature { /// Length of the signature. fn len(&self) -> usize; } -impl Signature for VarMap<::instance::info::VarInfo> { +impl Signature for VarMap<::info::VarInfo> { fn len(&self) -> usize { VarMap::len(self) } diff --git a/src/common/smt.rs b/src/common/smt.rs index 4c07ae72..290509ff 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -159,11 +159,11 @@ impl<'a> SmtPredApp<'a> { SmtPredApp { pred, args } } } -impl<'a, 'b> Expr2Smt<(&'b PrdInfos, bool)> for SmtPredApp<'a> { +impl<'a, 'b> Expr2Smt<(&'b Preds, bool)> for SmtPredApp<'a> { fn expr_to_smt2( &self, w: &mut Writer, - (infos, pos): (&'b PrdInfos, bool), + (infos, pos): (&'b Preds, bool), ) -> SmtRes<()> { if !pos { write!(w, "(not ")? @@ -196,11 +196,11 @@ impl<'a> NegQClause<'a> { NegQClause { clause } } } -impl<'a> Expr2Smt<(&'a PrdSet, &'a PrdSet, &'a PrdInfos)> for NegQClause<'a> { +impl<'a> Expr2Smt<(&'a PrdSet, &'a PrdSet, &'a Preds)> for NegQClause<'a> { fn expr_to_smt2( &self, w: &mut Writer, - (true_preds, false_preds, others): (&'a PrdSet, &'a PrdSet, &'a PrdInfos), + (true_preds, false_preds, others): (&'a PrdSet, &'a PrdSet, &'a Preds), ) -> SmtRes<()> { writeln!(w, "(not")?; self.clause.forall_write( @@ -758,7 +758,7 @@ impl FullParser { }, FPVar::Sym(name) => { - use instance::info::VarInfo; + use info::VarInfo; let (mut nu_sig, mut var_infos): ( VarMap, diff --git a/src/data/constraint.rs b/src/data/constraint.rs index 07f9c6cf..e3864648 100644 --- a/src/data/constraint.rs +++ b/src/data/constraint.rs @@ -361,11 +361,11 @@ impl Constraint { } impl<'a> PebcakFmt<'a> for Constraint { - type Info = &'a PrdInfos; + type Info = &'a Preds; fn pebcak_err(&self) -> ErrorKind { "during constraint pebcak formatting".into() } - fn pebcak_io_fmt(&self, w: &mut W, map: &'a PrdInfos) -> IoRes<()> { + fn pebcak_io_fmt(&self, w: &mut W, map: &'a Preds) -> IoRes<()> { if let Some(ref lhs) = self.lhs { if lhs.is_empty() { write!(w, "true ")? diff --git a/src/data/sample.rs b/src/data/sample.rs index 690aa549..39d1995e 100644 --- a/src/data/sample.rs +++ b/src/data/sample.rs @@ -37,11 +37,11 @@ impl Sample { } } impl<'a> PebcakFmt<'a> for Sample { - type Info = &'a PrdInfos; + type Info = &'a Preds; fn pebcak_err(&self) -> ErrorKind { "during sample pebcak formatting".into() } - fn pebcak_io_fmt(&self, w: &mut W, map: &'a PrdInfos) -> IoRes<()> { + fn pebcak_io_fmt(&self, w: &mut W, map: &'a Preds) -> IoRes<()> { write!(w, "({}", map[self.pred].name)?; for arg in self.args.iter() { write!(w, " {}", arg)? diff --git a/src/hoice.rs b/src/hoice.rs index 1ac0d7a9..2067b8ce 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -27,25 +27,24 @@ extern crate num; extern crate rand; extern crate rsmt2; -pub mod errors; #[macro_use] pub mod common; - -pub mod dtyp; -pub mod fun; -pub mod term; -pub mod val; -pub mod var_to; - pub mod check; pub mod data; +pub mod dtyp; +pub mod errors; +pub mod fun; +pub mod info; mod instance; pub mod learning; pub mod parse; +pub mod preproc; pub mod split; pub mod teacher; +pub mod term; pub mod unsat_core; -pub use instance::preproc; +pub mod val; +pub mod var_to; use common::*; use instance::Instance; @@ -169,7 +168,7 @@ pub fn read_and_work( let preproc_profiler = Profiler::new(); match profile! { |profiler| wrap { - instance::preproc::work(& mut instance, & preproc_profiler) + preproc::work(& mut instance, & preproc_profiler) } "top preproc" } { Ok(()) => (), diff --git a/src/info.rs b/src/info.rs new file mode 100644 index 00000000..33f103e3 --- /dev/null +++ b/src/info.rs @@ -0,0 +1,170 @@ +//! Types to store information about predicates and clause/function variables. + +use rsmt2::print::{Sort2Smt, Sym2Smt}; + +use common::*; + +/// Variable info for clauses or function definitions. +#[derive(Clone, Debug)] +pub struct VarInfo { + /// Variable's name. + pub name: String, + /// Variable's type. + pub typ: Typ, + /// Variable's index. + pub idx: VarIdx, + /// Is the variable active? + pub active: bool, +} +impl VarInfo { + /// Constructor. + pub fn new(name: String, typ: Typ, idx: VarIdx) -> Self { + VarInfo { + name, + typ, + idx, + active: true, + } + } + /// Name of the variable as bytes. + pub fn as_bytes(&self) -> &[u8] { + self.name.as_bytes() + } +} +impl Sym2Smt<()> for VarInfo { + fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> + where + Writer: Write, + { + write!(w, "v{}", self.idx)?; + Ok(()) + } +} +impl Sort2Smt for VarInfo { + fn sort_to_smt2(&self, w: &mut Writer) -> SmtRes<()> + where + Writer: Write, + { + self.typ.get().sort_to_smt2(w) + } +} +impl_fmt!{ + VarInfo(self, fmt) { + fmt.write_str(& self.name) + } +} + +/// Stores information about a predicate. +#[derive(Debug, Clone)] +pub struct Pred { + /// Name of the predicate. Should never be changed. + pub name: String, + /// Index of the predicate. Should never be changed. + pub idx: PrdIdx, + /// Current signature of the predicate. + pub sig: VarMap, + /// Original signature of a predicate, as it was declared. This is important when preprocessing + /// discovers that some arguments are irrelevant and removes them. This goes hand in hand with + /// the following `original_sig_map`. + original_sig: Sig, + /// Map from variables of the **current** signature to the original one. Used when + /// reconstructing a model. + /// + /// We should always have `self.original_sig.len() == self.original_sig_map.len()`. + original_sig_map: VarMap, + /// Definition, if any. Set by preprocessing. + pub tterm: Option, + /// Strengthener, if any. Currently, this comes from strict negative clauses. It means the + /// predicate has to be false when this term is false. So, given a candidate `cand` for this + /// predicate, the candidate should be strengthened to `cand /\ strength`. + pub strength: Option, + /// Companion functions. Function that were created specifically for this predicate, and must + /// be given to the user before giving the definition for this predicate. + pub funs: Vec, +} + +impl Pred { + /// Constructor. + pub fn new(name: String, idx: PrdIdx, sig: VarMap) -> Self { + let original_sig = sig.clone(); + let original_sig_map: VarMap<_> = VarRange::zero_to(sig.len()).collect::>().into(); + Pred { + name, + idx, + sig, + original_sig, + original_sig_map, + tterm: None, + strength: None, + funs: vec![], + } + } + + /// The original signature of the predicate, as it was declared. + pub fn original_sig(&self) -> &Sig { + &self.original_sig + } + /// Map from variables of the **current** signature to the original one. + pub fn original_sig_map(&self) -> &VarMap { + &self.original_sig_map + } + + /// A variable that does not appear in the **original** signature of the predicate. + pub fn fresh_var_idx(&self) -> VarIdx { + self.original_sig.next_index() + } + + /// Registers a new signature for the predicate. + /// + /// The `map` maps variables of the **new** signature to the original one from + /// `self.original_sig()`. + /// + /// In `debug`, checks that `map` is type-safe: forall `i`, `new_sig[i] == + /// self.original_sig[map[i]]`. Also checks that `new_sig` and `map` have the same length. + pub fn set_sig(&mut self, new_sig: Sig, map: VarMap) { + self.sig = new_sig; + self.original_sig_map = map; + self.check().unwrap_or_else(|e| { + print_err(&e); + panic!( + "illegal signature / map pair update for predicate {}", + self.name, + ) + }) + } + + /// Checks its invariant hold. Inactive in release. + #[cfg(debug_assertions)] + pub fn check(&self) -> Res<()> { + if self.sig.len() != self.original_sig_map.len() { + bail!( + "signature and map to original signature differ in length for {}", + self + ) + } + if !self + .original_sig_map + .index_iter() + .all(|(src, tgt)| self.sig[src] == self.original_sig[*tgt]) + { + bail!( + "signature and map to original signature do not type check for {}", + self + ) + } + Ok(()) + } + /// Checks its invariant hold. Inactive in release. + #[cfg(not(debug_assertions))] + #[inline] + pub fn check(&self) -> Res<()> { + Ok(()) + } +} + +use std::fmt; +impl fmt::Display for Pred { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.name) + } +} diff --git a/src/instance/instance/clause.rs b/src/instance/clause.rs similarity index 99% rename from src/instance/instance/clause.rs rename to src/instance/clause.rs index 1530db00..322e448e 100644 --- a/src/instance/instance/clause.rs +++ b/src/instance/clause.rs @@ -1,7 +1,7 @@ //! Contains the clause structure for encapsulation. use common::*; -use instance::info::VarInfo; +use info::VarInfo; use var_to::terms::VarTermsSet; /// Creates a clause. @@ -1202,7 +1202,7 @@ impl ::std::ops::Index for Clause { } } -impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clause { +impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a Preds)> for Clause { /// Writes the clause in SMT-LIB format. /// /// The boolean flag in the info specifies whether the clause should be @@ -1210,7 +1210,7 @@ impl<'a, 'b> Expr2Smt<&'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos)> for Clau fn expr_to_smt2( &self, writer: &mut Writer, - info: &'b (bool, &'a PrdSet, &'a PrdSet, &'a PrdInfos), + info: &'b (bool, &'a PrdSet, &'a PrdSet, &'a Preds), ) -> SmtRes<()> { let (pos, ref true_preds, ref false_preds, ref prd_info) = *info; diff --git a/src/instance/info.rs b/src/instance/info.rs deleted file mode 100644 index 007c7854..00000000 --- a/src/instance/info.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Types to store information about variables and predicates. - -use rsmt2::print::{Sort2Smt, Sym2Smt}; - -use common::*; - -use super::Typ; - -/// Variable info. -#[derive(Clone, Debug)] -pub struct VarInfo { - /// Variable's name. - pub name: String, - /// Variable's type. - pub typ: Typ, - /// Variable's index. - pub idx: VarIdx, - /// Is the variable active? - pub active: bool, -} -impl VarInfo { - /// Constructor. - pub fn new(name: String, typ: Typ, idx: VarIdx) -> Self { - VarInfo { - name, - typ, - idx, - active: true, - } - } - /// Name of the variable as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.name.as_bytes() - } -} -impl Sym2Smt<()> for VarInfo { - fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> - where - Writer: Write, - { - write!(w, "v{}", self.idx)?; - Ok(()) - } -} -impl Sort2Smt for VarInfo { - fn sort_to_smt2(&self, w: &mut Writer) -> SmtRes<()> - where - Writer: Write, - { - self.typ.get().sort_to_smt2(w) - } -} -impl_fmt!{ - VarInfo(self, fmt) { - fmt.write_str(& self.name) - } -} - -/// Predicate info. -#[derive(Clone)] -pub struct PrdInfo { - /// Predicate's name. - pub name: String, - /// Predicate's index. - pub idx: PrdIdx, - /// Signature. - pub sig: VarMap, -} -impl PrdInfo { - /// Name of the variable as bytes. - pub fn as_bytes(&self) -> &[u8] { - self.name.as_bytes() - } -} -impl_fmt!{ - PrdInfo(self, fmt) { - fmt.write_str(& self.name) - } -} -impl Sym2Smt<()> for PrdInfo { - fn sym_to_smt2(&self, w: &mut Writer, _: ()) -> SmtRes<()> { - write!(w, "p_{}", self.idx)?; - Ok(()) - } -} diff --git a/src/instance/instance/mod.rs b/src/instance/instance/mod.rs deleted file mode 100644 index 61224be5..00000000 --- a/src/instance/instance/mod.rs +++ /dev/null @@ -1,1828 +0,0 @@ -//! Actual instance structure. - -use common::*; -use data::Data; -use instance::info::*; -use var_to::terms::VarTermsSet; - -mod clause; -mod pre_instance; - -pub use self::clause::Clause; -pub use self::pre_instance::PreInstance; - -/// Stores the instance: the clauses, the factory and so on. -/// -/// # NB -/// -/// Clause indices can vary during instance building, because of the -/// simplifications that can remove clauses. -/// -/// So, `pred_to_clauses` has to be carefully maintained, the easiest way to -/// do this is to never access an instance's fields directly from the outside. -#[derive(Clone)] -pub struct Instance { - /// Predicates. - preds: PrdInfos, - /// Original predicates, for reconstruction. - /// - /// Stores the original signature of the predicates, and a map from the - /// variables of `preds` to the original signature. - old_preds: PrdMap<(Sig, VarMap)>, - /// Maps new variables to the new ones. - /// - /// Only available after finalize. - old_var_maps: PrdMap>, - /// Predicates for which a suitable term has been found. - pred_terms: PrdMap>, - - /// Predicates defined in `pred_terms`, sorted by predicate dependencies. - /// - /// Populated by the `finalize` function. - sorted_pred_terms: Vec, - - /// Strengthener for predicates. - pred_str: PrdMap>, - - /// Companion function for predicates. - /// - /// A companion function is a function that was created internally - /// specifically for a predicate. Meaning it needs to be given to the user - /// when printing the model. - companion_funs: PrdHMap>, - - /// Side-clauses. - /// - /// A side clause - /// - does not mention any predicate - /// - mentions a user-defined function - /// - /// It will be asserted when the instance is asked to initialize a solver. A - /// side-clause is viewed as additional information provided by the user, a - /// kind of lemma for the actual clauses. - side_clauses: Vec, - - /// Clauses. - clauses: ClsMap, - /// Maps predicates to the clauses where they appear in the lhs and rhs - /// respectively. - pred_to_clauses: PrdMap<(ClsSet, ClsSet)>, - /// Unsat flag. - is_unsat: bool, - /// Set of positive clauses. - /// - /// Only available after finalize. - pos_clauses: ClsSet, - /// Set of strictly negative clauses. - /// - /// A clause is strictly negative if it has strictly one predicate - /// application, and it's in the clause's body. Only available after - /// finalize. - strict_neg_clauses: ClsSet, - /// Set of non-strictly negative clauses. - /// - /// A clause is strictly negative if it has strictly one predicate - /// application, and it's in the clause's body. Only available after - /// finalize. - non_strict_neg_clauses: ClsSet, - /// Set of (non-strictly) negative clauses. - /// - /// Super set of strictly negative clauses. Only available after finalize. - neg_clauses: ClsSet, - /// Set of implication clauses. - /// - /// Only available after finalize. - imp_clauses: ClsSet, - /// True if finalized already ran. - is_finalized: bool, - /// If this instance is the result of a split, contains the index of the - /// clause of the original instance that the split was on. - /// - /// The constructor sets this to `None`. Function `clone_with_clauses` - /// automatically sets it to the clause kept. - split: Option, - - /// Define-funs parsed. - define_funs: HashMap, - - /// Maps **original** clause indexes to their optional name. - old_names: ClsHMap, - - /// Print success. - /// - /// Can only be set by `(set-option :print-success true)`. - print_success: bool, - /// Unsat core production. - /// - /// Can only be set by `(set-option :produce-unsat-cores true)`. - unsat_cores: bool, - /// Unsat core production. - /// - /// Can only be set by `(set-option :produce-proofs true)`. - proofs: bool, -} - -impl Default for Instance { - fn default() -> Self { - Self::new() - } -} - -impl Instance { - /// Instance constructor. - pub fn new() -> Instance { - let pred_capa = conf.instance.pred_capa; - let clause_capa = conf.instance.clause_capa; - Instance { - preds: PrdMap::with_capacity(pred_capa), - old_preds: PrdMap::with_capacity(pred_capa), - old_var_maps: PrdMap::with_capacity(pred_capa), - pred_terms: PrdMap::with_capacity(pred_capa), - sorted_pred_terms: Vec::with_capacity(pred_capa), - pred_str: PrdMap::with_capacity(pred_capa), - companion_funs: PrdHMap::new(), - - side_clauses: Vec::with_capacity(7), - clauses: ClsMap::with_capacity(clause_capa), - // clusters: CtrMap::with_capacity( clause_capa / 3 ), - pred_to_clauses: PrdMap::with_capacity(pred_capa), - is_unsat: false, - pos_clauses: ClsSet::new(), - strict_neg_clauses: ClsSet::new(), - non_strict_neg_clauses: ClsSet::new(), - neg_clauses: ClsSet::new(), - imp_clauses: ClsSet::new(), - is_finalized: false, - split: None, - define_funs: HashMap::new(), - old_names: ClsHMap::with_capacity(clause_capa), - print_success: false, - unsat_cores: false, - proofs: false, - } - } - - /// Clones itself. - /// - /// This is only used when splitting. `clause` will be remembered as the - /// clause the split is on. - /// - /// Fails (in debug) if `clause` is not a negative clause of `self` or if - /// `self` is not finalized. - pub fn clone_with_clauses(&self, clause: ClsIdx) -> Self { - debug_assert! { self.neg_clauses.contains(& clause) } - debug_assert! { self.is_finalized } - - Instance { - preds: self.preds.clone(), - old_preds: self.old_preds.clone(), - old_var_maps: PrdMap::with_capacity(self.old_preds.len()), - pred_terms: self.pred_terms.clone(), - sorted_pred_terms: Vec::with_capacity(self.preds.len()), - pred_str: vec![None; self.old_preds.len()].into(), - companion_funs: self.companion_funs.clone(), - - side_clauses: self.side_clauses.clone(), - clauses: self.clauses.clone(), - pred_to_clauses: self.pred_to_clauses.clone(), - is_unsat: false, - pos_clauses: ClsSet::new(), - strict_neg_clauses: ClsSet::new(), - non_strict_neg_clauses: ClsSet::new(), - neg_clauses: ClsSet::new(), - imp_clauses: ClsSet::new(), - is_finalized: false, - split: Some(clause), - define_funs: self.define_funs.clone(), - old_names: self.old_names.clone(), - print_success: false, - unsat_cores: false, - proofs: false, - } - } - - /// Set of positive clauses. - /// - /// Only available after finalize. - pub fn pos_clauses(&self) -> &ClsSet { - &self.pos_clauses - } - /// Set of negative clauses with exactly one predicate application. - /// - /// Only available after finalize. - pub fn strict_neg_clauses(&self) -> &ClsSet { - &self.strict_neg_clauses - } - /// Set of negative clauses. - /// - /// Only available after finalize. - pub fn neg_clauses(&self) -> &ClsSet { - &self.neg_clauses - } - /// Set of non-strict negative clauses. - /// - /// Only available after finalize. - pub fn non_strict_neg_clauses(&self) -> &ClsSet { - &self.non_strict_neg_clauses - } - /// Set of implication clauses ad negative clausesh. - /// - /// Only available after finalize. - pub fn imp_clauses(&self) -> &ClsSet { - &self.imp_clauses - } - - /// Number of active (not forced) predicates. - pub fn active_pred_count(&self) -> usize { - let mut count = 0; - for pred in self.pred_indices() { - if !self.is_known(pred) { - count += 1 - } - } - count - } - - /// Returns true if the instance is already solved. - pub fn is_solved(&self) -> bool { - if self.is_unsat { - return true; - } - for def in &self.pred_terms { - if def.is_none() { - return false; - } - } - true - } - - /// Map from the original signature of a predicate. - pub fn map_from_original_sig_of(&self, pred: PrdIdx) -> VarHMap { - let mut res = VarHMap::with_capacity(self.old_preds[pred].1.len()); - for (tgt, src) in self.old_preds[pred].1.index_iter() { - res.insert(*src, term::var(tgt, self.old_preds[pred].0[*src].clone())); - } - res - } - - /// Original signature of a predicate. - pub fn original_sig_of(&self, pred: PrdIdx) -> &Sig { - &self.old_preds[pred].0 - } - /// Map to the original signature of a predicate. - pub fn map_to_original_sig_of(&self, pred: PrdIdx) -> &VarMap { - &self.old_preds[pred].1 - } - - /// If this instance is the result of a split, returns the index of the - /// clause of the original instance that the split was on. - /// - /// Used mainly to create different folders for log files when splitting. - pub fn split(&self) -> Option { - self.split - } - - /// True if the unsat flag is set. - pub fn is_unsat(&self) -> bool { - self.is_unsat - } - - /// Sets the unsat flag in the instance. - pub fn set_unsat(&mut self) { - self.is_unsat = true - } - - /// True if a predicate is forced to something. - #[inline] - pub fn is_known(&self, pred: PrdIdx) -> bool { - self.pred_terms[pred].is_some() - } - - /// Adds a define fun. - pub fn add_define_fun>( - &mut self, - name: S, - sig: VarInfos, - body: ::parse::PTTerms, - ) -> Option<(VarInfos, ::parse::PTTerms)> { - self.define_funs.insert(name.into(), (sig, body)) - } - /// Retrieves a define fun. - pub fn get_define_fun(&self, name: &str) -> Option<&(VarInfos, ::parse::PTTerms)> { - self.define_funs.get(name) - } - - /// Returns the model corresponding to the input predicates and the forced - /// predicates. - /// - /// The model is sorted in topological order. - pub fn model_of(&self, candidates: Candidates) -> Res { - use std::iter::Extend; - - let mut model = Model::with_capacity(self.preds.len()); - model.extend( - candidates - .into_index_iter() - .filter_map(|(pred, tterms_opt)| { - tterms_opt.map(|term| { - let (term, _) = term.subst(&self.old_var_maps[pred]); - (pred, TTerms::of_term(None, term)) - }) - }), - ); - - for pred in &self.sorted_pred_terms { - let pred = *pred; - if let Some(ref tterms) = self.pred_terms[pred] { - model.push((pred, tterms.subst(&self.old_var_maps[pred]))) - } else { - bail!("inconsistency in sorted forced predicates") - } - } - - Ok(model) - } - - /// Returns the model corresponding to the input predicates and the forced - /// predicates. - /// - /// The model is sorted in topological order. - pub fn extend_model(&self, candidates: ConjCandidates) -> Res { - use std::iter::Extend; - let mut model = ConjModel::with_capacity(self.preds.len()); - let mut known_preds = PrdSet::new(); - let mut tmp: Vec<_> = candidates - .into_iter() - .map(|(pred, conj)| { - let mut preds = PrdSet::new(); - for tterms in &conj { - preds.extend(tterms.preds()) - } - (pred, preds, conj) - }).collect(); - let mut cnt; - let mut changed; - while !tmp.is_empty() { - cnt = 0; - changed = false; - while cnt < tmp.len() { - if tmp[cnt].1.iter().all(|pred| known_preds.contains(pred)) { - changed = true; - let (pred, _, dnf) = tmp.swap_remove(cnt); - let is_new = known_preds.insert(pred); - debug_assert! { is_new } - model.push(vec![(pred, dnf)]) - } else { - cnt += 1 - } - } - if !changed { - break; - } - } - if !tmp.is_empty() { - model.push(tmp.into_iter().map(|(pred, _, dnf)| (pred, dnf)).collect()) - } - - for pred in &self.sorted_pred_terms { - let pred = *pred; - if let Some(ref tterms) = self.pred_terms[pred] { - let tterms = tterms.subst(&self.old_var_maps[pred]); - model.push(vec![(pred, vec![tterms])]) - } else { - bail!("inconsistency in sorted forced predicates") - } - } - Ok(model) - } - - /// True if the instance is sat, false if unsat. - fn is_trivial(&self) -> Option { - if self.is_unsat { - Some(false) - } else if self.pred_terms.iter().all(|term| term.is_some()) { - Some(true) - } else { - None - } - } - - /// Returns a model for the instance when all the predicates have terms - /// assigned to them. - pub fn is_trivial_conj(&self) -> Res>> { - match self.is_trivial() { - None => Ok(None), - Some(false) => Ok(Some(MaybeModel::Unsat)), - Some(true) => self - .extend_model(PrdHMap::new()) - .map(|res| Some(MaybeModel::Model(res))), - } - } - - /// Returns a model for the instance when all the predicates have terms - /// assigned to them. - pub fn is_trivial_model(&self) -> Res>> { - match self.is_trivial() { - None => Ok(None), - Some(false) => Ok(Some(MaybeModel::Unsat)), - Some(true) => self - .model_of(PrdMap::new()) - .map(|res| Some(MaybeModel::Model(res))), - } - } - - /// Lhs and rhs predicates of a clause. - #[inline] - pub fn preds_of_clause(&self, clause: ClsIdx) -> (&PredApps, Option) { - ( - self[clause].lhs_preds(), - self[clause].rhs().map(|(prd, _)| prd), - ) - } - - /// Prints some top terms as a model. - /// - /// Meaning variables are printed with default printing: `` is - /// printed as `v_`. - pub fn print_tterms_as_model(&self, w: &mut W, tterms: &TTerms) -> IoRes<()> { - tterms.write( - w, - |w, var| var.default_write(w), - |w, pred, args| { - write!(w, "({}", self[pred])?; - let mut prev: VarIdx = 0.into(); - for (var, arg) in args.index_iter() { - let old_var = self.old_preds[pred].1[var]; - for var in VarRange::new(prev, old_var) { - write!(w, " {}", self.old_preds[pred].0[var].default_val())? - } - prev = old_var; - prev.inc(); - write!(w, " {}", arg)? - } - for var in VarRange::new(prev, self.old_preds[pred].0.next_index()) { - write!(w, " {}", self.old_preds[pred].0[var].default_val())? - } - write!(w, ")") - }, - ) - } - - /// Finalizes instance creation. - /// - /// - shrinks all collections - /// - sorts forced predicates by dependencies - /// - /// # TO DO - /// - /// - optimize sorting of forced preds by dependencies (low priority) - pub fn finalize(&mut self) -> Res<()> { - if self.is_finalized { - return Ok(()); - } - self.is_finalized = true; - - self.sorted_pred_terms.clear(); - self.preds.shrink_to_fit(); - self.old_preds.shrink_to_fit(); - self.pred_terms.shrink_to_fit(); - self.clauses.shrink_to_fit(); - - let mut tmp: Vec<(PrdIdx, PrdSet)> = Vec::with_capacity(self.preds.len()); - - for (idx, clause) in self.clauses.index_iter_mut() { - if clause.rhs().is_none() { - if clause.lhs_pred_apps_len() == 1 { - let is_new = self.strict_neg_clauses.insert(idx); - debug_assert! { is_new } - } else { - let is_new = self.non_strict_neg_clauses.insert(idx); - debug_assert! { is_new } - } - let is_new = self.neg_clauses.insert(idx); - debug_assert! { is_new } - } else if clause.lhs_preds().is_empty() { - if !(self.is_unsat || clause.rhs().is_some()) { - bail!( - "{}\nfound a clause with no predicate during finalization", - clause.to_string_info(&self.preds).unwrap() - ) - } - let is_new = self.pos_clauses.insert(idx); - debug_assert! { is_new } - } else { - let is_new = self.imp_clauses.insert(idx); - debug_assert! { is_new } - } - } - - // Populate `tmp`. - let mut known_preds = PrdSet::with_capacity(self.preds.len()); - - for pred in self.pred_indices() { - if let Some(ref tterms) = self.pred_terms[pred] { - tmp.push((pred, tterms.preds())) - } else { - known_preds.insert(pred); - } - } - // Sort by dependencies. - while !tmp.is_empty() { - let mut cnt = 0; // Will use swap remove. - 'find_preds: while cnt < tmp.len() { - for pred in &tmp[cnt].1 { - if !known_preds.contains(pred) { - // Don't know this predicate, keep going in `tmp`. - cnt += 1; - continue 'find_preds; - } - } - // Reachable only we already have all of the current pred's - // dependencies. - let (pred, _) = tmp.swap_remove(cnt); - self.sorted_pred_terms.push(pred); - known_preds.insert(pred); - () // No `cnt` increment after swap remove. - } - } - - for (pred, info) in self.preds.index_iter() { - let mut map = VarMap::with_capacity(info.sig.len()); - for (var, typ) in info.sig.index_iter() { - map.push(term::var(self.old_preds[pred].1[var], typ.clone())) - } - self.old_var_maps.push(map) - } - - self.sorted_pred_terms.shrink_to_fit(); - - Ok(()) - } - - /// Returns the term we already know works for a predicate, if any. - pub fn forced_terms_of(&self, pred: PrdIdx) -> Option<&TTerms> { - self.pred_terms[pred].as_ref() - } - - /// If the input predicate is forced to a constant boolean, returns its - /// value. - pub fn bool_value_of(&self, pred: PrdIdx) -> Option { - self.forced_terms_of(pred).and_then(|terms| terms.bool()) - } - - /// Forced predicates in topological order. - #[inline] - pub fn sorted_forced_terms(&self) -> &Vec { - &self.sorted_pred_terms - } - - /// Returns the clauses in which the predicate appears in the lhs and rhs - /// respectively. - #[inline] - pub fn clauses_of(&self, pred: PrdIdx) -> (&ClsSet, &ClsSet) { - (&self.pred_to_clauses[pred].0, &self.pred_to_clauses[pred].1) - } - /// Returns the clauses in which `pred` appears in the lhs. - #[inline] - pub fn lhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { - &self.pred_to_clauses[pred].0 - } - /// Returns the clauses in which `pred` appears in the rhs. - #[inline] - pub fn rhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { - &self.pred_to_clauses[pred].1 - } - - /// Adds a predicate application to a clause's lhs. - pub fn clause_add_lhs_pred(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarMap) { - self.pred_to_clauses[pred].0.insert(clause); - self.clauses[clause].insert_pred_app(pred, args.into()); - } - - /// Adds a term to a clause's lhs. - pub fn clause_add_lhs_term(&mut self, clause: ClsIdx, term: Term) { - self.clauses[clause].insert_term(term); - } - - /// Forces the rhs of a clause to a predicate application. - pub fn clause_force_rhs( - &mut self, - clause: ClsIdx, - pred: PrdIdx, - args: VarMap, - ) -> Res<()> { - self.pred_to_clauses[pred].1.insert(clause); - self.clauses[clause].set_rhs(pred, args.into()) - } - - /// Adds some terms to the lhs of a clause. - /// - /// Updates `pred_to_clauses`. - pub fn clause_lhs_extend>( - &mut self, - clause_idx: ClsIdx, - tterms: I, - ) { - let clause = &mut self.clauses[clause_idx]; - for tterm in tterms { - match tterm { - TTerm::P { pred, args } => { - self.pred_to_clauses[pred].0.insert(clause_idx); - clause.insert_pred_app(pred, args); - } - TTerm::T(term) => { - clause.insert_term(term); - } - } - } - } - - /// Replaces the rhs of a clause. - /// - /// Updates `pred_to_clauses` for the term it inserts but **not** the one it - /// removes. - pub fn clause_rhs_force(&mut self, clause_idx: ClsIdx, tterm: TTerm) -> Res<()> { - let clause = &mut self.clauses[clause_idx]; - match tterm { - TTerm::P { pred, args } => { - clause.set_rhs(pred, args)?; - let is_new = self.pred_to_clauses[pred].1.insert(clause_idx); - debug_assert!(is_new) - } - TTerm::T(term) => { - if term.bool() != Some(false) { - clause.insert_term(term::not(term)); - } - clause.unset_rhs(); - } - } - Ok(()) - } - - /// Range over the predicate indices. - pub fn pred_indices(&self) -> PrdRange { - PrdRange::zero_to(self.preds.len()) - } - /// Range over the clause indices. - pub fn clause_indices(&self) -> ClsRange { - ClsRange::zero_to(self.clauses.len()) - } - - /// Predicate accessor. - pub fn preds(&self) -> &PrdInfos { - &self.preds - } - /// Clause accessor. - pub fn clauses(&self) -> &ClsMap { - &self.clauses - } - - /// Pushes a new predicate and returns its index. - pub fn push_pred(&mut self, name: String, sig: Sig) -> PrdIdx { - let idx = self.preds.next_index(); - let mut var_map = VarMap::with_capacity(sig.len()); - for (var, _) in sig.index_iter() { - var_map.push(var) - } - self.old_preds.push((sig.clone(), var_map)); - self.preds.push(PrdInfo { name, idx, sig }); - self.pred_terms.push(None); - self.pred_str.push(None); - - self.pred_to_clauses - .push((ClsSet::with_capacity(17), ClsSet::with_capacity(17))); - idx - } - - /// Sets the strengthener for a predicate. - pub fn set_str(&mut self, pred: PrdIdx, term: Term) -> Option { - ::std::mem::replace(&mut self.pred_str[pred], Some(term)) - } - - /// Retrieves the strengthener for a predicate if any. - pub fn get_str(&self, pred: PrdIdx) -> Option<&Term> { - self.pred_str[pred].as_ref() - } - - /// Adds a companion function for a predicate. - pub fn add_companion_fun(&mut self, pred: PrdIdx, fun: Fun) { - self.companion_funs - .entry(pred) - .or_insert_with(Vec::new) - .push(fun) - } - - /// Retrieves the companion functions for a predicate. - pub fn get_companion_funs(&self, pred: PrdIdx) -> Option<&Vec> { - self.companion_funs.get(&pred) - } - - /// Removes and returns the indices of the clauses `pred` appears in the lhs - /// of from `self.pred_to_clauses`. - fn unlink_pred_lhs(&mut self, pred: PrdIdx, lhs: &mut LHS) - where - LHS: ::std::iter::Extend, - { - lhs.extend(self.pred_to_clauses[pred].0.drain()) - } - - /// Removes and returns the indices of the clauses `pred` appears in the rhs - /// of from `self.pred_to_clauses`. - fn unlink_pred_rhs(&mut self, pred: PrdIdx, rhs: &mut RHS) - where - RHS: ::std::iter::Extend, - { - rhs.extend(self.pred_to_clauses[pred].1.drain()) - } - - /// Goes trough the predicates in `from`, and updates `pred_to_clauses` so - /// that they point to `to` instead. - fn relink_preds_to_clauses(&mut self, from: ClsIdx, to: ClsIdx) -> Res<()> { - for pred in self.clauses[from].lhs_preds().keys() { - let pred = *pred; - let was_there = self.pred_to_clauses[pred].0.remove(&from); - let _ = self.pred_to_clauses[pred].0.insert(to); - debug_assert!(was_there) - } - if let Some((pred, _)) = self.clauses[from].rhs() { - let was_there = self.pred_to_clauses[pred].1.remove(&from); - let _ = self.pred_to_clauses[pred].1.insert(to); - debug_assert!(was_there) - } - Ok(()) - } - - /// Forget some clauses. - /// - /// Duplicates are handled as if there was only one. - pub fn forget_clauses(&mut self, clauses: &mut Vec) -> Res<()> { - // Forgetting is done by swap remove, we sort in DESCENDING order so that - // indices always make sense. - clauses.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); - let mut prev = None; - for clause in clauses.drain(0..) { - log!{ @6 - "forgetting {}", self[clause].to_string_info(& self.preds) ? - } - if prev == Some(clause) { - continue; - } - prev = Some(clause); - let _ = self.forget_clause(clause)?; - } - Ok(()) - } - - /// Forget a clause. **Does not preserve the order of the clauses.** - /// - /// After calling this function, clause indices kept outside of the instance - /// will in general refer to clauses different from the ones they pointed to - /// before the call. - /// - /// Also unlinks predicates from `pred_to_clauses`. - pub fn forget_clause(&mut self, clause: ClsIdx) -> Res { - for pred in self.clauses[clause].lhs_preds().keys() { - let pred = *pred; - let was_there = self.pred_to_clauses[pred].0.remove(&clause); - debug_assert!(was_there || self.is_known(pred)) - } - if let Some((pred, _)) = self.clauses[clause].rhs() { - self.pred_to_clauses[pred].1.remove(&clause); - } - // Relink the last clause as its index is going to be `clause`. Except if - // `clause` is already the last one. - let last_clause: ClsIdx = (self.clauses.len() - 1).into(); - if clause != last_clause { - self.relink_preds_to_clauses(last_clause, clause)? - } - let res = self.clauses.swap_remove(clause); - Ok(res) - } - - /// First free clause index. - pub fn next_clause_index(&self) -> ClsIdx { - self.clauses.next_index() - } - - /// Pushes a new clause. - pub fn push_new_clause( - &mut self, - vars: VarInfos, - lhs: Vec, - rhs: Option, - info: &'static str, - ) -> Res> { - let idx = self.clauses.next_index(); - let clause = clause::new(vars, lhs, rhs, info, idx); - self.push_clause(clause) - } - - /// The name of an original clause if any. - pub fn name_of_old_clause(&self, cls: ClsIdx) -> Option<&String> { - self.old_names.get(&cls) - } - - /// Sets the name for an original clause. - pub fn set_old_clause_name(&mut self, cls: ClsIdx, name: String) -> Res<()> { - let prev = self.old_names.insert(cls, name); - if let Some(prev) = prev { - bail!(format!( - "trying to name clause #{}, but it's already called {}", - cls, - conf.bad(&prev) - )) - } - Ok(()) - } - - /// Mutable accessor for side clauses. - /// - /// Does not expose function invariants. - pub fn side_clauses_retain(&mut self, mut keep: Keep) -> Res - where - Keep: FnMut(&mut Clause) -> Res, - { - let mut info = RedInfo::new(); - let mut cnt = 0; - while cnt < self.side_clauses.len() { - if !keep(&mut self.side_clauses[cnt])? { - info.clauses_rmed += 1; - self.side_clauses.swap_remove(cnt); - () - } else { - cnt += 1 - } - } - Ok(info) - } - - /// Asserts all the side-clauses in a solver. - pub fn assert_side_clauses

(&self, solver: &mut Solver

) -> Res<()> { - for side_clause in &self.side_clauses { - let side_clause = smt::SmtSideClause::new(side_clause); - solver.assert(&side_clause)? - } - Ok(()) - } - - /// Registers a clause as a side-clause. - /// - /// A side clause is a clause that does not mention any predicate, but - /// mentions a user-defined function. - pub fn add_side_clause(&mut self, clause: Clause) -> Res<()> { - if clause.rhs().is_some() { - bail!("cannot convert to side-clause: predicate application in rhs") - } - if !clause.lhs_preds().is_empty() { - bail!("cannot convert to side-clause: predicate application(s) in lhs") - } - - self.side_clauses.push(clause); - - Ok(()) - } - - /// Registers a new side clause: forces the term to be true. - /// - /// A side clause is a clause that does not mention any predicate, but - /// mentions a user-defined function. - pub fn add_new_side_clause( - &mut self, - vars: VarInfos, - term: Term, - info: &'static str, - ) -> Res<()> { - let clause = clause::new( - vars, - vec![TTerm::T(term::not(term))], - None, - info, - self.side_clauses.len().into(), - ); - - self.add_side_clause(clause) - } - - /// Pushes a clause. - /// - /// Returns its index, if it was added. - pub fn push_clause(&mut self, clause: Clause) -> Res> { - for term in clause.lhs_terms() { - if let Some(false) = term.bool() { - return Ok(None); - } - } - - if clause.lhs_preds().is_empty() && clause.rhs().is_none() && clause - .lhs_terms() - .iter() - .any(|term| term.has_fun_app_or_adt()) - { - self.add_side_clause(clause)?; - return Ok(None); - } - - let idx = self.clauses.next_index(); - let is_new = self.push_clause_unchecked(clause); - // self.check("after `push_clause`") ? ; - Ok(if is_new { Some(idx) } else { None }) - } - - /// True if the clause is redundant. - pub fn is_redundant(&self, idx: ClsIdx) -> bool { - let clause = &self.clauses[idx]; - - if let Some((pred, _)) = clause.rhs() { - for i in &self.pred_to_clauses[pred].1 { - if *i != idx && self[*i].same_as(&clause) { - return true; - } - } - } else if let Some((pred, _)) = clause.lhs_preds().iter().next() { - for i in &self.pred_to_clauses[*pred].0 { - if *i != idx && self[*i].same_as(&clause) { - return true; - } - } - } else { - for (i, c) in self.clauses.index_iter() { - if i != idx && c.same_as(&clause) { - return true; - } - } - } - false - } - - /// Pushes a new clause, does not sanity-check but redundancy-checks. - fn push_clause_unchecked(&mut self, clause: Clause) -> bool { - let clause_index = self.clauses.next_index(); - self.clauses.push(clause); - - if self.is_redundant(clause_index) { - self.clauses.pop(); - return false; - } - - for pred in self.clauses[clause_index].lhs_preds().keys() { - let pred = *pred; - let is_new = self.pred_to_clauses[pred].0.insert(clause_index); - debug_assert!(is_new) - } - if let Some((pred, _)) = self.clauses[clause_index].rhs() { - let is_new = self.pred_to_clauses[pred].1.insert(clause_index); - debug_assert!(is_new) - } - true - } - - /// Checks that the instance has no inconsistencies. - /// - /// Only active in debug. - #[cfg(not(debug_assertions))] - #[inline(always)] - pub fn check(&self, _: &'static str) -> Res<()> { - Ok(()) - } - - #[cfg(debug_assertions)] - pub fn check(&self, s: &'static str) -> Res<()> { - for clause in &self.clauses { - clause.check(s)? - } - self.check_pred_to_clauses() - .chain_err(|| format!("while checking `{}`", conf.sad("pred_to_clauses"))) - .chain_err(|| format!("instance consistency check failed: {}", conf.emph(s)))?; - self.check_preds_consistency()?; - - for (idx, clause) in self.clauses.index_iter() { - for term in clause.lhs_terms() { - if let Some(false) = term.bool() { - bail!( - "({}) found a trivial clause: #{} {}", - s, - idx, - clause.to_string_info(self.preds()).unwrap() - ) - } - } - - for pred in clause - .lhs_preds() - .iter() - .map(|(pred, _)| *pred) - .chain(clause.rhs().into_iter().map(|(pred, _)| pred)) - { - if let Some(tterms) = self.forced_terms_of(pred) { - bail! { - "predicate {} is forced{} but appears in a clause: {}", - conf.bad( & self[pred].name ), - match tterms.bool() { - Some(true) => " to true", - Some(false) => " to false", - None => "", - }, - s - } - } - } - } - - scoped! { - let mut clauses = self.clauses.iter() ; - while let Some(clause) = clauses.next() { - for c in clauses.clone() { - if clause.same_as(c) { - bail!( - "{}\n\n{}\n\nsame clause appears multiple times\n{}", - clause.to_string_info(self.preds()).unwrap(), - c.to_string_info(self.preds()).unwrap(), - conf.bad(s) - ) - } - } - } - } - - Ok(()) - } - - /// Checks `preds` and `old_preds` are consistent. - #[cfg(debug_assertions)] - fn check_preds_consistency(&self) -> Res<()> { - for (pred, info) in self.preds.index_iter() { - for (var, typ) in info.sig.index_iter() { - let (ref old_sig, ref var_map) = self.old_preds[pred]; - if old_sig[var_map[var]] != *typ { - bail!( - "type inconsistency between current and original predicates:\n\ - on {}, variable {}: {} is routed to {}: {}", - self[pred], - var.default_str(), - typ, - var_map[var].default_str(), - old_sig[var_map[var]] - ) - } - } - } - Ok(()) - } - - /// Pretty printer for a set of clauses. - #[cfg(debug_assertions)] - fn pretty_clauses(&self, clauses: &ClsSet) -> String { - let mut s = String::new(); - s.push('{'); - for clause in clauses { - s.push(' '); - s.push_str(&format!("{}", clause)) - } - s.push(' '); - s.push('}'); - s - } - - /// Checks the consistency of `pred_to_clauses`. - #[cfg(debug_assertions)] - fn check_pred_to_clauses(&self) -> Res<()> { - for (cls_idx, clause) in self.clauses.index_iter() { - for (pred, _) in clause.lhs_preds() { - let pred = *pred; - if self.is_known(pred) { - bail!( - "predicate {} is forced but appears in lhs of clause {}", - self[pred], - cls_idx - ) - } - if !self.pred_to_clauses[pred].0.contains(&cls_idx) { - bail!( - "predicate {} appears in lhs of clause {} \ - but is not registered as such\n{}\nlhs: {}\nrhs: {}", - self[pred], - cls_idx, - self.clauses[cls_idx].to_string_info(self.preds())?, - self.pretty_clauses(&self.pred_to_clauses[pred].0), - self.pretty_clauses(&self.pred_to_clauses[pred].1) - ) - } - } - if let Some((pred, _)) = clause.rhs() { - if self.is_known(pred) { - bail!( - "predicate {} is forced but appears in rhs of clause {}", - self[pred], - cls_idx - ) - } - if !self.pred_to_clauses[pred].1.contains(&cls_idx) { - bail!( - "predicate {} appears in rhs of clause {} \ - but is not registered as such\n{}\nlhs: {}\nrhs: {}", - self[pred], - cls_idx, - self.clauses[cls_idx].to_string_info(self.preds())?, - self.pretty_clauses(&self.pred_to_clauses[pred].0), - self.pretty_clauses(&self.pred_to_clauses[pred].1) - ) - } - } - } - - for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { - for clause in lhs { - if *clause >= self.clauses.len() { - bail!( - "predicate {} is registered as appearing in lhs of clause {} \ - which is above the maximal clause index", - self[pred], - clause - ) - } - if self.clauses[*clause].lhs_preds().get(&pred).is_none() { - bail!( - "predicate {} is registered as appearing in lhs of clause {} \ - but it's not the case\n{}\nlhs: {}\nrhs: {}", - self[pred], - clause, - self.clauses[*clause].to_string_info(self.preds())?, - self.pretty_clauses(&self.pred_to_clauses[pred].0), - self.pretty_clauses(&self.pred_to_clauses[pred].1) - ) - } - } - for clause in rhs { - if *clause >= self.clauses.len() { - bail!( - "predicate {} is registered as appearing in rhs of clause {} \ - which is above the maximal clause index", - self[pred], - clause - ) - } - if let Some((this_pred, _)) = self.clauses[*clause].rhs() { - if this_pred == pred { - continue; - } - } - bail!( - "predicate {} is registered to appear in rhs of clause {} \ - but it's not the case\n{}\nlhs: {}\nrhs: {}", - self[pred], - clause, - self.clauses[*clause].to_string_info(self.preds())?, - self.pretty_clauses(&self.pred_to_clauses[pred].0), - self.pretty_clauses(&self.pred_to_clauses[pred].1) - ) - } - } - Ok(()) - } - - /// Dumps the instance as an SMT-LIB 2 problem. - pub fn dump_as_smt2(&self, w: &mut File, blah: Blah) -> Res<()> - where - File: Write, - Blah: AsRef, - { - let blah = blah.as_ref(); - - for line in blah.lines() { - writeln!(w, "; {}", line)? - } - writeln!(w)?; - writeln!(w, "(set-logic HORN)")?; - writeln!(w)?; - - writeln!(w, "; Datatypes")?; - - dtyp::write_all(w, "")?; - - dtyp::write_constructor_map(w, "; ")?; - writeln!(w)?; - - writeln!(w, "; Functions")?; - fun::write_all(w, "", true)?; - - writeln!(w)?; - - writeln!(w, "; Side-clauses")?; - for side_clause in &self.side_clauses { - side_clause.write( - w, - |w, var_info| write!(w, "{}", var_info.name), - |_, _, _, _| panic!("illegal side-clause: found predicate application(s)"), - true, - )?; - writeln!(w)?; - } - - writeln!(w)?; - writeln!(w)?; - - for (pred_idx, pred) in self.preds.index_iter() { - if self.pred_terms[pred_idx].is_none() { - if let Some(term) = &self.pred_str[pred_idx] { - writeln!(w, "; Strengthening term:")?; - writeln!(w, "; {}", term)? - } - write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; - for typ in &pred.sig { - write!(w, " {}", typ)? - } - writeln!(w, " ) Bool\n)")? - } - } - - writeln!( - w, - "\n; Original clauses' names ({}) {{", - self.old_names.len() - )?; - for (idx, name) in &self.old_names { - writeln!(w, "; #{}: `{}`.", idx, name)? - } - writeln!(w, "; }}")?; - - for (idx, clause) in self.clauses.index_iter() { - writeln!(w, "\n; Clause #{}", idx)?; - - // Print source. - let from = clause.from(); - write!(w, "; from: #{}", from)?; - if let Some(name) = self.old_names.get(&from) { - write!(w, ": {}", name)? - } - writeln!(w)?; - - clause.write( - w, - |w, var_info| write!(w, "{}", var_info.name), - |w, p, args, bindings| { - if !args.is_empty() { - write!(w, "(")? - } - w.write_all(self[p].name.as_bytes())?; - for arg in args.iter() { - write!(w, " ")?; - arg.write_with(w, |w, var| write!(w, "{}", clause.vars[var]), bindings)? - } - if !args.is_empty() { - write!(w, ")") - } else { - Ok(()) - } - }, - true, - )?; - writeln!(w)?; - writeln!(w)? - } - - writeln!(w, "\n(check-sat)")?; - - Ok(()) - } - - /// Simplifies some predicate definitions. - /// - /// Simplifies its internal predicate definitions and the ones in the model. - pub fn simplify_pred_defs(&mut self, model: &mut Model) -> Res<()> { - let mut old_model = Vec::with_capacity(model.len()); - ::std::mem::swap(&mut old_model, model); - for (pred, def) in old_model { - let simplified = def.simplify_pred_apps(&model, &self.pred_terms); - model.push((pred, simplified)) - } - - if self.sorted_pred_terms.is_empty() { - self.finalize()? - } - - let mut old_tterms: PrdMap> = vec![None; self.pred_terms.len()].into(); - ::std::mem::swap(&mut old_tterms, &mut self.pred_terms); - for pred in &self.sorted_pred_terms { - let mut curr_def = None; - ::std::mem::swap(&mut curr_def, &mut old_tterms[*pred]); - if let Some(def) = curr_def { - let simplified = def.simplify_pred_apps(&model, &self.pred_terms); - self.pred_terms[*pred] = Some(simplified) - } - } - Ok(()) - } - - /// Writes a conjunction of top terms. - pub fn write_tterms_conj(&self, w: &mut W, conj: &[TTerms]) -> Res<()> { - if conj.is_empty() { - write!(w, "true")? - } else if conj.len() == 1 { - self.print_tterms_as_model(w, &conj[0])? - } else { - write!(w, "(and")?; - for tterms in conj { - write!(w, " ")?; - self.print_tterms_as_model(w, tterms)? - } - write!(w, ")")? - } - Ok(()) - } - - /// Writes a predicate signature. - /// - /// Does not write the name of the predicate. - pub fn write_pred_sig(&self, w: &mut W, pred: PrdIdx) -> Res<()> { - let (ref old_sig, _) = self.old_preds[pred]; - write!(w, "(")?; - for (var, typ) in old_sig.index_iter() { - write!(w, " ({} {})", var.default_str(), typ)? - } - write!(w, " ) {}", typ::bool())?; - Ok(()) - } - - /// Writes some definitions. - pub fn write_definitions( - &self, - w: &mut W, - pref: &str, - model: ConjModelRef, - ) -> Res<()> { - fun::write_all(w, pref, false)?; - - for defs in model { - if defs.is_empty() { - () - } else if defs.len() == 1 { - let (pred, ref tterms) = defs[0]; - - writeln!(w, "{}({} {}", pref, keywords::cmd::def_fun, self[pred].name)?; - write!(w, "{} ", pref)?; - self.write_pred_sig(w, pred)?; - write!(w, "\n{} ", pref)?; - self.write_tterms_conj(w, tterms)?; - writeln!(w, "\n{})", pref)? - } else { - write!(w, "{}({} (", pref, keywords::cmd::def_funs_rec)?; - for &(pred, _) in defs { - write!(w, "\n{} {} ", pref, self[pred].name)?; - self.write_pred_sig(w, pred)?; - } - write!(w, "\n{}) (", pref)?; - for &(_, ref tterms) in defs { - write!(w, "\n{} ", pref)?; - self.write_tterms_conj(w, tterms)?; - } - writeln!(w, "\n{}) )", pref)?; - } - } - - Ok(()) - } - - /// Writes a model. - pub fn write_model(&self, model: ConjModelRef, w: &mut W) -> Res<()> { - writeln!(w, "(model")?; - self.write_definitions(w, " ", model)?; - writeln!(w, ")")?; - Ok(()) - } - - /// Sets print-success flag. - pub fn set_print_success(&mut self, b: bool) { - self.print_success = b - } - /// Print-success flag accessor. - pub fn print_success(&self) -> bool { - self.print_success - } - /// Sets unsat-cores flag. - pub fn set_unsat_cores(&mut self, b: bool) { - self.unsat_cores = b - } - /// Unsat-cores flag. - pub fn unsat_cores(&self) -> bool { - self.unsat_cores - } - /// Sets proofs flag. - pub fn set_proofs(&mut self, b: bool) { - self.proofs = b - } - /// Unsat-cores flag. - pub fn proofs(&self) -> bool { - self.proofs - } - - /// True if the teacher needs to maintain a sample graph (unsat - /// cores/proofs). - pub fn track_samples(&self) -> bool { - self.unsat_cores() || self.proofs() - } - - /// Converts `"true"` to `true`, `"false"` to `false`, and everything else to - /// an error. - fn bool_of_str(s: &str) -> Res { - match s { - "true" => Ok(true), - "false" => Ok(false), - _ => bail!("expected boolean `true/false`, got `{}`", s), - } - } - - /// Sets an option. - pub fn set_option(&mut self, flag: &str, val: &str) -> Res<()> { - let flag_err = || format!("while handling set-option for {}", flag); - match flag { - "print-success" => { - let print_succ = Self::bool_of_str(&val).chain_err(flag_err)?; - self.set_print_success(print_succ) - } - "produce-unsat-cores" => { - let unsat_cores = Self::bool_of_str(&val).chain_err(flag_err)?; - self.set_unsat_cores(unsat_cores) - } - "produce-proofs" => { - let proofs = Self::bool_of_str(&val).chain_err(flag_err)?; - self.set_proofs(proofs) - } - _ => warn!( - "ignoring (set-option :{} {}): unknown flag {}", - flag, val, flag - ), - } - Ok(()) - } -} - -/// Lhs part of a cex. -type CexLhs = Vec<(PrdIdx, VarTermsSet)>; -/// Lhs part of a cex, reference version. -type CexLhsRef<'a> = &'a [(PrdIdx, VarTermsSet)]; -/// Rhs part of a cex. -type CexRhs<'a> = Option<(PrdIdx, &'a VarTerms)>; - -/// Cex-related functions. -impl Instance { - /// Retrieves the lhs and rhs cex part from a bias. - fn break_cex(&self, clause_idx: ClsIdx, bias: Bias) -> (CexLhs, CexRhs) { - let clause = &self[clause_idx]; - match bias { - // Consider the whole lhs of the clause positive. - Bias::Lft => (vec![], clause.rhs()), - - // Consider the rhs of the clause negative. - Bias::Rgt => ( - clause - .lhs_preds() - .iter() - .map(|(pred, argss)| (*pred, argss.clone())) - .collect(), - None, - ), - - // Consider the rhs of the clause negative, and all lhs applications - // positive except this one. - Bias::NuRgt(pred, args) => { - use var_to::terms::VarTermsSet; - debug_assert! { clause.lhs_preds().get(& pred).is_some() } - debug_assert! { - clause.lhs_preds().get(& pred).unwrap().contains(& args) - } - let mut argss = VarTermsSet::with_capacity(1); - argss.insert(args); - (vec![(pred, argss)], None) - } - - // No bias. - Bias::Non => ( - clause - .lhs_preds() - .iter() - .map(|(pred, argss)| (*pred, argss.clone())) - .collect(), - clause.rhs(), - ), - } - } - - /// Forces non-values in the cex if needed. - pub fn force_non_values(&self, cex: &mut Cex, lhs: CexLhsRef, rhs: &CexRhs) { - // Factored set of variables when fixing cex for arguments. - let mut known_vars = VarSet::new(); - - macro_rules! fix_cex { - ($args:expr) => {{ - log! { @6 "fixing {}", $args } - for arg in $args.iter() { - if let Some(var) = arg.var_idx() { - if !cex[var].is_known() { - // Value for `var` is a non-value. - let is_new = known_vars.insert(var); - // Variable appears in more than one arg, force its value. - if !is_new { - cex[var] = cex[var].typ().default_val() - } - } - } else { - for var in term::vars(arg) { - if !cex[var].is_known() { - cex[var] = cex[var].typ().default_val() - } - } - } - } - }}; - } - - // Force non-values in the cex if we're dealing with a constraint, not a - // sample. - if ( - // Positive sample? - lhs.is_empty() && rhs.is_some() - ) || ( - // Negative sample? - lhs.len() == 1 && lhs.iter().all( - |(_, argss)| argss.len() == 1 - ) && rhs.is_none() - ) { - // We're generating a sample. Still need to force variables that appear - // more than once in arguments. - for (_, argss) in lhs { - debug_assert_eq! { argss.len(), 1 } - for args in argss { - fix_cex!(args) - } - } - if let Some((_, args)) = rhs.as_ref() { - fix_cex!(args) - } - } else { - // We're dealing with a constraint, not a sample. Force non-values. - for val in cex.iter_mut() { - if !val.is_known() { - *val = val.typ().default_val() - } - } - } - } - - /// Transforms a cex for a clause into some learning data. - /// - /// Returns `true` if some new data was generated. - pub fn clause_cex_to_data(&self, data: &mut Data, clause_idx: ClsIdx, cex: BCex) -> Res { - let (mut cex, bias) = cex; - - if_log! { @6 - let clause = & self[clause_idx] ; - let mut s = String::new() ; - for (var, val) in cex.index_iter() { - s.push_str(& format!("{}: {}, ", var.default_str(), val)) - } - log! { @6 - "lhs preds: {}", clause.lhs_pred_apps_len() ; - " rhs: {}", if clause.rhs().is_some() { "some" } else { "none" } ; - " bias: {}", bias ; - " cex: {}", s - } - } - - let (lhs, rhs) = self.break_cex(clause_idx, bias); - self.force_non_values(&mut cex, &lhs, &rhs); - - // Evaluates some arguments for a predicate. - macro_rules! eval { - ($args:expr) => {{ - use var_to::vals::RVarVals; - let mut sample = RVarVals::with_capacity($args.len()); - for arg in $args.get() { - let val = arg.eval(&cex)?; - sample.push(val) - } - sample - }}; - } - - // Evaluate antecedents. - let mut antecedents = vec![]; - for (pred, argss) in lhs { - for args in argss { - let sample = eval!(args); - antecedents.push((pred, sample)) - } - } - - let consequent = if let Some((pred, args)) = rhs { - let sample = eval!(args); - Some((pred, sample)) - } else { - None - }; - - if_log! { @6 - let mut s = String::new() ; - if ! antecedents.is_empty() { - for (pred, sample) in & antecedents { - s.push_str( & format!("({} {}) ", self[* pred], sample) ) - } - } else { - s.push_str("true ") - } - s.push_str("=> ") ; - if let Some((pred, sample)) = consequent.as_ref() { - s.push_str( & format!("({} {})", self[* pred], sample) ) - } else { - s.push_str("false") - } - log! { @6 "{}", s } - } - - data.add_data(clause_idx, antecedents, consequent) - } - - /// Turns some teacher counterexamples into learning data. - pub fn cexs_to_data(&self, data: &mut Data, cexs: Cexs) -> Res { - let mut changed = false; - - for (clause_idx, cexs) in cexs { - log! { @5 "adding cexs for #{}", clause_idx } - - for cex in cexs { - let new_stuff = self.clause_cex_to_data(data, clause_idx, cex)?; - changed = changed || new_stuff - } - } - - let (pos, neg) = data.propagate()?; - - Ok(changed || pos > 0 || neg > 0) - } -} - -impl ::std::ops::Index for Instance { - type Output = PrdInfo; - fn index(&self, index: PrdIdx) -> &PrdInfo { - &self.preds[index] - } -} -impl ::std::ops::Index for Instance { - type Output = Clause; - fn index(&self, index: ClsIdx) -> &Clause { - &self.clauses[index] - } -} -impl ::std::ops::IndexMut for Instance { - fn index_mut(&mut self, index: ClsIdx) -> &mut Clause { - &mut self.clauses[index] - } -} -impl AsRef for Instance { - fn as_ref(&self) -> &Self { - self - } -} -impl AsMut for Instance { - fn as_mut(&mut self) -> &mut Self { - self - } -} -// impl ::std::ops::Deref for Instance { -// type Target = Self ; -// fn deref(& self) -> & Self { -// self -// } -// } -// impl ::std::ops::DerefMut for Instance { -// fn deref_mut(& mut self) -> & mut Self { -// self -// } -// } - -impl<'a> PebcakFmt<'a> for Clause { - type Info = &'a PrdInfos; - fn pebcak_err(&self) -> ErrorKind { - "during clause pebcak formatting".into() - } - fn pebcak_io_fmt(&self, w: &mut W, prds: &'a PrdInfos) -> IoRes<()> { - self.write( - w, - |w, var_info| write!(w, "{}", var_info.name), - |w, prd, args, bindings| { - write!(w, "(")?; - w.write_all(prds[prd].as_bytes())?; - for arg in args.iter() { - write!(w, " ")?; - arg.write_with(w, |w, var| write!(w, "{}", self.vars[var]), bindings)? - } - write!(w, ")") - }, - false, - ) - } -} - -impl<'a> PebcakFmt<'a> for Instance { - type Info = (); - fn pebcak_err(&self) -> ErrorKind { - "during instance pebcak formatting".into() - } - fn pebcak_io_fmt(&self, w: &mut W, _: ()) -> IoRes<()> { - writeln!(w, "; Datatypes:")?; - - dtyp::write_all(w, "")?; - - dtyp::write_constructor_map(w, "; ")?; - - for (pred_idx, pred) in self.preds.index_iter() { - if self.pred_terms[pred_idx].is_none() { - write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; - for typ in &pred.sig { - write!(w, " {}", typ)? - } - writeln!(w, " ) Bool\n)")?; - if pred.sig.len() != self.old_preds[pred_idx].0.len() { - write!(w, "; original signature:\n;")?; - for (var, typ) in self.old_preds[pred_idx].0.index_iter() { - write!(w, " ({} {})", var.default_str(), typ)? - } - writeln!(w, "\n; variable map (new -> old):\n;")?; - for (src, tgt) in self.old_preds[pred_idx].1.index_iter() { - write!(w, " {} -> {},", src.default_str(), tgt.default_str())? - } - writeln!(w)? - } - } - } - - let empty_prd_set = PrdSet::new(); - if self.sorted_pred_terms.is_empty() { - // Either there's no forced predicate, or we are printing before - // finalizing. - for (pred, tterms) in self - .pred_terms - .index_iter() - .filter_map(|(pred, tterms_opt)| tterms_opt.as_ref().map(|tt| (pred, tt))) - { - write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred])?; - for (var, typ) in self[pred].sig.index_iter() { - write!(w, " (v_{} {})", var, typ)? - } - write!(w, " ) Bool\n ")?; - tterms - .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) - .unwrap(); - writeln!(w, "\n)")? - } - } else { - for pred in &self.sorted_pred_terms { - write!(w, "({} {}\n (", keywords::cmd::def_fun, self[*pred])?; - for (var, typ) in self[*pred].sig.index_iter() { - write!(w, " (v_{} {})", var, typ)? - } - let tterms = self.pred_terms[*pred].as_ref().unwrap(); - write!(w, " ) Bool\n ")?; - tterms - .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) - .unwrap(); - writeln!(w, "\n)",)? - } - } - - writeln!( - w, - "\n; Original clauses' names ({}) {{", - self.old_names.len() - )?; - for (idx, name) in &self.old_names { - writeln!(w, "; Original clause #{} is called `{}`.", idx, name)? - } - writeln!(w, "; }}")?; - - for (idx, clause) in self.clauses.index_iter() { - writeln!(w, "\n; Clause #{}", idx)?; - let from = clause.from(); - write!(w, "; from: #{}", from)?; - if let Some(name) = self.old_names.get(&from) { - write!(w, ": {}", name)? - } - writeln!(w)?; - clause.pebcak_io_fmt(w, &self.preds)? - } - - writeln!(w, "\npred to clauses:")?; - for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { - write!(w, " {}: lhs {{", self[pred])?; - for lhs in lhs { - write!(w, " {}", lhs)? - } - write!(w, " }}, rhs {{")?; - for rhs in rhs { - write!(w, " {}", rhs)? - } - writeln!(w, " }}")? - } - - Ok(()) - } -} diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 4957b8e8..fb7f53a8 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -1,11 +1,1807 @@ -//! The instance stores the predicates, the clauses, and a lot of information. +//! Actual instance structure. use common::*; +use data::Data; +use info::*; +use var_to::terms::VarTermsSet; -pub mod info; -pub mod preproc; +mod clause; +mod pre_instance; -#[cfg_attr(feature = "cargo-clippy", allow(module_inception))] -mod instance; +pub use self::clause::Clause; +pub use self::pre_instance::PreInstance; -pub use self::instance::{Clause, Instance, PreInstance}; +/// Stores the instance: the clauses, the factory and so on. +/// +/// Clause indices can vary during instance building, because of the +/// simplifications that can remove clauses. +/// +/// So, `pred_to_clauses` has to be carefully maintained, the easiest way to do this is to never +/// access the fields of an instance directly from the outside. This is why all of them are +/// private. +/// +/// Instance mutation should only happen at two points: [parsing] (creation) and [preprocessing]. +/// (A minor exception is model reconstruction.) +/// +/// [parsing]: ../parse/index.html (parse module) +/// [preprocessing]: ../preproc/index.html (preproc module) +#[derive(Clone)] +pub struct Instance { + /// Predicates. + preds: Preds, + + /// Maps new variables to the new ones. + /// + /// Only available after finalize. + old_var_maps: PrdMap>, + /// Predicates for which a suitable term has been found. + pred_terms: PrdMap>, + + /// Predicates defined in `pred_terms`, sorted by predicate dependencies. + /// + /// Populated by the `finalize` function. + sorted_pred_terms: Vec, + + /// Strengthener for predicates. + pred_str: PrdMap>, + + /// Companion function for predicates. + /// + /// A companion function is a function that was created internally + /// specifically for a predicate. Meaning it needs to be given to the user + /// when printing the model. + companion_funs: PrdHMap>, + + /// Side-clauses. + /// + /// A side clause + /// - does not mention any predicate + /// - mentions a user-defined function + /// + /// It will be asserted when the instance is asked to initialize a solver. A + /// side-clause is viewed as additional information provided by the user, a + /// kind of lemma for the actual clauses. + side_clauses: Vec, + + /// Clauses. + clauses: ClsMap, + /// Maps predicates to the clauses where they appear in the lhs and rhs + /// respectively. + pred_to_clauses: PrdMap<(ClsSet, ClsSet)>, + /// Unsat flag. + is_unsat: bool, + /// Set of positive clauses. + /// + /// Only available after finalize. + pos_clauses: ClsSet, + /// Set of strictly negative clauses. + /// + /// A clause is strictly negative if it has strictly one predicate + /// application, and it's in the clause's body. Only available after + /// finalize. + strict_neg_clauses: ClsSet, + /// Set of non-strictly negative clauses. + /// + /// A clause is strictly negative if it has strictly one predicate + /// application, and it's in the clause's body. Only available after + /// finalize. + non_strict_neg_clauses: ClsSet, + /// Set of (non-strictly) negative clauses. + /// + /// Super set of strictly negative clauses. Only available after finalize. + neg_clauses: ClsSet, + /// Set of implication clauses. + /// + /// Only available after finalize. + imp_clauses: ClsSet, + /// True if finalized already ran. + is_finalized: bool, + /// If this instance is the result of a split, contains the index of the + /// clause of the original instance that the split was on. + /// + /// The constructor sets this to `None`. Function `clone_with_clauses` + /// automatically sets it to the clause kept. + split: Option, + + /// Define-funs parsed. + define_funs: HashMap, + + /// Maps **original** clause indexes to their optional name. + old_names: ClsHMap, + + /// Print success. + /// + /// Can only be set by `(set-option :print-success true)`. + print_success: bool, + /// Unsat core production. + /// + /// Can only be set by `(set-option :produce-unsat-cores true)`. + unsat_cores: bool, + /// Unsat core production. + /// + /// Can only be set by `(set-option :produce-proofs true)`. + proofs: bool, +} + +impl Default for Instance { + fn default() -> Self { + Self::new() + } +} + +impl Instance { + /// Instance constructor. + pub fn new() -> Instance { + let pred_capa = conf.instance.pred_capa; + let clause_capa = conf.instance.clause_capa; + Instance { + preds: Preds::with_capacity(pred_capa), + + old_var_maps: PrdMap::with_capacity(pred_capa), + pred_terms: PrdMap::with_capacity(pred_capa), + sorted_pred_terms: Vec::with_capacity(pred_capa), + pred_str: PrdMap::with_capacity(pred_capa), + companion_funs: PrdHMap::new(), + + side_clauses: Vec::with_capacity(7), + clauses: ClsMap::with_capacity(clause_capa), + // clusters: CtrMap::with_capacity( clause_capa / 3 ), + pred_to_clauses: PrdMap::with_capacity(pred_capa), + is_unsat: false, + pos_clauses: ClsSet::new(), + strict_neg_clauses: ClsSet::new(), + non_strict_neg_clauses: ClsSet::new(), + neg_clauses: ClsSet::new(), + imp_clauses: ClsSet::new(), + is_finalized: false, + split: None, + define_funs: HashMap::new(), + old_names: ClsHMap::with_capacity(clause_capa), + print_success: false, + unsat_cores: false, + proofs: false, + } + } + + /// Clones itself. + /// + /// This is only used when splitting. `clause` will be remembered as the + /// clause the split is on. + /// + /// Fails (in debug) if `clause` is not a negative clause of `self` or if + /// `self` is not finalized. + pub fn clone_with_clauses(&self, clause: ClsIdx) -> Self { + debug_assert! { self.neg_clauses.contains(& clause) } + debug_assert! { self.is_finalized } + + Instance { + preds: self.preds.clone(), + + old_var_maps: PrdMap::with_capacity(self.preds.len()), + pred_terms: self.pred_terms.clone(), + sorted_pred_terms: Vec::with_capacity(self.preds.len()), + pred_str: vec![None; self.preds.len()].into(), + companion_funs: self.companion_funs.clone(), + + side_clauses: self.side_clauses.clone(), + clauses: self.clauses.clone(), + pred_to_clauses: self.pred_to_clauses.clone(), + is_unsat: false, + pos_clauses: ClsSet::new(), + strict_neg_clauses: ClsSet::new(), + non_strict_neg_clauses: ClsSet::new(), + neg_clauses: ClsSet::new(), + imp_clauses: ClsSet::new(), + is_finalized: false, + split: Some(clause), + define_funs: self.define_funs.clone(), + old_names: self.old_names.clone(), + print_success: false, + unsat_cores: false, + proofs: false, + } + } + + /// Set of positive clauses. + /// + /// Only available after finalize. + pub fn pos_clauses(&self) -> &ClsSet { + &self.pos_clauses + } + /// Set of negative clauses with exactly one predicate application. + /// + /// Only available after finalize. + pub fn strict_neg_clauses(&self) -> &ClsSet { + &self.strict_neg_clauses + } + /// Set of negative clauses. + /// + /// Only available after finalize. + pub fn neg_clauses(&self) -> &ClsSet { + &self.neg_clauses + } + /// Set of non-strict negative clauses. + /// + /// Only available after finalize. + pub fn non_strict_neg_clauses(&self) -> &ClsSet { + &self.non_strict_neg_clauses + } + /// Set of implication clauses ad negative clausesh. + /// + /// Only available after finalize. + pub fn imp_clauses(&self) -> &ClsSet { + &self.imp_clauses + } + + /// Number of active (not forced) predicates. + pub fn active_pred_count(&self) -> usize { + let mut count = 0; + for pred in self.pred_indices() { + if !self.is_known(pred) { + count += 1 + } + } + count + } + + /// Returns true if the instance is already solved. + pub fn is_solved(&self) -> bool { + if self.is_unsat { + return true; + } + for def in &self.pred_terms { + if def.is_none() { + return false; + } + } + true + } + + /// Map from the original signature of a predicate. + pub fn map_from_original_sig_of(&self, pred: PrdIdx) -> VarHMap { + let mut res = VarHMap::with_capacity(self[pred].original_sig().len()); + + for (tgt, src) in self[pred].original_sig_map().index_iter() { + res.insert( + *src, + term::var(tgt, self[pred].original_sig()[*src].clone()), + ); + } + + res + } + + /// If this instance is the result of a split, returns the index of the + /// clause of the original instance that the split was on. + /// + /// Used mainly to create different folders for log files when splitting. + pub fn split(&self) -> Option { + self.split + } + + /// True if the unsat flag is set. + pub fn is_unsat(&self) -> bool { + self.is_unsat + } + + /// Sets the unsat flag in the instance. + pub fn set_unsat(&mut self) { + self.is_unsat = true + } + + /// True if a predicate is forced to something. + #[inline] + pub fn is_known(&self, pred: PrdIdx) -> bool { + self.pred_terms[pred].is_some() + } + + /// Adds a define fun. + pub fn add_define_fun>( + &mut self, + name: S, + sig: VarInfos, + body: ::parse::PTTerms, + ) -> Option<(VarInfos, ::parse::PTTerms)> { + self.define_funs.insert(name.into(), (sig, body)) + } + /// Retrieves a define fun. + pub fn get_define_fun(&self, name: &str) -> Option<&(VarInfos, ::parse::PTTerms)> { + self.define_funs.get(name) + } + + /// Returns the model corresponding to the input predicates and the forced + /// predicates. + /// + /// The model is sorted in topological order. + pub fn model_of(&self, candidates: Candidates) -> Res { + use std::iter::Extend; + + let mut model = Model::with_capacity(self.preds.len()); + model.extend( + candidates + .into_index_iter() + .filter_map(|(pred, tterms_opt)| { + tterms_opt.map(|term| { + let (term, _) = term.subst(&self.old_var_maps[pred]); + (pred, TTerms::of_term(None, term)) + }) + }), + ); + + for pred in &self.sorted_pred_terms { + let pred = *pred; + if let Some(ref tterms) = self.pred_terms[pred] { + model.push((pred, tterms.subst(&self.old_var_maps[pred]))) + } else { + bail!("inconsistency in sorted forced predicates") + } + } + + Ok(model) + } + + /// Returns the model corresponding to the input predicates and the forced + /// predicates. + /// + /// The model is sorted in topological order. + pub fn extend_model(&self, candidates: ConjCandidates) -> Res { + use std::iter::Extend; + let mut model = ConjModel::with_capacity(self.preds.len()); + let mut known_preds = PrdSet::new(); + let mut tmp: Vec<_> = candidates + .into_iter() + .map(|(pred, conj)| { + let mut preds = PrdSet::new(); + for tterms in &conj { + preds.extend(tterms.preds()) + } + (pred, preds, conj) + }).collect(); + let mut cnt; + let mut changed; + while !tmp.is_empty() { + cnt = 0; + changed = false; + while cnt < tmp.len() { + if tmp[cnt].1.iter().all(|pred| known_preds.contains(pred)) { + changed = true; + let (pred, _, dnf) = tmp.swap_remove(cnt); + let is_new = known_preds.insert(pred); + debug_assert! { is_new } + model.push(vec![(pred, dnf)]) + } else { + cnt += 1 + } + } + if !changed { + break; + } + } + if !tmp.is_empty() { + model.push(tmp.into_iter().map(|(pred, _, dnf)| (pred, dnf)).collect()) + } + + for pred in &self.sorted_pred_terms { + let pred = *pred; + if let Some(ref tterms) = self.pred_terms[pred] { + let tterms = tterms.subst(&self.old_var_maps[pred]); + model.push(vec![(pred, vec![tterms])]) + } else { + bail!("inconsistency in sorted forced predicates") + } + } + Ok(model) + } + + /// True if the instance is sat, false if unsat. + fn is_trivial(&self) -> Option { + if self.is_unsat { + Some(false) + } else if self.pred_terms.iter().all(|term| term.is_some()) { + Some(true) + } else { + None + } + } + + /// Returns a model for the instance when all the predicates have terms + /// assigned to them. + pub fn is_trivial_conj(&self) -> Res>> { + match self.is_trivial() { + None => Ok(None), + Some(false) => Ok(Some(MaybeModel::Unsat)), + Some(true) => self + .extend_model(PrdHMap::new()) + .map(|res| Some(MaybeModel::Model(res))), + } + } + + /// Returns a model for the instance when all the predicates have terms + /// assigned to them. + pub fn is_trivial_model(&self) -> Res>> { + match self.is_trivial() { + None => Ok(None), + Some(false) => Ok(Some(MaybeModel::Unsat)), + Some(true) => self + .model_of(PrdMap::new()) + .map(|res| Some(MaybeModel::Model(res))), + } + } + + /// Lhs and rhs predicates of a clause. + #[inline] + pub fn preds_of_clause(&self, clause: ClsIdx) -> (&PredApps, Option) { + ( + self[clause].lhs_preds(), + self[clause].rhs().map(|(prd, _)| prd), + ) + } + + /// Prints some top terms as a model. + /// + /// Meaning variables are printed with default printing: `` is printed as + /// `v_`. + pub fn print_tterms_as_model(&self, w: &mut W, tterms: &TTerms) -> IoRes<()> { + tterms.write( + w, + |w, var| var.default_write(w), + |w, pred, args| { + let pred = &self[pred]; + write!(w, "({}", pred)?; + let mut prev: VarIdx = 0.into(); + for (var, arg) in args.index_iter() { + let old_var = pred.original_sig_map()[var]; + for var in VarRange::new(prev, old_var) { + write!(w, " {}", pred.original_sig()[var].default_val())? + } + prev = old_var; + prev.inc(); + write!(w, " {}", arg)? + } + for var in VarRange::new(prev, pred.original_sig().next_index()) { + write!(w, " {}", pred.original_sig()[var].default_val())? + } + write!(w, ")") + }, + ) + } + + /// Finalizes instance creation. + /// + /// - shrinks all collections + /// - sorts forced predicates by dependencies + /// + /// # TO DO + /// + /// - optimize sorting of forced preds by dependencies (low priority) + pub fn finalize(&mut self) -> Res<()> { + if self.is_finalized { + return Ok(()); + } + self.is_finalized = true; + + self.sorted_pred_terms.clear(); + self.preds.shrink_to_fit(); + self.pred_terms.shrink_to_fit(); + self.clauses.shrink_to_fit(); + + let mut tmp: Vec<(PrdIdx, PrdSet)> = Vec::with_capacity(self.preds.len()); + + for (idx, clause) in self.clauses.index_iter_mut() { + if clause.rhs().is_none() { + if clause.lhs_pred_apps_len() == 1 { + let is_new = self.strict_neg_clauses.insert(idx); + debug_assert! { is_new } + } else { + let is_new = self.non_strict_neg_clauses.insert(idx); + debug_assert! { is_new } + } + let is_new = self.neg_clauses.insert(idx); + debug_assert! { is_new } + } else if clause.lhs_preds().is_empty() { + if !(self.is_unsat || clause.rhs().is_some()) { + bail!( + "{}\nfound a clause with no predicate during finalization", + clause.to_string_info(&self.preds).unwrap() + ) + } + let is_new = self.pos_clauses.insert(idx); + debug_assert! { is_new } + } else { + let is_new = self.imp_clauses.insert(idx); + debug_assert! { is_new } + } + } + + // Populate `tmp`. + let mut known_preds = PrdSet::with_capacity(self.preds.len()); + + for pred in self.pred_indices() { + if let Some(ref tterms) = self.pred_terms[pred] { + tmp.push((pred, tterms.preds())) + } else { + known_preds.insert(pred); + } + } + // Sort by dependencies. + while !tmp.is_empty() { + let mut cnt = 0; // Will use swap remove. + 'find_preds: while cnt < tmp.len() { + for pred in &tmp[cnt].1 { + if !known_preds.contains(pred) { + // Don't know this predicate, keep going in `tmp`. + cnt += 1; + continue 'find_preds; + } + } + // Reachable only we already have all of the current pred's + // dependencies. + let (pred, _) = tmp.swap_remove(cnt); + self.sorted_pred_terms.push(pred); + known_preds.insert(pred); + () // No `cnt` increment after swap remove. + } + } + + for pred in &self.preds { + let mut map = VarMap::with_capacity(pred.sig.len()); + for (var, typ) in pred.sig.index_iter() { + map.push(term::var(pred.original_sig_map()[var], typ.clone())) + } + self.old_var_maps.push(map) + } + + self.sorted_pred_terms.shrink_to_fit(); + + Ok(()) + } + + /// Returns the term we already know works for a predicate, if any. + pub fn forced_terms_of(&self, pred: PrdIdx) -> Option<&TTerms> { + self.pred_terms[pred].as_ref() + } + + /// If the input predicate is forced to a constant boolean, returns its + /// value. + pub fn bool_value_of(&self, pred: PrdIdx) -> Option { + self.forced_terms_of(pred).and_then(|terms| terms.bool()) + } + + /// Forced predicates in topological order. + #[inline] + pub fn sorted_forced_terms(&self) -> &Vec { + &self.sorted_pred_terms + } + + /// Returns the clauses in which the predicate appears in the lhs and rhs + /// respectively. + #[inline] + pub fn clauses_of(&self, pred: PrdIdx) -> (&ClsSet, &ClsSet) { + (&self.pred_to_clauses[pred].0, &self.pred_to_clauses[pred].1) + } + /// Returns the clauses in which `pred` appears in the lhs. + #[inline] + pub fn lhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { + &self.pred_to_clauses[pred].0 + } + /// Returns the clauses in which `pred` appears in the rhs. + #[inline] + pub fn rhs_clauses_of(&self, pred: PrdIdx) -> &ClsSet { + &self.pred_to_clauses[pred].1 + } + + /// Adds a predicate application to a clause's lhs. + pub fn clause_add_lhs_pred(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarMap) { + self.pred_to_clauses[pred].0.insert(clause); + self.clauses[clause].insert_pred_app(pred, args.into()); + } + + /// Adds a term to a clause's lhs. + pub fn clause_add_lhs_term(&mut self, clause: ClsIdx, term: Term) { + self.clauses[clause].insert_term(term); + } + + /// Forces the rhs of a clause to a predicate application. + pub fn clause_force_rhs( + &mut self, + clause: ClsIdx, + pred: PrdIdx, + args: VarMap, + ) -> Res<()> { + self.pred_to_clauses[pred].1.insert(clause); + self.clauses[clause].set_rhs(pred, args.into()) + } + + /// Adds some terms to the lhs of a clause. + /// + /// Updates `pred_to_clauses`. + pub fn clause_lhs_extend>( + &mut self, + clause_idx: ClsIdx, + tterms: I, + ) { + let clause = &mut self.clauses[clause_idx]; + for tterm in tterms { + match tterm { + TTerm::P { pred, args } => { + self.pred_to_clauses[pred].0.insert(clause_idx); + clause.insert_pred_app(pred, args); + } + TTerm::T(term) => { + clause.insert_term(term); + } + } + } + } + + /// Replaces the rhs of a clause. + /// + /// Updates `pred_to_clauses` for the term it inserts but **not** the one it + /// removes. + pub fn clause_rhs_force(&mut self, clause_idx: ClsIdx, tterm: TTerm) -> Res<()> { + let clause = &mut self.clauses[clause_idx]; + match tterm { + TTerm::P { pred, args } => { + clause.set_rhs(pred, args)?; + let is_new = self.pred_to_clauses[pred].1.insert(clause_idx); + debug_assert!(is_new) + } + TTerm::T(term) => { + if term.bool() != Some(false) { + clause.insert_term(term::not(term)); + } + clause.unset_rhs(); + } + } + Ok(()) + } + + /// Range over the predicate indices. + pub fn pred_indices(&self) -> PrdRange { + PrdRange::zero_to(self.preds.len()) + } + /// Range over the clause indices. + pub fn clause_indices(&self) -> ClsRange { + ClsRange::zero_to(self.clauses.len()) + } + + /// Predicate accessor. + pub fn preds(&self) -> &Preds { + &self.preds + } + + /// Clause accessor. + pub fn clauses(&self) -> &ClsMap { + &self.clauses + } + + /// Pushes a new predicate and returns its index. + pub fn push_pred(&mut self, name: String, sig: Sig) -> PrdIdx { + let idx = self.preds.next_index(); + + self.preds.push(Pred::new(name, idx, sig)); + + self.pred_terms.push(None); + self.pred_str.push(None); + + self.pred_to_clauses + .push((ClsSet::with_capacity(17), ClsSet::with_capacity(17))); + idx + } + + /// Sets the strengthener for a predicate. + pub fn set_str(&mut self, pred: PrdIdx, term: Term) -> Option { + ::std::mem::replace(&mut self.pred_str[pred], Some(term)) + } + + /// Retrieves the strengthener for a predicate if any. + pub fn get_str(&self, pred: PrdIdx) -> Option<&Term> { + self.pred_str[pred].as_ref() + } + + /// Adds a companion function for a predicate. + pub fn add_companion_fun(&mut self, pred: PrdIdx, fun: Fun) { + self.companion_funs + .entry(pred) + .or_insert_with(Vec::new) + .push(fun) + } + + /// Retrieves the companion functions for a predicate. + pub fn get_companion_funs(&self, pred: PrdIdx) -> Option<&Vec> { + self.companion_funs.get(&pred) + } + + /// Removes and returns the indices of the clauses `pred` appears in the lhs + /// of from `self.pred_to_clauses`. + fn unlink_pred_lhs(&mut self, pred: PrdIdx, lhs: &mut LHS) + where + LHS: ::std::iter::Extend, + { + lhs.extend(self.pred_to_clauses[pred].0.drain()) + } + + /// Removes and returns the indices of the clauses `pred` appears in the rhs + /// of from `self.pred_to_clauses`. + fn unlink_pred_rhs(&mut self, pred: PrdIdx, rhs: &mut RHS) + where + RHS: ::std::iter::Extend, + { + rhs.extend(self.pred_to_clauses[pred].1.drain()) + } + + /// Goes trough the predicates in `from`, and updates `pred_to_clauses` so + /// that they point to `to` instead. + fn relink_preds_to_clauses(&mut self, from: ClsIdx, to: ClsIdx) -> Res<()> { + for pred in self.clauses[from].lhs_preds().keys() { + let pred = *pred; + let was_there = self.pred_to_clauses[pred].0.remove(&from); + let _ = self.pred_to_clauses[pred].0.insert(to); + debug_assert!(was_there) + } + if let Some((pred, _)) = self.clauses[from].rhs() { + let was_there = self.pred_to_clauses[pred].1.remove(&from); + let _ = self.pred_to_clauses[pred].1.insert(to); + debug_assert!(was_there) + } + Ok(()) + } + + /// Forget some clauses. + /// + /// Duplicates are handled as if there was only one. + pub fn forget_clauses(&mut self, clauses: &mut Vec) -> Res<()> { + // Forgetting is done by swap remove, we sort in DESCENDING order so that + // indices always make sense. + clauses.sort_unstable_by(|c_1, c_2| c_2.cmp(c_1)); + let mut prev = None; + for clause in clauses.drain(0..) { + log!{ @6 "forgetting {}", self[clause].to_string_info(&self.preds).unwrap() } + if prev == Some(clause) { + continue; + } + prev = Some(clause); + let _ = self.forget_clause(clause)?; + } + Ok(()) + } + + /// Forget a clause. **Does not preserve the order of the clauses.** + /// + /// After calling this function, clause indices kept outside of the instance + /// will in general refer to clauses different from the ones they pointed to + /// before the call. + /// + /// Also unlinks predicates from `pred_to_clauses`. + pub fn forget_clause(&mut self, clause: ClsIdx) -> Res { + for pred in self.clauses[clause].lhs_preds().keys() { + let pred = *pred; + let was_there = self.pred_to_clauses[pred].0.remove(&clause); + debug_assert!(was_there || self.is_known(pred)) + } + if let Some((pred, _)) = self.clauses[clause].rhs() { + self.pred_to_clauses[pred].1.remove(&clause); + } + // Relink the last clause as its index is going to be `clause`. Except if + // `clause` is already the last one. + let last_clause: ClsIdx = (self.clauses.len() - 1).into(); + if clause != last_clause { + self.relink_preds_to_clauses(last_clause, clause)? + } + let res = self.clauses.swap_remove(clause); + Ok(res) + } + + /// First free clause index. + pub fn next_clause_index(&self) -> ClsIdx { + self.clauses.next_index() + } + + /// Pushes a new clause. + pub fn push_new_clause( + &mut self, + vars: VarInfos, + lhs: Vec, + rhs: Option, + info: &'static str, + ) -> Res> { + let idx = self.clauses.next_index(); + let clause = clause::new(vars, lhs, rhs, info, idx); + self.push_clause(clause) + } + + /// The name of an original clause if any. + pub fn name_of_old_clause(&self, cls: ClsIdx) -> Option<&String> { + self.old_names.get(&cls) + } + + /// Sets the name for an original clause. + pub fn set_old_clause_name(&mut self, cls: ClsIdx, name: String) -> Res<()> { + let prev = self.old_names.insert(cls, name); + if let Some(prev) = prev { + bail!(format!( + "trying to name clause #{}, but it's already called {}", + cls, + conf.bad(&prev) + )) + } + Ok(()) + } + + /// Mutable accessor for side clauses. + /// + /// Does not expose function invariants. + pub fn side_clauses_retain(&mut self, mut keep: Keep) -> Res + where + Keep: FnMut(&mut Clause) -> Res, + { + let mut info = RedInfo::new(); + let mut cnt = 0; + while cnt < self.side_clauses.len() { + if !keep(&mut self.side_clauses[cnt])? { + info.clauses_rmed += 1; + self.side_clauses.swap_remove(cnt); + () + } else { + cnt += 1 + } + } + Ok(info) + } + + /// Asserts all the side-clauses in a solver. + pub fn assert_side_clauses

(&self, solver: &mut Solver

) -> Res<()> { + for side_clause in &self.side_clauses { + let side_clause = smt::SmtSideClause::new(side_clause); + solver.assert(&side_clause)? + } + Ok(()) + } + + /// Registers a clause as a side-clause. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_side_clause(&mut self, clause: Clause) -> Res<()> { + if clause.rhs().is_some() { + bail!("cannot convert to side-clause: predicate application in rhs") + } + if !clause.lhs_preds().is_empty() { + bail!("cannot convert to side-clause: predicate application(s) in lhs") + } + + self.side_clauses.push(clause); + + Ok(()) + } + + /// Registers a new side clause: forces the term to be true. + /// + /// A side clause is a clause that does not mention any predicate, but + /// mentions a user-defined function. + pub fn add_new_side_clause( + &mut self, + vars: VarInfos, + term: Term, + info: &'static str, + ) -> Res<()> { + let clause = clause::new( + vars, + vec![TTerm::T(term::not(term))], + None, + info, + self.side_clauses.len().into(), + ); + + self.add_side_clause(clause) + } + + /// Pushes a clause. + /// + /// Returns its index, if it was added. + pub fn push_clause(&mut self, clause: Clause) -> Res> { + for term in clause.lhs_terms() { + if let Some(false) = term.bool() { + return Ok(None); + } + } + + if clause.lhs_preds().is_empty() && clause.rhs().is_none() && clause + .lhs_terms() + .iter() + .any(|term| term.has_fun_app_or_adt()) + { + self.add_side_clause(clause)?; + return Ok(None); + } + + let idx = self.clauses.next_index(); + let is_new = self.push_clause_unchecked(clause); + // self.check("after `push_clause`") ? ; + Ok(if is_new { Some(idx) } else { None }) + } + + /// True if the clause is redundant. + pub fn is_redundant(&self, idx: ClsIdx) -> bool { + let clause = &self.clauses[idx]; + + if let Some((pred, _)) = clause.rhs() { + for i in &self.pred_to_clauses[pred].1 { + if *i != idx && self[*i].same_as(&clause) { + return true; + } + } + } else if let Some((pred, _)) = clause.lhs_preds().iter().next() { + for i in &self.pred_to_clauses[*pred].0 { + if *i != idx && self[*i].same_as(&clause) { + return true; + } + } + } else { + for (i, c) in self.clauses.index_iter() { + if i != idx && c.same_as(&clause) { + return true; + } + } + } + false + } + + /// Pushes a new clause, does not sanity-check but redundancy-checks. + fn push_clause_unchecked(&mut self, clause: Clause) -> bool { + let clause_index = self.clauses.next_index(); + self.clauses.push(clause); + + if self.is_redundant(clause_index) { + self.clauses.pop(); + return false; + } + + for pred in self.clauses[clause_index].lhs_preds().keys() { + let pred = *pred; + let is_new = self.pred_to_clauses[pred].0.insert(clause_index); + debug_assert!(is_new) + } + if let Some((pred, _)) = self.clauses[clause_index].rhs() { + let is_new = self.pred_to_clauses[pred].1.insert(clause_index); + debug_assert!(is_new) + } + true + } + + /// Checks that the instance has no inconsistencies. + /// + /// Only active in debug. + #[cfg(not(debug_assertions))] + #[inline(always)] + pub fn check(&self, _: &'static str) -> Res<()> { + Ok(()) + } + + #[cfg(debug_assertions)] + pub fn check(&self, s: &'static str) -> Res<()> { + for clause in &self.clauses { + clause.check(s)? + } + self.check_pred_to_clauses() + .chain_err(|| format!("while checking `{}`", conf.sad("pred_to_clauses"))) + .chain_err(|| format!("instance consistency check failed: {}", conf.emph(s)))?; + self.check_preds_consistency()?; + + for (idx, clause) in self.clauses.index_iter() { + for term in clause.lhs_terms() { + if let Some(false) = term.bool() { + bail!( + "({}) found a trivial clause: #{} {}", + s, + idx, + clause.to_string_info(&self.preds).unwrap() + ) + } + } + + for pred in clause + .lhs_preds() + .iter() + .map(|(pred, _)| *pred) + .chain(clause.rhs().into_iter().map(|(pred, _)| pred)) + { + if let Some(tterms) = self.forced_terms_of(pred) { + bail! { + "predicate {} is forced{} but appears in a clause: {}", + conf.bad( & self[pred].name ), + match tterms.bool() { + Some(true) => " to true", + Some(false) => " to false", + None => "", + }, + s + } + } + } + } + + scoped! { + let mut clauses = self.clauses.iter() ; + while let Some(clause) = clauses.next() { + for c in clauses.clone() { + if clause.same_as(c) { + bail!( + "{}\n\n{}\n\nsame clause appears multiple times\n{}", + clause.to_string_info(&self.preds).unwrap(), + c.to_string_info(&self.preds).unwrap(), + conf.bad(s) + ) + } + } + } + } + + Ok(()) + } + + /// Checks predicate information. + #[cfg(debug_assertions)] + fn check_preds_consistency(&self) -> Res<()> { + for pred in &self.preds { + pred.check()? + } + Ok(()) + } + + /// Pretty printer for a set of clauses. + #[cfg(debug_assertions)] + fn pretty_clauses(&self, clauses: &ClsSet) -> String { + let mut s = String::new(); + s.push('{'); + for clause in clauses { + s.push(' '); + s.push_str(&format!("{}", clause)) + } + s.push(' '); + s.push('}'); + s + } + + /// Checks the consistency of `pred_to_clauses`. + #[cfg(debug_assertions)] + fn check_pred_to_clauses(&self) -> Res<()> { + for (cls_idx, clause) in self.clauses.index_iter() { + for (pred, _) in clause.lhs_preds() { + let pred = *pred; + if self.is_known(pred) { + bail!( + "predicate {} is forced but appears in lhs of clause {}", + self[pred], + cls_idx + ) + } + if !self.pred_to_clauses[pred].0.contains(&cls_idx) { + bail!( + "predicate {} appears in lhs of clause {} \ + but is not registered as such\n{}\nlhs: {}\nrhs: {}", + self[pred], + cls_idx, + self.clauses[cls_idx].to_string_info(&self.preds)?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + if let Some((pred, _)) = clause.rhs() { + if self.is_known(pred) { + bail!( + "predicate {} is forced but appears in rhs of clause {}", + self[pred], + cls_idx + ) + } + if !self.pred_to_clauses[pred].1.contains(&cls_idx) { + bail!( + "predicate {} appears in rhs of clause {} \ + but is not registered as such\n{}\nlhs: {}\nrhs: {}", + self[pred], + cls_idx, + self.clauses[cls_idx].to_string_info(&self.preds)?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + } + + for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { + for clause in lhs { + if *clause >= self.clauses.len() { + bail!( + "predicate {} is registered as appearing in lhs of clause {} \ + which is above the maximal clause index", + self[pred], + clause + ) + } + if self.clauses[*clause].lhs_preds().get(&pred).is_none() { + bail!( + "predicate {} is registered as appearing in lhs of clause {} \ + but it's not the case\n{}\nlhs: {}\nrhs: {}", + self[pred], + clause, + self.clauses[*clause].to_string_info(&self.preds)?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + for clause in rhs { + if *clause >= self.clauses.len() { + bail!( + "predicate {} is registered as appearing in rhs of clause {} \ + which is above the maximal clause index", + self[pred], + clause + ) + } + if let Some((this_pred, _)) = self.clauses[*clause].rhs() { + if this_pred == pred { + continue; + } + } + bail!( + "predicate {} is registered to appear in rhs of clause {} \ + but it's not the case\n{}\nlhs: {}\nrhs: {}", + self[pred], + clause, + self.clauses[*clause].to_string_info(&self.preds)?, + self.pretty_clauses(&self.pred_to_clauses[pred].0), + self.pretty_clauses(&self.pred_to_clauses[pred].1) + ) + } + } + Ok(()) + } + + /// Dumps the instance as an SMT-LIB 2 problem. + pub fn dump_as_smt2(&self, w: &mut File, blah: Blah) -> Res<()> + where + File: Write, + Blah: AsRef, + { + let blah = blah.as_ref(); + + for line in blah.lines() { + writeln!(w, "; {}", line)? + } + writeln!(w)?; + writeln!(w, "(set-logic HORN)")?; + writeln!(w)?; + + writeln!(w, "; Datatypes")?; + + dtyp::write_all(w, "")?; + + dtyp::write_constructor_map(w, "; ")?; + writeln!(w)?; + + writeln!(w, "; Functions")?; + fun::write_all(w, "", true)?; + + writeln!(w)?; + + writeln!(w, "; Side-clauses")?; + for side_clause in &self.side_clauses { + side_clause.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |_, _, _, _| panic!("illegal side-clause: found predicate application(s)"), + true, + )?; + writeln!(w)?; + } + + writeln!(w)?; + writeln!(w)?; + + for (pred_idx, pred) in self.preds.index_iter() { + if self.pred_terms[pred_idx].is_none() { + if let Some(term) = &self.pred_str[pred_idx] { + writeln!(w, "; Strengthening term:")?; + writeln!(w, "; {}", term)? + } + write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; + for typ in &pred.sig { + write!(w, " {}", typ)? + } + writeln!(w, " ) Bool\n)")? + } + } + + writeln!( + w, + "\n; Original clauses' names ({}) {{", + self.old_names.len() + )?; + for (idx, name) in &self.old_names { + writeln!(w, "; #{}: `{}`.", idx, name)? + } + writeln!(w, "; }}")?; + + for (idx, clause) in self.clauses.index_iter() { + writeln!(w, "\n; Clause #{}", idx)?; + + // Print source. + let from = clause.from(); + write!(w, "; from: #{}", from)?; + if let Some(name) = self.old_names.get(&from) { + write!(w, ": {}", name)? + } + writeln!(w)?; + + clause.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |w, p, args, bindings| { + if !args.is_empty() { + write!(w, "(")? + } + w.write_all(self[p].name.as_bytes())?; + for arg in args.iter() { + write!(w, " ")?; + arg.write_with(w, |w, var| write!(w, "{}", clause.vars[var]), bindings)? + } + if !args.is_empty() { + write!(w, ")") + } else { + Ok(()) + } + }, + true, + )?; + writeln!(w)?; + writeln!(w)? + } + + writeln!(w, "\n(check-sat)")?; + + Ok(()) + } + + /// Simplifies some predicate definitions. + /// + /// Simplifies its internal predicate definitions and the ones in the model. + pub fn simplify_pred_defs(&mut self, model: &mut Model) -> Res<()> { + let mut old_model = Vec::with_capacity(model.len()); + ::std::mem::swap(&mut old_model, model); + for (pred, def) in old_model { + let simplified = def.simplify_pred_apps(&model, &self.pred_terms); + model.push((pred, simplified)) + } + + if self.sorted_pred_terms.is_empty() { + self.finalize()? + } + + let mut old_tterms: PrdMap> = vec![None; self.pred_terms.len()].into(); + ::std::mem::swap(&mut old_tterms, &mut self.pred_terms); + for pred in &self.sorted_pred_terms { + let mut curr_def = None; + ::std::mem::swap(&mut curr_def, &mut old_tterms[*pred]); + if let Some(def) = curr_def { + let simplified = def.simplify_pred_apps(&model, &self.pred_terms); + self.pred_terms[*pred] = Some(simplified) + } + } + Ok(()) + } + + /// Writes a conjunction of top terms. + pub fn write_tterms_conj(&self, w: &mut W, conj: &[TTerms]) -> Res<()> { + if conj.is_empty() { + write!(w, "true")? + } else if conj.len() == 1 { + self.print_tterms_as_model(w, &conj[0])? + } else { + write!(w, "(and")?; + for tterms in conj { + write!(w, " ")?; + self.print_tterms_as_model(w, tterms)? + } + write!(w, ")")? + } + Ok(()) + } + + /// Writes a predicate signature. + /// + /// Does not write the name of the predicate. + pub fn write_pred_sig(&self, w: &mut W, pred: PrdIdx) -> Res<()> { + write!(w, "(")?; + for (var, typ) in self[pred].original_sig().index_iter() { + write!(w, " ({} {})", var.default_str(), typ)? + } + write!(w, " ) {}", typ::bool())?; + Ok(()) + } + + /// Writes some definitions. + pub fn write_definitions( + &self, + w: &mut W, + pref: &str, + model: ConjModelRef, + ) -> Res<()> { + fun::write_all(w, pref, false)?; + + for defs in model { + if defs.is_empty() { + () + } else if defs.len() == 1 { + let (pred, ref tterms) = defs[0]; + + writeln!(w, "{}({} {}", pref, keywords::cmd::def_fun, self[pred].name)?; + write!(w, "{} ", pref)?; + self.write_pred_sig(w, pred)?; + write!(w, "\n{} ", pref)?; + self.write_tterms_conj(w, tterms)?; + writeln!(w, "\n{})", pref)? + } else { + write!(w, "{}({} (", pref, keywords::cmd::def_funs_rec)?; + for &(pred, _) in defs { + write!(w, "\n{} {} ", pref, self[pred].name)?; + self.write_pred_sig(w, pred)?; + } + write!(w, "\n{}) (", pref)?; + for &(_, ref tterms) in defs { + write!(w, "\n{} ", pref)?; + self.write_tterms_conj(w, tterms)?; + } + writeln!(w, "\n{}) )", pref)?; + } + } + + Ok(()) + } + + /// Writes a model. + pub fn write_model(&self, model: ConjModelRef, w: &mut W) -> Res<()> { + writeln!(w, "(model")?; + self.write_definitions(w, " ", model)?; + writeln!(w, ")")?; + Ok(()) + } + + /// Sets print-success flag. + pub fn set_print_success(&mut self, b: bool) { + self.print_success = b + } + /// Print-success flag accessor. + pub fn print_success(&self) -> bool { + self.print_success + } + /// Sets unsat-cores flag. + pub fn set_unsat_cores(&mut self, b: bool) { + self.unsat_cores = b + } + /// Unsat-cores flag. + pub fn unsat_cores(&self) -> bool { + self.unsat_cores + } + /// Sets proofs flag. + pub fn set_proofs(&mut self, b: bool) { + self.proofs = b + } + /// Unsat-cores flag. + pub fn proofs(&self) -> bool { + self.proofs + } + + /// True if the teacher needs to maintain a sample graph (unsat + /// cores/proofs). + pub fn track_samples(&self) -> bool { + self.unsat_cores() || self.proofs() + } + + /// Converts `"true"` to `true`, `"false"` to `false`, and everything else to + /// an error. + fn bool_of_str(s: &str) -> Res { + match s { + "true" => Ok(true), + "false" => Ok(false), + _ => bail!("expected boolean `true/false`, got `{}`", s), + } + } + + /// Sets an option. + pub fn set_option(&mut self, flag: &str, val: &str) -> Res<()> { + let flag_err = || format!("while handling set-option for {}", flag); + match flag { + "print-success" => { + let print_succ = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_print_success(print_succ) + } + "produce-unsat-cores" => { + let unsat_cores = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_unsat_cores(unsat_cores) + } + "produce-proofs" => { + let proofs = Self::bool_of_str(&val).chain_err(flag_err)?; + self.set_proofs(proofs) + } + _ => warn!( + "ignoring (set-option :{} {}): unknown flag {}", + flag, val, flag + ), + } + Ok(()) + } +} + +/// Lhs part of a cex. +type CexLhs = Vec<(PrdIdx, VarTermsSet)>; +/// Lhs part of a cex, reference version. +type CexLhsRef<'a> = &'a [(PrdIdx, VarTermsSet)]; +/// Rhs part of a cex. +type CexRhs<'a> = Option<(PrdIdx, &'a VarTerms)>; + +/// Cex-related functions. +impl Instance { + /// Retrieves the lhs and rhs cex part from a bias. + fn break_cex(&self, clause_idx: ClsIdx, bias: Bias) -> (CexLhs, CexRhs) { + let clause = &self[clause_idx]; + match bias { + // Consider the whole lhs of the clause positive. + Bias::Lft => (vec![], clause.rhs()), + + // Consider the rhs of the clause negative. + Bias::Rgt => ( + clause + .lhs_preds() + .iter() + .map(|(pred, argss)| (*pred, argss.clone())) + .collect(), + None, + ), + + // Consider the rhs of the clause negative, and all lhs applications + // positive except this one. + Bias::NuRgt(pred, args) => { + use var_to::terms::VarTermsSet; + debug_assert! { clause.lhs_preds().get(& pred).is_some() } + debug_assert! { + clause.lhs_preds().get(& pred).unwrap().contains(& args) + } + let mut argss = VarTermsSet::with_capacity(1); + argss.insert(args); + (vec![(pred, argss)], None) + } + + // No bias. + Bias::Non => ( + clause + .lhs_preds() + .iter() + .map(|(pred, argss)| (*pred, argss.clone())) + .collect(), + clause.rhs(), + ), + } + } + + /// Forces non-values in the cex if needed. + pub fn force_non_values(&self, cex: &mut Cex, lhs: CexLhsRef, rhs: &CexRhs) { + // Factored set of variables when fixing cex for arguments. + let mut known_vars = VarSet::new(); + + macro_rules! fix_cex { + ($args:expr) => {{ + log! { @6 "fixing {}", $args } + for arg in $args.iter() { + if let Some(var) = arg.var_idx() { + if !cex[var].is_known() { + // Value for `var` is a non-value. + let is_new = known_vars.insert(var); + // Variable appears in more than one arg, force its value. + if !is_new { + cex[var] = cex[var].typ().default_val() + } + } + } else { + for var in term::vars(arg) { + if !cex[var].is_known() { + cex[var] = cex[var].typ().default_val() + } + } + } + } + }}; + } + + // Force non-values in the cex if we're dealing with a constraint, not a + // sample. + if ( + // Positive sample? + lhs.is_empty() && rhs.is_some() + ) || ( + // Negative sample? + lhs.len() == 1 && lhs.iter().all( + |(_, argss)| argss.len() == 1 + ) && rhs.is_none() + ) { + // We're generating a sample. Still need to force variables that appear + // more than once in arguments. + for (_, argss) in lhs { + debug_assert_eq! { argss.len(), 1 } + for args in argss { + fix_cex!(args) + } + } + if let Some((_, args)) = rhs.as_ref() { + fix_cex!(args) + } + } else { + // We're dealing with a constraint, not a sample. Force non-values. + for val in cex.iter_mut() { + if !val.is_known() { + *val = val.typ().default_val() + } + } + } + } + + /// Transforms a cex for a clause into some learning data. + /// + /// Returns `true` if some new data was generated. + pub fn clause_cex_to_data(&self, data: &mut Data, clause_idx: ClsIdx, cex: BCex) -> Res { + let (mut cex, bias) = cex; + + if_log! { @6 + let clause = & self[clause_idx] ; + let mut s = String::new() ; + for (var, val) in cex.index_iter() { + s.push_str(& format!("{}: {}, ", var.default_str(), val)) + } + log! { @6 + "lhs preds: {}", clause.lhs_pred_apps_len() ; + " rhs: {}", if clause.rhs().is_some() { "some" } else { "none" } ; + " bias: {}", bias ; + " cex: {}", s + } + } + + let (lhs, rhs) = self.break_cex(clause_idx, bias); + self.force_non_values(&mut cex, &lhs, &rhs); + + // Evaluates some arguments for a predicate. + macro_rules! eval { + ($args:expr) => {{ + use var_to::vals::RVarVals; + let mut sample = RVarVals::with_capacity($args.len()); + for arg in $args.get() { + let val = arg.eval(&cex)?; + sample.push(val) + } + sample + }}; + } + + // Evaluate antecedents. + let mut antecedents = vec![]; + for (pred, argss) in lhs { + for args in argss { + let sample = eval!(args); + antecedents.push((pred, sample)) + } + } + + let consequent = if let Some((pred, args)) = rhs { + let sample = eval!(args); + Some((pred, sample)) + } else { + None + }; + + if_log! { @6 + let mut s = String::new() ; + if ! antecedents.is_empty() { + for (pred, sample) in & antecedents { + s.push_str( & format!("({} {}) ", self[* pred], sample) ) + } + } else { + s.push_str("true ") + } + s.push_str("=> ") ; + if let Some((pred, sample)) = consequent.as_ref() { + s.push_str( & format!("({} {})", self[* pred], sample) ) + } else { + s.push_str("false") + } + log! { @6 "{}", s } + } + + data.add_data(clause_idx, antecedents, consequent) + } + + /// Turns some teacher counterexamples into learning data. + pub fn cexs_to_data(&self, data: &mut Data, cexs: Cexs) -> Res { + let mut changed = false; + + for (clause_idx, cexs) in cexs { + log! { @5 "adding cexs for #{}", clause_idx } + + for cex in cexs { + let new_stuff = self.clause_cex_to_data(data, clause_idx, cex)?; + changed = changed || new_stuff + } + } + + let (pos, neg) = data.propagate()?; + + Ok(changed || pos > 0 || neg > 0) + } +} + +impl ::std::ops::Index for Instance { + type Output = Pred; + fn index(&self, index: PrdIdx) -> &Pred { + &self.preds[index] + } +} +impl ::std::ops::Index for Instance { + type Output = Clause; + fn index(&self, index: ClsIdx) -> &Clause { + &self.clauses[index] + } +} +impl ::std::ops::IndexMut for Instance { + fn index_mut(&mut self, index: ClsIdx) -> &mut Clause { + &mut self.clauses[index] + } +} +impl AsRef for Instance { + fn as_ref(&self) -> &Self { + self + } +} +impl AsMut for Instance { + fn as_mut(&mut self) -> &mut Self { + self + } +} +// impl ::std::ops::Deref for Instance { +// type Target = Self ; +// fn deref(& self) -> & Self { +// self +// } +// } +// impl ::std::ops::DerefMut for Instance { +// fn deref_mut(& mut self) -> & mut Self { +// self +// } +// } + +impl<'a> PebcakFmt<'a> for Clause { + type Info = &'a Preds; + fn pebcak_err(&self) -> ErrorKind { + "during clause pebcak formatting".into() + } + fn pebcak_io_fmt(&self, w: &mut W, preds: &'a Preds) -> IoRes<()> { + self.write( + w, + |w, var_info| write!(w, "{}", var_info.name), + |w, prd, args, bindings| { + write!(w, "(")?; + w.write_all(preds[prd].name.as_bytes())?; + for arg in args.iter() { + write!(w, " ")?; + arg.write_with(w, |w, var| write!(w, "{}", self.vars[var]), bindings)? + } + write!(w, ")") + }, + false, + ) + } +} + +impl<'a> PebcakFmt<'a> for Instance { + type Info = (); + fn pebcak_err(&self) -> ErrorKind { + "during instance pebcak formatting".into() + } + fn pebcak_io_fmt(&self, w: &mut W, _: ()) -> IoRes<()> { + writeln!(w, "; Datatypes:")?; + + dtyp::write_all(w, "")?; + + dtyp::write_constructor_map(w, "; ")?; + + for pred in &self.preds { + if pred.tterm.is_none() { + write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; + for typ in &pred.sig { + write!(w, " {}", typ)? + } + writeln!(w, " ) Bool\n)")?; + if pred.sig.len() != pred.original_sig().len() { + write!(w, "; original signature:\n;")?; + for (var, typ) in pred.original_sig().index_iter() { + write!(w, " ({} {})", var.default_str(), typ)? + } + writeln!(w, "\n; variable map (new -> old):\n;")?; + for (src, tgt) in pred.original_sig_map().index_iter() { + write!(w, " {} -> {},", src.default_str(), tgt.default_str())? + } + writeln!(w)? + } + } + } + + let empty_prd_set = PrdSet::new(); + if self.sorted_pred_terms.is_empty() { + // Either there's no forced predicate, or we are printing before + // finalizing. + for (pred, tterms) in self + .pred_terms + .index_iter() + .filter_map(|(pred, tterms_opt)| tterms_opt.as_ref().map(|tt| (pred, tt))) + { + write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred])?; + for (var, typ) in self[pred].sig.index_iter() { + write!(w, " (v_{} {})", var, typ)? + } + write!(w, " ) Bool\n ")?; + tterms + .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) + .unwrap(); + writeln!(w, "\n)")? + } + } else { + for pred in &self.sorted_pred_terms { + write!(w, "({} {}\n (", keywords::cmd::def_fun, self[*pred])?; + for (var, typ) in self[*pred].sig.index_iter() { + write!(w, " (v_{} {})", var, typ)? + } + let tterms = self.pred_terms[*pred].as_ref().unwrap(); + write!(w, " ) Bool\n ")?; + tterms + .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) + .unwrap(); + writeln!(w, "\n)",)? + } + } + + writeln!( + w, + "\n; Original clauses' names ({}) {{", + self.old_names.len() + )?; + for (idx, name) in &self.old_names { + writeln!(w, "; Original clause #{} is called `{}`.", idx, name)? + } + writeln!(w, "; }}")?; + + for (idx, clause) in self.clauses.index_iter() { + writeln!(w, "\n; Clause #{}", idx)?; + let from = clause.from(); + write!(w, "; from: #{}", from)?; + if let Some(name) = self.old_names.get(&from) { + write!(w, ": {}", name)? + } + writeln!(w)?; + clause.pebcak_io_fmt(w, &self.preds)? + } + + writeln!(w, "\npred to clauses:")?; + for (pred, &(ref lhs, ref rhs)) in self.pred_to_clauses.index_iter() { + write!(w, " {}: lhs {{", self[pred])?; + for lhs in lhs { + write!(w, " {}", lhs)? + } + write!(w, " }}, rhs {{")?; + for rhs in rhs { + write!(w, " {}", rhs)? + } + writeln!(w, " }}")? + } + + Ok(()) + } +} diff --git a/src/instance/instance/pre_instance.rs b/src/instance/pre_instance.rs similarity index 94% rename from src/instance/instance/pre_instance.rs rename to src/instance/pre_instance.rs index e11b2284..24a7b8dc 100644 --- a/src/instance/instance/pre_instance.rs +++ b/src/instance/pre_instance.rs @@ -1,11 +1,10 @@ -//! A pre-instance wraps an instance and provides functionalities used by the -//! pre-processing. +//! A pre-instance wraps an instance for preprocessing. use common::{ smt::{ClauseTrivialExt, SmtImpl}, *, }; -use instance::{preproc::utils::ExtractionCxt, Clause}; +use preproc::utils::ExtractionCxt; /// Performs a checksat. macro_rules! check_sat { @@ -28,7 +27,13 @@ macro_rules! check_sat { }}; } -/// Wraps an instance for pre-processing. +/// Wraps an instance for preprocessing. +/// +/// Provides high-level functions to manipulate predicates and clauses. The reason for this wrapper +/// is that an [`Instance`](../common/struct.Instance.html) should almost never be mutated, except +/// during creation and preprocessing. +/// +/// This is why the instance type barely exposes any way to mutate its values. pub struct PreInstance<'a> { /// The instance wrapped. instance: &'a mut Instance, @@ -687,11 +692,9 @@ impl<'a> PreInstance<'a> { if let Some(res) = res? { Ok(res) } else { - log_debug!{ - "unsat because of {}", - self.instance[clause_idx].to_string_info( - self.instance.preds() - ) ? + log!{ @3 + "unsat because of {}", + self.instance[clause_idx].to_string_info(self.instance.preds()).unwrap() } bail!(ErrorKind::UnsatFrom(clause_idx)) } @@ -980,17 +983,15 @@ impl<'a> PreInstance<'a> { debug_assert! { self.clauses_to_simplify.is_empty() } self.instance .unlink_pred_lhs(pred, &mut self.clauses_to_simplify); - log! { @4 + log! { @4 | "updating lhs clauses ({})", self.clauses_to_simplify.len() } for clause in &self.clauses_to_simplify { let clause = *clause; log! { @4 - "- working on lhs of clause {}", - self.instance[clause].to_string_info( - self.instance.preds() - ).unwrap() + "- working on lhs of clause {}", + self.instance[clause].to_string_info(self.instance.preds()).unwrap() } let argss = if let Some(argss) = self.instance.clauses[clause].drop_lhs_pred(pred) { @@ -1063,10 +1064,7 @@ impl<'a> PreInstance<'a> { /// # Used by /// /// - sub instance generation, when splitting on one clause - pub fn extend_pred_left( - &mut self, - preds: &PrdHMap<::instance::preproc::PredExtension>, - ) -> Res { + pub fn extend_pred_left(&mut self, preds: &PrdHMap<::preproc::PredExtension>) -> Res { self.check("before `extend_pred_left`")?; // let mut tterm_set = TTermSet::new() ; @@ -1097,11 +1095,9 @@ impl<'a> PreInstance<'a> { continue 'clause_iter; } - log! { @4 - "- working on lhs of clause {}", - self[clause].to_string_info( - self.preds() - ).unwrap() + log! { @4 | + "- working on lhs of clause {}", + self[clause].to_string_info(self.preds()).unwrap() } for (pred, &(ref terms, ref quantified)) in preds { @@ -1175,9 +1171,9 @@ impl<'a> PreInstance<'a> { self.check("before `force_dnf_left`")?; - log! { @6 - "force_dnf_left {} ({} defs)", self[pred], def.len() ; - "unlinking rhs" + log! { @6 | + "force_dnf_left {} ({} defs)", self[pred], def.len() ; + "unlinking rhs" } // Make sure there's no rhs clause for `pred`. @@ -1191,7 +1187,7 @@ impl<'a> PreInstance<'a> { ) } - log! { @6 "unlinking lhs" } + log! { @6 | "unlinking lhs" } // Update lhs clauses. debug_assert! { self.clauses_to_simplify.is_empty() } @@ -1206,7 +1202,7 @@ impl<'a> PreInstance<'a> { for clause in self.clauses_to_simplify.drain(0..) { info.clauses_rmed += 1; - log! { @7 "working on #{}", clause } + log! { @7 | "working on #{}", clause } let pred_argss: Vec = if let Some(argss) = self.instance.clauses[clause].drop_lhs_pred(pred) { @@ -1215,7 +1211,7 @@ impl<'a> PreInstance<'a> { bail!("inconsistent instance state") }; - log! { @7 " {} applications", pred_argss.len() } + log! { @7 | " {} applications", pred_argss.len() } // This is why we rev-sorted: let clause = self.instance.forget_clause(clause)?; @@ -1354,8 +1350,8 @@ impl<'a> PreInstance<'a> { let quant = Quant::forall(qvars); - log_debug! { - "force pred right on {}...", conf.emph(& self.instance[pred].name) + log! { @debug | + "force pred right on {}...", conf.emph(& self.instance[pred].name) } // Update rhs clauses. @@ -1365,11 +1361,9 @@ impl<'a> PreInstance<'a> { 'clause_iter: for clause in &self.clauses_to_simplify { let clause = *clause; - log! { @4 "working on clause #{}", clause } + log! { @4 | "working on clause #{}", clause } log! { @4 - "{}", self.instance[clause].to_string_info( - self.instance.preds() - ).unwrap() + "{}", self.instance[clause].to_string_info(self.instance.preds()).unwrap() } let rhs = self.instance.clauses[clause].unset_rhs(); @@ -1396,7 +1390,7 @@ impl<'a> PreInstance<'a> { } // No `else`, clause's rhs is already `None`. - log! { @5 "generating new lhs pred apps" } + log! { @5 | "generating new lhs pred apps" } // New lhs predicate applications. for (pred, argss) in negated.preds() { @@ -1414,7 +1408,7 @@ impl<'a> PreInstance<'a> { } } - log! { @5 "generating new lhs terms" } + log! { @5 | "generating new lhs terms" } // New lhs terms. for term in negated.terms() { @@ -1469,10 +1463,10 @@ impl<'a> PreInstance<'a> { let mut to_add = Vec::with_capacity(17); let fls = term::fls(); - log_debug! { - "{} appears in {} clause's lhs", - conf.emph(& self[pred].name), - self.instance.pred_to_clauses[pred].0.len() + log! { @debug | + "{} appears in {} clause's lhs", + conf.emph(& self[pred].name), + self.instance.pred_to_clauses[pred].0.len() } for clause in &self.instance.pred_to_clauses[pred].0 { @@ -1507,9 +1501,9 @@ impl<'a> PreInstance<'a> { } } - log! { @4 - "pre-simplification {}", - nu_clause.to_string_info(& self.preds).unwrap() + log! { @4 | + "pre-simplification {}", + nu_clause.to_string_info(& self.preds).unwrap() } self.simplifier @@ -1517,8 +1511,8 @@ impl<'a> PreInstance<'a> { if !nu_clause.lhs_terms().contains(&fls) { log! { @4 - "staging clause {}", - nu_clause.to_string_info(& self.preds).unwrap() + "staging clause {}", + nu_clause.to_string_info(& self.preds).unwrap() } // nu_clause.from_unrolling = true ; to_add.push(nu_clause) @@ -1526,7 +1520,7 @@ impl<'a> PreInstance<'a> { } } - log_debug! { "adding {} clauses", to_add.len() } + log! { @debug "adding {} clauses", to_add.len() } for mut clause in to_add { if let Some(index) = self.instance.push_clause(clause)? { @@ -1602,8 +1596,7 @@ impl<'a> PreInstance<'a> { for mut clause in to_add { log! { @4 - "adding clause {}", - clause.to_string_info(& self.preds).unwrap() + "adding clause {}", clause.to_string_info(& self.preds).unwrap() } if let Some(index) = self.instance.push_clause(clause)? { let mut simplinfo = self.simplify_clause(index)?; @@ -1641,9 +1634,8 @@ impl<'a> PreInstance<'a> { }}; } - log! { @4 - "working on {} ({}/{})", - self[pred], to_keep.len(), self[pred].sig.len() + log! { @4 | + "working on {} ({}/{})", self[pred], to_keep.len(), self[pred].sig.len() } let mut rmed = 0; @@ -1654,7 +1646,7 @@ impl<'a> PreInstance<'a> { if to_keep.contains(&var) { // Re-route current **new** var to the original variable `var` is // pointing to. - var_map.push(self.old_preds[pred].1[var]); + var_map.push(self[pred].original_sig_map()[var]); nu_sig.push(typ.clone()) } else { rmed += 1 @@ -1662,9 +1654,7 @@ impl<'a> PreInstance<'a> { } // Update `preds` with the new signature. - self.instance.preds[pred].sig = nu_sig; - // Update `old_preds`'s map. - self.instance.old_preds[pred].1 = var_map; + self.instance.preds[pred].set_sig(nu_sig, var_map); // Propagate removal to clauses. let (ref lhs, ref rhs) = self.instance.pred_to_clauses[pred]; @@ -1697,21 +1687,21 @@ impl<'a> PreInstance<'a> { /// /// Simplifies before returning. /// - /// Removes useless arguments in the clauses. Updates `old_preds`, - /// `pred_terms`. + /// Removes useless arguments in the clauses. Updates the signature of the predicates in the + /// instance. pub fn rm_args(&mut self, to_keep: PrdHMap) -> Res { - if_debug! { - log_debug! { " rm_args ({})", to_keep.len() } - log_debug! { " to keep {{" } - for (pred, vars) in to_keep.iter() { - let mut s = String::new() ; - for var in vars { - s.push_str(" ") ; - s.push_str( & var.default_str() ) + if_log! { @debug + log! { @debug | " rm_args ({})", to_keep.len() } + log! { @debug | " to keep {{" } + for (pred, vars) in to_keep.iter() { + let mut s = String::new() ; + for var in vars { + s.push_str(" ") ; + s.push_str( & var.default_str() ) + } + log! { @debug | " {}:{}", self[* pred], s } } - log_debug! { " {}:{}", self[* pred], s } - } - log_debug! { " }}" } + log! { @debug | " }}" } } self.check("rm_args")?; @@ -1730,9 +1720,9 @@ impl<'a> PreInstance<'a> { // Remove args from applications in clauses. for (pred, to_keep) in to_keep { debug_assert!(to_keep.len() <= self[pred].sig.len()); - log! { @4 "- {}", self[pred] } + log! { @4 | "- {}", self[pred] } if to_keep.len() == self[pred].sig.len() { - log! { @4 "skipping" } + log! { @4 | "skipping" } continue; } @@ -1786,7 +1776,7 @@ impl<'a> PreInstance<'a> { self.instance.finalize()?; for pred in self.instance.sorted_forced_terms() { let pred = *pred; - log! { @4 "definining {}", self[pred] } + log! { @4 | "definining {}", self[pred] } let sig: Vec<_> = self.instance[pred] .sig @@ -1895,11 +1885,7 @@ impl ClauseSimplifier { /// /// Returns `None` iff the clause is trivial, otherwise returns `true` is /// something changed. - pub fn clause_propagate_bool( - &mut self, - clause: &mut Clause, - _preds: &PrdInfos, - ) -> Res> { + pub fn clause_propagate_bool(&mut self, clause: &mut Clause) -> Res> { debug_assert! { self.terms_to_add.is_empty() } debug_assert! { self.subst.is_empty() } debug_assert! { self.var_set.is_empty() } @@ -1907,9 +1893,6 @@ impl ClauseSimplifier { let mut trivial = false; let mut changed = false; - // println!("") ; - // println!("{}", clause.to_string_info(_preds).unwrap()) ; - // Propagate all top-level (negated) boolean variables. for term in clause.lhs_terms() { let plain_term = term.rm_neg(); @@ -1955,7 +1938,6 @@ impl ClauseSimplifier { } } - // println!("{}", clause.to_string_info(_preds).unwrap()) ; Ok(Some(changed)) } @@ -1966,7 +1948,7 @@ impl ClauseSimplifier { fn work_on_eq(&mut self, clause: &mut Clause, eq: &Term) -> Res { macro_rules! skip { () => {{ - log! { @5 "skipping" } + log! { @5 | "skipping" } return Ok(true); }}; } @@ -2032,7 +2014,7 @@ impl ClauseSimplifier { skip!() }; - log! { @5 "{} -> {}", eq, nu_term } + log! { @5 | "{} -> {}", eq, nu_term } clause.insert_term(nu_term); } @@ -2042,20 +2024,20 @@ impl ClauseSimplifier { } /// Propagates equalities in a clause. - pub fn clause_propagate(&mut self, clause: &mut Clause, _preds: &PrdInfos) -> Res<()> { + pub fn clause_propagate(&mut self, clause: &mut Clause, _preds: &Preds) -> Res<()> { debug_assert! { self.terms_to_add.is_empty() } debug_assert! { self.subst.is_empty() } debug_assert! { self.var_set.is_empty() } let mut eq = None; - log! { @6 "working on {}", clause.to_string_info( _preds ).unwrap() } + log! { @6 "working on\n{}", clause.to_string_info( _preds ).unwrap() } let mut changed = false; macro_rules! bool_prop { () => { - match self.clause_propagate_bool(clause, _preds)? { + match self.clause_propagate_bool(clause)? { // Clause is trivial. None => return Ok(()), Some(true) => changed = true, @@ -2075,7 +2057,7 @@ impl ClauseSimplifier { } if let Some(eq) = ::std::mem::replace(&mut eq, None) { - log! { @4 "{}", eq } + log! { @4 | "{}", eq } let was_there = clause.rm_term(&eq); debug_assert! { was_there } @@ -2091,7 +2073,7 @@ impl ClauseSimplifier { clause.insert_term(term); } changed = clause.subst(&self.subst) || changed; - log! { @5 "yielding {}", clause.to_string_info( _preds ).unwrap() } + log! { @5 | "yielding {}", clause.to_string_info( _preds ).unwrap() } for (var, _) in self.subst.drain() { clause.deactivate(var)? } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 6ed0ea7a..5e052940 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,7 +1,7 @@ //! SMT-LIB 2 horn clause problem parser. use common::*; -use instance::{info::VarInfo, *}; +use info::VarInfo; mod ptterms; pub use self::ptterms::*; diff --git a/src/instance/preproc/arg_red.rs b/src/preproc/arg_red.rs similarity index 99% rename from src/instance/preproc/arg_red.rs rename to src/preproc/arg_red.rs index 5d9d7b8b..1b95a42f 100644 --- a/src/instance/preproc/arg_red.rs +++ b/src/preproc/arg_red.rs @@ -1,7 +1,7 @@ //! Argument reduction. use common::*; -use instance::{instance::PreInstance, preproc::RedStrat, Clause}; +use preproc::{PreInstance, RedStrat}; /// Argument reduction. /// diff --git a/src/instance/preproc/bias_unroll.rs b/src/preproc/bias_unroll.rs similarity index 88% rename from src/instance/preproc/bias_unroll.rs rename to src/preproc/bias_unroll.rs index 8cdbf020..baaee049 100644 --- a/src/instance/preproc/bias_unroll.rs +++ b/src/preproc/bias_unroll.rs @@ -1,9 +1,9 @@ //! Bias unrolling module. use common::*; -use instance::{ - instance::{Clause, PreInstance}, - preproc::{utils::ExtractRes, utils::ExtractionCxt, RedStrat}, +use preproc::{ + utils::{ExtractRes, ExtractionCxt}, + PreInstance, RedStrat, }; type NegDef = (Quantfed, TermSet); @@ -511,78 +511,72 @@ impl BiasedUnroll { } log! { @4 - "generating positive clause(s) for {} from {} ({})", - instance[pred], - instance[rhs_clause].to_string_info( instance.preds() ) ?, - estimation + "generating positive clause(s) for {} from {} ({})", + instance[pred], + instance[rhs_clause].to_string_info(instance.preds())?, + estimation } let mut nu_clauses = vec![]; - scoped! { - - let mut clause = instance[rhs_clause].clone() ; + { + let mut clause = instance[rhs_clause].clone(); - let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect() ; - let mut map = Vec::with_capacity( lhs_preds.len() ) ; + let mut lhs_preds: Vec<_> = clause.drain_lhs_preds().collect(); + let mut map = Vec::with_capacity(lhs_preds.len()); - for (pred, argss) in & lhs_preds { - let mut arg_map = Vec::with_capacity( argss.len() ) ; + for (pred, argss) in &lhs_preds { + let mut arg_map = Vec::with_capacity(argss.len()); - if let Some(defs) = self.pos_defs.get(pred) { - for args in argss { - arg_map.push( (args, 0) ) - } + if let Some(defs) = self.pos_defs.get(pred) { + for args in argss { + arg_map.push((args, 0)) + } - map.push( (pred, defs, arg_map) ) - } else { - bail!("no definition for {} (positive, lhs)", instance[* pred]) - } - } - - macro_rules! map_inc { - () => ({ - let mut done = true ; - 'all_apps: for & mut (_, defs, ref mut argss) in & mut map { - for (_, ref mut index) in argss { - * index += 1 ; - if * index < defs.len() { - done = false ; - break 'all_apps - } else { - * index = 0 - } + map.push((pred, defs, arg_map)) + } else { + bail!("no definition for {} (positive, lhs)", instance[*pred]) } - } - done - }) - } - - let mut done = false ; - while ! done { - let mut clause = clause.clone() ; - - for (_, defs, argss) in & map { - for (args, index) in argss { - self.insert_terms( - & mut clause, args, & defs[* index].0, & defs[* index].1 - ) ? - } } - if let Some(trivial) = instance.is_this_clause_trivial( - & mut clause - ) ? { - if ! trivial { - nu_clauses.push(clause) - } - } else { - unimplemented!("unsat in biased unrolling") + macro_rules! map_inc { + () => {{ + let mut done = true; + 'all_apps: for &mut (_, defs, ref mut argss) in &mut map { + for (_, ref mut index) in argss { + *index += 1; + if *index < defs.len() { + done = false; + break 'all_apps; + } else { + *index = 0 + } + } + } + done + }}; } - done = map_inc!() - } + let mut done = false; + while !done { + let mut clause = clause.clone(); + + for (_, defs, argss) in &map { + for (args, index) in argss { + self.insert_terms(&mut clause, args, &defs[*index].0, &defs[*index].1)? + } + } + if let Some(trivial) = instance.is_this_clause_trivial(&mut clause)? { + if !trivial { + nu_clauses.push(clause) + } + } else { + unimplemented!("unsat in biased unrolling") + } + + done = map_inc!() + } } for mut clause in nu_clauses { @@ -675,13 +669,13 @@ impl BiasedUnroll { let argss_len = argss.len(); if active_lhs_pred_app < argss_len { let mut index = 0; - map_inc!( - @argss done, defs, argss ; { - let iff = index != active_lhs_pred_app ; - index += 1 ; - iff - } - ) + map_inc! { + @argss done, defs, argss ; { + let iff = index != active_lhs_pred_app ; + index += 1 ; + iff + } + } } if done { @@ -693,6 +687,7 @@ impl BiasedUnroll { done }}; + (@argss $done:ident, $defs:expr, $argss:expr ; $iff:expr) => { for (_, ref mut index) in $argss { if $iff { @@ -825,9 +820,9 @@ impl BiasedUnroll { } log! { @4 - "generating negative clause(s) for {} from {}", - instance[pred], - instance[lhs_clause].to_string_info( instance.preds() ) ? + "generating negative clause(s) for {} from {}", + instance[pred], + instance[lhs_clause].to_string_info( instance.preds() ) ? } let mut nu_clauses = vec![]; @@ -836,8 +831,8 @@ impl BiasedUnroll { for mut clause in nu_clauses { log! { @6 - "new clause: {}", - clause.to_string_info( instance.preds() ) ? + "new clause: {}", + clause.to_string_info( instance.preds() ) ? } clause.from_unrolling = true; @@ -857,7 +852,7 @@ impl BiasedUnroll { for pred in self.not_in_neg_clauses.clone() { log! { @4 - "trying to generate negative clauses for {}", instance[pred] + "trying to generate negative clauses for {}", instance[pred] } // self.print(instance) ; @@ -887,11 +882,11 @@ impl BiasedUnroll { let is_new = neg.insert(pred); debug_assert! { is_new } } - log! { @4 "-> success" } + log! { @4 | "-> success" } - log! { @verb - "generated {} negative clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) + log! { @verb | + "generated {} negative clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) } info += this_info; @@ -902,10 +897,10 @@ impl BiasedUnroll { } else { bail!("inconsistent BiasedUnroll state") } - log! { @4 "-> failure" } + log! { @4 | "-> failure" } } } else { - log! { @4 "-> nothing new, skipping" } + log! { @4 | "-> nothing new, skipping" } } } @@ -916,8 +911,8 @@ impl BiasedUnroll { let mut info = RedInfo::new(); for pred in self.not_in_pos_clauses.clone() { - log! { @4 - "trying to generate positive clauses for {}", instance[pred] + log! { @4 | + "trying to generate positive clauses for {}", instance[pred] } // self.print(instance) ; @@ -947,11 +942,11 @@ impl BiasedUnroll { let is_new = pos.insert(pred); debug_assert! { is_new } } - log! { @4 "-> success" } + log! { @4 | "-> success" } - log! { @verb - "generated {} positive clauses for {}", - this_info.clauses_added, conf.emph(& instance[pred].name) + log! { @verb | + "generated {} positive clauses for {}", + this_info.clauses_added, conf.emph(& instance[pred].name) } info += this_info; @@ -962,10 +957,10 @@ impl BiasedUnroll { } else { bail!("inconsistent BiasedUnroll state") } - log! { @4 "-> failure" } + log! { @4 | "-> failure" } } } else { - log! { @4 "-> nothing new, skipping" } + log! { @4 | "-> nothing new, skipping" } } } diff --git a/src/instance/preproc/cfg_red.rs b/src/preproc/cfg_red.rs similarity index 89% rename from src/instance/preproc/cfg_red.rs rename to src/preproc/cfg_red.rs index b5107f1c..b25a6720 100644 --- a/src/instance/preproc/cfg_red.rs +++ b/src/preproc/cfg_red.rs @@ -2,10 +2,7 @@ //! between predicates. use common::*; -use instance::{ - instance::PreInstance, - preproc::{utils, utils::ExtractionCxt, RedStrat}, -}; +use preproc::{utils, utils::ExtractionCxt, PreInstance, RedStrat}; use var_to::terms::VarTermsSet; /// Result of a DNF merge. @@ -337,10 +334,10 @@ impl Graph { max: Option, ) -> Res> { log! { - @6 "merging for {}, {} substitutions", instance[pred], substs.len() + @6 | "merging for {}, {} substitutions", instance[pred], substs.len() } - let fresh_index = instance.original_sig_of(pred).next_index(); - log! { @6 "fresh index: {}", fresh_index } + let fresh_index = instance[pred].fresh_var_idx(); + log! { @6 | "fresh index: {}", fresh_index } let mut result = Vec::with_capacity(lft.len() * rgt.len()); let mut estimation = 0; @@ -354,12 +351,12 @@ impl Graph { let mut fresh_index = fresh_index; for (idx, _) in r_qvars { - log! { @7 "- rgt qvar {}", idx } + log! { @7 | "- rgt qvar {}", idx } if *idx >= fresh_index { fresh_index = (1 + **idx).into() } } - log! { @7 "first legal index: {}", fresh_index } + log! { @7 | "first legal index: {}", fresh_index } // All combinations of elements of `lft` of len `substs`. let mut all_lft_combinations = CombinationIter::new(lft.iter(), substs.len()) @@ -421,7 +418,7 @@ impl Graph { // `lft`. for ((l_qvars, l_conj), subst) in combination.iter().zip(substs.iter()) { conf.check_timeout()?; - log! { @7 "working on substitution..." } + log! { @7 | "working on substitution..." } // Fresh map for this substitution. qvar_map.clear(); @@ -444,9 +441,9 @@ impl Graph { r_conj.reserve(l_conj.terms().len(), l_conj.preds().len()); // Working on terms. for term in l_conj.terms() { - log! { @8 "subst on {}", term } + log! { @8 | "subst on {}", term } let (term, _) = term.subst(&(&*qvar_map, subst)); - log! { @8 "-> {}", term } + log! { @8 | "-> {}", term } let is_false = term::simplify::conj_term_insert(term, r_conj.terms_mut()); @@ -487,7 +484,7 @@ impl Graph { max: Option, previous: &[(PrdIdx, Dnf)], ) -> Res> { - log! { @4 "dnf_of({}, {:?})", instance[pred], max } + log! { @4 | "dnf_of({}, {:?})", instance[pred], max } let forced_inlining = max.is_none(); @@ -519,7 +516,7 @@ impl Graph { )? { utils::ExtractRes::Success((qvars, mut tterms)) => { log! { @5 - "from clause {}", clause.to_string_info(& instance.preds()) ? + "from clause {}", clause.to_string_info(&instance.preds())? } if !forced_inlining && !tterms.preds().is_empty() { @@ -548,8 +545,8 @@ impl Graph { } if_log! { @5 - log! { @5 "current definition:" } - Self::log_definition(instance, & def) + log! { @5 |=> "current definition:" } + Self::log_definition(instance, & def) } } utils::ExtractRes::SuccessTrue => bail!( @@ -571,24 +568,24 @@ impl Graph { #[cfg(not(feature = "bench"))] fn log_definition(_instance: &Instance, _def: DnfRef) { for (qvars, tterms) in _def { - log! { @5 "and" } + log! { @5 |=> "and" } if !qvars.is_empty() { - log! { @5 " qvars {{" } + log! { @5 |=> " qvars {{" } for (var, typ) in qvars { - log! { @5 " {}: {}", var.default_str(), typ } + log! { @5 |=> " {}: {}", var.default_str(), typ } } - log! { @5 " }}" } + log! { @5 |=> " }}" } } for term in tterms.terms() { - log! { @5 " {}", term } + log! { @5 |=> " {}", term } } for (pred, argss) in tterms.preds() { for args in argss { - log! { @5 " ({} {})", _instance[* pred], args } + log! { @5 |=> " ({} {})", _instance[* pred], args } } } } - log! { @5 => " " } + log! { @5 |=> " " } } /// Handles a conjunction that's part of a definition for a predicate. @@ -638,21 +635,21 @@ impl Graph { tterms: TTermSet, ) -> Res> { if_log! { @5 - log! { @5 => "qvars {{" } - for (var, typ) in & qvars { - log! { @5 => " {}: {}", var.default_str(), typ } - } - log! { @5 => "}}" } - log! { @5 => "conj {{" } - for term in tterms.terms() { - log! { @5 => " {}", term } - } - for (pred, argss) in tterms.preds() { - for args in argss { - log! { @5 => " ({} {})", instance[* pred], args } + log! { @5 |=> "qvars {{" } + for (var, typ) in & qvars { + log! { @5 |=> " {}: {}", var.default_str(), typ } } - } - log! { @5 => "}}" } + log! { @5 |=> "}}" } + log! { @5 |=> "conj {{" } + for term in tterms.terms() { + log! { @5 |=> " {}", term } + } + for (pred, argss) in tterms.preds() { + for args in argss { + log! { @5 |=> " ({} {})", instance[* pred], args } + } + } + log! { @5 |=> "}}" } } let mut curr = vec![(qvars, tterms)]; @@ -661,9 +658,9 @@ impl Graph { conf.check_timeout()?; if_log! { @6 - Self::log_merge_defs( - _this_pred, instance, & argss, p_def, & curr - ) + Self::log_merge_defs( + _this_pred, instance, & argss, p_def, & curr + ) } if let Some(res) = Self::merge( @@ -699,27 +696,27 @@ impl Graph { _p_def: DnfRef, _curr: DnfRef, ) { - log! { @6 => "curr {{" } + log! { @6 |=> "curr {{" } let mut first = true; for &(ref qv, ref tterms) in _curr { if first { first = false } else { - log! { @6 => " " } + log! { @6 |=> " " } } for (var, typ) in qv { - log! { @6 => " {}: {}", var.default_str(), typ } + log! { @6 |=> " {}: {}", var.default_str(), typ } } for term in tterms.terms() { - log! { @6 => " {}", term } + log! { @6 |=> " {}", term } } for (pred, _argss) in tterms.preds() { for args in _argss { - log! { @6 => " ({} {})", _instance[* pred], args } + log! { @6 |=> " ({} {})", _instance[* pred], args } } } } - log! { @6 => "}}" } + log! { @6 |=> "}}" } Self::log_merge_defs_sub(_this_pred, _instance, _argss, _p_def) } @@ -731,37 +728,37 @@ impl Graph { _argss: &VarTermsSet, _p_def: DnfRef, ) { - log! { @6 => "argss for {} {{", _instance[_this_pred] } + log! { @6 |=> "argss for {} {{", _instance[_this_pred] } for args in _argss { let mut pref = " > "; for (var, arg) in args.index_iter() { - log! { @6 => "{}{} -> {}", pref, var.default_str(), arg } + log! { @6 |=> "{}{} -> {}", pref, var.default_str(), arg } pref = " " } } - log! { @6 => "}}" } - log! { @6 => "defs {{" } + log! { @6 |=> "}}" } + log! { @6 |=> "defs {{" } let mut first = true; for &(ref qv, ref tterms) in _p_def { if first { first = false } else { - log! { @6 => " " } + log! { @6 |=> " " } } for (var, typ) in qv { - log! { @6 => " {}: {}", var.default_str(), typ } + log! { @6 |=> " {}: {}", var.default_str(), typ } } for term in tterms.terms() { - log! { @6 => " {}", term } + log! { @6 |=> " {}", term } } for (pred, _argss) in tterms.preds() { for args in _argss { - log! { @6 => " ({} {})", _instance[* pred], args } + log! { @6 |=> " ({} {})", _instance[* pred], args } } } } - log! { @6 => "}}" } + log! { @6 |=> "}}" } } /// Constructs all the predicates not in `keep` by inlining the constraints. @@ -796,12 +793,12 @@ impl Graph { { continue 'find_pred; } - log_debug! { "looking at {}", instance[prd] } + log! { @3 | "looking at {}", instance[prd] } 'check_src: for (src, cnt) in srcs.index_iter() { if *cnt == 0 { continue 'check_src; } - log_debug! { "depends on {}", instance[src] } + log! { @3 | "depends on {}", instance[src] } if !keep.contains(&src) && !res_contains!(&src) { continue 'find_pred; } @@ -813,11 +810,11 @@ impl Graph { let pred = if let Some(p) = pred { p } else { - log_debug! { "no predicate illeligible for inlining" } + log! { @3 | "no predicate illeligible for inlining" } break 'construct; }; - log_debug! { "investigating inlining {}", instance[pred] } + log! { @3 | "investigating inlining {}", instance[pred] } macro_rules! keep_and_continue { ($pred:expr) => { @@ -842,9 +839,9 @@ impl Graph { &res, )? { upper_bound += res.estimation; - log! { @4 - "inlining {} (blow-up estimation: {})", - instance[pred], res.estimation + log! { @4 | + "inlining {} (blow-up estimation: {})", + instance[pred], res.estimation } res.def } else { @@ -852,8 +849,8 @@ impl Graph { }; if_log! { @4 - log! { @4 => "definition:" } - Self::log_definition(instance, & def) + log! { @4 |=> "definition:" } + Self::log_definition(instance, & def) } conf.check_timeout()?; @@ -904,9 +901,9 @@ impl Graph { } } // println!("blows up") ; - log! { @6 - "inlining for {} blows up: {} > {} (len: {})", - instance[pred], estimation, max, len + log! { @6 | + "inlining for {} blows up: {} > {} (len: {})", + instance[pred], estimation, max, len } return None; } @@ -922,30 +919,30 @@ impl Graph { forward: &PrdHMap, ) -> Option { if_log! { @3 - log! { @3 => " looking for a starting point with" } - log! { @3 => " - pos {{" } + log! { @3 |=> " looking for a starting point with" } + log! { @3 |=> " - pos {{" } for prd in pos { - log! { @3 => " {}", _instance[* prd] } + log! { @3 |=> " {}", _instance[* prd] } } - log! { @3 => " }}" } - log! { @3 => " - forward {{" } + log! { @3 |=> " }}" } + log! { @3 |=> " - forward {{" } for (prd, set) in forward { let mut s = String::new() ; for prd in set { s = format!("{} {}", s, _instance[* prd]) } - log! { @3 => " {} ->{}", _instance[* prd], s } + log! { @3 |=> " {} ->{}", _instance[* prd], s } } - log! { @3 => " }}" } + log! { @3 |=> " }}" } } // Find a starting point. if let Some(pred) = pos.iter().next() { - log! { @3 " found one in `pos`" } + log! { @3 | " found one in `pos`" } // There was something in `pos`, remove it and move on. Some(*pred) } else { - log! { @3 " no preds in `pos`, looking in `forward`" } + log! { @3 | " no preds in `pos`, looking in `forward`" } // There was nothing in `pos`, select something from `forward`. forward.iter().next().map(|(pred, _)| *pred) } @@ -972,11 +969,11 @@ impl Graph { let start = if let Some(pred) = start { pred } else { - log! { @3 " no starting point found, done" } + log! { @3 | " no starting point found, done" } break 'break_cycles; }; - log! { @3 " starting point is {}, following it", instance[start] } + log! { @3 | " starting point is {}, following it", instance[start] } // Follow it. let weights = Self::follow(instance, start, &forward)?; @@ -985,9 +982,9 @@ impl Graph { } if let Some(pred) = Self::find_heaviest(instance, &weights)? { - log! { @3 - " removing it from everything" ; - " remembering {}", instance[pred] + log! { @3 | + " removing it from everything" ; + " remembering {}", instance[pred] } // Remove the representative from everything. Self::forget(pred, &mut pos, &mut forward); @@ -995,11 +992,11 @@ impl Graph { let is_new = set.insert(pred); debug_assert!(is_new); - log! { @3 "" } + log! { @3 | "" } } else { // There's no cycle, forget everything in weight and keep going. for (pred, _weight) in weights { - log! { @3 " - forgetting {} ({})", instance[pred], _weight } + log! { @3 | " - forgetting {} ({})", instance[pred], _weight } Self::forget(pred, &mut pos, &mut forward); debug_assert!(_weight < 2) } @@ -1071,7 +1068,7 @@ impl Graph { let mut rep = None; for (prd, weight) in weights { let (prd, weight) = (*prd, *weight); - log! { @3 " {} -> {}", _instance[prd], weight } + log! { @3 | " {} -> {}", _instance[prd], weight } let curr_weight = if let Some(&(_, w)) = rep.as_ref() { w } else { @@ -1083,9 +1080,9 @@ impl Graph { } if let Some((pred, weight)) = rep { - log! { @3 " heaviest is {} ({})", _instance[pred], weight } + log! { @3 | " heaviest is {} ({})", _instance[pred], weight } if weight < 2 { - log! { @3 "no cycle, forgetting everything" } + log! { @3 | "no cycle, forgetting everything" } Ok(None) } else { Ok(Some(pred)) @@ -1283,10 +1280,10 @@ impl RedStrat for CfgRed { self.graph.check(&instance)?; if_log! { @verb - log! { @verb "inlining {} predicates", pred_defs.len() } - for (pred, _) in & pred_defs { - log! { @verb " {}", instance[* pred] } - } + log! { @verb | "inlining {} predicates", pred_defs.len() } + for (pred, _) in & pred_defs { + log! { @verb | " {}", instance[* pred] } + } } if pred_defs.len() == instance.active_pred_count() { diff --git a/src/instance/preproc/fun_preds.rs b/src/preproc/fun_preds.rs similarity index 80% rename from src/instance/preproc/fun_preds.rs rename to src/preproc/fun_preds.rs index 4564d41f..0a1d37b1 100644 --- a/src/instance/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -2,10 +2,13 @@ //! //! This preprocessor attempts to reconstruct function definitions from Horn clauses. Functions are //! reconstructed as multiple branches of an if-then-else. +//! +//! The core of how this works is the [`map_invert`](fn.map_invert.html) function. use common::*; use fun::RFun; -use instance::{info::VarInfo, instance::PreInstance, preproc::RedStrat, Clause}; +use info::VarInfo; +use preproc::{Clause, PreInstance, RedStrat}; /// A branch in the definition of a function. #[derive(Clone, Debug)] @@ -1047,6 +1050,12 @@ impl RedStrat for FunPreds { } /// Builds a cube and a substitution corresponding to inverting some arguments. +/// +/// It is essentially a repeated application of [`map_invert`](fn.map_invert.html) to all +/// arguments. It ends when +/// +/// - all of the arguments are inversed, or +/// - a subset of the arguments cannot be inversed at all (the result will be `None` in this case). pub fn args_invert(args: &VarTerms, args_len: usize) -> Res)>> { let (mut cube, mut subst) = (TermSet::new(), VarHMap::new()); @@ -1103,7 +1112,47 @@ pub fn args_invert(args: &VarTerms, args_len: usize) -> Res Res { debug_assert_eq! { to_invert.typ(), term.typ() } - let mut nu_cube = vec![]; - let mut nu_subst = VarHMap::::new(); + // These are the new terms to insert in `cube` and the new map from variables to terms that we + // need to add. We do not mutate `subst` nor `cube` directly because the invertion process + // might fail, in which case the caller will move on. + let (mut nu_cube, mut nu_subst) = (vec![], VarHMap::::new()); + // Stack of pairs composed of two terms. The second one is the term to invert, and the first + // one is the pivot. let mut stack = vec![(term, to_invert.get())]; while let Some((term, to_invert)) = stack.pop() { match to_invert { - RTerm::DTypNew { - typ, name, args, .. - } => { - let selectors = typ.selectors_of(name)?; - debug_assert_eq! { args.len(), selectors.len() } - - // `term` must be a specific variant: `name` - nu_cube.push(term::dtyp_tst(name.clone(), term.clone())); - - for (arg, (slc, _)) in args.iter().zip(selectors.iter()) { - stack.push((term::dtyp_slc(arg.typ(), slc.clone(), term.clone()), arg)) - } - } - RTerm::Cst(val) => { nu_cube.push(term::eq(term, term::val(val.clone()))); continue; @@ -1163,109 +1202,39 @@ pub fn map_invert( RTerm::App { typ, op, args, .. } => { match op { - Op::CMul => if args[0].val().is_some() { - let nu_term = if typ.is_int() { - // Current term modulo `val` is zero. - nu_cube.push(term::eq( - term::modulo(term.clone(), args[0].clone()), - term::int(0), - )); - term::idiv(vec![term, args[0].clone()]) - } else if typ.is_real() { - term::div(vec![term, args[0].clone()]) - } else { - bail!("unexpected multiplication over type {}", typ) - }; - stack.push((nu_term, args[1].get())); + Op::CMul => { + cmul_invert(to_invert, term, typ, args, &mut nu_cube, &mut stack)?; continue; - } else { - bail!("illegal CMul term {}", to_invert) - }, + } Op::Add => { - let mut subtraction = vec![term]; - let mut not_substed = None; - - for arg in args { - if let Some((term, _)) = arg.subst_total(&(&*subst, &nu_subst)) { - subtraction.push(term) - } else if not_substed.is_some() { - return Ok(false); - } else { - not_substed = Some(arg.get()) - } - } - - let nu_term = term::sub(subtraction); - - if let Some(nu_to_invert) = not_substed { - stack.push((nu_term, nu_to_invert)) - } else { - nu_cube.push(term::eq( - nu_term, - if typ.is_int() { - term::int_zero() - } else if typ.is_real() { - term::real_zero() - } else { - bail!("unexpected addition over type {}", typ) - }, - )); + let okay = add_invert( + term, + typ, + args, + &mut nu_cube, + &mut stack, + subst, + &nu_subst, + )?; + if !okay { + return Ok(false); } continue; } Op::Sub => { - let mut sub = None; - let mut add = vec![term]; - let mut not_substed = None; - - let mut first = true; - for arg in args { - if let Some((term, _)) = arg.subst_total(&(&*subst, &nu_subst)) { - if first { - debug_assert! { sub.is_none() } - sub = Some(term) - } else { - add.push(term) - } - } else if not_substed.is_some() { - return Ok(false); - } else { - // Careful of the polarity here vvvvv - not_substed = Some((arg.get(), first)) - } - - first = false - } - - let nu_term = term::add(add); - let nu_term = if let Some(sub) = sub { - term::sub(vec![nu_term, sub]) - } else { - nu_term - }; - - if let Some((nu_to_invert, positive)) = not_substed { - stack.push(( - if positive { - nu_term - } else { - term::u_minus(nu_term) - }, - nu_to_invert, - )) - } else { - nu_cube.push(term::eq( - nu_term, - if typ.is_int() { - term::int_zero() - } else if typ.is_real() { - term::real_zero() - } else { - bail!("unexpected addition over type {}", typ) - }, - )); + let okay = sub_invert( + term, + typ, + args, + &mut nu_cube, + &mut stack, + subst, + &nu_subst, + )?; + if !okay { + return Ok(false); } continue; @@ -1286,44 +1255,38 @@ pub fn map_invert( nu_cube.push(term::eq(term, term::app(*op, nu_args))) } + RTerm::DTypNew { + typ, name, args, .. + } => dtyp_new_invert(&term, typ, name, args, &mut nu_cube, &mut stack)?, + RTerm::DTypSlc { typ, name, term: inner, .. - } => if let Some((inner, _)) = inner.subst_total(&(&*subst, &nu_subst)) { - nu_cube.push(term::eq( - term, - term::dtyp_slc(typ.clone(), name.clone(), inner), - )) - } else { - return Ok(false); - }, + } => { + let okay = dtyp_slc_invert(term, typ, name, inner, &mut nu_cube, subst, &nu_subst); + if !okay { + return Ok(false); + } + } RTerm::DTypTst { name, term: inner, .. - } => if let Some((inner, _)) = inner.subst_total(&(&*subst, &nu_subst)) { - nu_cube.push(term::eq(term, term::dtyp_tst(name.clone(), inner))) - } else { - return Ok(false); - }, + } => { + let okay = dtyp_tst_invert(term, name, inner, &mut nu_cube, subst, &nu_subst); + if !okay { + return Ok(false); + } + } RTerm::Fun { typ, name, args, .. } => { - let mut nu_args = Vec::with_capacity(args.len()); - for arg in args { - if let Some((arg, _)) = arg.subst_total(&(&*subst, &nu_subst)) { - nu_args.push(arg) - } else { - return Ok(false); - } + let okay = fun_invert(term, typ, name, args, &mut nu_cube, subst, &nu_subst); + if !okay { + return Ok(false); } - - nu_cube.push(term::eq( - term, - term::fun(typ.clone(), name.clone(), nu_args), - )) } } } @@ -1339,6 +1302,231 @@ pub fn map_invert( Ok(true) } +/// Inverts a datatype constructor. +fn dtyp_new_invert<'a>( + term: &Term, + typ: &Typ, + name: &str, + args: &'a [Term], + cube: &mut Vec, + stack: &mut Vec<(Term, &'a RTerm)>, +) -> Res<()> { + let selectors = typ.selectors_of(name)?; + debug_assert_eq! { args.len(), selectors.len() } + + cube.push(term::dtyp_tst(name.into(), term.clone())); + + for (arg, (slc, _)) in args.iter().zip(selectors.iter()) { + stack.push(( + term::dtyp_slc(arg.typ(), slc.clone(), term.clone()), + arg.get(), + )) + } + + Ok(()) +} + +/// Inverts a cmul application. +fn cmul_invert<'a>( + to_invert: &RTerm, + term: Term, + typ: &Typ, + args: &'a [Term], + cube: &mut Vec, + stack: &mut Vec<(Term, &'a RTerm)>, +) -> Res<()> { + if args[0].val().is_some() { + let nu_term = if typ.is_int() { + // Current term modulo `args[0].val()` is zero. + cube.push(term::eq( + term::modulo(term.clone(), args[0].clone()), + term::int(0), + )); + term::idiv(vec![term, args[0].clone()]) + } else if typ.is_real() { + term::div(vec![term, args[0].clone()]) + } else { + bail!("unexpected multiplication over type {}", typ) + }; + stack.push((nu_term, args[1].get())); + } else { + bail!("illegal CMul term {}", to_invert) + } + + Ok(()) +} + +/// Inverts an addition. +fn add_invert<'a>( + term: Term, + typ: &Typ, + args: &'a [Term], + cube: &mut Vec, + stack: &mut Vec<(Term, &'a RTerm)>, + subst: &VarHMap, + nu_subst: &VarHMap, +) -> Res { + let mut subtraction = vec![term]; + let mut not_substed = None; + + for arg in args { + if let Some((term, _)) = arg.subst_total(&(subst, nu_subst)) { + subtraction.push(term) + } else if not_substed.is_some() { + return Ok(false); + } else { + not_substed = Some(arg.get()) + } + } + + let nu_term = term::sub(subtraction); + + if let Some(nu_to_invert) = not_substed { + stack.push((nu_term, nu_to_invert)) + } else { + cube.push(term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + }, + )) + } + + Ok(true) +} + +/// Inverts a subtraction. +fn sub_invert<'a>( + term: Term, + typ: &Typ, + args: &'a [Term], + cube: &mut Vec, + stack: &mut Vec<(Term, &'a RTerm)>, + subst: &VarHMap, + nu_subst: &VarHMap, +) -> Res { + let mut sub = None; + let mut add = vec![term]; + let mut not_substed = None; + + let mut first = true; + for arg in args { + if let Some((term, _)) = arg.subst_total(&(subst, nu_subst)) { + if first { + debug_assert! { sub.is_none() } + sub = Some(term) + } else { + add.push(term) + } + } else if not_substed.is_some() { + return Ok(false); + } else { + // Careful of the polarity here. + not_substed = Some((arg.get(), first)) + } + + first = false + } + + let nu_term = term::add(add); + let nu_term = if let Some(sub) = sub { + term::sub(vec![nu_term, sub]) + } else { + nu_term + }; + + if let Some((nu_to_invert, positive)) = not_substed { + stack.push(( + if positive { + nu_term + } else { + term::u_minus(nu_term) + }, + nu_to_invert, + )) + } else { + cube.push(term::eq( + nu_term, + if typ.is_int() { + term::int_zero() + } else if typ.is_real() { + term::real_zero() + } else { + bail!("unexpected addition over type {}", typ) + }, + )) + } + + Ok(true) +} + +/// Inverts a datatype selector. +fn dtyp_slc_invert<'a>( + term: Term, + typ: &Typ, + name: &str, + inner: &'a Term, + cube: &mut Vec, + subst: &VarHMap, + nu_subst: &VarHMap, +) -> bool { + if let Some((inner, _)) = inner.subst_total(&(subst, nu_subst)) { + cube.push(term::eq( + term, + term::dtyp_slc(typ.clone(), name.into(), inner), + )); + true + } else { + false + } +} + +/// Inverts a datatype tester. +fn dtyp_tst_invert<'a>( + term: Term, + name: &str, + inner: &'a Term, + cube: &mut Vec, + subst: &VarHMap, + nu_subst: &VarHMap, +) -> bool { + if let Some((inner, _)) = inner.subst_total(&(subst, nu_subst)) { + cube.push(term::eq(term, term::dtyp_tst(name.into(), inner))); + true + } else { + false + } +} + +/// Inverts a function application. +fn fun_invert<'a>( + term: Term, + typ: &Typ, + name: &str, + args: &'a [Term], + cube: &mut Vec, + subst: &VarHMap, + nu_subst: &VarHMap, +) -> bool { + let mut nu_args = Vec::with_capacity(args.len()); + for arg in args { + if let Some((arg, _)) = arg.subst_total(&(subst, nu_subst)) { + nu_args.push(arg) + } else { + return false; + } + } + + cube.push(term::eq(term, term::fun(typ.clone(), name.into(), nu_args))); + + true +} + +/// Creates a fresh (hopefully) function name for a predicate. fn make_fun_name(other_name: &str) -> Res { let split: Vec<_> = other_name.split('|').collect(); let str = match split.len() { diff --git a/src/instance/preproc/mod.rs b/src/preproc/mod.rs similarity index 91% rename from src/instance/preproc/mod.rs rename to src/preproc/mod.rs index 48f886fb..270a4552 100644 --- a/src/instance/preproc/mod.rs +++ b/src/preproc/mod.rs @@ -1,21 +1,26 @@ //! Reduction strategies. //! -//! The strategies are attached `struct`s so that they can be put in a vector -//! using single dispatch. That way, they can be combined however we want. +//! All strategies are `struct`s that implement the [`RedStrat`](trait.RedStrat.html) trait. The +//! [`Reductor`](struct.Reductor.html) then combines them in a cohesive preprocessing run in its +//! [`run` function](struct.Reductor.html#method.run). +//! +//! During preprocessing, the [`Instance`](../common/struct.Instance.html) is wrapped into a +//! [`PreInstance`](struct.PreInstance.html) which provides high-level functions to act on the +//! predicates and the clauses of the instance. use common::*; use instance::*; pub mod utils; -mod arg_red; -mod bias_unroll; -mod cfg_red; -mod fun_preds; -mod one_lhs; -mod one_rhs; -mod strict_neg_clauses; -mod unroll; +pub mod arg_red; +pub mod bias_unroll; +pub mod cfg_red; +pub mod fun_preds; +pub mod one_lhs; +pub mod one_rhs; +pub mod strict_neg_clauses; +pub mod unroll; pub use self::{ arg_red::ArgRed, bias_unroll::BiasedUnroll, cfg_red::CfgRed, fun_preds::FunPreds, @@ -30,9 +35,8 @@ pub type PredExtension = (TermSet, Vec<(Quantfed, Term)>); /// Runs pre-processing. /// -/// The boolean indicates wether a first pass of simplification runs on the -/// whole system before the rest. Should be true for top-level preproc, and -/// false for subsystems. +/// The boolean indicates wether a first pass of simplification runs on the whole system before the +/// rest. Should be true for top-level preproc, and false for subsystems. /// /// Finalizes the instance. pub fn work(instance: &mut Instance, profiler: &Profiler) -> Res<()> { @@ -195,17 +199,17 @@ pub fn work_on_split( clause.to_string_info( instance.preds() ) ? } - use instance::preproc::utils::ExtractRes; - - match profile!( - |profiler| wrap { - pre_instance.extraction().0.terms_of_lhs_app( - true, & instance, & clause.vars, - ( clause.lhs_terms(), clause.lhs_preds() ), - None, (pred, args) - ) - } "strengthening", "extraction" - )? { + use preproc::utils::ExtractRes; + + match profile! { + |profiler| wrap { + pre_instance.extraction().0.terms_of_lhs_app( + true, & instance, & clause.vars, + ( clause.lhs_terms(), clause.lhs_preds() ), + None, (pred, args) + ) + } "strengthening", "extraction" + }? { ExtractRes::Trivial => bail!("trivial clause during work_on_split"), ExtractRes::Failed => bail!("extraction failure during work_on_split"), ExtractRes::SuccessTrue => bail!("extracted true during work_on_split"), diff --git a/src/instance/preproc/one_lhs.rs b/src/preproc/one_lhs.rs similarity index 96% rename from src/instance/preproc/one_lhs.rs rename to src/preproc/one_lhs.rs index f1bdc5bb..60b68b92 100644 --- a/src/instance/preproc/one_lhs.rs +++ b/src/preproc/one_lhs.rs @@ -1,10 +1,7 @@ //! One lhs module. use common::*; -use instance::{ - instance::PreInstance, - preproc::{utils::ExtractRes, RedStrat}, -}; +use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// Tries to reduce predicates that appear as an antecedent in exactly one /// clause. @@ -176,12 +173,10 @@ impl OneLhs { }; log! { @4 - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? - } - log! { @2 - "unfolding {}", conf.emph(& instance[pred].name) + "from {}", + instance.clauses()[clause].to_string_info(instance.preds())? } + log! { @2 | "unfolding {}", conf.emph(& instance[pred].name) } use self::ExtractRes::*; let info = match extraction_res { diff --git a/src/instance/preproc/one_rhs.rs b/src/preproc/one_rhs.rs similarity index 93% rename from src/instance/preproc/one_rhs.rs rename to src/preproc/one_rhs.rs index b37c7be5..8b929b32 100644 --- a/src/instance/preproc/one_rhs.rs +++ b/src/preproc/one_rhs.rs @@ -1,10 +1,7 @@ //! One rhs module. use common::*; -use instance::{ - instance::PreInstance, - preproc::{utils::ExtractRes, RedStrat}, -}; +use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// Works on predicates that appear in only one rhs. /// @@ -133,26 +130,24 @@ impl OneRhs { }; log! { @4 - "from {}", - instance.clauses()[clause].to_string_info( instance.preds() ) ? - } - log! { @2 - "unfolding {}", conf.emph(& instance[pred].name) + "from {}", + instance.clauses()[clause].to_string_info(instance.preds()).unwrap() } + log! { @2 | "unfolding {}", conf.emph(& instance[pred].name) } use self::ExtractRes::*; let info = match extraction_res { Trivial => { - log! { @ 4 "=> trivial" } + log! { @ 4 | "=> trivial" } instance.force_false(pred)? } SuccessTrue => { - log! { @ 4 "=> true" } + log! { @ 4 | "=> true" } instance.force_true(pred)? } SuccessFalse => { - log! { @ 4 "=> false" } + log! { @ 4 | "=> false" } instance.force_false(pred)? } diff --git a/src/instance/preproc/strict_neg_clauses.rs b/src/preproc/strict_neg_clauses.rs similarity index 97% rename from src/instance/preproc/strict_neg_clauses.rs rename to src/preproc/strict_neg_clauses.rs index 0c523808..ccc47edd 100644 --- a/src/instance/preproc/strict_neg_clauses.rs +++ b/src/preproc/strict_neg_clauses.rs @@ -1,10 +1,7 @@ //! Works on strict negative clause. use common::*; -use instance::{ - instance::PreInstance, - preproc::{utils::ExtractRes, RedStrat}, -}; +use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// Works on strict negative clause. /// @@ -82,7 +79,7 @@ impl StrictNeg { let (extractor, instance, strict_clauses) = instance.strict_neg_clauses(); for (idx, clause) in strict_clauses { - log! { @3 "working on clause #{}", idx } + log! { @3 | "working on clause #{}", idx } log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { @@ -100,7 +97,7 @@ impl StrictNeg { let clause = clause .rewrite_clause_for_app(pred, args, 0.into()) .chain_err(|| "during clause rewriting")?; - log! { @3 "rewriting successful" } + log! { @3 | "rewriting successful" } log! { @5 "{}", clause.to_string_info(instance.preds()).unwrap() } let (pred, args) = if let Some((pred, argss)) = clause.lhs_preds().iter().next() { diff --git a/src/instance/preproc/unroll.rs b/src/preproc/unroll.rs similarity index 87% rename from src/instance/preproc/unroll.rs rename to src/preproc/unroll.rs index 586034bb..58e81859 100644 --- a/src/instance/preproc/unroll.rs +++ b/src/preproc/unroll.rs @@ -1,13 +1,9 @@ //! Bias unrolling module. use common::*; -use instance::{ - instance::PreInstance, - preproc::{ - utils::{ExtractRes, ExtractionCxt}, - RedStrat, - }, - Clause, +use preproc::{ + utils::{ExtractRes, ExtractionCxt}, + PreInstance, RedStrat, }; // /// Unrolls positive constraints once. @@ -173,30 +169,27 @@ impl RUnroll { debug_assert! { apps.is_none() } let (terms, pred_apps) = ts.destroy(); if_log! { @3 - let mut s = format!( - "from {}\n", - clause.to_string_info( - instance.preds() - ) ? - ) ; - s.push_str("terms {\n") ; - - for term in & terms { - s.push_str( & format!(" {}\n", term) ) - } - s.push_str("}\npred apps {{") ; - - for (pred, argss) in & pred_apps { - for args in argss { - s.push_str( & format!(" ({}", instance[* pred]) ) ; - for arg in args.iter() { - s.push_str( & format!(" {}", arg) ) - } - s.push_str(")\n") + let mut s = format!( + "from {}\n", clause.to_string_info(instance.preds()).unwrap() + ) ; + s.push_str("terms {\n") ; + + for term in & terms { + s.push_str( & format!(" {}\n", term) ) + } + s.push_str("}\npred apps {{") ; + + for (pred, argss) in & pred_apps { + for args in argss { + s.push_str( & format!(" ({}", instance[* pred]) ) ; + for arg in args.iter() { + s.push_str( & format!(" {}", arg) ) + } + s.push_str(")\n") + } } - } - s.push_str(")") ; - log! { @3 "{}", s } + s.push_str(")") ; + log! { @3 "{}", s } } debug_assert! { pred_apps.is_empty() } insert!(pred, Quant::exists(q), terms) diff --git a/src/instance/preproc/utils.rs b/src/preproc/utils.rs similarity index 99% rename from src/instance/preproc/utils.rs rename to src/preproc/utils.rs index 458f5b44..16f1d290 100644 --- a/src/instance/preproc/utils.rs +++ b/src/preproc/utils.rs @@ -419,7 +419,7 @@ impl ExtractionCxt { } // Index of the first quantified variable: fresh for `pred`'s variables. - self.fresh = instance.original_sig_of(pred).next_index(); + self.fresh = instance[pred].fresh_var_idx(); self.map.clear(); self.quantifiers = quantifiers; @@ -502,7 +502,7 @@ impl ExtractionCxt { } // Index of the first quantified variable: fresh for `pred`'s variables. - self.fresh = instance.original_sig_of(pred).next_index(); + self.fresh = instance[pred].fresh_var_idx(); self.map.clear(); self.quantifiers = quantifiers; diff --git a/src/split.rs b/src/split.rs index bc2459c8..880a43c3 100644 --- a/src/split.rs +++ b/src/split.rs @@ -335,7 +335,7 @@ fn preproc( instance[clause].rhs().is_none() } - let instance = ::instance::preproc::work_on_split(instance, clause, prev_clauses, profiler)?; + let instance = ::preproc::work_on_split(instance, clause, prev_clauses, profiler)?; if let Some(maybe_model) = instance.is_trivial_model()? { Ok(Either::Right(maybe_model)) diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 44eac5f1..9f704947 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -29,8 +29,8 @@ pub fn start_class( profiler: &Profiler, ) -> Res { log! { @debug - "starting the learning process" ; - " launching solver kid..." + "starting the learning process" ; + " launching solver kid..." } let mut teacher = Teacher::new(instance, profiler, partial_model)?; @@ -47,13 +47,12 @@ pub fn start_class( let core = teacher.unsat_core(); Ok(TeachRes::Unsat(core)) } - _ => { - if let Err(tmo) = conf.check_timeout() { - Err(tmo) - } else { - Err(e) - } - } + + _ => if let Err(tmo) = conf.check_timeout() { + Err(tmo) + } else { + Err(e) + }, }, }; @@ -836,17 +835,17 @@ impl<'a> Teacher<'a> { // True if we got some positive or negative samples. // let mut got_pos_neg_samples = false ; - log! { @verb - "looking for counterexamples in positive clauses ({})...", - instance.pos_clauses().len() + log! { @verb | + "looking for counterexamples in positive clauses ({})...", + instance.pos_clauses().len() } for clause in instance.pos_clauses() { handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, false))? } - log! { @verb - "looking for counterexamples in strict negative clauses ({})...", - instance.strict_neg_clauses().len() + log! { @verb | + "looking for counterexamples in strict negative clauses ({})...", + instance.strict_neg_clauses().len() } for clause in instance.strict_neg_clauses() { handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, false))? @@ -855,9 +854,9 @@ impl<'a> Teacher<'a> { // got_pos_neg_samples = ! map.is_empty() ; if map.is_empty() || !conf.teacher.max_bias { - log! { @verb - "looking for counterexamples in non-strict negative clauses ({})...", - instance.non_strict_neg_clauses().len() + log! { @verb | + "looking for counterexamples in non-strict negative clauses ({})...", + instance.non_strict_neg_clauses().len() } for clause in instance.non_strict_neg_clauses() { handle_clause_res!(self.get_cexs_of_clause(cands, *clause, &mut map, true))? @@ -865,9 +864,9 @@ impl<'a> Teacher<'a> { } if map.is_empty() || !conf.teacher.max_bias { - log_verb! { - "looking for counterexamples in implication clauses ({})...", - instance.imp_clauses().len() + log! { @verb | + "looking for counterexamples in implication clauses ({})...", + instance.imp_clauses().len() } for clause in instance.imp_clauses() { @@ -880,9 +879,7 @@ impl<'a> Teacher<'a> { } log! { @debug - "extracted {} cexs", map.iter().fold( - 0, |acc, (_, cexs)| acc + cexs.len() - ) + "extracted {} cexs", map.iter().fold(0, |acc, (_, cexs)| acc + cexs.len()) } // if got_pos_neg_samples && conf.teacher.max_bias { @@ -960,7 +957,7 @@ impl<'a> Teacher<'a> { ) -> Res> { if let Some((actlit, bias)) = bias { log! { @debug - " checksat with bias {}", bias.to_string(& self.instance) + " checksat with bias {}", bias.to_string(& self.instance) } self.solver.comment(&format!( "checksat with bias {}", @@ -1058,7 +1055,7 @@ impl<'a> Teacher<'a> { ) -> Res> { let mut cexs = vec![]; - log! { @debug "working on clause #{}", clause_idx } + log! { @debug | "working on clause #{}", clause_idx } // Macro to avoid borrowing `self.instance`. macro_rules! clause { @@ -1071,7 +1068,7 @@ impl<'a> Teacher<'a> { self.solver.comment_args(format_args!( "\n\nClause # {}: {}", clause_idx, - clause!().to_string_info(&self.instance.preds()).unwrap() + clause!().to_string_info(self.instance.preds()).unwrap() ))? } @@ -1081,7 +1078,7 @@ impl<'a> Teacher<'a> { if self.using_rec_funs { log! { @4 | "assert/check-sat lhs terms" } for term in clause!().lhs_terms() { - log! { @5 "{}", term } + log! { @5 | "{}", term } self.solver.assert(&smt::SmtTerm::new(term))?; } let res = smt::multi_try_check_sat(&mut self.solver); @@ -1157,7 +1154,7 @@ impl<'a> Teacher<'a> { // Don't try bias examples if instance is unsat. if unbiased_cex.is_some() && !self.using_rec_funs { - log! { @3 "generating bias actlits" } + log! { @3 | "generating bias actlits" } let biased = profile! { self wrap { self.bias.apply( @@ -1167,7 +1164,7 @@ impl<'a> Teacher<'a> { ) } "cexs", "bias generation" }?; - log! { @3 "working on {} biased checks", biased.len() } + log! { @3 | "working on {} biased checks", biased.len() } for (actlit, bias) in biased { get_cex! { actlit ; bias } } diff --git a/src/term/mod.rs b/src/term/mod.rs index a354120d..583cfd12 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -2,36 +2,26 @@ //! //! # Terms //! -//! The factory is a `static_ref` for easy creation. The `R`eal term structure -//! is [`RTerm`](enum.RTerm.html) which is hashconsed into -//! [`Term`](type.Term.html). The factory -//! ([`HashConsign`](https://crates.io/crates/hashconsing)) is not directly -//! accessible. Terms are created *via* the functions in this module, such as -//! [var](fn.var.html), [int](fn.int.html), [app](fn.app.html), *etc.* +//! The factory is a `static_ref` for easy creation. The `R`eal term structure is [`RTerm`] which +//! is hashconsed into [`Term`]. The factory ([`HashConsign`]) is not directly accessible. Terms +//! are created *via* the functions in this module, such as [`var`], [`int`], [`app`], *etc.* Terms +//! are simplified (when possible) at creation. In particular, the order of the arguments can +//! change, double negations will be simplified, *etc.* //! -//! Terms are not typed at all. A predicate application is **not** a term, only -//! operator applications are. -//! -//! Terms are simplified (when possible) at creation. In particular, the order -//! of the arguments can change, double negations will be simplified, *etc.* -//! See [`normalize`](fn.normalize.html) for more details. +//! A predicate application is **not** a term, only operator applications are. //! //! # Top-level terms //! -//! A [`TTerm`](enum.tterm.html) is either a term or a predicate application to -//! some terms. Top-level terms are not hashconsed as they are shallow. +//! A [`TTerm`] is either a term or a predicate application to some terms. Top-level terms are not +//! hashconsed as they are shallow. //! //! # Variables //! -//! A variable is a `usize` wrapped in a zero-cost -//! [`VarIdx`](../common/struct.VarIdx.html) for safety. It has no semantics at +//! A variable is a `usize` wrapped in a zero-cost [`VarIdx`] for safety. It has no semantics at //! all by itself. Variables are given meaning by //! -//! - the `sig` field of a [`PrdInfo`](../instance/info/struct.PrdInfo.html), -//! which gives them types; -//! - the [`VarInfo`s](../instance/info/struct.VarInfo.html) stored in a -//! [`Clause`](../instance/struct.Clause.html), which give them a name and a -//! type. +//! - the `sig` field of a [`Pred`], which gives them types; +//! - the [`VarInfo`]s stored in a [`Clause`], which give them a name and a type. //! //! # Examples //! @@ -39,22 +29,34 @@ //! # use hoice::term ; //! # use hoice::term::{ Op, RTerm, typ } ; //! let some_term = term::eq( -//! term::int(11), term::app( -//! Op::Mul, vec![ term::int_var(5), term::int(2) ] -//! ) +//! term::int(11), term::app( +//! Op::Mul, vec![ term::int_var(5), term::int(2) ] +//! ) //! ) ; //! # println!("{}", some_term) ; //! //! // A `Term` dereferences to an `RTerm`: //! match * some_term { -//! RTerm::App { ref typ, op: Op::Eql, ref args, .. } => { -//! assert_eq!( typ, & typ::bool() ) ; -//! assert_eq!( args.len(), 2 ) ; -//! assert_eq!( format!("{}", some_term), "(= (+ (* (- 2) v_5) 11) 0)" ) -//! }, -//! _ => panic!("not an equality"), +//! RTerm::App { ref typ, op: Op::Eql, ref args, .. } => { +//! assert_eq!( typ, & typ::bool() ) ; +//! assert_eq!( args.len(), 2 ) ; +//! assert_eq!( format!("{}", some_term), "(= (+ (* (- 2) v_5) 11) 0)" ) +//! }, +//! _ => panic!("not an equality"), //! } //! ``` +//! +//! [`RTerm`]: enum.RTerm.html (RTerm enum) +//! [`Term`]: type.Term.html (Term type) +//! [`HashConsign`]: https://crates.io/crates/hashconsing (hashconsing crate) +//! [`var`]: fn.var.html (var creation function) +//! [`int`]: fn.int.html (int creation function) +//! [`app`]: fn.app.html (app creation function) +//! [`TTerm`]: enum.tterm.html (top term enum) +//! [`VarIdx`]: ../common/struct.VarIdx.html (variable index struct) +//! [`Clause`]: ../common/struct.Clause.html (Clause struct) +//! [`VarInfo`]: ../info/struct.VarInfo.html (VarInfo struct) +//! [`Pred`]: ../info/struct.Pred.html (Pred struct) use hashconsing::*; @@ -1676,15 +1678,11 @@ impl_fmt!{ } } impl<'a> PebcakFmt<'a> for RTerm { - type Info = &'a VarMap<::instance::info::VarInfo>; + type Info = &'a VarMap<::info::VarInfo>; fn pebcak_err(&self) -> ErrorKind { "during term pebcak formatting".into() } - fn pebcak_io_fmt( - &self, - w: &mut W, - vars: &'a VarMap<::instance::info::VarInfo>, - ) -> IoRes<()> { + fn pebcak_io_fmt(&self, w: &mut W, vars: &'a VarMap<::info::VarInfo>) -> IoRes<()> { self.write(w, |w, var| w.write_all(vars[var].as_bytes())) } } diff --git a/src/term/tterms.rs b/src/term/tterms.rs index 3d0c0cc9..5bb2bf67 100644 --- a/src/term/tterms.rs +++ b/src/term/tterms.rs @@ -1401,21 +1401,11 @@ impl TTerms { } } -impl<'a, 'b> - Expr2Smt<&'b ( - &'a PrdSet, - &'a PrdSet, - &'a PrdMap<::instance::info::PrdInfo>, - )> for TTerms -{ +impl<'a, 'b> Expr2Smt<&'b (&'a PrdSet, &'a PrdSet, &'a Preds)> for TTerms { fn expr_to_smt2( &self, w: &mut Writer, - info: &'b ( - &'a PrdSet, - &'a PrdSet, - &'a PrdMap<::instance::info::PrdInfo>, - ), + info: &'b (&'a PrdSet, &'a PrdSet, &'a Preds), ) -> SmtRes<()> { let (true_preds, false_preds, pred_info) = *info; self.write_smt2(w, |w, pred, args| { @@ -1424,9 +1414,9 @@ impl<'a, 'b> } else if false_preds.contains(&pred) { write!(w, "false") } else if args.is_empty() { - write!(w, "{}", pred_info[pred]) + write!(w, "{}", pred_info[pred].name) } else { - write!(w, "({}", pred_info[pred])?; + write!(w, "({}", pred_info[pred].name)?; for arg in args.iter() { write!(w, " ")?; arg.write(w, |w, var| var.default_write(w))? diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index ed21a6da..ef5703ef 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -105,7 +105,7 @@ impl RVarVals { /// Constructor from a model. pub fn of_model( - info: &VarMap<::instance::info::VarInfo>, + info: &VarMap<::info::VarInfo>, model: Vec<(VarIdx, T, Val)>, partial: bool, ) -> Res { From 13a52f793f3fae3ec7d5874e083cb65d42000510 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 19 Sep 2018 17:56:30 +0900 Subject: [PATCH 63/94] done simplifying all predicate info in the instance --- src/info.rs | 178 +++++++++++++++++++++++++++--- src/instance/mod.rs | 150 +++++++------------------ src/instance/pre_instance.rs | 62 +++++++---- src/learning/ice/mod.rs | 2 +- src/learning/ice/quals.rs | 2 +- src/preproc/arg_red.rs | 4 +- src/preproc/bias_unroll.rs | 2 +- src/preproc/cfg_red.rs | 9 +- src/preproc/fun_preds.rs | 4 +- src/preproc/one_lhs.rs | 4 +- src/preproc/one_rhs.rs | 4 +- src/preproc/strict_neg_clauses.rs | 6 +- src/preproc/utils.rs | 8 +- src/split.rs | 2 +- src/teacher/mod.rs | 6 +- src/term/tterms.rs | 16 +-- 16 files changed, 277 insertions(+), 182 deletions(-) diff --git a/src/info.rs b/src/info.rs index 33f103e3..596dd253 100644 --- a/src/info.rs +++ b/src/info.rs @@ -72,15 +72,19 @@ pub struct Pred { /// /// We should always have `self.original_sig.len() == self.original_sig_map.len()`. original_sig_map: VarMap, + /// Same as `original_sig_map` but maps to the term corresponding to the old variable index. + /// + /// Populated by finalize. + original_sig_term_map: Option>, /// Definition, if any. Set by preprocessing. - pub tterm: Option, + def: Option, /// Strengthener, if any. Currently, this comes from strict negative clauses. It means the /// predicate has to be false when this term is false. So, given a candidate `cand` for this /// predicate, the candidate should be strengthened to `cand /\ strength`. - pub strength: Option, + strength: Option, /// Companion functions. Function that were created specifically for this predicate, and must /// be given to the user before giving the definition for this predicate. - pub funs: Vec, + funs: Vec, } impl Pred { @@ -94,7 +98,8 @@ impl Pred { sig, original_sig, original_sig_map, - tterm: None, + original_sig_term_map: None, + def: None, strength: None, funs: vec![], } @@ -109,6 +114,48 @@ impl Pred { &self.original_sig_map } + /// Maps variables of the current signature to the term for the variables of the original + /// signature. + /// + /// This function is only legal to call after [`finalize`] has been called. + /// + /// [`finalize`]: struct.Pred.html#method.finalize (finalize function) + pub fn original_sig_term_map(&self) -> Res<&VarMap> { + if let Some(map) = &self.original_sig_term_map { + Ok(map) + } else { + bail!( + "illegal call to {} before finalization on {}", + conf.bad(&"original_sig_term_map"), + conf.sad(&self.name) + ) + } + } + + /// Definition for this predicate, if any. + pub fn def(&self) -> Option<&TTerms> { + self.def.as_ref() + } + /// True if the predicate has a definition. + /// + /// Equivalent to `self.def().is_some()`. + pub fn is_defined(&self) -> bool { + self.def().is_some() + } + + /// Strengthener: the predicate must be false when this term is false. + pub fn strength(&self) -> Option<&Term> { + self.strength.as_ref() + } + + /// Companion functions. + /// + /// Function that were created specifically for this predicate, and must be given to the user + /// before giving the definition for this predicate. + pub fn funs(&self) -> &[Fun] { + &self.funs + } + /// A variable that does not appear in the **original** signature of the predicate. pub fn fresh_var_idx(&self) -> VarIdx { self.original_sig.next_index() @@ -133,25 +180,126 @@ impl Pred { }) } + /// Sets the predicate's definition. + /// + /// Only legal if the predicate has no definition. + pub fn set_def(&mut self, def: TTerms) -> Res<()> { + let prev = ::std::mem::replace(&mut self.def, Some(def)); + if prev.is_some() { + bail!( + "trying to set the definition for {} twice", + conf.bad(&self.name) + ) + } else { + Ok(()) + } + } + /// Removes the predicate's definition. + pub fn unset_def(&mut self) -> Option { + ::std::mem::replace(&mut self.def, None) + } + + /// Sets the strengthener for this predicate. + /// + /// Only legal if the predicate has not strengthener. + pub fn set_strength(&mut self, strength: Term) -> Res<()> { + let prev = ::std::mem::replace(&mut self.strength, Some(strength)); + if prev.is_some() { + bail!( + "trying to set the strengthener for {} twice", + conf.bad(&self.name) + ) + } else { + Ok(()) + } + } + /// Removes the predicate's strengthener. + pub fn unset_strength(&mut self) -> Option { + ::std::mem::replace(&mut self.strength, None) + } + + /// Adds a companion function. + pub fn add_fun(&mut self, fun: Fun) { + self.funs.push(fun) + } + + /// Finalizes the predicate information. + /// + /// After finalization, calls to [`original_sig_term_map`] will always succeed. + /// + /// Fails if this is the second time `finalize` is called. + /// + /// [`original_sig_term_map`]: struct.Pred.html#method.original_sig_term_map + /// (original_sig_term_map function) + pub fn finalize(&mut self) -> Res<()> { + if self.original_sig_term_map.is_some() { + bail!( + "cannot finalize information for {} more than once", + conf.bad(&self.name) + ) + } + + let sig_term_map: VarMap<_> = self + .original_sig_map + .iter() + .map(|old_var| term::var(*old_var, self.original_sig[*old_var].clone())) + .collect(); + self.original_sig_term_map = Some(sig_term_map); + + Ok(()) + } + /// Checks its invariant hold. Inactive in release. #[cfg(debug_assertions)] pub fn check(&self) -> Res<()> { if self.sig.len() != self.original_sig_map.len() { bail!( - "signature and map to original signature differ in length for {}", - self + "signature and sig map to original signature differ in length for {}", + conf.bad(&self.name) ) } - if !self - .original_sig_map - .index_iter() - .all(|(src, tgt)| self.sig[src] == self.original_sig[*tgt]) - { - bail!( - "signature and map to original signature do not type check for {}", - self - ) + for (src, tgt) in self.original_sig_map.index_iter() { + if self.sig[src] != self.original_sig[*tgt] { + bail!( + "signature and sig map to original signature do not type check on \ + {} -> {} for {}", + src.default_str(), + tgt.default_str(), + conf.bad(&self.name) + ) + } + } + + if let Some(map) = &self.original_sig_term_map { + if self.sig.len() != map.len() { + bail!( + "signature and term map to original signature differ in length for {}", + conf.bad(&self.name) + ) + } + + for (src, tgt) in map.index_iter() { + if let Some(tgt) = tgt.var_idx() { + if self.sig[src] != self.original_sig[tgt] { + bail!( + "signature and term map to original signature do not type check on \ + {} -> {} for {}", + src.default_str(), + tgt.default_str(), + conf.bad(&self.name) + ) + } + } else { + bail!( + "illegal term for {}: maps {} to non-variable term {}", + conf.bad(&self.name), + src.default_str(), + tgt + ) + } + } } + Ok(()) } /// Checks its invariant hold. Inactive in release. diff --git a/src/instance/mod.rs b/src/instance/mod.rs index fb7f53a8..483d1170 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -30,28 +30,11 @@ pub struct Instance { /// Predicates. preds: Preds, - /// Maps new variables to the new ones. - /// - /// Only available after finalize. - old_var_maps: PrdMap>, - /// Predicates for which a suitable term has been found. - pred_terms: PrdMap>, - /// Predicates defined in `pred_terms`, sorted by predicate dependencies. /// /// Populated by the `finalize` function. sorted_pred_terms: Vec, - /// Strengthener for predicates. - pred_str: PrdMap>, - - /// Companion function for predicates. - /// - /// A companion function is a function that was created internally - /// specifically for a predicate. Meaning it needs to be given to the user - /// when printing the model. - companion_funs: PrdHMap>, - /// Side-clauses. /// /// A side clause @@ -137,11 +120,7 @@ impl Instance { Instance { preds: Preds::with_capacity(pred_capa), - old_var_maps: PrdMap::with_capacity(pred_capa), - pred_terms: PrdMap::with_capacity(pred_capa), sorted_pred_terms: Vec::with_capacity(pred_capa), - pred_str: PrdMap::with_capacity(pred_capa), - companion_funs: PrdHMap::new(), side_clauses: Vec::with_capacity(7), clauses: ClsMap::with_capacity(clause_capa), @@ -177,11 +156,7 @@ impl Instance { Instance { preds: self.preds.clone(), - old_var_maps: PrdMap::with_capacity(self.preds.len()), - pred_terms: self.pred_terms.clone(), sorted_pred_terms: Vec::with_capacity(self.preds.len()), - pred_str: vec![None; self.preds.len()].into(), - companion_funs: self.companion_funs.clone(), side_clauses: self.side_clauses.clone(), clauses: self.clauses.clone(), @@ -237,7 +212,7 @@ impl Instance { pub fn active_pred_count(&self) -> usize { let mut count = 0; for pred in self.pred_indices() { - if !self.is_known(pred) { + if !self[pred].is_defined() { count += 1 } } @@ -249,8 +224,8 @@ impl Instance { if self.is_unsat { return true; } - for def in &self.pred_terms { - if def.is_none() { + for pred in &self.preds { + if pred.def().is_none() { return false; } } @@ -289,12 +264,6 @@ impl Instance { self.is_unsat = true } - /// True if a predicate is forced to something. - #[inline] - pub fn is_known(&self, pred: PrdIdx) -> bool { - self.pred_terms[pred].is_some() - } - /// Adds a define fun. pub fn add_define_fun>( &mut self, @@ -314,24 +283,18 @@ impl Instance { /// /// The model is sorted in topological order. pub fn model_of(&self, candidates: Candidates) -> Res { - use std::iter::Extend; - let mut model = Model::with_capacity(self.preds.len()); - model.extend( - candidates - .into_index_iter() - .filter_map(|(pred, tterms_opt)| { - tterms_opt.map(|term| { - let (term, _) = term.subst(&self.old_var_maps[pred]); - (pred, TTerms::of_term(None, term)) - }) - }), - ); + for (pred, tterms_opt) in candidates.into_index_iter() { + if let Some(term) = tterms_opt { + let (term, _) = term.subst(self[pred].original_sig_term_map()?); + model.push((pred, TTerms::of_term(None, term))) + } + } for pred in &self.sorted_pred_terms { let pred = *pred; - if let Some(ref tterms) = self.pred_terms[pred] { - model.push((pred, tterms.subst(&self.old_var_maps[pred]))) + if let Some(tterms) = self[pred].def() { + model.push((pred, tterms.subst(self[pred].original_sig_term_map()?))) } else { bail!("inconsistency in sorted forced predicates") } @@ -383,8 +346,8 @@ impl Instance { for pred in &self.sorted_pred_terms { let pred = *pred; - if let Some(ref tterms) = self.pred_terms[pred] { - let tterms = tterms.subst(&self.old_var_maps[pred]); + if let Some(tterms) = self[pred].def() { + let tterms = tterms.subst(self[pred].original_sig_term_map()?); model.push(vec![(pred, vec![tterms])]) } else { bail!("inconsistency in sorted forced predicates") @@ -397,7 +360,7 @@ impl Instance { fn is_trivial(&self) -> Option { if self.is_unsat { Some(false) - } else if self.pred_terms.iter().all(|term| term.is_some()) { + } else if self.preds.iter().all(|pred| pred.is_defined()) { Some(true) } else { None @@ -482,7 +445,6 @@ impl Instance { self.sorted_pred_terms.clear(); self.preds.shrink_to_fit(); - self.pred_terms.shrink_to_fit(); self.clauses.shrink_to_fit(); let mut tmp: Vec<(PrdIdx, PrdSet)> = Vec::with_capacity(self.preds.len()); @@ -517,7 +479,7 @@ impl Instance { let mut known_preds = PrdSet::with_capacity(self.preds.len()); for pred in self.pred_indices() { - if let Some(ref tterms) = self.pred_terms[pred] { + if let Some(tterms) = self[pred].def() { tmp.push((pred, tterms.preds())) } else { known_preds.insert(pred); @@ -543,12 +505,8 @@ impl Instance { } } - for pred in &self.preds { - let mut map = VarMap::with_capacity(pred.sig.len()); - for (var, typ) in pred.sig.index_iter() { - map.push(term::var(pred.original_sig_map()[var], typ.clone())) - } - self.old_var_maps.push(map) + for pred in &mut self.preds { + pred.finalize()? } self.sorted_pred_terms.shrink_to_fit(); @@ -556,15 +514,10 @@ impl Instance { Ok(()) } - /// Returns the term we already know works for a predicate, if any. - pub fn forced_terms_of(&self, pred: PrdIdx) -> Option<&TTerms> { - self.pred_terms[pred].as_ref() - } - /// If the input predicate is forced to a constant boolean, returns its /// value. pub fn bool_value_of(&self, pred: PrdIdx) -> Option { - self.forced_terms_of(pred).and_then(|terms| terms.bool()) + self[pred].def().and_then(|terms| terms.bool()) } /// Forced predicates in topological order. @@ -681,37 +634,11 @@ impl Instance { self.preds.push(Pred::new(name, idx, sig)); - self.pred_terms.push(None); - self.pred_str.push(None); - self.pred_to_clauses .push((ClsSet::with_capacity(17), ClsSet::with_capacity(17))); idx } - /// Sets the strengthener for a predicate. - pub fn set_str(&mut self, pred: PrdIdx, term: Term) -> Option { - ::std::mem::replace(&mut self.pred_str[pred], Some(term)) - } - - /// Retrieves the strengthener for a predicate if any. - pub fn get_str(&self, pred: PrdIdx) -> Option<&Term> { - self.pred_str[pred].as_ref() - } - - /// Adds a companion function for a predicate. - pub fn add_companion_fun(&mut self, pred: PrdIdx, fun: Fun) { - self.companion_funs - .entry(pred) - .or_insert_with(Vec::new) - .push(fun) - } - - /// Retrieves the companion functions for a predicate. - pub fn get_companion_funs(&self, pred: PrdIdx) -> Option<&Vec> { - self.companion_funs.get(&pred) - } - /// Removes and returns the indices of the clauses `pred` appears in the lhs /// of from `self.pred_to_clauses`. fn unlink_pred_lhs(&mut self, pred: PrdIdx, lhs: &mut LHS) @@ -777,7 +704,7 @@ impl Instance { for pred in self.clauses[clause].lhs_preds().keys() { let pred = *pred; let was_there = self.pred_to_clauses[pred].0.remove(&clause); - debug_assert!(was_there || self.is_known(pred)) + debug_assert!(was_there || self[pred].is_defined()) } if let Some((pred, _)) = self.clauses[clause].rhs() { self.pred_to_clauses[pred].1.remove(&clause); @@ -1006,7 +933,7 @@ impl Instance { .map(|(pred, _)| *pred) .chain(clause.rhs().into_iter().map(|(pred, _)| pred)) { - if let Some(tterms) = self.forced_terms_of(pred) { + if let Some(tterms) = self[pred].def() { bail! { "predicate {} is forced{} but appears in a clause: {}", conf.bad( & self[pred].name ), @@ -1069,7 +996,7 @@ impl Instance { for (cls_idx, clause) in self.clauses.index_iter() { for (pred, _) in clause.lhs_preds() { let pred = *pred; - if self.is_known(pred) { + if self[pred].is_defined() { bail!( "predicate {} is forced but appears in lhs of clause {}", self[pred], @@ -1089,7 +1016,7 @@ impl Instance { } } if let Some((pred, _)) = clause.rhs() { - if self.is_known(pred) { + if self[pred].is_defined() { bail!( "predicate {} is forced but appears in rhs of clause {}", self[pred], @@ -1202,8 +1129,8 @@ impl Instance { writeln!(w)?; for (pred_idx, pred) in self.preds.index_iter() { - if self.pred_terms[pred_idx].is_none() { - if let Some(term) = &self.pred_str[pred_idx] { + if !self[pred_idx].is_defined() { + if let Some(term) = self[pred_idx].strength() { writeln!(w, "; Strengthening term:")?; writeln!(w, "; {}", term)? } @@ -1272,7 +1199,7 @@ impl Instance { let mut old_model = Vec::with_capacity(model.len()); ::std::mem::swap(&mut old_model, model); for (pred, def) in old_model { - let simplified = def.simplify_pred_apps(&model, &self.pred_terms); + let simplified = def.simplify_pred_apps(&model, &self.preds); model.push((pred, simplified)) } @@ -1280,14 +1207,16 @@ impl Instance { self.finalize()? } - let mut old_tterms: PrdMap> = vec![None; self.pred_terms.len()].into(); - ::std::mem::swap(&mut old_tterms, &mut self.pred_terms); + let mut old_tterms: PrdMap<_> = + self.preds.iter_mut().map(|pred| pred.unset_def()).collect(); + for pred in &self.sorted_pred_terms { + let pred = *pred; let mut curr_def = None; - ::std::mem::swap(&mut curr_def, &mut old_tterms[*pred]); + ::std::mem::swap(&mut curr_def, &mut old_tterms[pred]); if let Some(def) = curr_def { - let simplified = def.simplify_pred_apps(&model, &self.pred_terms); - self.pred_terms[*pred] = Some(simplified) + let simplified = def.simplify_pred_apps(&model, &self.preds); + self.preds[pred].set_def(simplified)? } } Ok(()) @@ -1714,7 +1643,7 @@ impl<'a> PebcakFmt<'a> for Instance { dtyp::write_constructor_map(w, "; ")?; for pred in &self.preds { - if pred.tterm.is_none() { + if !pred.is_defined() { write!(w, "({}\n {}\n (", keywords::cmd::dec_fun, pred.name)?; for typ in &pred.sig { write!(w, " {}", typ)? @@ -1739,9 +1668,9 @@ impl<'a> PebcakFmt<'a> for Instance { // Either there's no forced predicate, or we are printing before // finalizing. for (pred, tterms) in self - .pred_terms - .index_iter() - .filter_map(|(pred, tterms_opt)| tterms_opt.as_ref().map(|tt| (pred, tt))) + .preds + .iter() + .filter_map(|pred| pred.def().map(|tt| (pred.idx, tt))) { write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred])?; for (var, typ) in self[pred].sig.index_iter() { @@ -1755,11 +1684,12 @@ impl<'a> PebcakFmt<'a> for Instance { } } else { for pred in &self.sorted_pred_terms { - write!(w, "({} {}\n (", keywords::cmd::def_fun, self[*pred])?; - for (var, typ) in self[*pred].sig.index_iter() { + let pred = *pred; + write!(w, "({} {}\n (", keywords::cmd::def_fun, self[pred])?; + for (var, typ) in self[pred].sig.index_iter() { write!(w, " (v_{} {})", var, typ)? } - let tterms = self.pred_terms[*pred].as_ref().unwrap(); + let tterms = self[pred].def().unwrap(); write!(w, " ) Bool\n ")?; tterms .expr_to_smt2(w, &(&empty_prd_set, &empty_prd_set, &self.preds)) diff --git a/src/instance/pre_instance.rs b/src/instance/pre_instance.rs index 24a7b8dc..96e1f052 100644 --- a/src/instance/pre_instance.rs +++ b/src/instance/pre_instance.rs @@ -107,6 +107,26 @@ impl<'a> PreInstance<'a> { Ok(()) } + /// Sets the strengthener for a predicate. + /// + /// A strengthener is a term such that the predicate should be false at least when this term is + /// false. + pub fn set_strength(&mut self, pred: PrdIdx, strengthener: Term) -> Res<()> { + self.instance.preds[pred].set_strength(strengthener) + } + /// Unsets the strengthener for a predicate. + pub fn unset_strength(&mut self, pred: PrdIdx) -> Option { + self.instance.preds[pred].unset_strength() + } + + /// Adds a companion function for a predicate. + /// + /// Function that were created specifically for this predicate, and must be given to the user + /// before giving the definition for this predicate. + pub fn add_companion_fun(&mut self, pred: PrdIdx, fun: Fun) { + self.instance.preds[pred].add_fun(fun) + } + /// Checks whether a clause alone forces the definition of a predicate. /// - forces to true all predicates appearing in `terms => (p vars)` where /// `vars` are all distinct and don't appear in `terms` @@ -188,7 +208,7 @@ impl<'a> PreInstance<'a> { fixed_point = true; 'all_preds: for pred in PrdRange::zero_to(self.instance.preds.len()) { - if self.instance.is_known(pred) { + if self.instance[pred].is_defined() { continue 'all_preds; } @@ -237,7 +257,7 @@ impl<'a> PreInstance<'a> { } for (pred, positive) in force { - if !self.is_known(pred) { + if !self[pred].is_defined() { fixed_point = false; info.preds += 1; if positive { @@ -754,8 +774,8 @@ impl<'a> PreInstance<'a> { .map(|(quantfed, conj)| (Quant::exists(quantfed), conj)) .collect(), ); - debug_assert! { self.instance.pred_terms[pred].is_none() } - self.instance.pred_terms[pred] = Some(def) + debug_assert! { !self.instance[pred].is_defined() } + self.instance.preds[pred].set_def(def)? } // Drop all clauses. @@ -779,7 +799,7 @@ impl<'a> PreInstance<'a> { log_debug! { " checking pred defs" } for (pred, _) in self.preds().index_iter() { - if !self.is_known(pred) { + if !self[pred].is_defined() { bail!(format!( "error in `force_all_preds`, no definition for {}", self[pred] @@ -799,7 +819,7 @@ impl<'a> PreInstance<'a> { /// Does not impact `pred_to_clauses`. fn force_pred(&mut self, pred: PrdIdx, tterms: TTerms) -> Res<()> { log! { @5 "forcing {}", conf.emph(& self.instance[pred].name) } - if self.instance.pred_terms[pred].as_ref().is_some() { + if self.instance[pred].is_defined() { let mut s: Vec = Vec::new(); tterms .write_smt2(&mut s, |w, pred, args| { @@ -815,7 +835,7 @@ impl<'a> PreInstance<'a> { String::from_utf8_lossy(&s) ) } else { - self.instance.pred_terms[pred] = Some(tterms) + self.instance.preds[pred].set_def(tterms)? } Ok(()) } @@ -1453,11 +1473,10 @@ impl<'a> PreInstance<'a> { /// /// Simplifies before returning. /// - /// For each clause `(pred args) /\ lhs => rhs`, adds `terms /\ lhs => rhs` - /// for terms in `pred_terms[p]`. + /// For each clause `(pred args) /\ lhs => rhs`, adds `terms /\ lhs => rhs` for terms in + /// `terms[p]`. /// - /// Only unrolls negative clauses where `(pred args)` is not the only - /// application. + /// Only unrolls negative clauses where `(pred args)` is not the only application. pub fn unroll(&mut self, pred: PrdIdx, terms: &[(Option, TTermSet)]) -> Res { let mut info = RedInfo::new(); let mut to_add = Vec::with_capacity(17); @@ -1543,8 +1562,8 @@ impl<'a> PreInstance<'a> { /// /// Simplifies before returning. /// - /// For each clause `lhs => (pred args)`, adds `(not terms) /\ lhs => false` - /// for terms in `pred_terms[p]`. + /// For each clause `lhs => (pred args)`, adds `(not terms) /\ lhs => false` for terms in + /// `terms[p]`. /// /// Only unrolls clauses which have at least one lhs predicate application. pub fn reverse_unroll( @@ -1709,9 +1728,10 @@ impl<'a> PreInstance<'a> { let mut info = RedInfo::new(); // Remove args from forced predicates. - for tterms_opt in &mut self.instance.pred_terms { - if let Some(tterms) = tterms_opt.as_mut() { - tterms.remove_vars(&to_keep) + for pred in &mut self.instance.preds { + if let Some(mut tterms) = pred.unset_def() { + tterms.remove_vars(&to_keep); + pred.set_def(tterms)? } } @@ -1774,17 +1794,17 @@ impl<'a> PreInstance<'a> { let set = PrdSet::new(); self.instance.finalize()?; - for pred in self.instance.sorted_forced_terms() { - let pred = *pred; + for pred_info in self.instance.preds() { + let pred = pred_info.idx; log! { @4 | "definining {}", self[pred] } - let sig: Vec<_> = self.instance[pred] + let sig: Vec<_> = pred_info .sig .index_iter() .map(|(var, typ)| (var.default_str(), typ.get())) .collect(); - if let Some(ref def) = self.instance.pred_terms[pred] { + if let Some(ref def) = pred_info.def() { self.solver.define_fun_with( &self.instance[pred].name, &sig, @@ -1795,7 +1815,7 @@ impl<'a> PreInstance<'a> { } else { bail!( "can't check predicate definitions, predicate {} is not defined", - self.instance.preds[pred] + conf.bad(&pred_info.name) ) } } diff --git a/src/learning/ice/mod.rs b/src/learning/ice/mod.rs index d1925507..3931fd42 100644 --- a/src/learning/ice/mod.rs +++ b/src/learning/ice/mod.rs @@ -359,7 +359,7 @@ impl<'core> IceLearner<'core> { self.predicates.clear(); for pred in self.instance.pred_indices() { - if self.instance.is_known(pred) || self.candidate[pred].is_some() { + if self.instance[pred].is_defined() || self.candidate[pred].is_some() { continue; } diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 35c4652f..e933d1cf 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -468,7 +468,7 @@ impl NuQuals { if mine { 'all_preds: for pred_info in instance.preds() { - if instance.is_known(pred_info.idx) { + if instance[pred_info.idx].is_defined() { continue 'all_preds; } diff --git a/src/preproc/arg_red.rs b/src/preproc/arg_red.rs index 1b95a42f..bee2fb77 100644 --- a/src/preproc/arg_red.rs +++ b/src/preproc/arg_red.rs @@ -67,7 +67,7 @@ impl ArgReductor { fn print(&mut self, instance: &Instance) { println!("keep {{"); for (pred, vars) in self.keep.index_iter() { - if instance.is_known(pred) { + if instance[pred].is_defined() { continue; } print!(" {}:", instance[pred]); @@ -229,7 +229,7 @@ impl ArgReductor { let mut res = PrdHMap::new(); for (pred, vars) in ::std::mem::replace(&mut self.keep, PrdMap::new()).into_index_iter() { - if !instance.is_known(pred) { + if !instance[pred].is_defined() { let mut prev = res.insert(pred, vars); debug_assert! { prev.is_none() } } diff --git a/src/preproc/bias_unroll.rs b/src/preproc/bias_unroll.rs index baaee049..02cbf493 100644 --- a/src/preproc/bias_unroll.rs +++ b/src/preproc/bias_unroll.rs @@ -165,7 +165,7 @@ impl BiasedUnroll { self.max_new_clauses = ::std::cmp::min(10, instance.clauses().len() / 20); for (pred, _) in instance.preds().index_iter() { - if instance.is_known(pred) { + if instance[pred].is_defined() { continue; } let (lhs_clauses, rhs_clauses) = instance.clauses_of(pred); diff --git a/src/preproc/cfg_red.rs b/src/preproc/cfg_red.rs index b25a6720..a3211208 100644 --- a/src/preproc/cfg_red.rs +++ b/src/preproc/cfg_red.rs @@ -115,7 +115,7 @@ impl Graph { ] ;" )?; for (prd, info) in instance.preds().index_iter() { - if instance.forced_terms_of(prd).is_none() { + if !instance[prd].is_defined() { if hi_lite.contains(&prd) { writeln!( w, @@ -787,10 +787,7 @@ impl Graph { // `keep`. let mut pred = None; 'find_pred: for (prd, srcs) in self.bakward.index_iter() { - if keep.contains(&prd) - || instance.forced_terms_of(prd).is_some() - || res_contains!(&prd) - { + if keep.contains(&prd) || instance[prd].is_defined() || res_contains!(&prd) { continue 'find_pred; } log! { @3 | "looking at {}", instance[prd] } @@ -1152,7 +1149,7 @@ impl CfgRed { let mut info = RedInfo::new(); for (pred, def) in pred_defs { - if instance.is_known(pred) { + if instance[pred].is_defined() { continue; } diff --git a/src/preproc/fun_preds.rs b/src/preproc/fun_preds.rs index 0a1d37b1..d8208b76 100644 --- a/src/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -778,7 +778,7 @@ impl FunDef { /// let pred: PrdIdx = 0.into(); /// debug_assert_eq! { "len_fun_preds_example", & instance[pred].name } /// -/// let funs = instance.get_companion_funs(pred).unwrap(); +/// let funs = instance[pred].funs(); /// assert_eq!( "len_fun_preds_example_hoice_reserved_fun", &funs[0].name); /// assert_eq! { /// "(ite \ @@ -934,7 +934,7 @@ impl RedStrat for FunPreds { // } // Predicate is still unknown. - if instance.is_known(pred) { + if instance[pred].is_defined() { // println!(" known"); continue 'all_preds; } diff --git a/src/preproc/one_lhs.rs b/src/preproc/one_lhs.rs index 60b68b92..f0d0a8f7 100644 --- a/src/preproc/one_lhs.rs +++ b/src/preproc/one_lhs.rs @@ -222,7 +222,7 @@ impl RedStrat for OneLhs { let mut red_info = RedInfo::new(); 'all_preds: for pred in instance.pred_indices() { - if instance.is_known(pred) || instance.clauses_of(pred).0.len() > 1 { + if instance[pred].is_defined() || instance.clauses_of(pred).0.len() > 1 { continue 'all_preds; } @@ -251,7 +251,7 @@ impl RedStrat for OneLhs { red_info.preds += 1; red_info += info; instance.check("after unfolding (one_lhs)")?; - debug_assert! { instance.is_known(pred) } + debug_assert! { instance[pred].is_defined() } } else { log! { @4 "failed to unfold {}", instance[pred] } } diff --git a/src/preproc/one_rhs.rs b/src/preproc/one_rhs.rs index 8b929b32..15d42d7b 100644 --- a/src/preproc/one_rhs.rs +++ b/src/preproc/one_rhs.rs @@ -176,7 +176,7 @@ impl RedStrat for OneRhs { let mut red_info = RedInfo::new(); 'all_preds: for pred in instance.pred_indices() { - if instance.is_known(pred) || instance.clauses_of(pred).1.len() > 1 { + if instance[pred].is_defined() || instance.clauses_of(pred).1.len() > 1 { continue 'all_preds; } @@ -205,7 +205,7 @@ impl RedStrat for OneRhs { red_info.preds += 1; red_info += info; instance.check("after unfolding (one_rhs)")?; - debug_assert! { instance.is_known(pred) } + debug_assert! { instance[pred].is_defined() } } else { log! { @4 "failed to unfold {}", instance[pred] } } diff --git a/src/preproc/strict_neg_clauses.rs b/src/preproc/strict_neg_clauses.rs index ccc47edd..73d56401 100644 --- a/src/preproc/strict_neg_clauses.rs +++ b/src/preproc/strict_neg_clauses.rs @@ -34,7 +34,7 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// let pred: PrdIdx = 0.into(); /// debug_assert_eq! { "pred", & instance[pred].name } /// -/// let strengthening = instance.get_str(pred).unwrap(); +/// let strengthening = instance[pred].strength().unwrap(); /// debug_assert_eq! { /// "(or \ /// (>= (* (- 1) v_0) 0) \ @@ -176,7 +176,7 @@ impl RedStrat for StrictNeg { log! { @4 |=> "{}", term } } } - if let Some(term) = instance.get_str(pred) { + if let Some(term) = instance.unset_strength(pred) { terms.push(term.clone()) } let conj = term::and(terms); @@ -187,7 +187,7 @@ impl RedStrat for StrictNeg { info += instance.force_false(pred)? } None => { - instance.set_str(pred, conj); + instance.set_strength(pred, conj)?; } } } else { diff --git a/src/preproc/utils.rs b/src/preproc/utils.rs index 16f1d290..a0e800b1 100644 --- a/src/preproc/utils.rs +++ b/src/preproc/utils.rs @@ -591,7 +591,7 @@ pub fn register_stats(instance: &Instance, _profiler: &Profiler, count: usize) - "pred count original" => add { let mut count = 0 ; for pred in instance.pred_indices() { - if ! instance.is_known(pred) { + if ! instance[pred].is_defined() { count += 1 } } @@ -603,7 +603,7 @@ pub fn register_stats(instance: &Instance, _profiler: &Profiler, count: usize) - "arg count original" => add { let mut args = 0 ; for info in instance.preds() { - if ! instance.is_known(info.idx) { + if ! instance[info.idx].is_defined() { args += info.sig.len() } } @@ -687,7 +687,7 @@ pub fn register_final_stats(instance: &Instance, _profiler: &Profiler) -> Res<() "pred count final" => add { let mut count = 0 ; for pred in instance.pred_indices() { - if ! instance.is_known(pred) { + if ! instance[pred].is_defined() { count += 1 } } @@ -700,7 +700,7 @@ pub fn register_final_stats(instance: &Instance, _profiler: &Profiler) -> Res<() "arg count final" => add { let mut args = 0 ; for info in instance.preds() { - if ! instance.is_known(info.idx) { + if ! instance[info.idx].is_defined() { args += info.sig.len() } } diff --git a/src/split.rs b/src/split.rs index 880a43c3..ec3a782e 100644 --- a/src/split.rs +++ b/src/split.rs @@ -119,7 +119,7 @@ pub fn run_on( /// Adds a model for a subinstance to a partial model. pub fn add_submodel(instance: &Arc, model: &mut ConjCandidates, submodel: Model) { for (pred, tterms) in submodel { - if !instance.is_known(pred) { + if !instance[pred].is_defined() { let conj = model.entry(pred).or_insert_with(|| vec![]); match tterms.bool() { Some(true) => continue, diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 9f704947..1d00ed7b 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -260,7 +260,7 @@ impl<'a> Teacher<'a> { fn model_of_candidates(&self, mut cands: Candidates) -> Candidates { for (pred, cand) in cands.index_iter_mut() { if let Some(cand) = cand.as_mut() { - if let Some(other) = self.instance.get_str(pred) { + if let Some(other) = self.instance[pred].strength() { *cand = term::and(vec![cand.clone(), other.clone()]) } } @@ -273,7 +273,7 @@ impl<'a> Teacher<'a> { for (pred, cand) in cands.index_iter_mut() { if let Some(cand) = cand.as_mut() { let mut others = None; - if let Some(other) = self.instance.get_str(pred) { + if let Some(other) = self.instance[pred].strength() { others.get_or_insert_with(Vec::new).push(other.clone()) } if let Some(other) = self.partial_model.get(&pred) { @@ -700,7 +700,7 @@ impl<'a> Teacher<'a> { let mut cands = PrdMap::with_capacity(self.instance.preds().len()); for pred in self.instance.pred_indices() { - if self.instance.forced_terms_of(pred).is_some() { + if self.instance[pred].is_defined() { cands.push(None) // } else if let Some(dnf) = partial_candidate.get(& pred) { diff --git a/src/term/tterms.rs b/src/term/tterms.rs index 5bb2bf67..9b3ce36c 100644 --- a/src/term/tterms.rs +++ b/src/term/tterms.rs @@ -1153,10 +1153,10 @@ impl TTerms { } /// Simplifies some top terms given some definitions for the predicates. - pub fn simplify_pred_apps(self, model: ModelRef, pred_terms: &PrdMap>) -> Self { + pub fn simplify_pred_apps(self, model: ModelRef, preds: &Preds) -> Self { macro_rules! if_defined { ($pred:ident then |$def:ident| $stuff:expr) => { - if let Some($def) = pred_terms[*$pred].as_ref() { + if let Some($def) = preds[*$pred].def() { $stuff } else { for (ref idx, ref $def) in model { @@ -1177,11 +1177,11 @@ impl TTerms { for pred in tterms.preds.keys() { if_defined! { - pred then |def| match def.bool() { - Some(true) => { to_rm.insert(* pred) ; () }, - Some(false) => return TTerms::fls(), - None => (), - } + pred then |def| match def.bool() { + Some(true) => { to_rm.insert(* pred) ; () }, + Some(false) => return TTerms::fls(), + None => (), + } } } @@ -1241,7 +1241,7 @@ impl TTerms { let mut nu_disj = Vec::with_capacity(disj.len()); for (quant, tterms) in disj { - match (TTerms::Conj { quant, tterms }).simplify_pred_apps(model, pred_terms) { + match (TTerms::Conj { quant, tterms }).simplify_pred_apps(model, preds) { TTerms::True => return TTerms::tru(), TTerms::False => (), From 08b1bd27d9660b84a1bf2a8be9f466b5905ac4da Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 19 Sep 2018 18:38:38 +0900 Subject: [PATCH 64/94] minor doc/architecture fixes --- src/common/mod.rs | 4 +-- src/common/smt.rs | 5 ++- src/fun/mod.rs | 7 ++-- src/instance/mod.rs | 22 ++++++++++-- src/instance/pre_instance.rs | 6 ++-- src/learning/ice/data.rs | 4 ++- src/learning/ice/quals.rs | 1 - src/learning/ice/synth/mod.rs | 11 +++--- src/preproc/fun_preds.rs | 8 +++-- src/preproc/mod.rs | 16 +++++---- src/term/bindings.rs | 1 - src/term/factory.rs | 64 ++++++++++++++++------------------- src/term/mod.rs | 48 +++++++++++--------------- 13 files changed, 105 insertions(+), 92 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index f1c2d6e7..ce16395c 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -454,9 +454,7 @@ impl CanBEvaled for Term { } } -/// Information returned by -/// [`RedStrat`](../instance/preproc/trait.RedStrat.html)s and -/// [`SolverRedStrat`](../instance/preproc/trait.SolverRedStrat.html)s. +/// Information returned by preprocessing techniques. #[must_use] #[derive(Debug, Default)] pub struct RedInfo { diff --git a/src/common/smt.rs b/src/common/smt.rs index 290509ff..1c4e440a 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -12,7 +12,6 @@ use common::{ *, }; use data::Constraint; -use instance::Clause; /// Initial setup for a solver. /// @@ -977,11 +976,11 @@ impl<'a> ModelParser for FullParser { /// Extends a solver so that it's able to check clause triviality. pub trait ClauseTrivialExt { /// Checks whether a clause is trivial. - fn is_clause_trivial(&mut self, &mut ::instance::Clause) -> Res>; + fn is_clause_trivial(&mut self, &mut Clause) -> Res>; } impl ClauseTrivialExt for Solver { - fn is_clause_trivial(&mut self, clause: &mut ::instance::Clause) -> Res> { + fn is_clause_trivial(&mut self, clause: &mut Clause) -> Res> { let mut lhs: Vec = Vec::with_capacity(17); for term in clause.lhs_terms() { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 1f84123e..5f8b0cc2 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -13,12 +13,13 @@ pub type Fun = Arc; /// Type of the function factory. /// -/// The usize indicates whether an element of the factory is being borrowed -/// **unsafely** by [`get_as_ref`](fun/fn.get_as_ref.html). If it is true, then -/// borrowing the factory mutably is unsafe. +/// The usize indicates whether an element of the factory is being borrowed **unsafely** by +/// [`get_as_ref`]. If it is true, then borrowing the factory mutably is unsafe. /// /// To avoid problems, **always** use the `factory` macro to access the /// factory. +/// +/// [`get_as_ref`]: fn.get_as_ref.html (get_as_ref function) type Factory = RwLock<(BTreeMap, usize)>; lazy_static! { /// Function factory. diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 483d1170..ae113292 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -23,8 +23,26 @@ pub use self::pre_instance::PreInstance; /// Instance mutation should only happen at two points: [parsing] (creation) and [preprocessing]. /// (A minor exception is model reconstruction.) /// +/// # Finalization +/// +/// Instance finalization is crucial. A lot of instance functions do not make sense unless the +/// instance is finalized. This is currently done at the end of preprocessing. +/// +/// Functions that do not make sense before finalization include: +/// +/// - [`pos_clauses`](struct.Instance.html#method.strict_neg_clauses) +/// - [`strict_neg_clauses`](struct.Instance.html#method.strict_neg_clauses) +/// - [`neg_clauses`](struct.Instance.html#method.neg_clauses) +/// - [`non_strict_neg_clauses`](struct.Instance.html#method.non_strict_neg_clauses) +/// - [`imp_clauses`](struct.Instance.html#method.imp_clauses) +/// - [`model_of`](struct.Instance.html#method.model_of) +/// - [`extend_model`](struct.Instance.html#method.extend_model) +/// - [`sorted_forced_terms`](struct.Instance.html#method.sorted_forced_terms) +/// - [`simplify_pred_defs`](struct.Instance.html#method.simplify_pred_defs) +/// /// [parsing]: ../parse/index.html (parse module) /// [preprocessing]: ../preproc/index.html (preproc module) +/// [finalize]: struct.Instance.html#method.finalize #[derive(Clone)] pub struct Instance { /// Predicates. @@ -87,7 +105,7 @@ pub struct Instance { split: Option, /// Define-funs parsed. - define_funs: HashMap, + define_funs: BTreeMap, /// Maps **original** clause indexes to their optional name. old_names: ClsHMap, @@ -134,7 +152,7 @@ impl Instance { imp_clauses: ClsSet::new(), is_finalized: false, split: None, - define_funs: HashMap::new(), + define_funs: BTreeMap::new(), old_names: ClsHMap::with_capacity(clause_capa), print_success: false, unsat_cores: false, diff --git a/src/instance/pre_instance.rs b/src/instance/pre_instance.rs index 96e1f052..17a0d435 100644 --- a/src/instance/pre_instance.rs +++ b/src/instance/pre_instance.rs @@ -30,10 +30,12 @@ macro_rules! check_sat { /// Wraps an instance for preprocessing. /// /// Provides high-level functions to manipulate predicates and clauses. The reason for this wrapper -/// is that an [`Instance`](../common/struct.Instance.html) should almost never be mutated, except -/// during creation and preprocessing. +/// is that an [`Instance`] should almost never be mutated, except during creation and +/// preprocessing. /// /// This is why the instance type barely exposes any way to mutate its values. +/// +/// [`Instance`]: ../common/struct.Instance.html (Instance struct) pub struct PreInstance<'a> { /// The instance wrapped. instance: &'a mut Instance, diff --git a/src/learning/ice/data.rs b/src/learning/ice/data.rs index 82195631..182d6f48 100644 --- a/src/learning/ice/data.rs +++ b/src/learning/ice/data.rs @@ -229,10 +229,12 @@ impl CData { } } - /// Modified entropy, uses [`EntropyBuilder`](struct.EntropyBuilder.html). + /// Modified entropy, uses [`EntropyBuilder`]. /// /// Only takes into account unclassified data when `conf.ice.simple_gain` /// is false. + /// + /// [`EntropyBuilder`]: struct.EntropyBuilder.html (EntropyBuilder struct) pub fn entropy(&self, pred: PrdIdx, data: &Data) -> Res { let mut proba = EntropyBuilder::new(); proba.set_pos_count(self.pos.len()); diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index e933d1cf..33b6e518 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -59,7 +59,6 @@ // use hashconsing::* ; use common::*; -use instance::Clause; /// Extracts qualifier-related information from a predicate application. fn qual_info_of( diff --git a/src/learning/ice/synth/mod.rs b/src/learning/ice/synth/mod.rs index 3b970531..43f081a1 100644 --- a/src/learning/ice/synth/mod.rs +++ b/src/learning/ice/synth/mod.rs @@ -23,9 +23,9 @@ pub type TermVals = TermMap; /// of a different theory to this one. /// /// A synthesizer generates more and more complex candidate qualifiers with -/// each call to [`increment`][increment]. +/// each call to [`increment`]. /// -/// [increment]: #tymethod.increment (increment method) +/// [`increment`]: #tymethod.increment (increment method) pub trait TheoSynth { /// Type of values supported by this synthesizer. fn typ(&self) -> &Typ; @@ -36,16 +36,17 @@ pub trait TheoSynth { fn restart(&mut self); /// Increments the synthesizer. fn increment(&mut self); + /// Synthesizes qualifiers. fn synth(&mut self, F, &VarVals, &mut TermVals, &Profiler) -> Res where F: FnMut(Term) -> Res; - /// Generates some [`TermVal`][term val]s for some other type. + + /// Generates some [`TermVals`] for some other type. /// /// Adds them to the input term to value map. /// - /// [term val]: struct.TermVal.html - /// (TermVal struct) + /// [`TermVals`]: type.TermVals.html (TermVals type) fn project(&self, &VarVals, &Typ, &mut TermVals) -> Res<()>; } diff --git a/src/preproc/fun_preds.rs b/src/preproc/fun_preds.rs index d8208b76..dceb93e1 100644 --- a/src/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -3,7 +3,9 @@ //! This preprocessor attempts to reconstruct function definitions from Horn clauses. Functions are //! reconstructed as multiple branches of an if-then-else. //! -//! The core of how this works is the [`map_invert`](fn.map_invert.html) function. +//! The core of how this works is the [`map_invert`] function. +//! +//! [`map_invert`]: (fn.map_invert.html) (map_invert function) use common::*; use fun::RFun; @@ -1051,11 +1053,13 @@ impl RedStrat for FunPreds { /// Builds a cube and a substitution corresponding to inverting some arguments. /// -/// It is essentially a repeated application of [`map_invert`](fn.map_invert.html) to all +/// It is essentially a repeated application of [`map_invert`] to all /// arguments. It ends when /// /// - all of the arguments are inversed, or /// - a subset of the arguments cannot be inversed at all (the result will be `None` in this case). +/// +/// [`map_invert`]: fn.map_invert.html (map_invert function) pub fn args_invert(args: &VarTerms, args_len: usize) -> Res)>> { let (mut cube, mut subst) = (TermSet::new(), VarHMap::new()); diff --git a/src/preproc/mod.rs b/src/preproc/mod.rs index 270a4552..a8676ddc 100644 --- a/src/preproc/mod.rs +++ b/src/preproc/mod.rs @@ -1,12 +1,16 @@ //! Reduction strategies. //! -//! All strategies are `struct`s that implement the [`RedStrat`](trait.RedStrat.html) trait. The -//! [`Reductor`](struct.Reductor.html) then combines them in a cohesive preprocessing run in its -//! [`run` function](struct.Reductor.html#method.run). +//! All strategies are `struct`s that implement the [`RedStrat`] trait. The [`Reductor`] then +//! combines them in a cohesive preprocessing run in its [`run`] function. //! -//! During preprocessing, the [`Instance`](../common/struct.Instance.html) is wrapped into a -//! [`PreInstance`](struct.PreInstance.html) which provides high-level functions to act on the -//! predicates and the clauses of the instance. +//! During preprocessing, the [`Instance`] is wrapped into a [`PreInstance`] which provides +//! high-level functions to act on the predicates and the clauses of the instance. +//! +//! [`RedStrat`]: trait.RedStrat.html (RedStrat trait) +//! [`Reductor`]: struct.Reductor.html (Reductor struct) +//! [`run`]: struct.Reductor.html#method.run (Reductor's run function) +//! [`Instance`]: ../common/struct.Instance.html (Instance struct) +//! [`PreInstance`]: struct.PreInstance.html (PreInstance struct) use common::*; use instance::*; diff --git a/src/term/bindings.rs b/src/term/bindings.rs index 1d29f904..9b4edab3 100644 --- a/src/term/bindings.rs +++ b/src/term/bindings.rs @@ -1,7 +1,6 @@ //! Let bindings module. use common::*; -use instance::Clause; /// Stores let bindings. /// diff --git a/src/term/factory.rs b/src/term/factory.rs index 7b60b507..20808f3a 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -271,42 +271,38 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { /// Creates an operator application. /// /// Assumes the application is well-typed, modulo int to real casting. -/// -/// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn app(op: Op, mut args: Vec) -> Term { let typ = expect!( - op.type_check(& mut args) => |e| - let res: Res<()> = Err( - "Fatal internal type checking error, \ - please notify the developer(s)".into() - ) ; - match e { - term::TypError::Typ { - expected, obtained, index - } => res.chain_err( - || format!( - "expected an expression of sort {}, found {} ({})", - expected.map(|t| format!("{}", t)).unwrap_or_else(|| "?".into()), - args[index], obtained - ) - ).chain_err( - || "in this operator application" - ).chain_err( - || { - use std::io::Write ; - let buff = & mut Vec::new() ; - write!(buff, "({}", op).unwrap() ; - for arg in args { - write!(buff, " {}[{}]", arg, arg.typ()).unwrap() - } - write!(buff, ")").unwrap() ; - String::from_utf8_lossy(buff).into_owned() - } - ), - term::TypError::Msg(blah) => res.chain_err(|| blah) - }.unwrap_err() - ); + op.type_check(& mut args) => |e| + let res: Res<()> = Err( + "Fatal internal type checking error, \ + please notify the developer(s)".into() + ) ; + match e { + term::TypError::Typ { expected, obtained, index } => res.chain_err( + || format!( + "expected an expression of sort {}, found {} ({})", + expected.map(|t| format!("{}", t)).unwrap_or_else(|| "?".into()), + args[index], obtained + ) + ).chain_err( + || "in this operator application" + ).chain_err( + || { + use std::io::Write ; + let buff = & mut Vec::new() ; + write!(buff, "({}", op).unwrap() ; + for arg in args { + write!(buff, " {}[{}]", arg, arg.typ()).unwrap() + } + write!(buff, ")").unwrap() ; + String::from_utf8_lossy(buff).into_owned() + } + ), + term::TypError::Msg(blah) => res.chain_err(|| blah) + }.unwrap_err() + ); normalize(op, args, typ.clone()) } @@ -353,8 +349,6 @@ pub fn dtyp_tst(name: String, term: Term) -> Term { /// /// Error if the application is ill-typed (int will be cast to real /// automatically). -/// -/// Runs [`normalize`](fn.normalize.html) and returns its result. #[inline] pub fn try_app(op: Op, mut args: Vec) -> Result { let typ = op.type_check(&mut args)?; diff --git a/src/term/mod.rs b/src/term/mod.rs index 583cfd12..339c62e8 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -626,10 +626,6 @@ impl RTerm { /// - `Some(Equal)` if `lhs` and `rhs` are equivalent. /// /// So *greater* really means *more generic*. - /// - /// See [the module's function][atom implies] for more details and examples. - /// - /// [atom implies]: fn.atom_implies.html (atom_implies module-level function) pub fn conj_simpl(&self, other: &Self) -> simplify::SimplRes { simplify::conj_simpl(&self, &other) } @@ -664,24 +660,24 @@ impl RTerm { /// println!("false") ; /// assert!( ! term.is_true() ) ; /// let term = term::eq( - /// term::int(7), term::int_var(1) + /// term::int(7), term::int_var(1) /// ) ; /// println!("7 = v_1") ; /// assert!( ! term.is_true() ) ; /// let term = term::eq( - /// term::int(9), term::int(9) + /// term::int(9), term::int(9) /// ) ; /// println!("9 = 9") ; /// assert!( term.is_true() ) ; /// let term = term::eq( - /// term::int(1), term::int(9) + /// term::int(1), term::int(9) /// ) ; /// println!("1 = 9") ; /// assert!( ! term.is_true() ) ; /// let term = term::le( - /// term::app( - /// Op::Add, vec![ term::int(3), term::int(4) ] - /// ), term::int(9) + /// term::app( + /// Op::Add, vec![ term::int(3), term::int(4) ] + /// ), term::int(9) /// ) ; /// println!("3 + 4 = 9") ; /// assert!( term.is_true() ) ; @@ -708,24 +704,24 @@ impl RTerm { /// println!("false") ; /// assert!( term.is_false() ) ; /// let term = term::eq( - /// term::int(7), term::int_var(1) + /// term::int(7), term::int_var(1) /// ) ; /// println!("7 = v_1") ; /// assert!( ! term.is_false() ) ; /// let term = term::eq( - /// term::int(9), term::int(9) + /// term::int(9), term::int(9) /// ) ; /// println!("9 = 9") ; /// assert!( ! term.is_false() ) ; /// let term = term::eq( - /// term::int(1), term::int(9) + /// term::int(1), term::int(9) /// ) ; /// println!("1 = 9") ; /// assert!( term.is_false() ) ; /// let term = term::le( - /// term::int(9), term::app( - /// Op::Add, vec![ term::int(3), term::int(4) ] - /// ) + /// term::int(9), term::app( + /// Op::Add, vec![ term::int(3), term::int(4) ] + /// ) /// ) ; /// println!("9 <= 3 + 4") ; /// assert!( term.is_false() ) ; @@ -1001,10 +997,6 @@ impl RTerm { } /// Term evaluation. - /// - /// # TODO - /// - /// - remove recursive call for constant arrays pub fn eval(&self, model: &E) -> Res { eval::eval(&factory::term(self.clone()), model) } @@ -1553,22 +1545,22 @@ impl RTerm { /// let term = term::u_minus( term::int_var(0) ) ; /// println!("{}", term) ; /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( (0.into(), term::u_minus( term::int_var(1) ) ) ) + /// term.invert( term::int_var(1) ), + /// Some( (0.into(), term::u_minus( term::int_var(1) ) ) ) /// } /// let term = term::sub( vec![ term::int_var(0), term::int(7) ] ) ; /// println!("{}", term) ; /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( (0.into(), term::add( vec![ term::int_var(1), term::int(7) ] ) ) ) + /// term.invert( term::int_var(1) ), + /// Some( (0.into(), term::add( vec![ term::int_var(1), term::int(7) ] ) ) ) /// } /// let term = term::add( vec![ term::int(7), term::int_var(0) ] ) ; /// println!("{}", term) ; /// assert_eq!{ - /// term.invert( term::int_var(1) ), - /// Some( - /// (0.into(), term::sub( vec![ term::int_var(1), term::int(7) ] ) ) - /// ) + /// term.invert( term::int_var(1) ), + /// Some( + /// (0.into(), term::sub( vec![ term::int_var(1), term::int(7) ] ) ) + /// ) /// } /// ``` pub fn invert(&self, term: Term) -> Option<(VarIdx, Term)> { From 8c4837f3323fd4f42eac97a489cfb9666f86cc59 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 21 Sep 2018 16:10:20 +0900 Subject: [PATCH 65/94] more tests, better term simplification, minor tweaking --- Cargo.lock | 16 +- src/common/config.rs | 2 +- src/dtyp/mod.rs | 56 +- src/fun/mod.rs | 62 +- src/info.rs | 6 +- src/instance/clause.rs | 53 +- src/instance/mod.rs | 41 +- src/learning/ice/quals.rs | 1 - src/learning/ice/synth/adt.rs | 7 +- src/parse/mod.rs | 26 +- src/preproc/fun_preds.rs | 30 +- src/preproc/mod.rs | 6 +- src/preproc/strict_neg_clauses.rs | 6 +- src/teacher/cex_bias.rs | 2 +- src/term/factory.rs | 42 +- src/term/mod.rs | 1695 +++++++++++++++++------------ src/term/simplify.rs | 251 +++-- src/term/typ.rs | 4 +- 18 files changed, 1380 insertions(+), 926 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3ffe76b..f17f0de1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,7 +158,7 @@ dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -167,7 +167,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -175,7 +175,7 @@ name = "num-complex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -183,7 +183,7 @@ name = "num-integer" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -192,7 +192,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -202,12 +202,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -334,7 +334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" -"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" diff --git a/src/common/config.rs b/src/common/config.rs index 14ed8553..2123ccc5 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -785,7 +785,7 @@ impl IceConf { (between 0 and 100)", ).validator(int_validator) .value_name("int") - .default_value("5") + .default_value("7") .takes_value(true) .number_of_values(1) .hidden(true) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index aab79d11..b02a7ec6 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -1,4 +1,9 @@ //! Datatypes. +//! +//! In test mode, the `List` datatype is automatically added, as well as a few functions (see the +//! [`fun`] module). This is so that dtyp-related doc/lib tests have something to work with. +//! +//! [`fun`]: ../fun/index.html (fun module) use common::*; @@ -305,27 +310,36 @@ pub type CArgs = Vec<(String, PartialTyp)>; /// Type of the datatype factory. type Factory = RwLock>; lazy_static! { - /// Datatype factory. - static ref factory: Factory = RwLock::new( - BTreeMap::new() - ) ; - - /// Set of reserved datatypes. - static ref reserved_dtyps: BTreeSet<& 'static str> = { - let mut set = BTreeSet::new() ; - set.insert("List") ; - set - } ; - - /// Map from constructors to datatypes. - static ref constructor_map: Factory = RwLock::new( - BTreeMap::new() - ) ; - - /// Set of selectors. - static ref selector_set: RwLock> = RwLock::new( - BTreeSet::new() - ) ; + /// Datatype factory. + static ref factory: Factory = RwLock::new( + BTreeMap::new() + ) ; + + /// Set of reserved datatypes. + static ref reserved_dtyps: BTreeSet<& 'static str> = { + let mut set = BTreeSet::new() ; + set.insert("List") ; + set + } ; + + /// Map from constructors to datatypes. + static ref constructor_map: Factory = RwLock::new( + BTreeMap::new() + ) ; + + /// Set of selectors. + static ref selector_set: RwLock> = RwLock::new( + BTreeSet::new() + ) ; +} + +/// Creates the list datatype. +/// +/// Only available in test mode. +#[test] +pub fn create_list_dtyp() { + let _ = mk(RDTyp::list()); + () } /// True if there is at least one datatype declared. diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 5f8b0cc2..7c259672 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1,5 +1,8 @@ //! Hash consed functions. //! +//! In test mode, the `List` datatype is automatically added, as well as a few functions (see the +//! [`fun`] module). This is so that dtyp-related doc/lib tests have something to work with. +//! //! # TODO //! //! Move this in the instance to avoid the unsafe code to borrow definitions. @@ -22,17 +25,53 @@ pub type Fun = Arc; /// [`get_as_ref`]: fn.get_as_ref.html (get_as_ref function) type Factory = RwLock<(BTreeMap, usize)>; lazy_static! { - /// Function factory. - static ref factory: Factory = RwLock::new( - ( BTreeMap::new(), 0 ) - ) ; + /// Function factory. + static ref factory: Factory = RwLock::new( + ( BTreeMap::new(), 0 ) + ) ; + + /// Stores function declarations before obtaining the actual definition. + static ref fun_decs: RwLock< BTreeMap > = RwLock::new( + BTreeMap::new() + ) ; } -lazy_static! { - /// Stores function declarations before obtaining the actual definition. - static ref fun_decs: RwLock< BTreeMap > = RwLock::new( - BTreeMap::new() - ) ; +/// Test-related stuff for functions. +pub mod test { + + /// Name of the length function over `(List Int)` (test mode only). + pub fn length_fun_name() -> &'static str { + "length" + } + + /// Creates the list datatype and a length function over `(List Int)` this should only be used + /// in (doc) tests. + pub fn create_length_fun() { + use super::*; + let name = length_fun_name(); + if get(name).is_some() { + return (); + } + + ::parse::fun_dtyp(&format!( + "\ + (define-funs-rec + ( + ({name} ( (l (List Int)) ) Int) + ) + ( + (ite + (= l nil) + 0 + (+ 1 ({name} (tail l))) + ) + ) + ) + (exit) + ", + name = name + )) + } } /// Registers a function declaration. @@ -369,7 +408,6 @@ pub fn get(name: &str) -> Option { pub fn type_apply( name: String, var_info: &VarInfos, - out: &Typ, args: Vec, ) -> Result { if args.len() != var_info.len() { @@ -391,7 +429,7 @@ pub fn type_apply( } } - Ok(term::fun(out.clone(), name, args)) + Ok(term::fun(name, args)) } /// Creates a function application. @@ -407,7 +445,7 @@ pub fn apply(name: String, args: Vec) -> Result ))); }; - type_apply(name, &def.sig, &def.typ, args) + type_apply(name, &def.sig, args) } /// Real structure for functions. diff --git a/src/info.rs b/src/info.rs index 596dd253..43af443a 100644 --- a/src/info.rs +++ b/src/info.rs @@ -18,7 +18,11 @@ pub struct VarInfo { } impl VarInfo { /// Constructor. - pub fn new(name: String, typ: Typ, idx: VarIdx) -> Self { + pub fn new(name: S, typ: Typ, idx: VarIdx) -> Self + where + S: Into, + { + let name = name.into(); VarInfo { name, typ, diff --git a/src/instance/clause.rs b/src/instance/clause.rs index 322e448e..c30fe831 100644 --- a/src/instance/clause.rs +++ b/src/instance/clause.rs @@ -422,30 +422,33 @@ impl Clause { prune_things = false; mem! { check empty } - scoped! { - let mut terms = self.lhs_terms.iter() ; - while let Some(term) = terms.next() { - for t in terms.clone() { - match t.conj_simpl(term) { - // `t` is more generic, `term` is redundant, keep `t`. - Cmp(Equal) | Cmp(Greater) => { - mem!( rmv term.clone() ) ; - }, - // `term` is more generic, discard `t`. - Cmp(Less) => { - mem!( rmv t.clone() ) ; - }, - // No relation. - None => (), - // The conjunction of the two terms yields a new one. - Yields(nu_term) => { - mem!( rmv t.clone() ) ; - mem!( rmv term.clone() ) ; - mem!( add nu_term ) ; - }, - } + { + let mut terms = self.lhs_terms.iter(); + while let Some(term) = terms.next() { + for t in terms.clone() { + match t.conj_cmp(term) { + // No relation. + None => (), + + // `t` is stronger, `term` is redundant, keep `t`. + Cmp(Equal) | Cmp(Greater) => { + mem!( rmv term.clone() ); + } + + // `term` is stronger, discard `t`. + Cmp(Less) => { + mem!( rmv t.clone() ); + } + + // The conjunction of the two terms yields a new one. + Yields(nu_term) => { + mem!( rmv t.clone() ); + mem!( rmv term.clone() ); + mem!( add nu_term ); + } + } + } } - } } pruned = pruned || !mem!(rmv empty) || !mem!(add empty); @@ -643,7 +646,7 @@ impl Clause { while keep_checking { keep_checking = false; - set.retain(|t| match t.conj_simpl(&term) { + set.retain(|t| match t.conj_cmp(&term) { // `t` is more generic, `term` is redundant, keep `t`. Cmp(Equal) | Cmp(Greater) => { redundant = true; @@ -760,7 +763,7 @@ impl Clause { while let Some(term) = terms.next() { for t in terms.clone() { - if term.conj_simpl(t).is_some() { + if term.conj_cmp(t).is_some() { bail!( "redundant atoms in clause:\n {}\n {}\n{}", term, t, conf.bad(blah) diff --git a/src/instance/mod.rs b/src/instance/mod.rs index ae113292..0072b9a2 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -26,23 +26,42 @@ pub use self::pre_instance::PreInstance; /// # Finalization /// /// Instance finalization is crucial. A lot of instance functions do not make sense unless the -/// instance is finalized. This is currently done at the end of preprocessing. +/// instance is finalized. This is currently done at the end of preprocessing with the [`finalize`] +/// function. /// /// Functions that do not make sense before finalization include: /// -/// - [`pos_clauses`](struct.Instance.html#method.strict_neg_clauses) -/// - [`strict_neg_clauses`](struct.Instance.html#method.strict_neg_clauses) -/// - [`neg_clauses`](struct.Instance.html#method.neg_clauses) -/// - [`non_strict_neg_clauses`](struct.Instance.html#method.non_strict_neg_clauses) -/// - [`imp_clauses`](struct.Instance.html#method.imp_clauses) -/// - [`model_of`](struct.Instance.html#method.model_of) -/// - [`extend_model`](struct.Instance.html#method.extend_model) -/// - [`sorted_forced_terms`](struct.Instance.html#method.sorted_forced_terms) -/// - [`simplify_pred_defs`](struct.Instance.html#method.simplify_pred_defs) +/// - [`pos_clauses`] +/// - [`strict_neg_clauses`] +/// - [`neg_clauses`] +/// - [`non_strict_neg_clauses`] +/// - [`imp_clauses`] +/// - [`model_of`] +/// - [`extend_model`] +/// - [`sorted_forced_terms`] +/// - [`simplify_pred_defs`] /// /// [parsing]: ../parse/index.html (parse module) /// [preprocessing]: ../preproc/index.html (preproc module) -/// [finalize]: struct.Instance.html#method.finalize +/// [`finalize`]: struct.Instance.html#method.finalize +/// [`pos_clauses`]: struct.Instance.html#method.strict_neg_clauses +/// (Instance's pos_clauses function) +/// [`strict_neg_clauses`]: struct.Instance.html#method.strict_neg_clauses +/// (strict_neg_clauses function) +/// [`neg_clauses`]: struct.Instance.html#method.neg_clauses +/// (neg_clauses function) +/// [`non_strict_neg_clauses`]: struct.Instance.html#method.non_strict_neg_clauses +/// (non_strict_neg_clauses function) +/// [`imp_clauses`]: struct.Instance.html#method.imp_clauses +/// (imp_clauses function) +/// [`model_of`]: struct.Instance.html#method.model_of +/// (model_of function) +/// [`extend_model`]: struct.Instance.html#method.extend_model +/// (extend_model function) +/// [`sorted_forced_terms`]: struct.Instance.html#method.sorted_forced_terms +/// (sorted_forced_terms function) +/// [`simplify_pred_defs`]: struct.Instance.html#method.simplify_pred_defs +/// (simplify_pred_defs function) #[derive(Clone)] pub struct Instance { /// Predicates. diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index 33b6e518..f0cefd51 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -617,7 +617,6 @@ impl NuQuals { if fun.typ.is_bool() { quals.insert( term::fun( - fun.typ.clone(), fun.name.clone(), vec![term::var(var, typ.clone())], ), diff --git a/src/learning/ice/synth/adt.rs b/src/learning/ice/synth/adt.rs index 993311e0..769c2e3f 100644 --- a/src/learning/ice/synth/adt.rs +++ b/src/learning/ice/synth/adt.rs @@ -78,7 +78,7 @@ impl AdtSynth { .eval(&input) .chain_err(|| format!("while evaluating ({} {})", fun.name, val))?; - let term = term::fun(typ.clone(), fun.name.clone(), vec![var.clone()]); + let term = term::fun(fun.name.clone(), vec![var.clone()]); let prev = map.insert(term, val); debug_assert! { prev.is_none() } @@ -208,10 +208,7 @@ impl AdtSynth { .eval(input) .chain_err(|| format!("while evaluating ({} {})", fun.name, val))?; - extended.push(( - term::fun(self.typ.clone(), fun.name.clone(), vec![var.clone()]), - val, - )) + extended.push((term::fun(fun.name.clone(), vec![var.clone()]), val)) } extended.push((var, val.clone())); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 5e052940..1c23ce26 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -9,7 +9,7 @@ pub use self::ptterms::*; use consts::keywords; /// Result yielded by the parser. -#[derive(PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Parsed { /// Check-sat. CheckSat, @@ -28,6 +28,11 @@ pub enum Parsed { /// End of file. Eof, } +impl_fmt! { + Parsed(self, fmt) { + write!(fmt, "{:?}", self) + } +} lazy_static! { /// Set of legal special characters in identifiers. @@ -1143,7 +1148,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { })?; let idx = args.next_index(); - args.push(VarInfo::new(arg_name.into(), sort, idx)); + args.push(VarInfo::new(arg_name, sort, idx)); if arg_map.insert(arg_name, idx).is_some() { bail!(self.error( @@ -1530,7 +1535,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { format!("found two quantifier variables named `{}`", conf.bad(ident)) )) } - var_map.push(VarInfo::new(ident.into(), sort, idx)) + var_map.push(VarInfo::new(ident, sort, idx)) } self.tag(")")?; var_map.shrink_to_fit(); @@ -2070,10 +2075,10 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) -> Res<(Term, Pos)> { use errors::TypError; - let res = if let Some((var_infos, typ)) = self.functions.get(&name as &str) { + let res = if let Some((var_infos, _)) = self.functions.get(&name as &str) { // Function application for one of the functions we are currently parsing // the definition of? (i.e. the function is not registered yet) - fun::type_apply(name, var_infos, typ, args) + fun::type_apply(name, var_infos, args) } else { // Function should already exist. fun::apply(name, args) @@ -3437,3 +3442,14 @@ pub fn instance(s: &str) -> Instance { instance } + +/// Parses some functions/datatypes. +pub fn fun_dtyp(s: &str) { + let mut dummy = Instance::new(); + let mut cxt = ParserCxt::new(); + + print_err!( + cxt.parser(s, 0, &Profiler::new()).parse(&mut dummy), + "while parsing function / datatypes" + ); +} diff --git a/src/preproc/fun_preds.rs b/src/preproc/fun_preds.rs index dceb93e1..8ac7da9a 100644 --- a/src/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -289,7 +289,7 @@ impl<'a> BranchBuilder<'a> { } let nu_args = ::std::mem::replace(&mut nu_args, Vec::with_capacity(self.args_len)); - let fun_app = term::fun(self.fun_typ.clone(), self.fun_name.into(), nu_args); + let fun_app = term::fun(self.fun_name, nu_args); let fun_app_var = register_call!(fun_app); let fun_app = term::var(fun_app_var, self.fun_typ.clone()); @@ -685,7 +685,6 @@ impl FunDef { let mut invs = Vec::with_capacity(self.invars.len()); if !self.invars.is_empty() { let subst: VarMap<_> = vec![term::fun( - self.typ, self.name.clone(), self.sig .into_iter() @@ -775,10 +774,10 @@ impl FunDef { /// let mut fun_preds = FunPreds::new(& instance); /// let mut instance = PreInstance::new(& mut instance).unwrap(); /// let info = fun_preds.apply(& mut instance).unwrap(); -/// debug_assert_eq! { info.preds, 2 } // 2 because `unused` is simplified by propagation +/// assert_eq! { info.preds, 2 } // 2 because `unused` is simplified by propagation /// /// let pred: PrdIdx = 0.into(); -/// debug_assert_eq! { "len_fun_preds_example", & instance[pred].name } +/// assert_eq! { "len_fun_preds_example", & instance[pred].name } /// /// let funs = instance[pred].funs(); /// assert_eq!( "len_fun_preds_example_hoice_reserved_fun", &funs[0].name); @@ -806,7 +805,6 @@ impl FunPreds { let mut to_rm = vec![]; log!(@2 "working on {}", conf.emph(& instance[pred].name)); - println!("working on {}", conf.emph(&instance[pred].name)); debug_assert! { to_rm.is_empty() } @@ -864,13 +862,11 @@ impl FunPreds { to_rm.push(clause); log! { @3 | "working on clause #{}", clause } - println! { "working on clause #{}", clause } fun_def = if let Some(new) = fun_def.register_clause(&instance[clause])? { new } else { log!{ @3 | "clause registration failed" } - println!{ "clause registration failed" } abort!() }; } @@ -888,7 +884,7 @@ impl FunPreds { for (var, typ) in instance[pred].sig.index_iter().take(args_len) { args.push(term::var(var, typ.clone())) } - let fun_app = term::fun(fun.typ.clone(), fun.name.clone(), args); + let fun_app = term::fun(fun.name.clone(), args); let def = if let Some(last) = last.as_ref() { term::eq(term::var(*last, fun.typ.clone()), fun_app) @@ -1284,10 +1280,8 @@ pub fn map_invert( } } - RTerm::Fun { - typ, name, args, .. - } => { - let okay = fun_invert(term, typ, name, args, &mut nu_cube, subst, &nu_subst); + RTerm::Fun { name, args, .. } => { + let okay = fun_invert(term, name, args, &mut nu_cube, subst, &nu_subst); if !okay { return Ok(false); } @@ -1318,7 +1312,7 @@ fn dtyp_new_invert<'a>( let selectors = typ.selectors_of(name)?; debug_assert_eq! { args.len(), selectors.len() } - cube.push(term::dtyp_tst(name.into(), term.clone())); + cube.push(term::dtyp_tst(name, term.clone())); for (arg, (slc, _)) in args.iter().zip(selectors.iter()) { stack.push(( @@ -1479,10 +1473,7 @@ fn dtyp_slc_invert<'a>( nu_subst: &VarHMap, ) -> bool { if let Some((inner, _)) = inner.subst_total(&(subst, nu_subst)) { - cube.push(term::eq( - term, - term::dtyp_slc(typ.clone(), name.into(), inner), - )); + cube.push(term::eq(term, term::dtyp_slc(typ.clone(), name, inner))); true } else { false @@ -1499,7 +1490,7 @@ fn dtyp_tst_invert<'a>( nu_subst: &VarHMap, ) -> bool { if let Some((inner, _)) = inner.subst_total(&(subst, nu_subst)) { - cube.push(term::eq(term, term::dtyp_tst(name.into(), inner))); + cube.push(term::eq(term, term::dtyp_tst(name, inner))); true } else { false @@ -1509,7 +1500,6 @@ fn dtyp_tst_invert<'a>( /// Inverts a function application. fn fun_invert<'a>( term: Term, - typ: &Typ, name: &str, args: &'a [Term], cube: &mut Vec, @@ -1525,7 +1515,7 @@ fn fun_invert<'a>( } } - cube.push(term::eq(term, term::fun(typ.clone(), name.into(), nu_args))); + cube.push(term::eq(term, term::fun(name, nu_args))); true } diff --git a/src/preproc/mod.rs b/src/preproc/mod.rs index a8676ddc..8b1c7483 100644 --- a/src/preproc/mod.rs +++ b/src/preproc/mod.rs @@ -86,15 +86,15 @@ fn finalize(res: Res<()>, instance: &mut Instance, _profiler: &Profiler) -> Res< profile! { |_profiler| - "positive clauses" => add instance.pos_clauses().len() + "clauses | positive" => add instance.pos_clauses().len() } profile! { |_profiler| - "negative clauses" => add instance.neg_clauses().len() + "clauses | negative" => add instance.neg_clauses().len() } profile! { |_profiler| - "negative (strict) clauses" => add instance.strict_neg_clauses().len() + "clauses | strict-negative" => add instance.strict_neg_clauses().len() } match res { diff --git a/src/preproc/strict_neg_clauses.rs b/src/preproc/strict_neg_clauses.rs index 73d56401..026d844e 100644 --- a/src/preproc/strict_neg_clauses.rs +++ b/src/preproc/strict_neg_clauses.rs @@ -29,13 +29,13 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// let mut strict_neg = StrictNeg::new(& instance); /// let mut instance = PreInstance::new(& mut instance).unwrap(); /// let info = strict_neg.apply(& mut instance).unwrap(); -/// debug_assert! { ! info.non_zero() } +/// assert! { ! info.non_zero() } /// /// let pred: PrdIdx = 0.into(); -/// debug_assert_eq! { "pred", & instance[pred].name } +/// assert_eq! { "pred", & instance[pred].name } /// /// let strengthening = instance[pred].strength().unwrap(); -/// debug_assert_eq! { +/// assert_eq! { /// "(or \ /// (>= (* (- 1) v_0) 0) \ /// (>= (+ v_3 (* (- 1) v_0)) 0) \ diff --git a/src/teacher/cex_bias.rs b/src/teacher/cex_bias.rs index 51785bce..eecef661 100644 --- a/src/teacher/cex_bias.rs +++ b/src/teacher/cex_bias.rs @@ -289,7 +289,7 @@ impl CexBias { // Otherwise we can just generate a negative constraint that's more // constrained. log! { @4 "partial bias right" } - profile! { |_profiler| "bias: partial right " => add 1 } + profile! { |_profiler| "bias: partial right" => add 1 } Bias::Non }; diff --git a/src/term/factory.rs b/src/term/factory.rs index 20808f3a..341d30f3 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -245,12 +245,14 @@ pub fn select(array: Term, idx: Term) -> Term { /// # Panics /// /// - if the function does not exist -/// - if the type does not make sense /// - if the arguments are illegal #[inline] -pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { - if let Err(e) = fun::dec_do(&name, |fun| { - debug_assert_eq! { typ, fun.typ } +pub fn fun(name: S, mut args: Vec) -> Term +where + S: Into, +{ + let name = name.into(); + match fun::dec_do(&name, |fun| { if args.len() != fun.sig.len() { panic!("illegal application of function {}", conf.bad(&name)) } @@ -259,13 +261,14 @@ pub fn fun(typ: Typ, name: String, mut args: Vec) -> Term { *arg = nu_arg } } - Ok(()) + Ok(fun.typ.clone()) }) { - print_err(&e); - panic!("illegal function application") + Ok(typ) => factory.mk(RTerm::new_fun(typ, name, args)), + Err(e) => { + print_err(&e); + panic!("illegal function application") + } } - - factory.mk(RTerm::new_fun(typ, name, args)) } /// Creates an operator application. @@ -313,8 +316,11 @@ pub fn val(val: Val) -> Term { } /// Creates a datatype constructor. -pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { - let rterm = term::simplify::dtyp_new(typ, name, args); +pub fn dtyp_new(typ: Typ, name: S, args: Vec) -> Term +where + S: Into, +{ + let rterm = term::simplify::dtyp_new(typ, name.into(), args); factory.mk(rterm) } @@ -323,8 +329,11 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> Term { /// # TODO /// /// - treat constants better -pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { - match term::simplify::dtyp_slc(typ, name, term) { +pub fn dtyp_slc(typ: Typ, name: S, term: Term) -> Term +where + S: Into, +{ + match term::simplify::dtyp_slc(typ, name.into(), term) { Either::Left(rterm) => factory.mk(rterm), Either::Right(term) => term, } @@ -335,8 +344,11 @@ pub fn dtyp_slc(typ: Typ, name: String, term: Term) -> Term { /// # TODO /// /// - treat constants better -pub fn dtyp_tst(name: String, term: Term) -> Term { - let (rterm, positive) = term::simplify::dtyp_tst(name, term); +pub fn dtyp_tst(name: S, term: Term) -> Term +where + S: Into, +{ + let (rterm, positive) = term::simplify::dtyp_tst(name.into(), term); let res = factory.mk(rterm); if !positive { not(res) diff --git a/src/term/mod.rs b/src/term/mod.rs index 339c62e8..6bba6bec 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -18,16 +18,13 @@ //! # Variables //! //! A variable is a `usize` wrapped in a zero-cost [`VarIdx`] for safety. It has no semantics at -//! all by itself. Variables are given meaning by -//! -//! - the `sig` field of a [`Pred`], which gives them types; -//! - the [`VarInfo`]s stored in a [`Clause`], which give them a name and a type. +//! all by itself. Variables are given meaning by the `sig` field of a [`Pred`] or the [`VarInfo`]s +//! stored in a [`Clause`]. //! //! # Examples //! //! ```rust -//! # use hoice::term ; -//! # use hoice::term::{ Op, RTerm, typ } ; +//! # use hoice::{ term, term::{ Op, RTerm, typ } } ; //! let some_term = term::eq( //! term::int(11), term::app( //! Op::Mul, vec![ term::int_var(5), term::int(2) ] @@ -86,17 +83,25 @@ mod test; /// Hash consed term. pub type Term = HConsed; -/// A real term. +/// A real term, before hashconsing. +/// +/// See the [module level documentation] for more details. +/// +/// [module level documentation]: ../term/index.html (term module) #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RTerm { /// A clause variable. Var(Typ, VarIdx), + /// A constant. Cst(Val), - /// A **constant** array. + /// A constant array. /// /// The type is the type of **the indices** of the arrays. + /// + /// A constant array is not (necessarily) a (constant) value. It is an array which maps all + /// indices to the same term. CArray { /// Depth of this term. depth: usize, @@ -167,17 +172,51 @@ pub enum RTerm { }, } +/// Constructors. impl RTerm { + /// Constructs an operator application. + fn new_app(typ: Typ, op: Op, args: Vec) -> Self { + let depth = args + .iter() + .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); + RTerm::App { + depth, + typ, + op, + args, + } + } + + /// Constructs a function application. + fn new_fun(typ: Typ, name: S, args: Vec) -> Self + where + S: Into, + { + let depth = args + .iter() + .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); + let name = name.into(); + RTerm::Fun { + depth, + typ, + name, + args, + } + } + /// Constructs a constant array. - pub fn new_carray(typ: Typ, term: Term) -> Self { + /// + /// + fn new_carray(typ: Typ, term: Term) -> Self { RTerm::CArray { depth: term.depth() + 1, typ, term, } } + /// Constructs a datatype selector application. - pub fn new_dtyp_slc(typ: Typ, name: S, term: Term) -> Self + fn new_dtyp_slc(typ: Typ, name: S, term: Term) -> Self where S: Into, { @@ -189,8 +228,9 @@ impl RTerm { term, } } + /// Constructs a datatype tester application. - pub fn new_dtyp_tst(typ: Typ, name: S, term: Term) -> Self + fn new_dtyp_tst(typ: Typ, name: S, term: Term) -> Self where S: Into, { @@ -202,36 +242,9 @@ impl RTerm { term, } } - /// Constructs a function application. - pub fn new_fun(typ: Typ, name: S, args: Vec) -> Self - where - S: Into, - { - let depth = args - .iter() - .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); - let name = name.into(); - RTerm::Fun { - depth, - typ, - name, - args, - } - } - /// Constructs an operator application. - pub fn new_app(typ: Typ, op: Op, args: Vec) -> Self { - let depth = args - .iter() - .fold(1, |acc, term| ::std::cmp::max(acc, term.depth() + 1)); - RTerm::App { - depth, - typ, - op, - args, - } - } + /// Constructs a datatype constructor application. - pub fn new_dtyp_new(typ: Typ, name: S, args: Vec) -> Self + fn new_dtyp_new(typ: Typ, name: S, args: Vec) -> Self where S: Into, { @@ -246,8 +259,24 @@ impl RTerm { args, } } +} +/// Accessors and testers. +impl RTerm { /// Size of a term: number of subterms. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let term = term::eq( + /// term::add(vec![ + /// term::cmul( 2, term::int_var(0) ), + /// term::int_var(1) + /// ]), term::int(7) + /// ); + /// assert_eq! { term.depth(), 4 } + /// ``` pub fn depth(&self) -> usize { match self { RTerm::Var(_, _) | RTerm::Cst(_) => 1, @@ -260,539 +289,565 @@ impl RTerm { } } - /// The operator and the kids of a term. - pub fn app_inspect(&self) -> Option<(Op, &Vec)> { - if let RTerm::App { op, ref args, .. } = *self { - Some((op, args)) - } else { - None + /// Type of the term. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let sum = term::add(vec![ + /// term::cmul( 2, term::int_var(0) ), + /// term::int_var(1) + /// ]); + /// assert! { sum.typ().is_int() } + /// + /// let to_real = term::to_real(sum); + /// assert! { to_real.typ().is_real() } + /// + /// let term = term::eq( + /// to_real, term::real_of_float(7.0) + /// ); + /// assert! { term.typ().is_bool() } + /// ``` + pub fn typ(&self) -> Typ { + match self { + RTerm::CArray { typ, term, .. } => typ::array(typ.clone(), term.typ()), + + RTerm::Cst(val) => val.typ(), + + RTerm::Var(typ, _) + | RTerm::App { typ, .. } + | RTerm::Fun { typ, .. } + | RTerm::DTypSlc { typ, .. } + | RTerm::DTypTst { typ, .. } + | RTerm::DTypNew { typ, .. } => typ.clone(), } } - /// Returns the kids of an ite. - pub fn ite_inspect(&self) -> Option<(&Term, &Term, &Term)> { - if let RTerm::App { - op: Op::Ite, - ref args, - .. - } = *self - { - debug_assert_eq! { args.len(), 3 } - Some((&args[0], &args[1], &args[2])) - } else { - None + /// Returns the variable index if the term is a variable. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let v_1 = term::int_var(1); + /// assert_eq! { v_1.var_idx(), Some(1.into()) } + /// + /// let mul = term::cmul( 2, v_1 ); + /// assert_eq! { mul.var_idx(), None } + /// ``` + pub fn var_idx(&self) -> Option { + match *self { + RTerm::Var(_, i) => Some(i), + _ => None, } } - /// Inspects a function application. - pub fn fun_inspect(&self) -> Option<(&String, &Vec)> { - if let RTerm::Fun { - ref name, ref args, .. - } = *self - { - Some((name, args)) - } else { - None + /// True if the term is zero (integer or real). + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let one = term::int(1); + /// assert! { ! one.is_zero() } + /// + /// let sum = term::add( vec![one.clone(), term::u_minus(one)] ); + /// assert! { sum.is_zero() } + /// ``` + pub fn is_zero(&self) -> bool { + match (self.int(), self.real()) { + (Some(i), _) => i.is_zero(), + (_, Some(r)) => r.is_zero(), + _ => false, } } - /// Returns the kid of a negation. - pub fn neg_inspect(&self) -> Option<&Term> { - if let RTerm::App { - op: Op::Not, - ref args, - .. - } = *self - { - debug_assert_eq! { args.len(), 1 } - Some(&args[0]) - } else { - None + /// True if the term is one (integer or real). + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let one = term::int(1); + /// assert! { one.is_one() } + /// + /// let sum = term::add( vec![one.clone(), term::u_minus(one)] ); + /// assert! { ! sum.is_one() } + /// ``` + pub fn is_one(&self) -> bool { + use num::One; + match (self.int(), self.real()) { + (Some(i), _) => i == Int::one(), + (_, Some(r)) => r == Rat::one(), + _ => false, } } - /// Returns the kids of conjunctions. - pub fn conj_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::And, - ref args, - .. - } = *self - { - Some(args) - } else { - None - } + /// Compares two terms as a conjunction. + /// + /// Returns + /// + /// - `None` if no conclusion was reached, + /// - `Some(Greater)` if `lhs => rhs`, + /// - `Some(Less)` if `lhs <= rhs`, + /// - `Some(Equal)` if `lhs` and `rhs` are equivalent. + /// + /// So *greater* really means *stronger*. + /// + /// # Examples + /// + /// ```rust + /// use std::cmp::Ordering::*; + /// use hoice::{ term, term::simplify::SimplRes::* }; + /// let eq = &term::eq( term::int_var(0), term::int(7) ); + /// let ge = &term::ge( term::int_var(0), term::int(7) ); + /// let gt = &term::gt( term::int_var(0), term::int(7) ); + /// let ge_2 = &term::ge( term::int(7), term::int_var(0) ); + /// + /// # println!("{} cmp {}", eq, eq); + /// assert_eq! { eq.conj_cmp(eq), Cmp(Equal ) } + /// # println!("{} cmp {}", eq, ge); + /// assert_eq! { eq.conj_cmp(ge), Cmp(Greater) } + /// # println!("{} cmp {}", ge, eq); + /// assert_eq! { ge.conj_cmp(eq), Cmp(Less ) } + /// + /// # println!(); + /// # println!("{} cmp {}", eq, gt); + /// assert_eq! { eq.conj_cmp(gt), Yields(term::fls()) } + /// # println!("{} cmp {}", gt, eq); + /// assert_eq! { gt.conj_cmp(eq), Yields(term::fls()) } + /// + /// # println!(); + /// # println!("{} cmp {}", gt, ge); + /// assert_eq! { gt.conj_cmp(ge), Cmp(Greater) } + /// # println!("{} cmp {}", ge, gt); + /// assert_eq! { ge.conj_cmp(gt), Cmp(Less ) } + /// + /// # println!(); + /// # println!("{} cmp {}", ge, ge_2); + /// assert_eq! { ge.conj_cmp(ge_2), Yields(eq.clone()) } + /// # println!("{} cmp {}", ge_2, ge); + /// assert_eq! { ge_2.conj_cmp(ge), Yields(eq.clone()) } + /// ``` + pub fn conj_cmp(&self, other: &Self) -> simplify::SimplRes { + simplify::conj_simpl(&self, &other) } - /// Returns the kids of disjunctions. - pub fn disj_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::Or, - ref args, - .. - } = *self - { - Some(args) - } else { - None + + /// True if the term has no variables and evaluates to true. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::{ term, term::Op }; + /// let term = term::tru() ; + /// println!("true") ; + /// assert!( term.is_true() ) ; + /// let term = term::fls() ; + /// println!("false") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::eq( + /// term::int(7), term::int_var(1) + /// ) ; + /// println!("7 = v_1") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::eq( + /// term::int(9), term::int(9) + /// ) ; + /// println!("9 = 9") ; + /// assert!( term.is_true() ) ; + /// let term = term::eq( + /// term::int(1), term::int(9) + /// ) ; + /// println!("1 = 9") ; + /// assert!( ! term.is_true() ) ; + /// let term = term::le( + /// term::app( + /// Op::Add, vec![ term::int(3), term::int(4) ] + /// ), term::int(9) + /// ) ; + /// println!("3 + 4 = 9") ; + /// assert!( term.is_true() ) ; + /// ``` + pub fn is_true(&self) -> bool { + match self.bool_eval(&()) { + Ok(Some(b)) => b, + _ => false, } } - /// Returns the kids of equalities. - pub fn eq_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::Eql, - ref args, - .. - } = *self - { - Some(args) - } else { - None - } - } - - /// Returns the kids of additions. - pub fn add_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::Add, - ref args, - .. - } = *self - { - Some(args) - } else { - None - } - } - /// Returns the kids of subtractions. - pub fn sub_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::Sub, - ref args, - .. - } = *self - { - Some(args) - } else { - None - } - } - /// Returns the kids of multiplications. - pub fn mul_inspect(&self) -> Option<&Vec> { - if let RTerm::App { - op: Op::Mul, - ref args, - .. - } = *self - { - Some(args) - } else { - None - } - } - /// Returns the kids of a constant multiplication. - pub fn cmul_inspect(&self) -> Option<(Val, &Term)> { - if let RTerm::App { - op: Op::CMul, - ref args, - .. - } = *self - { - if args.len() == 2 { - if let Some(val) = args[0].val() { - return Some((val, &args[1])); - } - } - panic!("illegal c_mul application: {}", self) - } else { - None - } - } - - /// Returns the kids of a datatype tester. - pub fn dtyp_tst_inspect(&self) -> Option<(&str, &Term)> { - if let RTerm::DTypTst { name, term, .. } = self { - Some((name, term)) - } else { - None - } - } - - /// Returns the kids of a datatype constructor. - pub fn dtyp_new_inspect(&self) -> Option<(&Typ, &str, &[Term])> { - if let RTerm::DTypNew { - typ, name, args, .. - } = self - { - Some((typ, name, args)) - } else { - None - } - } - - /// Returns the kids of a datatype selector. - pub fn dtyp_slc_inspect(&self) -> Option<(&Typ, &str, &Term)> { - if let RTerm::DTypSlc { - typ, name, term, .. - } = self - { - Some((typ, name, term)) - } else { - None - } - } - - /// Iterator over over all the leafs of a term. - pub fn leaf_iter(&self) -> LeafIter { - LeafIter::of_rterm(self) - } - - /// Iterates over all the subterms of a term, including itself. - pub fn iter(&self, mut f: F) - where - F: FnMut(&RTerm), - { - use RTerm::*; - let mut stack = vec![self]; - - while let Some(term) = stack.pop() { - f(term); - match term { - App { args, .. } | DTypNew { args, .. } | Fun { args, .. } => { - stack.extend(args.iter().map(|term| term.get())) - } - - CArray { term, .. } | DTypSlc { term, .. } | DTypTst { term, .. } => { - stack.push(term.get()) - } - - Var(_, _) | Cst(_) => (), - } - } - } - - /// Type of the term. - pub fn typ(&self) -> Typ { - match self { - RTerm::CArray { typ, term, .. } => typ::array(typ.clone(), term.typ()), - - RTerm::Cst(val) => val.typ(), - - RTerm::Var(typ, _) - | RTerm::App { typ, .. } - | RTerm::Fun { typ, .. } - | RTerm::DTypSlc { typ, .. } - | RTerm::DTypTst { typ, .. } - | RTerm::DTypNew { typ, .. } => typ.clone(), - } - } - - /// True if the term is zero (integer or real). - pub fn is_zero(&self) -> bool { - match (self.int(), self.real()) { - (Some(i), _) => i.is_zero(), - (_, Some(r)) => r.is_zero(), - _ => false, - } - } - - /// True if the term is one (integer or real). - pub fn is_one(&self) -> bool { - use num::One; - match (self.int(), self.real()) { - (Some(i), _) => i == Int::one(), - (_, Some(r)) => r == Rat::one(), - _ => false, - } - } - - /// Write a real term using a special function to write variables. - pub fn write(&self, w: &mut W, write_var: WriteVar) -> IoRes<()> - where - W: Write, - WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, - { - self.write_with(w, write_var, None) - } - - /// Write a real term using a special function to write variables. - pub fn write_with( - &self, - w: &mut W, - write_var: WriteVar, - bindings: Option<&bindings::Bindings>, - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, - { - self.write_with_raw(w, write_var, bindings.map(|b| b.bindings())) - } - - /// Write a real term using a special function to write variables. - fn write_with_raw( - &self, - w: &mut W, - write_var: WriteVar, - bindings: Option<&[TermMap]>, - ) -> IoRes<()> - where - W: Write, - WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, - { - // Stores triplets of - // - the elements to write, - // - the prefix string, written before next element - // - the suffix, written once there's no more elements to write - let mut stack = vec![(vec![self], "", "")]; - - 'work: while let Some((mut to_do, sep, end)) = stack.pop() { - use self::RTerm::*; - - if let Some(this_term) = to_do.pop() { - stack.push((to_do, sep, end)); - write!(w, "{}", sep)?; - - // Is term in the bindings? - if let Some(bindings) = bindings { - for map in bindings { - if let Some(var) = map.get(&this_term.to_hcons()) { - Bindings::write_var(w, *var)?; - continue 'work; - } - } - } - - match this_term { - Var(_, v) => write_var(w, *v)?, - - Cst(val) => write!(w, "{}", val)?, - - CArray { term, .. } => { - write!( - w, - "(({} {} {})", - keywords::op::as_, - keywords::op::const_, - this_term.typ() - )?; - stack.push((vec![term], " ", ")")) - } - - App { op, args, .. } => { - write!(w, "({}", op)?; - stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) - } - - DTypSlc { name, term, .. } => { - write!(w, "({}", name)?; - stack.push((vec![term], " ", ")")) - } - - DTypTst { name, term, .. } => { - write!(w, "({}-{}", keywords::op::is_, name)?; - stack.push((vec![term], " ", ")")) - } - - DTypNew { name, args, .. } => if args.is_empty() { - write!(w, "{}", name)? - } else { - write!(w, "({}", name)?; - stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) - }, - - Fun { name, args, .. } => if args.is_empty() { - write!(w, "{}", name)? - } else { - write!(w, "({}", name)?; - stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) - }, - } - } else { - w.write_all(end.as_bytes())? - } - } - - Ok(()) - } - - /// True if atom `self` implies atom `other` syntactically. - /// - /// Returns - /// - /// - `None` if no conclusion was reached, - /// - `Some(Greater)` if `lhs => rhs`, - /// - `Some(Less)` if `lhs <= rhs`, - /// - `Some(Equal)` if `lhs` and `rhs` are equivalent. - /// - /// So *greater* really means *more generic*. - pub fn conj_simpl(&self, other: &Self) -> simplify::SimplRes { - simplify::conj_simpl(&self, &other) - } - - /// Term evaluation (int). - pub fn int_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_int() - } - - /// Term evaluation (real). - pub fn real_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_real() - } - - /// Term evaluation (bool). - pub fn bool_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_bool() - } /// True if the term has no variables and evaluates to true. /// /// # Examples /// /// ```rust - /// use hoice::term ; - /// use hoice::term::Op ; - /// + /// # use hoice::{term, term::Op}; /// let term = term::tru() ; /// println!("true") ; - /// assert!( term.is_true() ) ; + /// assert!( ! term.is_false() ) ; /// let term = term::fls() ; /// println!("false") ; - /// assert!( ! term.is_true() ) ; + /// assert!( term.is_false() ) ; /// let term = term::eq( /// term::int(7), term::int_var(1) /// ) ; /// println!("7 = v_1") ; - /// assert!( ! term.is_true() ) ; + /// assert!( ! term.is_false() ) ; /// let term = term::eq( /// term::int(9), term::int(9) /// ) ; /// println!("9 = 9") ; - /// assert!( term.is_true() ) ; + /// assert!( ! term.is_false() ) ; /// let term = term::eq( /// term::int(1), term::int(9) /// ) ; /// println!("1 = 9") ; - /// assert!( ! term.is_true() ) ; + /// assert!( term.is_false() ) ; /// let term = term::le( - /// term::app( + /// term::int(9), term::app( /// Op::Add, vec![ term::int(3), term::int(4) ] - /// ), term::int(9) + /// ) /// ) ; - /// println!("3 + 4 = 9") ; - /// assert!( term.is_true() ) ; + /// println!("9 <= 3 + 4") ; + /// assert!( term.is_false() ) ; /// ``` - pub fn is_true(&self) -> bool { + pub fn is_false(&self) -> bool { match self.bool_eval(&()) { - Ok(Some(b)) => b, + Ok(Some(b)) => !b, _ => false, } } - /// True if the term has no variables and evaluates to true. + /// Returns a constant arithmetic version of the term if any. /// /// # Examples /// /// ```rust - /// use hoice::term ; - /// use hoice::term::Op ; + /// # use hoice::term; + /// let sum = term::add(vec![ + /// term::cmul( 2, term::int(0) ), + /// term::int(1) + /// ]); + /// assert_eq! { sum.arith(), Some(term::int(1)) } + /// + /// let to_real = term::to_real(sum); + /// assert_eq! { to_real.arith(), Some(term::real_of_float(1.0)) } /// - /// let term = term::tru() ; - /// println!("true") ; - /// assert!( ! term.is_false() ) ; - /// let term = term::fls() ; - /// println!("false") ; - /// assert!( term.is_false() ) ; /// let term = term::eq( - /// term::int(7), term::int_var(1) - /// ) ; - /// println!("7 = v_1") ; - /// assert!( ! term.is_false() ) ; + /// to_real, term::real_of_float(7.0) + /// ); + /// assert_eq! { term.arith(), None } + /// ``` + pub fn arith(&self) -> Option { + if let Some(i) = self.int() { + Some(term::int(i)) + } else if let Some(r) = self.real() { + Some(term::real(r)) + } else { + None + } + } + + /// If the term's an integer constant, returns the value. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::term; + /// let one = term::int(1); + /// assert_eq! { one.int_val(), Some(& 1.into()) } + /// + /// let sum = term::add(vec![term::int_var(0), one]); + /// assert_eq! { sum.int_val(), None } + /// + /// let one = term::real_of_float(1.0); + /// assert_eq! { one.int_val(), None } + /// ``` + pub fn int_val(&self) -> Option<&Int> { + if let RTerm::Cst(val) = self { + if let val::RVal::I(i) = val.get() { + return Some(i); + } + } + None + } + + /// If the term's a rational constant, returns the value. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ term, common::Rat }; + /// let one = term::real_of_float(1.0); + /// assert_eq! { one.real_val(), Some(&Rat::new(1.into(),1.into())) } + /// + /// let sum = term::add(vec![term::real_var(0), one]); + /// assert_eq! { sum.real_val(), None } + /// + /// let one = term::int(1); + /// assert_eq! { one.real_val(), None } + /// ``` + pub fn real_val(&self) -> Option<&Rat> { + if let RTerm::Cst(val) = self { + if let val::RVal::R(r) = val.get() { + return Some(r); + } + } + None + } + + /// Return true if the term mentions at least one variable from `vars`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let mut vars = VarSet::new(); + /// vars.insert(7.into()); + /// vars.insert(5.into()); + /// vars.insert(3.into()); + /// /// let term = term::eq( - /// term::int(9), term::int(9) - /// ) ; - /// println!("9 = 9") ; - /// assert!( ! term.is_false() ) ; + /// term::add(vec![ + /// term::cmul( 2, term::int_var(0) ), + /// term::int_var(6), + /// ]), + /// term::int(0) + /// ); + /// assert! { !term.mentions_one_of(&vars) } + /// /// let term = term::eq( - /// term::int(1), term::int(9) - /// ) ; - /// println!("1 = 9") ; - /// assert!( term.is_false() ) ; - /// let term = term::le( - /// term::int(9), term::app( - /// Op::Add, vec![ term::int(3), term::int(4) ] + /// term::add(vec![ + /// term::cmul( 2, term::int_var(7) ), + /// term::int_var(6), + /// ]), + /// term::int(0) + /// ); + /// assert! { term.mentions_one_of(&vars) } + /// ``` + pub fn mentions_one_of(&self, vars: &VarSet) -> bool { + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + if vars.contains(&var_idx) { + return true; + } + } + } + false + } + + /// Returns true if the term mentions a function. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ term, term::typ, dtyp, fun }; + /// fun::test::create_length_fun(); + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// + /// let t_1 = term::ge( + /// term::fun( + /// fun::test::length_fun_name(), + /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// ), + /// term::int(0) + /// ); + /// assert! { t_1.has_fun_apps() } + /// + /// let t_2 = term::ge( + /// term::int_var(7), + /// term::int(0) + /// ); + /// assert! { !t_2.has_fun_apps() } + /// ``` + pub fn has_fun_apps(&self) -> bool { + use self::zip::*; + + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |term| { + if let Some((_, _)) = term.fun_inspect() { + Err(()) + } else { + Ok(None) + } + }, + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) => Err(()), + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(_), + .. + } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: \ + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); + + res.is_err() + } + + /// Returns true if the term mentions a recursive function. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ term, term::typ, dtyp, fun, parse }; + /// fun::test::create_length_fun(); + /// // Adding a non-recursive function. + /// parse::fun_dtyp("\ + /// (define-fun get_head ( (lst (List Int)) ) Int + /// (head lst) /// ) - /// ) ; - /// println!("9 <= 3 + 4") ; - /// assert!( term.is_false() ) ; + /// "); + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// + /// let t_1 = term::ge( + /// term::fun( + /// fun::test::length_fun_name(), + /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// ), + /// term::int(0) + /// ); + /// assert! { t_1.has_rec_fun_apps() } + /// + /// let t_2 = term::ge( + /// term::fun( + /// "get_head", + /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// ), + /// term::int(0) + /// ); + /// assert! { !t_2.has_rec_fun_apps() } + /// + /// let t_3 = term::ge( + /// term::int_var(7), + /// term::int(0) + /// ); + /// assert! { !t_3.has_rec_fun_apps() } /// ``` - pub fn is_false(&self) -> bool { - match self.bool_eval(&()) { - Ok(Some(b)) => !b, - _ => false, - } - } - /// Boolean a constant boolean term evaluates to. - pub fn bool(&self) -> Option { - match self.bool_eval(&()) { - Ok(Some(b)) => Some(b), - _ => None, - } - } + pub fn has_rec_fun_apps(&self) -> bool { + use self::zip::*; - /// Evaluates a term with an empty model. - pub fn as_val(&self) -> Val { - if let Ok(res) = self.eval(&()) { - res - } else { - val::none(self.typ().clone()) - } - } + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |term| { + if let Some((name, _)) = term.fun_inspect() { + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() + { + Err(()) + } else { + Ok(None) + } + } else { + Ok(None) + } + }, + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(name) + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() => + { + Err(()) + } + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(name), + .. + } + if fun::get(name) + .expect("inconsistent function application: unknown function") + .is_recursive() => + { + Err(()) + } + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: \ + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); - /// Integer a constant integer term evaluates to. - pub fn int(&self) -> Option { - if self.typ() != typ::int() { - return None; - } - match self.int_eval(&()) { - Ok(Some(i)) => Some(i), - _ => None, - } - } - /// Integer a constant integer term evaluates to. - pub fn real(&self) -> Option { - match self.real_eval(&()) { - Ok(Some(r)) => Some(r), - _ => None, - } + res.is_err() } - /// Turns a constant term in a `Val`. - pub fn val(&self) -> Option { - match *self { - RTerm::Cst(ref val) => Some(val.clone()), - _ => None, - } - } + /// Returns true if the term mentions a function or an ADT. + pub fn has_fun_app_or_adt(&self) -> bool { + use self::zip::*; - /// Returns a constant arithmetic version of the term if any. - pub fn arith(&self) -> Option { - if let Some(i) = self.int() { - Some(term::int(i)) - } else if let Some(r) = self.real() { - Some(term::real(r)) - } else { - None - } + // Will be `Ok(())` if there's no function application, and `Err(())` + // otherwise. + let res = zip( + &self.to_hcons(), + |term| { + if term.fun_inspect().is_some() + || term.dtyp_new_inspect().is_some() + || term.dtyp_slc_inspect().is_some() + { + Err(()) + } else { + Ok(None) + } + }, + |_| Ok(()), + |zip_op, _, _: ()| match zip_op { + ZipOp::Fun(_) | ZipOp::New(_) | ZipOp::Slc(_) => Err(()), + _ => Ok(ZipDoTotal::Upp { yielded: () }), + }, + |frame| match frame { + ZipFrame { + thing: ZipOp::Fun(_), + .. + } + | ZipFrame { + thing: ZipOp::New(_), + .. + } + | ZipFrame { + thing: ZipOp::Slc(_), + .. + } => Err(()), + mut frame => { + let nu_term = frame.rgt_args.next().expect( + "illegal call to `partial_op`: + empty `rgt_args` (has_fun_app_or_adt)", + ); + Ok(ZipDo::Trm { nu_term, frame }) + } + }, + ); + + res.is_err() } +} - /// The kids of this term, if any. - pub fn kids(&self) -> Option<&[Term]> { - if let RTerm::App { ref args, .. } = *self { - Some(args) - } else { - None - } +/// Term modifiers. +impl RTerm { + /// Turns a real term in a hashconsed one. + #[inline] + pub fn to_hcons(&self) -> Term { + term(self.clone()) } /// Forces the type of a datatype constructor. @@ -931,7 +986,7 @@ impl RTerm { typ, name, args, .. } => { debug_assert_eq! { typ, & nu_typ } - term::fun(typ.clone(), name.clone(), args.clone()) + term::fun(name.clone(), args.clone()) } }; @@ -972,250 +1027,335 @@ impl RTerm { } } - /// Checks whether the term is a relation. - pub fn is_relation(&self) -> bool { - match *self { - RTerm::App { op: Op::Eql, .. } - | RTerm::App { op: Op::Gt, .. } - | RTerm::App { op: Op::Ge, .. } - | RTerm::App { op: Op::Lt, .. } - | RTerm::App { op: Op::Le, .. } => true, - RTerm::App { - op: Op::Not, - ref args, - .. - } => args[0].is_relation(), - _ => false, + /// If the term is a negation, returns what's below the negation. + pub fn rm_neg(&self) -> Option { + match *self { + RTerm::App { + op: Op::Not, + ref args, + .. + } => { + debug_assert_eq!(args.len(), 1); + Some(args[0].clone()) + } + _ => None, + } + } +} + +/// Variant deconstructors. +impl RTerm { + /// The operator and the kids of a term. + pub fn app_inspect(&self) -> Option<(Op, &Vec)> { + if let RTerm::App { op, ref args, .. } = *self { + Some((op, args)) + } else { + None + } + } + + /// Returns the kids of an ite. + pub fn ite_inspect(&self) -> Option<(&Term, &Term, &Term)> { + if let RTerm::App { + op: Op::Ite, + ref args, + .. + } = *self + { + debug_assert_eq! { args.len(), 3 } + Some((&args[0], &args[1], &args[2])) + } else { + None + } + } + + /// Inspects a function application. + pub fn fun_inspect(&self) -> Option<(&String, &Vec)> { + if let RTerm::Fun { + ref name, ref args, .. + } = *self + { + Some((name, args)) + } else { + None + } + } + + /// Returns the kid of a negation. + pub fn neg_inspect(&self) -> Option<&Term> { + if let RTerm::App { + op: Op::Not, + ref args, + .. + } = *self + { + debug_assert_eq! { args.len(), 1 } + Some(&args[0]) + } else { + None + } + } + + /// Returns the kids of conjunctions. + pub fn conj_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::And, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + + /// Returns the kids of disjunctions. + pub fn disj_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Or, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } + } + + /// Returns the kids of equalities. + pub fn eq_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Eql, + ref args, + .. + } = *self + { + Some(args) + } else { + None } } - /// Checks whether a term is an equality. - pub fn is_eq(&self) -> bool { - match *self { - RTerm::App { op: Op::Eql, .. } => true, - _ => false, + + /// Returns the kids of additions. + pub fn add_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Add, + ref args, + .. + } = *self + { + Some(args) + } else { + None } } - /// Term evaluation. - pub fn eval(&self, model: &E) -> Res { - eval::eval(&factory::term(self.clone()), model) + /// Returns the kids of subtractions. + pub fn sub_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Sub, + ref args, + .. + } = *self + { + Some(args) + } else { + None + } } - /// If the term's an integer constant, returns the value. - pub fn int_val(&self) -> Option<&Int> { - if let RTerm::Cst(val) = self { - if let val::RVal::I(i) = val.get() { - return Some(i); - } + /// Returns the kids of multiplications. + pub fn mul_inspect(&self) -> Option<&Vec> { + if let RTerm::App { + op: Op::Mul, + ref args, + .. + } = *self + { + Some(args) + } else { + None } - None } - /// If the term's a rational constant, returns the value. - pub fn real_val(&self) -> Option<&Rat> { - if let RTerm::Cst(val) = self { - if let val::RVal::R(r) = val.get() { - return Some(r); + + /// Returns the kids of a constant multiplication. + pub fn cmul_inspect(&self) -> Option<(Val, &Term)> { + if let RTerm::App { + op: Op::CMul, + ref args, + .. + } = *self + { + if args.len() == 2 { + if let Some(val) = args[0].val() { + return Some((val, &args[1])); + } } + panic!("illegal c_mul application: {}", self) + } else { + None } - None } - /// The highest variable index appearing in the term. - pub fn highest_var(&self) -> Option { - let mut max = None; - - for var_or_cst in self.leaf_iter() { - if let Either::Left((_, var_idx)) = var_or_cst { - max = Some(::std::cmp::max(var_idx, max.unwrap_or_else(|| 0.into()))) - } + /// Returns the kids of a datatype tester. + pub fn dtyp_tst_inspect(&self) -> Option<(&str, &Term)> { + if let RTerm::DTypTst { name, term, .. } = self { + Some((name, term)) + } else { + None } + } - max + /// Returns the kids of a datatype constructor. + pub fn dtyp_new_inspect(&self) -> Option<(&Typ, &str, &[Term])> { + if let RTerm::DTypNew { + typ, name, args, .. + } = self + { + Some((typ, name, args)) + } else { + None + } } - /// Returns the variable index if the term is a variable. - pub fn var_idx(&self) -> Option { - match *self { - RTerm::Var(_, i) => Some(i), - _ => None, + /// Returns the kids of a datatype selector. + pub fn dtyp_slc_inspect(&self) -> Option<(&Typ, &str, &Term)> { + if let RTerm::DTypSlc { + typ, name, term, .. + } = self + { + Some((typ, name, term)) + } else { + None } } +} - /// Return true if the term mentions at least one variable from `vars`. - pub fn mentions_one_of(&self, vars: &VarSet) -> bool { - for var_or_cst in self.leaf_iter() { - if let Either::Left((_, var_idx)) = var_or_cst { - if vars.contains(&var_idx) { - return true; +/// Fold/map/zip functions. +impl RTerm { + /// Iterator over over all the leafs of a term. + pub fn leaf_iter(&self) -> LeafIter { + LeafIter::of_rterm(self) + } + + /// Iterates over all the subterms of a term, including itself. + pub fn iter(&self, mut f: F) + where + F: FnMut(&RTerm), + { + use RTerm::*; + let mut stack = vec![self]; + + while let Some(term) = stack.pop() { + f(term); + match term { + App { args, .. } | DTypNew { args, .. } | Fun { args, .. } => { + stack.extend(args.iter().map(|term| term.get())) } + + CArray { term, .. } | DTypSlc { term, .. } | DTypTst { term, .. } => { + stack.push(term.get()) + } + + Var(_, _) | Cst(_) => (), } } - false } - /// If the term is a negation, returns what's below the negation. - pub fn rm_neg(&self) -> Option { - match *self { - RTerm::App { - op: Op::Not, - ref args, - .. - } => { - debug_assert_eq!(args.len(), 1); - Some(args[0].clone()) - } + /// Term evaluation (int). + pub fn int_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_int() + } + + /// Term evaluation (real). + pub fn real_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_real() + } + + /// Term evaluation (bool). + pub fn bool_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_bool() + } + + /// Boolean a constant boolean term evaluates to. + pub fn bool(&self) -> Option { + match self.bool_eval(&()) { + Ok(Some(b)) => Some(b), _ => None, } } - /// Turns a real term in a hashconsed one. - #[inline] - pub fn to_hcons(&self) -> Term { - term(self.clone()) + /// Evaluates a term with an empty model. + pub fn as_val(&self) -> Val { + if let Ok(res) = self.eval(&()) { + res + } else { + val::none(self.typ().clone()) + } } - /// Returns true if the term mentions a function. - pub fn has_fun_apps(&self) -> bool { - use self::zip::*; - - // Will be `Ok(())` if there's no function application, and `Err(())` - // otherwise. - let res = zip( - &self.to_hcons(), - |term| { - if let Some((_, _)) = term.fun_inspect() { - Err(()) - } else { - Ok(None) - } - }, - |_| Ok(()), - |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(_) => Err(()), - _ => Ok(ZipDoTotal::Upp { yielded: () }), - }, - |frame| match frame { - ZipFrame { - thing: ZipOp::Fun(_), - .. - } => Err(()), - mut frame => { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: \ - empty `rgt_args` (has_fun_app_or_adt)", - ); - Ok(ZipDo::Trm { nu_term, frame }) - } - }, - ); + /// Integer value the term evaluates to. + pub fn int(&self) -> Option { + if self.typ() != typ::int() { + return None; + } + match self.int_eval(&()) { + Ok(Some(i)) => Some(i), + _ => None, + } + } - res.is_err() + /// Real value the term evaluates to. + pub fn real(&self) -> Option { + match self.real_eval(&()) { + Ok(Some(r)) => Some(r), + _ => None, + } } - /// Returns true if the term mentions a recursive function. - pub fn has_rec_fun_apps(&self) -> bool { - use self::zip::*; + /// Turns a constant term in a `Val`. + pub fn val(&self) -> Option { + match *self { + RTerm::Cst(ref val) => Some(val.clone()), + _ => None, + } + } - // Will be `Ok(())` if there's no function application, and `Err(())` - // otherwise. - let res = zip( - &self.to_hcons(), - |term| { - if let Some((name, _)) = term.fun_inspect() { - if fun::get(name) - .expect("inconsistent function application: unknown function") - .is_recursive() - { - Err(()) - } else { - Ok(None) - } - } else { - Ok(None) - } - }, - |_| Ok(()), - |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(name) - if fun::get(name) - .expect("inconsistent function application: unknown function") - .is_recursive() => - { - Err(()) - } - _ => Ok(ZipDoTotal::Upp { yielded: () }), - }, - |frame| match frame { - ZipFrame { - thing: ZipOp::Fun(name), - .. - } - if fun::get(name) - .expect("inconsistent function application: unknown function") - .is_recursive() => - { - Err(()) - } - mut frame => { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: \ - empty `rgt_args` (has_fun_app_or_adt)", - ); - Ok(ZipDo::Trm { nu_term, frame }) - } - }, - ); + /// The kids of this term, if any. + pub fn kids(&self) -> Option<&[Term]> { + if let RTerm::App { ref args, .. } = *self { + Some(args) + } else { + None + } + } - res.is_err() + /// Checks whether a term is an equality. + pub fn is_eq(&self) -> bool { + match *self { + RTerm::App { op: Op::Eql, .. } => true, + _ => false, + } } - /// Returns true if the term mentions a function or an ADT. - pub fn has_fun_app_or_adt(&self) -> bool { - use self::zip::*; + /// Term evaluation. + pub fn eval(&self, model: &E) -> Res { + eval::eval(&factory::term(self.clone()), model) + } - // Will be `Ok(())` if there's no function application, and `Err(())` - // otherwise. - let res = zip( - &self.to_hcons(), - |term| { - if term.fun_inspect().is_some() - || term.dtyp_new_inspect().is_some() - || term.dtyp_slc_inspect().is_some() - { - Err(()) - } else { - Ok(None) - } - }, - |_| Ok(()), - |zip_op, _, _: ()| match zip_op { - ZipOp::Fun(_) | ZipOp::New(_) | ZipOp::Slc(_) => Err(()), - _ => Ok(ZipDoTotal::Upp { yielded: () }), - }, - |frame| match frame { - ZipFrame { - thing: ZipOp::Fun(_), - .. - } - | ZipFrame { - thing: ZipOp::New(_), - .. - } - | ZipFrame { - thing: ZipOp::Slc(_), - .. - } => Err(()), - mut frame => { - let nu_term = frame.rgt_args.next().expect( - "illegal call to `partial_op`: - empty `rgt_args` (has_fun_app_or_adt)", - ); - Ok(ZipDo::Trm { nu_term, frame }) - } - }, - ); + /// The highest variable index appearing in the term. + pub fn highest_var(&self) -> Option { + let mut max = None; - res.is_err() + for var_or_cst in self.leaf_iter() { + if let Either::Left((_, var_idx)) = var_or_cst { + max = Some(::std::cmp::max(var_idx, max.unwrap_or_else(|| 0.into()))) + } + } + + max } /// TOP-DOWN term substitution. @@ -1284,7 +1424,7 @@ impl RTerm { } else { panic!("illegal constant array application to 0 arguments") }, - ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), + ZipOp::Fun(name) => term::fun(name.clone(), acc), }; Ok(ZipDoTotal::Upp { yielded }) @@ -1378,7 +1518,7 @@ impl RTerm { } else { panic!("illegal constant array application to 0 arguments") }, - ZipOp::Fun(name) => term::fun(typ.clone(), name.clone(), acc), + ZipOp::Fun(name) => term::fun(name.clone(), acc), }; Ok(ZipDoTotal::Upp { yielded }) @@ -1443,14 +1583,13 @@ impl RTerm { /// # Examples /// /// ```rust - /// use hoice::term ; - /// + /// # use hoice::term ; /// let bv0 = term::bool_var(0) ; /// let bv1 = term::bool_var(1) ; /// let bv2 = term::bool_var(2) ; /// let rhs = term::or(vec![bv1, bv2]) ; /// let term = term::eq(bv0, rhs.clone()) ; - /// debug_assert_eq! { term.as_subst(), Some((0.into(), rhs)) } + /// assert_eq! { term.as_subst(), Some((0.into(), rhs)) } /// ``` pub fn as_subst(&self) -> Option<(VarIdx, Term)> { if let Some(kids) = self.eq_inspect() { @@ -1540,8 +1679,7 @@ impl RTerm { /// # Examples /// /// ```rust - /// use hoice::term ; - /// + /// # use hoice::term ; /// let term = term::u_minus( term::int_var(0) ) ; /// println!("{}", term) ; /// assert_eq!{ @@ -1657,6 +1795,119 @@ impl RTerm { } } +/// Term writing. +impl RTerm { + /// Write a real term using a special function to write variables. + pub fn write(&self, w: &mut W, write_var: WriteVar) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + self.write_with(w, write_var, None) + } + + /// Write a real term using a special function to write variables. + pub fn write_with( + &self, + w: &mut W, + write_var: WriteVar, + bindings: Option<&bindings::Bindings>, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + self.write_with_raw(w, write_var, bindings.map(|b| b.bindings())) + } + + /// Write a real term using a special function to write variables. + fn write_with_raw( + &self, + w: &mut W, + write_var: WriteVar, + bindings: Option<&[TermMap]>, + ) -> IoRes<()> + where + W: Write, + WriteVar: Fn(&mut W, VarIdx) -> IoRes<()>, + { + // Stores triplets of + // - the elements to write, + // - the prefix string, written before next element + // - the suffix, written once there's no more elements to write + let mut stack = vec![(vec![self], "", "")]; + + 'work: while let Some((mut to_do, sep, end)) = stack.pop() { + use self::RTerm::*; + + if let Some(this_term) = to_do.pop() { + stack.push((to_do, sep, end)); + write!(w, "{}", sep)?; + + // Is term in the bindings? + if let Some(bindings) = bindings { + for map in bindings { + if let Some(var) = map.get(&this_term.to_hcons()) { + Bindings::write_var(w, *var)?; + continue 'work; + } + } + } + + match this_term { + Var(_, v) => write_var(w, *v)?, + + Cst(val) => write!(w, "{}", val)?, + + CArray { term, .. } => { + write!( + w, + "(({} {} {})", + keywords::op::as_, + keywords::op::const_, + this_term.typ() + )?; + stack.push((vec![term], " ", ")")) + } + + App { op, args, .. } => { + write!(w, "({}", op)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + } + + DTypSlc { name, term, .. } => { + write!(w, "({}", name)?; + stack.push((vec![term], " ", ")")) + } + + DTypTst { name, term, .. } => { + write!(w, "({}-{}", keywords::op::is_, name)?; + stack.push((vec![term], " ", ")")) + } + + DTypNew { name, args, .. } => if args.is_empty() { + write!(w, "{}", name)? + } else { + write!(w, "({}", name)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + }, + + Fun { name, args, .. } => if args.is_empty() { + write!(w, "{}", name)? + } else { + write!(w, "({}", name)?; + stack.push((args.iter().rev().map(|t| t.get()).collect(), " ", ")")) + }, + } + } else { + w.write_all(end.as_bytes())? + } + } + + Ok(()) + } +} + impl_fmt!{ RTerm(self, fmt) { let mut buf = Vec::with_capacity(250) ; diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 73f628bb..cf77e452 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -1,4 +1,8 @@ //! Term simplification. +//! +//! # TODO +//! +//! - a lot of collection cloning could be avoided in this module pub use std::cmp::Ordering; use std::ops::Deref; @@ -52,6 +56,16 @@ impl SimplRes { } } +impl_fmt! { + SimplRes(self, fmt) { + match self { + SimplRes::None => write!(fmt, "none"), + SimplRes::Cmp(ord) => write!(fmt, "{:?}", ord), + SimplRes::Yields(trm) => write!(fmt, "yields({})", trm), + } + } +} + /// Adds a term to a set understood as a conjunction. /// /// Returns `true` if the resulting set is false. @@ -149,7 +163,7 @@ pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { } /// Simplifies a conjunction. -pub fn conj_vec_simpl(terms: &mut Vec) { +fn conj_vec_simpl(terms: &mut Vec) { let mut res = Vec::with_capacity(terms.len()); 'add_terms: while let Some(term) = terms.pop() { @@ -215,21 +229,21 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// # println!(" {}", lhs) ; /// let rhs = term::ge( term::int_var(0), term::int(3) ) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Equal) } +/// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Equal) } /// /// # println!(" {}", lhs) ; /// let rhs = term::ge( term::int_var(0), term::int(7) ) ; /// # println!("<= {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Less) } +/// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Less) } /// /// # println!(" {}", rhs) ; /// # println!("=> {}\n\n", lhs) ; -/// debug_assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Greater) } +/// assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Greater) } /// /// let lhs = term::gt( term::int_var(0), term::int(7) ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } +/// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } /// // / let lhs = term::le( term::int_var(0), term::int(7) ) ; // / # println!(" {}", lhs) ; @@ -240,7 +254,7 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// let lhs = term::le( term::int_var(1), term::int(7) ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), None } +/// assert_eq! { conj_simpl(& lhs, & rhs), None } /// /// let lhs = term::le( term::int_var(1), term::int(7) ) ; /// let rhs = term::or( @@ -248,11 +262,11 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } +/// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } /// /// # println!(" {}", rhs) ; /// # println!("=> {}\n\n", lhs) ; -/// debug_assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Less) } +/// assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Less) } /// /// let lhs = term::gt( term::real_var(1), term::real_zero() ) ; /// let rhs = term::ge( @@ -260,11 +274,11 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } +/// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } /// /// # println!(" {}", rhs) ; /// # println!("=> {}\n\n", lhs) ; -/// debug_assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Less) } +/// assert_eq! { conj_simpl(& rhs, & lhs), Cmp(Less) } /// /// let lhs = term::or( /// vec![ @@ -276,7 +290,7 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), None } +/// assert_eq! { conj_simpl(& lhs, & rhs), None } /// /// let lhs = term::or( /// vec![ @@ -288,7 +302,7 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), None } +/// assert_eq! { conj_simpl(& lhs, & rhs), None } /// /// let rhs = term::or( /// vec![ @@ -300,7 +314,7 @@ pub fn conj_vec_simpl(terms: &mut Vec) { /// ) ; /// # println!(" {}", lhs) ; /// # println!("=> {}\n\n", rhs) ; -/// debug_assert_eq! { conj_simpl(& lhs, & rhs), None } +/// assert_eq! { conj_simpl(& lhs, & rhs), None } /// ``` pub fn conj_simpl(lhs: &T1, rhs: &T2) -> SimplRes where @@ -310,7 +324,6 @@ where if conf.term_simpl == 0 { return SimplRes::None; } - let res = conj_simpl_2(lhs, rhs); if res.is_some() { @@ -379,13 +392,10 @@ where } } - match res { - SimplRes::Yields(_) if conf.term_simpl <= 1 => SimplRes::None, - res => res, - } + res } -pub fn conj_simpl_2(lhs: &T1, rhs: &T2) -> SimplRes +fn conj_simpl_2(lhs: &T1, rhs: &T2) -> SimplRes where T1: Deref, T2: Deref, @@ -396,7 +406,7 @@ where let mut greater_count = 0; let mut yields = vec![]; for lhs in args { - match int_conj_simpl(lhs, rhs, true) { + match int_conj_simpl(lhs, rhs) { SimplRes::Cmp(Equal) | SimplRes::Cmp(Less) => return SimplRes::Cmp(Less), SimplRes::Cmp(Greater) => greater_count += 1, @@ -413,7 +423,7 @@ where let mut less_count = 0; let mut yields = vec![]; for rhs in args { - match int_conj_simpl(lhs, rhs, true) { + match int_conj_simpl(lhs, rhs) { SimplRes::Cmp(Equal) | SimplRes::Cmp(Greater) => return SimplRes::Cmp(Greater), SimplRes::Cmp(Less) => less_count += 1, @@ -428,10 +438,115 @@ where } } - int_conj_simpl(lhs, rhs, true) + int_conj_simpl(lhs, rhs) +} + +struct Deconstructed<'a> { + trms: Vec<&'a Term>, +} + +impl<'a> From<&'a Term> for Deconstructed<'a> { + fn from(trm: &'a Term) -> Deconstructed<'a> { + Deconstructed { trms: vec![trm] } + } +} +impl<'a> From> for Deconstructed<'a> { + fn from(mut trms: Vec<&'a Term>) -> Deconstructed<'a> { + if trms.is_empty() { + panic!("trying to create an empty deconstructed term") + } + trms.sort_unstable(); + Deconstructed { trms } + } +} +impl<'a> Deconstructed<'a> { + fn into_term(self) -> Term { + if self.trms.len() == 1 { + self.trms[0].clone() + } else { + let res = term::add(self.trms.into_iter().cloned().collect()); + println!("created {}", res); + res + } + } + fn equal(&self, other: &Self) -> bool { + if self.trms.len() == other.trms.len() { + for (t_1, t_2) in self.trms.iter().zip(other.trms.iter()) { + if t_1 != t_2 { + return false; + } + } + true + } else { + false + } + } + + fn is_opposite(&self, other: &Self) -> bool { + if self.trms.len() == other.trms.len() { + for (t_1, t_2) in self.trms.iter().zip(other.trms.iter()) { + let t_1 = &term::u_minus((*t_1).clone()); + if t_1 != *t_2 { + return false; + } + } + true + } else { + false + } + } +} + +/// Deconstructs a relation between arithmetic subterms. +fn int_deconstruct(term: &T) -> Option<(Op, Deconstructed, Val)> +where + T: Deref, +{ + if let Some((op, args)) = term.deref().app_inspect() { + if args.len() != 2 && !args[0].typ().is_arith() { + return None; + } + + let mut val = if let Some(val) = args[1].val() { + val + } else { + return None; + }; + + if let Some(subargs) = args[0].add_inspect() { + let mut sum = Vec::with_capacity(subargs.len()); + + for arg in subargs { + if let Some(v) = arg.val() { + val = val + .add(&v.minus().expect("type inconsistency")) + .expect("type inconsistency") + } else { + sum.push(arg) + } + } + + let res: Deconstructed = if sum.len() == 1 { + sum.pop().expect("sum has length 1").into() + } else if subargs.len() == sum.len() { + (&args[0]).into() + } else if sum.is_empty() { + // This should be unreachable. + return None; + } else { + sum.into() + }; + + Some((op, res, val)) + } else { + Some((op, (&args[0]).into(), val)) + } + } else { + None + } } -fn int_conj_simpl(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes +fn int_conj_simpl(lhs_term: &T1, rhs_term: &T2) -> SimplRes where T1: Deref, T2: Deref, @@ -448,7 +563,7 @@ where }; } - let (lhs, rhs) = (lhs.deref(), rhs.deref()); + let (lhs, rhs) = (lhs_term.deref(), rhs_term.deref()); // A term implies itself. if lhs == rhs { @@ -468,37 +583,25 @@ where (None, None) => (), } - // Only legal atoms are `vars >= cst` and `vars > cst`. - let (lhs_op, lhs_vars, lhs_cst) = if let Some((op, args)) = lhs.app_inspect() { - if !args[0].typ().is_arith() { - return SimplRes::None; - } - ( - op, - &args[0], - args[1] - .val() - .expect("error during value unwrapping in term simplification (lhs)"), - ) + let (lhs_op, lhs_trm, lhs_cst) = if let Some(res) = int_deconstruct(lhs_term) { + res } else { return SimplRes::None; }; - let (rhs_op, rhs_vars, rhs_cst) = if let Some((op, args)) = rhs.app_inspect() { - if !args[0].typ().is_arith() { - return SimplRes::None; - } - ( - op, - &args[0], - args[1] - .val() - .expect("error during value unwrapping in term simplification (rhs)"), - ) + + let (rhs_op, rhs_trm, rhs_cst) = if let Some(res) = int_deconstruct(rhs_term) { + res } else { return SimplRes::None; }; - if lhs_vars == rhs_vars { + // println!(); + // println!("lhs:"); + // println!(" {} {} {}", lhs_trm.to_string(), lhs_op, lhs_cst); + // println!("rhs:"); + // println!(" {} {} {}", rhs_trm.to_string(), rhs_op, rhs_cst); + + if lhs_trm.equal(&rhs_trm) { match (lhs_op, rhs_op) { (Op::Gt, Op::Gt) | (Op::Ge, Op::Ge) => { return SimplRes::Cmp( @@ -524,9 +627,7 @@ where (Op::Eql, Op::Ge) | (Op::Eql, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { Some(Less) => return SimplRes::Yields(term::fls()), - Some(Equal) if rhs_op == Op::Gt => if conj { - return SimplRes::Yields(term::fls()); - }, + Some(Equal) if rhs_op == Op::Gt => return SimplRes::Yields(term::fls()), Some(Equal) | Some(Greater) => return SimplRes::Cmp(Greater), None => unreachable!(), }, @@ -551,18 +652,14 @@ where && lhs_cst == rhs_cst .minus() .expect("error during rhs inversion in term simplification") - && rhs_vars == &term::u_minus(lhs_vars.clone()) + && lhs_trm.is_opposite(&rhs_trm) { - if conj { - return SimplRes::Yields(term::eq( - lhs_vars.clone(), - lhs_cst - .to_term() - .expect("error during lhs cst to term conversion in term simplification"), - )); - } else { - return SimplRes::Yields(term::tru()); - } + return SimplRes::Yields(term::eq( + lhs_trm.into_term(), + lhs_cst + .to_term() + .expect("error during lhs cst to term conversion in term simplification"), + )); } SimplRes::None @@ -778,7 +875,7 @@ simpl_fun! { if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { if dtyp_args.is_empty() { args[0] = term::dtyp_tst( - constructor.into(), eq_args[1].clone() + constructor, eq_args[1].clone() ) } } @@ -786,7 +883,7 @@ simpl_fun! { if let Some( (_, constructor, dtyp_args) ) = val.dtyp_inspect() { if dtyp_args.is_empty() { args[0] = term::dtyp_tst( - constructor.into(), eq_args[0].clone() + constructor, eq_args[0].clone() ) } } @@ -843,7 +940,6 @@ simpl_fun! { args.push(term) } args.swap_remove(cnt) ; - args.sort_unstable() ; args.dedup() ; cnt = 0 } else { @@ -852,7 +948,7 @@ simpl_fun! { } // if conf.term_simpl >= 3 { - conj_vec_simpl(args) ; + conj_vec_simpl(args) ; // } if args.is_empty() { @@ -1708,7 +1804,11 @@ simpl_fun! { } /// Tries to create a constant datatype constructor. -fn cst_dtyp_new(typ: Typ, name: String, args: Vec) -> Either)> { +fn cst_dtyp_new(typ: Typ, name: S, args: Vec) -> Either)> +where + S: Into, +{ + let name = name.into(); if args.is_empty() { return Either::Left(val::dtyp_new(typ, name, vec![])); } @@ -1734,7 +1834,10 @@ fn cst_dtyp_new(typ: Typ, name: String, args: Vec) -> Either) -> RTerm { +pub fn dtyp_new(typ: Typ, name: S, args: Vec) -> RTerm +where + S: Into, +{ let (typ, name, mut args) = match cst_dtyp_new(typ, name, args) { Either::Left(val) => return RTerm::Cst(val), Either::Right(stuff) => stuff, @@ -1796,7 +1899,11 @@ pub fn dtyp_new(typ: Typ, name: String, args: Vec) -> RTerm { } /// Simplifies a datatype selector. -pub fn dtyp_slc(typ: Typ, field: String, term: Term) -> Either { +pub fn dtyp_slc(typ: Typ, field: S, term: Term) -> Either +where + S: Into, +{ + let field = field.into(); if let Some(val) = term.val() { if let Some(res) = val.dtyp_slc(&field) { return Either::Left(RTerm::Cst(res)); @@ -1827,7 +1934,11 @@ pub fn dtyp_slc(typ: Typ, field: String, term: Term) -> Either { /// /// The boolean flag returned indicates the polarity of the result. That is, if /// it is `false` then the term should be negated. -pub fn dtyp_tst(constructor: String, term: Term) -> (RTerm, bool) { +pub fn dtyp_tst(constructor: S, term: Term) -> (RTerm, bool) +where + S: Into, +{ + let constructor = constructor.into(); if let Some(val) = term.val() { if let val::RVal::DTypNew { name, .. } = val.get() { return (RTerm::Cst(val::bool(&constructor == name)), true); diff --git a/src/term/typ.rs b/src/term/typ.rs index ed158f79..0841ae8e 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -44,7 +44,7 @@ pub type Typ = HConsed; /// /// ```rust /// use hoice::term::typ::* ; -/// debug_assert_eq! { +/// assert_eq! { /// format!("{}", array(int(), array(int(), int()))), /// "(Array Int (Array Int Int))" /// } @@ -194,7 +194,7 @@ impl RTyp { /// /// let t_1 = typ::array( typ::int(), typ::unk() ) ; /// let t_2 = typ::array( typ::unk(), typ::real() ) ; - /// debug_assert! { t_1.is_compatible(& t_2) } + /// assert! { t_1.is_compatible(& t_2) } /// ``` pub fn is_compatible(&self, other: &Typ) -> bool { if self == other.get() { From 3f127dec5f8812531c5bb8ccf07a0a0ce0f7baa2 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 21 Sep 2018 16:24:22 +0900 Subject: [PATCH 66/94] tweaked term simplification a bit --- src/term/simplify.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/term/simplify.rs b/src/term/simplify.rs index cf77e452..89054bed 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -441,7 +441,13 @@ where int_conj_simpl(lhs, rhs) } +/// Result of deconstructing a sum. +/// +/// This is used in `int_deconstruct` below to deconstruct additions to compare relation over +/// arithmetic terms as a sum of non-constant terms (lhs), an operator and a constant (rhs). The +/// goal is to do without cloning anything. struct Deconstructed<'a> { + /// Terms of the sum. trms: Vec<&'a Term>, } @@ -459,7 +465,9 @@ impl<'a> From> for Deconstructed<'a> { Deconstructed { trms } } } + impl<'a> Deconstructed<'a> { + /// Turns a deconstructed sum in a term. fn into_term(self) -> Term { if self.trms.len() == 1 { self.trms[0].clone() @@ -469,6 +477,7 @@ impl<'a> Deconstructed<'a> { res } } + /// True if the two deconstructed sum are the same. fn equal(&self, other: &Self) -> bool { if self.trms.len() == other.trms.len() { for (t_1, t_2) in self.trms.iter().zip(other.trms.iter()) { @@ -481,12 +490,12 @@ impl<'a> Deconstructed<'a> { false } } - + /// True if the two deconstructed terms add to zero. fn is_opposite(&self, other: &Self) -> bool { if self.trms.len() == other.trms.len() { - for (t_1, t_2) in self.trms.iter().zip(other.trms.iter()) { + for t_1 in &self.trms { let t_1 = &term::u_minus((*t_1).clone()); - if t_1 != *t_2 { + if other.trms.iter().all(|t| *t != t_1) { return false; } } From e52cde63fa7ea141063e9b0ada32efab984147bc Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 21 Sep 2018 17:14:10 +0900 Subject: [PATCH 67/94] term inspection functions all documented --- src/term/mod.rs | 213 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 212 insertions(+), 1 deletion(-) diff --git a/src/term/mod.rs b/src/term/mod.rs index 6bba6bec..928967ad 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1046,6 +1046,17 @@ impl RTerm { /// Variant deconstructors. impl RTerm { /// The operator and the kids of a term. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::modulo( term::int_var(0), term::int(7) ); + /// + /// let (op, args) = t.app_inspect().unwrap(); + /// assert_eq! { op, Op::Mod } + /// assert_eq! { args, &vec![ term::int_var(0), term::int(7) ] } + /// ``` pub fn app_inspect(&self) -> Option<(Op, &Vec)> { if let RTerm::App { op, ref args, .. } = *self { Some((op, args)) @@ -1055,6 +1066,18 @@ impl RTerm { } /// Returns the kids of an ite. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::ite( term::bool_var(3), term::int_var(0), term::int(7) ); + /// + /// let (cnd, thn, els) = t.ite_inspect().unwrap(); + /// assert_eq! { cnd, &term::bool_var(3) } + /// assert_eq! { thn, &term::int_var(0) } + /// assert_eq! { els, &term::int(7) } + /// ``` pub fn ite_inspect(&self) -> Option<(&Term, &Term, &Term)> { if let RTerm::App { op: Op::Ite, @@ -1070,6 +1093,21 @@ impl RTerm { } /// Inspects a function application. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ term, term::typ, dtyp, fun }; + /// fun::test::create_length_fun(); + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// + /// let nil = term::dtyp_new(list.clone(), "nil", vec![]); + /// let t = term::fun( fun::test::length_fun_name(), vec![ nil.clone() ] ); + /// + /// let (name, args) = t.fun_inspect().unwrap(); + /// assert_eq! { name, fun::test::length_fun_name() } + /// assert_eq! { args, & vec![ nil ] } + /// ``` pub fn fun_inspect(&self) -> Option<(&String, &Vec)> { if let RTerm::Fun { ref name, ref args, .. @@ -1082,6 +1120,16 @@ impl RTerm { } /// Returns the kid of a negation. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::not(term::bool_var(3)); + /// + /// let kid = t.neg_inspect().unwrap(); + /// assert_eq! { kid, &term::bool_var(3) } + /// ``` pub fn neg_inspect(&self) -> Option<&Term> { if let RTerm::App { op: Op::Not, @@ -1097,6 +1145,18 @@ impl RTerm { } /// Returns the kids of conjunctions. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let not_v_3 = term::not(term::bool_var(3)); + /// let ge = term::ge( term::int_var(0), term::int(17) ); + /// let t = term::and(vec![ not_v_3.clone(), ge.clone() ]); + /// + /// let kids = t.conj_inspect().unwrap(); + /// assert_eq! { kids, &vec![not_v_3, ge] } + /// ``` pub fn conj_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::And, @@ -1111,6 +1171,18 @@ impl RTerm { } /// Returns the kids of disjunctions. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let not_v_3 = term::not(term::bool_var(3)); + /// let ge = term::ge( term::int_var(0), term::int(17) ); + /// let t = term::or(vec![ not_v_3.clone(), ge.clone() ]); + /// + /// let kids = t.disj_inspect().unwrap(); + /// assert_eq! { kids, &vec![not_v_3, ge] } + /// ``` pub fn disj_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Or, @@ -1125,6 +1197,28 @@ impl RTerm { } /// Returns the kids of equalities. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::eq( term::int_var(0), term::int(0) ); + /// + /// let kids = t.eq_inspect().unwrap(); + /// assert_eq! { kids, &vec![ term::int_var(0), term::int(0) ] } + /// + /// // Kids will change in general. For instance: + /// let t = term::eq( term::int_var(0), term::int(17) ); + /// + /// let kids = t.eq_inspect().unwrap(); + /// assert_ne! { kids, &vec![ term::int_var(0), term::int(17) ] } + /// assert_eq! { + /// kids, &vec![ + /// term::add(vec![term::int_var(0), term::u_minus(term::int(17))]), + /// term::int(0) + /// ] + /// } + /// ``` pub fn eq_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Eql, @@ -1139,6 +1233,23 @@ impl RTerm { } /// Returns the kids of additions. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int(17)] ); + /// + /// let kids = t.add_inspect().unwrap(); + /// assert_eq! { kids, &vec![ term::int_var(0), term::int(17) ] } + /// + /// // Be careful that the order might change. + /// let t = term::add( vec![term::int_var(3), term::int_var(0)] ); + /// + /// let kids = t.add_inspect().unwrap(); + /// assert_ne! { kids, &vec![ term::int_var(3), term::int_var(0) ] } + /// assert_eq! { kids, &vec![ term::int_var(0), term::int_var(3) ] } + /// ``` pub fn add_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Add, @@ -1153,7 +1264,10 @@ impl RTerm { } /// Returns the kids of subtractions. - pub fn sub_inspect(&self) -> Option<&Vec> { + /// + /// Unused currently, subtraction are rewritten as addition with unary minuses. + #[allow(dead_code)] + fn sub_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Sub, ref args, @@ -1167,6 +1281,28 @@ impl RTerm { } /// Returns the kids of multiplications. + /// + /// Deconstructs *only* multiplications in the sense of [`Op::Mul`], *not to be confused* with + /// [`Op::CMul`]. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::mul( vec![term::int_var(0), term::int_var(2)] ); + /// + /// let kids = t.mul_inspect().unwrap(); + /// assert_eq! { kids, &vec![ term::int_var(0), term::int_var(2) ] } + /// + /// // Careful of `Op::CMul`. + /// let t = term::mul( vec![term::int(3), term::int_var(0)] ); + /// + /// assert! { t.mul_inspect().is_none() } + /// assert! { t.cmul_inspect().is_some() } + /// ``` + /// + /// [`Op::Mul`]: enum.Op.html#variant.Mul + /// [`Op::CMul`]: enum.Op.html#variant.CMul pub fn mul_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Mul, @@ -1181,6 +1317,29 @@ impl RTerm { } /// Returns the kids of a constant multiplication. + /// + /// Deconstructs *only* multiplications in the sense of [`Op::CMul`], *not to be confused* with + /// [`Op::Mul`]. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::mul( vec![term::int(3), term::int_var(0)] ); + /// + /// let (val, kid) = t.cmul_inspect().unwrap(); + /// assert_eq! { val, val::int(3) } + /// assert_eq! { kid, &term::int_var(0) } + /// + /// let t = term::cmul( 7, t.clone() ); + /// + /// let (val, kid) = t.cmul_inspect().unwrap(); + /// assert_eq! { val, val::int(7 * 3) } + /// assert_eq! { kid, &term::int_var(0) } + /// ``` + /// + /// [`Op::Mul`]: enum.Op.html#variant.Mul + /// [`Op::CMul`]: enum.Op.html#variant.CMul pub fn cmul_inspect(&self) -> Option<(Val, &Term)> { if let RTerm::App { op: Op::CMul, @@ -1200,6 +1359,29 @@ impl RTerm { } /// Returns the kids of a datatype tester. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// let t = term::dtyp_tst("insert", term::var(0, list.clone())); + /// + /// # println!("{}", t); + /// let (constructor, term) = t.dtyp_tst_inspect().unwrap(); + /// assert_eq! { constructor, "insert" } + /// assert_eq! { term, &term::var(0, list.clone()) } + /// + /// // Careful of unary tester rewriting. + /// let t = term::dtyp_tst("nil", term::var(0, list.clone())); + /// # println!("{}", t); + /// assert! { t.dtyp_tst_inspect().is_none() } + /// + /// let negated = t.neg_inspect().unwrap(); + /// let (constructor, term) = negated.dtyp_tst_inspect().unwrap(); + /// assert_eq! { constructor, "insert" } + /// assert_eq! { term, &term::var(0, list) } + /// ``` pub fn dtyp_tst_inspect(&self) -> Option<(&str, &Term)> { if let RTerm::DTypTst { name, term, .. } = self { Some((name, term)) @@ -1209,6 +1391,23 @@ impl RTerm { } /// Returns the kids of a datatype constructor. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// let nil = term::dtyp_new(list.clone(), "nil", vec![]); + /// let only_v_0 = term::dtyp_new( + /// list.clone(), "insert", vec![ term::int_var(0), nil.clone()] + /// ); + /// + /// # println!("{}", only_v_0); + /// let (typ, constructor, kids) = only_v_0.dtyp_new_inspect().unwrap(); + /// assert_eq! { typ, & list } + /// assert_eq! { constructor, "insert" } + /// assert_eq! { kids, &[term::int_var(0), nil] } + /// ``` pub fn dtyp_new_inspect(&self) -> Option<(&Typ, &str, &[Term])> { if let RTerm::DTypNew { typ, name, args, .. @@ -1221,6 +1420,18 @@ impl RTerm { } /// Returns the kids of a datatype selector. + /// + /// ```rust + /// # use hoice::common::*; + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// let only_v_0 = term::dtyp_slc(list.clone(), "tail", term::int_var(0)); + /// + /// # println!("{}", only_v_0); + /// let (typ, constructor, kids) = only_v_0.dtyp_slc_inspect().unwrap(); + /// assert_eq! { typ, & list } + /// assert_eq! { constructor, "tail" } + /// assert_eq! { kids, &term::int_var(0) } + /// ``` pub fn dtyp_slc_inspect(&self) -> Option<(&Typ, &str, &Term)> { if let RTerm::DTypSlc { typ, name, term, .. From 9fba835657e2d00f294c157695831f0dfe54686c Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 21 Sep 2018 18:52:38 +0900 Subject: [PATCH 68/94] almost done test/documenting core term functions --- src/term/factory.rs | 2 +- src/term/mod.rs | 409 ++++++++++++++++++++++++++++++++++++++------ src/term/test.rs | 10 +- src/val/mod.rs | 5 + 4 files changed, 369 insertions(+), 57 deletions(-) diff --git a/src/term/factory.rs b/src/term/factory.rs index 341d30f3..0e9eab06 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -149,7 +149,7 @@ pub fn real>(r: R) -> Term { } /// Creates a real constant from a float. #[inline] -pub fn real_of_float(f: f64) -> Term { +pub fn real_of(f: f64) -> Term { real(rat_of_float(f)) } /// Creates the constant `0`. diff --git a/src/term/mod.rs b/src/term/mod.rs index 928967ad..2f7f27c4 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -305,7 +305,7 @@ impl RTerm { /// assert! { to_real.typ().is_real() } /// /// let term = term::eq( - /// to_real, term::real_of_float(7.0) + /// to_real, term::real_of(7.0) /// ); /// assert! { term.typ().is_bool() } /// ``` @@ -531,10 +531,10 @@ impl RTerm { /// assert_eq! { sum.arith(), Some(term::int(1)) } /// /// let to_real = term::to_real(sum); - /// assert_eq! { to_real.arith(), Some(term::real_of_float(1.0)) } + /// assert_eq! { to_real.arith(), Some(term::real_of(1.0)) } /// /// let term = term::eq( - /// to_real, term::real_of_float(7.0) + /// to_real, term::real_of(7.0) /// ); /// assert_eq! { term.arith(), None } /// ``` @@ -560,7 +560,7 @@ impl RTerm { /// let sum = term::add(vec![term::int_var(0), one]); /// assert_eq! { sum.int_val(), None } /// - /// let one = term::real_of_float(1.0); + /// let one = term::real_of(1.0); /// assert_eq! { one.int_val(), None } /// ``` pub fn int_val(&self) -> Option<&Int> { @@ -578,7 +578,7 @@ impl RTerm { /// /// ```rust /// use hoice::{ term, common::Rat }; - /// let one = term::real_of_float(1.0); + /// let one = term::real_of(1.0); /// assert_eq! { one.real_val(), Some(&Rat::new(1.into(),1.into())) } /// /// let sum = term::add(vec![term::real_var(0), one]); @@ -840,6 +840,45 @@ impl RTerm { res.is_err() } + + /// The kids of this term, if any. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let kids = vec![term::int_var(0), term::int(2)]; + /// let t = term::add( kids.clone() ); + /// # println!("{}", t); + /// assert_eq! { t.kids(), Some(&kids as &[Term]) } + /// ``` + pub fn kids(&self) -> Option<&[Term]> { + if let RTerm::App { ref args, .. } = *self { + Some(args) + } else { + None + } + } + + /// Checks whether a term is an equality. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int(2)] ); + /// # println!("{}", t); + /// assert! { ! t.is_eq() } + /// let t = term::eq( t, term::int(42) ); + /// # println!("{}", t); + /// assert! { t.is_eq() } + /// ``` + pub fn is_eq(&self) -> bool { + match *self { + RTerm::App { op: Op::Eql, .. } => true, + _ => false, + } + } } /// Term modifiers. @@ -1447,11 +1486,62 @@ impl RTerm { /// Fold/map/zip functions. impl RTerm { /// Iterator over over all the leafs of a term. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let sum = term::add(vec![ + /// term::int_var(0), + /// term::cmul(2, term::int_var(1)) + /// ]); + /// let t = term::ge(sum, term::int(0)); + /// # println!("{}", t); + /// + /// let int = typ::int(); + /// let (zero, two) = (val::int(0), val::int(2)); + /// let mut leaves: Vec> = vec![ + /// Either::Left((&int, 0.into())), // v_0: Int | + /// Either::Right(& two), // 2 |- LHS of the `<=` + /// Either::Left((&int, 1.into())), // v_1: Int | + /// Either::Right(& zero), // 0, RHS of the `<=` + /// ]; + /// leaves.reverse(); // <- We're going to pop below, hence the reversal. + /// for leaf in t.leaf_iter() { + /// assert_eq! { Some(leaf), leaves.pop() } + /// } + /// ``` pub fn leaf_iter(&self) -> LeafIter { LeafIter::of_rterm(self) } /// Iterates over all the subterms of a term, including itself. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let v_0 = term::int_var(0); + /// let v_1 = term::int_var(1); + /// let two = term::int(2); + /// let two_v_1 = term::mul(vec![ two.clone(), v_1.clone() ]); + /// let sum = term::add(vec![ v_0.clone(), two_v_1.clone() ]); + /// let zero = term::int(0); + /// let t = term::ge(sum.clone(), zero.clone()); + /// # println!("{}", t); + /// let mut all: TermSet = vec![ + /// v_0, v_1, two, two_v_1, sum, zero, t.clone() + /// ].into_iter().collect(); + /// + /// t.iter( + /// |term| { + /// let term = term.to_hcons(); + /// let was_there = all.remove(&term); + /// assert! { was_there } + /// } + /// ); + /// assert! { all.is_empty() } + /// ``` pub fn iter(&self, mut f: F) where F: FnMut(&RTerm), @@ -1475,22 +1565,22 @@ impl RTerm { } } - /// Term evaluation (int). - pub fn int_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_int() - } - - /// Term evaluation (real). - pub fn real_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_real() - } - - /// Term evaluation (bool). - pub fn bool_eval(&self, model: &E) -> Res> { - self.eval(model)?.to_bool() - } - /// Boolean a constant boolean term evaluates to. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::ge( term::int_var(0), term::int_var(0) ); + /// # println!("{}", t); + /// assert_eq! { t.bool(), Some(true) } + /// let t = term::not(t); + /// # println!("{}", t); + /// assert_eq! { t.bool(), Some(false) } + /// let t = term::ge( term::int_var(0), term::int(2) ); + /// # println!("{}", t); + /// assert_eq! { t.bool(), None } + /// ``` pub fn bool(&self) -> Option { match self.bool_eval(&()) { Ok(Some(b)) => Some(b), @@ -1498,16 +1588,19 @@ impl RTerm { } } - /// Evaluates a term with an empty model. - pub fn as_val(&self) -> Val { - if let Ok(res) = self.eval(&()) { - res - } else { - val::none(self.typ().clone()) - } - } - /// Integer value the term evaluates to. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.int(), None } + /// let t = term::add( vec![term::int(5), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.int(), Some(7.into()) } + /// ``` pub fn int(&self) -> Option { if self.typ() != typ::int() { return None; @@ -1519,6 +1612,18 @@ impl RTerm { } /// Real value the term evaluates to. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::real_var(0), term::real_of(2.)] ); + /// # println!("{}", t); + /// assert_eq! { t.real(), None } + /// let t = term::add( vec![term::real_of(5.), term::real_of(2.)] ); + /// # println!("{}", t); + /// assert_eq! { t.real(), Some(rat_of_float(7.).into()) } + /// ``` pub fn real(&self) -> Option { match self.real_eval(&()) { Ok(Some(r)) => Some(r), @@ -1526,37 +1631,60 @@ impl RTerm { } } - /// Turns a constant term in a `Val`. - pub fn val(&self) -> Option { - match *self { - RTerm::Cst(ref val) => Some(val.clone()), - _ => None, - } - } - - /// The kids of this term, if any. - pub fn kids(&self) -> Option<&[Term]> { - if let RTerm::App { ref args, .. } = *self { - Some(args) + /// Evaluates a term with an empty model. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.as_val(), val::none(typ::int()) } + /// let t = term::add( vec![term::int(5), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.as_val(), val::int(7) } + /// ``` + pub fn as_val(&self) -> Val { + if let Ok(res) = self.eval(&()) { + res } else { - None + val::none(self.typ().clone()) } } - /// Checks whether a term is an equality. - pub fn is_eq(&self) -> bool { + /// Turns a constant term in a `Val`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.val(), None } + /// let t = term::add( vec![term::int(5), term::int(2)] ); + /// # println!("{}", t); + /// assert_eq! { t.val(), Some(val::int(7)) } + /// ``` + pub fn val(&self) -> Option { match *self { - RTerm::App { op: Op::Eql, .. } => true, - _ => false, + RTerm::Cst(ref val) => Some(val.clone()), + _ => None, } } - /// Term evaluation. - pub fn eval(&self, model: &E) -> Res { - eval::eval(&factory::term(self.clone()), model) - } - /// The highest variable index appearing in the term. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::ge( + /// term::add( vec![term::real_var(0), term::real_var(2), term::real_of(17.)] ), + /// term::real_var(666) + /// ); + /// # println!("{}", t); + /// assert_eq! { t.highest_var(), Some(666.into()) } + /// ``` pub fn highest_var(&self) -> Option { let mut max = None; @@ -1570,11 +1698,77 @@ impl RTerm { } /// TOP-DOWN term substitution. + /// + /// Applies the term map provided to all matching terms while going down in the term. When it + /// replaces a subterm with `trm` from `map`, this function will not go down `trm`. + /// + /// This is used by let-[`bindings`] in clauses for concise printing. + /// + /// This function is equivalent to [`self.top_down_map(|t| map.get(t).cloned())`]. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let three_v_0 = term::cmul( 3, term::int_var(0) ); + /// let t = term::ge( + /// term::add(vec![ three_v_0.clone(), term::int_var(2) ]), + /// term::int(3) + /// ); + /// # println!("{}", t); + /// let map: TermMap = vec![ + /// ( three_v_0, term::int_var(7) ), + /// // This mapping won't be used, as the mapping above will trigger first. + /// ( term::int_var(0), term::int_var(8) ), + /// // Likewise, this mapping will only trigger in the LHS of the `>=`, not in the + /// // multiplication since the first binding will replace it. + /// ( term::int(3), term::int_var(9) ), + /// // This last one will not be used at all since the function does not go down new terms. + /// ( term::int_var(7), term::int_var(42) ), + /// ].into_iter().collect(); + /// + /// assert_eq! { &format!("{}", t.term_subst(&map)), "(>= (+ v_2 v_7 (* (- 1) v_9)) 0)" } + /// ``` + /// + /// [`bindings`]: bindings/index.html (bindings module) + /// [`self.top_down_map(|t| map.get(t).cloned())`]: #method.top_down_map + /// (top_down_map function over RTerm) pub fn term_subst(&self, map: &TermMap) -> Term { self.top_down_map(|term| map.get(term).cloned()) } /// TOP-DOWN map over terms. + /// + /// Applies the term map provided (as a function) to all matching terms while going down in the + /// term. When it replaces a subterm with `trm` from `map`, this function will not go down + /// `trm`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let three_v_0 = term::cmul( 3, term::int_var(0) ); + /// let t = term::ge( + /// term::add(vec![ three_v_0.clone(), term::int_var(2) ]), + /// term::int(3) + /// ); + /// # println!("{}", t); + /// let map: TermMap = vec![ + /// ( three_v_0, term::int_var(7) ), + /// // This mapping won't be used, as the mapping above will trigger first. + /// ( term::int_var(0), term::int_var(8) ), + /// // Likewise, this mapping will only trigger in the LHS of the `>=`, not in the + /// // multiplication since the first binding will replace it. + /// ( term::int(3), term::int_var(9) ), + /// // This last one will not be used at all since the function does not go down new terms. + /// ( term::int_var(7), term::int_var(42) ), + /// ].into_iter().collect(); + /// + /// assert_eq! { + /// &format!("{}", t.top_down_map(|t| map.get(t).cloned())), + /// "(>= (+ v_2 v_7 (* (- 1) v_9)) 0)" + /// } + /// ``` pub fn top_down_map(&self, mut f: Fun) -> Term where Fun: for<'a> FnMut(&'a Term) -> Option, @@ -2006,6 +2200,119 @@ impl RTerm { } } +/// Term evaluation. +impl RTerm { + /// Term evaluation. + /// + /// Fails when the model given does not type-check with respect to the model provided. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int_var(2), term::int(17)] ); + /// let values: VarMap<_> = vec![ + /// val::int(7), // v_0 + /// val::int(0), // v_1 (not used in `t`) + /// val::int(2), // v_2 + /// ].into(); + /// assert_eq! { t.eval(&values).unwrap(), val::int(7 + 2 + 17) } + /// + /// let ill_typed: VarMap<_> = vec![ + /// val::int(7), // v_0 + /// val::int(0), // v_1 (not used in `t`) + /// val::bool(false), // v_2 ILL-TYPED + /// ].into(); + /// assert! { t.eval(&ill_typed).is_err() } + /// ``` + pub fn eval(&self, model: &E) -> Res { + eval::eval(&factory::term(self.clone()), model) + } + + /// Term evaluation (int). + /// + /// Fails whenever [`self.eval(model)`] would fail, or if the term evaluates to a value that's + /// not of type `Int`. Returns `None` if the model is partial and evaluation resulted in a + /// non-value. + /// + /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_int())`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( vec![term::int_var(0), term::int_var(2), term::int(17)] ); + /// let values: VarMap<_> = vec![ + /// val::int(7), // v_0 + /// val::int(0), // v_1 (not used in `t`) + /// val::int(2), // v_2 + /// ].into(); + /// assert_eq! { t.int_eval(&values).unwrap(), Some( (7 + 2 + 17).into() ) } + /// ``` + /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + pub fn int_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_int() + } + + /// Term evaluation (real). + /// + /// Fails whenever [`self.eval(model)`] would fail, or if the term evaluates to a value that's + /// not of type `Real`. Returns `None` if the model is partial and evaluation resulted in a + /// non-value. + /// + /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_real())`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::add( + /// vec![term::real_var(0), term::real_var(2), term::real_of(17.)] + /// ); + /// let values: VarMap<_> = vec![ + /// val::real_of(7.), // v_0 + /// val::real_of(0.), // v_1 (not used in `t`) + /// val::real_of(2.), // v_2 + /// ].into(); + /// assert_eq! { t.real_eval(&values).unwrap(), Some( rat_of_float(7. + 2. + 17.).into() ) } + /// ``` + /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + pub fn real_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_real() + } + + /// Term evaluation (bool). + /// + /// Fails whenever [`self.eval(model)`] would fail, or if the term evaluates to a value that's + /// not of type `Bool`. Returns `None` if the model is partial and evaluation resulted in a + /// non-value. + /// + /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_bool())`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let t = term::ge( + /// term::add( vec![term::real_var(0), term::real_var(2), term::real_of(17.)] ), + /// term::real_of(42.) + /// ); + /// let values: VarMap<_> = vec![ + /// val::real_of(7.), // v_0 + /// val::real_of(0.), // v_1 (not used in `t`) + /// val::real_of(2.), // v_2 + /// ].into(); + /// assert_eq! { t.bool_eval(&values).unwrap(), Some( false.into() ) } + /// ``` + /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + pub fn bool_eval(&self, model: &E) -> Res> { + self.eval(model)?.to_bool() + } +} + /// Term writing. impl RTerm { /// Write a real term using a special function to write variables. diff --git a/src/term/test.rs b/src/term/test.rs index 18245e52..4040f560 100644 --- a/src/term/test.rs +++ b/src/term/test.rs @@ -385,7 +385,7 @@ fn cst_or_5() { #[test] fn add_real_1() { let v_1 = term::var(0, typ::real()); - let add = term::add(vec![term::real_of_float(-2.0), v_1.clone()]); + let add = term::add(vec![term::real_of(-2.0), v_1.clone()]); let model = model!(val::real(rat_of_float(1.0))); assert_eval!( real model => add, - 1.0 ); } @@ -395,11 +395,11 @@ fn ite_1() { let v_1 = term::var(0, typ::real()); let ite = term::ite( term::eq( - term::add(vec![term::real_of_float(-2.0), v_1.clone()]), - term::real_of_float(0.0), + term::add(vec![term::real_of(-2.0), v_1.clone()]), + term::real_of(0.0), ), - term::real_of_float(1.0), - term::add(vec![term::real_of_float(1.0), v_1.clone()]), + term::real_of(1.0), + term::add(vec![term::real_of(1.0), v_1.clone()]), ); let model = model!(val::real(rat_of_float(1.0))); println!("ite_1"); diff --git a/src/val/mod.rs b/src/val/mod.rs index 76a30ef9..76ca5938 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -123,6 +123,11 @@ pub fn int>(i: I) -> Val { pub fn real>(r: R) -> Val { mk(RVal::R(r.into())) } +/// Creates a real value from a float. +pub fn real_of(f: f64) -> Val { + real(rat_of_float(f)) +} + /// Creates a non-value for a type. pub fn none(typ: Typ) -> Val { mk(RVal::N(typ)) From c27b2019cff267862e0d144223573c0c89e58430 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 24 Sep 2018 16:09:03 +0900 Subject: [PATCH 69/94] done documenting Term type --- src/term/mod.rs | 460 +++++++++++++++++++++++++++++++----------------- 1 file changed, 294 insertions(+), 166 deletions(-) diff --git a/src/term/mod.rs b/src/term/mod.rs index 2f7f27c4..0d9f67ef 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -21,6 +21,18 @@ //! all by itself. Variables are given meaning by the `sig` field of a [`Pred`] or the [`VarInfo`]s //! stored in a [`Clause`]. //! +//! [`RTerm`]: enum.RTerm.html (RTerm enum) +//! [`Term`]: type.Term.html (Term type) +//! [`HashConsign`]: https://crates.io/crates/hashconsing (hashconsing crate) +//! [`var`]: fn.var.html (var creation function) +//! [`int`]: fn.int.html (int creation function) +//! [`app`]: fn.app.html (app creation function) +//! [`TTerm`]: enum.tterm.html (top term enum) +//! [`VarIdx`]: ../common/struct.VarIdx.html (variable index struct) +//! [`Clause`]: ../common/struct.Clause.html (Clause struct) +//! [`VarInfo`]: ../info/struct.VarInfo.html (VarInfo struct) +//! [`Pred`]: ../info/struct.Pred.html (Pred struct) +//! //! # Examples //! //! ```rust @@ -42,18 +54,6 @@ //! _ => panic!("not an equality"), //! } //! ``` -//! -//! [`RTerm`]: enum.RTerm.html (RTerm enum) -//! [`Term`]: type.Term.html (Term type) -//! [`HashConsign`]: https://crates.io/crates/hashconsing (hashconsing crate) -//! [`var`]: fn.var.html (var creation function) -//! [`int`]: fn.int.html (int creation function) -//! [`app`]: fn.app.html (app creation function) -//! [`TTerm`]: enum.tterm.html (top term enum) -//! [`VarIdx`]: ../common/struct.VarIdx.html (variable index struct) -//! [`Clause`]: ../common/struct.Clause.html (Clause struct) -//! [`VarInfo`]: ../info/struct.VarInfo.html (VarInfo struct) -//! [`Pred`]: ../info/struct.Pred.html (Pred struct) use hashconsing::*; @@ -1324,6 +1324,9 @@ impl RTerm { /// Deconstructs *only* multiplications in the sense of [`Op::Mul`], *not to be confused* with /// [`Op::CMul`]. /// + /// [`Op::Mul`]: enum.Op.html#variant.Mul + /// [`Op::CMul`]: enum.Op.html#variant.CMul + /// /// # Examples /// /// ```rust @@ -1339,9 +1342,6 @@ impl RTerm { /// assert! { t.mul_inspect().is_none() } /// assert! { t.cmul_inspect().is_some() } /// ``` - /// - /// [`Op::Mul`]: enum.Op.html#variant.Mul - /// [`Op::CMul`]: enum.Op.html#variant.CMul pub fn mul_inspect(&self) -> Option<&Vec> { if let RTerm::App { op: Op::Mul, @@ -1360,6 +1360,9 @@ impl RTerm { /// Deconstructs *only* multiplications in the sense of [`Op::CMul`], *not to be confused* with /// [`Op::Mul`]. /// + /// [`Op::Mul`]: enum.Op.html#variant.Mul + /// [`Op::CMul`]: enum.Op.html#variant.CMul + /// /// # Examples /// /// ```rust @@ -1376,9 +1379,6 @@ impl RTerm { /// assert_eq! { val, val::int(7 * 3) } /// assert_eq! { kid, &term::int_var(0) } /// ``` - /// - /// [`Op::Mul`]: enum.Op.html#variant.Mul - /// [`Op::CMul`]: enum.Op.html#variant.CMul pub fn cmul_inspect(&self) -> Option<(Val, &Term)> { if let RTerm::App { op: Op::CMul, @@ -1483,6 +1483,261 @@ impl RTerm { } } +/// Variable substitution. +/// +/// All substitution functions take a mapping from variables to terms in the form of a +/// [`VarIndexed`]. +/// +/// The central variable substitution function is [`subst_custom`]. It applies a substitution and +/// takes a flag indicating whether the substitution should be considered total or not. The +/// difference is crucial: when applying a substitution to a term coming from the body of a clause +/// to form a term for a predicate definition, the substitution should **always** be total. +/// Otherwise clauses variables will end up in the predicate's definition which makes no sense and +/// is dangerous. Conversely, substitution from a predicate term to a clause term should always be +/// total. +/// +/// As such, makes sure that you use [`subst_total`] whenever you're doing clause-to-predicate or +/// predicate-to-clause conversions. Otherwise, [`subst`] performs partial variable substitution, +/// which can never fail. Last, [`subst_fp`] iterates until the fixed-point of a substitution. +/// +/// [`VarIndexed`]: ../common/trait.VarIndexed.html (VarIndexed trait) +/// [`subst_custom`]: enum.RTerm.html#method.subst_custom (subst_custom function for RTerm) +/// [`subst_total`]: enum.RTerm.html#method.subst_total (subst_total function for RTerm) +/// [`subst_fp`]: enum.RTerm.html#method.subst_fp (subst_fp function for RTerm) +/// [`subst`]: enum.RTerm.html#method.subst (subst function for RTerm) +impl RTerm { + /// Variable substitution. + /// + /// The `total` flag causes substitution to fail if a variable that's not in `map`. The boolean + /// returned is true if at least one substitution occurred. The map itself is given as a + /// [`VarIndexed`]. + /// + /// [`VarIndexed`]: ../common/trait.VarIndexed.html (VarIndexed trait) + /// + /// # Examples + /// + /// Total substitution. + /// + /// ```rust + /// # use hoice::common::*; + /// let three_v_0 = term::cmul( 3, term::int_var(0) ); + /// let t = term::ge( + /// term::add(vec![ three_v_0.clone(), term::int_var(2) ]), + /// term::mul(vec![ term::int(3), term::int_var(1) ]) + /// ); + /// # println!("{}", t); + /// let map: VarMap<_> = vec![ + /// // v_0 -> + /// term::add( vec![term::int_var(7), term::int(2)] ), + /// // v_1 -> + /// term::int_var(8), + /// // v_2 -> + /// term::int_var(9), + /// ].into_iter().collect(); + /// + /// // Asking for total substitution ~~~~~~~~~~~~~~vvvv + /// let (res, at_least_one) = t.subst_custom(&map, true).unwrap(); + /// assert! { at_least_one } + /// assert_eq! { + /// &format!("{}", res), "(>= (+ v_9 (* 3 v_7) (* (- 3) v_8)) (- 6))" + /// } + /// ``` + /// + /// Partial substitution. + /// + /// ```rust + /// # use hoice::common::*; + /// let three_v_0 = term::cmul( 3, term::int_var(0) ); + /// let t = term::ge( + /// term::add(vec![ three_v_0.clone(), term::int_var(2) ]), + /// term::mul(vec![ term::int(3), term::int_var(1) ]) + /// ); + /// # println!("{}", t); + /// // Map does not mention `v_2`. + /// let map: VarMap<_> = vec![ + /// // v_0 -> + /// term::add( vec![term::int_var(7), term::int(2)] ), + /// // v_1 -> + /// term::int_var(8), + /// ].into_iter().collect(); + /// + /// // Asking for total substitution ~~~vvvv + /// let res = t.subst_custom(&map, true); + /// // Total substitution failed. + /// assert! { res.is_none() } + /// + /// // Asking for partial substitution ~vvvvv + /// let res = t.subst_custom(&map, false); + /// // Success. + /// let (res, at_least_one) = res.unwrap(); + /// assert! { at_least_one } + /// assert_eq! { + /// &format!("{}", res), "(>= (+ v_2 (* 3 v_7) (* (- 3) v_8)) (- 6))" + /// } // `v_2` is still here ~~~~~~~~^^^ + /// ``` + pub fn subst_custom>( + &self, + map: &Map, + total: bool, + ) -> Option<(Term, bool)> { + use self::zip::*; + let mut changed = false; + + let res = zip( + &self.to_hcons(), + |_| Ok(None), + |zip_null| match zip_null { + ZipNullary::Cst(val) => Ok(cst(val.clone())), + ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { + debug_assert_eq! { typ, & term.typ() } + changed = true; + Ok(term) + } else if total { + Err(()) + } else { + Ok(term::var(var, typ.clone())) + }, + }, + |zip_op, typ, mut acc| { + let yielded = match zip_op { + ZipOp::Op(op) => term::app(op, acc), + ZipOp::New(name) => term::dtyp_new(typ.clone(), name.clone(), acc), + + ZipOp::Slc(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype selector {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_slc(typ.clone(), name.clone(), kid) + } else { + panic!( + "illegal application of datatype selector {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::Tst(name) => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal application of datatype tester {} to {} arguments", + conf.bad(name), + acc.len() + 1 + ) + } + term::dtyp_tst(name.clone(), kid) + } else { + panic!( + "illegal application of datatype tester {} to 0 arguments", + conf.bad(name) + ) + }, + + ZipOp::CArray => if let Some(kid) = acc.pop() { + if !acc.is_empty() { + panic!( + "illegal constant array application to {} arguments", + acc.len() + 1 + ) + } + term::cst_array(typ.clone(), kid) + } else { + panic!("illegal constant array application to 0 arguments") + }, + ZipOp::Fun(name) => term::fun(name.clone(), acc), + }; + + Ok(ZipDoTotal::Upp { yielded }) + }, + |mut frame| { + let nu_term = frame + .rgt_args + .next() + .expect("illegal call to `partial_op`: empty `rgt_args` (subst_custom)"); + Ok(ZipDo::Trm { nu_term, frame }) + }, + ); + + if let Ok(term) = res { + Some((term, changed)) + } else { + None + } + } + + /// Variable substitution. + /// + /// Returns the new term and a boolean indicating whether any substitution occurred. Used for + /// substitutions in the same clause / predicate scope. + /// + /// Equivalent to `self.subst_custom(map, false).unwrap()`. For examples see [`subst_custom`]. + /// + /// [`subst_custom`]: #method.subst_custom (subst_custom function for RTerm) + #[inline] + pub fn subst>(&self, map: &Map) -> (Term, bool) { + self.subst_custom(map, false) + .expect("partial substitution can't fail") + } + + /// Fixed-point (partial) variable substitution. + /// + /// Applies `term = term.subst(map)`, starting with `self`, until the substitution does not + /// change the term anymore. Returns the new term and a boolean indicating whether any + /// substitution occurred. + /// + /// This function will loop forever if no fixed-point exists (the map is circular). + /// + /// ```rust + /// # use hoice::common::*; + /// let three_v_0 = term::cmul( 3, term::int_var(0) ); + /// let t = term::ge( + /// term::add(vec![ three_v_0.clone(), term::int_var(2) ]), + /// term::mul(vec![ term::int(3), term::int_var(1) ]) + /// ); + /// # println!("{}", t); + /// let map: VarMap<_> = vec![ + /// // v_0 -> vvvvvvvvvv~~~ v_0 maps to a term mentioning v_1 + /// term::add( vec![term::int_var(1), term::int(2)] ), + /// // v_1 -> + /// term::int_var(8), + /// // v_2 -> + /// term::int_var(1), // v_2 maps to v_1 + /// ].into_iter().collect(); + /// + /// let (res, at_least_one) = t.subst_fp(&map); + /// assert! { at_least_one } + /// assert_eq! { + /// &format!("{}", res), "(>= v_8 (- 6))" + /// } + /// ``` + /// + /// [`subst`]: #method.subst (subst function for RTerm) + pub fn subst_fp>(&self, map: &Map) -> (Term, bool) { + let (mut term, changed) = self.subst(map); + let mut fp = !changed; + while !fp { + let (new_term, changed) = term.subst(map); + term = new_term; + fp = !changed + } + (term, changed) + } + + /// Total variable substitution. + /// + /// Returns the new term and a boolean indicating whether any substitution occurred. Used for + /// substitutions between different same clause / predicate scopes. + /// + /// Equivalent to `self.subst_custom(map, true)`. For examples see [`subst_custom`]. + /// + /// [`subst_custom`]: #method.subst_custom (subst_custom function for RTerm) + pub fn subst_total>(&self, map: &Map) -> Option<(Term, bool)> { + self.subst_custom(map, true) + } +} + /// Fold/map/zip functions. impl RTerm { /// Iterator over over all the leafs of a term. @@ -1706,6 +1961,10 @@ impl RTerm { /// /// This function is equivalent to [`self.top_down_map(|t| map.get(t).cloned())`]. /// + /// [`bindings`]: bindings/index.html (bindings module) + /// [`self.top_down_map(|t| map.get(t).cloned())`]: #method.top_down_map + /// (top_down_map function over RTerm) + /// /// # Examples /// /// ```rust @@ -1729,10 +1988,6 @@ impl RTerm { /// /// assert_eq! { &format!("{}", t.term_subst(&map)), "(>= (+ v_2 v_7 (* (- 1) v_9)) 0)" } /// ``` - /// - /// [`bindings`]: bindings/index.html (bindings module) - /// [`self.top_down_map(|t| map.get(t).cloned())`]: #method.top_down_map - /// (top_down_map function over RTerm) pub fn term_subst(&self, map: &TermMap) -> Term { self.top_down_map(|term| map.get(term).cloned()) } @@ -1846,141 +2101,6 @@ impl RTerm { res.expect("top down map can never fail") } - /// Variable substitution. - /// - /// The `total` flag causes substitution to fail if a variable that's not in - /// `map`. - /// - /// The boolean returned is true if at least on substitution occured. - pub fn subst_custom>( - &self, - map: &Map, - total: bool, - ) -> Option<(Term, bool)> { - use self::zip::*; - let mut changed = false; - - let res = zip( - &self.to_hcons(), - |_| Ok(None), - |zip_null| match zip_null { - ZipNullary::Cst(val) => Ok(cst(val.clone())), - ZipNullary::Var(typ, var) => if let Some(term) = map.var_get(var) { - debug_assert_eq! { typ, & term.typ() } - changed = true; - Ok(term) - } else if total { - Err(()) - } else { - Ok(term::var(var, typ.clone())) - }, - }, - |zip_op, typ, mut acc| { - let yielded = match zip_op { - ZipOp::Op(op) => term::app(op, acc), - ZipOp::New(name) => term::dtyp_new(typ.clone(), name.clone(), acc), - - ZipOp::Slc(name) => if let Some(kid) = acc.pop() { - if !acc.is_empty() { - panic!( - "illegal application of datatype selector {} to {} arguments", - conf.bad(name), - acc.len() + 1 - ) - } - term::dtyp_slc(typ.clone(), name.clone(), kid) - } else { - panic!( - "illegal application of datatype selector {} to 0 arguments", - conf.bad(name) - ) - }, - - ZipOp::Tst(name) => if let Some(kid) = acc.pop() { - if !acc.is_empty() { - panic!( - "illegal application of datatype tester {} to {} arguments", - conf.bad(name), - acc.len() + 1 - ) - } - term::dtyp_tst(name.clone(), kid) - } else { - panic!( - "illegal application of datatype tester {} to 0 arguments", - conf.bad(name) - ) - }, - - ZipOp::CArray => if let Some(kid) = acc.pop() { - if !acc.is_empty() { - panic!( - "illegal constant array application to {} arguments", - acc.len() + 1 - ) - } - term::cst_array(typ.clone(), kid) - } else { - panic!("illegal constant array application to 0 arguments") - }, - ZipOp::Fun(name) => term::fun(name.clone(), acc), - }; - - Ok(ZipDoTotal::Upp { yielded }) - }, - |mut frame| { - let nu_term = frame - .rgt_args - .next() - .expect("illegal call to `partial_op`: empty `rgt_args` (subst_custom)"); - Ok(ZipDo::Trm { nu_term, frame }) - }, - ); - - if let Ok(term) = res { - Some((term, changed)) - } else { - None - } - } - - /// Variable substitution. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occured. - /// - /// Used for substitutions in the same clause / predicate scope. - #[inline] - pub fn subst>(&self, map: &Map) -> (Term, bool) { - self.subst_custom(map, false) - .expect("total substitution can't fail") - } - - /// Fixed-point (partial) variable substitution. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occured. - pub fn subst_fp>(&self, map: &Map) -> (Term, bool) { - let (mut term, mut changed) = self.subst(map); - while changed { - let (new_term, new_changed) = term.subst(map); - term = new_term; - changed = new_changed - } - (term, changed) - } - - /// Total variable substition, returns `None` if there was a variable in the - /// term that was not in the map. - /// - /// Returns the new term and a boolean indicating whether any substitution - /// occsured. - /// - /// Used for substitutions between different same clause / predicate scopes. - pub fn subst_total>(&self, map: &Map) -> Option<(Term, bool)> { - self.subst_custom(map, true) - } - /// Tries to turn a term into a substitution. /// /// Works only on equalities. @@ -2237,6 +2357,8 @@ impl RTerm { /// /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_int())`. /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + /// /// # Examples /// /// ```rust @@ -2249,8 +2371,7 @@ impl RTerm { /// ].into(); /// assert_eq! { t.int_eval(&values).unwrap(), Some( (7 + 2 + 17).into() ) } /// ``` - /// - /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + #[inline] pub fn int_eval(&self, model: &E) -> Res> { self.eval(model)?.to_int() } @@ -2263,6 +2384,8 @@ impl RTerm { /// /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_real())`. /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + /// /// # Examples /// /// ```rust @@ -2277,8 +2400,7 @@ impl RTerm { /// ].into(); /// assert_eq! { t.real_eval(&values).unwrap(), Some( rat_of_float(7. + 2. + 17.).into() ) } /// ``` - /// - /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + #[inline] pub fn real_eval(&self, model: &E) -> Res> { self.eval(model)?.to_real() } @@ -2291,6 +2413,8 @@ impl RTerm { /// /// In fact, this is strictly equivalent to `self.eval(model).and_then(|val| val.to_bool())`. /// + /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + /// /// # Examples /// /// ```rust @@ -2306,8 +2430,7 @@ impl RTerm { /// ].into(); /// assert_eq! { t.bool_eval(&values).unwrap(), Some( false.into() ) } /// ``` - /// - /// [`self.eval(model)`]: #method.eval (eval function over RTerm) + #[inline] pub fn bool_eval(&self, model: &E) -> Res> { self.eval(model)?.to_bool() } @@ -2315,7 +2438,7 @@ impl RTerm { /// Term writing. impl RTerm { - /// Write a real term using a special function to write variables. + /// Writes a term in a writer. pub fn write(&self, w: &mut W, write_var: WriteVar) -> IoRes<()> where W: Write, @@ -2324,7 +2447,10 @@ impl RTerm { self.write_with(w, write_var, None) } - /// Write a real term using a special function to write variables. + /// Writes a term in a writer using optional bindings. + /// + /// Also takes some optional bindings. This is used when printing the body of a clause to apply + /// let-binding factoring on the fly, while printing to the solver. pub fn write_with( &self, w: &mut W, @@ -2338,7 +2464,9 @@ impl RTerm { self.write_with_raw(w, write_var, bindings.map(|b| b.bindings())) } - /// Write a real term using a special function to write variables. + /// Write a term in a writer. + /// + /// Factors code for `write` and `write_with` by taking optional bindings. fn write_with_raw( &self, w: &mut W, From d025f507fc1e69f5d96170c1f0dfd3ae4b63a8a3 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 24 Sep 2018 18:44:02 +0900 Subject: [PATCH 70/94] started documenting factory module --- src/term/factory.rs | 373 ++++++++++++++++++++++++++++++++++++++++++- src/term/simplify.rs | 19 ++- 2 files changed, 382 insertions(+), 10 deletions(-) diff --git a/src/term/factory.rs b/src/term/factory.rs index 0e9eab06..605e006d 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -46,6 +46,26 @@ fn scan_vars(t: &Term) -> VarSet { } /// Variables appearing in a term (cached). +/// +/// This function is not directly defined for `RTerm`s because it only makes sense on hashconsed +/// `Term`s. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let term = term::ge( +/// term::add(vec![ +/// term::int_var(7), +/// term::cmul(7, term::int_var(3)), +/// term::mul( vec![term::int_var(2), term::int_var(5)] ) +/// ]), term::int(0) +/// ); +/// let expected: VarSet = vec![ +/// 7.into(), 3.into(), 2.into(), 5.into() +/// ].into_iter().collect(); +/// assert_eq! { term::vars(&term), expected } +/// ``` #[inline] pub fn vars(t: &Term) -> VarSet { if let Some(vars) = var_cache @@ -63,7 +83,11 @@ pub fn vars(t: &Term) -> VarSet { .clone() } -/// Map over the variables appearing in a term (cached). +/// Iterator over the variables appearing in a term (cached). +/// +/// Each variable is visited only once. +/// +/// Pretty much equivalent to `term::vars(t).into_iter().map(f)`. #[inline] pub fn map_vars(t: &Term, mut f: F) where @@ -92,37 +116,113 @@ where () } -/// Creates a term. +/// Creates a term from a non-hconsed `RTerm`. +/// +/// This function is not really meant to be used outside of the term module, although it cannot do +/// anything bad. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let kid_1 = term::add(vec![term::int_var(0), term::cmul(7, term::int_var(1))]); +/// let kid_2 = term::int(42); +/// let t = term::ge( kid_1, kid_2 ); +/// +/// let rterm = t.get().clone(); +/// let other = term::term(rterm); +/// +/// assert_eq! { other, t } +/// assert_eq! { other.uid(), t.uid() } +/// assert_eq! { other.get(), t.get() } +/// ``` #[inline] pub fn term(t: RTerm) -> Term { factory.mk(t) } /// Creates a variable. +/// +/// See also [`int_var`], [`real_var`] and [`bool_var`]. +/// +/// [`int_var`]: fn.int_var.html (int_var term creation function) +/// [`real_var`]: fn.real_var.html (real_var term creation function) +/// [`bool_var`]: fn.bool_var.html (bool_var term creation function) +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::var(3, typ::bool()); +/// assert_eq! { t.var_idx(), Some(3.into()) } +/// assert_eq! { t.typ(), typ::bool() } +/// assert_eq! { t.get(), & RTerm::Var(typ::bool(), 3.into()) } +/// ``` #[inline] pub fn var>(v: V, typ: Typ) -> Term { factory.mk(RTerm::Var(typ, v.into())) } /// Creates an integer variable. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::int_var(3); +/// assert_eq! { t.var_idx(), Some(3.into()) } +/// assert_eq! { t.typ(), typ::int() } +/// assert_eq! { t.get(), & RTerm::Var(typ::int(), 3.into()) } +/// ``` #[inline] pub fn int_var>(v: V) -> Term { factory.mk(RTerm::Var(typ::int(), v.into())) } /// Creates a real variable. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::real_var(3); +/// assert_eq! { t.var_idx(), Some(3.into()) } +/// assert_eq! { t.typ(), typ::real() } +/// assert_eq! { t.get(), & RTerm::Var(typ::real(), 3.into()) } +/// ``` #[inline] pub fn real_var>(v: V) -> Term { factory.mk(RTerm::Var(typ::real(), v.into())) } /// Creates a boolean variable. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::bool_var(3); +/// assert_eq! { t.var_idx(), Some(3.into()) } +/// assert_eq! { t.typ(), typ::bool() } +/// assert_eq! { t.get(), & RTerm::Var(typ::bool(), 3.into()) } +/// ``` #[inline] pub fn bool_var>(v: V) -> Term { factory.mk(RTerm::Var(typ::bool(), v.into())) } /// Creates a constant. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let val = val::int(42); +/// let t = term::cst(val.clone()); +/// assert_eq! { t.val(), Some(val.clone()) } +/// assert_eq! { t.typ(), typ::int() } +/// assert_eq! { t.get(), & RTerm::Cst(val) } +/// ``` #[inline] pub fn cst>(val: V) -> Term { let val = val.into(); @@ -136,82 +236,343 @@ pub fn cst>(val: V) -> Term { } /// Creates an integer constant. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let val = val::int(42); +/// let t = term::int(42); +/// assert_eq! { t.val(), Some(val.clone()) } +/// assert_eq! { t.typ(), typ::int() } +/// assert_eq! { t.get(), & RTerm::Cst(val) } +/// ``` #[inline] pub fn int>(i: I) -> Term { let i = i.into(); factory.mk(RTerm::Cst(val::int(i))) } /// Creates a real constant. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let val = val::real_of(42.); +/// let t = term::real(Rat::new(42.into(), 1.into())); +/// assert_eq! { t.val(), Some(val.clone()) } +/// assert_eq! { t.typ(), typ::real() } +/// assert_eq! { t.get(), & RTerm::Cst(val) } +/// ``` #[inline] pub fn real>(r: R) -> Term { let r = r.into(); factory.mk(RTerm::Cst(val::real(r))) } /// Creates a real constant from a float. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let val = val::real_of(42.); +/// let t = term::real_of(42.); +/// assert_eq! { t.val(), Some(val.clone()) } +/// assert_eq! { t.typ(), typ::real() } +/// assert_eq! { t.get(), & RTerm::Cst(val) } +/// ``` #[inline] pub fn real_of(f: f64) -> Term { real(rat_of_float(f)) } -/// Creates the constant `0`. + +/// Creates the integer constant `0`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::int_zero(); +/// assert_eq! { t.get(), & RTerm::Cst(val::int(0)) } +/// ``` #[inline] pub fn int_zero() -> Term { int(Int::zero()) } -/// Creates the constant `1`. +/// Creates the integer constant `1`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::int_one(); +/// assert_eq! { t.get(), & RTerm::Cst(val::int(1)) } +/// ``` #[inline] pub fn int_one() -> Term { int(Int::one()) } -/// Creates the constant `0`. + +/// Creates the real constant `0`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::real_zero(); +/// assert_eq! { t.get(), & RTerm::Cst(val::real_of(0.)) } +/// ``` #[inline] pub fn real_zero() -> Term { real(Rat::zero()) } -/// Creates the constant `1`. +/// Creates the real constant `1`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::real_one(); +/// assert_eq! { t.get(), & RTerm::Cst(val::real_of(1.)) } +/// ``` #[inline] pub fn real_one() -> Term { real(Rat::one()) } /// Creates a boolean. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::bool(true); +/// assert_eq! { t.typ(), typ::bool() } +/// assert_eq! { t.get(), & RTerm::Cst(val::bool(true)) } +/// ``` #[inline] pub fn bool(b: bool) -> Term { factory.mk(RTerm::Cst(val::bool(b))) } /// Creates the constant `true`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::tru(); +/// assert_eq! { t.get(), & RTerm::Cst(val::bool(true)) } +/// ``` #[inline] pub fn tru() -> Term { bool(true) } /// Creates the constant `false`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::fls(); +/// assert_eq! { t.get(), & RTerm::Cst(val::bool(false)) } +/// ``` #[inline] pub fn fls() -> Term { bool(false) } /// If-then-else. +/// +/// # Examples +/// +/// General case: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::ite( +/// term::bool_var(0), +/// term::int_var(7), +/// term::int(17), +/// ); +/// assert_eq! { +/// &format!("{}", t), "(ite v_0 v_7 17)" +/// } +/// ``` +/// +/// ## Simplifications +/// +/// Guard is trivial: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::ite( +/// term::fls(), +/// term::int_var(7), +/// term::int(17), +/// ); +/// assert_eq! { &format!("{}", t), "17" } +/// ``` +/// +/// Both branches are equal: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::ite( +/// term::fls(), +/// term::int_var(7), +/// term::int_var(7), +/// ); +/// assert_eq! { &format!("{}", t), "v_7" } +/// ``` #[inline] pub fn ite(c: Term, t: Term, e: Term) -> Term { app(Op::Ite, vec![c, t, e]) } /// Implication. +/// +/// Implications `A => B` are rewritten as `(not A) or B`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::implies( +/// term::bool_var(0), +/// term::or( vec![term::bool_var(7), term::bool_var(3)] ) +/// ); +/// assert_eq! { &format!("{}", t), "(or v_3 (not v_0) v_7)" } +/// ``` #[inline] pub fn implies(lhs: Term, rhs: Term) -> Term { app(Op::Impl, vec![lhs, rhs]) } /// Negates a term. +/// +/// Negations are pushed down as much as possible. +/// +/// # Examples +/// +/// General case: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::not(term::bool_var(0)); +/// assert_eq! { &format!("{}", t), "(not v_0)" } +/// ``` +/// +/// ## Simplifications +/// +/// Double negations: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::not(term::not(term::bool_var(0))); +/// assert_eq! { &format!("{}", t), "v_0" } +/// ``` +/// +/// Negations are pushed down: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::not( +/// term::or(vec![ term::bool_var(0), term::not(term::bool_var(1)) ]) +/// ); +/// assert_eq! { &format!("{}", t), "(and v_1 (not v_0))" } +/// let t = term::not( +/// term::and(vec![ term::bool_var(0), term::not(term::bool_var(1)) ]) +/// ); +/// assert_eq! { &format!("{}", t), "(or v_1 (not v_0))" } +/// ``` #[inline] pub fn not(term: Term) -> Term { app(Op::Not, vec![term]) } + /// Disjunction. +/// +/// # Examples +/// +/// General case: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::or(vec![ term::bool_var(0), term::bool_var(1) ]); +/// assert_eq! { &format!("{}", t), "(or v_0 v_1)" } +/// ``` +/// +/// ## Simplifications +/// +/// Nullary applications: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::or(vec![]); +/// assert_eq! { &format!("{}", t), "false" } +/// ``` +/// +/// Unary applications: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::or(vec![term::bool_var(0)]); +/// assert_eq! { &format!("{}", t), "v_0" } +/// ``` +/// +// / Trivial disjunctions: +// / +// / ```rust +// / # use hoice::common::*; +// / let t = term::or(vec![ +// / term::bool_var(1), term::bool_var(0), term::not(term::bool_var(1)) +// / ]); +// / assert_eq! { &format!("{}", t), "true" } +// / ``` #[inline] pub fn or(terms: Vec) -> Term { app(Op::Or, terms) } + /// Conjunction. +/// +/// # Examples +/// +/// General case: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::and(vec![ term::bool_var(0), term::bool_var(1) ]); +/// assert_eq! { &format!("{}", t), "(and v_0 v_1)" } +/// ``` +/// +/// ## Simplifications +/// +/// Nullary applications: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::and(vec![]); +/// assert_eq! { &format!("{}", t), "true" } +/// ``` +/// +/// Unary applications: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::and(vec![term::bool_var(0)]); +/// assert_eq! { &format!("{}", t), "v_0" } +/// ``` +/// +/// Trivial conjunctions: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::and(vec![ +/// term::bool_var(1), term::bool_var(0), term::not(term::bool_var(1)) +/// ]); +/// assert_eq! { &format!("{}", t), "false" } +/// ``` #[inline] pub fn and(terms: Vec) -> Term { app(Op::And, terms) diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 89054bed..b90c04cc 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -402,6 +402,19 @@ where { use std::cmp::Ordering::*; + // A term is equal to itself. + if lhs.deref() == rhs.deref() { + return SimplRes::Cmp(Equal); + } + + // A term and its negation yield false. + if let Some(sub_lhs) = lhs.neg_inspect() { + if sub_lhs.get() == rhs.deref() { return SimplRes::Yields(term::fls()) } + } + if let Some(sub_rhs) = rhs.neg_inspect() { + if sub_rhs.get() == lhs.deref() { return SimplRes::Yields(term::fls()) } + } + if let Some(args) = lhs.disj_inspect() { let mut greater_count = 0; let mut yields = vec![]; @@ -472,9 +485,7 @@ impl<'a> Deconstructed<'a> { if self.trms.len() == 1 { self.trms[0].clone() } else { - let res = term::add(self.trms.into_iter().cloned().collect()); - println!("created {}", res); - res + term::add(self.trms.into_iter().cloned().collect()) } } /// True if the two deconstructed sum are the same. @@ -574,7 +585,7 @@ where let (lhs, rhs) = (lhs_term.deref(), rhs_term.deref()); - // A term implies itself. + // A term is equal to itself. if lhs == rhs { return SimplRes::Cmp(Equal); } From df3f07a78a38cb5d9f22201b10c96327713e876b Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 26 Sep 2018 13:25:38 +0900 Subject: [PATCH 71/94] term simplification bug fix --- src/common/consts.rs | 128 +++--- src/errors.rs | 158 ++++--- src/instance/pre_instance.rs | 6 +- src/preproc/mod.rs | 42 +- src/preproc/one_lhs.rs | 23 +- src/preproc/one_rhs.rs | 13 +- src/preproc/strict_neg_clauses.rs | 24 +- src/preproc/utils.rs | 6 +- src/term/factory.rs | 75 +++- src/term/simplify.rs | 716 +++++++++++++++++++++--------- 10 files changed, 792 insertions(+), 399 deletions(-) diff --git a/src/common/consts.rs b/src/common/consts.rs index be31d0e9..ee7cadfa 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -1,64 +1,78 @@ //! Constants of the crate. lazy_static! { - /// The constant `10` as an [`Int`][int]. - /// - /// [int]: ../type.Int.html - /// (Int type) - pub static ref ten: ::common::Int = 10.into() ; + /// The constant `10` as an [`Int`]. + /// + /// [`Int`]: ../type.Int.html (Int type) + pub static ref ten: ::common::Int = 10.into() ; } + + +/// Error-related constants. +pub mod err { + /// Description for unsat error(s). + pub static unsat_desc: &'static str = "unsat"; + /// Description for unknown error(s). + pub static unknown_desc: &'static str = "unknown"; + /// Description for timeout error(s). + pub static timeout_desc: &'static str = "timeout"; + /// Description for exit error(s). + pub static exit_desc: &'static str = "exit"; +} + + + /// Use this macro to declare keywords. /// -/// Declares everything and creates a function testing if a string is a -/// keyword. +/// Declares everything and creates a function testing if a string is a keyword. macro_rules! keys { - // Create one keyword. - (|internal def| - $id:ident $def:expr, $doc:meta $(,$meta:meta)* $(,)* - ) => ( - #[$doc] - $(#[$meta])* - pub const $id: & str = $def ; - ) ; - - // Creates some keywords and some functions, if any. - ( - funs { - $( $fn_kind:tt ( $($fn_stuff:tt)* ) )* - } - keys { - $( $id:ident ( $($stuff:tt)* ) )* - } - $( - $doc:meta mod $mod:ident { $($mod_stuff:tt)* } - )* - ) => ( - #[doc = " - True if input is one of the keywords defined in this module and its - submodules. - "] - pub fn is_keyword(s: & str) -> bool { - $( - if $id == s { return true } - )* - $( - if $mod::is_keyword(s) { return true } - )* - false - } - $( keys! { |internal def| $id $($stuff)* } )* - $( - #[$doc] - pub mod $mod { keys! { $($mod_stuff)* } } - )* - ) ; - - (|internal lockstep| - $( ($($left:tt)*) )*, $mid:tt, $right:tt - ) => ( - keys!{ $($($left)* $mid $right)* } - ) ; + // Create one keyword. + (|internal def| + $id:ident $def:expr, $doc:meta $(,$meta:meta)* $(,)* + ) => ( + #[$doc] + $(#[$meta])* + pub const $id: &str = $def ; + ); + + // Creates some keywords and some functions, if any. + ( + funs { + $( $fn_kind:tt ( $($fn_stuff:tt)* ) )* + } + keys { + $( $id:ident ( $($stuff:tt)* ) )* + } + $( + $doc:meta mod $mod:ident { $($mod_stuff:tt)* } + )* + ) => ( + #[doc = " + True if input is one of the keywords defined in this module and its + submodules. + "] + pub fn is_keyword(s: & str) -> bool { + $( + if $id == s { return true } + )* + $( + if $mod::is_keyword(s) { return true } + )* + false + } + $( keys! { |internal def| $id $($stuff)* } )* + $( + #[$doc] + pub mod $mod { keys! { $($mod_stuff)* } } + )* + ); + + (|internal lockstep| + $( ($($left:tt)*) )*, $mid:tt, $right:tt + ) => ( + keys!{ $($($left)* $mid $right)* } + ); } /// Values used in hoice. @@ -78,10 +92,10 @@ pub mod values { pub mod errors { /// Unsat core asked but not active. pub const no_unsat_cores: &str = "\ - unsat core production is not active:\n\ - consider adding `(set-option :produce-unsat-core true)`\n\ - at the start of your script - "; + unsat core production is not active:\n\ + consider adding `(set-option :produce-unsat-core true)`\n\ + at the start of your script + "; } /// Language keywords. diff --git a/src/errors.rs b/src/errors.rs index 318c94c3..806bb746 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,16 +1,18 @@ //! Error types. //! -//! Two specific events are handled are errors so that they propagate upwards -//! naturally, although technically they're not really errors. +//! Some events are handled are errors so that they propagate upwards naturally, although +//! technically they're not really errors: //! -//! - [`ErrorKind::Unsat`][unsat], self-explanatory ; -//! - [`LError::Exit`][exit], used in learners to bail out of the learning -//! phase when the teacher sent an exit order. +//! - [`ErrorKind::Unsat`] +//! - [`ErrorKind::Timeout`] +//! - [`ErrorKind::Unknown`] (when hoice gave up on solving the clauses) //! -//! [unsat]: enum.ErrorKind.html#variant.Unsat -//! (Unsat variant of the ErrorKind enum) -//! [exit]: learners/enum.LError.html#variant.Exit -//! (Exit variant of the LError enum) +//! As such, one should **not** use the usual `chain_err` function but [`chain`] instead. +//! +//! [`ErrorKind::Unsat`]: enum.ErrorKind.html#variant.Unsat (Unsat variant of ErrorKind) +//! [`ErrorKind::Timeout`]: enum.ErrorKind.html#variant.Timeout (Timeout variant of ErrorKind) +//! [`ErrorKind::Unknown`]: enum.ErrorKind.html#variant.Unknown (Unknown variant of ErrorKind) +//! [`chain`]: struct.Error.html#method.chain (chain function over Error) use common::*; @@ -93,57 +95,57 @@ impl_fmt!{ } error_chain!{ - types { - Error, ErrorKind, ResultExt, Res ; - } - - links { - SmtError( - ::rsmt2::errors::Error, ::rsmt2::errors::ErrorKind - ) #[doc = "Error at SMT level."] ; - } - - foreign_links { - Io(::std::io::Error) #[doc = "IO error."] ; - } - - errors { - #[doc = "Parse error."] - ParseError(data: ParseErrorData) { - description("parse error") - display("{}", data) - } - #[doc = "Could not spawn z3."] - Z3SpawnError { - description("could not spawn z3") - display("could not spawn z3") + types { + Error, ErrorKind, ResultExt, Res ; } - #[doc = "Not really an error, unknown early return."] - Unknown { - description("unknown") - display("unknown") - } - #[doc = "Not really an error, unsat early return."] - Unsat { - description("unsat") - display("unsat") - } - #[doc = "Not really an error, unsat early return because of some clause."] - UnsatFrom(clause: ClsIdx) { - description("unsat") - display("unsat by #{}", clause) + + links { + SmtError( + ::rsmt2::errors::Error, ::rsmt2::errors::ErrorKind + ) #[doc = "Error at SMT level."] ; } - #[doc = "Not really an error, exit early return."] - Exit { - description("exit") - display("exit") + + foreign_links { + Io(::std::io::Error) #[doc = "IO error."] ; } - #[doc = "Timeout reached."] - Timeout { - description("timeout") - display("timeout") + + errors { + #[doc = "Parse error."] + ParseError(data: ParseErrorData) { + description("parse error") + display("{}", data) + } + #[doc = "Could not spawn z3."] + Z3SpawnError { + description("could not spawn z3") + display("could not spawn z3") + } + #[doc = "Not really an error, unknown early return."] + Unknown { + description(consts::err::unknown_desc) + display("unknown") + } + #[doc = "Not really an error, unsat early return."] + Unsat { + description(consts::err::unsat_desc) + display("unsat") + } + #[doc = "Not really an error, unsat early return because of some clause."] + UnsatFrom(clause: ClsIdx) { + description(consts::err::unsat_desc) + display("unsat by #{}", clause) + } + #[doc = "Not really an error, exit early return."] + Exit { + description(consts::err::exit_desc) + display("exit") + } + #[doc = "Timeout reached."] + Timeout { + description("timeout") + display("timeout") + } } - } } impl Error { @@ -152,11 +154,12 @@ impl Error { /// [unsat]: enum.ErrorKind.html#variant.Unsat /// (ErrorKind's Unsat variant) pub fn is_unsat(&self) -> bool { - match *self.kind() { - ErrorKind::Unsat => true, - ErrorKind::UnsatFrom(_) => true, - _ => false, + for err in self.iter() { + if err.description() == consts::err::unsat_desc { + return true + } } + false } /// True if the kind of the error is [`ErrorKind::SmtError::Unknown`]. @@ -172,9 +175,15 @@ impl Error { /// [unknown]: enum.ErrorKind.html#variant.Unknown /// (ErrorKind's Unknown variant) pub fn is_unknown(&self) -> bool { - match *self.kind() { - ErrorKind::Unknown => true, - _ => self.is_smt_unknown(), + if self.is_smt_unknown() { + true + } else { + for err in self.iter() { + if err.description() == consts::err::unknown_desc { + return true + } + } + false } } @@ -191,22 +200,27 @@ impl Error { /// [timeout]: enum.ErrorKind.html#variant.Timeout /// (ErrorKind's Timeout variant) pub fn is_timeout(&self) -> bool { - match self.kind() { - ErrorKind::Timeout => true, - ErrorKind::SmtError(smt_err) => smt_err.is_timeout(), - _ => false, + if let ErrorKind::SmtError(smt_err) =self.kind() { + return smt_err.is_timeout() } + for err in self.iter() { + if err.description() == consts::err::timeout_desc { + return true + } + } + false } /// True if the kind of the error is [`ErrorKind::Exit`][exit]. /// - /// [exit]: enum.ErrorKind.html#variant.Exit - /// (ErrorKind's Exit variant) + /// [exit]: enum.ErrorKind.html#variant.Exit (ErrorKind's Exit variant) pub fn is_exit(&self) -> bool { - match *self.kind() { - ErrorKind::Exit => true, - _ => false, + for err in self.iter() { + if err.description() == consts::err::exit_desc { + return true + } } + false } } diff --git a/src/instance/pre_instance.rs b/src/instance/pre_instance.rs index 17a0d435..4b2fde4b 100644 --- a/src/instance/pre_instance.rs +++ b/src/instance/pre_instance.rs @@ -770,7 +770,7 @@ impl<'a> PreInstance<'a> { // Force predicates. for (pred, def) in defs { - log! { @4 " forcing {}", self[pred] } + log! { @4 " forcing (1) {}", self[pred] } let def = TTerms::dnf( def.into_iter() .map(|(quantfed, conj)| (Quant::exists(quantfed), conj)) @@ -820,7 +820,7 @@ impl<'a> PreInstance<'a> { /// /// Does not impact `pred_to_clauses`. fn force_pred(&mut self, pred: PrdIdx, tterms: TTerms) -> Res<()> { - log! { @5 "forcing {}", conf.emph(& self.instance[pred].name) } + log! { @5 "forcing (2) {}", conf.emph(& self.instance[pred].name) } if self.instance[pred].is_defined() { let mut s: Vec = Vec::new(); tterms @@ -2095,7 +2095,7 @@ impl ClauseSimplifier { clause.insert_term(term); } changed = clause.subst(&self.subst) || changed; - log! { @5 | "yielding {}", clause.to_string_info( _preds ).unwrap() } + log! { @5 "yielding {}", clause.to_string_info( _preds ).unwrap() } for (var, _) in self.subst.drain() { clause.deactivate(var)? } diff --git a/src/preproc/mod.rs b/src/preproc/mod.rs index 8b1c7483..f055b15d 100644 --- a/src/preproc/mod.rs +++ b/src/preproc/mod.rs @@ -367,28 +367,28 @@ impl<'a> Reductor<'a> { // // Returns `true` if the pre-processor did something. macro_rules! run { - (@ info $info_opt:expr) => ( - $info_opt.unwrap_or( RedInfo::new() ) - ) ; - (@ bool $info_opt:expr) => ( - $info_opt.map(|info: RedInfo| info.non_zero()).unwrap_or(false) - ) ; - - ($preproc:ident) => ( run!($preproc bool) ) ; - ($preproc:ident $($tail:tt)*) => ( - if let Some(preproc) = self.$preproc.as_mut() { - if let Some(red_info) = utils::run_preproc( - & mut self.instance, _profiler, preproc, & mut count - ) ? { - run! { @ $($tail)* Some(red_info) } - } else { - return Ok(()) - } - } else { - run! { @ $($tail)* None } + (@ info $info_opt:expr) => ( + $info_opt.unwrap_or( RedInfo::new() ) + ) ; + (@ bool $info_opt:expr) => ( + $info_opt.map(|info: RedInfo| info.non_zero()).unwrap_or(false) + ) ; + + ($preproc:ident) => ( run!($preproc bool) ) ; + ($preproc:ident $($tail:tt)*) => ( + if let Some(preproc) = self.$preproc.as_mut() { + if let Some(red_info) = utils::run_preproc( + & mut self.instance, _profiler, preproc, & mut count + ) ? { + run! { @ $($tail)* Some(red_info) } + } else { + return Ok(()) + } + } else { + run! { @ $($tail)* None } + } + ) ; } - ) ; - } utils::register_stats(&self.instance, _profiler, count)?; diff --git a/src/preproc/one_lhs.rs b/src/preproc/one_lhs.rs index f0d0a8f7..91272440 100644 --- a/src/preproc/one_lhs.rs +++ b/src/preproc/one_lhs.rs @@ -68,7 +68,10 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// &String::from_utf8_lossy(&s) /// } /// ``` -pub struct OneLhs; +pub struct OneLhs { + /// True if introducing quantifiers is okay. + quantifiers: bool, +} impl OneLhs { /// Logs an extraction result. @@ -131,7 +134,7 @@ impl OneLhs { /// /// Returns `None` if unfolding failed. fn work_on( - &self, + &mut self, pred: PrdIdx, clause: ClsIdx, instance: &mut PreInstance, @@ -162,7 +165,7 @@ impl OneLhs { match clause.rhs() { Some((p, _)) if p == pred => ExtractRes::SuccessTrue, _ => extraction.terms_of_lhs_app( - false, + self.quantifiers, instance, clause.vars(), (clause.lhs_terms(), clause.lhs_preds()), @@ -215,7 +218,7 @@ impl RedStrat for OneLhs { } fn new(_: &Instance) -> Self { - OneLhs + OneLhs { quantifiers: false } } fn apply(&mut self, instance: &mut PreInstance) -> Res { @@ -240,6 +243,16 @@ impl RedStrat for OneLhs { continue 'all_preds; }; + // Skip if more than one application of the predicate. + if instance[clause] + .lhs_preds() + .get(&pred) + .map(|argss| argss.len() != 1) + .unwrap_or(true) + { + continue 'all_preds + } + log! { @3 "looking at {} ({}, {})", instance[pred], @@ -257,6 +270,8 @@ impl RedStrat for OneLhs { } } + self.quantifiers = !self.quantifiers; + Ok(red_info) } } diff --git a/src/preproc/one_rhs.rs b/src/preproc/one_rhs.rs index 15d42d7b..8d2a0e15 100644 --- a/src/preproc/one_rhs.rs +++ b/src/preproc/one_rhs.rs @@ -56,7 +56,10 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// &String::from_utf8_lossy(&s) /// } /// ``` -pub struct OneRhs; +pub struct OneRhs { + /// True if introducing quantifiers is okay. + quantifiers: bool, +} impl OneRhs { /// Logs an extraction result. @@ -100,7 +103,7 @@ impl OneRhs { /// /// Returns `None` if unfolding failed. fn work_on( - &self, + &mut self, pred: PrdIdx, clause: ClsIdx, instance: &mut PreInstance, @@ -116,7 +119,7 @@ impl OneRhs { match clause.lhs_preds().get(&pred) { Some(apps) if !apps.is_empty() => ExtractRes::SuccessFalse, _ => extraction.terms_of_rhs_app( - true, + self.quantifiers, instance, clause.vars(), clause.lhs_terms(), @@ -169,7 +172,7 @@ impl RedStrat for OneRhs { } fn new(_: &Instance) -> Self { - OneRhs + OneRhs { quantifiers: false } } fn apply(&mut self, instance: &mut PreInstance) -> Res { @@ -211,6 +214,8 @@ impl RedStrat for OneRhs { } } + self.quantifiers = ! self.quantifiers; + Ok(red_info) } } diff --git a/src/preproc/strict_neg_clauses.rs b/src/preproc/strict_neg_clauses.rs index 026d844e..ec328e85 100644 --- a/src/preproc/strict_neg_clauses.rs +++ b/src/preproc/strict_neg_clauses.rs @@ -10,7 +10,7 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// # Examples /// /// ``` -/// # use hoice::{ common::PrdIdx, parse, preproc::{ PreInstance, RedStrat, StrictNeg } }; +/// # use hoice::{ common::*, parse, preproc::{ PreInstance, RedStrat, StrictNeg } }; /// let mut instance = parse::instance(" /// (declare-fun pred ( Int Int Int Int ) Bool) /// (assert @@ -35,14 +35,20 @@ use preproc::{utils::ExtractRes, PreInstance, RedStrat}; /// assert_eq! { "pred", & instance[pred].name } /// /// let strengthening = instance[pred].strength().unwrap(); -/// assert_eq! { -/// "(or \ -/// (>= (* (- 1) v_0) 0) \ -/// (>= (+ v_3 (* (- 1) v_0)) 0) \ -/// (not (= (+ (- 1) (* (- 1) v_2)) 0)) \ -/// (not (= v_1 0))\ -/// )", & format!("{}", strengthening) -/// } +/// let expected = "(or \ +/// (>= (* (- 1) v_0) 0) \ +/// (not (= v_1 0)) \ +/// (>= (+ (* (- 1) v_0) v_3) 0) \ +/// (not (= (+ (- 1) (* (- 1) v_2)) 0))\ +/// )"; +/// let info: VarMap<_> = vec![ +/// ::hoice::info::VarInfo::new("v_0", typ::int(), 0.into()), +/// ::hoice::info::VarInfo::new("v_1", typ::int(), 1.into()), +/// ::hoice::info::VarInfo::new("v_2", typ::int(), 2.into()), +/// ::hoice::info::VarInfo::new("v_3", typ::int(), 3.into()), +/// ].into_iter().collect(); +/// let expected = &::hoice::parse::term(expected, &info, &instance); +/// assert_eq! { expected, strengthening } /// ``` pub struct StrictNeg { /// Stores partial definitions for the predicates. diff --git a/src/preproc/utils.rs b/src/preproc/utils.rs index a0e800b1..fb51dff5 100644 --- a/src/preproc/utils.rs +++ b/src/preproc/utils.rs @@ -771,11 +771,15 @@ pub fn run_preproc( log! { @verb "running {}", conf.emph( preproc.name() ) } - let red_info = preproc.apply(instance)?; + let red_info = preproc.apply(instance).chain_err( + || format!("while running preprocessor {}", conf.bad(preproc.name())) + ); profile! { |_profiler| mark "preproc", preproc.name() } + let red_info = red_info?; + process_red_info(instance, _profiler, preproc.name(), count, &red_info)?; if check_solved(instance, _profiler)? { diff --git a/src/term/factory.rs b/src/term/factory.rs index 605e006d..ef5d8ed6 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -88,6 +88,29 @@ pub fn vars(t: &Term) -> VarSet { /// Each variable is visited only once. /// /// Pretty much equivalent to `term::vars(t).into_iter().map(f)`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let term = term::ge( +/// term::add(vec![ +/// term::int_var(7), +/// term::cmul(7, term::int_var(3)), +/// term::mul( vec![term::int_var(2), term::int_var(7), term::int_var(5)] ) +/// ]), term::int(0) +/// ); +/// let mut expected: VarSet = vec![ +/// 7.into(), 3.into(), 2.into(), 5.into() +/// ].into_iter().collect(); +/// term::map_vars( +/// &term, |var| { +/// let was_there = expected.remove(&var); +/// assert! { was_there } +/// } +/// ); +/// assert! { expected.is_empty() } +/// ``` #[inline] pub fn map_vars(t: &Term, mut f: F) where @@ -441,7 +464,7 @@ pub fn ite(c: Term, t: Term, e: Term) -> Term { /// term::bool_var(0), /// term::or( vec![term::bool_var(7), term::bool_var(3)] ) /// ); -/// assert_eq! { &format!("{}", t), "(or v_3 (not v_0) v_7)" } +/// assert_eq! { &format!("{}", t), "(or v_7 v_3 (not v_0))" } /// ``` #[inline] pub fn implies(lhs: Term, rhs: Term) -> Term { @@ -520,15 +543,29 @@ pub fn not(term: Term) -> Term { /// assert_eq! { &format!("{}", t), "v_0" } /// ``` /// -// / Trivial disjunctions: -// / -// / ```rust -// / # use hoice::common::*; -// / let t = term::or(vec![ -// / term::bool_var(1), term::bool_var(0), term::not(term::bool_var(1)) -// / ]); -// / assert_eq! { &format!("{}", t), "true" } -// / ``` +/// Trivial disjunctions: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::or(vec![ +/// term::bool_var(1), term::bool_var(0), term::not(term::bool_var(1)) +/// ]); +/// assert_eq! { &format!("{}", t), "true" } +/// ``` +/// +/// Arithmetic simplification: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::or(vec![ +/// term::ge(term::int_var(0), term::int(7)), term::le(term::int_var(0), term::int(7)), +/// ]); +/// assert_eq! { &format!("{}", t), "true" } +/// let t = term::or(vec![ +/// term::ge(term::int_var(0), term::int(7)), term::gt(term::int_var(0), term::int(7)), +/// ]); +/// assert_eq! { &format!("{}", t), "(>= v_0 7)" } +/// ``` #[inline] pub fn or(terms: Vec) -> Term { app(Op::Or, terms) @@ -573,6 +610,24 @@ pub fn or(terms: Vec) -> Term { /// ]); /// assert_eq! { &format!("{}", t), "false" } /// ``` +/// +/// Arithmetic simplification: +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::and(vec![ +/// term::ge(term::int_var(0), term::int(7)), term::le(term::int_var(0), term::int(7)), +/// ]); +/// assert_eq! { &format!("{}", t), "(= (+ v_0 (- 7)) 0)" } +/// let t = term::and(vec![ +/// term::ge(term::int_var(0), term::int(7)), term::gt(term::int_var(0), term::int(7)), +/// ]); +/// assert_eq! { &format!("{}", t), "(>= v_0 8)" } +/// let t = term::and(vec![ +/// term::ge(term::int_var(0), term::int(7)), term::lt(term::int_var(0), term::int(7)), +/// ]); +/// assert_eq! { &format!("{}", t), "false" } +/// ``` #[inline] pub fn and(terms: Vec) -> Term { app(Op::And, terms) diff --git a/src/term/simplify.rs b/src/term/simplify.rs index b90c04cc..7f7518fb 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -12,6 +12,9 @@ use term::factory::NormRes; use common::*; lazy_static! { + /// Solver used to check term simplification. + /// + /// Needs to be activated via a command-line argument. None by default. static ref simpl_solver: RwLock>> = RwLock::new(if conf.check_simpl { Some( @@ -44,6 +47,16 @@ impl SimplRes { Yields(term) => Yields(term), } } + + /// Inverts the result if the flag is true. + pub fn invert_if(self, do_it: bool) -> Self { + if do_it { + self.invert() + } else { + self + } + } + /// True if `self` is not `None`. #[inline] pub fn is_some(&self) -> bool { @@ -54,6 +67,74 @@ impl SimplRes { pub fn is_none(&self) -> bool { !self.is_some() } + + /// Constructor for `Less`. + /// + /// # Examples + /// + /// ```rust + /// use std::cmp::Ordering::*; + /// # use hoice::term::simplify::SimplRes; + /// assert_eq! { SimplRes::lt(), SimplRes::Cmp(Less) } + /// ``` + #[inline] + pub fn lt() -> Self { + SimplRes::Cmp(Ordering::Less) + } + + /// Constructor for `Equal`. + /// + /// # Examples + /// + /// ```rust + /// use std::cmp::Ordering::*; + /// # use hoice::term::simplify::SimplRes; + /// assert_eq! { SimplRes::eq(), SimplRes::Cmp(Equal) } + /// ``` + #[inline] + pub fn eq() -> Self { + SimplRes::Cmp(Ordering::Equal) + } + + /// Constructor for `Greater`. + /// + /// # Examples + /// + /// ```rust + /// use std::cmp::Ordering::*; + /// # use hoice::term::simplify::SimplRes; + /// assert_eq! { SimplRes::gt(), SimplRes::Cmp(Greater) } + /// ``` + #[inline] + pub fn gt() -> Self { + SimplRes::Cmp(Ordering::Greater) + } + + /// Constructor for `false`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::{ term, term::simplify::SimplRes }; + /// assert_eq! { SimplRes::fls(), SimplRes::Yields(term::fls()) } + /// ``` + #[inline] + pub fn fls() -> Self { + SimplRes::Yields(term::fls()) + } + + /// Constructor for `true`. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::{ term, term::simplify::SimplRes }; + /// assert_eq! { SimplRes::tru(), SimplRes::Yields(term::tru()) } + /// ``` + #[inline] + pub fn tru() -> Self { + SimplRes::Yields(term::tru()) + } } impl_fmt! { @@ -66,9 +147,89 @@ impl_fmt! { } } + + +/// Checks the result of a binary simplification. +fn check_bin_simpl(lhs: &T1, rhs: &T2, res: &SimplRes, conj: bool) +where + T1: Deref, + T2: Deref, +{ + if res.is_some() { + if let Some(solver) = simpl_solver + .write() + .expect("could not retrieve term simplification solver") + .as_mut() + { + solver + .push(1) + .expect("error on `push` during term simplification"); + let mut vars = VarSet::new(); + lhs.iter(|term| { + if let Some(idx) = term.var_idx() { + let is_new = vars.insert(idx); + if is_new { + solver + .declare_const(&format!("{}", term), term.typ().get()) + .expect("error on `declare_const` during term simplification (lhs)") + } + } + }); + rhs.iter(|term| { + if let Some(idx) = term.var_idx() { + let is_new = vars.insert(idx); + if is_new { + solver + .declare_const(&format!("{}", term), term.typ().get()) + .expect("error on `declare_const` during term simplification (rhs)") + } + } + }); + + use std::cmp::Ordering::*; + let op = if conj { "and" } else { "or" }; + let check = match res { + SimplRes::Cmp(Equal) => format!("(= {} {})", lhs.deref(), rhs.deref()), + SimplRes::Cmp(Less) => format!( + "(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), rhs.deref() + ), + SimplRes::Cmp(Greater) => format!( + "(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), lhs.deref() + ), + SimplRes::Yields(ref term) => { + format!("(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), term) + } + SimplRes::None => unreachable!(), + }; + + solver + .assert(&format!("(not {})", check)) + .expect("error on `assert` during term simplification"); + + if solver + .check_sat() + .expect("could not retrieve check-sat result in conjunction simplification") + { + log! { @0 + " " ; + "{}", lhs.deref() ; + "{}", rhs.deref() ; + "{}", res ; + " " + } + print_err(& "simplification failure".into()) + } + + solver + .pop(1) + .expect("error on `pop` during term simplification") + } + } +} + /// Adds a term to a set understood as a conjunction. /// -/// Returns `true` if the resulting set is false. +/// Returns `true` if the resulting set is false (think `is_unsat`). pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { let mut next_term = Some(term); let mut fls = false; @@ -122,7 +283,7 @@ pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { } use std::cmp::Ordering::*; - match conj_simpl(&term, set_term) { + match bin_simpl(&term, set_term, true) { SimplRes::None => true, SimplRes::Cmp(Less) | SimplRes::Cmp(Equal) => { @@ -162,42 +323,84 @@ pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { fls } -/// Simplifies a conjunction. -fn conj_vec_simpl(terms: &mut Vec) { - let mut res = Vec::with_capacity(terms.len()); - 'add_terms: while let Some(term) = terms.pop() { - let mut cnt = 0; - while cnt < res.len() { - use self::SimplRes::*; - use std::cmp::Ordering::*; +/// Simplifies two boolean terms. +/// +/// Treats the pair of terms as a conjunction if `conj` is true, as a disjunction otherwise. +fn bin_simpl_2(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes +where T1: Deref, T2: Deref { + // use std::cmp::Ordering::*; - match conj_simpl(&term, &res[cnt]) { - None => cnt += 1, + if lhs.deref() == rhs.deref() { + return SimplRes::eq(); + } - Cmp(Less) | Cmp(Equal) => continue 'add_terms, + match (lhs.bool(), rhs.bool()) { + (Some(true), _) => if conj { + return SimplRes::lt() + } else { + return SimplRes::tru() + }, + (_, Some(true)) => if conj { + return SimplRes::gt() + } else { + return SimplRes::tru() + }, - Cmp(Greater) => { - res.swap_remove(cnt); - } + (Some(false), _) => if conj { + return SimplRes::fls() + } else { + return SimplRes::lt() + }, + (_, Some(false)) => if conj { + return SimplRes::fls() + } else { + return SimplRes::gt() + }, - Yields(term) => { - res.swap_remove(cnt); - terms.push(term); - continue 'add_terms; - } - } - } + (None, None) => (), + } - res.push(term) + let negated_lhs = term::not( lhs.deref().to_hcons() ); + if negated_lhs.get() == rhs.deref() { + if conj { + return SimplRes::fls() + } else { + return SimplRes::tru() + } } - res.shrink_to_fit(); - res.sort_unstable(); - res.dedup(); + int_simpl(lhs, rhs, conj) +} - ::std::mem::swap(terms, &mut res) + +/// Simplifies two boolean terms. +/// +/// Treats the pair of terms as a conjunction if `conj` is true, as a disjunction otherwise. +pub fn bin_simpl(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes +where T1: Deref, T2: Deref { + if conf.term_simpl == 0 { + return SimplRes::None; + } + + let res = if conj { + if let Some(args) = lhs.disj_inspect() { + conj_of_disj_and_term(args, rhs) + } else if let Some(args) = rhs.disj_inspect() { + conj_of_disj_and_term(args, lhs).invert() + } else { + bin_simpl_2(lhs, rhs, conj) + } + } else if let Some(args) = lhs.conj_inspect() { + disj_of_conj_and_term(args, rhs) + } else if let Some(args) = rhs.conj_inspect() { + disj_of_conj_and_term(args, lhs).invert() + } else { + bin_simpl_2(lhs, rhs, conj) + }; + check_bin_simpl(lhs, rhs, &res, conj); + res } /// Checks the conjunction of two terms for simplification. @@ -245,11 +448,11 @@ fn conj_vec_simpl(terms: &mut Vec) { /// # println!("=> {}\n\n", rhs) ; /// assert_eq! { conj_simpl(& lhs, & rhs), Cmp(Greater) } /// -// / let lhs = term::le( term::int_var(0), term::int(7) ) ; -// / # println!(" {}", lhs) ; -// / # println!("=> {}\n\n", rhs) ; -// / let expected = term::eq( term::int_var(0), term::int(7) ) ; -// / debug_assert_eq! { conj_simpl(& lhs, & rhs), Yields(expected) } +/// let lhs = term::le( term::int_var(0), term::int(7) ) ; +/// # println!(" {}", lhs) ; +/// # println!("=> {}\n\n", rhs) ; +/// let expected = term::eq( term::int_var(0), term::int(7) ) ; +/// debug_assert_eq! { conj_simpl(& lhs, & rhs), Yields(expected) } /// /// let lhs = term::le( term::int_var(1), term::int(7) ) ; /// # println!(" {}", lhs) ; @@ -317,141 +520,112 @@ fn conj_vec_simpl(terms: &mut Vec) { /// assert_eq! { conj_simpl(& lhs, & rhs), None } /// ``` pub fn conj_simpl(lhs: &T1, rhs: &T2) -> SimplRes -where - T1: Deref, - T2: Deref, -{ - if conf.term_simpl == 0 { - return SimplRes::None; - } - let res = conj_simpl_2(lhs, rhs); - - if res.is_some() { - if let Some(solver) = simpl_solver - .write() - .expect("could not retrieve term simplification solver") - .as_mut() - { - solver - .push(1) - .expect("error on `push` during term simplification"); - let mut vars = VarSet::new(); - lhs.iter(|term| { - if let Some(idx) = term.var_idx() { - let is_new = vars.insert(idx); - if is_new { - solver - .declare_const(&format!("{}", term), term.typ().get()) - .expect("error on `declare_const` during term simplification (lhs)") - } - } - }); - rhs.iter(|term| { - if let Some(idx) = term.var_idx() { - let is_new = vars.insert(idx); - if is_new { - solver - .declare_const(&format!("{}", term), term.typ().get()) - .expect("error on `declare_const` during term simplification (rhs)") - } - } - }); - - use std::cmp::Ordering::*; - let check = match res { - SimplRes::Cmp(Equal) => format!("(= {} {})", lhs.deref(), rhs.deref()), - SimplRes::Cmp(Less) => format!("(=> {} {})", rhs.deref(), lhs.deref()), - SimplRes::Cmp(Greater) => format!("(=> {} {})", lhs.deref(), rhs.deref()), - SimplRes::Yields(ref term) => { - format!("(= (and {} {}) {})", lhs.deref(), rhs.deref(), term) - } - SimplRes::None => unreachable!(), - }; +where T1: Deref, T2: Deref { + bin_simpl(lhs, rhs, true) +} - solver - .assert(&format!("(not {})", check)) - .expect("error on `assert` during term simplification"); - if solver - .check_sat() - .expect("could not retrieve check-sat result in conjunction simplification") - { - log! { @0 - " " ; - "{}", lhs.deref() ; - "{}", rhs.deref() ; - "{:?}", res ; - " " - } - panic! { "simplification failure" } - } +/// Compares the disjunction of a conjunction and a term. +fn disj_of_conj_and_term(conj: & [Term], rhs: &T) -> SimplRes +where T: Deref { + use std::cmp::Ordering::*; - solver - .pop(1) - .expect("error on `pop` during term simplification") + let mut lesser_count = 0; + let mut yields = vec![]; + for lhs in conj { + match bin_simpl(lhs, rhs, false) { + SimplRes::Cmp(Equal) | SimplRes::Cmp(Greater) => return SimplRes::gt(), + SimplRes::Cmp(Less) => lesser_count += 1, + SimplRes::Yields(term) => yields.push(term), + SimplRes::None => (), } } - - res + if yields.len() == conj.len() { + SimplRes::Yields(term::and(yields)) + } else if lesser_count == conj.len() { + SimplRes::lt() + } else { + SimplRes::None + } } -fn conj_simpl_2(lhs: &T1, rhs: &T2) -> SimplRes -where - T1: Deref, - T2: Deref, -{ - use std::cmp::Ordering::*; - // A term is equal to itself. - if lhs.deref() == rhs.deref() { - return SimplRes::Cmp(Equal); - } +/// Compares the conjunction of a disjunction and a term. +fn conj_of_disj_and_term(disj: & [Term], rhs: &T) -> SimplRes +where T: Deref { + use std::cmp::Ordering::*; - // A term and its negation yield false. - if let Some(sub_lhs) = lhs.neg_inspect() { - if sub_lhs.get() == rhs.deref() { return SimplRes::Yields(term::fls()) } + let mut greater_count = 0; + let mut yields = vec![]; + for lhs in disj { + match bin_simpl(lhs, rhs, true) { + SimplRes::Cmp(Equal) | SimplRes::Cmp(Less) => return SimplRes::lt(), + SimplRes::Cmp(Greater) => greater_count += 1, + SimplRes::Yields(term) => yields.push(term), + SimplRes::None => (), + } } - if let Some(sub_rhs) = rhs.neg_inspect() { - if sub_rhs.get() == lhs.deref() { return SimplRes::Yields(term::fls()) } + if yields.len() == disj.len() { + SimplRes::Yields(term::or(yields)) + } else if greater_count == disj.len() { + SimplRes::gt() + } else { + SimplRes::None } +} - if let Some(args) = lhs.disj_inspect() { - let mut greater_count = 0; - let mut yields = vec![]; - for lhs in args { - match int_conj_simpl(lhs, rhs) { - SimplRes::Cmp(Equal) | SimplRes::Cmp(Less) => return SimplRes::Cmp(Less), - SimplRes::Cmp(Greater) => greater_count += 1, +/// Simplifies a disjunction of terms. +/// +/// Treats `terms` as a conjunction if `conj` is true, as a disjunction otherwise. +/// +/// Assumes none of the terms are conjunctions (disjunctions) if `conj` is true (false). +fn vec_simpl(terms: &mut Vec, conj: bool) { - SimplRes::Yields(term) => yields.push(term), - SimplRes::None => (), - } - } - if yields.len() == args.len() { - return SimplRes::Yields(term::or(yields)); - } else if greater_count == args.len() { - return SimplRes::Cmp(Greater); - } - } else if let Some(args) = rhs.disj_inspect() { - let mut less_count = 0; - let mut yields = vec![]; - for rhs in args { - match int_conj_simpl(lhs, rhs) { - SimplRes::Cmp(Equal) | SimplRes::Cmp(Greater) => return SimplRes::Cmp(Greater), - SimplRes::Cmp(Less) => less_count += 1, - - SimplRes::Yields(term) => yields.push(term), - SimplRes::None => (), + let mut res = Vec::with_capacity(terms.len()); + + 'add_terms: while let Some(term) = terms.pop() { + let mut cnt = 0; + + while cnt < res.len() { + use std::cmp::Ordering::*; + + match bin_simpl(&term, &res[cnt], conj) { + SimplRes::None => cnt += 1, + + SimplRes::Cmp(Less) | SimplRes::Cmp(Equal) => continue 'add_terms, + + SimplRes::Cmp(Greater) => { + res.swap_remove(cnt); + } + + SimplRes::Yields(term) => { + res.swap_remove(cnt); + + match (term.bool(), conj) { + (Some(false), false) | + (Some(true), true) => (), + + (Some(true), false) | + (Some(false), true) => { + terms.clear(); + terms.push(term); + return () + }, + + (None, _) => terms.push(term), + } + + continue 'add_terms; + } } } - if yields.len() == args.len() { - return SimplRes::Yields(term::or(yields)); - } else if less_count == args.len() { - return SimplRes::Cmp(Greater); - } + + res.push(term) } - int_conj_simpl(lhs, rhs) + res.shrink_to_fit(); + + ::std::mem::swap(terms, &mut res) } /// Result of deconstructing a sum. @@ -459,6 +633,7 @@ where /// This is used in `int_deconstruct` below to deconstruct additions to compare relation over /// arithmetic terms as a sum of non-constant terms (lhs), an operator and a constant (rhs). The /// goal is to do without cloning anything. +#[derive(Debug, Clone)] struct Deconstructed<'a> { /// Terms of the sum. trms: Vec<&'a Term>, @@ -517,6 +692,20 @@ impl<'a> Deconstructed<'a> { } } +impl<'a> ::std::fmt::Display for Deconstructed<'a> { + fn fmt(&self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { + if self.trms.len() == 1 { + self.trms[0].fmt(fmt) + } else { + write!(fmt, "(+")?; + for trm in &self.trms { + write!(fmt, " {}", trm)?; + } + write!(fmt, ")") + } + } +} + /// Deconstructs a relation between arithmetic subterms. fn int_deconstruct(term: &T) -> Option<(Op, Deconstructed, Val)> where @@ -566,42 +755,12 @@ where } } -fn int_conj_simpl(lhs_term: &T1, rhs_term: &T2) -> SimplRes +fn int_simpl(lhs_term: &T1, rhs_term: &T2, conj: bool) -> SimplRes where T1: Deref, T2: Deref, { use std::cmp::Ordering::*; - // Input boolean is true (false) for `lhs` => `rhs` (reversed). - macro_rules! ord_of_bool { - ($b:expr) => { - if $b { - SimplRes::Cmp(Greater) - } else { - SimplRes::Cmp(Less) - } - }; - } - - let (lhs, rhs) = (lhs_term.deref(), rhs_term.deref()); - - // A term is equal to itself. - if lhs == rhs { - return SimplRes::Cmp(Equal); - } - - match (lhs.bool(), rhs.bool()) { - // True can only imply true. - (Some(true), rhs) => return ord_of_bool!(rhs.unwrap_or(false)), - // False implies anything. - (Some(false), _) => return ord_of_bool!(true), - // False can only be implied by false. - (lhs, Some(false)) => return ord_of_bool!(!lhs.unwrap_or(true)), - // True is implied by anything. - (_, Some(true)) => return ord_of_bool!(true), - // Otherwise we don't know (yet). - (None, None) => (), - } let (lhs_op, lhs_trm, lhs_cst) = if let Some(res) = int_deconstruct(lhs_term) { res @@ -609,12 +768,25 @@ where return SimplRes::None; }; - let (rhs_op, rhs_trm, rhs_cst) = if let Some(res) = int_deconstruct(rhs_term) { + let (mut rhs_op, mut rhs_trm, mut rhs_cst) = if let Some(res) = int_deconstruct(rhs_term) { res } else { return SimplRes::None; }; + if rhs_trm.is_opposite(&lhs_trm) { + rhs_trm = lhs_trm.clone(); + rhs_cst = rhs_cst.minus().expect("illegal term found during simplification"); + rhs_op = match rhs_op { + Op::Eql => Op::Eql, + Op::Gt => Op::Lt, + Op::Ge => Op::Le, + Op::Lt => Op::Gt, + Op::Le => Op::Ge, + _ => panic!("unexpected operator {} during integer relation simplification", rhs_op) + } + } + // println!(); // println!("lhs:"); // println!(" {} {} {}", lhs_trm.to_string(), lhs_op, lhs_cst); @@ -622,64 +794,176 @@ where // println!(" {} {} {}", rhs_trm.to_string(), rhs_op, rhs_cst); if lhs_trm.equal(&rhs_trm) { + match (lhs_op, rhs_op) { + (Op::Gt, Op::Gt) | (Op::Ge, Op::Ge) => { return SimplRes::Cmp( lhs_cst.get().compare(&rhs_cst).expect( "error during comparison unwrapping in term simplification (same op)", ), - ) + ).invert_if(!conj) + } + + (Op::Le, Op::Le) | (Op::Lt, Op::Lt) => { + return SimplRes::Cmp( + lhs_cst.get().compare(&rhs_cst).expect( + "error during comparison unwrapping in term simplification (same op)", + ), + ).invert_if(conj) } + // Almost the same operator. (Op::Gt, Op::Ge) | (Op::Ge, Op::Gt) => { if lhs_cst == rhs_cst { + // Same constant. if lhs_op == Op::Gt { - return SimplRes::Cmp(Greater); + return SimplRes::gt().invert_if(!conj); } else { - return SimplRes::Cmp(Less); + return SimplRes::lt().invert_if(!conj); } } else { return SimplRes::Cmp(lhs_cst.get().compare(&rhs_cst).expect( "error during comparison unwrapping in term simplification (diff op)", - )); + )).invert_if(!conj); + } + } + + // Almost the same operator. + (Op::Lt, Op::Le) | (Op::Le, Op::Lt) => { + if lhs_cst == rhs_cst { + // Same constant. + if lhs_op == Op::Gt { + return SimplRes::gt().invert_if(conj); + } else { + return SimplRes::lt().invert_if(conj); + } + } else { + return SimplRes::Cmp(lhs_cst.get().compare(&rhs_cst).expect( + "error during comparison unwrapping in term simplification (diff op)", + )).invert_if(conj); } } (Op::Eql, Op::Ge) | (Op::Eql, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { - Some(Less) => return SimplRes::Yields(term::fls()), - Some(Equal) if rhs_op == Op::Gt => return SimplRes::Yields(term::fls()), - Some(Equal) | Some(Greater) => return SimplRes::Cmp(Greater), + Some(Less) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, + Some(Equal) if rhs_op == Op::Gt => if conj { + return SimplRes::fls() + } else { + return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + }, + Some(Equal) | Some(Greater) => return SimplRes::gt().invert_if(!conj), None => unreachable!(), - }, + } + + (Op::Eql, Op::Le) | (Op::Eql, Op::Lt) => match lhs_cst.get().compare(&rhs_cst) { + Some(Greater) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, + Some(Equal) if rhs_op == Op::Lt => if conj { + return SimplRes::fls() + } else { + return SimplRes::Yields( term::le( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + }, + Some(Equal) | Some(Less) => return SimplRes::gt().invert_if(!conj), + None => unreachable!(), + } (Op::Ge, Op::Eql) | (Op::Gt, Op::Eql) => match rhs_cst.get().compare(&lhs_cst) { - Some(Less) => return SimplRes::Yields(term::fls()), - Some(Equal) if lhs_op == Op::Gt => return SimplRes::Yields(term::fls()), - Some(Equal) | Some(Greater) => return SimplRes::Cmp(Less), + Some(Less) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, + Some(Equal) if lhs_op == Op::Gt => if conj { + return SimplRes::fls() + } else { + return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + }, + Some(Equal) | Some(Greater) => return SimplRes::lt().invert_if(!conj), + None => unreachable!(), + }, + + (Op::Le, Op::Eql) | (Op::Lt, Op::Eql) => match rhs_cst.get().compare(&lhs_cst) { + Some(Greater) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, + Some(Equal) if lhs_op == Op::Gt => if conj { + return SimplRes::fls() + } else { + return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + }, + Some(Equal) | Some(Less) => return SimplRes::lt().invert_if(!conj), + None => unreachable!(), + }, + + (Op::Ge, Op::Le) | (Op::Gt, Op::Le) | + (Op::Ge, Op::Lt) | (Op::Gt, Op::Lt) => match lhs_cst.get().compare(&rhs_cst) { + Some(Less) => if conj { + return SimplRes::None + } else { + return SimplRes::tru() + }, + Some(Equal) if conj && (lhs_op == Op::Gt || rhs_op == Op::Lt) => { + return SimplRes::fls() + }, + Some(Equal) => if conj { + return SimplRes::Yields( term::eq(lhs_trm.into_term(), term::cst(lhs_cst)) ) + } else { + return SimplRes::tru() + }, + Some(Greater) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, + None => unreachable!(), + }, + + (Op::Le, Op::Ge) | (Op::Le, Op::Gt) | + (Op::Lt, Op::Ge) | (Op::Lt, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { + Some(Greater) => if conj { + return SimplRes::None + } else { + return SimplRes::tru() + }, + Some(Equal) if conj && (lhs_op == Op::Lt || rhs_op == Op::Gt) => { + return SimplRes::fls() + }, + Some(Equal) => if conj { + return SimplRes::Yields( term::eq(lhs_trm.into_term(), term::cst(lhs_cst)) ) + } else if lhs_op == Op::Lt && rhs_op == Op::Gt { + return SimplRes::None + } else { + return SimplRes::tru() + }, + Some(Less) => if conj { + return SimplRes::fls() + } else { + return SimplRes::None + }, None => unreachable!(), }, (Op::Eql, Op::Eql) => if rhs_cst.equal(&lhs_cst) { - return SimplRes::Cmp(Greater); + return SimplRes::eq(); + } else if conj { + return SimplRes::fls(); } else { - return SimplRes::Yields(term::fls()); + return SimplRes::None; }, _ => (), } - } else if lhs_op == Op::Ge - && rhs_op == Op::Ge - && lhs_cst == rhs_cst - .minus() - .expect("error during rhs inversion in term simplification") - && lhs_trm.is_opposite(&rhs_trm) - { - return SimplRes::Yields(term::eq( - lhs_trm.into_term(), - lhs_cst - .to_term() - .expect("error during lhs cst to term conversion in term simplification"), - )); + } SimplRes::None @@ -940,9 +1224,6 @@ simpl_fun! { simpl_fun! { fn and(args) { - args.sort_unstable() ; - args.dedup() ; - let mut cnt = 0 ; while cnt < args.len() { @@ -967,9 +1248,8 @@ simpl_fun! { } } - // if conf.term_simpl >= 3 { - conj_vec_simpl(args) ; - // } + vec_simpl(args, true) ; + args.sort_unstable(); if args.is_empty() { Some( @@ -986,9 +1266,6 @@ simpl_fun! { // Disjunction. fn or(args) { - args.sort_unstable() ; - args.dedup() ; - let mut cnt = 0 ; while cnt < args.len() { @@ -1012,6 +1289,9 @@ simpl_fun! { } } + vec_simpl(args, false) ; + args.sort_unstable(); + if args.is_empty() { Some( NormRes::Term( term::fls() ) From cece0fe5ff6da67eb5ea2d7898c4a0e5af2281a9 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 26 Sep 2018 14:26:00 +0900 Subject: [PATCH 72/94] more documentation for factory functions --- src/term/factory.rs | 220 +++++++++++++++++++++++++++++++++++++++++--- src/term/mod.rs | 13 +-- 2 files changed, 214 insertions(+), 19 deletions(-) diff --git a/src/term/factory.rs b/src/term/factory.rs index ef5d8ed6..b8704f65 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -635,7 +635,19 @@ pub fn and(terms: Vec) -> Term { /// Constant array. /// +/// A constant array is not necessarily a value. It is an array which maps all indices to the same +/// (non-necessarily constant) term. +/// /// The type is the type of **the indices** of the array. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let arr = term::cst_array(typ::int(), term::int_var(3)); +/// assert! { arr.val().is_none() } +/// assert_eq! { &format!("{}", arr), "((as const (Array Int Int)) v_3)" } +/// ``` #[inline] pub fn cst_array(typ: Typ, default: Term) -> Term { if let Some(val) = default.val() { @@ -645,12 +657,41 @@ pub fn cst_array(typ: Typ, default: Term) -> Term { } } -/// Store operation in an array. +/// Store operation in arrays. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let arr = term::cst_array(typ::int(), term::int_var(3)); +/// let arr = term::store(arr, term::int(7), term::int(0)); +/// assert_eq! { &format!("{}", arr), "(store ((as const (Array Int Int)) v_3) 7 0)" } +/// ``` #[inline] pub fn store(array: Term, idx: Term, val: Term) -> Term { app(Op::Store, vec![array, idx, val]) } -/// Select operation for an array. + +/// Select operation for arrays. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let arr = term::cst_array(typ::int(), term::int_var(2)); +/// let arr = term::store(arr, term::int(7), term::int(0)); +/// let select_2 = term::select(arr.clone(), term::int(2)); +/// assert_eq! { +/// &format!("{}", select_2), "(select (store ((as const (Array Int Int)) v_2) 7 0) 2)" +/// } +/// let select_7 = term::select(arr.clone(), term::int(7)); +/// assert_eq! { +/// &format!("{}", select_7), "(select (store ((as const (Array Int Int)) v_2) 7 0) 7)" +/// } +/// let model: VarMap<_> = vec![ val::int(17), val::int(17), val::int(13) ].into(); +/// assert_eq! { select_2.eval(&model).unwrap(), val::int(13) } +/// assert_eq! { select_7.eval(&model).unwrap(), val::int(0) } +/// ``` #[inline] pub fn select(array: Term, idx: Term) -> Term { app(Op::Select, vec![array, idx]) @@ -662,24 +703,96 @@ pub fn select(array: Term, idx: Term) -> Term { /// /// - if the function does not exist /// - if the arguments are illegal +/// +/// # Examples +/// +/// ```rust +/// use hoice::{ term, term::typ, dtyp, fun }; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let _ = term::fun( +/// fun::test::length_fun_name(), +/// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ], +/// ); +/// ``` +/// +/// Constant arguments: +/// +/// ```rust +/// use hoice::{ term, term::typ, dtyp, fun, val }; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let cst = term::fun( +/// fun::test::length_fun_name(), +/// vec![term::dtyp_new( +/// list.clone(), "insert", vec![term::int(7), term::dtyp_new(list.clone(), "nil", vec![])] +/// )], +/// ); +/// assert_eq! { cst.val().unwrap(), val::int(1) } +/// ``` +/// +/// Ill-typed application (panic): +/// +/// ```rust,should_panic +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// +/// let _ = term::fun( // This panics. +/// fun::test::length_fun_name(), vec![ term::int(7) ], +/// ); +/// ``` +/// +/// Function does not exist (panic): +/// +/// ```rust,should_panic +/// # use hoice::common::*; +/// let _ = term::fun( // This panics. +/// "unknown_function", vec![ term::int_var(0) ] +/// ); +/// ``` #[inline] pub fn fun(name: S, mut args: Vec) -> Term where S: Into, { let name = name.into(); + let mut all_args_constant = true; match fun::dec_do(&name, |fun| { if args.len() != fun.sig.len() { - panic!("illegal application of function {}", conf.bad(&name)) + panic!( + "illegal application of function {} to {} arguments, expected {}", + conf.bad(&name), args.len(), fun.sig.len() + ) } for (info, arg) in fun.sig.iter().zip(args.iter_mut()) { + if arg.val().is_none() { + all_args_constant = false + } if let Some(nu_arg) = arg.force_dtyp(info.typ.clone()) { *arg = nu_arg + } else if info.typ != arg.typ() { + panic!( + "ill-typed application of function {}, {} does not have type {}", + conf.bad(&name), arg, info.typ + ) } } Ok(fun.typ.clone()) }) { - Ok(typ) => factory.mk(RTerm::new_fun(typ, name, args)), + Ok(typ) => { + let term = factory.mk(RTerm::new_fun(typ, name, args)); + if all_args_constant { + if let Ok(val) = term.eval(&()) { + cst(val) + } else { + term + } + } else { + term + } + }, Err(e) => { print_err(&e); panic!("illegal function application") @@ -689,7 +802,7 @@ where /// Creates an operator application. /// -/// Assumes the application is well-typed, modulo int to real casting. +/// This is the function all operator application functions end up calling. #[inline] pub fn app(op: Op, mut args: Vec) -> Term { let typ = expect!( @@ -732,6 +845,36 @@ pub fn val(val: Val) -> Term { } /// Creates a datatype constructor. +/// +/// # Examples +/// +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::dtyp_new( +/// list.clone(), +/// "insert", +/// vec![ term::int(7), term::var(0, list) ], +/// ); +/// assert_eq! { &format!("{}", t), "(insert 7 v_0)" } +/// ``` +/// +/// Constant term: +/// +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::dtyp_new( +/// list.clone(), +/// "insert", +/// vec![ term::int(7), term::dtyp_new(list, "nil", vec![]) ], +/// ); // This term is actually a constant. +/// assert_eq! { &format!("{}", t.val().unwrap()), "(insert 7 (as nil (List Int)))" } +/// ``` pub fn dtyp_new(typ: Typ, name: S, args: Vec) -> Term where S: Into, @@ -742,9 +885,37 @@ where /// Creates a new datatype selector. /// -/// # TODO +/// # Examples +/// +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::dtyp_new( +/// list.clone(), +/// "insert", +/// vec![ term::int(7), term::var(0, list) ], +/// ); +/// assert_eq! { &format!("{}", t), "(insert 7 v_0)" } +/// ``` +/// +/// Constant term: /// -/// - treat constants better +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::dtyp_new( +/// list.clone(), +/// "insert", +/// vec![ term::int(7), term::dtyp_new(list, "nil", vec![]) ], +/// ); // This term is actually a constant. +/// assert_eq! { &format!("{}", t.val().unwrap()), "(insert 7 (as nil (List Int)))" } +/// let t = term::dtyp_slc( typ::int(), "head", t ); +/// assert_eq! { t.val().unwrap(), val::int(7) } +/// ``` pub fn dtyp_slc(typ: Typ, name: S, term: Term) -> Term where S: Into, @@ -757,9 +928,37 @@ where /// Creates a new datatype tester. /// -/// # TODO +/// # Examples +/// +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::var(0, list); +/// let t = term::dtyp_tst("insert", t); +/// assert_eq! { &format!("{}", t), "(is-insert v_0)" } +/// ``` /// -/// - treat constants better +/// Evaluation on non-constant term: +/// +/// ```rust +/// use hoice::common::*; +/// fun::test::create_length_fun(); +/// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// +/// let t = term::dtyp_new( +/// list.clone(), +/// "insert", +/// vec![ term::int(7), term::var(0, list) ], +/// ); +/// let is_nil = term::dtyp_tst("nil", t.clone()); +/// # println!("is_nil: {}", is_nil); +/// assert_eq! { is_nil.val().unwrap(), val::bool(false) } +/// let is_insert = term::dtyp_tst("insert", t); +/// # println!("is_insert: {}", is_insert); +/// assert_eq! { is_insert.val().unwrap(), val::bool(true) } +/// ``` pub fn dtyp_tst(name: S, term: Term) -> Term where S: Into, @@ -775,8 +974,7 @@ where /// Creates an operator application. /// -/// Error if the application is ill-typed (int will be cast to real -/// automatically). +/// Error if the application is ill-typed (int will be cast to real automatically). #[inline] pub fn try_app(op: Op, mut args: Vec) -> Result { let typ = op.type_check(&mut args)?; diff --git a/src/term/mod.rs b/src/term/mod.rs index 0d9f67ef..d805b2c1 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -648,7 +648,7 @@ impl RTerm { /// let t_1 = term::ge( /// term::fun( /// fun::test::length_fun_name(), - /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// vec![ term::var(0, list.clone()) ] /// ), /// term::int(0) /// ); @@ -714,8 +714,7 @@ impl RTerm { /// /// let t_1 = term::ge( /// term::fun( - /// fun::test::length_fun_name(), - /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// fun::test::length_fun_name(), vec![ term::var(0, list.clone()) ] /// ), /// term::int(0) /// ); @@ -723,8 +722,7 @@ impl RTerm { /// /// let t_2 = term::ge( /// term::fun( - /// "get_head", - /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ] + /// "get_head", vec![ term::var(0, list.clone()) ] /// ), /// term::int(0) /// ); @@ -1140,12 +1138,11 @@ impl RTerm { /// fun::test::create_length_fun(); /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); /// - /// let nil = term::dtyp_new(list.clone(), "nil", vec![]); - /// let t = term::fun( fun::test::length_fun_name(), vec![ nil.clone() ] ); + /// let t = term::fun( fun::test::length_fun_name(), vec![ term::var(0, list.clone()) ] ); /// /// let (name, args) = t.fun_inspect().unwrap(); /// assert_eq! { name, fun::test::length_fun_name() } - /// assert_eq! { args, & vec![ nil ] } + /// assert_eq! { args, & vec![ term::var(0, list) ] } /// ``` pub fn fun_inspect(&self) -> Option<(&String, &Vec)> { if let RTerm::Fun { From e1e995032976ef98d7df6d8f5d97d0d4a541b976 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 26 Sep 2018 14:26:25 +0900 Subject: [PATCH 73/94] rmed travis cargo cache --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 413e6638..b6b2b002 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,6 @@ env: global: - RUSTFLAGS="-C link-dead-code" -cache: cargo - addons: apt: packages: From bd8f2a0d50e707d9e94dc921f6d38a964e1e381a Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 26 Sep 2018 15:22:59 +0900 Subject: [PATCH 74/94] strict_neg clause bug fix --- src/data/mod.rs | 14 ++++++++++++++ src/teacher/mod.rs | 9 ++------- src/term/simplify.rs | 13 ++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/data/mod.rs b/src/data/mod.rs index a7bb607b..115576c4 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -682,6 +682,20 @@ impl Data { ) -> Res { let rhs = match rhs { Some((pred, sample)) => if lhs.is_empty() { + let add_as_neg = if let Some(str) = self.instance[pred].strength() { + // If the strengthening term of the predicate evaluates to false on a positive + // sample, this thing's unsat. + if let Some(value) = str.bool_eval(&sample)? { + ! value + } else { + false + } + } else { + false + }; + if add_as_neg { + self.add_raw_neg(clause, pred, sample.clone()); + } // Positive sample. let new = self.add_raw_pos(clause, pred, sample); return Ok(new); diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 1d00ed7b..c597c53e 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -559,15 +559,10 @@ impl<'a> Teacher<'a> { Ok(true) => { // New data. for (index, &mut (_, _, ref mut changed)) in self.learners.index_iter_mut() { - *changed = index != idx + *changed = *changed || index != idx } } - Ok(false) => if self.learners[idx].2 { - // Something has changed since the last candidate of this learner. - // The fact that the current candidate generated no new data is not - // a problem. - () - } else { + Ok(false) => if self.learners.len() == 1 { bail! { "translation of cexs to data for {} generated no new data", conf.emph( & self.learners[idx].1 ) diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 7f7518fb..9fea344c 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -217,7 +217,10 @@ where "{}", res ; " " } - print_err(& "simplification failure".into()) + print_err(&format!( + "{} simplification failure", if conj { "conjunction" } else { "disjunction" } + ).into()); + panic!("internal error") } solver @@ -530,19 +533,19 @@ fn disj_of_conj_and_term(conj: & [Term], rhs: &T) -> SimplRes where T: Deref { use std::cmp::Ordering::*; - let mut lesser_count = 0; + let mut greater_count = 0; let mut yields = vec![]; for lhs in conj { match bin_simpl(lhs, rhs, false) { - SimplRes::Cmp(Equal) | SimplRes::Cmp(Greater) => return SimplRes::gt(), - SimplRes::Cmp(Less) => lesser_count += 1, + SimplRes::Cmp(Equal) | SimplRes::Cmp(Less) => return SimplRes::lt(), + SimplRes::Cmp(Greater) => greater_count += 1, SimplRes::Yields(term) => yields.push(term), SimplRes::None => (), } } if yields.len() == conj.len() { SimplRes::Yields(term::and(yields)) - } else if lesser_count == conj.len() { + } else if greater_count == conj.len() { SimplRes::lt() } else { SimplRes::None From 9439e75ce38601967d33f056a488734570ccbe2e Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 26 Sep 2018 15:55:28 +0900 Subject: [PATCH 75/94] gain pivot tweaks --- src/common/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/config.rs b/src/common/config.rs index 2123ccc5..06f54fa9 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -785,7 +785,7 @@ impl IceConf { (between 0 and 100)", ).validator(int_validator) .value_name("int") - .default_value("7") + .default_value("20") .takes_value(true) .number_of_values(1) .hidden(true) @@ -798,7 +798,7 @@ impl IceConf { (inactive if > 100)", ).validator(int_validator) .value_name("int") - .default_value("1") + .default_value("10") .takes_value(true) .number_of_values(1) .hidden(true) From b962214a97f31ebd5ee853fe4402aa3dd49c48e0 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 27 Sep 2018 19:02:15 +0900 Subject: [PATCH 76/94] entry point unsat proof --- src/common/consts.rs | 4 - src/common/mod.rs | 7 + src/data/mod.rs | 193 +++++++++++++---- src/data/sample.rs | 2 +- src/errors.rs | 12 +- src/hoice.rs | 32 ++- src/instance/mod.rs | 4 +- src/preproc/one_lhs.rs | 2 +- src/preproc/one_rhs.rs | 2 +- src/preproc/utils.rs | 6 +- src/teacher/mod.rs | 23 +- src/term/factory.rs | 10 +- src/term/simplify.rs | 232 +++++++++++---------- src/unsat_core/entry_points.rs | 371 +++++++++++++++++++++++++++++++++ src/unsat_core/mod.rs | 116 ++++------- tests/tests.rs | 267 ++++++++++++------------ 16 files changed, 889 insertions(+), 394 deletions(-) create mode 100644 src/unsat_core/entry_points.rs diff --git a/src/common/consts.rs b/src/common/consts.rs index ee7cadfa..72da22c1 100644 --- a/src/common/consts.rs +++ b/src/common/consts.rs @@ -7,8 +7,6 @@ lazy_static! { pub static ref ten: ::common::Int = 10.into() ; } - - /// Error-related constants. pub mod err { /// Description for unsat error(s). @@ -21,8 +19,6 @@ pub mod err { pub static exit_desc: &'static str = "exit"; } - - /// Use this macro to declare keywords. /// /// Declares everything and creates a function testing if a string is a keyword. diff --git a/src/common/mod.rs b/src/common/mod.rs index ce16395c..052fb14f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -68,6 +68,13 @@ lazy_static!{ // |===| Helpers. +/// Provides a discard function that drops anything. +pub trait Discard: Sized { + /// Drops self. + fn discard(self) {} +} +impl Discard for T {} + /// Stdout. pub use std::io::stdout; diff --git a/src/data/mod.rs b/src/data/mod.rs index 115576c4..1c0bdd8d 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -44,6 +44,8 @@ pub struct Data { // graph: Option, /// Profiler. _profiler: Profiler, + /// Entry point tracker. + entry_points: Option<::unsat_core::entry_points::EntryPoints>, } impl Clone for Data { @@ -62,6 +64,7 @@ impl Clone for Data { cstr_info: self.cstr_info.clone(), // graph: None, _profiler: Profiler::new(), + entry_points: None, } } } @@ -100,6 +103,12 @@ impl Data { } // let track_samples = instance.track_samples() ; + let entry_points = if instance.proofs() { + Some(::unsat_core::entry_points::EntryPoints::new()) + } else { + None + }; + let constraints = CstrMap::with_capacity(103); Data { instance, @@ -117,6 +126,7 @@ impl Data { // None // }, _profiler: Profiler::new(), + entry_points, } } @@ -272,12 +282,60 @@ impl Data { Ok(useful) } + /// Registers a sample dependency. + /// + /// Input sample in the sample that is positive, second one is the one that depends on it. + fn register_sample_dep( + &mut self, + pred: PrdIdx, + args: &VarVals, + rhs: Option, + ) -> Res<()> { + if let Some(rhs) = rhs { + self.entry_points + .as_mut() + .map(|e| e.register_dep(rhs, &Sample::new(pred, args.clone()))) + .unwrap_or(Ok(()))?; + } + Ok(()) + } + + /// Registers a sample dependency. + /// + /// Input sample in the sample that is positive, second one is the one that depends on it. + fn register_raw_sample_dep( + &mut self, + pred: PrdIdx, + args: &VarVals, + rhs: Option<&(PrdIdx, RVarVals)>, + ) -> Res<()> { + if let Some((rhs_pred, var_vals)) = rhs { + let rhs_args = var_to::vals::new(var_vals.clone()); + let rhs = Sample::new(*rhs_pred, rhs_args); + self.register_sample_dep(pred, args, Some(rhs))? + } + Ok(()) + } + + /// Registers a constraint simplification (lhs part). + /// + /// Input sample in the sample that is positive and was removed. + fn register_lhs_constraint_simpl( + &mut self, + constraint: CstrIdx, + pred: PrdIdx, + args: &VarVals, + ) -> Res<()> { + let rhs = self.constraints[constraint].rhs().cloned(); + self.register_sample_dep(pred, args, rhs) + } + /// Adds a positive example. /// /// The `clause` input is necessary for unsat core extraction. /// /// Does not propagate. - pub fn add_raw_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { + fn add_raw_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { let args = var_to::vals::new(args); self.add_pos(clause, pred, args.clone()) } @@ -287,7 +345,7 @@ impl Data { /// The `clause` input is necessary for unsat core extraction. /// /// Does not propagate. - pub fn add_raw_neg(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { + fn add_raw_neg(&mut self, clause: ClsIdx, pred: PrdIdx, args: RVarVals) -> bool { let args = var_to::vals::new(args); self.add_neg(clause, pred, args.clone()) } @@ -297,7 +355,21 @@ impl Data { /// The `clause` input is necessary for unsat core extraction. /// /// Does not propagate. - pub fn add_pos(&mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { + pub fn add_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { + // println!("add_pos ({} {})", self.instance[pred], args); + // println!( + // "#{} {}", + // clause, + // self.instance[clause] + // .to_string_info(self.instance.preds()) + // .unwrap() + // ); + if self.instance[clause].lhs_preds().is_empty() { + // println!("positive clause"); + if let Some(e) = self.entry_points.as_mut() { + e.register(Sample::new(pred, args.clone())) + } + } // if self.add_pos_untracked( pred, args.clone() ) { // if let Some(graph) = self.graph.as_mut() { // graph.add( @@ -461,17 +533,42 @@ impl Data { } /// Checks whether the data is contradictory. - pub fn is_unsat(&self) -> Option> { + pub fn is_unsat(&self) -> bool { + self.get_unsat_proof().is_ok() + } + + /// Retrieves a proof of unsat. + pub fn get_unsat_proof(&self) -> Res<::unsat_core::UnsatRes> { + // println!( + // "all learning data:\n{}", + // self.string_do(&(), |s| s.to_string()).unwrap() + // ); + // println!("data:"); + // for line in self + // .entry_points + // .as_ref() + // .unwrap() + // .to_string(&*self.instance) + // .lines() + // { + // println!(" {}", line) + // } + // println!("is unsat"); for (pred, samples) in self.pos.index_iter() { for sample in samples { for neg in &self.neg[pred] { if sample.is_complementary(neg) { - return Some(vec![(pred, sample.clone()), (pred, neg.clone())]); + let entry_points = if let Some(entry_points) = &self.entry_points { + Some(entry_points.entry_points_of(&Sample::new(pred, sample.clone()))?) + } else { + None + }; + return Ok(::unsat_core::UnsatRes::new(entry_points)); } } } } - None + bail!("asked for unsat proof while learning data is not unsat") } /// Propagates all staged samples. @@ -559,22 +656,33 @@ impl Data { if let Some(constraints) = self.remove_subs(pred, &args) { profile! { self tick "propagate", "cstr update" } for constraint_idx in constraints { - let constraint = &mut self.constraints[constraint_idx]; - let map = &mut self.map; + macro_rules! constraint { + () => { + self.constraints[constraint_idx] + }; + } - let tautology = constraint - .force_sample(pred, &args, pos, |pred, args| { - Self::tauto_fun(map, constraint_idx, pred, &args) - }).chain_err(|| "in propagate")?; + let tautology = { + let map = &mut self.map; + let constraint = &mut constraint!(); + constraint + .force_sample(pred, &args, pos, |pred, args| { + Self::tauto_fun(map, constraint_idx, pred, &args) + }).chain_err(|| "in propagate")? + }; if tautology { // Tautology, discard. self.cstr_info.forget(constraint_idx) } else { - match constraint.try_trivial() { + if pos { + self.register_lhs_constraint_simpl(constraint_idx, pred, &args)? + } + + match constraint!().try_trivial() { Either::Left((Sample { pred, args }, pos)) => { // Constraint is trivial: unlink and forget. - if let Some(set) = map[pred].get_mut(&args) { + if let Some(set) = self.map[pred].get_mut(&args) { let was_there = set.remove(&constraint_idx); debug_assert! { was_there } } @@ -586,7 +694,7 @@ impl Data { // Otherwise, the constraint was modified and we're keeping // it. self.cstr_info - .register_modded(constraint_idx, &constraint)?; + .register_modded(constraint_idx, &constraint!())?; modded_constraints.insert(constraint_idx); } Either::Right(true) => { @@ -681,12 +789,12 @@ impl Data { rhs: Option<(PrdIdx, RVarVals)>, ) -> Res { let rhs = match rhs { - Some((pred, sample)) => if lhs.is_empty() { + Some((pred, sample)) => { let add_as_neg = if let Some(str) = self.instance[pred].strength() { // If the strengthening term of the predicate evaluates to false on a positive // sample, this thing's unsat. if let Some(value) = str.bool_eval(&sample)? { - ! value + !value } else { false } @@ -696,13 +804,15 @@ impl Data { if add_as_neg { self.add_raw_neg(clause, pred, sample.clone()); } - // Positive sample. - let new = self.add_raw_pos(clause, pred, sample); - return Ok(new); - } else { - // Constraint. - Some((pred, sample)) - }, + if lhs.is_empty() { + // Positive sample. + let new = self.add_raw_pos(clause, pred, sample); + return Ok(new); + } else { + // Constraint. + Some((pred, sample)) + } + } None => if lhs.len() == 1 { // Negative sample. @@ -745,6 +855,7 @@ impl Data { // If no partial examples and sample is new, no need to check anything. if conf.teacher.partial || !is_new { if args.set_subsumed(&self.pos[pred]) { + self.register_raw_sample_dep(pred, &args, rhs.as_ref())?; // Positive, skip. continue 'lhs_iter; } else if args.set_subsumed(&self.neg[pred]) { @@ -815,29 +926,28 @@ impl Data { /// Partial samples ARE NOT ALLOWED in constraints. /// /// - propagates staged samples beforehand - pub fn add_cstr( + fn add_cstr( &mut self, _clause: ClsIdx, lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)>, ) -> Res { profile!( - self wrap { self.propagate() } - "add cstr", "pre-propagate" - )?; + self wrap { self.propagate() } "add cstr", "pre-propagate" + )?; if_log! { @4 - log! { @4 "adding constraint" } - if let Some((pred, args)) = rhs.as_ref() { - log! { @4 "({} {})", self.instance[* pred], args } - } else { - log! { @4 "false" } - } - let mut pref = "<=" ; - for (pred, args) in & lhs { - log! { @4 "{} ({} {})", pref, self.instance[* pred], args } - pref = " " - } + log! { @4 "adding constraint" } + if let Some((pred, args)) = rhs.as_ref() { + log! { @4 "({} {})", self.instance[* pred], args } + } else { + log! { @4 "false" } + } + let mut pref = "<=" ; + for (pred, args) in & lhs { + log! { @4 "{} ({} {})", pref, self.instance[* pred], args } + pref = " " + } } profile! { self tick "add cstr", "pre-checks" } @@ -867,9 +977,8 @@ impl Data { Either::Right(false) => { // Handles linking and constraint info registration. let is_new = profile!( - self wrap { self.raw_add_cstr(constraint) } - "add cstr", "raw" - )?; + self wrap { self.raw_add_cstr(constraint) } "add cstr", "raw" + )?; self.check("after add_cstr")?; diff --git a/src/data/sample.rs b/src/data/sample.rs index 39d1995e..1b5235ba 100644 --- a/src/data/sample.rs +++ b/src/data/sample.rs @@ -1,7 +1,7 @@ use common::{var_to::vals::VarValsSet, *}; /// A sample is some values for a predicate. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub struct Sample { /// Predicate the sample is for. pub pred: PrdIdx, diff --git a/src/errors.rs b/src/errors.rs index 806bb746..be1008cf 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -156,7 +156,7 @@ impl Error { pub fn is_unsat(&self) -> bool { for err in self.iter() { if err.description() == consts::err::unsat_desc { - return true + return true; } } false @@ -180,7 +180,7 @@ impl Error { } else { for err in self.iter() { if err.description() == consts::err::unknown_desc { - return true + return true; } } false @@ -200,12 +200,12 @@ impl Error { /// [timeout]: enum.ErrorKind.html#variant.Timeout /// (ErrorKind's Timeout variant) pub fn is_timeout(&self) -> bool { - if let ErrorKind::SmtError(smt_err) =self.kind() { - return smt_err.is_timeout() + if let ErrorKind::SmtError(smt_err) = self.kind() { + return smt_err.is_timeout(); } for err in self.iter() { if err.description() == consts::err::timeout_desc { - return true + return true; } } false @@ -217,7 +217,7 @@ impl Error { pub fn is_exit(&self) -> bool { for err in self.iter() { if err.description() == consts::err::exit_desc { - return true + return true; } } false diff --git a/src/hoice.rs b/src/hoice.rs index 2067b8ce..fa2ff588 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -118,6 +118,9 @@ pub fn read_and_work( // - `None` if not unsat let mut unsat = None; + // Original instance. + let mut original_instance = None; + 'parse_work: loop { use parse::Parsed; @@ -163,6 +166,12 @@ pub fn read_and_work( // Check-sat, start class. Parsed::CheckSat => { + if instance.proofs() { + let mut old = instance.clone(); + old.finalize() + .chain_err(|| "while finalizing original instance")?; + original_instance = Some(old) + } log! { @info "Running top pre-processing" } let preproc_profiler = Profiler::new(); @@ -180,12 +189,7 @@ pub fn read_and_work( println!("unknown"); continue; } else if e.is_unsat() { - if let Some(clause) = e.unsat_cause() { - unsat = Some(unsat_core::UnsatRes::Clause(clause)) - } else { - unsat = Some(unsat_core::UnsatRes::None) - } - () + unsat = Some(unsat_core::UnsatRes::None) } else { bail!(e) }, @@ -268,7 +272,21 @@ pub fn read_and_work( Parsed::GetUnsatCore => println!("unsupported"), // Print unsat core if available. - Parsed::GetProof => println!("unsupported"), + Parsed::GetProof => if let Some(unsat_res) = unsat.as_ref() { + if let Err(e) = original_instance + .as_ref() + .ok_or::( + "unable to retrieve original instance for proof reconstruction".into(), + ).and_then(|original| { + unsat_res + .write_proof(&mut stdout(), &instance, original) + .chain_err(|| "while writing unsat proof") + }) { + print_err(&e) + } + } else { + print_err(&"no unsat proof available".into()) + }, // Print model if available. Parsed::GetModel => if let Some(model) = model.as_mut() { diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 0072b9a2..d5de39fc 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -1363,7 +1363,7 @@ impl Instance { /// True if the teacher needs to maintain a sample graph (unsat /// cores/proofs). pub fn track_samples(&self) -> bool { - self.unsat_cores() || self.proofs() + false // self.unsat_cores() || self.proofs() } /// Converts `"true"` to `true`, `"false"` to `false`, and everything else to @@ -1413,6 +1413,8 @@ impl Instance { /// Retrieves the lhs and rhs cex part from a bias. fn break_cex(&self, clause_idx: ClsIdx, bias: Bias) -> (CexLhs, CexRhs) { let clause = &self[clause_idx]; + let bias = if self.proofs { Bias::Non } else { bias }; + match bias { // Consider the whole lhs of the clause positive. Bias::Lft => (vec![], clause.rhs()), diff --git a/src/preproc/one_lhs.rs b/src/preproc/one_lhs.rs index 91272440..7acd16a9 100644 --- a/src/preproc/one_lhs.rs +++ b/src/preproc/one_lhs.rs @@ -250,7 +250,7 @@ impl RedStrat for OneLhs { .map(|argss| argss.len() != 1) .unwrap_or(true) { - continue 'all_preds + continue 'all_preds; } log! { @3 diff --git a/src/preproc/one_rhs.rs b/src/preproc/one_rhs.rs index 8d2a0e15..b54b82fb 100644 --- a/src/preproc/one_rhs.rs +++ b/src/preproc/one_rhs.rs @@ -214,7 +214,7 @@ impl RedStrat for OneRhs { } } - self.quantifiers = ! self.quantifiers; + self.quantifiers = !self.quantifiers; Ok(red_info) } diff --git a/src/preproc/utils.rs b/src/preproc/utils.rs index fb51dff5..cf5a0c45 100644 --- a/src/preproc/utils.rs +++ b/src/preproc/utils.rs @@ -771,9 +771,9 @@ pub fn run_preproc( log! { @verb "running {}", conf.emph( preproc.name() ) } - let red_info = preproc.apply(instance).chain_err( - || format!("while running preprocessor {}", conf.bad(preproc.name())) - ); + let red_info = preproc + .apply(instance) + .chain_err(|| format!("while running preprocessor {}", conf.bad(preproc.name()))); profile! { |_profiler| mark "preproc", preproc.name() } diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index c597c53e..5a3b47d6 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -44,7 +44,7 @@ pub fn start_class( unsat core will not be available\n\ please consider contacting the developer" } - let core = teacher.unsat_core(); + let core = teacher.unsat_core()?; Ok(TeachRes::Unsat(core)) } @@ -112,7 +112,10 @@ pub fn teach(teacher: &mut Teacher) -> Res { match teacher.get_candidates(false)? { // Unsat result, done. - Either::Right(unsat) => return Ok(TeachRes::Unsat(unsat)), + Either::Right(unsat) => { + teacher.data.is_unsat(); + return Ok(TeachRes::Unsat(unsat)); + } // Got a candidate. Either::Left((idx, candidates)) => { @@ -353,6 +356,7 @@ impl<'a> Teacher<'a> { } self.assistant = None; log_debug! { "draining messages" } + self.data.is_unsat(); while let Ok(_) = self.get_candidates(true) {} if conf.stats { @@ -570,7 +574,7 @@ impl<'a> Teacher<'a> { }, Err(e) => { if e.is_unsat() { - return Ok(Some(TeachRes::Unsat(self.unsat_core()))); + return Ok(Some(TeachRes::Unsat(self.unsat_core()?))); } else { bail!(e) } @@ -643,8 +647,8 @@ impl<'a> Teacher<'a> { } // Are we unsat? - if self.data.is_unsat().is_some() { - return Ok(Either::Right(self.unsat_core())); + if self.data.is_unsat() { + return Ok(Either::Right(self.unsat_core()?)); } } @@ -671,17 +675,16 @@ impl<'a> Teacher<'a> { } } - MsgKind::Unsat => if self.data.is_unsat().is_some() { - return Ok(Either::Right(self.unsat_core())); + MsgKind::Unsat => if self.data.is_unsat() { + return Ok(Either::Right(self.unsat_core()?)); }, } } } /// Retrieves the unsat core, if any. - pub fn unsat_core(&mut self) -> UnsatRes { - // UnsatRes::new( self.data.sample_graph() ) - UnsatRes::new(None) + pub fn unsat_core(&mut self) -> Res { + self.data.get_unsat_proof() } /// Initial check, where all candidates are `true`. diff --git a/src/term/factory.rs b/src/term/factory.rs index b8704f65..3934e7a8 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -763,7 +763,9 @@ where if args.len() != fun.sig.len() { panic!( "illegal application of function {} to {} arguments, expected {}", - conf.bad(&name), args.len(), fun.sig.len() + conf.bad(&name), + args.len(), + fun.sig.len() ) } for (info, arg) in fun.sig.iter().zip(args.iter_mut()) { @@ -775,7 +777,9 @@ where } else if info.typ != arg.typ() { panic!( "ill-typed application of function {}, {} does not have type {}", - conf.bad(&name), arg, info.typ + conf.bad(&name), + arg, + info.typ ) } } @@ -792,7 +796,7 @@ where } else { term } - }, + } Err(e) => { print_err(&e); panic!("illegal function application") diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 9fea344c..96d0bf51 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -147,8 +147,6 @@ impl_fmt! { } } - - /// Checks the result of a binary simplification. fn check_bin_simpl(lhs: &T1, rhs: &T2, res: &SimplRes, conj: bool) where @@ -191,10 +189,18 @@ where let check = match res { SimplRes::Cmp(Equal) => format!("(= {} {})", lhs.deref(), rhs.deref()), SimplRes::Cmp(Less) => format!( - "(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), rhs.deref() + "(= ({} {} {}) {})", + op, + lhs.deref(), + rhs.deref(), + rhs.deref() ), SimplRes::Cmp(Greater) => format!( - "(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), lhs.deref() + "(= ({} {} {}) {})", + op, + lhs.deref(), + rhs.deref(), + lhs.deref() ), SimplRes::Yields(ref term) => { format!("(= ({} {} {}) {})", op, lhs.deref(), rhs.deref(), term) @@ -217,9 +223,12 @@ where "{}", res ; " " } - print_err(&format!( - "{} simplification failure", if conj { "conjunction" } else { "disjunction" } - ).into()); + print_err( + &format!( + "{} simplification failure", + if conj { "conjunction" } else { "disjunction" } + ).into(), + ); panic!("internal error") } @@ -326,13 +335,14 @@ pub fn conj_term_insert(term: Term, set: &mut TermSet) -> bool { fls } - - /// Simplifies two boolean terms. /// /// Treats the pair of terms as a conjunction if `conj` is true, as a disjunction otherwise. fn bin_simpl_2(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes -where T1: Deref, T2: Deref { +where + T1: Deref, + T2: Deref, +{ // use std::cmp::Ordering::*; if lhs.deref() == rhs.deref() { @@ -341,48 +351,50 @@ where T1: Deref, T2: Deref { match (lhs.bool(), rhs.bool()) { (Some(true), _) => if conj { - return SimplRes::lt() + return SimplRes::lt(); } else { - return SimplRes::tru() + return SimplRes::tru(); }, (_, Some(true)) => if conj { - return SimplRes::gt() + return SimplRes::gt(); } else { - return SimplRes::tru() + return SimplRes::tru(); }, (Some(false), _) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::lt() + return SimplRes::lt(); }, (_, Some(false)) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::gt() + return SimplRes::gt(); }, (None, None) => (), } - let negated_lhs = term::not( lhs.deref().to_hcons() ); + let negated_lhs = term::not(lhs.deref().to_hcons()); if negated_lhs.get() == rhs.deref() { if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::tru() + return SimplRes::tru(); } } int_simpl(lhs, rhs, conj) } - /// Simplifies two boolean terms. /// /// Treats the pair of terms as a conjunction if `conj` is true, as a disjunction otherwise. pub fn bin_simpl(lhs: &T1, rhs: &T2, conj: bool) -> SimplRes -where T1: Deref, T2: Deref { +where + T1: Deref, + T2: Deref, +{ if conf.term_simpl == 0 { return SimplRes::None; } @@ -523,14 +535,18 @@ where T1: Deref, T2: Deref { /// assert_eq! { conj_simpl(& lhs, & rhs), None } /// ``` pub fn conj_simpl(lhs: &T1, rhs: &T2) -> SimplRes -where T1: Deref, T2: Deref { +where + T1: Deref, + T2: Deref, +{ bin_simpl(lhs, rhs, true) } - /// Compares the disjunction of a conjunction and a term. -fn disj_of_conj_and_term(conj: & [Term], rhs: &T) -> SimplRes -where T: Deref { +fn disj_of_conj_and_term(conj: &[Term], rhs: &T) -> SimplRes +where + T: Deref, +{ use std::cmp::Ordering::*; let mut greater_count = 0; @@ -552,10 +568,11 @@ where T: Deref { } } - /// Compares the conjunction of a disjunction and a term. -fn conj_of_disj_and_term(disj: & [Term], rhs: &T) -> SimplRes -where T: Deref { +fn conj_of_disj_and_term(disj: &[Term], rhs: &T) -> SimplRes +where + T: Deref, +{ use std::cmp::Ordering::*; let mut greater_count = 0; @@ -583,7 +600,6 @@ where T: Deref { /// /// Assumes none of the terms are conjunctions (disjunctions) if `conj` is true (false). fn vec_simpl(terms: &mut Vec, conj: bool) { - let mut res = Vec::with_capacity(terms.len()); 'add_terms: while let Some(term) = terms.pop() { @@ -605,15 +621,13 @@ fn vec_simpl(terms: &mut Vec, conj: bool) { res.swap_remove(cnt); match (term.bool(), conj) { - (Some(false), false) | - (Some(true), true) => (), + (Some(false), false) | (Some(true), true) => (), - (Some(true), false) | - (Some(false), true) => { + (Some(true), false) | (Some(false), true) => { terms.clear(); terms.push(term); - return () - }, + return (); + } (None, _) => terms.push(term), } @@ -696,7 +710,7 @@ impl<'a> Deconstructed<'a> { } impl<'a> ::std::fmt::Display for Deconstructed<'a> { - fn fmt(&self, fmt: & mut ::std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { if self.trms.len() == 1 { self.trms[0].fmt(fmt) } else { @@ -779,14 +793,19 @@ where if rhs_trm.is_opposite(&lhs_trm) { rhs_trm = lhs_trm.clone(); - rhs_cst = rhs_cst.minus().expect("illegal term found during simplification"); + rhs_cst = rhs_cst + .minus() + .expect("illegal term found during simplification"); rhs_op = match rhs_op { Op::Eql => Op::Eql, Op::Gt => Op::Lt, Op::Ge => Op::Le, Op::Lt => Op::Gt, Op::Le => Op::Ge, - _ => panic!("unexpected operator {} during integer relation simplification", rhs_op) + _ => panic!( + "unexpected operator {} during integer relation simplification", + rhs_op + ), } } @@ -797,9 +816,7 @@ where // println!(" {} {} {}", rhs_trm.to_string(), rhs_op, rhs_cst); if lhs_trm.equal(&rhs_trm) { - match (lhs_op, rhs_op) { - (Op::Gt, Op::Gt) | (Op::Ge, Op::Ge) => { return SimplRes::Cmp( lhs_cst.get().compare(&rhs_cst).expect( @@ -850,44 +867,44 @@ where (Op::Eql, Op::Ge) | (Op::Eql, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { Some(Less) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::None + return SimplRes::None; }, Some(Equal) if rhs_op == Op::Gt => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + return SimplRes::Yields(term::ge(lhs_trm.into_term(), term::cst(lhs_cst))); }, Some(Equal) | Some(Greater) => return SimplRes::gt().invert_if(!conj), None => unreachable!(), - } + }, (Op::Eql, Op::Le) | (Op::Eql, Op::Lt) => match lhs_cst.get().compare(&rhs_cst) { Some(Greater) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::None + return SimplRes::None; }, Some(Equal) if rhs_op == Op::Lt => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::Yields( term::le( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + return SimplRes::Yields(term::le(lhs_trm.into_term(), term::cst(lhs_cst))); }, Some(Equal) | Some(Less) => return SimplRes::gt().invert_if(!conj), None => unreachable!(), - } + }, (Op::Ge, Op::Eql) | (Op::Gt, Op::Eql) => match rhs_cst.get().compare(&lhs_cst) { Some(Less) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::None + return SimplRes::None; }, Some(Equal) if lhs_op == Op::Gt => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + return SimplRes::Yields(term::ge(lhs_trm.into_term(), term::cst(lhs_cst))); }, Some(Equal) | Some(Greater) => return SimplRes::lt().invert_if(!conj), None => unreachable!(), @@ -895,66 +912,68 @@ where (Op::Le, Op::Eql) | (Op::Lt, Op::Eql) => match rhs_cst.get().compare(&lhs_cst) { Some(Greater) => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::None + return SimplRes::None; }, Some(Equal) if lhs_op == Op::Gt => if conj { - return SimplRes::fls() + return SimplRes::fls(); } else { - return SimplRes::Yields( term::ge( lhs_trm.into_term(), term::cst(lhs_cst) ) ) + return SimplRes::Yields(term::ge(lhs_trm.into_term(), term::cst(lhs_cst))); }, Some(Equal) | Some(Less) => return SimplRes::lt().invert_if(!conj), None => unreachable!(), }, - (Op::Ge, Op::Le) | (Op::Gt, Op::Le) | - (Op::Ge, Op::Lt) | (Op::Gt, Op::Lt) => match lhs_cst.get().compare(&rhs_cst) { - Some(Less) => if conj { - return SimplRes::None - } else { - return SimplRes::tru() - }, - Some(Equal) if conj && (lhs_op == Op::Gt || rhs_op == Op::Lt) => { - return SimplRes::fls() - }, - Some(Equal) => if conj { - return SimplRes::Yields( term::eq(lhs_trm.into_term(), term::cst(lhs_cst)) ) - } else { - return SimplRes::tru() - }, - Some(Greater) => if conj { - return SimplRes::fls() - } else { - return SimplRes::None - }, - None => unreachable!(), - }, + (Op::Ge, Op::Le) | (Op::Gt, Op::Le) | (Op::Ge, Op::Lt) | (Op::Gt, Op::Lt) => { + match lhs_cst.get().compare(&rhs_cst) { + Some(Less) => if conj { + return SimplRes::None; + } else { + return SimplRes::tru(); + }, + Some(Equal) if conj && (lhs_op == Op::Gt || rhs_op == Op::Lt) => { + return SimplRes::fls() + } + Some(Equal) => if conj { + return SimplRes::Yields(term::eq(lhs_trm.into_term(), term::cst(lhs_cst))); + } else { + return SimplRes::tru(); + }, + Some(Greater) => if conj { + return SimplRes::fls(); + } else { + return SimplRes::None; + }, + None => unreachable!(), + } + } - (Op::Le, Op::Ge) | (Op::Le, Op::Gt) | - (Op::Lt, Op::Ge) | (Op::Lt, Op::Gt) => match lhs_cst.get().compare(&rhs_cst) { - Some(Greater) => if conj { - return SimplRes::None - } else { - return SimplRes::tru() - }, - Some(Equal) if conj && (lhs_op == Op::Lt || rhs_op == Op::Gt) => { - return SimplRes::fls() - }, - Some(Equal) => if conj { - return SimplRes::Yields( term::eq(lhs_trm.into_term(), term::cst(lhs_cst)) ) - } else if lhs_op == Op::Lt && rhs_op == Op::Gt { - return SimplRes::None - } else { - return SimplRes::tru() - }, - Some(Less) => if conj { - return SimplRes::fls() - } else { - return SimplRes::None - }, - None => unreachable!(), - }, + (Op::Le, Op::Ge) | (Op::Le, Op::Gt) | (Op::Lt, Op::Ge) | (Op::Lt, Op::Gt) => { + match lhs_cst.get().compare(&rhs_cst) { + Some(Greater) => if conj { + return SimplRes::None; + } else { + return SimplRes::tru(); + }, + Some(Equal) if conj && (lhs_op == Op::Lt || rhs_op == Op::Gt) => { + return SimplRes::fls() + } + Some(Equal) => if conj { + return SimplRes::Yields(term::eq(lhs_trm.into_term(), term::cst(lhs_cst))); + } else if lhs_op == Op::Lt && rhs_op == Op::Gt { + return SimplRes::None; + } else { + return SimplRes::tru(); + }, + Some(Less) => if conj { + return SimplRes::fls(); + } else { + return SimplRes::None; + }, + None => unreachable!(), + } + } (Op::Eql, Op::Eql) => if rhs_cst.equal(&lhs_cst) { return SimplRes::eq(); @@ -966,7 +985,6 @@ where _ => (), } - } SimplRes::None diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs new file mode 100644 index 00000000..f24bb42e --- /dev/null +++ b/src/unsat_core/entry_points.rs @@ -0,0 +1,371 @@ +//! Entry point extraction data. +//! +//! Keeps track of the dependencies between positive samples. + +use common::*; +use data::sample::Sample; + +/// Set of samples. +pub type SampleSet = BTreeSet; +/// Map of samples. +pub type SampleMap = BTreeMap; + +/// Type of the solver used for reconstruction. +type Slvr = Solver; + +/// Entry point extraction type. +#[derive(Debug, Clone, Default)] +pub struct EntryPoints { + /// Real positive samples. + real_pos_samples: SampleSet, + /// Maps RHS of implication constraints to the real positive samples they are known to depend + /// on this far. + pos_sample_map: SampleMap, +} + +impl EntryPoints { + /// Constructor. + pub fn new() -> Self { + EntryPoints { + real_pos_samples: SampleSet::new(), + pos_sample_map: SampleMap::new(), + } + } + + /// String representation. + pub fn to_string(&self, instance: &Instance) -> String { + let mut s = "real_pos_samples:".to_string(); + for sample in &self.real_pos_samples { + s += &format!("\n ({} {})", instance[sample.pred], sample.args) + } + s += "\npos_sample_map:"; + for (sample, set) in &self.pos_sample_map { + s += &format!("\n ({} {})", instance[sample.pred], sample.args); + for sample in set { + s += &format!("\n -> ({} {})", instance[sample.pred], sample.args) + } + } + s + } + + /// Registers a positive sample. + pub fn register(&mut self, sample: Sample) { + self.real_pos_samples.insert(sample); + } + + /// Registers a dependency between the RHS of an implication constraint and a positive sample. + pub fn register_dep(&mut self, sample: Sample, dep: &Sample) -> Res<()> { + let mut set = self + .pos_sample_map + .remove(&sample) + .unwrap_or_else(SampleSet::new); + if self.real_pos_samples.contains(dep) { + set.insert(dep.clone()); + } else if let Some(dep_set) = self.pos_sample_map.get(dep) { + for sample in dep_set { + set.insert(sample.clone()); + } + } else { + bail!( + "trying to register dependency to unknown positive sample {}", + dep + ) + }; + let prev = self.pos_sample_map.insert(sample, set); + debug_assert! { prev.is_none() } + Ok(()) + } + + /// Retrieves the real positive samples corresponding to a sample. + pub fn entry_points_of(&self, sample: &Sample) -> Res { + if self.real_pos_samples.contains(sample) { + let samples: SampleSet = vec![sample.clone()].into_iter().collect(); + return Ok(samples.into()); + } + self.pos_sample_map + .get(sample) + .map(|entry| entry.clone().into()) + .ok_or_else::(|| { + format!( + "trying to recover entry points for unknown sample {}", + sample + ).into() + }) + } +} + +/// Entry points leading to a contradiction. +#[derive(Debug, Clone)] +pub struct Entry { + /// Positive samples leading to a contradiction. + pub samples: SampleSet, +} + +impl From for Entry { + fn from(samples: SampleSet) -> Self { + Entry::new(samples) + } +} + +impl Entry { + /// Constructor. + pub fn new(samples: SampleSet) -> Self { + Entry { samples } + } + + /// Rewrites the entry points in terms of the original signatures. + fn rewrite(&self, instance: &Instance) -> Vec { + let mut samples = vec![]; + + for Sample { pred, args } in &self.samples { + let pred = *pred; + let original_sig = instance[pred].original_sig(); + let mut nu_args = VarMap::with_capacity(original_sig.len()); + for typ in original_sig { + nu_args.push(val::none(typ.clone())) + } + for (var, val) in args.index_iter() { + let old_var = instance[pred].original_sig_map()[var]; + nu_args[old_var] = val.clone() + } + let args = var_to::vals::new(nu_args); + samples.push(Sample { pred, args }) + } + + samples + } + + /// Reconstructs some entry points given the original instance. + pub fn reconstruct(&self, instance: &Instance, original: &Instance) -> Res { + let samples = self.rewrite(instance); + log! { @2 | "reconstructing {} sample(s)", samples.len() } + let mut solver = conf + .solver + .spawn("proof_reconstruction", smt::FullParser, original)?; + let samples = Reconstr::new(original, instance, samples, &mut solver).work()?; + Ok(Self::new(samples)) + } +} + +// /// Result of looking for antecedents for a positive sample. +// enum AnteRes { +// /// No antecedent, the sample can be derived from a positive clause. +// Positive, +// /// List of conjunction of antecedents leading to this sample. +// Ante(Vec), +// /// Positive sample cannot be derived. +// Dead, +// } + +/// Entry point reconstructor. +struct Reconstr<'a> { + /// Predicates that are safe to inline: they are defined in the instance mention only other + /// defined predicates. + safe_preds: PrdSet, + /// Predicates that are defined and can be used in positive samples. + pos_preds: PrdSet, + /// Original instance. + original: &'a Instance, + /// Instance. + instance: &'a Instance, + /// Samples to reconstruct. + to_do: Vec, + /// Positive samples for the original instance. + samples: SampleSet, + // /// Stack of things, used when reconstructing a sample. + // stack: Vec<()>, + /// Solver. + solver: &'a mut Slvr, +} + +impl<'a> Reconstr<'a> { + /// Constructor. + pub fn new( + original: &'a Instance, + instance: &'a Instance, + to_do: Vec, + solver: &'a mut Slvr, + ) -> Self { + let mut safe_preds = PrdSet::new(); + let mut pos_preds = PrdSet::new(); + let mut fp = false; + while !fp { + fp = true; + for pred in instance.preds() { + if safe_preds.contains(&pred.idx) { + continue; + } else if let Some(def) = pred.def() { + if def.preds().is_empty() { + pos_preds.insert(pred.idx); + } + if def.preds().is_subset(&safe_preds) { + fp = false; + safe_preds.insert(pred.idx); + } + } + } + } + + if_log! { @3 + log! { @3 |=> "safe predicates:" } + for pred in &safe_preds { + log! { @3 |=> " {}", instance[*pred] } + } + } + + Reconstr { + safe_preds, + pos_preds, + original, + instance, + to_do, + solver, + samples: SampleSet::new(), + } + } + + /// Finds clauses of the original instance elligible for reconstruction for a predicate. + /// + /// Returns + /// + /// - the positive clauses in which `pred` appears, + /// - the clauses in which `pred` is the rhs and *all* predicates in the LHS are defined in the + /// instance. + fn clauses_for(&self, pred: PrdIdx) -> (Vec, Vec) { + let mut pos = vec![]; + let mut others = vec![]; + for clause_idx in self.original.rhs_clauses_of(pred) { + let clause_preds = self.original[*clause_idx].lhs_preds(); + if clause_preds.is_empty() { + pos.push(*clause_idx) + } else if clause_preds + .keys() + .all(|pred| self.safe_preds.contains(pred)) + { + others.push(*clause_idx) + } + } + (pos, others) + } + + /// Tries to reconstruct a positive sample from a clause. + /// + /// Returns `true` if the reconstruction was positive. If it was, (potentially) new positive + /// samples have been added to `self.samples`. + fn work_on_clause(&mut self, pred: PrdIdx, sample: &VarVals, clause: ClsIdx) -> Res { + debug_assert! { self.instance[clause].rhs().map(|(p, _)| p == pred).unwrap_or(false) } + self.solver.push(1)?; + // Declare clause variables. + self.original[clause].declare(self.solver)?; + // Assert lhs terms. + for term in self.original[clause].lhs_terms() { + self.solver.assert(&smt::SmtTerm::new(term))?; + } + // Assert lhs preds. + for (pred, argss) in self.original[clause].lhs_preds() { + for args in argss { + self.solver.assert_with( + &smt::SmtPredApp::new(*pred, args), + (self.instance.preds(), true), + )? + } + } + + if let Some((p, args)) = self.instance[clause].rhs() { + debug_assert_eq! { pred, p } + self.solver.assert(&smt::EqConj::new(args, &sample))? + } else { + bail!("proof reconstruction, illegal clause-level call (no rhs)") + } + + let sat = self.solver.check_sat()?; + + let model = if sat { + let model = self.solver.get_model()?; + Some(smt::FullParser.fix_model(model)?) + } else { + None + }; + + self.solver.pop(1)?; + + if let Some(model) = model { + let model = Cex::of_model(self.original[clause].vars(), model, true)?; + // Reconstruct all LHS applications. + for (pred, argss) in self.original[clause].lhs_preds() { + let mut samples = vec![]; + for args in argss { + let mut sample = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let val = arg.eval(&model)?; + sample.push(val) + } + samples.push(Sample::new(*pred, var_to::vals::new(sample))) + } + if self.pos_preds.contains(pred) { + self.samples.extend(samples.into_iter()) + } + } + Ok(true) + } else { + Ok(false) + } + } + + /// Reconstructs a single positive sample. + fn work_on_sample(&mut self, Sample { pred, args }: Sample) -> Res<()> { + log! { @3 | "working on ({} {})", self.instance[pred], args } + let (pos, others) = self.clauses_for(pred); + log! { @4 | "{} positive clause(s), {} usable clause(s)", pos.len(), others.len() } + if_log! { @5 + if ! pos.is_empty() { + log! { @4 |=> "positive clause(s)" } + for idx in &pos { + log! { @5 => "{}", self.original[*idx].to_string_info(self.original.preds())? } + } + } + if ! others.is_empty() { + log! { @4 |=> "usable clause(s)" } + for idx in &others { + log! { @5 => "{}", self.original[*idx].to_string_info(self.original.preds())? } + } + } + } + + for clause in pos { + let okay = self.work_on_clause(pred, &args, clause)?; + if okay { + log! { @3 | " reconstructed using positive clause #{}", clause } + return Ok(()); + } + } + for clause in others { + let okay = self.work_on_clause(pred, &args, clause)?; + if okay { + log! { @3 | " reconstructed using non-positive clause #{}", clause } + return Ok(()); + } + } + + bail!( + "could not reconstruct sample ({} {})", + self.instance[pred], + args + ) + } + + /// Reconstructs the positive samples. + pub fn work(mut self) -> Res { + if !self.safe_preds.is_empty() { + let model = self.instance.extend_model(PrdHMap::new())?; + self.instance.write_definitions(self.solver, "", &model)? + } + + while let Some(sample) = self.to_do.pop() { + self.work_on_sample(sample.clone())?; + } + + self.solver.reset()?; + Ok(self.samples) + } +} diff --git a/src/unsat_core/mod.rs b/src/unsat_core/mod.rs index a3d7ffa8..044611f8 100644 --- a/src/unsat_core/mod.rs +++ b/src/unsat_core/mod.rs @@ -4,27 +4,32 @@ use common::*; +pub mod entry_points; pub mod sample_graph; +use self::entry_points::Entry; + pub use self::sample_graph::SampleGraph; -use self::sample_graph::UnsatProof; +// use self::sample_graph::UnsatProof; /// An unsat result. pub enum UnsatRes { /// Unsat cores were not active. None, - /// A sample dependency graph: raw result from teacher. - Graph(SampleGraph), - /// A proof, obtained from a graph. - Proof(UnsatProof), - /// An unsat result from a single clause. - Clause(ClsIdx), + // /// A sample dependency graph: raw result from teacher. + // Graph(SampleGraph), + // /// A proof, obtained from a graph. + // Proof(UnsatProof), + // /// An unsat result from a single clause. + // Clause(ClsIdx), + /// Some entry points. + Entry(Entry), } impl UnsatRes { /// Constructor. - pub fn new(graph: Option) -> Self { - if let Some(graph) = graph { - UnsatRes::Graph(graph) + pub fn new(entry: Option) -> Self { + if let Some(entry) = entry { + UnsatRes::Entry(entry) } else { UnsatRes::None } @@ -38,80 +43,33 @@ impl UnsatRes { } } - /// Retrieves the unsat core. - fn get_core(&mut self, instance: &Instance) -> Res { - let (nu_self, res) = match self { - UnsatRes::None => bail!( - "cannot produce unsat cores without `{}`", - conf.emph("(set-option :produce-unsat-cores true)") - ), - UnsatRes::Graph(graph) => { - let proof = graph.get_proof(instance)?; - let core = proof.core(); - - (UnsatRes::Proof(proof), core) - } - UnsatRes::Proof(proof) => return Ok(proof.core()), - UnsatRes::Clause(clause) => { - let mut set = ClsSet::new(); - set.insert(*clause); - return Ok(set); - } - }; - - *self = nu_self; - - Ok(res) + /// Tries to retrieve the unsat proof. + fn get_proof(&self, instance: &Instance, original: &Instance) -> Res> { + match self { + UnsatRes::None => Ok(None), + UnsatRes::Entry(entry) => Ok(Some(entry.reconstruct(instance, original)?)), + } } - /// Writes the unsat core. - pub fn write_core(&mut self, w: &mut W, instance: &Instance) -> Res<()> { - let core = self.get_core(&instance)?; - if !instance.unsat_cores() { + /// Tries to write the unsat proof. + pub fn write_proof( + &self, + w: &mut W, + instance: &Instance, + original: &Instance, + ) -> Res<()> { + if let Some(entry) = self.get_proof(instance, original)? { + writeln!(w, "(")?; + for sample in &entry.samples { + writeln!(w, " ({} {})", instance[sample.pred], sample.args)? + } + writeln!(w, ")")?; + } else { bail!( - "cannot produce unsat cores without `{}`", - conf.emph("(set-option :produce-unsat-cores true)") + "cannot produce unsat proof without `{}`", + conf.emph("(set-option :produce-unsat-proof true)") ) } - write!(w, "(")?; - for clause in core { - if let Some(name) = instance.name_of_old_clause(clause) { - write!(w, " {}", name)? - } - } - writeln!(w, " )")?; - Ok(()) - } - - /// Writes an unsat proof. - pub fn write_proof(&mut self, w: &mut W, instance: &Instance) -> Res<()> { - let err = || { - ErrorKind::from(format!( - "cannot produce proof without `{}`", - conf.emph("(set-option :produce-proofs true)") - )) - }; - let nu_self = match self { - _ if !instance.proofs() => bail!(err()), - UnsatRes::None => bail!(err()), - UnsatRes::Graph(graph) => { - let proof = graph.get_proof(instance)?; - proof.write(w, instance)?; - - UnsatRes::Proof(proof) - } - UnsatRes::Proof(proof) => { - proof.write(w, instance)?; - return Ok(()); - } - UnsatRes::Clause(_) => { - writeln!(w, "( () () () )")?; - return Ok(()); - } - }; - - *self = nu_self; - Ok(()) } } diff --git a/tests/tests.rs b/tests/tests.rs index 0a8f3609..96380d8f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,53 +1,63 @@ //! Top-level tests on regression scripts in `tests`. #![allow(non_upper_case_globals)] -extern crate hoice ; +extern crate hoice; -use std::fs::read_dir ; -use std::fs::OpenOptions ; +use std::fs::read_dir; +use std::fs::OpenOptions; -use hoice::common::* ; -use hoice::read_and_work ; +use hoice::common::*; +use hoice::read_and_work; -static sat_files_dir: & str = "rsc/sat" ; -static unsat_files_dir: & str = "rsc/unsat" ; -static err_files_dir: & str = "rsc/error" ; +static sat_files_dir: &str = "rsc/sat"; +static unsat_files_dir: &str = "rsc/unsat"; +static err_files_dir: &str = "rsc/error"; macro_rules! run { - ($f:expr) => ( - if let Err(e) = $f { - println!("Error:") ; - for e in e.iter() { - let mut pref = "> " ; - for line in format!("{}", e).lines() { - println!("{}{}", pref, line) ; - pref = " " + ($f:expr) => { + if let Err(e) = $f { + println!("Error:"); + for e in e.iter() { + let mut pref = "> "; + for line in format!("{}", e).lines() { + println!("{}{}", pref, line); + pref = " " + } + } + panic!("failure") } - } - panic!("failure") - } - ) ; + }; } #[test] -fn sat() { run!( run_sat() ) } +fn sat() { + run!(run_sat()) +} #[test] -fn sat_ackermann() { run!( run_sat_on("rsc/sat/long/Ackermann00.smt2") ) } +fn sat_ackermann() { + run!(run_sat_on("rsc/sat/long/Ackermann00.smt2")) +} #[test] -fn sat_file() { run!( run_sat_on("rsc/sat/long/file.smt2") ) } +fn sat_file() { + run!(run_sat_on("rsc/sat/long/file.smt2")) +} #[test] fn sat_rec_simpl() { - run!( run_sat_on("rsc/sat/long/recursive_simplifications.smt2") ) + run!(run_sat_on("rsc/sat/long/recursive_simplifications.smt2")) } #[test] -fn unsat() { run!( run_unsat() ) } +fn unsat() { + run!(run_unsat()) +} #[test] -fn err() { run!( run_err() ) } +fn err() { + run!(run_err()) +} macro_rules! map_err { ($e:expr, $msg:expr) => ( @@ -59,119 +69,118 @@ macro_rules! map_err { } fn run_err() -> Res<()> { - - let files = map_err!( - read_dir(err_files_dir), format!("while reading `{}`", err_files_dir) - ) ; - - for entry in files { - let entry = map_err!( - entry, "while reading entry" - ) ; - let file_name = format!("{}", entry.file_name().to_string_lossy()) ; - if map_err!( - entry.file_type(), "while reading entry (file type of `{}`)", file_name - ).is_file() { - println!("looking at `{}`", file_name) ; - let file = OpenOptions::new().read(true).open(entry.path()).chain_err( - || format!( "while opening file {}", file_name ) - ) ? ; - match read_and_work(file, true, true, true) { - Err(e) => println!("got {}", e), - Ok((model, _)) => return Err( - format!( - "expected error, got {}", - if model.is_some() { "sat" } else { "unsat" } - ).into() - ), - } + let files = map_err!( + read_dir(err_files_dir), + format!("while reading `{}`", err_files_dir) + ); + + for entry in files { + let entry = map_err!(entry, "while reading entry"); + let file_name = format!("{}", entry.file_name().to_string_lossy()); + if map_err!( + entry.file_type(), + "while reading entry (file type of `{}`)", + file_name + ).is_file() + { + println!("looking at `{}`", file_name); + let file = OpenOptions::new() + .read(true) + .open(entry.path()) + .chain_err(|| format!("while opening file {}", file_name))?; + match read_and_work(file, true, true, true) { + Err(e) => println!("got {}", e), + Ok((model, _)) => { + return Err(format!( + "expected error, got {}", + if model.is_some() { "sat" } else { "unsat" } + ).into()) + } + } + } } - } - Ok(()) + Ok(()) } fn run_sat() -> Res<()> { - - let files = map_err!( - read_dir(sat_files_dir), format!("while reading `{}`", sat_files_dir) - ) ; - - for entry in files { - let entry = map_err!( - entry, "while reading entry" - ) ; - let file_name = format!("{}", entry.file_name().to_string_lossy()) ; - if map_err!( - entry.file_type(), "while reading entry (file type of `{}`)", file_name - ).is_file() { - run_sat_on(& entry.path()) ? + let files = map_err!( + read_dir(sat_files_dir), + format!("while reading `{}`", sat_files_dir) + ); + + for entry in files { + let entry = map_err!(entry, "while reading entry"); + let file_name = format!("{}", entry.file_name().to_string_lossy()); + if map_err!( + entry.file_type(), + "while reading entry (file type of `{}`)", + file_name + ).is_file() + { + run_sat_on(&entry.path())? + } } - } - Ok(()) + Ok(()) } -fn run_sat_on + ?Sized>(path: & P) -> Res<()> { - let file_name = path.as_ref() ; - println!("looking at `{}`", file_name.display()) ; - let file = OpenOptions::new().read(true).open(file_name).chain_err( - || format!( "while opening file {}", file_name.display() ) - ) ? ; - let (model, instance) = read_and_work(file, true, true, true).chain_err( - || "while reading file and getting model" - ) ? ; - if let Some(model) = model { - let mut buff: Vec = vec![] ; - instance.write_model(& model, & mut buff).chain_err( - || "while writing model" - ) ? ; - let buff = map_err!( - String::from_utf8(buff), "converting model from bytes to utf8" - ) ; - ::hoice::check::do_it_from_str(file_name, & buff).chain_err( - || "while checking model" - ) ? ; - println!("- is okay") ; - Ok(()) - } else { - Err( - format!( "got unsat on `{}`, expected sat", file_name.display() ).into() - ) - } +fn run_sat_on + ?Sized>(path: &P) -> Res<()> { + let file_name = path.as_ref(); + println!("looking at `{}`", file_name.display()); + let file = OpenOptions::new() + .read(true) + .open(file_name) + .chain_err(|| format!("while opening file {}", file_name.display()))?; + let (model, instance) = read_and_work(file, true, true, true) + .chain_err(|| "while reading file and getting model")?; + if let Some(model) = model { + let mut buff: Vec = vec![]; + instance + .write_model(&model, &mut buff) + .chain_err(|| "while writing model")?; + let buff = map_err!( + String::from_utf8(buff), + "converting model from bytes to utf8" + ); + ::hoice::check::do_it_from_str(file_name, &buff).chain_err(|| "while checking model")?; + println!("- is okay"); + Ok(()) + } else { + Err(format!("got unsat on `{}`, expected sat", file_name.display()).into()) + } } fn run_unsat() -> Res<()> { - - let files = map_err!( - read_dir(unsat_files_dir), format!("while reading `{}`", unsat_files_dir) - ) ; - - for entry in files { - let entry = map_err!( - entry, "while reading entry" - ) ; - let file_name = format!("{}", entry.file_name().to_string_lossy()) ; - if map_err!( - entry.file_type(), "while reading entry (file type of `{}`)", file_name - ).is_file() { - println!("looking at `{}`", file_name) ; - let file = OpenOptions::new().read(true).open(entry.path()).chain_err( - || format!( "while opening file {}", file_name ) - ) ? ; - let (model, instance) = read_and_work(file, true, true, true) ? ; - if let Some(model) = model { - println!("sat") ; - instance.write_model(& model, & mut ::std::io::stdout()) ? ; - println!("") ; - return Err( - format!( "got sat on `{}`, expected unsat", file_name ).into() - ) - } else { - println!("- is okay") - } + let files = map_err!( + read_dir(unsat_files_dir), + format!("while reading `{}`", unsat_files_dir) + ); + + for entry in files { + let entry = map_err!(entry, "while reading entry"); + let file_name = format!("{}", entry.file_name().to_string_lossy()); + if map_err!( + entry.file_type(), + "while reading entry (file type of `{}`)", + file_name + ).is_file() + { + println!("looking at `{}`", file_name); + let file = OpenOptions::new() + .read(true) + .open(entry.path()) + .chain_err(|| format!("while opening file {}", file_name))?; + let (model, instance) = read_and_work(file, true, true, true)?; + if let Some(model) = model { + println!("sat"); + instance.write_model(&model, &mut ::std::io::stdout())?; + println!(""); + return Err(format!("got sat on `{}`, expected unsat", file_name).into()); + } else { + println!("- is okay") + } + } } - } - Ok(()) - -} \ No newline at end of file + Ok(()) +} From 7a50395c5e25b030456d04e100e9f9ba1c4159b4 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 28 Sep 2018 12:49:57 +0900 Subject: [PATCH 77/94] entry point unsat proof: compatible with assistant + reconstruction complete --- src/data/mod.rs | 87 ++++------------------- src/teacher/assistant.rs | 73 +++++++++++++------ src/term/tterms.rs | 27 +++++++ src/unsat_core/entry_points.rs | 126 +++++++++++++++++++++++++++++++-- src/var_to/vals.rs | 19 +++++ 5 files changed, 233 insertions(+), 99 deletions(-) diff --git a/src/data/mod.rs b/src/data/mod.rs index 1c0bdd8d..76618804 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -155,11 +155,6 @@ impl Data { self._profiler } - /// Clears the modified constraint set. - pub fn clear_modded(&mut self) { - self.cstr_info.clear_modded() - } - /// String representation of a constraint. #[allow(dead_code)] fn str_of(&self, c: CstrIdx) -> String { @@ -172,17 +167,6 @@ impl Data { ) } - // /// The sample graph, used for unsat cores. - // pub fn sample_graph(& mut self) -> Option { - // if let Some(ref mut graph) = self.graph { - // let mut old_graph = SampleGraph::new() ; - // ::std::mem::swap(graph, & mut old_graph) ; - // Some(old_graph) - // } else { - // None - // } - // } - /// Clones the new/modded constraints to create a new `Data`. /// /// **Clears the set of modified constraints.** @@ -203,8 +187,6 @@ impl Data { /// Merges the positive and negative samples in `other` to `self`. /// - /// Does not propagate. - /// /// Returns the number of new positive/negative examples. pub fn merge_samples(&mut self, other: Data) -> Res<(usize, usize)> { for (pred, samples) in other.pos.into_index_iter() { @@ -217,13 +199,13 @@ impl Data { self.staged.add_neg(pred, sample); } } - // if let Some(graph) = self.graph.as_mut() { - // if let Some(other) = other.graph { - // graph.merge(other) - // } else { - // bail!("inconsistent sample dependency tracking") - // } - // } + if let Some(e) = self.entry_points.as_mut() { + if let Some(other) = other.entry_points { + e.merge(other) + } else { + bail!("failed to merge entry points while merging data samples") + } + } self.propagate() } @@ -231,7 +213,7 @@ impl Data { /// /// Remove all constraints that this constraint makes useless, including the /// one(s) it is equal to. - pub fn cstr_useful(&mut self, index: CstrIdx) -> Res { + fn cstr_useful(&mut self, index: CstrIdx) -> Res { profile! { self tick "constraint subsumption" } let mut to_check = CstrSet::new(); scoped! { @@ -355,37 +337,18 @@ impl Data { /// The `clause` input is necessary for unsat core extraction. /// /// Does not propagate. - pub fn add_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { - // println!("add_pos ({} {})", self.instance[pred], args); - // println!( - // "#{} {}", - // clause, - // self.instance[clause] - // .to_string_info(self.instance.preds()) - // .unwrap() - // ); + fn add_pos(&mut self, clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { if self.instance[clause].lhs_preds().is_empty() { // println!("positive clause"); if let Some(e) = self.entry_points.as_mut() { e.register(Sample::new(pred, args.clone())) } } - // if self.add_pos_untracked( pred, args.clone() ) { - // if let Some(graph) = self.graph.as_mut() { - // graph.add( - // pred, self.instance[clause].rhs().unwrap().1.clone(), - // args.clone(), clause, PrdHMap::new() - // ) - // } - // true - // } else { - // false - // } self.add_pos_untracked(pred, args) } /// Adds a positive example. /// - /// Does track dependencies for unsat core. + /// Does track dependencies for unsat proof. /// /// Used by the learner(s). pub fn add_pos_untracked(&mut self, pred: PrdIdx, args: VarVals) -> bool { @@ -397,36 +360,12 @@ impl Data { /// The `clause` input is necessary for unsat core extraction. /// /// Does not propagate. - pub fn add_neg(&mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { - // if self.add_neg_untracked( pred, args.clone() ) { - // if let Some(graph) = self.graph.as_mut() { - // let mut lhs = PrdHMap::with_capacity(1) ; - // let mut farg_map = HConMap::new() ; - // // debug_assert_eq! { 1, self.instance[clause].lhs_preds().len() } - - // let ( - // p, argss - // ) = self.instance[clause].lhs_preds().iter().next().unwrap() ; - // debug_assert_eq! { pred, * p } - // debug_assert_eq! { 1, argss.len() } - // let prev = farg_map.insert( - // argss.iter().next().unwrap().clone(), args - // ) ; - // debug_assert! { prev.is_none() } - - // let prev = lhs.insert(pred, farg_map) ; - // debug_assert! { prev.is_none() } - // graph.add_neg(clause, lhs) - // } - // true - // } else { - // false - // } + fn add_neg(&mut self, _clause: ClsIdx, pred: PrdIdx, args: VarVals) -> bool { self.add_neg_untracked(pred, args) } /// Adds a negative example. /// - /// Does track dependencies for unsat core. + /// Does track dependencies for unsat proof. /// /// Used by the learner(s). pub fn add_neg_untracked(&mut self, pred: PrdIdx, args: VarVals) -> bool { @@ -841,7 +780,7 @@ impl Data { /// /// Removes samples that are known to be true/false. Returns `None` if the /// constraint is trivial. - pub fn prune_cstr( + fn prune_cstr( &mut self, lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)>, diff --git a/src/teacher/assistant.rs b/src/teacher/assistant.rs index 59f7e380..f0f78b02 100644 --- a/src/teacher/assistant.rs +++ b/src/teacher/assistant.rs @@ -2,15 +2,24 @@ use common::*; use data::{Data, Sample}; +use var_to::vals::RVarVals; /// Result of trying to force a sample positive/negative. pub enum ForceRes { /// Failure. None, /// Sample was classified as positive. - Pos { sample: Sample, clause: ClsIdx }, + Pos { + pred: PrdIdx, + sample: RVarVals, + clause: ClsIdx, + }, /// Sample was classified as negative. - Neg { sample: Sample, clause: ClsIdx }, + Neg { + pred: PrdIdx, + sample: RVarVals, + clause: ClsIdx, + }, } /// Stores data from a positive / strict negative clause. @@ -271,11 +280,11 @@ impl Assistant { // Discard the constraint, regardless of what will happen. profile! { self tick "data" } data.tautologize(cstr)?; - for (Sample { pred, args }, clause) in pos.drain(0..) { - data.add_pos(clause, pred, args); + for (pred, args, clause) in pos.drain(0..) { + data.add_data(clause, vec![], Some((pred, args)))?; } - for (Sample { pred, args }, clause) in neg.drain(0..) { - data.add_neg(clause, pred, args); + for (pred, args, clause) in neg.drain(0..) { + data.add_data(clause, vec![(pred, args)], None)?; } data.propagate()?; profile! { self mark "data" } @@ -286,14 +295,22 @@ impl Assistant { if let Some(&Sample { pred, ref args }) = data.constraints[cstr].rhs() { match self.try_force(data, pred, args)? { ForceRes::None => (), - ForceRes::Pos { sample, clause } => { - pos.push((sample, clause)); + ForceRes::Pos { + pred, + sample, + clause, + } => { + pos.push((pred, sample, clause)); // Constraint is trivial, move on. trivial = true } - ForceRes::Neg { sample, clause } => { + ForceRes::Neg { + pred, + sample, + clause, + } => { rhs_false = true; - neg.push((sample, clause)) + neg.push((pred, sample, clause)) } } } @@ -309,9 +326,17 @@ impl Assistant { lhs_unknown += 1; lhs_trivial = false } - ForceRes::Pos { sample, clause } => pos.push((sample, clause)), - ForceRes::Neg { sample, clause } => { - neg.push((sample, clause)); + ForceRes::Pos { + pred, + sample, + clause, + } => pos.push((pred, sample, clause)), + ForceRes::Neg { + pred, + sample, + clause, + } => { + neg.push((pred, sample, clause)); trivial = true; // Constraint is trivial, move on. // break 'lhs @@ -411,7 +436,7 @@ impl Assistant { solver!(pop); if sat { - let args = if let Some(vars) = vars { + let sample = if let Some(vars) = vars { let mut nu_vals = var_to::vals::RVarVals::with_capacity(vals.len()); for (idx, val) in vals.index_iter() { if vars.contains(&idx) { @@ -420,24 +445,30 @@ impl Assistant { nu_vals.push(val::none(val.typ())) } } - var_to::vals::new(nu_vals) + nu_vals } else { - vals.clone() + vals.get().clone() }; self.solver.comment_args(format_args!( "success, yielding {} sample ({} {})", if *pos { "positive" } else { "negative" }, self.instance[pred], - args + sample ))?; - let sample = Sample { pred, args }; - if *pos { - return Ok(ForceRes::Pos { sample, clause }); + return Ok(ForceRes::Pos { + pred, + sample, + clause, + }); } else { - return Ok(ForceRes::Neg { sample, clause }); + return Ok(ForceRes::Neg { + pred, + sample, + clause, + }); } } } diff --git a/src/term/tterms.rs b/src/term/tterms.rs index 9b3ce36c..06e89c2f 100644 --- a/src/term/tterms.rs +++ b/src/term/tterms.rs @@ -841,6 +841,33 @@ impl TTerms { TTerms::Dnf { disj }.simplify() } + /// The predicate applications appearing in the top term. + pub fn pred_apps(&self) -> Vec<&PredApps> { + let mut res = vec![]; + match self { + TTerms::True | TTerms::False => (), + TTerms::Conj { tterms, .. } => if !tterms.preds().is_empty() { + res.push(tterms.preds()) + }, + TTerms::Disj { + tterms, neg_preds, .. + } => { + if !tterms.preds().is_empty() { + res.push(tterms.preds()); + } + if !neg_preds.is_empty() { + res.push(neg_preds) + } + } + TTerms::Dnf { disj } => for (_, tterms) in disj { + if !tterms.preds().is_empty() { + res.push(tterms.preds()) + } + }, + } + res + } + /// Predicates appearing in the top terms. pub fn preds(&self) -> PrdSet { let mut res = PrdSet::new(); diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index f24bb42e..0aa731d5 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -32,6 +32,16 @@ impl EntryPoints { } } + /// Merges with another entry point tracker. + pub fn merge(&mut self, other: Self) { + for sample in other.real_pos_samples { + self.real_pos_samples.insert(sample); + } + for (sample, set) in other.pos_sample_map { + self.pos_sample_map.entry(sample).or_insert(set); + } + } + /// String representation. pub fn to_string(&self, instance: &Instance) -> String { let mut s = "real_pos_samples:".to_string(); @@ -164,6 +174,7 @@ struct Reconstr<'a> { safe_preds: PrdSet, /// Predicates that are defined and can be used in positive samples. pos_preds: PrdSet, + nu_pos_preds: PrdSet, /// Original instance. original: &'a Instance, /// Instance. @@ -186,6 +197,15 @@ impl<'a> Reconstr<'a> { to_do: Vec, solver: &'a mut Slvr, ) -> Self { + let nu_pos_preds: PrdSet = original + .pos_clauses() + .iter() + .map(|idx| { + original[*idx] + .rhs() + .expect("positive clauses necessarily have a RHS") + .0 + }).collect(); let mut safe_preds = PrdSet::new(); let mut pos_preds = PrdSet::new(); let mut fp = false; @@ -196,6 +216,7 @@ impl<'a> Reconstr<'a> { continue; } else if let Some(def) = pred.def() { if def.preds().is_empty() { + fp = false; pos_preds.insert(pred.idx); } if def.preds().is_subset(&safe_preds) { @@ -207,15 +228,20 @@ impl<'a> Reconstr<'a> { } if_log! { @3 - log! { @3 |=> "safe predicates:" } - for pred in &safe_preds { - log! { @3 |=> " {}", instance[*pred] } + if safe_preds.is_empty() { + log! { @3 |=> "no safe predicates" } + } else { + log! { @3 |=> "safe predicates:" } + for pred in &safe_preds { + log! { @3 |=> " {}", instance[*pred] } + } } } Reconstr { safe_preds, pos_preds, + nu_pos_preds, original, instance, to_do, @@ -312,9 +338,98 @@ impl<'a> Reconstr<'a> { } } + /// Reconstructs a sample using the definitions of the positive predicates. + fn work_on_defs(&mut self, pred: PrdIdx, vals: &VarVals) -> Res { + let mut current_pred = PrdSet::with_capacity(1); + current_pred.insert(pred); + + log! { @4 "trying to reconstruct from {} definition(s)", self.nu_pos_preds.len() } + + 'find_pos_pred: for pos_pred in &self.nu_pos_preds { + let pos_pred = *pos_pred; + if let Some(def) = self.instance[pos_pred].def() { + let mut pred_args = None; + for pred_apps in def.pred_apps() { + 'current_apps: for (p, argss) in pred_apps { + if self.safe_preds.contains(p) { + continue 'current_apps; + } else if *p == pred { + for args in argss { + let prev = ::std::mem::replace(&mut pred_args, Some(args)); + if prev.is_some() { + continue 'find_pos_pred; + } + } + } else { + continue 'find_pos_pred; + } + } + } + + let pred_args = if let Some(args) = pred_args { + args + } else { + continue 'find_pos_pred; + }; + + log! { @5 + "positive predicate {} mentions {}: ({} {})", + self.instance[pos_pred], self.instance[pred], self.instance[pred], pred_args + } + + self.solver.push(1)?; + + for (var, typ) in self.original[pos_pred].sig.index_iter() { + self.solver.declare_const(&var, typ.get())?; + } + + self.solver + .assert_with(def, &(¤t_pred, &PrdSet::new(), self.instance.preds()))?; + + self.solver.assert(&smt::EqConj::new(pred_args, vals))?; + + let sat = self.solver.check_sat()?; + + let model = if sat { + let model = self.solver.get_model()?; + Some(smt::FullParser.fix_model(model)?) + } else { + None + }; + + self.solver.pop(1)?; + + if let Some(model) = model { + log! { @5 "sat, getting sample" } + let vals = Cex::of_pred_model(&self.original[pos_pred].sig, model, true)?; + let vals = var_to::vals::new(vals); + self.samples.insert(Sample::new(pos_pred, vals)); + return Ok(true); + } else { + log! { @5 "unsat" } + } + } + } + Ok(false) + } + /// Reconstructs a single positive sample. fn work_on_sample(&mut self, Sample { pred, args }: Sample) -> Res<()> { log! { @3 | "working on ({} {})", self.instance[pred], args } + + // Already an entry point for the original instance? + if self.nu_pos_preds.contains(&pred) { + log! { @4 | "already a legal entry point" } + self.samples.insert(Sample::new(pred, args)); + return Ok(()); + } + + // Try reconstructing by using predicate definitions directly. + let done = self.work_on_defs(pred, &args)?; + if done { + return Ok(()); + } + let (pos, others) = self.clauses_for(pred); log! { @4 | "{} positive clause(s), {} usable clause(s)", pos.len(), others.len() } if_log! { @5 @@ -362,7 +477,10 @@ impl<'a> Reconstr<'a> { } while let Some(sample) = self.to_do.pop() { - self.work_on_sample(sample.clone())?; + if let Err(e) = self.work_on_sample(sample.clone()) { + print_err(&e); + self.samples.insert(sample); + } } self.solver.reset()?; diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index ef5703ef..259e8894 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -126,6 +126,25 @@ impl RVarVals { Ok(slf) } + /// Constructor from a model for a predicate. + pub fn of_pred_model(sig: &Sig, model: Vec<(VarIdx, T, Val)>, partial: bool) -> Res { + let mut slf = RVarVals { + map: sig + .iter() + .map(|typ| { + if partial { + val::none(typ.clone()) + } else { + typ.default_val() + } + }).collect(), + }; + for (var, _, val) in model { + slf[var] = val.cast(&sig[var])?; + } + Ok(slf) + } + /// Evaluates some arguments and yields the resulting `VarMap`. pub fn apply_to(&self, args: &VarMap<::term::Term>) -> ::errors::Res { let mut res = Self::with_capacity(args.len()); From a7eca0d3f3866360f7fffeedaaf1675407d38163 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 28 Sep 2018 12:56:01 +0900 Subject: [PATCH 78/94] reg tests for unsat proof extraction --- rsc/unsat/proof_1.smt2 | 33 +++++++++++++++++++++ rsc/unsat/proof_2.smt2 | 46 +++++++++++++++++++++++++++++ rsc/unsat/proof_3.smt2 | 67 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 rsc/unsat/proof_1.smt2 create mode 100644 rsc/unsat/proof_2.smt2 create mode 100644 rsc/unsat/proof_3.smt2 diff --git a/rsc/unsat/proof_1.smt2 b/rsc/unsat/proof_1.smt2 new file mode 100644 index 00000000..595f343c --- /dev/null +++ b/rsc/unsat/proof_1.smt2 @@ -0,0 +1,33 @@ +(set-option :produce-proofs true) + +(set-logic HORN) + +(declare-fun mc_91_out (Int Int) Bool) + +(assert + (! (forall ((n Int)) + (=> + (> n 100) + (mc_91_out n (- n 10)) + ) + ) :named a_1) +) +(assert + (! (forall ( (n Int) (tmp Int) (res Int) ) + (=> + (and (<= n 100) (mc_91_out (+ n 11) tmp) (mc_91_out tmp res)) + (mc_91_out n tmp) + ) + ) :named a_2) +) +(assert + (! (forall ( (m Int) (res Int) ) + (=> + (and (<= m 101) (mc_91_out m res)) + (<= res 100) + ) + ) :named a_3) +) + +(check-sat) +(get-proof) \ No newline at end of file diff --git a/rsc/unsat/proof_2.smt2 b/rsc/unsat/proof_2.smt2 new file mode 100644 index 00000000..42205331 --- /dev/null +++ b/rsc/unsat/proof_2.smt2 @@ -0,0 +1,46 @@ +(set-option :produce-proofs true) + +(set-logic HORN) + +(declare-fun useless (Int Int) Bool) +(declare-fun mc_91_out (Int Int) Bool) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> n 100) + ) + (useless n m) + ) + ) +) + +(assert + (forall ((n Int) (m Int)) + (=> + (useless n m) + (mc_91_out n (- n 10)) + ) + ) +) + +(assert + (forall ( (n Int) (tmp Int) (res Int) ) + (=> + (and (<= n 100) (mc_91_out (+ n 11) tmp) (mc_91_out tmp res)) + (mc_91_out n tmp) + ) + ) +) +(assert + (forall ( (m Int) (res Int) ) + (=> + (and (<= m 101) (mc_91_out m res)) + (<= res 100) + ) + ) +) + +(check-sat) +(get-proof) \ No newline at end of file diff --git a/rsc/unsat/proof_3.smt2 b/rsc/unsat/proof_3.smt2 new file mode 100644 index 00000000..f32103b2 --- /dev/null +++ b/rsc/unsat/proof_3.smt2 @@ -0,0 +1,67 @@ +(set-option :produce-proofs true) + +(set-logic HORN) + +(declare-fun useless (Int Int) Bool) +(declare-fun useless_2 (Int Int) Bool) +(declare-fun mc_91_out (Int Int) Bool) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> n 100) + (> m 7) + (<= m (+ n 2)) + ) + (useless n m) + ) + ) +) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> n 100) + (> m 9) + (< m 100000) + (<= m (+ n 2)) + ) + (useless_2 n m) + ) + ) +) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (useless n m) + (useless_2 n m) + (<= m (+ n 2)) + ) + (mc_91_out n (- n 10)) + ) + ) +) + +(assert + (forall ( (n Int) (tmp Int) (res Int) ) + (=> + (and (<= n 100) (mc_91_out (+ n 11) tmp) (mc_91_out tmp res)) + (mc_91_out n tmp) + ) + ) +) +(assert + (forall ( (m Int) (res Int) ) + (=> + (and (<= m 101) (mc_91_out m res)) + (<= res 100) + ) + ) +) + +(check-sat) +(get-proof) \ No newline at end of file From e0d1ce9da7930aa3ac5b10e610646d7c9e4bbceb Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 28 Sep 2018 13:35:11 +0900 Subject: [PATCH 79/94] bug fix in entry points reconstruction, reg test --- rsc/unsat/proof_4.smt2 | 80 ++++++++++++++++++++++++++++++++++ src/unsat_core/entry_points.rs | 44 +++++++++++++------ 2 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 rsc/unsat/proof_4.smt2 diff --git a/rsc/unsat/proof_4.smt2 b/rsc/unsat/proof_4.smt2 new file mode 100644 index 00000000..9a8c6584 --- /dev/null +++ b/rsc/unsat/proof_4.smt2 @@ -0,0 +1,80 @@ +(set-option :produce-proofs true) + +(set-logic HORN) + +(declare-fun useless (Int Int) Bool) +(declare-fun useless_2 (Int Int) Bool) +(declare-fun useless_3 (Int Int) Bool) +(declare-fun mc_91_out (Int Int) Bool) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> n 100) + (> m 7) + (<= m (+ n 2)) + ) + (useless n m) + ) + ) +) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> m 9) + (< m 100000) + (useless n m) + ) + (useless_2 n m) + ) + ) +) + +(assert + (forall ((n Int) (m Int)) + (=> + (and + (> m 2) + (useless n m) + ) + (useless_3 n m) + ) + ) +) + +(assert + (forall ((n Int) (m_1 Int) (m_2 Int)) + (=> + (and + (useless_3 n m_1) + (useless_2 n m_2) + (<= m_1 (+ n 2)) + (<= m_2 (+ n 2)) + ) + (mc_91_out n (- n 10)) + ) + ) +) + +(assert + (forall ( (n Int) (tmp Int) (res Int) ) + (=> + (and (<= n 100) (mc_91_out (+ n 11) tmp) (mc_91_out tmp res)) + (mc_91_out n tmp) + ) + ) +) +(assert + (forall ( (m Int) (res Int) ) + (=> + (and (<= m 101) (mc_91_out m res)) + (<= res 100) + ) + ) +) + +(check-sat) +(get-proof) diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index 0aa731d5..ccd7e01e 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -174,7 +174,6 @@ struct Reconstr<'a> { safe_preds: PrdSet, /// Predicates that are defined and can be used in positive samples. pos_preds: PrdSet, - nu_pos_preds: PrdSet, /// Original instance. original: &'a Instance, /// Instance. @@ -197,7 +196,7 @@ impl<'a> Reconstr<'a> { to_do: Vec, solver: &'a mut Slvr, ) -> Self { - let nu_pos_preds: PrdSet = original + let pos_preds: PrdSet = original .pos_clauses() .iter() .map(|idx| { @@ -207,7 +206,6 @@ impl<'a> Reconstr<'a> { .0 }).collect(); let mut safe_preds = PrdSet::new(); - let mut pos_preds = PrdSet::new(); let mut fp = false; while !fp { fp = true; @@ -215,10 +213,6 @@ impl<'a> Reconstr<'a> { if safe_preds.contains(&pred.idx) { continue; } else if let Some(def) = pred.def() { - if def.preds().is_empty() { - fp = false; - pos_preds.insert(pred.idx); - } if def.preds().is_subset(&safe_preds) { fp = false; safe_preds.insert(pred.idx); @@ -241,7 +235,6 @@ impl<'a> Reconstr<'a> { Reconstr { safe_preds, pos_preds, - nu_pos_preds, original, instance, to_do, @@ -279,7 +272,7 @@ impl<'a> Reconstr<'a> { /// Returns `true` if the reconstruction was positive. If it was, (potentially) new positive /// samples have been added to `self.samples`. fn work_on_clause(&mut self, pred: PrdIdx, sample: &VarVals, clause: ClsIdx) -> Res { - debug_assert! { self.instance[clause].rhs().map(|(p, _)| p == pred).unwrap_or(false) } + debug_assert! { self.original[clause].rhs().map(|(p, _)| p == pred).unwrap_or(false) } self.solver.push(1)?; // Declare clause variables. self.original[clause].declare(self.solver)?; @@ -297,7 +290,7 @@ impl<'a> Reconstr<'a> { } } - if let Some((p, args)) = self.instance[clause].rhs() { + if let Some((p, args)) = self.original[clause].rhs() { debug_assert_eq! { pred, p } self.solver.assert(&smt::EqConj::new(args, &sample))? } else { @@ -329,7 +322,21 @@ impl<'a> Reconstr<'a> { samples.push(Sample::new(*pred, var_to::vals::new(sample))) } if self.pos_preds.contains(pred) { + if_log! { @5 + log! { @5 |=> "generated positive samples:" } + for sample in &samples { + log! { @5 |=> " ({} {})", self.original[sample.pred], sample.args } + } + } self.samples.extend(samples.into_iter()) + } else { + if_log! { @5 + log! { @5 |=> "generated new samples:" } + for sample in &samples { + log! { @5 |=> " ({} {})", self.original[sample.pred], sample.args } + } + } + self.to_do.extend(samples.into_iter()) } } Ok(true) @@ -343,9 +350,9 @@ impl<'a> Reconstr<'a> { let mut current_pred = PrdSet::with_capacity(1); current_pred.insert(pred); - log! { @4 "trying to reconstruct from {} definition(s)", self.nu_pos_preds.len() } + log! { @4 "trying to reconstruct from {} definition(s)", self.pos_preds.len() } - 'find_pos_pred: for pos_pred in &self.nu_pos_preds { + 'find_pos_pred: for pos_pred in &self.pos_preds { let pos_pred = *pos_pred; if let Some(def) = self.instance[pos_pred].def() { let mut pred_args = None; @@ -418,7 +425,7 @@ impl<'a> Reconstr<'a> { log! { @3 | "working on ({} {})", self.instance[pred], args } // Already an entry point for the original instance? - if self.nu_pos_preds.contains(&pred) { + if self.pos_preds.contains(&pred) { log! { @4 | "already a legal entry point" } self.samples.insert(Sample::new(pred, args)); return Ok(()); @@ -476,6 +483,17 @@ impl<'a> Reconstr<'a> { self.instance.write_definitions(self.solver, "", &model)? } + if_log! { @4 + log! { @4 |=> "{} safe preds", self.safe_preds.len() } + for pred in &self.safe_preds { + log! { @4 |=> " {}", self.instance[*pred] } + } + log! { @4 |=> "{} pos preds", self.pos_preds.len() } + for pred in &self.pos_preds { + log! { @4 |=> " {}", self.instance[*pred] } + } + } + while let Some(sample) = self.to_do.pop() { if let Err(e) = self.work_on_sample(sample.clone()) { print_err(&e); From 6d762f1c6753d2ad46030c9eb8daffc723a12466 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Fri, 28 Sep 2018 15:10:22 +0900 Subject: [PATCH 80/94] bug fix in unsat proof tracking --- rsc/unsat/proof_5.smt2 | 143 +++++++++++++++++++++++++++++++++ src/data/mod.rs | 16 +++- src/hoice.rs | 11 +-- src/unsat_core/entry_points.rs | 86 +++++++++++++++++--- src/unsat_core/mod.rs | 7 +- 5 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 rsc/unsat/proof_5.smt2 diff --git a/rsc/unsat/proof_5.smt2 b/rsc/unsat/proof_5.smt2 new file mode 100644 index 00000000..d2df4c40 --- /dev/null +++ b/rsc/unsat/proof_5.smt2 @@ -0,0 +1,143 @@ +(set-option :produce-proofs true) + +(set-logic HORN) + +(declare-fun |omega_without_checking_1162$unknown:41| ( Int Int Int ) Bool) +(declare-fun |omega_1032$unknown:37| ( Int Int Int ) Bool) +(declare-fun |f_1034$unknown:19| ( Int Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |omega_1032$unknown:33| ( Int Int Int ) Bool) +(declare-fun |f_1034$unknown:23| ( Int Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |fail$unknown:25| ( Int ) Bool) + +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) ) + (=> + (and + (|f_1034$unknown:23| M L K J I H G F E D C B A) + true + ) + (|f_1034$unknown:19| M L K J I H G F E D C B A) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) ) + (=> + (and + (|f_1034$unknown:19| A B V N M E L K J I F H G) + (and (= L 0) + (= K 0) + (= J 0) + (= I 0) + (= H 0) + (= G 0) + (= F 0) + (= E 0) + (= D 0) + (= C 0) + (= R 0) + (= Q 0) + (= P 0) + (= O 0) + (= N 0) + (= U 1) + (= T 0) + (= S 0) + (= M 0)) + ) + (|omega_without_checking_1162$unknown:41| A B V) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) ) + (=> + (and + (|omega_1032$unknown:33| A B G) + (|omega_without_checking_1162$unknown:41| E D C) + (= F 1) + ) + (|omega_without_checking_1162$unknown:41| A B G) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) ) + (=> + (and + (|omega_1032$unknown:37| C B A) + (and (= D 1) (not (= 0 A))) + ) + (|fail$unknown:25| D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) ) + (=> + (and + (|omega_1032$unknown:37| D C B) + (and (= A 1) (= 0 B)) + ) + (|omega_1032$unknown:33| D C B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (v_4 Int) ) + (=> + (and + (|omega_without_checking_1162$unknown:41| C B A) + (and (= D 1) (= v_4 C)) + ) + (|omega_1032$unknown:37| C v_4 D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) ) + (=> + (and + (and (= M 0) + (= L 0) + (= K 0) + (= J 0) + (= I 0) + (= H 0) + (= G 0) + (= F 0) + (= E 0) + (= D 0) + (= C 0) + (= B 0) + (= A 0) + (= S 0) + (= R 0) + (= Q 0) + (= P 0) + (= O 0) + (= W 1) + (= V 0) + (= U 0) + (= T 0) + (= N 0)) + ) + (|f_1034$unknown:23| W V U T S A R Q P O B N M) + ) + ) +) +(assert + (forall ( (A Int) ) + (=> + (and + (|fail$unknown:25| A) + true + ) + false + ) + ) +) + +(check-sat) +(get-proof) +(exit) \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index 76618804..45400547 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -782,6 +782,7 @@ impl Data { /// constraint is trivial. fn prune_cstr( &mut self, + clause: ClsIdx, lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)>, ) -> Res, Option)>> { @@ -816,6 +817,10 @@ impl Data { let nu_rhs = if let Some((pred, args)) = rhs { let (args, is_new) = var_to::vals::new_is_new(args.clone()); + if nu_lhs.is_empty() { + self.add_pos(clause, pred, args.clone()); + } + let args = if conf.teacher.partial || !is_new { if args.set_subsumed(&self.pos[pred]) { profile! { self mark "add cstr", "pre-checks" } @@ -867,7 +872,7 @@ impl Data { /// - propagates staged samples beforehand fn add_cstr( &mut self, - _clause: ClsIdx, + clause: ClsIdx, lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)>, ) -> Res { @@ -891,12 +896,15 @@ impl Data { profile! { self tick "add cstr", "pre-checks" } - let (nu_lhs, nu_rhs) = if let Some(res) = self.prune_cstr(lhs, rhs)? { + let (nu_lhs, nu_rhs) = if let Some(res) = self.prune_cstr(clause, lhs, rhs)? { res } else { return Ok(false); }; + let (pos, neg) = self.propagate()?; + let nu_stuff = pos != 0 || neg != 0; + let mut constraint = Constraint::new(nu_lhs, nu_rhs); constraint.check().chain_err(|| { format!( @@ -911,7 +919,7 @@ impl Data { match constraint.try_trivial() { Either::Left((Sample { pred, args }, pos)) => { let is_new = self.staged.add(pred, args, pos); - Ok(is_new) + Ok(nu_stuff || is_new) } Either::Right(false) => { // Handles linking and constraint info registration. @@ -921,7 +929,7 @@ impl Data { self.check("after add_cstr")?; - Ok(is_new) + Ok(nu_stuff || is_new) } Either::Right(true) => unsat!("by `true => false` in constraint (data, add_cstr)"), } diff --git a/src/hoice.rs b/src/hoice.rs index fa2ff588..2a9ce976 100644 --- a/src/hoice.rs +++ b/src/hoice.rs @@ -202,12 +202,13 @@ pub fn read_and_work( if !maybe_model.is_unsat() { println!("sat") } else { + use unsat_core::UnsatRes; println!("unsat"); - if unsat.as_ref().map(|unsat| unsat.is_none()).unwrap_or(true) - && (instance.unsat_cores() || instance.proofs()) - { - bail!("unsat cores/proofs from preprocessing") - } + unsat = Some(if instance.proofs() { + UnsatRes::empty_entry() + } else { + UnsatRes::None + }) } maybe_model.into_option() } else { diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index ccd7e01e..2ede2a32 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -69,18 +69,32 @@ impl EntryPoints { .pos_sample_map .remove(&sample) .unwrap_or_else(SampleSet::new); - if self.real_pos_samples.contains(dep) { - set.insert(dep.clone()); - } else if let Some(dep_set) = self.pos_sample_map.get(dep) { - for sample in dep_set { - set.insert(sample.clone()); + + use var_to::vals::SubsumeExt; + let mut real_dep = None; + for s in &self.real_pos_samples { + if s.args.subsumes(&dep.args) { + real_dep = Some(s.clone()); + break; } + } + + if let Some(res) = real_dep { + set.insert(res); } else { - bail!( - "trying to register dependency to unknown positive sample {}", - dep + set.extend( + self.pos_sample_map + .get(dep) + .ok_or_else::(|| { + format!( + "trying to register dependency to unknown positive sample ({})", + sample + ).into() + })?.iter() + .cloned(), ) - }; + } + let prev = self.pos_sample_map.insert(sample, set); debug_assert! { prev.is_none() } Ok(()) @@ -97,7 +111,7 @@ impl EntryPoints { .map(|entry| entry.clone().into()) .ok_or_else::(|| { format!( - "trying to recover entry points for unknown sample {}", + "trying to recover entry points for unknown sample ({})", sample ).into() }) @@ -476,8 +490,60 @@ impl<'a> Reconstr<'a> { ) } + /// Generates a sample for each positive clause. + pub fn samples_of_pos_clauses(&mut self) -> Res<()> { + for clause in self.original.pos_clauses() { + let clause = *clause; + let (pred, args) = self.original[clause].rhs().ok_or_else::(|| { + "illegal (original) instance state, positive clause has no RHS".into() + })?; + + debug_assert! { self.original[clause].lhs_preds().is_empty() } + + self.solver.push(1)?; + // Declare clause variables. + self.original[clause].declare(self.solver)?; + // Assert lhs terms. + for term in self.original[clause].lhs_terms() { + self.solver.assert(&smt::SmtTerm::new(term))?; + } + + let sat = self.solver.check_sat()?; + + let model = if sat { + let model = self.solver.get_model()?; + Some(smt::FullParser.fix_model(model)?) + } else { + None + }; + + self.solver.pop(1)?; + + if let Some(model) = model { + let model = Cex::of_model(self.original[clause].vars(), model, true)?; + // Reconstruct all LHS applications. + let mut sample = VarMap::with_capacity(args.len()); + for arg in args.iter() { + let val = arg.eval(&model)?; + sample.push(val) + } + self.samples + .insert(Sample::new(pred, var_to::vals::new(sample))); + } + } + + Ok(()) + } + /// Reconstructs the positive samples. pub fn work(mut self) -> Res { + if self.to_do.is_empty() { + log! { @4 | "no samples to reconstruct, generating samples from positive clauses" } + self.samples_of_pos_clauses()?; + log! { @4 | "done, generated {} sample(s)", self.samples.len() } + return Ok(self.samples); + } + if !self.safe_preds.is_empty() { let model = self.instance.extend_model(PrdHMap::new())?; self.instance.write_definitions(self.solver, "", &model)? diff --git a/src/unsat_core/mod.rs b/src/unsat_core/mod.rs index 044611f8..63843bb3 100644 --- a/src/unsat_core/mod.rs +++ b/src/unsat_core/mod.rs @@ -7,7 +7,7 @@ use common::*; pub mod entry_points; pub mod sample_graph; -use self::entry_points::Entry; +pub use self::entry_points::Entry; pub use self::sample_graph::SampleGraph; // use self::sample_graph::UnsatProof; @@ -35,6 +35,11 @@ impl UnsatRes { } } + /// Empty entry constructor. + pub fn empty_entry() -> Self { + UnsatRes::Entry(Entry::new(entry_points::SampleSet::new())) + } + /// True if none. pub fn is_none(&self) -> bool { match self { From ba12f6193ef27ee867e35def9f6351d43891ce98 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 2 Oct 2018 15:36:11 +0900 Subject: [PATCH 81/94] minor log tweaks --- src/learning/ice/quals.rs | 30 +++++++++++++++--------------- src/teacher/mod.rs | 11 ++++++----- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/learning/ice/quals.rs b/src/learning/ice/quals.rs index f0cefd51..d923ff4c 100644 --- a/src/learning/ice/quals.rs +++ b/src/learning/ice/quals.rs @@ -194,20 +194,20 @@ fn qual_of_terms Res<()>>( || (nu_vars.len() <= 2) || (nu_vars.len() < old_vars_1.len() + old_vars_2.len()); - if use_qual { - log! { @4 - "from {}", term ; - " {}", othr - } - } else { - // log! { @1 - // " " ; - // "skipping" ; - // "from {}", term ; - // " {}", othr ; - // " -> {}", nu_lhs - // } - } + // if use_qual { + // log! { @4 + // "from {}", term ; + // " {}", othr + // } + // } else { + // // log! { @1 + // // " " ; + // // "skipping" ; + // // "from {}", term ; + // // " {}", othr ; + // // " -> {}", nu_lhs + // // } + // } if use_qual { let op = match (op_1, op_2) { @@ -229,7 +229,7 @@ fn qual_of_terms Res<()>>( ); f(nu_term.clone())?; - log! { @4 " -> {}", nu_term } + // log! { @4 " -> {}", nu_term } if op_1 == Op::Eql { let nu_lhs = term::sub(vec![othr_args[0].clone(), term_args[0].clone()]); diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 5a3b47d6..2ca3b71a 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -954,7 +954,7 @@ impl<'a> Teacher<'a> { just_try: bool, ) -> Res> { if let Some((actlit, bias)) = bias { - log! { @debug + log! { @debug | " checksat with bias {}", bias.to_string(& self.instance) } self.solver.comment(&format!( @@ -965,17 +965,17 @@ impl<'a> Teacher<'a> { let sat = { self.solver.check_sat_act(Some(&actlit))? }; if sat { - log! { @debug " sat, getting cex" } + log! { @debug | " sat, getting cex" } profile!{ self mark "cexs", "biased check-sat" } let cex = self.get_bias_cex(clause, &bias)?; - log! { @debug " {}", cex } + log! { @debug | " {}", cex } self.solver.de_actlit(actlit)?; Ok(Some((cex, bias))) } else { Ok(None) } } else { - log! { @debug " checksat" } + log! { @debug | " checksat" } let sat = profile! { self wrap { @@ -1020,7 +1020,7 @@ impl<'a> Teacher<'a> { }?; if sat { - log! { @debug " sat, getting cex" } + log! { @debug | " sat, getting cex" } let bias = if self.instance[clause].is_positive() { Bias::Lft } else if self.instance[clause].is_strict_neg() { @@ -1038,6 +1038,7 @@ impl<'a> Teacher<'a> { log! { @debug " {}", cex } Ok(Some((cex, bias))) } else { + log! { @debug | " unsat" } Ok(None) } } From d175b8eef2f32b0bdcea4245dc17ecf6ba1020a8 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 2 Oct 2018 16:38:42 +0900 Subject: [PATCH 82/94] test for the term factory --- src/errors.rs | 15 ++- src/term/factory.rs | 237 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 7 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index be1008cf..b350bee1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -17,17 +17,22 @@ use common::*; /// A term type-checking error. +/// +/// Can be created by [`try_app`] when creating operator applications. +/// +/// [`try_app`]: ../term/fn.try_app.html (try_app function) +#[derive(Clone, Debug)] pub enum TypError { /// No type info, just an error message. Msg(String), - /// Type info: - /// - /// - the type expected (if known), - /// - the type obtained, - /// - the index of the argument that caused it. + /// Type error on a specific argument. Typ { + /// The type that was expected. `None` if the type is unknown, for arithmetic operators for + /// instance. expected: Option, + /// The actual type found. obtained: Typ, + /// The index of the argument that caused the error. index: usize, }, } diff --git a/src/term/factory.rs b/src/term/factory.rs index 3934e7a8..32c3a8c0 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -850,6 +850,8 @@ pub fn val(val: Val) -> Term { /// Creates a datatype constructor. /// +/// This function assumes the term created is legal. It will panic if the term is ill-typed. +/// /// # Examples /// /// ```rust @@ -889,6 +891,8 @@ where /// Creates a new datatype selector. /// +/// This function assumes the term created is legal. It will panic if the term is ill-typed. +/// /// # Examples /// /// ```rust @@ -932,6 +936,8 @@ where /// Creates a new datatype tester. /// +/// This function assumes the term created is legal. It will panic if the term is ill-typed. +/// /// # Examples /// /// ```rust @@ -979,6 +985,27 @@ where /// Creates an operator application. /// /// Error if the application is ill-typed (int will be cast to real automatically). +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// use hoice::errors::TypError; +/// let bool_var = term::bool_var(0); +/// let int_term = term::add( vec![ term::int_var(1), term::int(7) ] ); +/// let ill_typed = term::try_app( Op::And, vec![ bool_var, int_term ] ); +/// # println!("{:?}", ill_typed); +/// +/// assert! { ill_typed.is_err() } +/// match ill_typed.unwrap_err() { +/// TypError::Msg(blah) => panic!("unexpected error message `{}`", blah), +/// TypError::Typ { expected, obtained, index } => { +/// assert_eq! { expected, Some(typ::bool()) } +/// assert_eq! { obtained, typ::int() } +/// assert_eq! { index, 1 } +/// } +/// } +/// ``` #[inline] pub fn try_app(op: Op, mut args: Vec) -> Result { let typ = op.type_check(&mut args)?; @@ -986,71 +1013,226 @@ pub fn try_app(op: Op, mut args: Vec) -> Result { } /// Creates a less than or equal to. +/// +/// Currently rewritten as a greater than or equal to. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::le( term::int_var(7), term::int(0) ); +/// assert_eq! { &format!("{}", t), "(>= (* (- 1) v_7) 0)" } +/// ``` #[inline] pub fn le(lhs: Term, rhs: Term) -> Term { app(Op::Le, vec![lhs, rhs]) } + /// Creates a less than. +/// +/// Currently rewritten as a greater than (reals), or a greater than or equal to (integers). +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::lt( term::int_var(7), term::int(0) ); +/// assert_eq! { &format!("{}", t), "(>= (* (- 1) v_7) 1)" } +/// let t = term::lt( term::real_var(7), term::real_of(0f64) ); +/// assert_eq! { &format!("{}", t), "(> (* (- 1.0) v_7) 0.0)" } +/// ``` #[inline] pub fn lt(lhs: Term, rhs: Term) -> Term { app(Op::Lt, vec![lhs, rhs]) } + /// Creates a greater than. +/// +/// Currently rewritten as a greater than or equal to for integers. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::gt( term::int_var(7), term::int(0) ); +/// assert_eq! { &format!("{}", t), "(>= v_7 1)" } +/// let t = term::gt( term::real_var(7), term::real_of(0f64) ); +/// assert_eq! { &format!("{}", t), "(> v_7 0.0)" } +/// ``` #[inline] pub fn gt(lhs: Term, rhs: Term) -> Term { app(Op::Gt, vec![lhs, rhs]) } + /// Creates a greater than or equal to. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::ge( term::int_var(7), term::int(0) ); +/// assert_eq! { &format!("{}", t), "(>= v_7 0)" } +/// ``` #[inline] pub fn ge(lhs: Term, rhs: Term) -> Term { app(Op::Ge, vec![lhs, rhs]) } /// Creates an equality. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::eq( term::int_var(7), term::int(2) ); +/// assert_eq! { &format!("{}", t), "(= (+ v_7 (- 2)) 0)" } +/// ``` #[inline] pub fn eq(lhs: Term, rhs: Term) -> Term { app(Op::Eql, vec![lhs, rhs]) } /// Creates a distinct application. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::distinct( vec![term::int_var(7), term::int(2)] ); +/// assert_eq! { &format!("{}", t), "(not (= (+ v_7 (- 2)) 0))" } +/// let t = term::distinct( vec![term::int_var(7), term::int(2), term::int_var(3)] ); +/// assert_eq! { &format!("{}", t), "(distinct v_7 2 v_3)" } +/// ``` #[inline] pub fn distinct(terms: Vec) -> Term { app(Op::Distinct, terms) } /// Creates a sum. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::add( vec![term::int_var(7), term::int(2), term::int_var(7)] ); +/// assert_eq! { &format!("{}", t), "(+ 2 (* 2 v_7))" } +/// let t = term::add( vec![term::int_var(7), term::int(2), term::int(40)] ); +/// assert_eq! { &format!("{}", t), "(+ v_7 42)" } +/// let t = term::add( vec![term::int(7), term::int(2)] ); +/// assert_eq! { &format!("{}", t), "9" } +/// ``` #[inline] pub fn add(kids: Vec) -> Term { app(Op::Add, kids) } + /// Creates a sum, binary version. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::add2(term::int_var(7), term::int(2)); +/// assert_eq! { &format!("{}", t), "(+ v_7 2)" } +/// let t = term::add2(term::int_var(7), term::int_var(7)); +/// assert_eq! { &format!("{}", t), "(* 2 v_7)" } +/// ``` #[inline] pub fn add2(kid_1: Term, kid_2: Term) -> Term { app(Op::Add, vec![kid_1, kid_2]) } /// Creates a subtraction. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::sub( vec![term::int_var(3), term::int(2), term::int_var(7)] ); +/// assert_eq! { &format!("{}", t), "(+ v_3 (* (- 1) v_7) (- 2))" } +/// let t = term::sub( vec![term::int_var(7), term::int(2), term::int_var(7)] ); +/// assert_eq! { &format!("{}", t), "(- 2)" } +/// let t = term::sub( vec![term::int_var(7), term::int(2), term::u_minus(term::int_var(7))] ); +/// assert_eq! { &format!("{}", t), "(+ (- 2) (* 2 v_7))" } +/// let t = term::sub( vec![term::int(7), term::int(2)] ); +/// assert_eq! { &format!("{}", t), "5" } +/// ``` #[inline] pub fn sub(kids: Vec) -> Term { app(Op::Sub, kids) } + /// Creates a subtraction, binary version. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::sub2( term::int_var(3), term::int(2) ); +/// assert_eq! { &format!("{}", t), "(+ v_3 (- 2))" } +/// let t = term::sub2( term::int_var(7), term::int_var(7) ); +/// assert_eq! { &format!("{}", t), "0" } +/// ``` #[inline] pub fn sub2(kid_1: Term, kid_2: Term) -> Term { app(Op::Sub, vec![kid_1, kid_2]) } /// Creates a unary minus. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::u_minus( term::int_var(3) ); +/// assert_eq! { &format!("{}", t), "(* (- 1) v_3)" } +/// let t = term::u_minus( term::int(3) ); +/// assert_eq! { &format!("{}", t), "(- 3)" } +/// let t = term::u_minus( t ); +/// assert_eq! { &format!("{}", t), "3" } +/// let add = term::add2( term::int_var(7), term::int(5) ); +/// let t = term::u_minus( add ); +/// assert_eq! { &format!("{}", t), "(+ (* (- 1) v_7) (- 5))" } +/// ``` #[inline] pub fn u_minus(kid: Term) -> Term { app(Op::Sub, vec![kid]) } + /// Creates a multiplication. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::mul( vec![ term::int_var(3), term::int_var(7) ] ); +/// assert_eq! { &format!("{}", t), "(* v_3 v_7)" } +/// let t = term::mul( vec![ term::int_var(3), term::int(7) ] ); +/// assert_eq! { &format!("{}", t), "(* 7 v_3)" } +/// assert! { t.cmul_inspect().is_some() } +/// let t = term::mul( vec![term::int(7), term::int(3)] ); +/// assert_eq! { &format!("{}", t), "21" } +/// let t = term::mul( vec![term::int_var(3), term::add2(term::int_var(7), term::int(3))] ); +/// assert_eq! { &format!("{}", t), "(* v_3 (+ v_7 3))" } +/// ``` #[inline] pub fn mul(kids: Vec) -> Term { app(Op::Mul, kids) } + /// Creates a multiplication by a constant. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::cmul( 7, term::int_var(3) ); +/// assert_eq! { &format!("{}", t), "(* 7 v_3)" } +/// let t = term::cmul( 7, term::int(3) ); +/// assert_eq! { &format!("{}", t), "21" } +/// let t = term::cmul( 7, term::add2(term::int_var(7), term::int(3)) ); +/// assert_eq! { &format!("{}", t), "(+ 21 (* 7 v_7))" } +/// ``` #[inline] pub fn cmul(cst: V, term: Term) -> Term where @@ -1068,27 +1250,78 @@ where } /// Creates an integer division. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::idiv( vec![ term::int(7), term::int_var(3) ] ); +/// assert_eq! { &format!("{}", t), "(div 7 v_3)" } +/// let t = term::idiv( vec![ term::int(7), term::int(3) ] ); +/// assert_eq! { &format!("{}", t), "2" } +/// ``` #[inline] pub fn idiv(kids: Vec) -> Term { app(Op::IDiv, kids) } -/// Creates a division. + +/// Creates a division for terms of sort `Real`. +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::div( vec![ term::real_of(7.), term::real_var(3) ] ); +/// assert_eq! { &format!("{}", t), "(/ 7.0 v_3)" } +/// let t = term::div( vec![ term::real_of(7.), term::real_of(3.) ] ); +/// assert_eq! { &format!("{}", t), "(/ 7 3)" } +/// ``` #[inline] pub fn div(kids: Vec) -> Term { app(Op::Div, kids) } + /// Creates a modulo application. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::modulo( term::int(7), term::int_var(3) ); +/// assert_eq! { &format!("{}", t), "(mod 7 v_3)" } +/// let t = term::modulo( term::int(7), term::int(3) ); +/// assert_eq! { &format!("{}", t), "1" } +/// ``` #[inline] pub fn modulo(a: Term, b: Term) -> Term { app(Op::Mod, vec![a, b]) } /// Creates a conversion from `Int` to `Real`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::to_real( term::int_var(3) ); +/// assert_eq! { &format!("{}", t), "(to_real v_3)" } +/// let t = term::to_real( term::int(3) ); +/// assert_eq! { &format!("{}", t), "3.0" } +/// ``` #[inline] pub fn to_real(int: Term) -> Term { app(Op::ToReal, vec![int]) } + /// Creates a conversion from `Real` to `Int`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let t = term::to_int( term::real_var(3) ); +/// assert_eq! { &format!("{}", t), "(to_int v_3)" } +/// let t = term::to_int( term::real_of(3.) ); +/// assert_eq! { &format!("{}", t), "3" } +/// ``` #[inline] pub fn to_int(real: Term) -> Term { app(Op::ToInt, vec![real]) @@ -1253,7 +1486,7 @@ fn normalize(op: Op, args: Vec, typ: Typ) -> Term { pub enum NormRes { /// Just a term. Term(Term), - /// More stuff to do. + /// A new application to normalize. App(Typ, Op, Vec), } From 06ad5fa6f8b4c412d21afa5595ffee7de830a383 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 2 Oct 2018 18:36:32 +0900 Subject: [PATCH 83/94] tests for the dtyp module --- src/dtyp/mod.rs | 326 +++++++++++++++++++++++++++++++++++++++++++++-- src/parse/mod.rs | 6 +- 2 files changed, 317 insertions(+), 15 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index b02a7ec6..421ff1a1 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -22,6 +22,10 @@ wrap_usize! { } /// A concrete type, a polymorphic type, a datatype or a type parameter. +/// +/// This is used by [`RDTyp`] to represent datatype (potentially) with type parameters. +/// +/// [`RDTyp`]: struct.RDTyp.html (RDTyp struct) #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PartialTyp { /// Array. @@ -42,6 +46,24 @@ impl From for PartialTyp { impl PartialTyp { /// True if the type mentions the datatype provided. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::dtyp::*; + /// # use hoice::common::*; + /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let ptyp = PartialTyp::DTyp( + /// "MyADT".into(), dummy_pos, vec![ + /// list.into(), PartialTyp::DTyp( "SubADT".into(), dummy_pos, vec![].into() ) + /// ].into() + /// ); + /// assert! { ptyp.mentions_dtyp("MyADT") } + /// assert! { ptyp.mentions_dtyp("SubADT") } + /// assert! { ptyp.mentions_dtyp("List") } + /// assert! { !ptyp.mentions_dtyp("NotThere") } + /// ``` pub fn mentions_dtyp(&self, dtyp_name: &str) -> bool { let mut to_do = vec![self]; let mut typ_to_do = vec![]; @@ -89,6 +111,43 @@ impl PartialTyp { } /// Resolves a partial type against a type. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::dtyp::*; + /// # use hoice::common::*; + /// ::hoice::fun::test::create_length_fun(); + /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let param_0: TPrmIdx = 0.into(); + /// let ptyp = PartialTyp::DTyp( + /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() + /// ); + /// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// + /// let mut params: TPrmMap = vec![ typ::unk() ].into(); + /// assert_eq! { ¶ms[param_0], &typ::unk() } + /// ptyp.unify(&int_list, &mut params).unwrap(); + /// assert_eq! { ¶ms[param_0], &typ::int() } + /// ``` + /// + /// ```rust + /// # use hoice::dtyp::*; + /// # use hoice::common::*; + /// ::hoice::fun::test::create_length_fun(); + /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let param_0: TPrmIdx = 0.into(); + /// let ptyp = PartialTyp::DTyp( + /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() + /// ); + /// let int = typ::int(); + /// + /// let mut params: TPrmMap = vec![ typ::unk() ].into(); + /// assert_eq! { ¶ms[param_0], &typ::unk() } + /// let res = ptyp.unify(&int, &mut params); + /// let err = res.unwrap_err(); + /// assert_eq! { &format!("{}", err), "cannot unify (List '0) with (Int)" } + /// ``` pub fn unify(&self, typ: &Typ, map: &mut TPrmMap) -> Res<()> { let mut stack = vec![(self, typ)]; @@ -162,13 +221,26 @@ impl PartialTyp { } /// Checks a partial type is legal. + /// + /// ```rust + /// # use hoice::common::*; + /// # use hoice::dtyp::*; + /// let _ = dtyp::get("List"); + /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let unknown = PartialTyp::DTyp("Unknown".into(), dummy_pos, vec![].into()); + /// let ptyp = PartialTyp::DTyp( + /// "List".into(), dummy_pos, vec![ unknown ].into() + /// ); + /// let err = ptyp.check().unwrap_err().1; + /// assert_eq! { &err, "unknown sort `Unknown`" } + /// ``` pub fn check(&self) -> Result<(), (::parse::Pos, String)> { let mut stack = vec![self]; while let Some(ptyp) = stack.pop() { if let PartialTyp::DTyp(name, pos, args) = ptyp { if get(name).is_err() { - return Err((*pos, format!("unknown sort `{}`", name))); + return Err((*pos, format!("unknown sort `{}`", conf.bad(name)))); } for arg in args { stack.push(arg) @@ -180,6 +252,26 @@ impl PartialTyp { } /// Turns a partial type into a concrete type. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::dtyp::*; + /// # use hoice::common::*; + /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let param_0: TPrmIdx = 0.into(); + /// let ptyp = PartialTyp::DTyp( + /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() + /// ); + /// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// + /// let mut params: TPrmMap = vec![ typ::unk() ].into(); + /// assert_eq! { ¶ms[param_0], &typ::unk() } + /// ptyp.unify(&int_list, &mut params).unwrap(); + /// assert_eq! { ¶ms[param_0], &typ::int() } + /// let typ = ptyp.to_type(¶ms).unwrap(); + /// assert_eq! { int_list, typ } + /// ``` pub fn to_type(&self, prms: &TPrmMap) -> Result { enum Frame<'a> { ArrayLft(&'a PartialTyp), @@ -336,7 +428,6 @@ lazy_static! { /// Creates the list datatype. /// /// Only available in test mode. -#[test] pub fn create_list_dtyp() { let _ = mk(RDTyp::list()); () @@ -460,6 +551,18 @@ pub fn mk(mut dtyp: RDTyp) -> Res { } /// Retrieves the datatype corresponding to a constructor. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let list = dtyp::get("List").unwrap(); +/// let dtyp = dtyp::of_constructor("nil"); +/// assert_eq! { list, dtyp.unwrap() } +/// let dtyp = dtyp::of_constructor("insert"); +/// assert_eq! { list, dtyp.unwrap() } +/// assert! { dtyp::of_constructor("unknown").is_none() } +/// ``` pub fn of_constructor(constructor: &str) -> Option { constructor_map .read() @@ -469,6 +572,16 @@ pub fn of_constructor(constructor: &str) -> Option { } /// True if the identifier is known to be a selector. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let _ = dtyp::get("List"); +/// assert! { dtyp::is_selector("tail") } +/// assert! { dtyp::is_selector("head") } +/// assert! { !dtyp::is_selector("unknown") } +/// ``` pub fn is_selector(selector: &str) -> bool { selector_set .read() @@ -476,7 +589,37 @@ pub fn is_selector(selector: &str) -> bool { .contains(selector) } -/// Lists (some of) the constructors of a datatype as an error. +/// Lists the constructors of a datatype as an error. +/// +/// One line per constructor. If there are more than three constructor, lists only the first three +/// followed by "and `` others" where `` is the number of constructors left. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let _ = dtyp::get("List"); +/// let err = dtyp::constructors_as_error("List"); +/// assert_eq! { +/// &format!("{}", err), +/// "insert (head '0) (tail (List '0))\n\ +/// nil" +/// } +/// ``` +/// +/// ```rust +/// # use hoice::common::*; +/// ::hoice::parse::fun_dtyp( +/// "(declare-datatypes ( (Enum 0) ) \ +/// ( ( A B C D E F G H ) ) +/// )" +/// ); +/// let err = dtyp::constructors_as_error("Enum"); +/// assert_eq! { +/// &format!("{}", err), +/// "A\nB\nC\nand 5 others" +/// } +/// ``` pub fn constructors_as_error(dtyp: &str) -> Error { let dtyp = match get(dtyp) { Ok(res) => res, @@ -484,12 +627,13 @@ pub fn constructors_as_error(dtyp: &str) -> Error { }; let mut s = String::new(); for (count, (constructor, args)) in dtyp.news.iter().enumerate() { + if count > 0 { + s.push_str("\n") + } if count >= 3 { - s.push_str(&format!("and {} others...", dtyp.news.len() - 3)) + s.push_str(&format!("and {} others", dtyp.news.len() - 3)); + break; } else { - if count > 0 { - s.push_str("\n") - } s.push_str(constructor); if !args.is_empty() { for (selector, typ) in args { @@ -507,6 +651,15 @@ pub fn constructors_as_error(dtyp: &str) -> Error { /// /// - the datatype does not exist, or /// - can't access the datatype map. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// assert! { dtyp::get("Unknown").is_err() } +/// let lst = dtyp::get("List").unwrap(); +/// assert_eq! { lst.name, "List" } +/// ``` pub fn get(dtyp: &str) -> Res { let maybe_res = if let Ok(f) = factory.read() { f.get(dtyp).cloned() @@ -540,7 +693,7 @@ pub fn write_constructor_map(w: &mut W, pref: &str) -> ::std::io::Resu Ok(()) } -/// Writes all the datatypes. +/// Writes all the datatypes, SMT-LIB style. pub fn write_all(w: &mut W, pref: &str) -> ::std::io::Result<()> { let decs = get_all(); @@ -600,6 +753,32 @@ pub fn write_all(w: &mut W, pref: &str) -> ::std::io::Result<()> { } /// Types a constructor application. +/// +/// Returns `None` if the constructor is unknown. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// let maybe_typ = dtyp::type_constructor( +/// "insert", & [ term::int(7), term::dtyp_new(int_list.clone(), "nil", vec![]) ] +/// ).unwrap(); +/// assert_eq! { maybe_typ, Some(int_list) } +/// ``` +/// +/// ```rust +/// # use hoice::common::*; +/// let _ = dtyp::get("List"); +/// let maybe_typ = dtyp::type_constructor("nil", &[]).unwrap(); +/// assert_eq! { &format!("{}", maybe_typ.unwrap()), "(List _)" } +/// ``` +/// +/// ```rust +/// # use hoice::common::*; +/// let maybe_typ = dtyp::type_constructor("unknown", &[]).unwrap(); +/// assert! { maybe_typ.is_none() } +/// ``` pub fn type_constructor(constructor: &str, args: &[Term]) -> Res> { let dtyp = if let Some(dtyp) = of_constructor(constructor) { dtyp @@ -639,6 +818,31 @@ pub fn type_constructor(constructor: &str, args: &[Term]) -> Res> { } /// Types a datatype selector application. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// let typ = dtyp::type_selector( +/// "head", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// ).unwrap(); +/// assert_eq! { typ, typ::int() } +/// let typ = dtyp::type_selector( +/// "tail", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// ).unwrap(); +/// assert_eq! { typ, int_list } +/// ``` +/// +/// ```rust +/// # use hoice::common::*; +/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// let (_, blah) = dtyp::type_selector("unknown", dummy, &term::int(7)).unwrap_err(); +/// assert_eq! { +/// &blah, "`unknown` is not a legal selector for sort Int" +/// } +/// ``` pub fn type_selector( selector: &str, slc_pos: ::parse::Pos, @@ -657,7 +861,7 @@ pub fn type_selector( Err(( slc_pos, format!( - "cannot apply selector `{}` to term of type {}", + "`{}` is not a legal selector for sort {}", conf.bad(selector), term.typ() ), @@ -665,6 +869,34 @@ pub fn type_selector( } /// Types a datatype tester application. +/// +/// Just like in term creation, the `tester` name is simply the name of the constructor (`"insert"` +/// for instance) as opposed to the name of the tester function (`"is-insert"` in this case). +/// +/// Does not return a type, since a tester always has sort `Bool`. +/// +/// # Examples +/// +/// ```rust +/// # use hoice::common::*; +/// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// dtyp::type_tester( +/// "nil", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// ).unwrap(); +/// dtyp::type_tester( +/// "insert", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// ).unwrap(); +/// ``` +/// +/// ```rust +/// # use hoice::common::*; +/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// let (_, blah) = dtyp::type_tester("unknown", dummy, &term::int(7)).unwrap_err(); +/// assert_eq! { +/// &blah, "`unknown` is not a legal tester for sort Int" +/// } +/// ``` pub fn type_tester( tester: &str, tst_pos: ::parse::Pos, @@ -679,7 +911,7 @@ pub fn type_tester( Err(( tst_pos, format!( - "cannot apply tester `{}` to term of type {}, no such constructor", + "`{}` is not a legal tester for sort {}", conf.bad(tester), term.typ() ), @@ -757,8 +989,8 @@ impl RDTyp { idx } - /// Writes the declaration for a datatype. - pub fn write_dec(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { + /// Writes the *body* of the declaration for a datatype. + fn write_dec(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { let close_par = if self.prms.is_empty() { writeln!(w, "{}( ; {}", pref, self.name)?; false @@ -784,8 +1016,30 @@ impl RDTyp { writeln!(w, "{}){}", pref, if close_par { " )" } else { "" }) } + /// String representation. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let list = dtyp::get("List").unwrap(); + /// assert_eq! { + /// (* list).to_string(), "\ + /// (List + /// (insert (head T) (tail (List T))) + /// nil + /// )\n\ + /// " + /// } + /// ``` + pub fn to_string(&self) -> String { + let mut buff: Vec = vec![]; + self.write(&mut buff, "").unwrap(); + String::from_utf8_lossy(&buff).into() + } + /// Writes a single datatype. - pub fn write(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { + fn write(&self, w: &mut W, pref: &str) -> ::std::io::Result<()> { writeln!(w, "{}({}", pref, self.name)?; for (name, args) in &self.news { if args.is_empty() { @@ -806,6 +1060,9 @@ impl RDTyp { } /// Adds a datatype dependency. + /// + /// Dependencies indicates that some datatypes depend on each other. This useful mostly for + /// printing at SMT-level. pub fn add_dep(&mut self, dep: S) where S: Into, @@ -862,6 +1119,14 @@ impl RDTyp { /// /// - there are only two constructors /// - one of them is recursive + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let list = dtyp::get("List").unwrap(); + /// assert_eq! { list.rec_constructor(), Some("insert") } + /// ``` pub fn rec_constructor(&self) -> Option<&str> { if self.news.len() == 2 { for (new, args) in &self.news { @@ -876,12 +1141,29 @@ impl RDTyp { } /// Returns the selectors of a constructor. + /// + /// # Examples + /// + /// ```rust + /// # use hoice::common::*; + /// let list = dtyp::get("List").unwrap(); + /// let slcs = list.selectors_of("nil").unwrap(); + /// assert! { slcs.is_empty() } + /// let slcs = list.selectors_of("insert").unwrap(); + /// let mut slcs = slcs.iter(); + /// assert_eq! { &slcs.next().unwrap().0, "head" } + /// assert_eq! { &slcs.next().unwrap().0, "tail" } + /// assert! { slcs.next().is_none() } + /// + /// let err = list.selectors_of("node").unwrap_err(); + /// assert_eq! { &format!("{}", err), "unknown constructor `node` for dtyp List" } + /// ``` pub fn selectors_of(&self, constructor: &str) -> Res<&CArgs> { if let Some(selectors) = self.news.get(constructor) { Ok(selectors) } else { bail!( - "unknown constructor {} for dtyp {}, no selectors", + "unknown constructor `{}` for dtyp {}", conf.bad(constructor), conf.emph(&self.name) ) @@ -891,3 +1173,19 @@ impl RDTyp { impl_fmt! { RDTyp(self, fmt) { write!(fmt, "{}", self.name) } } + +#[test] +fn dtyp_write_dec() { + let list = get("List").unwrap(); + let mut buff: Vec = vec![]; + list.write_dec(&mut buff, "").unwrap(); + assert_eq!( + &String::from_utf8_lossy(&buff), + "\ +(par ( T ) ( + (insert (head T) (tail (List T))) + (nil) +) )\n\ + " + ) +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1c23ce26..5d6e7c46 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -469,9 +469,13 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Returns the current position. - pub fn pos(&mut self) -> Pos { + pub fn pos(&self) -> Pos { Pos(self.cursor) } + /// Returns a dummy position, mostly for testing. + pub fn dummy_pos() -> Pos { + Pos(0) + } /// Consumes whitespaces and comments. pub fn ws_cmt(&mut self) { From d9399d6b00171f1c8343f9840030c04349464a99 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 3 Oct 2018 12:04:18 +0900 Subject: [PATCH 84/94] full example for mutually rec datatype creation --- src/dtyp/mod.rs | 133 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 15 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 421ff1a1..81fc225b 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -3,7 +3,100 @@ //! In test mode, the `List` datatype is automatically added, as well as a few functions (see the //! [`fun`] module). This is so that dtyp-related doc/lib tests have something to work with. //! +//! # Creating Mutually Recursive Datatypes +//! +//! Consider the following mutually recursive datatypes (stolen and fixed from the [z3 tutorial]) +//! +//! ```lisp +//! (declare-datatypes ( (Tree 1) (TreeList 1) ) +//! ((par (T) +//! (leaf) +//! (node (value T) (children (TreeList T))) +//! )(par (T) +//! (nil) +//! (cons (car (Tree T)) (cdr (TreeList T))) +//! )) +//! ) +//! ``` +//! +//! The main steps are +//! +//! - create [`RDTyp`] structures encoding each datatype, which itself consists in +//! - creating the list of type parameters if any +//! - creating the list of constructors +//! - register them as depending on each other (important for SMT-level printing) +//! - finalize by hashconsing the definitions, thus obtaining [`DTyp`]s +//! - check the definitions are fine +//! +//! If you skip the non-mandatory-but-strongly-recommended last step, problems will arise during +//! term creation. +//! +//! ```rust +//! use hoice::{ dtyp, dtyp::{ RDTyp, PartialTyp } }; +//! let dummy_pos = ::hoice::parse::Pos::default(); +//! let (tree_name, tree_list_name) = ("DTypTestTree", "DTypTestTreeList"); +//! let (mut tree, mut tree_list) = (RDTyp::new(tree_name), RDTyp::new(tree_list_name)); +//! +//! // Working on `Tree`. +//! let t_param = tree.push_typ_param("T"); +//! // `leaf` constructor takes no arguments +//! tree.add_constructor("leaf", vec![]).expect("while adding `leaf` constructor"); +//! let node_params = vec![ +//! // `value` field of `node` has type `T`, registered as `t_param` +//! ("value".to_string(), PartialTyp::Param(t_param)), +//! // `children` field of `node` has type `(TreeList T)` +//! ( +//! "children".to_string(), +//! PartialTyp::DTyp( +//! tree_list_name.into(), +//! dummy_pos, // Used for error reporting when actually parsing user's input. +//! // `TreeList` has a single argument, our type parameter `T` +//! vec![ PartialTyp::Param(t_param) ].into(), +//! ) +//! ), +//! ]; +//! tree.add_constructor("node", node_params).expect("while adding node constructor"); +//! // Finally, register dependency to `TreeList`. +//! tree.add_dep(tree_list_name); +//! +//! // Working on `TreeList`. +//! let t_param = tree_list.push_typ_param("T"); +//! tree_list.add_constructor("nil", vec![]).expect("while adding `nil` constructor"); +//! let cons_params = vec![ +//! ( +//! "car".to_string(), +//! PartialTyp::DTyp( +//! tree_name.into(), +//! dummy_pos, +//! vec![ PartialTyp::Param(t_param) ].into(), +//! ) +//! ), +//! ( +//! "cdr".to_string(), +//! PartialTyp::DTyp( +//! tree_list_name.into(), +//! dummy_pos, +//! vec![ PartialTyp::Param(t_param) ].into(), +//! ) +//! ), +//! ]; +//! tree_list.add_constructor("cons", cons_params).expect("while adding `cons` constructor"); +//! tree_list.add_dep(tree_name); +//! +//! // Actually register. +//! let tree = dtyp::mk(tree).expect("while registering `tree`"); +//! let tree_list = dtyp::mk(tree_list).expect("while registering `tree_list`"); +//! +//! // Everything okay? +//! tree.check().expect("while checking `tree`"); +//! tree_list.check().expect("while checking `tree_list`"); +//! ``` +//! //! [`fun`]: ../fun/index.html (fun module) +//! [z3 tutorial]: https://rise4fun.com/z3/tutorial +//! (z3 tutorial at Rise4Fun) +//! [`RDTyp`]: struct.RDTyp.html (RDTyp struct) +//! [`DTyp`]: type.DTyp.html (DTyp type) use common::*; @@ -53,7 +146,7 @@ impl PartialTyp { /// # use hoice::dtyp::*; /// # use hoice::common::*; /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); - /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let dummy_pos = ::hoice::parse::Pos::default(); /// let ptyp = PartialTyp::DTyp( /// "MyADT".into(), dummy_pos, vec![ /// list.into(), PartialTyp::DTyp( "SubADT".into(), dummy_pos, vec![].into() ) @@ -118,7 +211,7 @@ impl PartialTyp { /// # use hoice::dtyp::*; /// # use hoice::common::*; /// ::hoice::fun::test::create_length_fun(); - /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let dummy_pos = ::hoice::parse::Pos::default(); /// let param_0: TPrmIdx = 0.into(); /// let ptyp = PartialTyp::DTyp( /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() @@ -135,7 +228,7 @@ impl PartialTyp { /// # use hoice::dtyp::*; /// # use hoice::common::*; /// ::hoice::fun::test::create_length_fun(); - /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let dummy_pos = ::hoice::parse::Pos::default(); /// let param_0: TPrmIdx = 0.into(); /// let ptyp = PartialTyp::DTyp( /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() @@ -222,11 +315,13 @@ impl PartialTyp { /// Checks a partial type is legal. /// + /// This checks that all datatypes mentioned in the partial type exist. + /// /// ```rust /// # use hoice::common::*; /// # use hoice::dtyp::*; /// let _ = dtyp::get("List"); - /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let dummy_pos = ::hoice::parse::Pos::default(); /// let unknown = PartialTyp::DTyp("Unknown".into(), dummy_pos, vec![].into()); /// let ptyp = PartialTyp::DTyp( /// "List".into(), dummy_pos, vec![ unknown ].into() @@ -258,7 +353,7 @@ impl PartialTyp { /// ```rust /// # use hoice::dtyp::*; /// # use hoice::common::*; - /// let dummy_pos = ::hoice::parse::Parser::dummy_pos(); + /// let dummy_pos = ::hoice::parse::Pos::default(); /// let param_0: TPrmIdx = 0.into(); /// let ptyp = PartialTyp::DTyp( /// "List".into(), dummy_pos, vec![ PartialTyp::Param(param_0) ].into() @@ -824,21 +919,21 @@ pub fn type_constructor(constructor: &str, args: &[Term]) -> Res> { /// ```rust /// # use hoice::common::*; /// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); -/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// let dummy_pos = ::hoice::parse::Pos::default(); /// let typ = dtyp::type_selector( -/// "head", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// "head", dummy_pos, & term::dtyp_new(int_list.clone(), "nil", vec![]) /// ).unwrap(); /// assert_eq! { typ, typ::int() } /// let typ = dtyp::type_selector( -/// "tail", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// "tail", dummy_pos, & term::dtyp_new(int_list.clone(), "nil", vec![]) /// ).unwrap(); /// assert_eq! { typ, int_list } /// ``` /// /// ```rust /// # use hoice::common::*; -/// let dummy = ::hoice::parse::Parser::dummy_pos(); -/// let (_, blah) = dtyp::type_selector("unknown", dummy, &term::int(7)).unwrap_err(); +/// let dummy_pos = ::hoice::parse::Pos::default(); +/// let (_, blah) = dtyp::type_selector("unknown", dummy_pos, &term::int(7)).unwrap_err(); /// assert_eq! { /// &blah, "`unknown` is not a legal selector for sort Int" /// } @@ -880,19 +975,19 @@ pub fn type_selector( /// ```rust /// # use hoice::common::*; /// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); -/// let dummy = ::hoice::parse::Parser::dummy_pos(); +/// let dummy_pos = ::hoice::parse::Pos::default(); /// dtyp::type_tester( -/// "nil", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// "nil", dummy_pos, & term::dtyp_new(int_list.clone(), "nil", vec![]) /// ).unwrap(); /// dtyp::type_tester( -/// "insert", dummy, & term::dtyp_new(int_list.clone(), "nil", vec![]) +/// "insert", dummy_pos, & term::dtyp_new(int_list.clone(), "nil", vec![]) /// ).unwrap(); /// ``` /// /// ```rust /// # use hoice::common::*; -/// let dummy = ::hoice::parse::Parser::dummy_pos(); -/// let (_, blah) = dtyp::type_tester("unknown", dummy, &term::int(7)).unwrap_err(); +/// let dummy_pos = ::hoice::parse::Pos::default(); +/// let (_, blah) = dtyp::type_tester("unknown", dummy_pos, &term::int(7)).unwrap_err(); /// assert_eq! { /// &blah, "`unknown` is not a legal tester for sort Int" /// } @@ -1071,6 +1166,14 @@ impl RDTyp { } /// Checks a datatype is legal. + /// + /// This checks that all partial types are legal, meaning that all datatypes referenced *must + /// be registered*. So, checking a datatype before it is hashconsed and before its dependencies + /// are hashconsed will usually fail. + /// + /// For more see the [module-level documentation]. + /// + /// [module-level documentation]: index.html (dtyp module documentation) pub fn check(&self) -> Result<(), (::parse::Pos, String)> { for (constructor, cargs) in &self.news { for (selector, ptyp) in cargs { From 8c777c1d970033b911989b7a5c1ddb48271839e0 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 3 Oct 2018 12:57:56 +0900 Subject: [PATCH 85/94] datatypes are now much safer --- src/dtyp/mod.rs | 103 +++++++++++++++++++++--- src/fun/mod.rs | 2 +- src/parse/mod.rs | 33 ++++---- src/preproc/fun_preds.rs | 2 +- src/val/mod.rs | 164 +++++++++++++++++++-------------------- 5 files changed, 191 insertions(+), 113 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 81fc225b..603414ff 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -7,7 +7,7 @@ //! //! Consider the following mutually recursive datatypes (stolen and fixed from the [z3 tutorial]) //! -//! ```lisp +//! ```scheme //! (declare-datatypes ( (Tree 1) (TreeList 1) ) //! ((par (T) //! (leaf) @@ -26,7 +26,6 @@ //! - creating the list of constructors //! - register them as depending on each other (important for SMT-level printing) //! - finalize by hashconsing the definitions, thus obtaining [`DTyp`]s -//! - check the definitions are fine //! //! If you skip the non-mandatory-but-strongly-recommended last step, problems will arise during //! term creation. @@ -84,12 +83,14 @@ //! tree_list.add_dep(tree_name); //! //! // Actually register. -//! let tree = dtyp::mk(tree).expect("while registering `tree`"); -//! let tree_list = dtyp::mk(tree_list).expect("while registering `tree_list`"); +//! let dtyps = dtyp::new_recs( +//! vec![tree, tree_list], |_, err| err +//! ).expect("while registering `tree`"); //! //! // Everything okay? -//! tree.check().expect("while checking `tree`"); -//! tree_list.check().expect("while checking `tree_list`"); +//! for dtyp in dtyps { +//! dtyp.check().expect("while checking datatype") +//! } //! ``` //! //! [`fun`]: ../fun/index.html (fun module) @@ -488,10 +489,14 @@ impl_fmt! { } } -/// A datatype. +/// A hashconsed datatype. pub type DTyp = Arc; /// Constructor arguments. +/// +/// Each string is the name of the selector, *i.e.* the "name" of the field. It is associated to a +/// partial type because, in general, it will mention type parameters of the datatype it belongs +/// to. pub type CArgs = Vec<(String, PartialTyp)>; /// Type of the datatype factory. @@ -524,7 +529,9 @@ lazy_static! { /// /// Only available in test mode. pub fn create_list_dtyp() { - let _ = mk(RDTyp::list()); + let _ = new(RDTyp::list(), |_, blah| { + format!("failed to create List datatype: {}", blah) + }); () } @@ -553,11 +560,81 @@ pub fn check_reserved(_name: &str) -> Res<()> { /// Will fail if either /// /// - the datatype already exists +/// - it mentions a datatype that doesn't exist +/// - one of the constructors of the datatype already exists +/// - can't access the datatype map +/// - the datatype has no constructor +/// - the datatype has no constructor that don't mention itself +/// +/// Do *not* use this to construct mutually-recursive datatypes as it will fail. Use [`new_recs`] +/// instead. +/// +/// For more see the [module-level documentation]. +/// +/// [module-level documentation]: index.html (dtyp module documentation) +/// [`new_recs`]: fn.new_recs.html (datatypes construction function) +pub fn new(dtyp: RDTyp, err: F) -> Res +where + E: Into, + F: Fn(::parse::Pos, String) -> E, +{ + check_reserved(&dtyp.name)?; + new_raw(dtyp).and_then(|dtyp| { + if let Err((pos, blah)) = dtyp.check() { + bail!(err(pos, blah)) + } else { + Ok(dtyp) + } + }) +} + +/// Creates mutually recursive datatypes. +/// +/// Will fail if either +/// +/// - one of the datatypes already exists +/// - one of them mentions a datatype that doesn't exist +/// - one of the constructors already exists +/// - can't access the datatype map +/// - one of the datatypes has no constructor +/// - one of the datatypes has no constructor that don't mention itself +/// +/// If an error occured, returns the index of the datatype for which it occured and the error +/// itself. +/// +/// For more see the [module-level documentation]. +/// +/// [module-level documentation]: index.html (dtyp module documentation) +pub fn new_recs(dtyp: RDTyps, err: F) -> Result, (usize, Error)> +where + E: Into, + F: Fn(::parse::Pos, String) -> E, + RDTyps: IntoIterator, +{ + let mut res = vec![]; + for (index, dtyp) in dtyp.into_iter().enumerate() { + check_reserved(&dtyp.name).map_err(|e| (index, e))?; + res.push(new_raw(dtyp).map_err(|e| (index, e))?) + } + for (index, dtyp) in res.iter().enumerate() { + if let Err((pos, blah)) = dtyp.check() { + return Err((index, err(pos, blah).into())); + } + } + Ok(res) +} + +/// Creates a datatype. +/// +/// Will fail if either +/// +/// - the datatype already exists +/// - name is reserved /// - one of the constructors of the datatype already exists /// - can't access the datatype map /// - the datatype has no constructor /// - the datatype has no constructor that don't mention itself -pub fn mk(mut dtyp: RDTyp) -> Res { +fn new_raw(mut dtyp: RDTyp) -> Res { let name = dtyp.name.clone(); if dtyp.news.is_empty() { @@ -765,7 +842,9 @@ pub fn get(dtyp: &str) -> Res { if let Some(res) = maybe_res { Ok(res) } else if dtyp == "List" { - mk(RDTyp::list()) + new(RDTyp::list(), |_, blah| { + format!("failed to create List datatype: {}", blah) + }) } else { bail!("unknown datatype `{}`", dtyp) } @@ -1171,9 +1250,13 @@ impl RDTyp { /// be registered*. So, checking a datatype before it is hashconsed and before its dependencies /// are hashconsed will usually fail. /// + /// This check runs automatically when hashconsing datatype(s) with [`new`] and [`new_recs`]. + /// /// For more see the [module-level documentation]. /// /// [module-level documentation]: index.html (dtyp module documentation) + /// [`new`]: fn.new.html (datatype construction function) + /// [`new_recs`]: fn.new_recs.html (datatypes construction function) pub fn check(&self) -> Result<(), (::parse::Pos, String)> { for (constructor, cargs) in &self.news { for (selector, ptyp) in cargs { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 7c259672..e3ecacf8 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -198,7 +198,7 @@ where } /// Creates a function definition. -pub fn mk(fun: RFun) -> Res { +pub fn new(fun: RFun) -> Res { let fun = Arc::new(fun); let f = factory!(write); let prev = f.insert(fun.name.clone(), fun.clone()); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 5d6e7c46..bc328f67 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1244,7 +1244,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { let _ = fun::retrieve_dec(&fun.name)?; - fun::mk(fun).chain_err(|| self.error(pos, "while registering this function"))?; + fun::new(fun).chain_err(|| self.error(pos, "while registering this function"))?; } self.ws_cmt(); @@ -1410,6 +1410,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt(); let mut dtyps = vec![]; + let mut dtyps_pos = vec![]; while self.tag_opt("(") { let (dtyp_pos, dtyp_ident) = self.ident().chain_err(|| "declaring a new datatype")?; @@ -1424,7 +1425,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { ))) }; - dtyps.push((dtyp_pos, dtyp::RDTyp::new(dtyp_ident), arity)); + dtyps.push((dtyp::RDTyp::new(dtyp_ident), arity)); + dtyps_pos.push(dtyp_pos); self.tag(")").chain_err(|| { format!("closing symbol/arity pair for `{}`", conf.emph(dtyp_ident)) @@ -1442,35 +1444,28 @@ impl<'cxt, 's> Parser<'cxt, 's> { let mut final_dtyps = Vec::with_capacity(dtyps.len()); - for (dtyp_pos, mut dtyp, dtyp_arity) in dtyps { + for (index, (mut dtyp, dtyp_arity)) in dtyps.into_iter().enumerate() { self.dtyp_dec(&mut dtyp, Some(dtyp_arity)) .chain_err(|| { format!( "while parsing the declaration for datatype `{}`", conf.emph(&dtyp.name) ) - }).chain_err(|| self.error(dtyp_pos, "declared here"))?; + }).chain_err(|| self.error(dtyps_pos[index], "declared here"))?; self.ws_cmt(); - - let is_okay = dtyp::check_reserved(&dtyp.name); - - let dtyp = is_okay.and_then(|_| dtyp::mk(dtyp)).chain_err(|| { - self.error(dtyp_pos, "while parsing the declaration for this datatype") - })?; - final_dtyps.push((dtyp_pos, dtyp)) + final_dtyps.push(dtyp) } self.tag(")") .chain_err(|| "closing the list of datatype declaration")?; - for (dtyp_pos, dtyp) in final_dtyps { - if let Err((pos, err)) = dtyp.check() { - let err: Error = self.error(pos, err).into(); - bail!(err.chain_err(|| self.error(dtyp_pos, "in this datatype declaration"))) - } + match dtyp::new_recs(final_dtyps, |pos, blah| self.error(pos, blah)) { + Err((index, err)) => bail!(err.chain_err(|| self.error( + dtyps_pos[index], + "while parsing the declaration for this datatype" + ))), + Ok(_) => Ok(true), } - - Ok(true) } /// Predicate declaration. @@ -2569,7 +2564,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { if let Some(term) = body.to_term()? { let mut fun = RFun::new(name, var_info, out_sort); fun.set_def(term); - let _ = fun::mk(fun) + let _ = fun::new(fun) .chain_err(|| self.error(name_pos, "while registering this function"))?; () } else { diff --git a/src/preproc/fun_preds.rs b/src/preproc/fun_preds.rs index 8ac7da9a..e2a14ab5 100644 --- a/src/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -723,7 +723,7 @@ impl FunDef { dec.set_def(def); dec.invariants.extend(invs); - let fun = fun::mk(dec).chain_err(|| { + let fun = fun::new(dec).chain_err(|| { format!( "while creating internal function for predicate {}", conf.bad(&instance[pred].name) diff --git a/src/val/mod.rs b/src/val/mod.rs index 76ca5938..d6f6635c 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -19,7 +19,7 @@ new_consign! { pub type Val = HConsed; /// Creates a value. -pub fn mk>(val: V) -> Val { +pub fn new>(val: V) -> Val { let val = val.into(); // if val.typ().has_unk() { // panic!( @@ -32,7 +32,7 @@ pub fn mk>(val: V) -> Val { /// Creates a boolean value. pub fn bool(b: bool) -> Val { - mk(RVal::B(b)) + new(RVal::B(b)) } /// Creates an array with a default value. @@ -45,7 +45,7 @@ pub fn array>(idx_typ: Typ, default: Tgt) -> Val { } else { default }; - mk(RVal::Array { + new(RVal::Array { idx_typ, default, vals: Vec::new(), @@ -67,7 +67,7 @@ pub fn array_of_fun(idx_typ: Typ, term: &Term) -> Res { |(c, v)| c.typ() == idx_typ && v.typ() == val.typ() ) } - return Ok(mk(RVal::Array { + return Ok(new(RVal::Array { idx_typ, default: val, vals, @@ -117,11 +117,11 @@ pub fn array_of_fun(idx_typ: Typ, term: &Term) -> Res { /// Creates an integer value. pub fn int>(i: I) -> Val { - mk(RVal::I(i.into())) + new(RVal::I(i.into())) } /// Creates a rational value. pub fn real>(r: R) -> Val { - mk(RVal::R(r.into())) + new(RVal::R(r.into())) } /// Creates a real value from a float. pub fn real_of(f: f64) -> Val { @@ -130,7 +130,7 @@ pub fn real_of(f: f64) -> Val { /// Creates a non-value for a type. pub fn none(typ: Typ) -> Val { - mk(RVal::N(typ)) + new(RVal::N(typ)) } /// Creates a new datatype value. @@ -171,7 +171,7 @@ pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { } } } - mk(RVal::DTypNew { typ, name, args }) + new(RVal::DTypNew { typ, name, args }) } /// Values. @@ -417,7 +417,7 @@ impl RVal { use num::One; use term::typ::RTyp; if &self.typ() == typ { - return Ok(mk(self.clone())); + return Ok(new(self.clone())); } match (self, typ.get()) { @@ -669,15 +669,15 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(35), val::mk(7)) ; + /// let (mut lft, mut rgt) = (val::new(35), val::new(7)) ; /// let res = lft.add(& rgt).unwrap() ; /// # println!("{} + {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(42) } + /// assert_eq!{ res, val::new(42) } /// lft = val::none( typ::int() ) ; /// let res = lft.add(& rgt).unwrap() ; /// # println!("{} + {} = {}", lft, rgt, res) ; /// assert!{ ! res.is_known() } - /// rgt = val::mk(false) ; + /// rgt = val::new(false) ; /// let res = lft.add(& rgt) ; /// # println!("{} + {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -692,13 +692,13 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(49), val::mk(7)) ; + /// let (mut lft, mut rgt) = (val::new(49), val::new(7)) ; /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; - /// assert_eq!{ lft.sub(& rgt).unwrap(), val::mk(42) } + /// assert_eq!{ lft.sub(& rgt).unwrap(), val::new(42) } /// lft = val::none( typ::int() ) ; /// # println!("{} - {} = {}", lft, rgt, lft.sub(& rgt).unwrap()) ; /// assert_eq!{ lft.sub(& rgt).unwrap(), val::none( typ::int() ) } - /// rgt = val::mk(false) ; + /// rgt = val::new(false) ; /// # println!("{} - {} = {:?}", lft, rgt, lft.sub(& rgt)) ; /// assert!{ lft.sub(& rgt).is_err() } /// ``` @@ -712,27 +712,27 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(6), val::mk(7)) ; + /// let (mut lft, mut rgt) = (val::new(6), val::new(7)) ; /// let res = lft.mul(& rgt).unwrap() ; /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(42) } + /// assert_eq!{ res, val::new(42) } /// lft = val::none(typ::int()) ; /// let res = lft.mul(& rgt).unwrap() ; /// # println!("{} * {} = {}", lft, rgt, res) ; /// assert_eq!{ res, lft } - /// rgt = val::mk(0) ; + /// rgt = val::new(0) ; /// let res = lft.mul(& rgt).unwrap() ; /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(0) } - /// rgt = val::mk((0, 1)) ; + /// assert_eq!{ res, val::new(0) } + /// rgt = val::new((0, 1)) ; /// let res = lft.mul(& rgt).unwrap() ; /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk((0, 1)) } - /// lft = val::mk(7) ; + /// assert_eq!{ res, val::new((0, 1)) } + /// lft = val::new(7) ; /// let res = lft.mul(& rgt).unwrap() ; /// # println!("{} * {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk((0, 1)) } - /// lft = val::mk(false) ; + /// assert_eq!{ res, val::new((0, 1)) } + /// lft = val::new(false) ; /// let res = lft.mul(& rgt) ; /// # println!("{} * {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -742,7 +742,7 @@ impl RVal { match self { RVal::N(ref typ) if typ.is_int() => match other.get() { RVal::I(ref i) if i.is_zero() => Ok(int(0)), - RVal::R(ref r) if r.is_zero() => Ok(mk((0, 1))), + RVal::R(ref r) if r.is_zero() => Ok(new((0, 1))), RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), RVal::I(_) => Ok(none(typ.clone())), RVal::R(_) => Ok(none(typ::real())), @@ -750,8 +750,8 @@ impl RVal { }, RVal::N(ref typ) if typ.is_real() => match other.get() { - RVal::I(ref i) if i.is_zero() => Ok(mk((0, 1))), - RVal::R(ref r) if r.is_zero() => Ok(mk((0, 1))), + RVal::I(ref i) if i.is_zero() => Ok(new((0, 1))), + RVal::R(ref r) if r.is_zero() => Ok(new((0, 1))), RVal::N(ref t_2) if t_2.is_arith() => Ok(none(t_2.clone())), RVal::I(_) => Ok(none(typ.clone())), RVal::R(_) => Ok(none(typ::real())), @@ -876,27 +876,27 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; + /// let (mut lft, mut rgt) = (val::new(3), val::new(42)) ; /// let res = lft.g_t(& rgt).unwrap() ; /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } + /// assert_eq!{ res, val::new(false) } /// lft = val::none( typ::int() ) ; /// let res = lft.g_t(& rgt).unwrap() ; /// # println!("{} > {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(103) ; + /// lft = val::new(103) ; /// let res = lft.g_t(& rgt).unwrap() ; /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((103,1)) ; + /// assert_eq!{ res, val::new(true) } + /// lft = val::new((103,1)) ; /// let res = lft.g_t(& rgt).unwrap() ; /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new((42,1)) ; /// let res = lft.g_t(& rgt).unwrap() ; /// # println!("{} > {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(false) ; /// let res = lft.g_t(& rgt) ; /// # println!("{} > {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -913,27 +913,27 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(3), val::mk(42)) ; + /// let (mut lft, mut rgt) = (val::new(3), val::new(42)) ; /// let res = lft.g_e(& rgt).unwrap() ; /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } + /// assert_eq!{ res, val::new(false) } /// lft = val::none( typ::int() ) ; /// let res = lft.g_e(& rgt).unwrap() ; /// # println!("{} >= {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(42) ; + /// lft = val::new(42) ; /// let res = lft.g_e(& rgt).unwrap() ; /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((42,1)) ; + /// assert_eq!{ res, val::new(true) } + /// lft = val::new((42,1)) ; /// let res = lft.g_e(& rgt).unwrap() ; /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new((42,1)) ; /// let res = lft.g_e(& rgt).unwrap() ; /// # println!("{} >= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(false) ; /// let res = lft.g_e(& rgt) ; /// # println!("{} >= {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -949,27 +949,27 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; + /// let (mut lft, mut rgt) = (val::new(42), val::new(3)) ; /// let res = lft.l_e(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } + /// assert_eq!{ res, val::new(false) } /// lft = val::none( typ::int() ) ; /// let res = lft.l_e(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(3) ; + /// lft = val::new(3) ; /// let res = lft.l_e(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((3,1)) ; + /// assert_eq!{ res, val::new(true) } + /// lft = val::new((3,1)) ; /// let res = lft.l_e(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((3,1)) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new((3,1)) ; /// let res = lft.l_e(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(false) ; /// let res = lft.l_e(& rgt) ; /// # println!("{} <= {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -986,27 +986,27 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(42), val::mk(3)) ; + /// let (mut lft, mut rgt) = (val::new(42), val::new(3)) ; /// let res = lft.l_t(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } + /// assert_eq!{ res, val::new(false) } /// lft = val::none( typ::int() ) ; /// let res = lft.l_t(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(2) ; + /// lft = val::new(2) ; /// let res = lft.l_t(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// lft = val::mk((2,1)) ; + /// assert_eq!{ res, val::new(true) } + /// lft = val::new((2,1)) ; /// let res = lft.l_t(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk((42,1)) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new((42,1)) ; /// let res = lft.l_t(& rgt).unwrap() ; /// # println!("{} <= {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(false) ; /// let res = lft.l_t(& rgt) ; /// # println!("{} <= {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -1050,23 +1050,23 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(true), val::mk(false)) ; + /// let (mut lft, mut rgt) = (val::new(true), val::new(false)) ; /// let res = lft.and(& rgt).unwrap() ; /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } + /// assert_eq!{ res, val::new(false) } /// lft = val::none( typ::bool() ) ; /// let res = lft.and(& rgt).unwrap() ; /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// rgt = val::mk(true) ; + /// assert_eq!{ res, val::new(false) } + /// rgt = val::new(true) ; /// let res = lft.and(& rgt).unwrap() ; /// # println!("{} && {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(true) ; + /// lft = val::new(true) ; /// let res = lft.and(& rgt).unwrap() ; /// # println!("{} && {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(7) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(7) ; /// let res = lft.and(& rgt) ; /// # println!("{} && {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -1095,23 +1095,23 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let (mut lft, mut rgt) = (val::mk(false), val::mk(true)) ; + /// let (mut lft, mut rgt) = (val::new(false), val::new(true)) ; /// let res = lft.or(& rgt).unwrap() ; /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } + /// assert_eq!{ res, val::new(true) } /// lft = val::none( typ::bool() ) ; /// let res = lft.or(& rgt).unwrap() ; /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(true) } - /// rgt = val::mk(false) ; + /// assert_eq!{ res, val::new(true) } + /// rgt = val::new(false) ; /// let res = lft.or(& rgt).unwrap() ; /// # println!("{} || {} = {}", lft, rgt, res) ; /// assert_eq!{ res, val::none( typ::bool() ) } - /// lft = val::mk(false) ; + /// lft = val::new(false) ; /// let res = lft.or(& rgt).unwrap() ; /// # println!("{} || {} = {}", lft, rgt, res) ; - /// assert_eq!{ res, val::mk(false) } - /// rgt = val::mk(7) ; + /// assert_eq!{ res, val::new(false) } + /// rgt = val::new(7) ; /// let res = lft.or(& rgt) ; /// # println!("{} || {} = {:?}", lft, rgt, res) ; /// assert!{ res.is_err() } @@ -1139,19 +1139,19 @@ impl RVal { /// ``` /// use hoice::term::typ ; /// use hoice::val ; - /// let mut buru = val::mk(true) ; + /// let mut buru = val::new(true) ; /// let res = buru.not().unwrap() ; /// # println!("not {} = {}", buru, res) ; - /// assert_eq!{ res, val::mk(false) } - /// buru = val::mk(false) ; + /// assert_eq!{ res, val::new(false) } + /// buru = val::new(false) ; /// let res = buru.not().unwrap() ; /// # println!("not {} = {}", buru, res) ; - /// assert_eq!{ res, val::mk(true) } + /// assert_eq!{ res, val::new(true) } /// buru = val::none( typ::bool() ) ; /// let res = buru.not().unwrap() ; /// # println!("not {} = {}", buru, res) ; /// assert_eq!{ res, buru } - /// buru = val::mk(7) ; + /// buru = val::new(7) ; /// let res = buru.not() ; /// # println!("not {} = {:?}", buru, res) ; /// assert!{ res.is_err() } From 34268b9d47b8c15a23128d62b6db29260ba53287 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Wed, 3 Oct 2018 13:58:40 +0900 Subject: [PATCH 86/94] no more unsafe in function evaluation --- src/fun/mod.rs | 78 +++++++++++------------------------------------- src/term/eval.rs | 37 ++++++++--------------- src/term/zip.rs | 63 ++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 101 deletions(-) diff --git a/src/fun/mod.rs b/src/fun/mod.rs index e3ecacf8..fd6af5dc 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -2,10 +2,6 @@ //! //! In test mode, the `List` datatype is automatically added, as well as a few functions (see the //! [`fun`] module). This is so that dtyp-related doc/lib tests have something to work with. -//! -//! # TODO -//! -//! Move this in the instance to avoid the unsafe code to borrow definitions. use std::sync::{RwLockReadGuard, RwLockWriteGuard}; @@ -16,18 +12,13 @@ pub type Fun = Arc; /// Type of the function factory. /// -/// The usize indicates whether an element of the factory is being borrowed **unsafely** by -/// [`get_as_ref`]. If it is true, then borrowing the factory mutably is unsafe. -/// /// To avoid problems, **always** use the `factory` macro to access the /// factory. -/// -/// [`get_as_ref`]: fn.get_as_ref.html (get_as_ref function) -type Factory = RwLock<(BTreeMap, usize)>; +type Factory = RwLock>; lazy_static! { /// Function factory. static ref factory: Factory = RwLock::new( - ( BTreeMap::new(), 0 ) + BTreeMap::new() ) ; /// Stores function declarations before obtaining the actual definition. @@ -121,7 +112,7 @@ where F: for<'a> FnMut(&'a RFun) -> Res, { if let Ok(defs) = factory.read() { - if let Some(def) = defs.0.get(fun) { + if let Some(def) = defs.get(fun) { return f(def); } } else { @@ -155,7 +146,7 @@ where } /// Read version of the factory. -fn read_factory<'a>() -> RwLockReadGuard<'a, (BTreeMap, usize)> { +fn read_factory<'a>() -> RwLockReadGuard<'a, BTreeMap> { if let Ok(res) = factory.read() { res } else { @@ -163,25 +154,18 @@ fn read_factory<'a>() -> RwLockReadGuard<'a, (BTreeMap, usize)> { } } /// Write version of the factory. -fn write_factory<'a>() -> RwLockWriteGuard<'a, (BTreeMap, usize)> { - loop { - if let Ok(res) = factory.write() { - if res.1 != 0 { - continue; - } - return res; - } else { - panic!("failed to access function factory (write)") - } - } +fn write_factory<'a>() -> RwLockWriteGuard<'a, BTreeMap> { + factory + .write() + .expect("failed to access function factory (write)") } macro_rules! factory { (read) => { - &read_factory().0 + &read_factory() }; (write) => { - &mut write_factory().0 + &mut write_factory() }; } @@ -191,7 +175,7 @@ where F: FnMut(&Fun) -> Res<()>, { let defs = read_factory(); - for def in defs.0.values() { + for def in defs.values() { f(def)? } Ok(()) @@ -212,7 +196,7 @@ pub fn new(fun: RFun) -> Res { /// Groups all functions by dependencies. pub fn ordered() -> Res>> { - let mut all: Vec<_> = read_factory().0.values().cloned().collect(); + let mut all: Vec<_> = read_factory().values().cloned().collect(); let mut groups = vec![]; @@ -359,42 +343,14 @@ pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { Ok(()) } -/// Retrieves the definition of a function as a reference. +/// Retrieves all function definitions. /// -/// This actually uses unsafe code, this kind of borrow should not be possible. -/// If something modifies the factory while the borrow is alive, then it might -/// end up pointing to arbitrary data. -/// -/// It's made safe by keeping track of how many references have been created -/// and preventing modifying the factory as long as this count is not zero. -/// This function hence works in conjunction with [`decrease_ref_count`][link]. -/// When using this function, you must keep track of how many references you -/// have created and when you are sure they're dead, call `decrease_ref_count`. -/// -/// link: fun/fn.decrease_ref_count.html -/// (decrease_ref_count function) -pub fn get_as_ref<'a>(name: &'a str) -> Option<&'a Fun> { - let mut pair = if let Ok(mut f) = factory.write() { +/// This used by term evaluation to zero-copy-evaluate function. +pub fn all_defs<'a>() -> ::std::sync::RwLockReadGuard<'a, BTreeMap> { + if let Ok(f) = factory.read() { f } else { - panic!("failed to access function factory (write)") - }; - pair.1 += 1; - unsafe { ::std::mem::transmute::, Option<&'a Fun>>(pair.0.get(name)) } -} - -pub fn decrease_ref_count(count: usize) { - if count == 0 { - return (); - } - if let Ok(mut f) = factory.write() { - if count <= f.1 { - f.1 -= count - } else { - panic!("trying to decrease ref count for function factory by too much") - } - } else { - panic!("failed to access function factory (write)") + panic!("failed to access function factory (read)") } } diff --git a/src/term/eval.rs b/src/term/eval.rs index 7be1275c..07eeaab6 100644 --- a/src/term/eval.rs +++ b/src/term/eval.rs @@ -24,29 +24,16 @@ pub fn eval(term: &Term, model: &E) -> Res { } } - let mut fun_ref_count = 0; - - let res = zip( - term, - |_| Ok(None), - |zip_null| leaf(model, zip_null), - |op, typ, values| total(op, typ, values, &mut fun_ref_count), + let fun_defs = fun::all_defs(); + + zip_with( + &*term, + &*fun_defs, + |_, _| Ok(None), + |_, zip_null| leaf(model, zip_null), + total, partial, - ); - - fun::decrease_ref_count(fun_ref_count); - // if let Ok(res) = res.as_ref() { - // if model.len() > 0 - // && ! res.is_known() { - // println!("eval {}", term) ; - // for v in 0 .. model.len() { - // println!(" v_{}: {}", v, model.get( v.into() )) - // } - // println!("= {}", res) ; - // pause(" blah", & Profiler::new()) ; - // } - // } - res + ) } macro_rules! go { @@ -70,10 +57,10 @@ fn leaf<'a, E: Evaluator>(model: &E, zip_null: ZipNullary<'a>) -> Res { } fn total<'a>( + fun_defs: &'a BTreeMap, op: ZipOp<'a>, typ: &'a Typ, mut values: Vec, - fun_ref_count: &mut usize, ) -> Res> { let yielded = match op { ZipOp::Op(op) => op @@ -159,12 +146,11 @@ fn total<'a>( }, ZipOp::Fun(name) => { - let fun = if let Some(fun) = fun::get_as_ref(name) { + let fun = if let Some(fun) = fun_defs.get(name) { fun } else { bail!("cannot evaluate unknown function `{}`", conf.bad(name)) }; - *fun_ref_count += 1; if values.len() != fun.sig.len() { bail!( @@ -186,6 +172,7 @@ fn total<'a>( } fn partial<'a>( + _: &BTreeMap, Frame { thing, typ, diff --git a/src/term/zip.rs b/src/term/zip.rs index 37ecb97a..a5ccad81 100644 --- a/src/term/zip.rs +++ b/src/term/zip.rs @@ -4,8 +4,7 @@ //! //! - explain -use std::fmt; -use std::slice::Iter; +use std::{fmt, slice::Iter}; use common::*; @@ -170,6 +169,37 @@ where AppF: for<'a> FnMut(ZipOp<'a>, &'a Typ, Acc) -> Result, E>, Partial: for<'a> FnMut(ZipFrame<'a, Acc>) -> Result, E>, +{ + zip_with( + term, + &(), + |_, term| dwn_do(term), + |_, nul| nul_do(nul), + |_, op, typ, acc| app_do(op, typ, acc), + |_, frame| partial(frame), + ) +} + +/// Zip function. +pub fn zip_with( + term: &Term, + info: &Info, + mut dwn_do: DwnF, + mut nul_do: NulF, + mut app_do: AppF, + mut partial: Partial, +) -> Result +where + Acc: Accumulator, + Yield: Clone, + + DwnF: for<'a> FnMut(&'a Info, &'a Term) -> Result, E>, + + NulF: for<'a> FnMut(&'a Info, ZipNullary<'a>) -> Result, + + AppF: for<'a> FnMut(&'a Info, ZipOp<'a>, &'a Typ, Acc) -> Result, E>, + + Partial: for<'a> FnMut(&'a Info, ZipFrame<'a, Acc>) -> Result, E>, { // Empty vector of terms, useful when handling unary operators. let empty: Vec = Vec::with_capacity(0); @@ -195,7 +225,7 @@ where 'inspect_term: loop { // stack_print!() ; - let result = if let Some(yielded) = dwn_do(term)? { + let result = if let Some(yielded) = dwn_do(info, term)? { ZipDoTotal::Upp { yielded } } else { match *term.get() { @@ -205,12 +235,12 @@ where } } else { ZipDoTotal::Upp { - yielded: nul_do(ZipNullary::Var(typ, var_idx))?, + yielded: nul_do(info, ZipNullary::Var(typ, var_idx))?, } }, RTerm::Cst(ref cst) => ZipDoTotal::Upp { - yielded: nul_do(ZipNullary::Cst(cst))?, + yielded: nul_do(info, ZipNullary::Cst(cst))?, }, RTerm::CArray { @@ -252,7 +282,7 @@ where continue 'inspect_term; } else { - app_do(op, typ, lft_args)? + app_do(info, op, typ, lft_args)? } } @@ -278,7 +308,7 @@ where continue 'inspect_term; } else { - app_do(op, typ, lft_args)? + app_do(info, op, typ, lft_args)? } } @@ -348,7 +378,7 @@ where continue 'inspect_term; } else { - app_do(op, typ, lft_args)? + app_do(info, op, typ, lft_args)? } } } @@ -390,7 +420,7 @@ where lft_args.push(result); if rgt_args.len() == 0 { - match app_do(thing, typ, lft_args)? { + match app_do(info, thing, typ, lft_args)? { ZipDoTotal::Upp { yielded } => { result = yielded; continue 'inspect_do_res; @@ -405,12 +435,15 @@ where } } } else { - match partial(ZipFrame { - thing, - typ, - lft_args, - rgt_args, - })? { + match partial( + info, + ZipFrame { + thing, + typ, + lft_args, + rgt_args, + }, + )? { ZipDo::Trm { nu_term, frame } => { term = nu_term; stack.push((frame, subst.clone())); From f092507c1facabb2c1b20a9f08cf528eeb920713 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 4 Oct 2018 17:57:31 +0900 Subject: [PATCH 87/94] cleaned up fun module + doc and tests --- src/errors.rs | 24 +- src/fun/mod.rs | 472 +++++++++++++++++++++++++++------------ src/parse/mod.rs | 39 +--- src/preproc/fun_preds.rs | 20 +- src/term/factory.rs | 94 ++++---- 5 files changed, 425 insertions(+), 224 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index b350bee1..47f98c80 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -18,9 +18,13 @@ use common::*; /// A term type-checking error. /// -/// Can be created by [`try_app`] when creating operator applications. +/// Can be created by /// -/// [`try_app`]: ../term/fn.try_app.html (try_app function) +/// - [`term::try_app`] when creating operator applications +/// - [`term::try_fun`] when creating function calls +/// +/// [`term::try_app`]: ../term/fn.try_app.html (try_app function) +/// [`term::try_fun`]: ../term/fn.try_fun.html (try_fun function) #[derive(Clone, Debug)] pub enum TypError { /// No type info, just an error message. @@ -36,6 +40,22 @@ pub enum TypError { index: usize, }, } +impl_fmt! { + TypError(self, fmt) { + match self { + TypError::Msg(s) => write!(fmt, "{}", s), + TypError::Typ { + expected, obtained, index + } => write!(fmt, + "error on argument #{}: found {} expected {}", + index, obtained, + expected.as_ref().map( + |t| format!("{}", t) + ).unwrap_or_else(|| "something else".to_string()) + ) + } + } +} impl TypError { /// Message constructor. pub fn msg>(s: S) -> Self { diff --git a/src/fun/mod.rs b/src/fun/mod.rs index fd6af5dc..f74607e0 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1,19 +1,119 @@ //! Hash consed functions. //! -//! In test mode, the `List` datatype is automatically added, as well as a few functions (see the -//! [`fun`] module). This is so that dtyp-related doc/lib tests have something to work with. +//! "Functions" of this module are user-defined recursive functions and non-necessarily recursive +//! function create by hoice, typically when working with datatypes. Function can be evaluated and +//! can be used as qualifiers (for datatypes, typically). +//! +//! # Usage +//! +//! Creating (mutually) recursive is tricky: when constructing the body of the function(s), the +//! definition does not really exist yet ---since we're constructing the body which is part of the +//! definition. So if `f` calls itself recursively, we cannot create the term `(f )` because +//! `f` does not really exist yet: hoice will fail to check that the call is legal because it +//! cannot retrieve `f`'s definition. +//! +//! To address this problem, this module allows to register some *function declarations*. The idea +//! is to +//! +//! - create a [`FunSig`] for all mutually recursive functions +//! - register these signatures using [`register_sig`], meaning that they become visible during +//! term creation +//! - construct the bodies, including the recursive calls which are now legal +//! - retrieve the signatures with [`retrieve_sig`] and add the bodies constructed in the previous +//! step +//! - create the [`RFun`] from the `FunSig`, and register it as a function using [`new`] +//! +//! # Examples +//! +//! Consider the following function +//! +//! ```lisp +//! (define-funs-rec +//! ( +//! (len ( (l (List Int)) ) Int) +//! ) +//! ( +//! (ite +//! (= l nil) +//! 0 +//! (+ 1 (len (tail l))) +//! ) +//! ) +//! ) +//! ``` +//! +//! Then creating this function works as follows. +//! +//! ```rust +//! use hoice::{ common::*, info::VarInfo }; +//! let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); +//! let fun_name = "fun_test_len"; +//! +//! let mut var_infos = VarInfos::new(); +//! let v_0 = var_infos.next_index(); +//! var_infos.push( VarInfo::new("l", int_list.clone(), v_0) ); +//! +//! let signature = fun::FunSig::new(fun_name, var_infos, typ::int()); +//! fun::register_sig(signature).expect("while registering function signature"); +//! +//! // Function declared, we can now create terms that use it. +//! let v_0 = term::var(v_0, int_list.clone()); +//! let tail_of_v_0 = term::dtyp_slc(int_list.clone(), "tail", v_0.clone()); +//! let rec_call = term::fun(fun_name, vec![ tail_of_v_0 ]); +//! let body = term::ite( +//! term::eq(v_0.clone(), term::dtyp_new(int_list.clone(), "nil", vec![])), +//! term::int(0), +//! term::add( vec![ term::int(1), rec_call ] ) +//! ); +//! assert_eq! { +//! format!("(ite (not (is-insert v_0)) 0 (+ ({} (tail v_0)) 1))", fun_name), +//! format!("{}", body) +//! } +//! +//! // Retrieve and complete function definition. +//! let sig = fun::retrieve_sig(fun_name).expect("while retrieving signature"); +//! let def = sig.into_fun(body); +//! let _ = fun::new(def).expect("while hashconsing function"); +//! +//! // Done! +//! let len_app = term::fun( +//! fun_name, vec![ +//! term::dtyp_new( +//! int_list.clone(), "insert", vec![ +//! term::int_var(0), term::dtyp_new(int_list.clone(), "nil", vec![]) +//! ] +//! ) +//! ] +//! ); +//! assert_eq! { +//! format!("({} (insert v_0 (as nil (List Int))))", fun_name), +//! format!("{}", len_app) +//! } +//! +//! let model: VarMap = vec![val::int(7)].into(); +//! assert_eq! { len_app.eval(& model).expect("during evaluation"), val::int(1) } +//! ``` +//! +//! # Testing +//! +//! The [`test`] module allows to create a length function over lists and should only be used in +//! documentation/unit tests. The module does not exist in `release`. +//! +//! [`FunSig`]: struct.FunSig.html (FunSig struct) +//! [`register_sig`]: fn.register_sig.html (register_sig function) +//! [`retrieve_sig`]: fn.retrieve_sig.html (retrieve_sig function) +//! [`Fun`]: type.Fun.html (Fun type) +//! [`new`]: fn.new.html (new function) +//! [`test`]: test/index.hmtl (function test module) use std::sync::{RwLockReadGuard, RwLockWriteGuard}; use common::*; -/// A function. +/// A hashconsed function. pub type Fun = Arc; /// Type of the function factory. -/// -/// To avoid problems, **always** use the `factory` macro to access the -/// factory. type Factory = RwLock>; lazy_static! { /// Function factory. @@ -21,53 +121,19 @@ lazy_static! { BTreeMap::new() ) ; - /// Stores function declarations before obtaining the actual definition. - static ref fun_decs: RwLock< BTreeMap > = RwLock::new( + /// Stores function signatures to construct the functions' bodies. + static ref fun_sigs: RwLock< BTreeMap > = RwLock::new( BTreeMap::new() ) ; } -/// Test-related stuff for functions. -pub mod test { - - /// Name of the length function over `(List Int)` (test mode only). - pub fn length_fun_name() -> &'static str { - "length" - } - - /// Creates the list datatype and a length function over `(List Int)` this should only be used - /// in (doc) tests. - pub fn create_length_fun() { - use super::*; - let name = length_fun_name(); - if get(name).is_some() { - return (); - } - - ::parse::fun_dtyp(&format!( - "\ - (define-funs-rec - ( - ({name} ( (l (List Int)) ) Int) - ) - ( - (ite - (= l nil) - 0 - (+ 1 ({name} (tail l))) - ) - ) - ) - (exit) - ", - name = name - )) - } -} - -/// Registers a function declaration. -pub fn register_dec(fun: RFun) -> Res<()> { - if let Ok(mut decs) = fun_decs.write() { +/// Registers a function signature. +/// +/// Used to create (mutually) recursive function(s), see [module-level documentation]. +/// +/// [module-level documentation]: index.html (module-level documentation) +pub fn register_sig(fun: FunSig) -> Res<()> { + if let Ok(mut decs) = fun_sigs.write() { let prev = decs.insert(fun.name.clone(), fun); if let Some(prev) = prev { bail!("the function {} is declared twice", conf.bad(&prev.name)) @@ -78,21 +144,25 @@ pub fn register_dec(fun: RFun) -> Res<()> { Ok(()) } -/// Retrieves a function declaration. -pub fn retrieve_dec(fun: &str) -> Res { - if let Ok(mut decs) = fun_decs.write() { - if let Some(dec) = decs.remove(fun) { - Ok(dec) +/// Retrieves a function signature. +/// +/// Used to create (mutually) recursive function(s), see [module-level documentation]. +/// +/// [module-level documentation]: index.html (module-level documentation) +pub fn retrieve_sig(fun: &str) -> Res { + if let Ok(mut sigs) = fun_sigs.write() { + if let Some(sig) = sigs.remove(fun) { + Ok(sig) } else { let mut s = format!( - "trying to retrieve declaration for unknown function {}\n", + "trying to retrieve signature for unknown function {}\n", conf.bad(fun) ); - if decs.is_empty() { - s += "no function declaration present" + if sigs.is_empty() { + s += "no function signature present" } else { - s += "function(s) declared:"; - for (name, _) in decs.iter() { + s += "function(s) signatures available:"; + for (name, _) in sigs.iter() { s += " "; s += name; s += "," @@ -106,42 +176,47 @@ pub fn retrieve_dec(fun: &str) -> Res { } } -/// Accesses the declaration of a function. -pub fn dec_do(fun: &str, mut f: F) -> Res +/// Accesses the signature of a function, if any. +/// +/// Used to type-check function calls. +pub fn sig_do(fun: &str, mut f: F) -> Result where - F: for<'a> FnMut(&'a RFun) -> Res, + F: for<'a> FnMut(&'a FunSig) -> Result, { + use errors::TypError; if let Ok(defs) = factory.read() { if let Some(def) = defs.get(fun) { - return f(def); + return f(&def.info); } } else { - bail!("unable to retrieve function factory") + return Err(TypError::Msg("unable to retrieve function factory".into())); } - if let Ok(defs) = fun_decs.read() { - if let Some(def) = defs.get(fun) { - f(def) + if let Ok(sigs) = fun_sigs.read() { + if let Some(sig) = sigs.get(fun) { + f(sig) } else { let mut s = format!( - "trying to retrieve declaration for unknown function {}\n", + "trying to retrieve signature for unknown function {}\n", conf.bad(fun) ); - if defs.is_empty() { - s += "no function declaration present" + if sigs.is_empty() { + s += "no function signature present" } else { - s += "function(s) declared:"; - for (name, _) in defs.iter() { + s += "function(s) signatures available:"; + for (name, _) in sigs.iter() { s += " "; s += name; s += "," } } - bail!(s) + return Err(TypError::Msg(s)); } } else { - bail!("unable to retrieve function declarations") + return Err(TypError::Msg( + "unable to retrieve function declarations".into(), + )); } } @@ -182,20 +257,40 @@ where } /// Creates a function definition. +/// +/// Fails if the function is already defined. +/// +/// # Examples +/// +/// ```rust +/// use hoice::{ common::*, fun, info::VarInfo }; +/// let sig: VarInfos = vec![ VarInfo::new("v_0", typ::int(), 0.into()) ].into(); +/// let fun_name = "fun_new_test_identity"; +/// let sig = fun::FunSig::new(fun_name, sig, typ::int()); +/// let def = sig.into_fun( term::int_var(0) ); +/// fun::new(def.clone()).expect("during first function registration"); +/// assert_eq! { +/// format!("{}", fun::new(def).unwrap_err()), +/// format!("attempting to redefine function `{}`", fun_name) +/// } +/// ``` pub fn new(fun: RFun) -> Res { let fun = Arc::new(fun); let f = factory!(write); - let prev = f.insert(fun.name.clone(), fun.clone()); + let prev = f.insert(fun.name().clone(), fun.clone()); if let Some(prev) = prev { - bail!("attempting to redefine function `{}`", prev.name) + bail!("attempting to redefine function `{}`", prev.name()) } Ok(fun) } /// Groups all functions by dependencies. -pub fn ordered() -> Res>> { +/// +/// Returns a list of functions classes. A function class is a list of function that depend on each +/// other, meaning that they must be defined together at SMT-level. +fn ordered() -> Res>> { let mut all: Vec<_> = read_factory().values().cloned().collect(); let mut groups = vec![]; @@ -204,7 +299,7 @@ pub fn ordered() -> Res>> { let mut group = vec![fun]; if !group[0].deps.is_empty() { all.retain(|fun| { - if group[0].deps.contains(&fun.name) { + if group[0].deps.contains(fun.name()) { group.push(fun.clone()); false } else { @@ -219,7 +314,7 @@ pub fn ordered() -> Res>> { } /// Defines a function, and all functions related to it. -pub fn write(w: &mut W, fun: &RFun, pref: &str) -> Res<()> +fn write(w: &mut W, fun: &RFun, pref: &str) -> Res<()> where W: Write, { @@ -234,7 +329,7 @@ where } else { bail!( "function `{}` depends on unknown function `{}`", - conf.emph(&fun.name), + conf.emph(fun.name()), conf.bad(&dep) ) } @@ -245,11 +340,11 @@ where // Write all signatures. for fun in &all { write!(w, "{} (", pref)?; - write!(w, "{} (", fun.name)?; - for info in &fun.sig { + write!(w, "{} (", fun.name())?; + for info in fun.sig() { write!(w, " ({} {})", info.idx.default_str(), info.typ)? } - writeln!(w, " ) {})", fun.typ)? + writeln!(w, " ) {})", fun.typ())? } writeln!(w, "{}) (", pref)?; @@ -266,7 +361,7 @@ where Ok(()) } -/// Defines all the functions. +/// Defines all the functions in SMT-LIB. pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { for mut group in ordered()? { if group.len() == 1 { @@ -278,12 +373,12 @@ pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { consts::keywords::cmd::def_fun }; - writeln!(w, "{}({} {}", pref, def_key, fun.name)?; + writeln!(w, "{}({} {}", pref, def_key, fun.name())?; write!(w, "{} (", pref)?; - for info in &fun.sig { + for info in fun.sig() { write!(w, " ({} {})", info.idx.default_str(), info.typ)? } - writeln!(w, " ) {}", fun.typ)?; + writeln!(w, " ) {}", fun.typ())?; write!(w, "{} ", pref)?; @@ -295,11 +390,11 @@ pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { // Write all signatures. for fun in &group { write!(w, "{} (", pref)?; - write!(w, "{} (", fun.name)?; - for info in &fun.sig { + write!(w, "{} (", fun.name())?; + for info in fun.sig() { write!(w, " ({} {})", info.idx.default_str(), info.typ)? } - writeln!(w, " ) {})", fun.typ)? + writeln!(w, " ) {})", fun.typ())? } writeln!(w, "{}) (", pref)?; @@ -324,7 +419,7 @@ pub fn write_all(w: &mut W, pref: &str, invariants: bool) -> Res<()> { writeln!(w, "{}(assert", pref)?; writeln!(w, "{} (forall", pref)?; write!(w, "{} (", pref)?; - for info in &fun.sig { + for info in fun.sig() { write!(w, " ({} {})", info.idx.default_str(), info.typ)? } writeln!(w, " )")?; @@ -360,63 +455,84 @@ pub fn get(name: &str) -> Option { f.get(name).cloned() } -/// Types and creates a function application. -pub fn type_apply( - name: String, - var_info: &VarInfos, - args: Vec, -) -> Result { - if args.len() != var_info.len() { - return Err(TypError::Msg(format!( - "function `{}` is applied to {} arguments, expected {}", - conf.emph(name), - args.len(), - var_info.len() - ))); - } - - for (arg, info) in args.iter().zip(var_info.iter()) { - if !arg.typ().is_compatible(&info.typ) { - return Err(TypError::Typ { - expected: Some(info.typ.clone()), - obtained: arg.typ().clone(), - index: *info.idx, - }); - } +/// A function signature, used when creating (mutually) recursive function(s). +#[derive(Debug, Clone)] +pub struct FunSig { + /// Name. + pub name: String, + /// Signature. + pub sig: VarInfos, + /// Type. + pub typ: Typ, +} +impl PartialEq for FunSig { + fn eq(&self, other: &Self) -> bool { + self.name == other.name } +} +impl Eq for FunSig {} - Ok(term::fun(name, args)) +impl PartialOrd for FunSig { + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + self.name.partial_cmp(&other.name) + } +} +impl Ord for FunSig { + fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + self.name.cmp(&other.name) + } } -/// Creates a function application. -pub fn apply(name: String, args: Vec) -> Result { - use errors::TypError; +impl ::std::hash::Hash for FunSig { + fn hash(&self, state: &mut H) { + self.name.hash(state) + } +} +impl FunSig { + /// Constructor. + pub fn new(name: S, sig: VarInfos, typ: Typ) -> Self + where + S: Into, + { + FunSig { + name: name.into(), + sig, + typ, + } + } - let def = if let Some(def) = get(&name) { - def - } else { - return Err(TypError::Msg(format!( - "unknown function `{}`", - conf.bad(name) - ))); - }; + /// Sets the definition of a function. + pub fn into_fun(self, def: Term) -> RFun { + let mut deps = BTreeSet::new(); + let mut recursive = false; + def.iter(|trm| { + if let Some((name, _)) = trm.fun_inspect() { + if name == &self.name { + recursive = true + } else { + deps.insert(name.to_string()); + } + } + }); - type_apply(name, &def.sig, args) + RFun { + info: self, + deps, + def, + synthetic: None, + invariants: TermSet::new(), + recursive, + } + } } /// Real structure for functions. #[derive(Debug, Clone)] pub struct RFun { - /// Name. - pub name: String, + /// Signature. + pub info: FunSig, /// Other functions this function depends on. pub deps: BTreeSet, - /// Signature. - /// - /// The string stored is the original name of the argument. - pub sig: VarInfos, - /// Type. - pub typ: Typ, /// Definition. pub def: Term, /// The index of the predicate this function was created for. @@ -427,27 +543,34 @@ pub struct RFun { recursive: bool, } +impl ::std::ops::Deref for RFun { + type Target = FunSig; + fn deref(&self) -> &FunSig { + &self.info + } +} + impl PartialEq for RFun { fn eq(&self, other: &Self) -> bool { - self.name == other.name + self.info.name == other.info.name } } impl Eq for RFun {} impl PartialOrd for RFun { fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - self.name.partial_cmp(&other.name) + self.info.name.partial_cmp(&other.info.name) } } impl Ord for RFun { fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - self.name.cmp(&other.name) + self.info.name.cmp(&other.info.name) } } impl ::std::hash::Hash for RFun { fn hash(&self, state: &mut H) { - self.name.hash(state) + self.info.name.hash(state) } } @@ -459,10 +582,8 @@ impl RFun { pub fn new>(name: S, sig: VarInfos, typ: Typ) -> Self { let name = name.into(); RFun { - name, + info: FunSig::new(name, sig, typ), deps: BTreeSet::new(), - sig, - typ, def: term::tru(), synthetic: None, invariants: TermSet::new(), @@ -470,12 +591,28 @@ impl RFun { } } + /// Name of the function. + #[inline] + pub fn name(&self) -> &String { + &self.info.name + } + /// Signature of the function. + #[inline] + pub fn sig(&self) -> &VarInfos { + &self.info.sig + } + /// Type of the function. + #[inline] + pub fn typ(&self) -> &Typ { + &self.info.typ + } + /// Insert a dependency. /// /// Only inserts if `dep` is not `self.name`. pub fn insert_dep>(&mut self, dep: S) -> bool { let dep = dep.into(); - if self.name == dep { + if self.info.name == dep { false } else { self.deps.insert(dep) @@ -501,7 +638,7 @@ impl RFun { pub fn set_def(&mut self, def: Term) { def.iter(|trm| { if let Some((name, _)) = trm.fun_inspect() { - if name == &self.name { + if name == self.name() { self.recursive = true } else { self.deps.insert(name.to_string()); @@ -521,7 +658,7 @@ impl RFun { if get(dep).is_none() { bail!( "function `{}` depends on unknown function `{}`", - conf.emph(&self.name), + conf.emph(self.name()), conf.bad(dep) ) } @@ -560,14 +697,14 @@ impl Functions { let mut from_to_typ = vec![]; 'find_funs: for fun in f.values() { - let mut sig = fun.sig.iter(); + let mut sig = fun.sig().iter(); let ftyp = match sig.next() { Some(info) => info.typ == typ && sig.next().is_none(), _ => false, }; - let ttyp = fun.typ == typ; + let ttyp = fun.typ() == &typ; match (ftyp, ttyp) { (true, true) => from_to_typ.push(fun.clone()), @@ -585,3 +722,44 @@ impl Functions { } } } + +/// Test-related stuff for functions. +/// +/// This module is not available in `release` as it should only be used for testing purposes. +#[cfg(debug_assertions)] +pub mod test { + + /// Name of the length function over `(List Int)` (test mode only). + pub fn length_fun_name() -> &'static str { + "length" + } + + /// Creates the list datatype and a length function over `(List Int)` this should only be used + /// in (doc) tests. + pub fn create_length_fun() { + use super::*; + let name = length_fun_name(); + if get(name).is_some() { + return (); + } + + ::parse::fun_dtyp(&format!( + "\ + (define-funs-rec + ( + ({name} ( (l (List Int)) ) Int) + ) + ( + (ite + (= l nil) + 0 + (+ 1 ({name} (tail l))) + ) + ) + ) + (exit) + ", + name = name + )) + } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index bc328f67..cee73493 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1100,11 +1100,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { return Ok(false); } - use fun::RFun; + use fun::FunSig; self.functions.clear(); - let mut funs: Vec<(RFun, Pos, _)> = vec![]; + let mut funs: Vec<(FunSig, Pos, _)> = vec![]; self.ws_cmt(); self.tag("(") @@ -1182,8 +1182,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { .sort() .chain_err(|| format!("sort of function `{}`", conf.emph(name)))?; - let mut fun = RFun::new(name, args, typ); - fun::register_dec(fun.clone())?; + let fun = FunSig::new(name, args, typ); + fun::register_sig(fun.clone())?; // Check this is the first time we see this function and populate // dependencies. @@ -1196,9 +1196,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { ).into(); bail!(e.chain_err(|| self.error(*other_pos, "first appearance"))) } - - other.insert_dep(fun.name.clone()); - fun.insert_dep(other.name.clone()); } let prev = self @@ -1221,7 +1218,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { // Parse all definitions. for (mut fun, pos, var_map) in funs { - if let Some(term) = self + let def = if let Some(term) = self .term_opt(&fun.sig, &var_map, instance) .chain_err(|| { format!( @@ -1230,21 +1227,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) }).chain_err(|| self.error(pos, "declared here"))? { + self.ws_cmt(); // Success. - fun.set_def(term); - self.ws_cmt() + let sig = fun::retrieve_sig(&fun.name)?; + sig.into_fun(term) } else { let e: Error = self .error_here(format!( "expected definition (term) for function `{}`", - conf.emph(&fun.name) + conf.emph(fun.name) )).into(); bail!(e.chain_err(|| self.error(pos, "declared here"))) - } - - let _ = fun::retrieve_dec(&fun.name)?; + }; - fun::new(fun).chain_err(|| self.error(pos, "while registering this function"))?; + fun::new(def).chain_err(|| self.error(pos, "while registering this function"))?; } self.ws_cmt(); @@ -2074,18 +2070,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { ) -> Res<(Term, Pos)> { use errors::TypError; - let res = if let Some((var_infos, _)) = self.functions.get(&name as &str) { - // Function application for one of the functions we are currently parsing - // the definition of? (i.e. the function is not registered yet) - fun::type_apply(name, var_infos, args) - } else { - // Function should already exist. - fun::apply(name, args) - }; - - // Parsing a application of a function that's already defined. - - match res { + match term::try_fun(name, args) { Ok(term) => Ok((term, name_pos)), Err(TypError::Typ { diff --git a/src/preproc/fun_preds.rs b/src/preproc/fun_preds.rs index e2a14ab5..c16460e9 100644 --- a/src/preproc/fun_preds.rs +++ b/src/preproc/fun_preds.rs @@ -8,7 +8,7 @@ //! [`map_invert`]: (fn.map_invert.html) (map_invert function) use common::*; -use fun::RFun; +use fun::FunSig; use info::VarInfo; use preproc::{Clause, PreInstance, RedStrat}; @@ -719,11 +719,13 @@ impl FunDef { // Finally creating the function. let pred = self.pred; - let mut dec = fun::retrieve_dec(&self.name)?; - dec.set_def(def); - dec.invariants.extend(invs); + let sig = fun::retrieve_sig(&self.name)?; + let mut rfun = sig.into_fun(def); - let fun = fun::new(dec).chain_err(|| { + rfun.set_synthetic(pred); + rfun.invariants.extend(invs); + + let fun = fun::new(rfun).chain_err(|| { format!( "while creating internal function for predicate {}", conf.bad(&instance[pred].name) @@ -839,18 +841,16 @@ impl FunPreds { typ::bool() }; - let mut rfun = RFun::new( + let sig = FunSig::new( pred_fun_name.clone(), var_infos.clone(), pred_fun_typ.clone(), ); - rfun.set_synthetic(pred); - - fun::register_dec(rfun)?; + fun::register_sig(sig)?; macro_rules! abort { ($($stuff:tt)*) => {{ - let _ = fun::retrieve_dec(&pred_fun_name); + let _ = fun::retrieve_sig(&pred_fun_name); return Ok(None); }}; } diff --git a/src/term/factory.rs b/src/term/factory.rs index 32c3a8c0..59f993be 100644 --- a/src/term/factory.rs +++ b/src/term/factory.rs @@ -699,10 +699,21 @@ pub fn select(array: Term, idx: Term) -> Term { /// Function application. /// -/// # Panics +/// Panics when [`try_fun`] fails. See [`try_fun`] for examples. /// -/// - if the function does not exist -/// - if the arguments are illegal +/// [`try_fun`]: fn.try_fun.html (try_fun function) +#[inline] +pub fn fun(name: S, args: Vec) -> Term +where + S: Into, +{ + match try_fun(name, args) { + Ok(res) => res, + Err(e) => panic!("{}", e), + } +} + +/// Function application. /// /// # Examples /// @@ -711,10 +722,10 @@ pub fn select(array: Term, idx: Term) -> Term { /// fun::test::create_length_fun(); /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); /// -/// let _ = term::fun( +/// let _ = term::try_fun( /// fun::test::length_fun_name(), /// vec![ term::dtyp_new(list.clone(), "nil", vec![]) ], -/// ); +/// ).expect("during function call creation"); /// ``` /// /// Constant arguments: @@ -724,84 +735,91 @@ pub fn select(array: Term, idx: Term) -> Term { /// fun::test::create_length_fun(); /// let list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); /// -/// let cst = term::fun( +/// let cst = term::try_fun( /// fun::test::length_fun_name(), /// vec![term::dtyp_new( /// list.clone(), "insert", vec![term::int(7), term::dtyp_new(list.clone(), "nil", vec![])] /// )], -/// ); +/// ).expect("during function call creation"); /// assert_eq! { cst.val().unwrap(), val::int(1) } /// ``` /// -/// Ill-typed application (panic): +/// Ill-typed application: /// -/// ```rust,should_panic +/// ```rust /// use hoice::common::*; /// fun::test::create_length_fun(); /// -/// let _ = term::fun( // This panics. +/// let err = term::try_fun( /// fun::test::length_fun_name(), vec![ term::int(7) ], -/// ); +/// ).unwrap_err(); +/// assert_eq! { +/// format!("{}", err), +/// format!( +/// "ill-typed application of function {}, 7 does not have type (List Int)", +/// fun::test::length_fun_name() +/// ) +/// } /// ``` /// /// Function does not exist (panic): /// -/// ```rust,should_panic +/// ```rust /// # use hoice::common::*; -/// let _ = term::fun( // This panics. +/// let err = term::try_fun( /// "unknown_function", vec![ term::int_var(0) ] -/// ); +/// ).unwrap_err(); +/// assert_eq! { +/// format!("{}", err), +/// "trying to retrieve signature for unknown function unknown_function\n\ +/// no function signature present".to_string() +/// } /// ``` #[inline] -pub fn fun(name: S, mut args: Vec) -> Term +pub fn try_fun(name: S, mut args: Vec) -> Result where S: Into, { let name = name.into(); let mut all_args_constant = true; - match fun::dec_do(&name, |fun| { - if args.len() != fun.sig.len() { - panic!( + + fun::sig_do(&name, |info| { + if args.len() != info.sig.len() { + return Err(TypError::Msg(format!( "illegal application of function {} to {} arguments, expected {}", conf.bad(&name), args.len(), - fun.sig.len() - ) + info.sig.len(), + ))); } - for (info, arg) in fun.sig.iter().zip(args.iter_mut()) { + for (info, arg) in info.sig.iter().zip(args.iter_mut()) { if arg.val().is_none() { all_args_constant = false } if let Some(nu_arg) = arg.force_dtyp(info.typ.clone()) { *arg = nu_arg } else if info.typ != arg.typ() { - panic!( + return Err(TypError::Msg(format!( "ill-typed application of function {}, {} does not have type {}", conf.bad(&name), arg, info.typ - ) + ))); } } - Ok(fun.typ.clone()) - }) { - Ok(typ) => { - let term = factory.mk(RTerm::new_fun(typ, name, args)); - if all_args_constant { - if let Ok(val) = term.eval(&()) { - cst(val) - } else { - term - } + Ok(info.typ.clone()) + }).map(|typ| { + let term = factory.mk(RTerm::new_fun(typ, name, args)); + if all_args_constant { + if let Ok(val) = term.eval(&()) { + cst(val) } else { term } + } else { + term } - Err(e) => { - print_err(&e); - panic!("illegal function application") - } - } + }) } /// Creates an operator application. From 8fb0934d513e7fc87c4e2255c73361eb1255dfab Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Thu, 4 Oct 2018 18:34:00 +0900 Subject: [PATCH 88/94] more fun module documentation and tests --- src/fun/mod.rs | 95 ++++++++++++++++++++++++++++++------------------ src/parse/mod.rs | 6 +-- 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/src/fun/mod.rs b/src/fun/mod.rs index f74607e0..197d847d 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -456,6 +456,10 @@ pub fn get(name: &str) -> Option { } /// A function signature, used when creating (mutually) recursive function(s). +/// +/// For details, see [module-level documentation]. +/// +/// [module-level documentation]: index.html (module-level documentation) #[derive(Debug, Clone)] pub struct FunSig { /// Name. @@ -501,7 +505,7 @@ impl FunSig { } } - /// Sets the definition of a function. + /// Transforms a signature in a function definition. pub fn into_fun(self, def: Term) -> RFun { let mut deps = BTreeSet::new(); let mut recursive = false; @@ -527,6 +531,10 @@ impl FunSig { } /// Real structure for functions. +/// +/// For details, see [module-level documentation]. +/// +/// [module-level documentation]: index.html (module-level documentation) #[derive(Debug, Clone)] pub struct RFun { /// Signature. @@ -607,18 +615,6 @@ impl RFun { &self.info.typ } - /// Insert a dependency. - /// - /// Only inserts if `dep` is not `self.name`. - pub fn insert_dep>(&mut self, dep: S) -> bool { - let dep = dep.into(); - if self.info.name == dep { - false - } else { - self.deps.insert(dep) - } - } - /// Flips the flag indicating that the function was created internally for a /// predicate. pub fn set_synthetic(&mut self, pred: PrdIdx) { @@ -630,28 +626,6 @@ impl RFun { self.recursive } - /// Sets the definition of a function. - /// - /// # Panics - /// - /// - if `self.def` is not `term::tru()` - pub fn set_def(&mut self, def: Term) { - def.iter(|trm| { - if let Some((name, _)) = trm.fun_inspect() { - if name == self.name() { - self.recursive = true - } else { - self.deps.insert(name.to_string()); - } - } - }); - match *self.def { - RTerm::Cst(ref cst) if cst.is_true() => (), - _ => panic!("trying to set the definition of a function twice"), - } - self.def = def - } - /// Checks the function is legal. pub fn check(&self) -> Res<()> { for dep in &self.deps { @@ -676,6 +650,55 @@ impl RFun { } /// Stores functions from and to some type. +/// +/// Automatically retrieves everything on creation. Only considers unary functions. +/// +/// # Examples +/// +/// ```rust +/// use hoice::{ parse, fun::Functions, common::* }; +/// let ( +/// lst_name, len_name, rpt_name, rdc_name +/// ) = ( +/// "FunFunctionsTestLst", +/// "FunFunctionsTestLen", +/// "FunFunctionsTestRpt", +/// "FunFunctionsTestRdc", +/// ); +/// parse::fun_dtyp(&format!(" +/// (declare-datatypes ( ({lst_name} 1) ) ( +/// (par (T) ( +/// (nil) +/// (cons (head T) (tail ({lst_name} T))) +/// ) ) +/// ) ) +/// (define-funs-rec +/// ( ({len_name} ( (l ({lst_name} Int)) ) Int) ) +/// ( (ite (= l nil) 0 (+ 1 ({len_name} (tail l)))) ) +/// ) +/// (define-funs-rec +/// ( ({rpt_name} ( (n Int) ) ({lst_name} Int)) ) +/// ( (ite (= n 0) nil (cons n ({rpt_name} (- n 1)))) ) +/// ) +/// (define-funs-rec +/// ( ({rdc_name} ( (l ({lst_name} Int)) ) ({lst_name} Int)) ) +/// ( (ite (= l nil) nil (tail l)) ) +/// ) +/// (exit) +/// ", +/// lst_name = lst_name, +/// len_name = len_name, +/// rpt_name = rpt_name, +/// rdc_name = rdc_name, +/// )); +/// let int_lst = typ::dtyp( +/// dtyp::get(lst_name).expect("while retrieving dtyp"), vec![typ::int()].into() +/// ); +/// let funs = Functions::new(int_lst); +/// assert_eq! { &funs.from_typ[0].name, len_name } +/// assert_eq! { &funs.to_typ[0].name, rpt_name } +/// assert_eq! { &funs.from_to_typ[0].name, rdc_name } +/// ``` #[derive(Debug, Clone)] pub struct Functions { /// Type these functions are for. @@ -697,7 +720,7 @@ impl Functions { let mut from_to_typ = vec![]; 'find_funs: for fun in f.values() { - let mut sig = fun.sig().iter(); + let mut sig = fun.sig.iter(); let ftyp = match sig.next() { Some(info) => info.typ == typ && sig.next().is_none(), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index cee73493..c88ef21c 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2503,8 +2503,6 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// Tries to parse a `define-fun`. fn define_fun(&mut self, instance: &mut Instance) -> Res { - use fun::RFun; - if !self.word_opt(keywords::cmd::def_fun) { return Ok(false); } @@ -2547,8 +2545,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { } if let Some(term) = body.to_term()? { - let mut fun = RFun::new(name, var_info, out_sort); - fun.set_def(term); + use fun::FunSig; + let fun = FunSig::new(name, var_info, out_sort).into_fun(term); let _ = fun::new(fun) .chain_err(|| self.error(name_pos, "while registering this function"))?; () From 3591034b3cfb6e1bacf63f48e936075e6c691c4d Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 9 Oct 2018 14:08:30 +0900 Subject: [PATCH 89/94] parse module simplifications + doc + tests --- src/dtyp/mod.rs | 24 +- src/parse/mod.rs | 626 ++++++++++++++++++++----------------------- src/term/simplify.rs | 2 +- src/term/typ.rs | 2 +- src/val/mod.rs | 2 +- 5 files changed, 310 insertions(+), 346 deletions(-) diff --git a/src/dtyp/mod.rs b/src/dtyp/mod.rs index 603414ff..22924d01 100644 --- a/src/dtyp/mod.rs +++ b/src/dtyp/mod.rs @@ -365,10 +365,13 @@ impl PartialTyp { /// assert_eq! { ¶ms[param_0], &typ::unk() } /// ptyp.unify(&int_list, &mut params).unwrap(); /// assert_eq! { ¶ms[param_0], &typ::int() } - /// let typ = ptyp.to_type(¶ms).unwrap(); + /// let typ = ptyp.to_type(Some(¶ms)).unwrap(); /// assert_eq! { int_list, typ } /// ``` - pub fn to_type(&self, prms: &TPrmMap) -> Result { + pub fn to_type( + &self, + prms: Option<&TPrmMap>, + ) -> Result, String)> { enum Frame<'a> { ArrayLft(&'a PartialTyp), ArrayRgt(Typ), @@ -394,7 +397,7 @@ impl PartialTyp { let dtyp = if let Ok(dtyp) = get(name) { dtyp } else { - return Err((*pos, "unknown datatype".into())); + return Err((Some(*pos), "unknown datatype".into())); }; if let Some(partial) = prms.next() { @@ -409,7 +412,14 @@ impl PartialTyp { PartialTyp::Typ(typ) => typ.clone(), - PartialTyp::Param(idx) => prms[*idx].clone(), + PartialTyp::Param(idx) => { + if let Some(prms) = prms { + prms[*idx].clone() + } else { + return Err( + (None, "trying to convert a partial sort in a total one without type parameters".into())); + } + } }; 'go_up: loop { @@ -1021,19 +1031,19 @@ pub fn type_selector( selector: &str, slc_pos: ::parse::Pos, term: &Term, -) -> Result { +) -> Result, String)> { if let Some((dtyp, prms)) = term.typ().dtyp_inspect() { for args in dtyp.news.values() { for (slc, partial_typ) in args { if slc == selector { - return partial_typ.to_type(prms); + return partial_typ.to_type(Some(prms)); } } } } Err(( - slc_pos, + Some(slc_pos), format!( "`{}` is not a legal selector for sort {}", conf.bad(selector), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index c88ef21c..e0dda8b5 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,4 +1,70 @@ -//! SMT-LIB 2 horn clause problem parser. +//! SMT-LIB 2 Horn clause parser. +//! +//! Parsing is with done with two structures. [`ParserCxt`], the *context*, stores collections used +//! to build terms during parsing. [`Parser`] wraps a context, stores the text being parsed provide +//! cursor-like functionalities as well as the actual parsing functions. +//! +//! The workflow for parsing is to create a context, and then create a parser everytime we are +//! parsing some text. +//! +//! ```rust +//! use hoice::{ common::*, parse::{ParserCxt, Parsed} }; +//! let mut instance = Instance::new(); +//! let mut cxt = ParserCxt::new(); +//! let prof = profiling::Profiler::new(); +//! let first_pred = instance.preds().next_index(); +//! +//! { +//! let parser = cxt.parser("\ +//! (declare-fun pred (Int Int) Bool) +//! (assert (forall ((n Int)) (pred n n))) +//! ", 0, &prof); +//! let res = parser.parse(&mut instance).expect("during first parsing test"); +//! assert_eq! { res, Parsed::Items } +//! } +//! +//! assert_eq! { instance.preds().len(), 1 } +//! assert_eq! { &instance.preds()[first_pred].name, "pred" } +//! assert_eq! { instance.clauses().len(), 1 } +//! +//! let second_pred = instance.preds().next_index(); +//! +//! { +//! let parser = cxt.parser("\ +//! (declare-fun other_pred (Int Int) Bool) +//! (assert (forall ((n Int)) (=> (other_pred n n) false))) +//! ", 0, &prof); +//! let res = parser.parse(&mut instance).expect("during second parsing test"); +//! assert_eq! { res, Parsed::Items } +//! } +//! +//! assert_eq! { instance.preds().len(), 2 } +//! assert_eq! { &instance.preds()[second_pred].name, "other_pred" } +//! assert_eq! { instance.clauses().len(), 2 } +//! +//! { +//! let parser = cxt.parser("\ +//! (check-sat) +//! ", 0, &prof); +//! let res = parser.parse(&mut instance).expect("during third parsing test"); +//! assert_eq! { res, Parsed::CheckSat } +//! } +//! ``` +//! +//! # Parsing Terms +//! +//! The context mainly maintains a stack of [`TermFrame`]s. When parsing a compound term, such as +//! an operator application or a function call, the parser pushes a term frame on the stack in the +//! context and moves on to parse the subterms. This is refered to as *going down* in the code, +//! since the parser goes down the term. +//! +//! After a term is parsed successfuly, the parser checks whether the term stack is empty. If it is +//! not empty, the parser will push the term as an argument of the compound term represented by the +//! frame operator on top of the stack. It then resumes parsing to gather the remaining arguments. +//! +//! [`Parser`]: struct.Parser.html (Parser struct) +//! [`ParserCxt`]: struct.ParserCxt.html (ParserCxt struct) +//! [`TermFrame`]: struct.TermFrame.html (TermFrame struct) use common::*; use info::VarInfo; @@ -348,6 +414,16 @@ impl From for LetCount { } } +/// Result of parsing a term token. +enum TermTokenRes { + /// The token was a full term: value or variable. + Term(Term), + /// A frame to push on the stack. Compound term, *e.g.* an operator application. + Push(TermFrame), + /// Not a term, give up on parsing the (non-)term. + NotATerm, +} + /// Parser structure. Generated from a `ParserCxt`. pub struct Parser<'cxt, 's> { /// Parsing context. @@ -428,7 +504,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { /// The next character, does not move the cursor. fn peek(&self) -> Option<&'s str> { if self.has_next() { - Some(&self.string[self.cursor..self.cursor + 1]) + Some(&self.string[self.cursor..=self.cursor]) } else { None } @@ -439,7 +515,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { if self.cursor >= self.string.len() { false } else { - let char = &self.string[self.cursor..self.cursor + 1]; + let char = &self.string[self.cursor..=self.cursor]; char.is_alphanumeric() || id_special_chars.contains(&char) } } @@ -745,196 +821,44 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok(true) } - /// Parses a sort or fails. - fn sort(&mut self) -> Res { - if let Some(sort) = self.sort_opt()? { - Ok(sort) - } else { - bail!(self.error_here("expected sort (Int or Bool)")) - } - } - - /// Tries to parse a sort. + /// Parses a sort. pub fn sort_opt(&mut self) -> Res> { let start_pos = self.pos(); - let res = self.internal_sort_opt(); - if let Ok(Some(typ)) = &res { - typ.check().chain_err(|| self.error(start_pos, ""))? + if let Some(res) = self.inner_sort_opt(None)? { + match res.to_type(None) { + Ok(res) => Ok(Some(res)), + Err((pos, msg)) => bail!(self.error(pos.unwrap_or(start_pos), msg)), + } + } else { + Ok(None) } - res } - /// Tries to parse a sort. - pub fn internal_sort_opt(&mut self) -> Res> { - // Compound type under construction. - // - // The position is always that of the opening paren of the type. - enum CTyp<'a> { - // Array under construction, meaning we're parsing the index sort. - Array { - pos: Pos, - }, - // Array with a source, meaning we're parsing the target sort. - ArraySrc { - pos: Pos, - src: Typ, - }, - // A datatype application. - DTyp { - name: &'a str, - pos: Pos, - typs: dtyp::TPrmMap, - }, - } - - let mut stack = vec![]; - - let start_pos = self.pos(); - - 'go_down: loop { - self.ws_cmt(); - let current_pos = self.pos(); - - let mut typ = if self.tag_opt("(") { - self.ws_cmt(); - // Parsing a compound type. - - if self.tag_opt("Array") { - if !self.legal_id_char() { - // We're parsing an array type. - stack.push(CTyp::Array { pos: current_pos }); - continue 'go_down; - } else { - None - } - } else if let Some((pos, name)) = self.ident_opt()? { - stack.push(CTyp::DTyp { - name, - pos, - typs: dtyp::TPrmMap::new(), - }); - continue 'go_down; - } else { - None - } - } else if self.tag_opt("Int") { - if !self.legal_id_char() { - Some(typ::int()) - } else { - None - } - } else if self.tag_opt("Real") { - if !self.legal_id_char() { - Some(typ::real()) - } else { - None - } - } else if self.tag_opt("Bool") { - if !self.legal_id_char() { - Some(typ::bool()) - } else { - None - } - } else { - None - }; - - if typ.is_none() { - if let Some((pos, name)) = self.ident_opt()? { - if let Ok(dtyp) = dtyp::get(name) { - typ = Some(typ::dtyp(dtyp, vec![].into())) - } else { - bail!(self.error(pos, format!("unknown sort `{}`", conf.bad(name)))) - } - } - } - - 'go_up: loop { - if let Some(typ) = &typ { - typ.check() - .chain_err(|| self.error(start_pos, "while parsing this sort"))? - } - - match stack.pop() { - Some(CTyp::Array { pos }) => if let Some(src) = typ { - stack.push(CTyp::ArraySrc { pos, src }); - // Need to parse the domain now. - continue 'go_down; - } else { - Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) - .chain_err(|| self.error(current_pos, "expected index sort"))? - }, - - Some(CTyp::ArraySrc { pos, src }) => if let Some(tgt) = typ { - typ = Some(typ::array(src, tgt)); - - // Parse closing paren. - self.ws_cmt(); - if !self.tag_opt(")") { - let err: Error = - self.error(pos, "while parsing this array sort").into(); - Err(err).chain_err(|| { - self.error(current_pos, "expected expected closing paren") - })? - } - - continue 'go_up; - } else { - Err::<_, Error>(self.error(pos, "while parsing this array sort").into()) - .chain_err(|| self.error(current_pos, "expected domain sort"))? - }, - - Some(CTyp::DTyp { - name, - pos, - mut typs, - }) => if let Some(prm) = typ { - typs.push(prm); - - self.ws_cmt(); - if self.tag_opt(")") { - if let Ok(dtyp) = dtyp::get(name) { - typ = Some(typ::dtyp(dtyp, typs)) - } else { - let msg = format!("unknown sort `{}`", conf.bad(name)); - bail!(self.error(pos, msg)) - } - continue 'go_up; - } else { - stack.push(CTyp::DTyp { name, pos, typs }); - continue 'go_down; - } - } else { - Err::<_, Error>(self.error(pos, "while parsing this sort").into())? - }, - - None => if typ.is_none() { - self.backtrack_to(start_pos); - return Ok(None); - } else { - return Ok(typ); - }, - } - } + /// Parses a sort. + pub fn sort(&mut self) -> Res { + if let Some(res) = self.sort_opt()? { + Ok(res) + } else { + bail!(self.error_here("expected sort")) } } - /// Parses a sort. - pub fn nu_sort( + /// Parses a sort with some type parameters. + pub fn paramed_sort( &mut self, - type_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, + typ_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, ) -> Res { - if let Some(res) = self.nu_sort_opt(type_params)? { + if let Some(res) = self.inner_sort_opt(Some(typ_params))? { Ok(res) } else { bail!(self.error_here("expected sort")) } } - /// Tries to parse a sort. - pub fn nu_sort_opt( + /// Tries to parse a sort given some optional type parameters. + fn inner_sort_opt( &mut self, - type_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, + type_params: Option<&BTreeMap<&'s str, dtyp::TPrmIdx>>, ) -> Res> { use dtyp::PartialTyp; @@ -1014,7 +938,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { if typ.is_none() { if let Some((pos, name)) = self.ident_opt()? { // Type parameter? - typ = if let Some(idx) = type_params.get(name) { + typ = if let Some(idx) = type_params.and_then(|params| params.get(name)) { Some(PartialTyp::Param(*idx)) } else { Some(PartialTyp::DTyp(name.into(), pos, vec![].into())) @@ -1276,7 +1200,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { )) } - let ptyp = self.nu_sort(¶ms_map)?; + let ptyp = self.paramed_sort(¶ms_map)?; selectors.push((selector_ident.to_string(), ptyp)); self.tag(")") @@ -2014,8 +1938,7 @@ impl<'cxt, 's> Parser<'cxt, 's> { match dtyp::type_selector(&name, slc_pos, &arg) { Ok(typ) => Ok((term::dtyp_slc(typ, name, arg), slc_pos)), - - Err((pos, blah)) => bail!(self.error(pos, blah)), + Err((pos, msg)) => bail!(self.error(pos.unwrap_or(slc_pos), msg)), } } @@ -2225,7 +2148,179 @@ impl<'cxt, 's> Parser<'cxt, 's> { res } + /// Parses a token from a term. + /// + /// Returns a term when the next token was a constant or a variable. Returns `None` when + /// something new was pushed on the term stack, typically an opening paren and an operator. + fn inner_term_token( + &mut self, + var_map: &VarInfos, + map: &BTreeMap<&'s str, VarIdx>, + bind_count: LetCount, + ) -> Res { + let term = if let Some(int) = self.int() { + term::int(int) + } else if let Some(real) = self.real()? { + term::real(real) + } else if let Some(b) = self.bool() { + term::bool(b) + } else if let Some((pos, id)) = self.ident_opt()? { + if let Some(idx) = map.get(id) { + term::var(*idx, var_map[*idx].typ.clone()) + } else if let Some(ptterms) = self.get_bind(id) { + if let Some(term) = ptterms + .to_term() + .chain_err(|| format!("while retrieving binding for {}", conf.emph(id)))? + { + term + } else { + // Not in a legal term. + return Ok(TermTokenRes::NotATerm); + } + } else if self.cxt.pred_name_map.get(id).is_some() { + // Identifier is a predicate, we're not in a legal term. + return Ok(TermTokenRes::NotATerm); + } else if let Some(datatype) = dtyp::of_constructor(id) { + if let Some(constructor) = datatype.news.get(id) { + if constructor.is_empty() { + let (term, _) = + self.build_dtyp_new(id.into(), &datatype, pos, &[], vec![])?; + term + } else { + bail!(self.error( + pos, + format!( + "constructor `{}` of datatype `{}` takes {} value(s), \ + applied here to none", + conf.bad(id), + conf.emph(&datatype.name), + constructor.len() + ) + )) + } + } else { + bail!("inconsistent datatype map internal state") + } + } else { + bail!(self.error(pos, format!("unknown identifier `{}`", conf.bad(id)))) + } + } else if self.tag_opt("(") { + self.ws_cmt(); + let op_pos = self.pos(); + + if let Some(op) = self.op_opt()? { + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::Op(op), + op_pos, + bind_count, + ))); + } else if self.tag_opt(keywords::op::as_) { + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::Cast, + op_pos, + bind_count, + ))); + } else if self.tag_opt("(") { + self.ws_cmt(); + + // Try to parse a constant array. + if self.tag_opt(keywords::op::as_) { + self.ws_cmt(); + self.tag(keywords::op::const_)?; + self.ws_cmt(); + let sort_pos = self.pos(); + let typ = self.sort()?; + + self.ws_cmt(); + self.tag(")")?; + + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::CArray(typ, sort_pos), + op_pos, + bind_count, + ))); + } else if self.tag_opt(keywords::op::lambda_) { + self.ws_cmt(); + self.tag(keywords::op::is_)?; + self.ws_cmt(); + + let (op_pos, ident) = if let Some(res) = self.ident_opt()? { + res + } else if self.tag_opt("(") { + self.ws_cmt(); + let res = self.ident()?; + self.ws_cmt(); + self.tag("(")?; + self.ws_cmt(); + while self.sort_opt()?.is_some() { + self.ws_cmt() + } + self.tag(")")?; + self.ws_cmt(); + self.sort()?; + self.ws_cmt(); + self.tag(")")?; + res + } else { + bail!(self.error_here("unexpected token")) + }; + + self.ws_cmt(); + self.tag(")")?; + + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::DTypTst(ident.into()), + op_pos, + bind_count, + ))); + } else { + bail!(self.error_here("unexpected token")) + } + } else if let Some((pos, id)) = self + .ident_opt() + .chain_err(|| "while trying to parse datatype")? + { + if let Some(datatype) = dtyp::of_constructor(id) { + debug_assert! { datatype.news.get(id).is_some() } + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::DTypNew(id.into(), datatype), + op_pos, + bind_count, + ))); + } else if dtyp::is_selector(id) { + return Ok(TermTokenRes::Push(TermFrame::new( + FrameOp::DTypSlc(id.into()), + op_pos, + bind_count, + ))); + } else if self.functions.get(id).is_some() || fun::get(id).is_some() { + let op = FrameOp::Fun(id.into()); + return Ok(TermTokenRes::Push(TermFrame::new(op, op_pos, bind_count))); + } + + if self.cxt.term_stack.is_empty() { + return Ok(TermTokenRes::NotATerm); + } else { + for fun in self.functions.keys() { + println!("- {}", fun) + } + bail!(self.error(pos, format!("unknown identifier (term) `{}`", conf.bad(id)))) + } + } else if self.cxt.term_stack.is_empty() { + return Ok(TermTokenRes::NotATerm); + } else { + bail!(self.error_here("unexpected token")) + } + } else { + return Ok(TermTokenRes::NotATerm); + }; + + Ok(TermTokenRes::Term(term)) + } + /// Parses a single term. + /// + /// A term cannot contain operator applications. fn inner_term_opt( &mut self, var_map: &VarInfos, @@ -2249,6 +2344,8 @@ impl<'cxt, 's> Parser<'cxt, 's> { } conf.check_timeout()?; + let start_pos = self.pos(); + // The correct (non-error) way to exit this loop is // // `break 'read_kids ` @@ -2264,163 +2361,20 @@ impl<'cxt, 's> Parser<'cxt, 's> { self.ws_cmt(); let mut term_pos = self.pos(); - let mut term = - if let Some(int) = self.int() { - term::int(int) - } else if let Some(real) = self.real()? { - term::real(real) - } else if let Some(b) = self.bool() { - term::bool(b) - } else if let Some((pos, id)) = self.ident_opt()? { - if let Some(idx) = map.get(id) { - term::var(*idx, var_map[*idx].typ.clone()) - } else if let Some(ptterms) = self.get_bind(id) { - if let Some(term) = ptterms.to_term().chain_err(|| { - format!("while retrieving binding for {}", conf.emph(id)) - })? { - term - } else { - // Not in a legal term. - break 'read_kids None; - } - } else if self.cxt.pred_name_map.get(id).is_some() { - // Identifier is a predicate, we're not in a legal term. - break 'read_kids None; - } else if let Some(datatype) = dtyp::of_constructor(id) { - if let Some(constructor) = datatype.news.get(id) { - if constructor.is_empty() { - let (term, _) = - self.build_dtyp_new(id.into(), &datatype, pos, &[], vec![])?; - term - } else { - bail!(self.error( - pos, - format!( - "constructor `{}` of datatype `{}` takes {} value(s), \ - applied here to none", - conf.bad(id), - conf.emph(&datatype.name), - constructor.len() - ) - )) - } - } else { - bail!("inconsistent datatype map internal state") - } - } else { - bail!(self.error(pos, format!("unknown identifier `{}`", conf.bad(id)))) - } - } else if self.tag_opt("(") { - self.ws_cmt(); - let op_pos = self.pos(); - - if let Some(op) = self.op_opt()? { - let frame = TermFrame::new(FrameOp::Op(op), op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else if self.tag_opt(keywords::op::as_) { - let frame = TermFrame::new(FrameOp::Cast, op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else if self.tag_opt("(") { - self.ws_cmt(); - - // Try to parse a constant array. - if self.tag_opt(keywords::op::as_) { - self.ws_cmt(); - self.tag(keywords::op::const_)?; - self.ws_cmt(); - let sort_pos = self.pos(); - let typ = self.sort()?; - - self.ws_cmt(); - self.tag(")")?; - - let frame = - TermFrame::new(FrameOp::CArray(typ, sort_pos), op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else if self.tag_opt(keywords::op::lambda_) { - self.ws_cmt(); - self.tag(keywords::op::is_)?; - self.ws_cmt(); - - let (op_pos, ident) = if let Some(res) = self.ident_opt()? { - res - } else if self.tag_opt("(") { - self.ws_cmt(); - let res = self.ident()?; - self.ws_cmt(); - self.tag("(")?; - self.ws_cmt(); - while self.sort_opt()?.is_some() { - self.ws_cmt() - } - self.tag(")")?; - self.ws_cmt(); - self.sort()?; - self.ws_cmt(); - self.tag(")")?; - res - } else { - bail!(self.error_here("unexpected token")) - }; - - self.ws_cmt(); - self.tag(")")?; - - let frame = - TermFrame::new(FrameOp::DTypTst(ident.into()), op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else { - bail!(self.error_here("unexpected token")) - } - } else if let Some((pos, id)) = self - .ident_opt() - .chain_err(|| "while trying to parse datatype")? - { - if let Some(datatype) = dtyp::of_constructor(id) { - debug_assert! { datatype.news.get(id).is_some() } - let frame = TermFrame::new( - FrameOp::DTypNew(id.into(), datatype), - op_pos, - bind_count, - ); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else if dtyp::is_selector(id) { - let frame = - TermFrame::new(FrameOp::DTypSlc(id.into()), op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } else if self.functions.get(id).is_some() || fun::get(id).is_some() { - let op = FrameOp::Fun(id.into()); - let frame = TermFrame::new(op, op_pos, bind_count); - self.cxt.term_stack.push(frame); - continue 'read_kids; - } - - if self.cxt.term_stack.is_empty() { - self.backtrack_to(pos); - break 'read_kids None; - } else { - for fun in self.functions.keys() { - println!("- {}", fun) - } - bail!(self.error( - pos, - format!("unknown identifier (term) `{}`", conf.bad(id)) - )) - } - } else if self.cxt.term_stack.is_empty() { - break 'read_kids None; - } else { - bail!(self.error_here("unexpected token")) - } - } else { + let mut term = match self.inner_term_token(var_map, map, bind_count)? { + TermTokenRes::Term(term) => term, + TermTokenRes::Push(frame) => { + // Push on the stack and keep parsing terms. + self.cxt.term_stack.push(frame); + continue 'read_kids; + } + // Not a legal term. + TermTokenRes::NotATerm => { + self.cxt.term_stack.clear(); + self.backtrack_to(start_pos); break 'read_kids None; - }; + } + }; 'go_up: while let Some(mut frame) = self.cxt.term_stack.pop() { self.ws_cmt(); diff --git a/src/term/simplify.rs b/src/term/simplify.rs index 96d0bf51..50842754 100644 --- a/src/term/simplify.rs +++ b/src/term/simplify.rs @@ -2179,7 +2179,7 @@ where for (arg, param) in args.iter_mut().zip(fargs.iter()) { let typ = param .1 - .to_type(prms) + .to_type(Some(prms)) .unwrap_or_else(|_| panic!("ill-formed datatype constructor: {}", typ)); if let Some(typ) = typ.merge(&arg.typ()) { if let Some(nu_arg) = arg.force_dtyp(typ) { diff --git a/src/term/typ.rs b/src/term/typ.rs index 0841ae8e..9150c66e 100644 --- a/src/term/typ.rs +++ b/src/term/typ.rs @@ -395,7 +395,7 @@ impl RTyp { .expect("inconsistent datatype factory/map state") { let arg_typ = arg_typ - .to_type(prms) + .to_type(Some(prms)) .unwrap_or_else(|_| panic!("illegal type {}", current)); args.push(arg_typ) } diff --git a/src/val/mod.rs b/src/val/mod.rs index d6f6635c..bafa266e 100644 --- a/src/val/mod.rs +++ b/src/val/mod.rs @@ -162,7 +162,7 @@ pub fn dtyp_new(typ: Typ, name: String, mut args: Vec) -> Val { for (count, (_, ptyp)) in cargs.iter().enumerate() { let typ = ptyp - .to_type(typ_args) + .to_type(Some(typ_args)) .unwrap_or_else(|_| panic!("illegal datatype {}", typ)); if let Some(nu) = args[count].typ().merge(&typ) { if let Some(nu) = args[count].force_dtyp(nu) { From acf8b5ffa187ebafead04b3de0bf6fbfe91c9c70 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 9 Oct 2018 15:04:16 +0900 Subject: [PATCH 90/94] parse module simplifications and docs --- src/common/config.rs | 2 +- src/common/smt.rs | 5 +-- src/parse/mod.rs | 89 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/common/config.rs b/src/common/config.rs index 06f54fa9..06dd19ee 100644 --- a/src/common/config.rs +++ b/src/common/config.rs @@ -785,7 +785,7 @@ impl IceConf { (between 0 and 100)", ).validator(int_validator) .value_name("int") - .default_value("20") + .default_value("10") .takes_value(true) .number_of_values(1) .hidden(true) diff --git a/src/common/smt.rs b/src/common/smt.rs index 1c4e440a..63c2625f 100644 --- a/src/common/smt.rs +++ b/src/common/smt.rs @@ -865,10 +865,7 @@ impl<'a> IdentParser for FullParser { } } fn parse_type(self, input: &'a str) -> SmtRes { - let mut cxt = ::parse::ParserCxt::new(); - let dummy_profiler = Profiler::new(); - let mut parser = cxt.parser(input, 0, &dummy_profiler); - match parser.sort_opt() { + match ::parse::sort_opt(input) { Ok(Some(s)) => Ok(s), _ => Err(format!("unexpected type `{}`", input).into()), } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index e0dda8b5..f879a4f2 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -126,7 +126,7 @@ lazy_static! { } /// String extensions, lift char functions. -pub trait StringExt { +trait StringExt { /// Lifts `char::is_alphanumeric`. fn is_alphanumeric(&self) -> bool; /// Lifts `char::is_alphabetic`. @@ -225,8 +225,11 @@ impl ItemRead for T { } /// String cursor. -pub type Cursor = usize; +type Cursor = usize; + /// Position in the text. +/// +/// Used mostly for backtracking the parser. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Pos(usize); impl Default for Pos { @@ -261,7 +264,7 @@ impl ClauseRes { /// The operator of an s-expression. #[derive(Clone, Debug, PartialEq, Eq)] -pub enum FrameOp { +enum FrameOp { /// An actual operator. Op(Op), /// A constant array constructor. @@ -293,7 +296,7 @@ impl FrameOp { } /// Term stack frame used in the parser to avoid recursion. -pub struct TermFrame { +struct TermFrame { /// Operator when going up. op: FrameOp, /// Position of the operator. @@ -347,6 +350,11 @@ impl TermFrame { } /// Parser context. +/// +/// The context stores collections used by term parsing so that they don't need to be re-allocated +/// everytime. See the [module-level documentation] for more. +/// +/// [module-level documentation]: index.html (dtyp module documentation) #[derive(Default)] pub struct ParserCxt { /// Term stack to avoid recursion. @@ -366,11 +374,6 @@ impl ParserCxt { } } - /// Term stack accessor. - pub fn term_stack(&self) -> &Vec { - &self.term_stack - } - /// Generates a parser from itself. pub fn parser<'cxt, 's>( &'cxt mut self, @@ -399,7 +402,7 @@ impl ParserCxt { /// Wraps an integer, represents a number of let-bindings parsed. #[must_use] #[derive(Clone, Copy)] -pub struct LetCount { +struct LetCount { n: usize, } impl LetCount { @@ -425,6 +428,10 @@ enum TermTokenRes { } /// Parser structure. Generated from a `ParserCxt`. +/// +/// For details on how parsing works see the [module-level documentation]. +/// +/// [module-level documentation]: index.html (dtyp module documentation) pub struct Parser<'cxt, 's> { /// Parsing context. cxt: &'cxt mut ParserCxt, @@ -821,7 +828,37 @@ impl<'cxt, 's> Parser<'cxt, 's> { Ok(true) } - /// Parses a sort. + /// Tries to parse a sort. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ common::*, parse::* }; + /// let prof = Profiler::new(); + /// let mut cxt = ParserCxt::new(); + /// assert_eq! { + /// cxt.parser("Int", 0, &prof).sort_opt().expect("on Int"), Some(typ::int()) + /// } + /// assert_eq! { + /// cxt.parser("Real", 0, &prof).sort_opt().expect("on Real"), Some(typ::real()) + /// } + /// assert_eq! { + /// cxt.parser("Bool", 0, &prof).sort_opt().expect("on Bool"), Some(typ::bool()) + /// } + /// assert_eq! { + /// cxt.parser("(Array Int Int)", 0, &prof).sort_opt().expect("on (Array Int Int)"), + /// Some(typ::array(typ::int(), typ::int())) + /// } + /// assert_eq! { + /// cxt.parser("7", 0, &prof).sort_opt().expect("on (Array Int Int)"), + /// None + /// } + /// hoice::dtyp::create_list_dtyp(); + /// let int_list = typ::dtyp(dtyp::get("List").unwrap(), vec![typ::int()].into()); + /// assert_eq! { + /// cxt.parser("(List Int)", 0, &prof).sort_opt().expect("on (List Int)"), Some(int_list) + /// } + /// ``` pub fn sort_opt(&mut self) -> Res> { let start_pos = self.pos(); if let Some(res) = self.inner_sort_opt(None)? { @@ -835,6 +872,11 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Parses a sort. + /// + /// Same as [`sort_opt`], but whenever `sort_opt` returns `None` then this function returns an + /// error. + /// + /// [`sort_opt`]: #method.sort_opt (sort_opt function) pub fn sort(&mut self) -> Res { if let Some(res) = self.sort_opt()? { Ok(res) @@ -844,6 +886,23 @@ impl<'cxt, 's> Parser<'cxt, 's> { } /// Parses a sort with some type parameters. + /// + /// # Examples + /// + /// ```rust + /// use hoice::{ common::*, parse::*, dtyp::TPrmIdx }; + /// let prof = Profiler::new(); + /// let mut cxt = ParserCxt::new(); + /// let mut tparams = BTreeMap::new(); + /// let mut index: TPrmIdx = 0.into(); + /// tparams.insert("T_1", index); + /// index.inc(); + /// tparams.insert("T_2", index); + /// let res = cxt.parser("(Array T_1 T_2)", 0, &prof) + /// .paramed_sort(&tparams) + /// .expect("on (Array T_1 T_2)"); + /// assert_eq! { &format!("{}", res), "(Array '0 '1)" } + /// ``` pub fn paramed_sort( &mut self, typ_params: &BTreeMap<&'s str, dtyp::TPrmIdx>, @@ -3339,6 +3398,14 @@ pub fn var_infos(s: &str) -> VarInfos { var_infos } +/// Tries to parse a sort from an SMT 2 string. +pub fn sort_opt(s: &str) -> Res> { + let mut cxt = ParserCxt::new(); + let dummy_profiler = Profiler::new(); + let mut parser = cxt.parser(s, 0, &dummy_profiler); + parser.sort_opt() +} + /// Parses a term from an SMT 2 string. /// /// Used for testing / documentation. From 55de136667686ec97ad4efb0eb54e48663f9effe Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 9 Oct 2018 16:55:45 +0900 Subject: [PATCH 91/94] unsat proof bugfixes + reg test --- rsc/unsat/proof_6.smt2 | 83 ++++++++++++++++++++++++++++ src/data/mod.rs | 99 +++++++++++++++++++--------------- src/teacher/mod.rs | 12 ++--- src/unsat_core/entry_points.rs | 8 ++- 4 files changed, 148 insertions(+), 54 deletions(-) create mode 100644 rsc/unsat/proof_6.smt2 diff --git a/rsc/unsat/proof_6.smt2 b/rsc/unsat/proof_6.smt2 new file mode 100644 index 00000000..d6c62ea5 --- /dev/null +++ b/rsc/unsat/proof_6.smt2 @@ -0,0 +1,83 @@ +(set-option :produce-proofs true) +(set-logic HORN) + +(declare-fun |state| ( Int Int Bool Bool Int Int Int Int Bool Int Int Bool Bool Bool Int Bool Bool Int Int Int ) Bool) + +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Bool) (F Int) (G Int) (H Int) (I Int) (J Bool) (K Bool) (L Int) (M Bool) (N Bool) (O Bool) (P Int) (Q Int) (R Bool) (S Int) (T Bool) ) + (=> + (and + (let ((a!1 (= (or (not K) (and R T) (not (<= Q L))) J)) + (a!2 (and (or R (not (<= Q P))) (or T (not (<= Q S))) (<= 1 Q)))) + (and (= I L) + (= G Q) + (= G F) + (= A 0) + (= A S) + (= H 0) + (= H P) + a!1 + (= E a!2) + (= E M) + (= M K) + (= I 0))) + ) + (state I L E M H P A S K G Q R T J F O N B C D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Bool) (F Int) (G Bool) (H Bool) (I Int) (J Int) (K Int) (L Int) (M Bool) (N Bool) (O Int) (P Bool) (Q Int) (R Bool) (S Int) (T Bool) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Bool) (B1 Int) (C1 Bool) (D1 Bool) (E1 Int) (F1 Bool) (G1 Bool) (H1 Int) (I1 Bool) (J1 Int) (K1 Int) (L1 Bool) (M1 Int) (N1 Bool) ) + (=> + (and + (state L O H P K J1 A M1 N J K1 L1 N1 M I T R B C D) + (let ((a!1 (= (or (not N) (and L1 N1) (not (<= K1 O))) M)) + (a!2 (= (or (not G1) (and C1 A1) (not (<= Z H1))) F1)) + (a!3 (and (or C1 (not (<= Z B1))) (or A1 (not (<= Z Y))) (<= 1 Z))) + (a!4 (or (not R) (not T) (= (+ O (* (- 1) S)) (- 1)))) + (a!5 (or (not T) (= (+ J1 (* (- 1) V)) (- 1)))) + (a!6 (or (not R) (= (+ M1 (* (- 1) Q)) (- 1))))) + (and (= W Q) + (= X V) + (= Y X) + (= Z U) + (= B1 W) + (= E1 S) + (= L O) + (= K J1) + (= J K1) + (= H1 E1) + (= A M1) + a!1 + a!2 + (= D1 (or P a!3)) + (= P N) + (= H P) + (= I1 D1) + (= I1 G1) + a!4 + (or (and R T) (= S 0)) + a!5 + (or T (= V 0)) + a!6 + (or R (= Q 0)) + (= K1 U))) + ) + (state E1 H1 D1 I1 X Y W B1 G1 U Z A1 C1 F1 F E G Q V S) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Bool) (F Int) (G Int) (H Int) (I Int) (J Bool) (K Bool) (L Int) (M Bool) (N Bool) (O Bool) (P Int) (Q Int) (R Bool) (S Int) (T Bool) ) + (=> + (and + (state I L E M H P A S K G Q R T J F O N B C D) + (not J) + ) + false + ) + ) +) + +(check-sat) +(get-proof) diff --git a/src/data/mod.rs b/src/data/mod.rs index 45400547..71c3e105 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -289,12 +289,10 @@ impl Data { &mut self, pred: PrdIdx, args: &VarVals, - rhs: Option<&(PrdIdx, RVarVals)>, + rhs: &Option, ) -> Res<()> { - if let Some((rhs_pred, var_vals)) = rhs { - let rhs_args = var_to::vals::new(var_vals.clone()); - let rhs = Sample::new(*rhs_pred, rhs_args); - self.register_sample_dep(pred, args, Some(rhs))? + if let Some(rhs) = rhs { + self.register_sample_dep(pred, args, Some(rhs.clone()))? } Ok(()) } @@ -472,12 +470,16 @@ impl Data { } /// Checks whether the data is contradictory. - pub fn is_unsat(&self) -> bool { + /// + /// Mutable because data needs to be propagated. + pub fn check_unsat(&mut self) -> bool { self.get_unsat_proof().is_ok() } /// Retrieves a proof of unsat. - pub fn get_unsat_proof(&self) -> Res<::unsat_core::UnsatRes> { + /// + /// Unsat because data needs to be propagated. + pub fn get_unsat_proof(&mut self) -> Res<::unsat_core::UnsatRes> { // println!( // "all learning data:\n{}", // self.string_do(&(), |s| s.to_string()).unwrap() @@ -493,9 +495,13 @@ impl Data { // println!(" {}", line) // } // println!("is unsat"); + self.propagate()?; for (pred, samples) in self.pos.index_iter() { + // println!("{}", self.instance[pred]); for sample in samples { + // println!(" {}", sample); for neg in &self.neg[pred] { + // println!(" {}", neg); if sample.is_complementary(neg) { let entry_points = if let Some(entry_points) = &self.entry_points { Some(entry_points.entry_points_of(&Sample::new(pred, sample.clone()))?) @@ -783,19 +789,49 @@ impl Data { fn prune_cstr( &mut self, clause: ClsIdx, - lhs: Vec<(PrdIdx, RVarVals)>, + mut lhs: Vec<(PrdIdx, RVarVals)>, rhs: Option<(PrdIdx, RVarVals)>, ) -> Res, Option)>> { + let nu_rhs = if let Some((pred, args)) = rhs { + let (args, is_new) = var_to::vals::new_is_new(args.clone()); + + let args = if conf.teacher.partial || !is_new { + if args.set_subsumed(&self.pos[pred]) { + profile! { self mark "add cstr", "pre-checks" } + profile! { self "trivial constraints" => add 1 } + // Positive, constraint is trivial. + return Ok(None); + } else if args.set_subsumed(&self.neg[pred]) { + // Negative, ignore. + None + } else { + Some(args) + } + } else { + Some(args) + }; + + args.map(|args| Sample { pred, args }) + } else { + None + }; + let mut nu_lhs = PrdHMap::with_capacity(lhs.len()); // Look at the lhs and remove stuff we know is true. - 'lhs_iter: for (pred, args) in lhs { + 'lhs_iter: while let Some((pred, args)) = lhs.pop() { let (args, is_new) = var_to::vals::new_is_new(args); // If no partial examples and sample is new, no need to check anything. if conf.teacher.partial || !is_new { if args.set_subsumed(&self.pos[pred]) { - self.register_raw_sample_dep(pred, &args, rhs.as_ref())?; + self.register_raw_sample_dep(pred, &args, &nu_rhs)?; + // Is this the last (positive) sample in a `... => false` constraint? + if nu_rhs.is_none() && lhs.is_empty() { + // Then register as negative to record the conflict. + self.add_neg(clause, pred, args); + unsat!("by `true => false` in constraint (data, prune_cstr)") + } // Positive, skip. continue 'lhs_iter; } else if args.set_subsumed(&self.neg[pred]) { @@ -814,47 +850,22 @@ impl Data { () } - let nu_rhs = if let Some((pred, args)) = rhs { - let (args, is_new) = var_to::vals::new_is_new(args.clone()); - + if let Some(Sample { pred, args }) = nu_rhs.as_ref() { if nu_lhs.is_empty() { - self.add_pos(clause, pred, args.clone()); - } + self.add_pos(clause, *pred, args.clone()); + } else if let Some(argss) = nu_lhs.get(pred) { + // Subsumed by lhs? - let args = if conf.teacher.partial || !is_new { - if args.set_subsumed(&self.pos[pred]) { + // Partial samples are not allowed in constraints, no subsumption + // check. + if argss.contains(&args) { profile! { self mark "add cstr", "pre-checks" } profile! { self "trivial constraints" => add 1 } - // Positive, constraint is trivial. + // Trivially implied by lhs. return Ok(None); - } else if args.set_subsumed(&self.neg[pred]) { - // Negative, ignore. - None - } else { - Some(args) - } - } else { - Some(args) - }; - - if let Some(args) = args.as_ref() { - // Subsumed by lhs? - if let Some(argss) = nu_lhs.get(&pred) { - // Partial samples are not allowed in constraints, no subsumption - // check. - if argss.contains(&args) { - profile! { self mark "add cstr", "pre-checks" } - profile! { self "trivial constraints" => add 1 } - // Trivially implied by lhs. - return Ok(None); - } } } - - args.map(|args| Sample { pred, args }) - } else { - None - }; + } nu_lhs.shrink_to_fit(); diff --git a/src/teacher/mod.rs b/src/teacher/mod.rs index 2ca3b71a..16843c8f 100644 --- a/src/teacher/mod.rs +++ b/src/teacher/mod.rs @@ -112,10 +112,7 @@ pub fn teach(teacher: &mut Teacher) -> Res { match teacher.get_candidates(false)? { // Unsat result, done. - Either::Right(unsat) => { - teacher.data.is_unsat(); - return Ok(TeachRes::Unsat(unsat)); - } + Either::Right(unsat) => return Ok(TeachRes::Unsat(unsat)), // Got a candidate. Either::Left((idx, candidates)) => { @@ -356,7 +353,6 @@ impl<'a> Teacher<'a> { } self.assistant = None; log_debug! { "draining messages" } - self.data.is_unsat(); while let Ok(_) = self.get_candidates(true) {} if conf.stats { @@ -647,7 +643,7 @@ impl<'a> Teacher<'a> { } // Are we unsat? - if self.data.is_unsat() { + if self.data.check_unsat() { return Ok(Either::Right(self.unsat_core()?)); } } @@ -675,9 +671,7 @@ impl<'a> Teacher<'a> { } } - MsgKind::Unsat => if self.data.is_unsat() { - return Ok(Either::Right(self.unsat_core()?)); - }, + MsgKind::Unsat => return Ok(Either::Right(self.unsat_core()?)), } } } diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index 2ede2a32..e6b7963d 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -308,7 +308,7 @@ impl<'a> Reconstr<'a> { debug_assert_eq! { pred, p } self.solver.assert(&smt::EqConj::new(args, &sample))? } else { - bail!("proof reconstruction, illegal clause-level call (no rhs)") + bail!("proof reconstruction: illegal clause-level call (no rhs)") } let sat = self.solver.check_sat()?; @@ -545,6 +545,12 @@ impl<'a> Reconstr<'a> { } if !self.safe_preds.is_empty() { + for pred in self.instance.preds() { + if !pred.is_defined() { + let sig: Vec<_> = pred.original_sig().iter().map(|typ| typ.get()).collect(); + self.solver.declare_fun(&pred.name, &sig, "Bool")? + } + } let model = self.instance.extend_model(PrdHMap::new())?; self.instance.write_definitions(self.solver, "", &model)? } From 5e4c46bdb28f4edb6854a47c897893438130f2cc Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Mon, 15 Oct 2018 09:44:45 +0200 Subject: [PATCH 92/94] unsat proof bug fix --- src/unsat_core/entry_points.rs | 2 +- src/var_to/vals.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index e6b7963d..42c4f77a 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -73,7 +73,7 @@ impl EntryPoints { use var_to::vals::SubsumeExt; let mut real_dep = None; for s in &self.real_pos_samples { - if s.args.subsumes(&dep.args) { + if s.pred == dep.pred && s.args.subsumes(&dep.args) { real_dep = Some(s.clone()); break; } diff --git a/src/var_to/vals.rs b/src/var_to/vals.rs index 259e8894..b7e32800 100644 --- a/src/var_to/vals.rs +++ b/src/var_to/vals.rs @@ -1,5 +1,4 @@ -/*! Hashconsed maps from variables to terms. -*/ +//! Hashconsed maps from variables to terms. use std::cmp::Ordering; From 35efc1a54cfa2b060d9d27601a0ac671c8e383b8 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 16 Oct 2018 15:37:24 +0200 Subject: [PATCH 93/94] unsat proof fix + reg tests --- rsc/unsat/proof_paths.smt2 | 116 ++++++++++ rsc/unsat/proof_unroll.smt2 | 409 +++++++++++++++++++++++++++++++++ src/data/mod.rs | 5 + src/unsat_core/entry_points.rs | 137 ++++++++--- 4 files changed, 633 insertions(+), 34 deletions(-) create mode 100644 rsc/unsat/proof_paths.smt2 create mode 100644 rsc/unsat/proof_unroll.smt2 diff --git a/rsc/unsat/proof_paths.smt2 b/rsc/unsat/proof_paths.smt2 new file mode 100644 index 00000000..03755330 --- /dev/null +++ b/rsc/unsat/proof_paths.smt2 @@ -0,0 +1,116 @@ +(set-option :produce-proofs true) +(set-logic HORN) + +(declare-fun |twice$unknown:11| ( Int Int ) Bool) +(declare-fun |twice$unknown:13| ( Int Int ) Bool) +(declare-fun |twice$unknown:9| ( Int Int ) Bool) +(declare-fun |g$unknown:3| ( Int Int Int ) Bool) +(declare-fun |neg$unknown:7| ( Int Int ) Bool) +(declare-fun |twice$unknown:15| ( Int Int ) Bool) +(declare-fun |neg$unknown:5| ( Int Int ) Bool) + +(assert + (forall ( (A Int) (B Int) (C Int) ) + (=> + (and + (= A B) + ) + (|g$unknown:3| A C B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) ) + (=> + (and + (|g$unknown:3| A C B) + (and (not (= 0 E)) (= D 1) (= (not (= 0 E)) (>= B 0))) + ) + (|twice$unknown:13| A C) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) ) + (=> + (and + (|neg$unknown:5| D C) + (and (= C 1) (= A (* (- 1) D))) + ) + (|neg$unknown:7| A B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) ) + (=> + (and + (|twice$unknown:11| A B) + true + ) + (|twice$unknown:9| A B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) ) + (=> + (and + (|twice$unknown:11| C B) + (= A C) + ) + (|twice$unknown:15| A B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) ) + (=> + (and + (|twice$unknown:13| A B) + true + ) + (|twice$unknown:9| A B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) ) + (=> + (and + (|twice$unknown:9| B A) + (and (not (= 0 E)) (= D 1) (= (not (= 0 E)) (>= C 0))) + ) + (|neg$unknown:5| B A) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) ) + (=> + (and + (|neg$unknown:7| B A) + (and (not (= 0 E)) (= D 1) (= (not (= 0 E)) (>= C 0))) + ) + (|twice$unknown:11| B A) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) ) + (=> + (and + (|twice$unknown:15| D C) + (and (= (not (= 0 E)) (>= A 0)) + (= 0 B) + (not (= 0 E)) + (= C 1) + (= (not (= 0 B)) (>= D 0))) + ) + false + ) + ) +) + +(check-sat) +(get-proof) diff --git a/rsc/unsat/proof_unroll.smt2 b/rsc/unsat/proof_unroll.smt2 new file mode 100644 index 00000000..879d7164 --- /dev/null +++ b/rsc/unsat/proof_unroll.smt2 @@ -0,0 +1,409 @@ +(set-option :produce-proofs true) +(set-logic HORN) + +(declare-fun |g_1032$unknown:30| ( Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_without_checking_1152$unknown:41| ( Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_1032$unknown:24| ( Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_without_checking_1152$unknown:46| ( Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |f_1035$unknown:11| ( Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |succ_1030$unknown:52| ( Int Int Int Int Int ) Bool) +(declare-fun |g_1032$unknown:29| ( Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_without_checking_1152$unknown:42| ( Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_1032$unknown:25| ( Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |fail$unknown:12| ( Int ) Bool) +(declare-fun |f_1035$unknown:10| ( Int Int Int Int Int Int Int Int ) Bool) +(declare-fun |g_without_checking_1152$unknown:47| ( Int Int Int Int Int Int Int Int Int Int Int Int ) Bool) + +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) ) + (=> + (and + (|f_1035$unknown:10| B A E D I H G F) + (|succ_1030$unknown:52| C B A E D) + (and (not (= 0 L)) (= K 0) (= J 0) (= (not (= 0 L)) (= I 0))) + ) + (|f_1035$unknown:11| C B A E D I H G F) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (v_14 Int) (v_15 Int) (v_16 Int) ) + (=> + (and + (|f_1035$unknown:10| A D C B H G F E) + (and (= 0 K) + (= J 0) + (= I 0) + (= N (+ L I)) + (= M (+ (- 1) H)) + (= L (* J H)) + (= (not (= 0 K)) (= H 0)) + (= v_14 G) + (= v_15 F) + (= v_16 E)) + ) + (|g_1032$unknown:29| A D C B G F E N v_14 v_15 v_16) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (v_15 Int) (v_16 Int) (v_17 Int) ) + (=> + (and + (|f_1035$unknown:10| D C B A I H G F) + (|g_1032$unknown:30| E D C B A H G F O v_15 v_16 v_17) + (and (= v_15 H) + (= v_16 G) + (= v_17 F) + (= 0 L) + (= K 0) + (= J 0) + (= O (+ M J)) + (= N (+ (- 1) I)) + (= M (* K I)) + (= (not (= 0 L)) (= I 0))) + ) + (|f_1035$unknown:11| E D C B A I H G F) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (v_15 Int) (v_16 Int) (v_17 Int) (v_18 Int) (v_19 Int) (v_20 Int) ) + (=> + (and + (|f_1035$unknown:11| D C B A N M G F E) + (|g_1032$unknown:24| C B A N G F E O v_15 v_16 v_17) + (and (= v_15 G) + (= v_16 F) + (= v_17 E) + (= 0 K) + (= J 0) + (= I 0) + (= O (+ L I)) + (= M (+ (- 1) H)) + (= L (* J H)) + (= (not (= 0 K)) (= H 0)) + (= v_18 G) + (= v_19 F) + (= v_20 E)) + ) + (|g_1032$unknown:25| D C B A N G F E O v_18 v_19 v_20) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (v_14 Int) (v_15 Int) (v_16 Int) ) + (=> + (and + (|g_1032$unknown:24| C B A M F E D N v_14 v_15 v_16) + (and (= v_14 F) + (= v_15 E) + (= v_16 D) + (= 0 J) + (= I 0) + (= H 0) + (= N (+ K H)) + (= L (+ (- 1) G)) + (= K (* I G)) + (= (not (= 0 J)) (= G 0))) + ) + (|f_1035$unknown:10| C B A M L F E D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Int) (B1 Int) (C1 Int) (D1 Int) (E1 Int) (F1 Int) (G1 Int) (H1 Int) (I1 Int) ) + (=> + (and + (|g_1032$unknown:25| D C B A L K J I H G F E) + (|g_without_checking_1152$unknown:41| C B A L K J I H G F E) + (|g_1032$unknown:29| P O N M K J I H G F E) + (let ((a!1 (= (not (= 0 I1)) (and (not (= 0 H1)) (not (= 0 B1)))))) + (and (= (not (= 0 H1)) (>= G1 0)) + (not (= 0 M)) + (not (= 0 I1)) + (= E1 (+ C1 D1)) + (= D1 (* (- 4) H)) + (= C1 2) + (= A1 (+ Y Z)) + (= Z 0) + (= Y (+ W X)) + (= X (* (- 4) H)) + (= W 2) + (= V (+ T U)) + (= U 0) + (= T (+ R S)) + (= S (* (- 4) N)) + (= R 2) + (= Q 1) + (= G1 (+ E1 F1)) + (= F1 0) + (= (= 0 B1) (<= V A1)) + a!1)) + ) + (|g_without_checking_1152$unknown:42| D C B A L K J I H G F E) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) ) + (=> + (and + (|g_1032$unknown:25| D C B A L K J I H G F E) + (|g_without_checking_1152$unknown:41| C B A L K J I H G F E) + (|g_1032$unknown:29| P O N M K J I H G F E) + (and (= Q 1) (= 0 M)) + ) + (|g_without_checking_1152$unknown:42| D C B A L K J I H G F E) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Int) (B1 Int) (C1 Int) (D1 Int) (E1 Int) (F1 Int) (G1 Int) (H1 Int) ) + (=> + (and + (|g_1032$unknown:29| O N M L J I H G F E D) + (|g_without_checking_1152$unknown:41| C B A K J I H G F E D) + (let ((a!1 (= (not (= 0 H1)) (and (not (= 0 G1)) (not (= 0 A1)))))) + (and (= (not (= 0 G1)) (>= F1 0)) + (not (= 0 L)) + (not (= 0 H1)) + (= D1 (+ B1 C1)) + (= C1 (* (- 4) G)) + (= B1 2) + (= Z (+ X Y)) + (= Y 0) + (= X (+ V W)) + (= W (* (- 4) G)) + (= V 2) + (= U (+ S T)) + (= T 0) + (= S (+ Q R)) + (= R (* (- 4) M)) + (= Q 2) + (= P 1) + (= F1 (+ D1 E1)) + (= E1 0) + (= (= 0 A1) (<= U Z)) + a!1)) + ) + (|g_1032$unknown:24| C B A K J I H G F E D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) ) + (=> + (and + (|g_1032$unknown:29| O N M L J I H G F E D) + (|g_without_checking_1152$unknown:41| C B A K J I H G F E D) + (and (= P 1) (= 0 L)) + ) + (|g_1032$unknown:24| C B A K J I H G F E D) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Int) (B1 Int) (C1 Int) (D1 Int) (E1 Int) (F1 Int) ) + (=> + (and + (|g_1032$unknown:29| L K J I H G F E D C B) + (|g_without_checking_1152$unknown:47| N L K J I H G F E D C B) + (let ((a!1 (= (not (= 0 F1)) (and (not (= 0 E1)) (not (= 0 Y)))))) + (and (= (not (= 0 E1)) (>= D1 0)) + (not (= 0 I)) + (not (= 0 F1)) + (= B1 (+ Z A1)) + (= A1 (* (- 4) E)) + (= Z 2) + (= X (+ V W)) + (= W 0) + (= V (+ T U)) + (= U (* (- 4) E)) + (= T 2) + (= S (+ Q R)) + (= R 0) + (= Q (+ O P)) + (= P (* (- 4) J)) + (= O 2) + (= M 1) + (= A N) + (= D1 (+ B1 C1)) + (= C1 0) + (= (= 0 Y) (<= S X)) + a!1)) + ) + (|g_1032$unknown:30| A L K J I H G F E D C B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) ) + (=> + (and + (|g_1032$unknown:29| L K J I H G F E D C B) + (|g_without_checking_1152$unknown:47| N L K J I H G F E D C B) + (and (= A N) (= M 1) (= 0 I)) + ) + (|g_1032$unknown:30| A L K J I H G F E D C B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Int) (B1 Int) (C1 Int) (D1 Int) ) + (=> + (and + (|g_1032$unknown:29| K J I H G F E D C B A) + (let ((a!1 (= (not (= 0 D1)) (and (not (= 0 C1)) (not (= 0 W)))))) + (and (= (not (= 0 C1)) (>= B1 0)) + (not (= 0 H)) + (not (= 0 D1)) + (= Z (+ X Y)) + (= Y (* (- 4) D)) + (= X 2) + (= V (+ T U)) + (= U 0) + (= T (+ R S)) + (= S (* (- 4) D)) + (= R 2) + (= Q (+ O P)) + (= P 0) + (= O (+ M N)) + (= N (* (- 4) I)) + (= M 2) + (= L 1) + (= B1 (+ Z A1)) + (= A1 0) + (= (= 0 W) (<= Q V)) + a!1)) + ) + (|g_without_checking_1152$unknown:46| K J I H G F E D C B A) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (P Int) (Q Int) (R Int) (S Int) (T Int) (U Int) (V Int) (W Int) (X Int) (Y Int) (Z Int) (A1 Int) (B1 Int) (C1 Int) (D1 Int) ) + (=> + (and + (|g_1032$unknown:29| K J I H G F E D C B A) + (let ((a!1 (= (not (= 0 C1)) (and (not (= 0 B1)) (not (= 0 V)))))) + (and (= (not (= 0 B1)) (>= A1 0)) + (not (= 0 H)) + (= 0 C1) + (= Z 0) + (= Y (+ W X)) + (= X (* (- 4) D)) + (= W 2) + (= U (+ S T)) + (= T 0) + (= S (+ Q R)) + (= R (* (- 4) D)) + (= Q 2) + (= P (+ N O)) + (= O 0) + (= N (+ L M)) + (= M (* (- 4) I)) + (= L 2) + (= D1 1) + (= A1 (+ Y Z)) + (= (= 0 V) (<= P U)) + a!1)) + ) + (|fail$unknown:12| D1) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) ) + (=> + (and + (|g_1032$unknown:29| K J I H G F E D C B A) + (and (= L 1) (= 0 H)) + ) + (|g_without_checking_1152$unknown:46| K J I H G F E D C B A) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (N Int) (O Int) (v_15 Int) (v_16 Int) (v_17 Int) ) + (=> + (and + (|g_without_checking_1152$unknown:42| N O I B J E D C v_15 M L K) + (|g_without_checking_1152$unknown:46| I H G F E D C B M L K) + (|g_without_checking_1152$unknown:42| O I v_16 B J E D C v_17 M L K) + (and (= v_15 B) (= v_16 I) (= v_17 B) (= A N) (= J 1)) + ) + (|g_without_checking_1152$unknown:47| A I H G F E D C B M L K) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (M Int) (v_13 Int) (v_14 Int) (v_15 Int) ) + (=> + (and + (|g_without_checking_1152$unknown:42| M H v_13 A I D C B v_14 L K J) + (|g_without_checking_1152$unknown:46| H G F E D C B A L K J) + (and (= v_13 H) (= v_14 A) (= I 1) (= v_15 A)) + ) + (|g_without_checking_1152$unknown:41| M H A I D C B v_15 L K J) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (L Int) (v_12 Int) (v_13 Int) ) + (=> + (and + (|g_without_checking_1152$unknown:46| H G F E D C B A L K J) + (and (= I 1) (= v_12 H) (= v_13 A)) + ) + (|g_without_checking_1152$unknown:41| H v_12 A I D C B v_13 L K J) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) ) + (=> + (and + (= A (+ 1 E)) + ) + (|succ_1030$unknown:52| A E D C B) + ) + ) +) +(assert + (forall ( (A Int) (B Int) (C Int) (D Int) (E Int) (F Int) (G Int) (H Int) (I Int) (J Int) (K Int) (v_11 Int) (v_12 Int) (v_13 Int) ) + (=> + (and + (let ((a!1 (= (not (= 0 K)) (and (not (= 0 I)) (not (= 0 J)))))) + (and (= (not (= 0 J)) (>= B 0)) + (= (not (= 0 I)) (>= A 0)) + (not (= 0 K)) + (= G 0) + (= F 0) + (= E 0) + (= D 0) + (= C 0) + (= H 0) + a!1 + (= v_11 H) + (= v_12 G) + (= v_13 F))) + ) + (|f_1035$unknown:10| B H G F A v_11 v_12 v_13) + ) + ) +) +(assert + (forall ( (A Int) ) + (=> + (and + (|fail$unknown:12| A) + true + ) + false + ) + ) +) + +(check-sat) +(get-proof) diff --git a/src/data/mod.rs b/src/data/mod.rs index 71c3e105..f2436894 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -504,6 +504,11 @@ impl Data { // println!(" {}", neg); if sample.is_complementary(neg) { let entry_points = if let Some(entry_points) = &self.entry_points { + log! { @5 + "retrieving entry points for ({} {})\n{}", + self.instance[pred], sample, + entry_points.to_string(&self.instance) + } Some(entry_points.entry_points_of(&Sample::new(pred, sample.clone()))?) } else { None diff --git a/src/unsat_core/entry_points.rs b/src/unsat_core/entry_points.rs index 42c4f77a..601b2f66 100644 --- a/src/unsat_core/entry_points.rs +++ b/src/unsat_core/entry_points.rs @@ -193,11 +193,13 @@ struct Reconstr<'a> { /// Instance. instance: &'a Instance, /// Samples to reconstruct. - to_do: Vec, + /// + /// Each element of the vector is a path toward what we're reconstructing. If we're trying to + /// reconstruct `(p vals)`, then several path are created for this sample if there are more + /// than one clauses that lead to this sample. + to_do: Vec>, /// Positive samples for the original instance. samples: SampleSet, - // /// Stack of things, used when reconstructing a sample. - // stack: Vec<()>, /// Solver. solver: &'a mut Slvr, } @@ -210,6 +212,7 @@ impl<'a> Reconstr<'a> { to_do: Vec, solver: &'a mut Slvr, ) -> Self { + let to_do = vec![to_do]; let pos_preds: PrdSet = original .pos_clauses() .iter() @@ -263,18 +266,26 @@ impl<'a> Reconstr<'a> { /// /// - the positive clauses in which `pred` appears, /// - the clauses in which `pred` is the rhs and *all* predicates in the LHS are defined in the - /// instance. + /// instance or appear in a positive clause. fn clauses_for(&self, pred: PrdIdx) -> (Vec, Vec) { let mut pos = vec![]; let mut others = vec![]; + log! { + @4 | "retrieving {} clause(s) for {}", + self.original.rhs_clauses_of(pred).len(), self.original[pred] + } for clause_idx in self.original.rhs_clauses_of(pred) { + log! { + @5 "{}", self.original[*clause_idx].to_string_info(&self.original.preds()).unwrap() + } let clause_preds = self.original[*clause_idx].lhs_preds(); if clause_preds.is_empty() { pos.push(*clause_idx) - } else if clause_preds - .keys() - .all(|pred| self.safe_preds.contains(pred)) - { + } else if clause_preds.keys().all(|pred| { + self.instance[*pred].is_defined() + || self.safe_preds.contains(pred) + || self.pos_preds.contains(pred) + }) { others.push(*clause_idx) } } @@ -285,7 +296,12 @@ impl<'a> Reconstr<'a> { /// /// Returns `true` if the reconstruction was positive. If it was, (potentially) new positive /// samples have been added to `self.samples`. - fn work_on_clause(&mut self, pred: PrdIdx, sample: &VarVals, clause: ClsIdx) -> Res { + fn work_on_clause( + &mut self, + pred: PrdIdx, + sample: &VarVals, + clause: ClsIdx, + ) -> Res>>> { debug_assert! { self.original[clause].rhs().map(|(p, _)| p == pred).unwrap_or(false) } self.solver.push(1)?; // Declare clause variables. @@ -324,6 +340,7 @@ impl<'a> Reconstr<'a> { if let Some(model) = model { let model = Cex::of_model(self.original[clause].vars(), model, true)?; + let mut res = vec![]; // Reconstruct all LHS applications. for (pred, argss) in self.original[clause].lhs_preds() { let mut samples = vec![]; @@ -342,7 +359,7 @@ impl<'a> Reconstr<'a> { log! { @5 |=> " ({} {})", self.original[sample.pred], sample.args } } } - self.samples.extend(samples.into_iter()) + res.push(samples) } else { if_log! { @5 log! { @5 |=> "generated new samples:" } @@ -350,16 +367,18 @@ impl<'a> Reconstr<'a> { log! { @5 |=> " ({} {})", self.original[sample.pred], sample.args } } } - self.to_do.extend(samples.into_iter()) + res.push(samples) } } - Ok(true) + Ok(Some(res)) } else { - Ok(false) + Ok(None) } } /// Reconstructs a sample using the definitions of the positive predicates. + /// + /// Returns `true` if the sample was discovered to be positive. fn work_on_defs(&mut self, pred: PrdIdx, vals: &VarVals) -> Res { let mut current_pred = PrdSet::with_capacity(1); current_pred.insert(pred); @@ -435,20 +454,23 @@ impl<'a> Reconstr<'a> { } /// Reconstructs a single positive sample. - fn work_on_sample(&mut self, Sample { pred, args }: Sample) -> Res<()> { + /// + /// Returns `true` if the sample was found to be a legal positive sample for the original + /// instance. + fn work_on_sample(&mut self, Sample { pred, args }: Sample, path: &Vec) -> Res { log! { @3 | "working on ({} {})", self.instance[pred], args } // Already an entry point for the original instance? if self.pos_preds.contains(&pred) { log! { @4 | "already a legal entry point" } self.samples.insert(Sample::new(pred, args)); - return Ok(()); + return Ok(true); } // Try reconstructing by using predicate definitions directly. let done = self.work_on_defs(pred, &args)?; if done { - return Ok(()); + return Ok(true); } let (pos, others) = self.clauses_for(pred); @@ -469,25 +491,40 @@ impl<'a> Reconstr<'a> { } for clause in pos { - let okay = self.work_on_clause(pred, &args, clause)?; - if okay { - log! { @3 | " reconstructed using positive clause #{}", clause } - return Ok(()); + if let Some(steps) = self.work_on_clause(pred, &args, clause)? { + if !steps.is_empty() { + debug_assert! { steps.iter().all(|step| step.is_empty()) } + log! { @3 | " reconstructed using positive clause #{}", clause } + return Ok(true); + } } } + let mut clause_count = 0; for clause in others { - let okay = self.work_on_clause(pred, &args, clause)?; - if okay { - log! { @3 | " reconstructed using non-positive clause #{}", clause } - return Ok(()); + if let Some(steps) = self.work_on_clause(pred, &args, clause)? { + log! { + @3 | " reconstructed using non-positive clause #{}, {} step(s)", + clause, steps.len() + } + clause_count += 1; + for step in steps { + let mut path = path.clone(); + path.extend(step.into_iter()); + self.to_do.push(path) + } } } - bail!( - "could not reconstruct sample ({} {})", - self.instance[pred], - args - ) + if clause_count == 0 { + // Reconstruction failed. This can be okay, when there are more than one path and the + // current one has no solution. + log! { @3 + "could not reconstruct sample ({} {})", + self.instance[pred], + args + } + } + Ok(false) } /// Generates a sample for each positive clause. @@ -537,7 +574,7 @@ impl<'a> Reconstr<'a> { /// Reconstructs the positive samples. pub fn work(mut self) -> Res { - if self.to_do.is_empty() { + if self.to_do.iter().all(|to_do| to_do.is_empty()) { log! { @4 | "no samples to reconstruct, generating samples from positive clauses" } self.samples_of_pos_clauses()?; log! { @4 | "done, generated {} sample(s)", self.samples.len() } @@ -566,14 +603,46 @@ impl<'a> Reconstr<'a> { } } - while let Some(sample) = self.to_do.pop() { - if let Err(e) = self.work_on_sample(sample.clone()) { - print_err(&e); - self.samples.insert(sample); + 'all_branches: while let Some(mut to_do) = self.to_do.pop() { + if_log ! { @3 + log! { @3 |=> "to_do {} other branch(es):", self.to_do.len() } + for sample in &to_do { + log! { @3 |=> " ({} {})", self.instance[sample.pred], sample.args } + } + } + while let Some(sample) = to_do.pop() { + match self.work_on_sample(sample.clone(), &to_do) { + Err(e) => { + print_err(&e); + self.samples.insert(sample); + } + Ok(true) => { + log! { @4 | "positive" } + // This sample was positive, keep going in the current branch. + () + } + Ok(false) => { + log! { @4 | "new branches" } + // New branches to explore, keep going. + continue 'all_branches; + } + } } + + // Reachable if all samples in `to_do` have been explained positively. + log! { @3 | + "solution found, discarding {} remaining path(s)", self.to_do.len() + } + self.to_do.clear(); + break 'all_branches; } self.solver.reset()?; + + if self.samples.is_empty() { + bail!("could not reconstruct entry points") + } + Ok(self.samples) } } From 7e66fa06abd8f4b251e5cd5b2a5e0fbe7096efa2 Mon Sep 17 00:00:00 2001 From: AdrienChampion Date: Tue, 16 Oct 2018 15:38:18 +0200 Subject: [PATCH 94/94] version bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 538a6b06..b4f633ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hoice" -version = "1.5.0" +version = "1.7.0" authors = ["Adrien Champion "] description = "A ICE-based Horn clause solver." homepage = "https://github.com/hopv/hoice"