razor_chase/chase.rs
1//! Provides a framework and interfaces to various components that are used for implementing the
2//! Chase. It also implements entrypoints for running the Chase.
3//!
4//! ## Background
5//! Razor implements a variant of [the Chase] algorithm to construct models for first-order theories with
6//! equality. The Chase operates on *[geometric theories]*, theories that contain a syntactic
7//! variation of first-order formulae which we refer to as the Geometric Normal Form (GNF). Formulae
8//! in GNF have the following shape:
9//!
10//! A<sub>1</sub> ∧ ... ∧ A<sub>m</sub> →
11//! (∃ x<sub>11</sub>, ..., x<sub>1j<sub>1</sub></sub> . A<sub>11</sub> ∧ ... ∧ A<sub>1n<sub>1</sub></sub>) </br>
12//! > >
13//! ∨ (∃ x<sub>21</sub>, ..., x<sub>2j<sub>2</sub></sub> . A<sub>21</sub> ∧ ... ∧ A<sub>2n<sub>2</sub></sub>) </br>
14//! > >
15//! ∨ ... </br>
16//! > >
17//! ∨ (∃ x<sub>i1</sub>, ..., x<sub>ij<sub>i</sub></sub> . A<sub>i1</sub> ∧ ... ∧ A<sub>in<sub>i</sub></sub>)
18//!
19//! where A<sub>k</sub>s are (positive) atomic formulae (possibly including equality) and free
20//! variables are assumed to be universally qualified over the entire formula.
21//!
22//! In the context of a [run of the Chase], we refer to formulae in the their GNF as
23//! [*sequents*][sequent]. The premise (left side) and the consequence (right side) of the
24//! implication are respectively said to be the *body* and the *head* of the sequent.
25//!
26//! ### Satisfiability of Geometric Theories
27//! It turns out that every first-order theory can be transformed to a geometric theory that is
28//! *equisatisfiable* to the original theory via [standard syntactic manipulation].
29//! In fact, for every model *N* of the original theory, there exists a model *M* of the geometric
30//! theory such that there is a homomorphism from *M* to *N*. This is an important result that
31//! enables Razor to utilize the Chase to construct homomorphically minimal models of a given
32//! first-order theory.
33//!
34//! In the context of a model-finding application, the models that the Chase produces are desirable
35//! since they contain minimum amount of information, thus they induce minimal distraction.
36//! As a direct consequence of semi-decidability of satisfiability in first-order logic
37//! (see [Gödel's incompleteness theorems][godel]), satisfiability of geometric theories is
38//! semi-decidable as well.
39//!
40//! **Note**: A comprehensive discussion on the properties of the models that are constructed by
41//! the Chase is out of the scope of this document.
42//!
43//! [the Chase]: https://en.wikipedia.org/wiki/Chase_(algorithm)
44//! [geometric theories]: https://www.cs.bham.ac.uk/~sjv/GLiCS.pdf
45//! [run of the Chase]: ./fn.solve_all.html
46//! [sequent]: ./trait.SequentTrait.html
47//! [standard syntactic manipulation]: ../formula/syntax/enum.Formula.html#method.gnf
48//! [godel]: https://en.wikipedia.org/wiki/Gödel%27s_incompleteness_theorems
49//!
50//! ## The Chase
51//! Given a geometric theory and starting with an empty model, a run of Chase consists of repeated
52//! applications of [chase-step]s by which the model is augmented with *necessary* pieces of
53//! information until there is a contradiction or the model satisfies the theory. Within
54//! Razor's implementation, instances of any type that implements [ModelTrait] can serve as a
55//! first-order model. Also, inspired by [Steven Vickers][vickers], we refer to the units of
56//! information that augment models as [observation]s.
57//!
58//! [chase-step]: index.html#chase-step
59//! [vickers]: https://www.cs.bham.ac.uk/~sjv/GeoZ.pdf
60//!
61//! ### Chase Step
62//! Given a geometric theory and an existing model, a chase-step proceeds as follows:
63//!
64//! 1. A sequent from the theory is selected to be evaluated against the model. Razor uses an
65//! instance of [StrategyTrait] to select the sequent.
66//!
67//! 2. The selected sequent is evaluated against the model: given an assignment from the free
68//! variables of the sequent to the elements of the model, if the body of the sequent is true and
69//! its head is not true in the model, new observations are added to the model to make the
70//! sequent's head true. Instances of any type that implements [EvaluatorTrait] may be used to
71//! evaluate the sequent in the model.
72//!
73//! 2.1. If the sequent is headless, meaning its consequence is falsehood (an empty disjunction),
74//! the Chase fails on the given model.
75//!
76//! 2.2. If the head of the sequent contains more than one disjunct (with at least one
77//! non-trivial disjunction), the Chase branches and satisfies each disjunct independently on clones
78//! of the model. Razor uses instances of [SchedulerTrait] to schedule various branches of the Chase
79//! for future Chase steps.
80//!
81//! 2.3. If no sequent can be found such that its body and head are respectively true and false
82//! in the model, the model already satisfies the theory and will be returned as an output of the
83//! Chase.
84//!
85//! [model]: ./trait.ModelTrait.html
86//! [observation]: ./enum.Observation.html
87//! [ModelTrait]: ./trait.ModelTrait.html
88//! [StrategyTrait]: ./trait.StrategyTrait.html
89//! [EvaluatorTrait]: ./trait.EvaluatorTrait.html
90//! [SchedulerTrait]: ./trait.SchedulerTrait.html
91//!
92//! ### Termination
93//! As a result of semi-decidability of geometric theories, it can be shown if a geometric theory
94//! is unsatisfiable, a run of the Chase on the theory always terminates, although it may take
95//! a very very long time.
96//! However, when the theory is satisfiable, a run of the Chase may not terminate, producing
97//! infinitely large models and/or infinitely many models that satisfy the theory. Nevertheless,
98//! in practice, Razor can *bound* the size of models created by the Chase to guarantee termination.
99//! Razor uses instances of types that implement [BounderTrait] to implement various strategies to
100//! cap the size of search space for models.
101//!
102//! [BounderTrait]: ./trait.BounderTrait.html
103//!
104//! ## Implementation
105//! The primary motivation for implementing Razor is to study the Chase and improve its performance
106//! for practical applications. The flexible (but somehow complex) design of Razor allows for
107//! experimenting with various data structures to represent [models] and [sequents], [evaluating]
108//! sequents in models using a variety of algorithms, testing different ideas for [scheduling]
109//! branches of the Chase and devising various [strategies] for selecting sequents. Also, because of
110//! theoretical and non-theoretical consequences of non-termination of the Chase, Razor is going to
111//! explore a variety of ideas for limiting the search space by [bounding] the size of models:
112//!
113//! Interesting combinations of these various options are constantly benchmarked and the
114//! configuration with the best average performance is used by the Rusty Razor application.
115//!
116//! [models]: ./trait.ModelTrait.html
117//! [sequents]: ./trait.SequentTrait.html
118//! [evaluating]: ./trait.EvaluatorTrait.html
119//! [scheduling]: ./trait.SchedulerTrait.html
120//! [strategies]: ./trait.StrategyTrait.html
121//! [bounding]: ./trait.BounderTrait.html
122//!
123pub mod r#impl;
124pub mod strategy;
125pub mod scheduler;
126pub mod bounder;
127
128use razor_fol::syntax::*;
129use std::fmt;
130
131use tracing;
132use itertools::Either;
133
134/// Is a symbol to represent elements of first-order models. An element is identified by an index.
135#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
136pub struct E(pub i32);
137
138impl E {
139 /// Identifies the receiver with `other` element by collapsing their indices.
140 /// ```rust
141 /// # use razor_chase::chase::E;
142 /// let mut e_1 = E::from(1);
143 /// let e_2 = E::from(2);
144 /// assert_ne!(e_1, e_2);
145 ///
146 /// e_1.identify(&e_2);
147 /// assert_eq!(e_1, e_2);
148 /// ```
149 pub fn identify(&mut self, other: &Self) {
150 self.0 = other.0;
151 }
152}
153
154impl From<i32> for E {
155 fn from(i: i32) -> Self {
156 E(i)
157 }
158}
159
160impl fmt::Display for E {
161 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
162 write!(f, "e#{}", self.0)
163 }
164}
165
166impl fmt::Debug for E {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 write!(f, "{}", self.to_string())
169 }
170}
171
172/// Is the trait for special kind of variable-free terms that are used to witness existence of
173/// model elements. These terms are used as provenance information for models to describe *why*
174/// elements exist or facts are true in models.
175pub trait WitnessTermTrait: Clone + PartialEq + Eq + fmt::Display + FApp {
176 /// Is the type of elements that are witnessed by the witness term.
177 ///
178 /// **Note**: Although [`E`] is often the underlying symbol for representing model
179 /// elements, models may implement their own element types based on or independent from `E`.
180 /// See [`chase::impl::reference::Model`](./impl/reference/struct.Model.html) for an example.
181 ///
182 /// [`E`]: ./struct.E.html
183 ///
184 type ElementType;
185
186 /// Returns an equational [`Observation`] between the receiver and the given [witness term].
187 ///
188 /// [`Observation`]: ./enum.Observation.html
189 /// [witness term]: ./trait.WitnessTermTrait.html
190 fn equals(self, rhs: Self) -> Observation<Self> {
191 Observation::Identity { left: self, right: rhs }
192 }
193}
194
195/// Relations are semantic counterparts of predicates and are used to store [`Fact`]s in models.
196///
197/// **Note**: `Rel` is the counterpart of [`Pred`] at a semantic level.
198///
199/// [`Fact`]: ./enum.Observation.html#variant.Fact
200/// [`Pred`]: ../formula/syntax/struct.Pred.html
201#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
202pub struct Rel(pub String);
203
204impl Rel {
205 /// Applies the receiver to a list of witness terms.
206 pub fn app<T: WitnessTermTrait>(self, terms: Vec<T>) -> Observation<T> {
207 Observation::Fact { relation: self, terms }
208 }
209
210 /// Applies the (nullary) receiver.
211 pub fn app0<T: WitnessTermTrait>(self) -> Observation<T> {
212 Observation::Fact { relation: self, terms: vec![] }
213 }
214
215 /// Applies the (unary) receiver on a witness term.
216 pub fn app1<T: WitnessTermTrait>(self, first: T) -> Observation<T> {
217 Observation::Fact { relation: self, terms: vec![first] }
218 }
219
220 /// Applies the (binary) receiver on two witness terms.
221 pub fn app2<T: WitnessTermTrait>(self, first: T, second: T) -> Observation<T> {
222 Observation::Fact { relation: self, terms: vec![first, second] }
223 }
224
225 /// Applies the (ternary) receiver on three witness terms.
226 pub fn app3<T: WitnessTermTrait>(self, first: T, second: T, third: T) -> Observation<T> {
227 Observation::Fact { relation: self, terms: vec![first, second, third] }
228 }
229
230 /// Applies the (4-ary) receiver on four witness terms.
231 pub fn app4<T: WitnessTermTrait>(self, first: T, second: T, third: T, fourth: T) -> Observation<T> {
232 Observation::Fact { relation: self, terms: vec![first, second, third, fourth] }
233 }
234
235 /// Applies the (5-ary) receiver on five witness terms.
236 pub fn app5<T: WitnessTermTrait>(self, first: T, second: T, third: T, fourth: T, fifth: T) -> Observation<T> {
237 Observation::Fact { relation: self, terms: vec![first, second, third, fourth, fifth] }
238 }
239}
240
241impl From<&str> for Rel {
242 fn from(s: &str) -> Self {
243 Rel(s.to_owned())
244 }
245}
246
247impl From<String> for Rel {
248 fn from(s: String) -> Self {
249 Rel(s)
250 }
251}
252
253impl fmt::Display for Rel {
254 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
255 write!(f, "{}", self.0)
256 }
257}
258
259impl fmt::Debug for Rel {
260 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261 write!(f, "{}", self.to_string())
262 }
263}
264
265impl From<Pred> for Rel {
266 fn from(predicate: Pred) -> Self {
267 Rel(predicate.0)
268 }
269}
270
271/// Represents positive units of information by which [`Model`]s are constructed. Once a `Model` is
272/// augmented by an observation, the observation remains true in the model.
273///
274/// [`Model`]: ./trait.ModelTrait.html
275#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
276pub enum Observation<T: WitnessTermTrait> {
277 /// Is a relational fact, indicating that an atomic formula is true about a list of model
278 /// elements, denoted by a list of [witness terms].
279 ///
280 /// [witness terms]: ./trait.WitnessTermTrait.html
281 Fact { relation: Rel, terms: Vec<T> },
282
283 /// Is an equational fact, corresponding to an identity between two model elements, denoted
284 /// by a two [witness terms].
285 ///
286 /// [witness terms]: ./trait.WitnessTermTrait.html
287 Identity { left: T, right: T },
288}
289
290impl<T: WitnessTermTrait> fmt::Display for Observation<T> {
291 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
292 match self {
293 Observation::Fact { relation, terms } => {
294 let ts: Vec<String> = terms.iter().map(|t| t.to_string()).collect();
295 write!(f, "<{}({})>", relation, ts.join(", "))
296 }
297 Observation::Identity { left, right } => write!(f, "<{} = {}>", left, right),
298 }
299 }
300}
301
302impl<T: WitnessTermTrait> fmt::Debug for Observation<T> {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 write!(f, "{}", self.to_string())
305 }
306}
307
308/// Is the trait for various implementations of first-order models that are constructed by the
309/// Chase.
310pub trait ModelTrait: Clone + fmt::Display + ToString {
311 /// Is the type of witness terms, denoting elements of this model. [`WitnessTerm::ElementType`]
312 /// is the type of elements for this model.
313 ///
314 /// [`WitnessTerm::ElementType`]: ./trait.WitnessTermTrait.html#associatedtype.ElementType
315 type TermType: WitnessTermTrait;
316
317 /// Returns a unique ID for the receiver.
318 fn get_id(&self) -> u64;
319
320 /// Returns the domain of the receiver. The domain of a model consists of all elements in the
321 /// model.
322 fn domain(&self) -> Vec<&<Self::TermType as WitnessTermTrait>::ElementType>;
323
324 /// Returns the set of relational [`Fact`]s that are true in the receiver.
325 ///
326 /// [`Fact`]: ./enum.Observation.html#variant.Fact
327 fn facts(&self) -> Vec<&Observation<Self::TermType>>;
328
329 /// Augments the receiver with `observation`, making `observation`true in the receiver.
330 fn observe(&mut self, observation: &Observation<Self::TermType>);
331
332 /// Returns true if `observation` is true in the receiver; otherwise, returns false.
333 fn is_observed(&self, observation: &Observation<Self::TermType>) -> bool;
334
335 /// Returns a set of all witness terms in the receiver that are denoted by `element`.
336 fn witness(&self, element: &<Self::TermType as WitnessTermTrait>::ElementType) -> Vec<&Self::TermType>;
337
338 /// Returns the element in the receiver that is denoted by `witness`.
339 fn element(&self, witness: &Self::TermType) -> Option<&<Self::TermType as WitnessTermTrait>::ElementType>;
340}
341
342/// Is the trait for types that represents a [geometric sequent][sequent] in the
343/// context of an implementation of the Chase.
344///
345/// [sequent]: ./index.html#background
346pub trait SequentTrait: Clone {
347 /// Returns the *body* (premise) of the sequent as a formula.
348 fn body(&self) -> Formula;
349
350 /// Returns the *head* (consequence) of the sequent as a formula.
351 fn head(&self) -> Formula;
352}
353
354/// Strategy is the trait of algorithms for choosing sequents in the context of an implementation
355/// of the Chase. Strategy instances provide the next sequent to be evaluated by the Chase.
356///
357/// **Note**: See [here] for more information about how strategy instances are used.
358///
359/// [here]: ./index.html#implementation
360pub trait StrategyTrait: Clone + Iterator {
361 /// Construct an instance of `Strategy` from a given list of sequents as items.
362 fn new(sequents: Vec<Self::Item>) -> Self;
363}
364
365/// Bounder is the trait of algorithms for [bounding] the size of models generated by the Chase.
366///
367/// [bounding]: ./index.html#termination
368pub trait BounderTrait {
369 /// Returns true if the given observation is outside of the bounds set for the given model
370 /// according to the algorithm implemented by the receiver. If the result is true, the Chase
371 /// stops processing the model.
372 fn bound<M: ModelTrait>(&self, model: &M, observation: &Observation<M::TermType>) -> bool;
373}
374
375/// Is the trait of algorithms that evaluate an implementation of [SequentTrait] in an
376/// implementation of [ModelTrait] within a [chase-step].
377///
378/// [SequentTrait]: ./trait.SequentTrait.html
379/// [ModelTrait]: ./trait.ModelTrait.html
380/// [chase-step]: ./index.html#chase-step
381pub trait EvaluatorTrait<'s, Stg: StrategyTrait<Item=&'s Self::Sequent>, B: BounderTrait> {
382 /// The type of [sequents] that can be processed by this evaluator.
383 ///
384 /// [SequentTrait]: ./trait.SequentTrait.html
385 type Sequent: 's + SequentTrait;
386
387 /// The type of [models] that can be processed by this evaluator.
388 ///
389 /// [models]: ./trait.ModelTrait.html
390 type Model: ModelTrait;
391
392 /// Applies a [chase-step] by evaluating the sequents of `strategy` in `model` and produces
393 /// extensions of `model`, corresponding to new branches of the Chase. New models that do not
394 /// reach the bound provided by `bounder` are returned as `open_models` in the resulting
395 /// [`EvaluateResult`]. New models that reach the bound provided by `bounder` are returned
396 /// as `bounded_models` in the return value.
397 ///
398 /// **Note**: The `open_models` in the return value will be scheduled for future chase-steps.
399 ///
400 /// [chase-step]: ./index.html#chase-step
401 /// [`EvaluateResult`]: ./struct.EvaluateResult.html
402 fn evaluate(
403 &self,
404 model: &Self::Model,
405 strategy: &mut Stg,
406 bounder: Option<&B>,
407 ) -> Option<EvaluateResult<Self::Model>>;
408}
409
410/// Is result of [evaluating] a model in a [chase-step].
411///
412/// [evaluating]: ./trait.EvaluatorTrait.html
413/// [chase-step]: ./index.html#chase-step
414pub struct EvaluateResult<M: ModelTrait> {
415 /// Is a list of all not-bounded extensions of a model after [evaluation].
416 ///
417 /// [evaluation]: ./trait.EvaluatorTrait.html#tymethod.evaluate
418 open_models: Vec<M>,
419
420 /// Is a list of bounded extensions of a model after a [evaluation].
421 ///
422 /// [evaluation]: ./trait.EvaluatorTrait.html#tymethod.evaluate
423 bounded_models: Vec<M>,
424}
425
426impl<M: ModelTrait> EvaluateResult<M> {
427 /// Returns an empty instance of [`EvaluateResult`].
428 ///
429 /// [`EvaluateResult`]: ./struct.EvaluateResult.html
430 pub fn new() -> Self {
431 Self {
432 open_models: Vec::new(),
433 bounded_models: Vec::new(),
434 }
435 }
436
437 /// Returns true if the receiver contains no models (neither `open_models` nor `bounded_models`)
438 /// and false otherwise.
439 pub fn empty(&self) -> bool {
440 self.open_models.is_empty() && self.bounded_models.is_empty()
441 }
442
443 /// Clears the list of `open_models` in the receiver.
444 pub fn clear_open_models(&mut self) {
445 self.open_models.clear();
446 }
447
448 /// Clears the list of `bounded_models` in the receiver.
449 pub fn clear_bounded_models(&mut self) {
450 self.bounded_models.clear();
451 }
452
453 /// Appends `model` to the list of `open_models` of the receiver.
454 pub fn append_open_model(&mut self, model: M) {
455 self.open_models.push(model);
456 }
457
458 /// Appends `model` to the list of `bounded_models` of the receiver.
459 pub fn append_bounded_model(&mut self, model: M) {
460 self.bounded_models.push(model);
461 }
462
463 /// Appends the value in `model` to the list of `open_models` or `bounded_models` of the
464 /// receiver if `model` is respectively a left or a right value.
465 pub fn append(&mut self, model: Either<M, M>) {
466 match model {
467 Either::Left(m) => self.append_open_model(m),
468 Either::Right(m) => self.append_bounded_model(m),
469 };
470 }
471}
472
473impl<M: ModelTrait> From<Vec<Either<M, M>>> for EvaluateResult<M> {
474 fn from(models: Vec<Either<M, M>>) -> Self {
475 let mut result = EvaluateResult::new();
476 models.into_iter().for_each(|m| result.append(m));
477 result
478 }
479}
480
481/// Is the trait of algorithms for scheduling various branches of the Chase. A branch of the Chase
482/// is represented with a [model] together with a [strategy] for scheduling sequents to be
483/// evaluated in the model.
484///
485/// **Note**: See [here] for more information about scheduling branches of the Chase.
486///
487/// [model]: ./trait.ModelTrait.html
488/// [strategy]: ./trait.StrategyTrait.html
489/// [here]: ./index.html#implementation
490pub trait SchedulerTrait<'s, S: 's + SequentTrait, M: ModelTrait, Stg: StrategyTrait<Item=&'s S>> {
491 /// Returns true if the scheduler is empty and false otherwise.
492 fn empty(&self) -> bool;
493
494 /// Schedules a [model] and its associated [strategy] as a branch of the Chase.
495 ///
496 /// [model]: ./trait.ModelTrait.html
497 /// [strategy]: ./trait.StrategyTrait.html
498 fn add(&mut self, model: M, strategy: Stg);
499
500 /// Removes the next scheduled item from the receiver and returns its associated [model] and
501 /// [strategy].
502 ///
503 /// [model]: ./trait.ModelTrait.html
504 /// [strategy]: ./trait.StrategyTrait.html
505 fn remove(&mut self) -> Option<(M, Stg)>;
506}
507
508/// Given a [`scheduler`], an [`evaluator`] and possibly a [`bounder`], runs an implementation
509/// independent run of [the Chase] and returns *all* resulting models. The return value is empty if
510/// the theory is not satisfiable.
511///
512/// [`scheduler`]: ./trait.SchedulerTrait.html
513/// [`evaluator`]: ./trait.EvaluatorTrait.html
514/// [`bounder`]: ./trait.BounderTrait.html
515/// [the Chase]: ./index.html#the-chase
516///
517/// ```rust
518/// use razor_fol::syntax::Theory;
519/// use razor_chase::chase::{
520/// SchedulerTrait, StrategyTrait, chase_all,
521/// r#impl::basic,
522/// strategy::Linear,
523/// scheduler::FIFO,
524/// bounder::DomainSize,
525/// };
526///
527/// // parse the theory:
528/// let theory: Theory = r#"
529/// exists x . P(x);
530/// P(x) implies Q(x) | R(x);
531/// R(x) -> exists y . Q(x, y);
532/// "#.parse().unwrap();
533///
534/// let geometric_theory = theory.gnf(); // convert the theory to geometric
535///
536/// // create sequents for the geometric theory:
537/// let sequents: Vec<basic::Sequent> = geometric_theory
538/// .formulae
539/// .iter()
540/// .map(|f| f.into())
541/// .collect();
542///
543/// let evaluator = basic::Evaluator {};
544/// let strategy = Linear::new(sequents.iter().collect()); // use the `Linear` strategy
545/// let mut scheduler = FIFO::new(); // schedule branches in first-in-first-out manner
546///
547/// // run unbounded model-finding (note that the Chase terminates on the given theory):
548/// let bounder: Option<&DomainSize> = None;
549/// scheduler.add(basic::Model::new(), strategy); // schedule the initial (empty) model
550/// let models = chase_all(&mut scheduler, &evaluator, bounder);
551///
552/// assert_eq!(2, models.len()); // two models are found
553/// ```
554pub fn chase_all<'s, S, M, Stg, Sch, E, B>(
555 scheduler: &mut Sch,
556 evaluator: &E,
557 bounder: Option<&B>,
558) -> Vec<M>
559 where S: 's + SequentTrait,
560 M: ModelTrait,
561 Stg: StrategyTrait<Item=&'s S>,
562 Sch: SchedulerTrait<'s, S, M, Stg>,
563 E: EvaluatorTrait<'s, Stg, B, Sequent=S, Model=M>,
564 B: BounderTrait {
565 let mut result: Vec<M> = Vec::new();
566 while !scheduler.empty() {
567 chase_step(scheduler, evaluator, bounder, |m| result.push(m), |_|{});
568 }
569 result
570}
571
572/// Given a [`scheduler`], an [`evaluator`], possibly a [`bounder`] and a `consumer` closure, runs
573/// an implementation independent [chase-step]. Satisfying models of the theory that are produced
574/// by the `chase-step` will be consumed by the `consumer`. In contrast, `incomplete_consumer`
575/// (if provided) consumes incomplete non-models of the theory that are rejected by the bounder.
576///
577/// [`scheduler`]: ./trait.SchedulerTrait.html
578/// [`evaluator`]: ./trait.EvaluatorTrait.html
579/// [`bounder`]: ./trait.BounderTrait.html
580/// [chase-step]: ./index.html#chase-step
581///
582/// ```rust
583/// use razor_fol::syntax::Theory;
584/// use razor_chase::chase::{
585/// SchedulerTrait, StrategyTrait, chase_step,
586/// r#impl::basic,
587/// strategy::Linear,
588/// scheduler::FIFO,
589/// bounder::DomainSize,
590/// };
591///
592/// // parse the theory:
593/// let theory: Theory = r#"
594/// exists x . P(x);
595/// P(x) implies Q(x) | R(x);
596/// R(x) -> exists y . Q(x, y);
597/// "#.parse().unwrap();
598///
599/// let geometric_theory = theory.gnf(); // convert the theory to geometric
600///
601/// // create sequents for the geometric theory:
602/// let sequents: Vec<basic::Sequent> = geometric_theory
603/// .formulae
604/// .iter()
605/// .map(|f| f.into())
606/// .collect();
607///
608/// let evaluator = basic::Evaluator {};
609/// let strategy = Linear::new(sequents.iter().collect()); // use the `Linear` strategy
610/// let mut scheduler = FIFO::new(); // schedule branches in first-in-first-out manner
611///
612/// // run unbounded model-finding (note that the Chase terminates on the given theory):
613/// let bounder: Option<&DomainSize> = None;
614/// scheduler.add(basic::Model::new(), strategy); // schedule the initial (empty) model
615///
616/// let mut counter = 0; // a counter to count the number of models
617/// while !scheduler.empty() {
618/// let models = chase_step(
619/// &mut scheduler,
620/// &evaluator,
621/// bounder,
622/// |m| {counter += 1}, // the closure counts the models found
623/// |_|{}, // ignore incomplete non-models
624/// );
625/// }
626///
627/// assert_eq!(2, counter); // two models are found
628/// ```
629pub fn chase_step<'s, S, M, Stg, Sch, E, B>(
630 scheduler: &mut Sch,
631 evaluator: &E,
632 bounder: Option<&B>,
633 mut consumer: impl FnMut(M),
634 mut incomplete_consumer: impl FnMut(M),
635) where S: 's + SequentTrait,
636 M: ModelTrait,
637 Stg: StrategyTrait<Item=&'s S>,
638 Sch: SchedulerTrait<'s, S, M, Stg>,
639 E: EvaluatorTrait<'s, Stg, B, Sequent=S, Model=M>,
640 B: BounderTrait {
641 let (base_model, mut strategy) = scheduler.remove().unwrap();
642 let base_id = &base_model.get_id();
643 let span = span!(tracing::Level::TRACE, super::trace::CHASE_STEP, id = base_id);
644 let models = evaluator.evaluate(&base_model, &mut strategy, bounder);
645
646 if let Some(models) = models {
647 if !models.empty() {
648 models.open_models.into_iter().for_each(|m| {
649 let _enter = span.enter();
650 info!(
651 event = super::trace::EXTEND,
652 model_id = &m.get_id(),
653 parent = base_id,
654 model = %m,
655 );
656 scheduler.add(m, strategy.clone());
657 });
658 models.bounded_models.into_iter().for_each(|m| {
659 let _enter = span.enter();
660 info!(
661 event = super::trace::BOUND,
662 model_id = &m.get_id(),
663 parent = base_id,
664 model = %m,
665 );
666 incomplete_consumer(m);
667 });
668 } else {
669 let _enter = span.enter();
670 info!(
671 event = super::trace::MODEL,
672 model_id = &base_id,
673 model = %base_model,
674 );
675 consumer(base_model);
676 }
677 } else {
678 let _enter = span.enter();
679 info!(
680 event = super::trace::FAIL,
681 model_id = &base_id,
682 model = %base_model,
683 );
684 }
685}
686
687//// Tests -------------------------------------
688#[cfg(test)]
689mod test_chase {
690 use crate::test_prelude::*;
691
692 #[test]
693 fn test_element() {
694 assert_eq!("e#0", e_0().to_string());
695 assert_eq!(e_0(), e_0());
696 assert_ne!(e_0(), e_1());
697 assert!(e_0() < e_1());
698 assert!(!(e_0() > e_1()));
699 assert!(!(e_1() > e_1()));
700 assert!(!(e_1() < e_1()));
701 {
702 let mut e0 = e_0();
703 let e1 = e_1();
704 e0.identify(&e1);
705 assert_eq!(e_1(), e0);
706 assert_eq!(e_1(), e1);
707 }
708 }
709
710 #[test]
711 fn test_rel() {
712 assert_eq!(_R_(), R().into());
713 assert_eq!("R", _R_().to_string());
714 }
715}