├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── book ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ ├── background.md │ ├── background │ ├── chase.md │ ├── satisfiability.md │ └── termination.md │ ├── build.md │ ├── example.md │ ├── example │ ├── golden-head.md │ ├── hold-the-door.md │ └── valar-morghulis.md │ ├── intro.md │ ├── run.md │ ├── run │ ├── bounded.md │ └── scheduler.md │ ├── syntax.md │ └── syntax │ ├── grammar.md │ ├── precedence.md │ ├── syntax │ └── variations.md ├── razor-chase ├── Cargo.toml ├── benches │ └── perf_test.rs └── src │ ├── chase.rs │ ├── chase │ ├── bounder.rs │ ├── impl.rs │ ├── impl │ │ ├── basic.rs │ │ ├── batch.rs │ │ ├── collapse.rs │ │ ├── relational.rs │ │ └── relational │ │ │ ├── attribute.rs │ │ │ ├── constants.rs │ │ │ ├── evaluator.rs │ │ │ ├── expression.rs │ │ │ ├── model.rs │ │ │ ├── pre_processor.rs │ │ │ ├── rewrite.rs │ │ │ ├── sequent.rs │ │ │ └── symbol.rs │ ├── scheduler.rs │ └── strategy.rs │ ├── lib.rs │ ├── test_prelude.rs │ ├── trace.rs │ └── trace │ └── subscriber.rs ├── razor-fol ├── Cargo.toml ├── build.rs └── src │ ├── grammar.lalrpop │ ├── lib.rs │ ├── parser.rs │ ├── syntax.rs │ ├── syntax │ ├── formula.rs │ ├── formula │ │ ├── clause.rs │ │ ├── fof.rs │ │ └── qff.rs │ ├── macros.rs │ ├── signature.rs │ ├── symbol.rs │ ├── term.rs │ └── theory.rs │ ├── test_macros.rs │ ├── transform.rs │ └── transform │ ├── cnf.rs │ ├── dnf.rs │ ├── gnf.rs │ ├── linear.rs │ ├── nnf.rs │ ├── pnf.rs │ ├── range_restrict.rs │ ├── relational.rs │ ├── simplify.rs │ └── snf.rs ├── razor ├── Cargo.toml └── src │ ├── command.rs │ ├── constants.rs │ ├── main.rs │ ├── terminal.rs │ └── utils.rs └── theories ├── bounded ├── thy0.config ├── thy0.model ├── thy0.raz ├── thy1.config ├── thy1.model ├── thy1.raz ├── thy2.config ├── thy2.model ├── thy2.raz ├── thy3.config ├── thy3.model ├── thy3.raz ├── thy4.config ├── thy4.model ├── thy4.raz ├── thy5.config ├── thy5.model ├── thy5.raz ├── thy6.config ├── thy6.model ├── thy6.raz ├── thy7.config ├── thy7.model └── thy7.raz ├── core ├── thy0.model ├── thy0.raz ├── thy1.model ├── thy1.raz ├── thy10.model ├── thy10.raz ├── thy11.model ├── thy11.raz ├── thy12.model ├── thy12.raz ├── thy13.model ├── thy13.raz ├── thy14.model ├── thy14.raz ├── thy15.model ├── thy15.raz ├── thy16.model ├── thy16.raz ├── thy17.model ├── thy17.raz ├── thy18.model ├── thy18.raz ├── thy19.model ├── thy19.raz ├── thy2.model ├── thy2.raz ├── thy20.model ├── thy20.raz ├── thy21.model ├── thy21.raz ├── thy22.model ├── thy22.raz ├── thy23.model ├── thy23.raz ├── thy24.model ├── thy24.raz ├── thy25.model ├── thy25.raz ├── thy26.model ├── thy26.raz ├── thy27.model ├── thy27.raz ├── thy28.model ├── thy28.raz ├── thy29.model ├── thy29.raz ├── thy3.model ├── thy3.raz ├── thy30.model ├── thy30.raz ├── thy31.model ├── thy31.raz ├── thy32.model ├── thy32.raz ├── thy33.model ├── thy33.raz ├── thy35.model ├── thy35.raz ├── thy36.model ├── thy36.raz ├── thy37.model ├── thy37.raz ├── thy38.model ├── thy38.raz ├── thy39.model ├── thy39.raz ├── thy4.model ├── thy4.raz ├── thy40.model ├── thy40.raz ├── thy41.model ├── thy41.raz ├── thy42.model ├── thy42.raz ├── thy43.model ├── thy43.raz ├── thy44.model ├── thy44.raz ├── thy45.model ├── thy45.raz ├── thy46.model ├── thy46.raz ├── thy47.model ├── thy47.raz ├── thy5.model ├── thy5.raz ├── thy6.model ├── thy6.raz ├── thy7.model ├── thy7.raz ├── thy8.model ├── thy8.raz ├── thy9.model └── thy9.raz └── examples ├── golden-lion.raz ├── grandpa.raz ├── hodor-linear.raz ├── hodor-time-loop.raz ├── lannisters.raz ├── toy.raz └── valar-morghulis.raz /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Build 13 | run: cargo build --verbose 14 | - name: Run tests 15 | run: cargo test --verbose 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | /target 13 | **/*.rs.bk 14 | rusty-razor.iml 15 | .idea/ 16 | .criterion/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "razor-fol", 4 | "razor-chase", 5 | "razor", 6 | ] -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.37 2 | 3 | COPY . . 4 | 5 | RUN cargo test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Salman Saghafi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rusty-razor 2 | 3 | Rusty Razor is a tool for constructing finite models for first-order theories. The model-finding algorithm is inspired 4 | by [the chase](https://en.wikipedia.org/wiki/Chase_(algorithm)) for database systems. Given a first-order theory, 5 | Razor constructs a set of *homomorphically minimal* models for the theory. 6 | 7 | Check out Razor's [documentation](https://salmans.github.io/rusty-razor/intro.html) for more information about the project and introductory examples. 8 | 9 | ## Build 10 | 11 | rusty-razor is written in Rust, so you will need [Rust](https://www.rust-lang.org) 1.48.0 or newer to compile it. 12 | To build rusty-razor: 13 | 14 | ``` 15 | git clone https://github.com/salmans/rusty-razor.git 16 | cd rusty-razor 17 | cargo build --release 18 | ``` 19 | 20 | This puts rusty-razor's executable in `/target/release`. 21 | 22 | ## Run 23 | 24 | ### `solve` 25 | 26 | Use the `solve` command to find models for a theory written in an `` file: 27 | 28 | ``` 29 | razor solve -i 30 | ``` 31 | 32 | The `--count` parameter limits the number of models to construct: 33 | 34 | ``` 35 | razor solve -i --count 36 | ``` 37 | 38 | #### Bounded Model-Finding 39 | 40 | Unlike conventional model-finders such as [Alloy](http://alloytools.org), Razor doesn't require the user to provide a 41 | bound on the size of the models that it constructs. However, Razor may never terminate when running on theories with 42 | non-finite models -- it can be shown that a run of Razor on an unsatisfiable theory (i.e., a theory with no models) 43 | is guaranteed to terminate (although it might take a very very long time).This is a direct consequence of 44 | semi-decidability of first-order logic. 45 | 46 | To guarantee termination, limit the size of the resulting models by the number of their elements using the `--bound` 47 | option with a value for the `domain` parameter: 48 | 49 | ``` 50 | razor solve -i --bound domain= 51 | ``` 52 | 53 | #### Model-Finding Scheduler 54 | 55 | Use the `--scheduler` option to choose how Razor processes search branches. The `fifo` scheduler (the default scheduler) 56 | schedules new branches last and is a more suitable option for processing theories with few small satisfying models. 57 | The `lifo` scheduler schedules new branches first, and is more suitable for processing theories with many large models. 58 | 59 | ``` 60 | razor solve -i --scheduler 61 | ``` 62 | 63 | ## Toy Example 64 | 65 | Consider the following example: 66 | 67 | ``` 68 | // All cats love all toys: 69 | forall c, t . (Cat(c) and Toy(t) implies Loves(c, t)); 70 | 71 | // All squishies are toys: 72 | forall s . (Squishy(s) implies Toy(s)); 73 | 74 | Cat('duchess); // Duchess is a cat 75 | Squishy('ducky); // Ducky is a squishy 76 | ``` 77 | 78 | You can download the example file [here](https://github.com/salmans/rusty-razor/blob/master/theories/examples/toy.raz) and run the `razor` command-line tool on it: 79 | 80 | ``` 81 | razor solve -i theories/examples/toy.raz 82 | ``` 83 | 84 | Razor returns only one model with `e#0` and `e#1` as elements that denote `'duchess` and 85 | `'ducky` respectively. The facts `Cat(e#0)`, `Squishy(e#1)`, and `Toy(e#1)` in the model 86 | are directly forced by the last two formula in the input theory. The fact `Loves(e#0, e#1)` 87 | is deduced by Razor: 88 | 89 | ``` 90 | Domain: e#0, e#1 91 | 92 | Elements: 'duchess -> e#0, 'ducky -> e#1 93 | 94 | Facts: Cat(e#0), Loves(e#0, e#1), Squishy(e#1), Toy(e#1) 95 | ``` 96 | 97 | Razor's documentation describes the [syntax](https://salmans.github.io/rusty-razor/syntax.html) 98 | of Razor's input and contains more [examples](https://salmans.github.io/rusty-razor/example.html). 99 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Salman Saghafi"] 3 | multilingual = false 4 | src = "src" 5 | title = "rusty-razor" 6 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](./intro.md) 4 | - [Background](./background.md) 5 | - [Satisfiability of Geometric Theories](./background/satisfiability.md) 6 | - [The Chase](./background/chase.md) 7 | - [Termination](./background/termination.md) 8 | - [Syntax](./syntax.md) 9 | - [Syntactic Variations](./syntax/variations.md) 10 | - [Connective Precedence](./syntax/precedence.md) 11 | - [Grammar](./syntax/grammar.md) 12 | - [Build](./build.md) 13 | - [Run](./run.md) 14 | - [Bounded Model-Finding](./run/bounded.md) 15 | - [Model-Finding Scheduler](./run/scheduler.md) 16 | - [Example](./example.md) 17 | - [Valar Morghulis](./example/valar-morghulis.md) 18 | - [Golden Head](./example/golden-head.md) 19 | - [Hold the Door](./example/hold-the-door.md) -------------------------------------------------------------------------------- /book/src/background.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | Razor implements a variant of [the chase][chase] algorithm to construct models for first-order theories with equality. The chase operates on [geometric theories][geometric], theories that contain a syntactic 4 | variation of first-order formulae which we refer to as the __Geometric Normal Form__ (GNF). Formulae 5 | in GNF have the following shape: 6 | 7 | A1 ∧ ... ∧ Am → 8 | (∃ x11, ..., x1j1 . A11 ∧ ... ∧ A1n1)
9 |     10 | ∨ (∃ x21, ..., x2j2 . A21 ∧ ... ∧ A2n2)
11 |     12 | ∨ ...
13 |     14 | ∨ (∃ xi1, ..., xiji . Ai1 ∧ ... ∧ Aini) 15 | 16 | where Aks are (positive) atomic formulae (possibly including equality) and free 17 | variables are assumed to be universally qualified over the entire formula. 18 | 19 | In the context of a run of the chase, we refer to the formulae in their GNF as 20 | __sequents__. The premise (left side) and the consequence (right side) of the implication are 21 | respectively said to be the _body_ and the _head_ of the sequent. 22 | 23 | [chase]: https://en.wikipedia.org/wiki/Chase_(algorithm) 24 | [geometric]: https://www.cs.bham.ac.uk/~sjv/GLiCS.pdf 25 | -------------------------------------------------------------------------------- /book/src/background/chase.md: -------------------------------------------------------------------------------- 1 | ## The Chase 2 | 3 | Given a geometric theory and starting with an empty model, a run of the chase consists of repeated 4 | applications of [chase-steps](#step) by which the model is augmented with _necessary_ pieces of 5 | information until there is a contradiction or the model satisfies the theory. Inspired by 6 | [Steven Vickers][vickers], we refer to the units of information that augment models as _observations_. 7 | 8 | ### Chase Step 9 | Given a geometric theory and an existing model, a chase-step proceeds as follows: 10 | 11 | 1. A sequent from the theory is selected to be evaluated against the model. 12 | 13 | 2. The selected sequent is evaluated against the model: given an assignment from the free 14 | variables of the sequent to the elements of the model, if the body of the sequent is true and 15 | its head is not true in the model, new observations are added to the model to make the 16 | sequent's head true. 17 | 18 | 2.1. If the sequent is headless, meaning its consequence is falsehood (an empty disjunction), 19 | the chase fails on the given model. 20 | 21 | 2.2. If the head of the sequent contains more than one disjunct (with at least one 22 | non-trivial disjunction), the chase branches and satisfies each disjunct independently on clones 23 | of the model. 24 | 25 | 2.3. If no sequent can be found such that its body and head are respectively true and false 26 | in the model, the model already satisfies the theory and will be returned as an output of the 27 | chase. 28 | 29 | [vickers]: https://www.cs.bham.ac.uk/~sjv/GeoZ.pdf 30 | -------------------------------------------------------------------------------- /book/src/background/satisfiability.md: -------------------------------------------------------------------------------- 1 | ## Satisfiability of Geometric Theories 2 | 3 | It turns out that every first-order theory can be transformed to a geometric theory that is 4 | _equisatisfiable_ to the original theory via standard syntactic manipulation. 5 | In fact, for every model `N` of the original theory, there exists a model `M` of the geometric 6 | theory such that there is a homomorphism from `M` to `N`. This is an important result that 7 | enables Razor to utilize the chase to construct homomorphically minimal models of a given 8 | first-order theory. 9 | 10 | In the context of a model-finding application, the models that the chase produces are desirable 11 | since they contain minimum amount of information, thus they induce minimal distraction. 12 | As a direct consequence of semi-decidability of satisfiability in first-order logic 13 | (see [Gödel's incompleteness theorems][godel]), satisfiability of geometric theories is 14 | semi-decidable as well. 15 | 16 | > __Note:__ A comprehensive discussion on the properties of the models that are constructed by 17 | the chase is out of the scope of this document. 18 | 19 | [godel]: https://en.wikipedia.org/wiki/Gödel%27s_incompleteness_theorems 20 | -------------------------------------------------------------------------------- /book/src/background/termination.md: -------------------------------------------------------------------------------- 1 | ## Termination 2 | 3 | As a result of semi-decidability of geometric theories, it can be shown if a geometric theory 4 | is unsatisfiable, a run of the chase on the theory always terminates, although it may take 5 | a very very long time. 6 | However, when the theory is satisfiable, a run of the chase may not terminate, producing 7 | infinitely large models and/or infinitely many models that satisfy the theory. Nevertheless, 8 | in practice, Razor can _bound_ the size of models created by the chase to guarantee termination. 9 | 10 | -------------------------------------------------------------------------------- /book/src/build.md: -------------------------------------------------------------------------------- 1 | # Build 2 | 3 | rusty-razor is written in Rust, so you will need [Rust](https://www.rust-lang.org) 1.37.0 or newer to compile it. 4 | To build rusty-razor: 5 | 6 | ``` 7 | git clone https://github.com/salmans/rusty-razor.git 8 | cd rusty-razor 9 | cargo build --release 10 | ``` 11 | 12 | This puts rusty-razor's executable in `/target/release`. -------------------------------------------------------------------------------- /book/src/example.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | This section presents sample first-order theories, written in Razor's syntax. 4 | All examples are inspired by the events of Game of Thrones (_spoiler alert!!_). 5 | 6 | - [Valar Morghulis](./example/valar-morghulis.html): demonstrates a run of Razor on a simple example. 7 | - [Golden Head](./example/golden-head.html): runs Razor over an unsatisfiable theory for which no models exist. 8 | - [Hold the Door](./example/hold-the-door.html): covers more advanced features of Razor on theories with infinite models. 9 | 10 | For more examples, see Razor's [example theories](https://github.com/salmans/rusty-razor/tree/master/theories/examples). 11 | -------------------------------------------------------------------------------- /book/src/example/golden-head.md: -------------------------------------------------------------------------------- 1 | ## Golden Head 2 | 3 | While reading "The Lineages and Histories of the Great Houses of the Seven Kingdoms", Lord Eddard Stark learns that 4 | throughout the history, all male members of House Baratheon were described as "black of hair" and concludes that King 5 | Robert is not Prince Joffrey's (biological) father. A judgment that eventually put his head on a spike. 6 | 7 | The next theory describes Ned's thought process: 8 | 9 | ``` 10 | // A person "x" cannot be both "black of hair" and "golden head" 11 | ~(BlackOfHair(x) & GoldenHead(x)); 12 | 13 | // Traditionally, a Baratheon child "y" inherited his/her father's ("x"'s) family name 14 | Baratheon(x) & father(y) = x -> Baratheon(y); 15 | 16 | // King Robert Baratheon is black of hair 17 | Baratheon('robert) & BlackOfHair('robert); 18 | 19 | // King Robert is Joffrey's father 20 | father('joffrey) = 'robert; 21 | 22 | // Joffrey has golden hair 23 | GoldenHead('joffrey); 24 | 25 | // Ned Stark's discovery (every Baratheon "x" is black of hair) 26 | Baratheon(x) -> BlackOfHair(x); 27 | ``` 28 | 29 | We can verify Ned's conclusion by running Razor on this theory 30 | [golden-lion.raz](https://github.com/salmans/rusty-razor/blob/master/theories/examples/golden-lion.raz), asking for a 31 | scenario (i.e., model of the theory) that justifies Joffrey's golden head: 32 | 33 | ``` 34 | razor solve -i theories/examples/golden-lion.raz 35 | ``` 36 | 37 | Razor cannot find a model for the previous theory, meaning the theory is inconsistent. Notice that this theory 38 | is satisfiable (i.e., has a model) in the absence of Ned's discovery (try running Razor after commenting out the last 39 | line). 40 | -------------------------------------------------------------------------------- /book/src/example/hold-the-door.md: -------------------------------------------------------------------------------- 1 | ## Hold the Door 2 | 3 | Wyllis was a young stable boy when he heard a voice from his future: "Hold the Door!" The voice transformed Wyllis to 4 | Hodor (Hold the door, Holdde door, Hoddedor, Hodor, Hodor...!), putting him on a life-long journey, leading him to the 5 | moment that he saves Bran's life. Indeed, because of this defining moment in his future, Wyllis became Hodor in his past. 6 | 7 | #### Linear Time 8 | The theory below describes Hodor's journey assuming that time progresses linearly 9 | [hodor-linear.raz](https://github.com/salmans/rusty-razor/blob/master/theories/examples/hodor-linear.raz) 10 | 11 | ``` 12 | // Wyllis hears "Hold the Door" (at time `t`), then he becomes Hodor in the next 13 | // point of time 14 | HoldTheDoor(t) -> Hodor(next(t)); 15 | 16 | // Hodor, after turning into Hodor at time "t", holds the Door at some time "tt" 17 | // in future ("tt > t") 18 | Hodor(t) -> ? tt . HoldTheDoor(tt) & After(t, tt); 19 | 20 | // These are the rules by which time progresses linearly: 21 | // (1) a point of time "t1" that is the next of "t0" (i.e., "next(t0)") is a point of 22 | // time after "t0" ("t1 > t0") 23 | next(t0) = t1 -> After(t0, t1); 24 | 25 | // (2) if a point of time "t1" is after "t0", it is either immediately 26 | // after "t0" (i.e., "next(t0)") or there exists some point of time "t2" 27 | // that is immediately after "t0" and before "t1". 28 | After(t0, t1) -> next(t0) = t1 | ? t2 . next(t0) = t2 & After(t2, t1); 29 | 30 | // And we know at some point of time (namely "'t_hodor"), Wyllis became Hodor 31 | Hodor('t_hodor); 32 | ``` 33 | 34 | An unbounded run of Razor on the previous theory will never terminate (feel free to press `ctrl + c` after a 35 | few seconds): 36 | 37 | ``` 38 | razor solve -i theories/examples/hodor-linear.raz 39 | ``` 40 | 41 | Assuming that time progresses linearly, the circular causality between the two events of "holding the door" and 42 | "becoming Hodor" results in an infinitely large model where time progresses unboundedly. We can restrict the size of 43 | the structures constructed by Razor by bounding the number of their elements. For example, if we restrict the number of 44 | elements to 4, Razor will find 9 *incomplete* structures, which do *not* satisfy the theory: 45 | 46 | ``` 47 | razor solve -i theories/examples/hodor-linear.raz --bound domain=4 48 | ``` 49 | 50 | For example, the following structure corresponds to an incomplete model where `e#0` denotes the starting point `t_hodor` 51 | and `e#1`, `e#2` and `e#4` are other points in time: 52 | 53 | ``` 54 | Domain: e#0, e#2, e#4, e#1 55 | 56 | Elements: 't_hodor -> e#0, sk#0[e#0] -> e#1, next[e#0], sk#1[e#0, e#1] -> e#2, 57 | next[e#1] -> e#4 58 | 59 | Facts: After(e#0, e#1), After(e#2, e#1), Hodor(e#0), Hodor(e#4), HoldTheDoor(e#1) 60 | ``` 61 | 62 | Now consider `e#1` and `e#2`. The incomplete model shows that `e#1` is after `e#2`, but neither `e#1` 63 | immediately follows `e#2` (no next point for `e#2`) nor there exists a point that is after `e#2` and 64 | before `e#1`, violating the second rule of linear time progression. In general, it may be possible to extend the 65 | incomplete structure to a model of the theory by adding more information to the model. Any model of this particular 66 | theory, however, is infinitely large. 67 | 68 | #### Time-Loop 69 | 70 | Next, we model time as a "big ball of wibbly wobbly timey wimey stuff!" To make it simple, let's assume that time-loops 71 | can only happen at the moment that Hodor heard a voice from the future, namely `'t_hodor`, changing our rules of 72 | time progression ([hodor-time-loop.raz](https://github.com/salmans/rusty-razor/blob/master/theories/examples/hodor-time-loop.raz)): 73 | 74 | ``` 75 | HoldTheDoor(t) -> Hodor(next(t)); 76 | 77 | Hodor(t) -> ? tt . HoldTheDoor(tt) & After(t, tt); 78 | 79 | next(t0) = t1 -> After(t0, t1); 80 | After(t0, t1) -> (next(t0) = t1) | ? t2 . next(t0) = t2 & After(t2, t1); 81 | 82 | // Hold the door moment only happens at 't_hodor 83 | HoldTheDoor(t) -> t = 't_hodor; 84 | 85 | Hodor('t_hodor); 86 | ``` 87 | 88 | In presence of time-loops, Razor can explain Hodor's curious journey: 89 | 90 | ``` 91 | razor solve -i theories/examples/hodor-time-loop.raz 92 | ``` 93 | 94 | This time, Razor produces infinitely many (finite) models with time-loops of different size. Use can use the `--count` 95 | option to limit the number of models and halt the process. 96 | -------------------------------------------------------------------------------- /book/src/example/valar-morghulis.md: -------------------------------------------------------------------------------- 1 | ## Valar Morghulis 2 | 3 | All men must die. 4 | Ser Gregor is a man. 5 | 6 | ``` 7 | // All men must die: 8 | forall x. (Man(x) implies MustDie(x)); 9 | 10 | // Ser Gregor is a man: 11 | Man('gregor); 12 | ``` 13 | 14 | Run Razor on the previous theory [valar-morghulis.raz](https://github.com/salmans/rusty-razor/blob/master/theories/examples/valar-morghulis.raz): 15 | 16 | ``` 17 | razor solve -i theories/examples/valar-morghulis.raz 18 | ``` 19 | 20 | Razor returns only one model: 21 | 22 | ``` 23 | Domain: e#0 24 | 25 | Elements: 'gregor -> e#0 26 | 27 | Facts: Man(e#0), MustDie(e#0) 28 | ``` 29 | 30 | The model contains only one element `e#0` in its domain. This element denotes `'gregor`, a constant in the theory that 31 | represents Ser Gregor. The model also contains two facts: `Man(e#0)` is a fact that is derived from the second statement 32 | of the theory (i.e., `Man('gregor)`). The fact `MustDie(e#0)` is deduced by Razor according to the first statement of 33 | the theory. 34 | 35 | > Notice that the previous model is a "minimal" model for the given theory. The element `e#0` is required to represent 36 | the constant `'gregor`; the fact `Man(e#0)` must be present because the theory says so; and, the fact `MustDie(e#0)` 37 | must be true because of the first statement. Removing any piece of information makes the given structure a non-model of 38 | the theory. -------------------------------------------------------------------------------- /book/src/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Rusty Razor is a tool for constructing finite models for first-order theories. The model-finding algorithm is inspired 4 | by [the chase](https://en.wikipedia.org/wiki/Chase_(algorithm)) for database systems. Given a first-order theory, 5 | Razor constructs a set of *homomorphically minimal* models. The constructed models are models of an equisatisfiable 6 | theory over an alphabet, extended with Skolem functions that represent the existential quantifiers of the original theory. 7 | 8 | To learn more about the theoretical foundation of Razor, check out my 9 | [PhD dissertation](https://digitalcommons.wpi.edu/etd-dissertations/458/). 10 | -------------------------------------------------------------------------------- /book/src/run.md: -------------------------------------------------------------------------------- 1 | # Run 2 | 3 | ## `solve` 4 | 5 | Use the `solve` command to find models for an input theory. The `-i` (short for `--input`) 6 | reads the input from a file: 7 | 8 | ``` 9 | razor solve -i 10 | ``` 11 | 12 | > Run `solve` without the `-i` option to read the input from the standard input. 13 | 14 | The `--count` parameter limits the number of models to construct: 15 | 16 | ``` 17 | razor solve -i --count 18 | ``` -------------------------------------------------------------------------------- /book/src/run/bounded.md: -------------------------------------------------------------------------------- 1 | ### Bounded Model-Finding 2 | 3 | Unlike conventional model-finders such as [Alloy](http://alloytools.org), Razor doesn't require the user to provide a 4 | bound on the size of the models that it constructs. However, Razor may never terminate when running on theories with 5 | non-finite models -- it can be shown that a run of Razor on an unsatisfiable theory (i.e., a theory with no models) 6 | is guaranteed to terminate (although it might take a very very long time).This is a direct consequence of 7 | semi-decidability of first-order logic. 8 | 9 | To guarantee termination, limit the size of the resulting models by the number of their elements using the `--bound` 10 | option with a value for the `domain` parameter: 11 | 12 | ``` 13 | razor solve -i --bound domain= 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /book/src/run/scheduler.md: -------------------------------------------------------------------------------- 1 | ### Model-Finding Scheduler 2 | 3 | Use the `--scheduler` option to choose how Razor processes search branches. The `fifo` scheduler (the default scheduler) 4 | schedules new branches last and is a more suitable option for processing theories with few small satisfying models. 5 | The `lifo` scheduler schedules new branches first, and is more suitable for processing theories with many large models. 6 | 7 | ``` 8 | razor solve -i --scheduler 9 | ``` -------------------------------------------------------------------------------- /book/src/syntax.md: -------------------------------------------------------------------------------- 1 | # Syntax 2 | 3 | The syntax and semantics of Razor's input is that of conventional 4 | [first-order logic](https://en.wikipedia.org/wiki/First-order_logic), 5 | also known as predicate logic. 6 | 7 | Razor's input supports three [syntactic variations](./syntax/variations.html) 8 | for the logical symbols and follows one of the more predominant conventions for 9 | the [precedence](./syntax/precedence.html) of the logical connectives. 10 | For consistency and readability purposes, we use the _alpha_ variation 11 | everywhere in this document. 12 | 13 | ## Identifier 14 | 15 | Lowercase and uppercase identifiers in Razor are defined by the following: 16 | 17 | * A _lowercase_ identifier is a word starting with either a lowercase alphabetic character (`[a-z]`) or 18 | the underscore (`_`), followed by any number of alphanumeric characters (`[a-zA-Z0-9]`) and/or the 19 | underscore. For example, `rusty`, `_razor`, and `rusty123_RAZOR456` are lowercase identifiers. 20 | * An _uppercase_ identifier is a word that starts with an uppercase alphabetic character (`[A-Z]`) and 21 | is followed by any number of alphanumeric characters (`[a-zA-Z0-9]`) and/or the underscore (`_`). 22 | For example, `Rusty`, and `RAZOR_123` are uppercase identifiers. 23 | 24 | ## Term 25 | 26 | A _term_ in Razor's input is a conventional first-order term, inductively defined by the following: 27 | 28 | * A __variable__ is a lowercase identifier, and it is a term on its own. For example, `v`, 29 | `variable`, and `_var` may be used as variable symbols. 30 | * A _composite_ term consists of a lowercase identifier as a __function__ symbol that is applied 31 | to zero or more terms as arguments that are wrapped in parentheses. For example, `f()`, `f(a)`, 32 | `f(g(a, b), c)`, and `func(plus(x, y))` are terms. 33 | 34 | Razor treats _nullary_ functions (of arity zero that take no arguments) 35 | as __constants__. An apostrophe (`'`) followed by a lowercase identifier is a syntactic sugar 36 | for constructing a constant. For example, `'a` is a constant that is syntactically 37 | equivalent to `a()`. 38 | 39 | ## Formula 40 | 41 | A _formula_ in Razor's input is a conventional first-order formula _with equality_, inductively 42 | defined by the following: 43 | 44 | * An __atomic__ formula consists of an upper case identifier as a __predicate__ symbol that is 45 | applied to zero or more terms as arguments that are wrapped in parentheses. For example, `R()`, 46 | `R(x)`, and `R(f(x, 'a), y, 'b)` are atomic formulae. 47 | 48 | * An __equality__ is a especial type of atomic formula, with a binary infix connective `=` that is 49 | applied on two terms as its arguments. Razor treats an equality as identity between its arguments. 50 | For example, `x = y`, and `f(x) = g(f(y), 'a)` are equalities. 51 | 52 | * The result of applying the logical connectives `and`, `or`, `implies` and `iff` as infix binary 53 | connectives to two fromulae is itself a formula. Parentheses may be used to override the conventional 54 | [precedence](./syntax/precedence.html) of the connectives. 55 | For example `P(x) and x = y`, `P(x) and (Q(y) or R(x))`, and `P(x, y) implies x = y and R(z)` 56 | are formulae. 57 | 58 | * The result of applying the logical connectives `forall` and `exists` to one or more comma (`,`) 59 | separated _bound_ variables and a formula, as a propositional function, is itself a formula. The 60 | list of bound variables and the propositional function are separated by a period (`.`). 61 | For example, `exists x. P(x)`, `forall x, y . Q(x, y) or R(z)`, and 62 | `forall x. exists y. P(x, y)` are formulae. 63 | 64 | > __Note:__ A variable that is not bound by a universal (`forall`) or existential (`exists`) quantifier 65 | is said to be a _free_ variable. Razor assumes all free variables to be universally 66 | quantified over the entire formula in which they appear. 67 | For example, `P(x) -> exists y . Q(x, y)` is assumed to be equivalent to 68 | `forall x . (P(x) -> exists y . Q(x, y))`. 69 | 70 | ## Input 71 | 72 | Razor's input is a first-order theory, consisting of a list of zero or more formulae, separated by 73 | the semi-colon (`;`). The input may contain conventional C-style comments (`//` for comment lines and 74 | `/*` and `*/` for comment blocks). Whitespaces including new lines are allowed. 75 | See the following input for an example: 76 | 77 | ``` 78 | // equality axioms 79 | 80 | forall x . x = x; /* Reflexitivity */ 81 | forall x, y . (x = y implies y = x); /* Symmetry */ 82 | forall x, y, z . (x = y and y = z implies x = z); /* Transitivity */ 83 | ``` -------------------------------------------------------------------------------- /book/src/syntax/grammar.md: -------------------------------------------------------------------------------- 1 | ## Grammar 2 | 3 | The input theory accepted by Razor is defined by the grammar below: 4 | 5 | ``` 6 | LOWER ::= [a-z_][a-zA-Z0-9_]* 7 | UPPER ::= [A-Z][a-zA-Z0-9_]* 8 | 9 | TRUE ::= "true" | "'|'" | "⊤" (U+22A4) 10 | FALSE ::= "false" | "_|_" | "⟘" (U+27D8) 11 | NOT ::= "not" | "~" | "¬" (U+00AC) 12 | AND ::= "and" | "&" | "∧" (U+2227) 13 | OR ::= "or" | "|" | "∨" (U+2228) 14 | IMPLIES ::= "implies" | "->" | "→" (U+2192) 15 | IFF ::= "iff" | "<=>" | "⇔" (U+21D4) 16 | EXISTS ::= "exists" | "?" | "∃" (U+2203) 17 | FORALL ::= "forall" | "!" | "∀" (U+2200) 18 | 19 | VARIABLE ::= LOWER 20 | FUNCTION ::= LOWER 21 | PREDICATE ::= UPPER 22 | VARIABLES ::= VARIABLE ("," VARIABLES)* 23 | TERM ::= VARIABLE | FUNCTION "(" TERMS? ")" 24 | TERMS ::= TERM ("," TERMS)* 25 | 26 | ATOM ::= TRUE | FALSE 27 | | TERM "=" TERM | PREDICATE "(" TERMS? ")" 28 | | "(" FORMULA ")" 29 | F_NOT ::= NOT F_QUANTIFIED | ATOM 30 | F_AND ::= F_NOT (AND F_QUANTIFIED)? 31 | F_OR ::= F_AND (OR F_QUANTIFIED)? 32 | F_QUANTIFIED ::= (EXISTS | FORALL) VARIABLES "." F_QUANTIFIED | F_OR 33 | FORMULA ::= F_QUANTIFIED ((IMPLIES | IFF) F_QUANTIFIED)* 34 | 35 | THEORY ::= (FORMULA ";")* 36 | ``` -------------------------------------------------------------------------------- /book/src/syntax/precedence.md: -------------------------------------------------------------------------------- 1 | ## Connective Precedence 2 | 3 | When a formula contains two or more logical connectives, the connectives 4 | are applied by the following order from the highest to the lowest precedence: 5 | 6 | * Negation (`not`) is applied first. 7 | * Conjunction (`and`) is applied next. 8 | * Disjunction (`or`) is applied next. 9 | * Implication (`implies`) and bi-implication (`iff`) are applied next. 10 | * Existential (`exists`) and universal (`forall`) quantifiers are applied last. 11 | 12 | A connective with a higher precedence is applied before a consecutive 13 | connective with a lower precedence; that is, the connective with the higher 14 | precedence binds tighter to the formula on which it operates. 15 | For example, `P() implies not Q() and R()` is a formula consisting of 16 | an implication where `P()` is the premise and the conjunction of `not Q()` 17 | and `R()` is the consequence. 18 | 19 | Parentheses may be used to override the precedence of connectives. 20 | For example, in `P() and (Q() or R())` the disjunction (`or`) is applied before 21 | the conjunction (`and`). 22 | 23 | ### Associativity 24 | 25 | All binary connectives of equal precedence except for implication 26 | (`implies`) and bi-implication (`iff`) are left-associative. 27 | For example, `P() | Q() | R()` is evaluated as `(P() | Q()) | R()`. 28 | 29 | Implication and bi-implication are right-associative. 30 | For example, `P() <=> Q() -> R() <=> S()` is evaluated as 31 | `P() <=> (Q() -> (R() <=> S()))`. -------------------------------------------------------------------------------- /book/src/syntax/syntax: -------------------------------------------------------------------------------- 1 | /Users/salmansaghafi/code/rusty-razor/book/src/syntax: 2 | total used in directory 24 available 2447352537 3 | drwxr-xr-x 5 salmansaghafi staff 160 Dec 29 16:57 . 4 | drwxr-xr-x 13 salmansaghafi staff 416 Dec 28 18:45 .. 5 | -rw-r--r-- 1 salmansaghafi staff 817 Dec 29 17:04 grammar.md 6 | -rw-r--r-- 1 salmansaghafi staff 983 Dec 29 16:57 precedence.md 7 | -rw-r--r-- 1 salmansaghafi staff 1629 Dec 28 18:19 variations.md 8 | -------------------------------------------------------------------------------- /book/src/syntax/variations.md: -------------------------------------------------------------------------------- 1 | ## Syntactic Variations 2 | 3 | Razor supports three syntactic variations of the logical symbols in the input: 4 | 5 | * __alpha__ where logical symbols are written as alphabetic words. 6 | * __compact__ where ASCII notations represent logical symbols. 7 | * __math__ where (Unicode) mathematical symbols are used. 8 | 9 | > __Note:__ Currently, Razor's parser accepts inputs that are comprised of any combination 10 | of the syntactic variations mentioned above. However, future releases of Razor may restrict 11 | the input to use only one of the variations above. 12 | 13 | The table below shows all syntactic variations of the logical symbols: 14 | 15 | | symbol | alpha | compact | math | 16 | | ------------------------ |:----------:| :-------------------------------:|:------------:| 17 | | _truth_ | `true` | '|' | `⊤` (U+22A4) | 18 | | _falsehood_ | `false` | _|_ | `⟘` (U+27D8) | 19 | | _negation_ | `not` | `~` | `¬` (U+00AC) | 20 | | _conjunction_ | `and` | `&` | `∧` (U+2227) | 21 | | _disjunction_ | `or` | | | `∨` (U+2228) | 22 | | _implication_ | `implies` | `->` | `→` (U+2192) | 23 | | _bi-implication_ | `iff` | `<=>` | `⇔` (U+21D4) | 24 | | _existential quantifier_ | `exists` | `?` | `∃` (U+2203) | 25 | | _universal quantifier_ | `forall` | `!` | `∀` (U+2200) | -------------------------------------------------------------------------------- /razor-chase/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "razor-chase" 3 | version = "0.1.0" 4 | authors = ["Salman Saghafi "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "razor-chase implements a variation of the chase algorithm to find models for theories in geometric form." 8 | documentation = "https://salmans.github.io/rusty-razor/intro.html" 9 | homepage = "https://github.com/salmans/rusty-razor" 10 | repository = "https://github.com/salmans/rusty-razor" 11 | keywords = ["razor", "chase", "geometric", "logic", "model-finder"] 12 | categories = ["mathematics"] 13 | 14 | [dependencies] 15 | itertools = "0.7" 16 | criterion = "0.1" 17 | tracing = "0.1" 18 | rand = "0.6.5" 19 | serde = "1.0.93" 20 | serde_json = "1.0.39" 21 | serde_derive = "1.0.93" 22 | either = "1.6.1" 23 | thiserror = "^1.0" 24 | razor-fol = { path = "../razor-fol", version = "0.1.0" } 25 | codd = { version = "^0.1" } 26 | petgraph = "^0.5" 27 | 28 | [dev-dependencies] 29 | codd = { version = "^0.1", features = ["unstable"] } 30 | 31 | [lib] 32 | name = "razor_chase" 33 | path = "src/lib.rs" 34 | 35 | [[bench]] 36 | name = "perf_test" 37 | harness = false -------------------------------------------------------------------------------- /razor-chase/benches/perf_test.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 2 | use itertools::Itertools; 3 | use razor_chase::chase::{ 4 | bounder::DomainSize, 5 | chase_all, 6 | r#impl::*, 7 | scheduler::FIFO, 8 | strategy::{Bootstrap, Fair}, 9 | PreProcessor, Scheduler, Strategy, 10 | }; 11 | use razor_fol::syntax::Theory; 12 | use std::{fs, io::Read}; 13 | 14 | macro_rules! read_theory { 15 | ($file_name: expr) => {{ 16 | let mut f = fs::File::open($file_name).expect("file not found"); 17 | let mut contents = String::new(); 18 | f.read_to_string(&mut contents) 19 | .expect("something went wrong reading the file"); 20 | 21 | contents.parse::().unwrap() 22 | }}; 23 | } 24 | 25 | macro_rules! run_unbounded { 26 | ( 27 | $name: literal, 28 | pre_processor = $prep: expr, 29 | evaluator = $eval: path, 30 | sequent = $seq: path, 31 | model = $model: path, 32 | $crit: ident 33 | ) => {{ 34 | fn solve(theory: &Theory) -> Vec<$model> { 35 | let (sequents, init_model) = $prep.pre_process(theory); 36 | let evaluator = $eval; 37 | let selector: Bootstrap<$seq, Fair<$seq>> = Bootstrap::new(sequents.iter()); 38 | let mut strategy = FIFO::new(); 39 | let bounder: Option<&DomainSize> = None; 40 | strategy.add(init_model, selector); 41 | chase_all(&mut strategy, &evaluator, bounder) 42 | } 43 | 44 | fn run_bench(b: &mut Bencher) { 45 | let theories = &fs::read_dir("../theories/core") 46 | .unwrap() 47 | .map(|item| read_theory!(item.unwrap().path().display().to_string()).gnf()) 48 | .collect_vec(); 49 | 50 | b.iter(|| { 51 | for theory in theories { 52 | solve(&theory); 53 | } 54 | }) 55 | } 56 | $crit.bench_function(stringify!($name), run_bench); 57 | }}; 58 | } 59 | 60 | fn bench_batch(c: &mut Criterion) { 61 | run_unbounded!( 62 | "batch", 63 | pre_processor = collapse::ColPreProcessor, 64 | evaluator = batch::BatchEvaluator, 65 | sequent = collapse::ColSequent, 66 | model = collapse::ColModel, 67 | c 68 | ); 69 | } 70 | 71 | fn bench_relational(c: &mut Criterion) { 72 | run_unbounded!( 73 | "relational", 74 | pre_processor = relational::RelPreProcessor::new(false), 75 | evaluator = relational::RelEvaluator, 76 | sequent = relational::RelSequent, 77 | model = relational::RelModel, 78 | c 79 | ); 80 | } 81 | 82 | fn bench_relational_memoized(c: &mut Criterion) { 83 | run_unbounded!( 84 | "relational_memoized", 85 | pre_processor = relational::RelPreProcessor::new(true), 86 | evaluator = relational::RelEvaluator, 87 | sequent = relational::RelSequent, 88 | model = relational::RelModel, 89 | c 90 | ); 91 | } 92 | 93 | criterion_group!( 94 | benches, 95 | bench_batch, 96 | bench_relational, 97 | bench_relational_memoized 98 | ); 99 | criterion_main!(benches); 100 | -------------------------------------------------------------------------------- /razor-chase/src/chase/bounder.rs: -------------------------------------------------------------------------------- 1 | //! Implements various bounders for bounding the size of models, generated by the chase. 2 | //! 3 | //! The bounders are instances of [`Bounder`]. 4 | //! 5 | //! [`Bounder`]: crate::chase::Bounder 6 | use crate::chase::{Bounder, Model, Observation, WitnessTerm}; 7 | 8 | /// Bounds the size of a [model] by the number of elements in its [domain]. 9 | /// 10 | /// [model]: crate::chase::Model 11 | /// [domain]: crate::chase::Model::domain() 12 | pub struct DomainSize { 13 | /// Is the maximum size of the [domain] of elements for models accepted by this bounder. 14 | /// 15 | /// [domain]: crate::chase::Model::domain() 16 | max_domain_size: usize, 17 | } 18 | 19 | impl From for DomainSize { 20 | fn from(size: usize) -> Self { 21 | Self { 22 | max_domain_size: size, 23 | } 24 | } 25 | } 26 | 27 | impl Bounder for DomainSize { 28 | fn bound(&self, model: &M, observation: &Observation) -> bool { 29 | match observation { 30 | Observation::Fact { relation: _, terms } => { 31 | let model_size = model.domain().len(); 32 | let terms: Vec::TermType as WitnessTerm>::ElementType>> = 33 | terms 34 | .iter() 35 | .map(|t| model.element(t)) 36 | .filter(|t| t.is_none()) 37 | .collect(); 38 | let size = terms.len(); 39 | model_size + size > self.max_domain_size 40 | } 41 | Observation::Identity { left, right } => { 42 | let mut size = model.domain().len(); 43 | if model.element(left).is_none() { 44 | size += 1; 45 | } 46 | if model.element(right).is_none() { 47 | size += 1; 48 | } 49 | size > self.max_domain_size 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl.rs: -------------------------------------------------------------------------------- 1 | //! Contains all implementations of models, sequents and evaluators. 2 | //! 3 | //! The module implements different versions of the chase and various instances of 4 | //! [`Model`], [`Sequent`] and [`Evaluator`] 5 | //! 6 | //! [`Model`]: crate::chase::Model 7 | //! [`Sequent`]: crate::chase::Sequent 8 | //! [`Evaluator`]: crate::chase::Evaluator 9 | pub mod basic; 10 | pub mod batch; 11 | pub mod collapse; 12 | pub mod relational; 13 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/batch.rs: -------------------------------------------------------------------------------- 1 | //! Improves `collapse` by evaluating the sequent provided by the strategy 2 | //! against all assignments from the free variables of the sequent to the elements of the 3 | //! model in which it is being evaluated. 4 | //! 5 | //! [`ColEvaluator`] extends the [`Model`] that it processes in a [chase-step] 6 | //! only for one assignment from the free variables of the [`Sequent`] that it is 7 | //! evaluating to the elements of the [`Model`]. [`BatchEvaluator`] is the only 8 | //! different component between [`collapse`] and [`batch`] implementations. 9 | //! 10 | //! [`collapse`]: crate::chase::impl::collapse 11 | //! [`Sequent`]: crate::chase::impl::basic::BasicSequent 12 | //! [chase-step]: crate::chase#chase-step 13 | //! [`Model`]: crate::chase::impl::collapse::ColModel 14 | //! [`ColEvaluator`]: crate::chase::impl::collapse::ColEvaluator 15 | //! [`batch`]: self 16 | 17 | use crate::chase::{ 18 | r#impl::{ 19 | basic, collapse, 20 | collapse::{ColWitnessTerm, Element}, 21 | }, 22 | Bounder, EvaluateResult, Evaluator, Observation, Rel, Strategy, 23 | }; 24 | use either::Either; 25 | use itertools::Itertools; 26 | use razor_fol::syntax::{formula::Atomic, Var}; 27 | use std::{collections::HashMap, iter}; 28 | 29 | /// Simple evaluator that evaluates a Sequnet in a Model. 30 | pub struct BatchEvaluator; 31 | 32 | impl<'s, Stg: Strategy<&'s BatchSequent>, B: Bounder> Evaluator<'s, Stg, B> for BatchEvaluator { 33 | type Sequent = BatchSequent; 34 | type Model = BatchModel; 35 | 36 | fn evaluate( 37 | &self, 38 | initial_model: &BatchModel, 39 | strategy: &mut Stg, 40 | bounder: Option<&B>, 41 | ) -> Option> { 42 | let mut result = EvaluateResult::new(); 43 | 44 | let domain: Vec<&Element> = initial_model.domain_ref(); 45 | let domain_size = domain.len(); 46 | for sequent in strategy { 47 | let vars = &sequent.free_vars; 48 | let vars_size = vars.len(); 49 | if domain_size == 0 && vars_size > 0 { 50 | continue; // empty models can only be extended with sequents with no free variables. 51 | } 52 | 53 | // maintain a list of indices into the model elements to which variables are mapped 54 | let mut assignment: Vec = iter::repeat(0).take(vars_size).collect(); 55 | 56 | // try all the variable assignments to the elements of the model 57 | // (notice the do-while pattern) 58 | while { 59 | // construct a map from variables to elements 60 | let mut assignment_map: HashMap<&Var, Element> = HashMap::new(); 61 | for (i, item) in assignment.iter().enumerate() { 62 | assignment_map 63 | .insert(vars.get(i).unwrap(), (*domain.get(*item).unwrap()).clone()); 64 | } 65 | // construct a "characteristic function" for the assignment map 66 | let assignment_func = |v: &Var| assignment_map.get(v).unwrap().clone(); 67 | 68 | // lift the variable assignments to literals (used to make observations) 69 | let observe_literal = make_observe_literal(assignment_func); 70 | 71 | // build body and head observations 72 | let body = sequent.body.iter().map(&observe_literal).collect_vec(); 73 | let head = sequent 74 | .head 75 | .iter() 76 | .map(|l| l.iter().map(&observe_literal).collect_vec()) 77 | .collect_vec(); 78 | 79 | // if all body observations are true in the model but not all the head observations 80 | // are true, extend the model: 81 | if body.iter().all(|o| initial_model.is_observed(o)) 82 | && !head 83 | .iter() 84 | .any(|os| os.iter().all(|o| initial_model.is_observed(o))) 85 | { 86 | if head.is_empty() { 87 | return None; // the chase fails if the head is empty (FALSE) 88 | } else { 89 | if result.open_models.is_empty() { 90 | result.open_models.push(initial_model.clone()); 91 | } 92 | 93 | // extending all extensions of this model with the new observations: 94 | let models = result.open_models.into_iter().flat_map::, _>(|m| { 95 | if let Some(bounder) = bounder { 96 | let extend = make_bounded_extend(bounder, &m); 97 | head.iter().map(extend).collect() 98 | } else { 99 | let extend = make_extend(&m); 100 | head.iter().map(extend).collect() 101 | } 102 | }); 103 | 104 | // all previous extensions are now extended further. so remove them from 105 | // the result: 106 | result.open_models = vec![]; 107 | models.for_each(|m| result.append(m)); 108 | } 109 | } 110 | 111 | // try the next variable assignment 112 | domain_size > 0 && next_assignment(&mut assignment, domain_size - 1) 113 | } {} 114 | } 115 | 116 | Some(result) 117 | } 118 | } 119 | 120 | // Returns a closure that returns a cloned extension of the given model, extended by a given set of 121 | // observations. 122 | fn make_extend<'m>( 123 | model: &'m BatchModel, 124 | ) -> impl FnMut(&'m Vec>) -> Either { 125 | move |os: &'m Vec>| { 126 | let mut model = model.clone(); 127 | os.iter().foreach(|o| model.observe(o)); 128 | Either::Left(model) 129 | } 130 | } 131 | 132 | // Returns a closure that returns a cloned extension of the given model, extended by a given set of 133 | // observations. Unlike `make_extend`, `make_bounded_extend` extends the model with respect to a 134 | // bounder: a model wrapped in `Either::Right` has not reached the bounds while a model wrapped in 135 | // `Either::Left` has reached the bounds provided by `bounder`. 136 | fn make_bounded_extend<'m, B: Bounder>( 137 | bounder: &'m B, 138 | model: &'m BatchModel, 139 | ) -> impl FnMut(&'m Vec>) -> Either { 140 | move |os: &Vec>| { 141 | let mut model = model.clone(); 142 | let mut modified = false; 143 | os.iter().foreach(|o| { 144 | if bounder.bound(&model, o) { 145 | if !model.is_observed(o) { 146 | modified = true; 147 | } 148 | } else if !model.is_observed(o) { 149 | model.observe(o); 150 | } 151 | }); 152 | if modified { 153 | Either::Right(model) 154 | } else { 155 | Either::Left(model) 156 | } 157 | } 158 | } 159 | 160 | // Given an function from variables to elements of a model, returns a closure that lift the variable 161 | // assignments to literals of a sequent, returning observations. 162 | fn make_observe_literal( 163 | assignment_func: impl Fn(&Var) -> Element, 164 | ) -> impl Fn(&Literal) -> Observation { 165 | move |lit: &Literal| match lit { 166 | Atomic::Atom(this) => { 167 | let terms = this 168 | .terms 169 | .iter() 170 | .map(|t| ColWitnessTerm::witness(t, &assignment_func)) 171 | .collect(); 172 | Observation::Fact { 173 | relation: Rel::from(this.predicate.name()), 174 | terms, 175 | } 176 | } 177 | Atomic::Equals(this) => { 178 | let left = ColWitnessTerm::witness(&this.left, &assignment_func); 179 | let right = ColWitnessTerm::witness(&this.right, &assignment_func); 180 | Observation::Identity { left, right } 181 | } 182 | } 183 | } 184 | 185 | // Implements a counter to enumerate all the possible assignments of a domain of elements to free 186 | // variables of a sequent. It mutates the given a list of indices, corresponding to mapping of each 187 | // position to an element of a domain to the next assignment. Returns true if a next assignment 188 | // exists and false otherwise. 189 | fn next_assignment(vec: &mut Vec, last: usize) -> bool { 190 | for item in vec.iter_mut() { 191 | if *item != last { 192 | *item += 1; 193 | return true; 194 | } else { 195 | *item = 0; 196 | } 197 | } 198 | false 199 | } 200 | 201 | pub type BatchSequent = basic::BasicSequent; 202 | pub type BatchPreProcessor = collapse::ColPreProcessor; 203 | pub type Literal = basic::Literal; 204 | pub type BatchModel = collapse::ColModel; 205 | 206 | #[cfg(test)] 207 | mod test_batch { 208 | use super::{next_assignment, BatchEvaluator}; 209 | use crate::chase::r#impl::collapse::ColModel; 210 | use crate::chase::{ 211 | bounder::DomainSize, chase_all, scheduler::FIFO, strategy::Linear, Scheduler, 212 | }; 213 | use crate::test_prelude::*; 214 | use razor_fol::syntax::{Theory, FOF}; 215 | use std::collections::HashSet; 216 | 217 | static IGNORE_TEST: [&'static str; 1] = ["thy24.raz"]; 218 | 219 | #[test] 220 | fn test_next_assignment() { 221 | { 222 | let mut assignment = vec![]; 223 | assert_eq!(false, next_assignment(&mut assignment, 1)); 224 | assert!(assignment.is_empty()); 225 | } 226 | { 227 | let mut assignment = vec![0]; 228 | assert_eq!(true, next_assignment(&mut assignment, 1)); 229 | assert_eq!(vec![1], assignment); 230 | } 231 | { 232 | let mut assignment = vec![1]; 233 | assert_eq!(false, next_assignment(&mut assignment, 1)); 234 | assert_eq!(vec![0], assignment); 235 | } 236 | { 237 | let mut assignment = vec![0, 1]; 238 | assert_eq!(true, next_assignment(&mut assignment, 1)); 239 | assert_eq!(vec![1, 1], assignment); 240 | } 241 | { 242 | let mut assignment = vec![1, 1]; 243 | assert_eq!(true, next_assignment(&mut assignment, 2)); 244 | assert_eq!(vec![2, 1], assignment); 245 | } 246 | { 247 | let mut assignment = vec![2, 1]; 248 | assert_eq!(true, next_assignment(&mut assignment, 2)); 249 | assert_eq!(vec![0, 2], assignment); 250 | } 251 | { 252 | let mut assignment = vec![2, 2]; 253 | assert_eq!(false, next_assignment(&mut assignment, 2)); 254 | assert_eq!(vec![0, 0], assignment); 255 | } 256 | { 257 | let mut counter = 1; 258 | let mut vec = vec![0, 0, 0, 0, 0]; 259 | while next_assignment(&mut vec, 4) { 260 | counter += 1; 261 | } 262 | assert_eq!(3125, counter); 263 | } 264 | } 265 | 266 | fn run(theory: &Theory) -> Vec { 267 | use crate::chase::r#impl::collapse::ColPreProcessor; 268 | use crate::chase::PreProcessor; 269 | 270 | let pre_processor = ColPreProcessor; 271 | let (sequents, init_model) = pre_processor.pre_process(theory); 272 | 273 | let evaluator = BatchEvaluator; 274 | let strategy: Linear<_> = sequents.iter().collect(); 275 | let mut scheduler = FIFO::new(); 276 | let bounder: Option<&DomainSize> = None; 277 | scheduler.add(init_model, strategy); 278 | chase_all(&mut scheduler, &evaluator, bounder) 279 | } 280 | 281 | #[test] 282 | fn test() { 283 | std::fs::read_dir("../theories/core") 284 | .unwrap() 285 | .map(|item| item.unwrap().path()) 286 | .filter(|path| path.ends_with(".raz")) 287 | .filter(|path| { 288 | IGNORE_TEST.contains(&path.file_name().and_then(|f| f.to_str()).unwrap()) 289 | }) 290 | .for_each(|path| { 291 | let theory = read_theory_from_file(path.to_str().unwrap()); 292 | let expected: HashSet = 293 | read_file(path.with_extension("model").to_str().unwrap()) 294 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 295 | .filter(|s| !s.is_empty()) 296 | .map(Into::into) 297 | .collect(); 298 | let result: HashSet<_> = run(&theory) 299 | .into_iter() 300 | .map(|m| print_collapse_model(m)) 301 | .collect(); 302 | assert_eq!( 303 | expected, 304 | result, 305 | "invalid result for theory {}", 306 | path.with_extension("").to_str().unwrap() 307 | ); 308 | }); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational.rs: -------------------------------------------------------------------------------- 1 | //! Provides an implementation of the chase based on relational algebra. 2 | mod attribute; 3 | mod constants; 4 | mod evaluator; 5 | mod expression; 6 | mod model; 7 | mod pre_processor; 8 | mod rewrite; 9 | mod sequent; 10 | mod symbol; 11 | 12 | pub use evaluator::RelEvaluator; 13 | pub use model::RelModel; 14 | pub use pre_processor::RelPreProcessor; 15 | pub use sequent::RelSequent; 16 | 17 | use razor_fol::syntax::{formula::Atom, term::Variable}; 18 | use std::collections::HashMap; 19 | use thiserror::Error; 20 | 21 | /// Is the type of unnamed tuples used in database instances. 22 | type Tuple = Vec; 23 | 24 | /// Is a named tuple where every element is identified by an attribute. 25 | type NamedTuple<'a> = HashMap<&'a attribute::Attribute, crate::chase::E>; 26 | 27 | /// Returns an empty named tuple. 28 | fn empty_named_tuple<'a>() -> NamedTuple<'a> { 29 | HashMap::new() 30 | } 31 | 32 | /// Is the type of errors arising from inconsistencies in the syntax of formulae. 33 | #[derive(Error, Debug)] 34 | pub enum Error { 35 | /// Is returned when an unsupported operation is performed on an expression. 36 | #[error("missing symbol `{symbol:?}`")] 37 | MissingSymbol { symbol: String }, 38 | 39 | /// Is a wrapper around the underlying database error. 40 | #[error("database error")] 41 | DatabaseError { 42 | #[from] 43 | source: codd::Error, 44 | }, 45 | 46 | /// Is returned when a witness term cannot be constructed. 47 | #[error("cannot create witness term for symbol `{symbol:?}`")] 48 | BadWitnessTerm { symbol: String }, 49 | 50 | /// Is returned when a relational attribute cannot be constructed. 51 | #[error("invalid attribute name `{name:?}`")] 52 | BadAttributeName { name: String }, 53 | 54 | /// Is returned when a relationalized atom is of a wrong arity. 55 | #[error("invalid relational function application `{atom:?}`")] 56 | BadRelationalAtom { atom: Atom }, 57 | 58 | /// Is returned when an existential variable is not the output 59 | /// of one of the previous functional literals in the head of a sequent. 60 | #[error("existential variable with no origin `{var:?}`")] 61 | BadExistentialVariable { var: Variable }, 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use super::*; 67 | use crate::{ 68 | chase::{ 69 | bounder::DomainSize, 70 | chase_all, 71 | scheduler::FIFO, 72 | strategy::{Bootstrap, Fair}, 73 | PreProcessor, Scheduler, 74 | }, 75 | test_prelude::*, 76 | }; 77 | use razor_fol::syntax::{Theory, FOF}; 78 | 79 | fn run(theory: &Theory, pre_processor: &RelPreProcessor) -> Vec { 80 | let (sequents, init_model) = pre_processor.pre_process(theory); 81 | 82 | let evaluator = RelEvaluator; 83 | let strategy: Bootstrap<_, Fair<_>> = sequents.iter().collect(); 84 | let mut scheduler = FIFO::new(); 85 | let bounder: Option<&DomainSize> = None; 86 | scheduler.add(init_model, strategy); 87 | chase_all(&mut scheduler, &evaluator, bounder) 88 | } 89 | 90 | #[test] 91 | fn test() { 92 | std::fs::read_dir("../theories/core") 93 | .unwrap() 94 | .map(|item| item.unwrap().path()) 95 | .filter(|path| path.ends_with(".raz")) 96 | .for_each(|path| { 97 | let theory = read_theory_from_file(path.to_str().unwrap()); 98 | let expected_len = read_file(path.with_extension("model").to_str().unwrap()) 99 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 100 | .filter(|s| !s.is_empty()) 101 | .map(|_| ()) 102 | .collect::>() 103 | .len(); 104 | let result_len = run(&theory, &RelPreProcessor::new(false)).len(); 105 | assert_eq!( 106 | expected_len, 107 | result_len, 108 | "invalid result for theory {}", 109 | path.with_extension("").to_str().unwrap() 110 | ); 111 | }); 112 | } 113 | 114 | #[test] 115 | fn test_materialized() { 116 | std::fs::read_dir("../theories/core") 117 | .unwrap() 118 | .map(|item| item.unwrap().path()) 119 | .filter(|path| path.ends_with(".raz")) 120 | .for_each(|path| { 121 | let theory = read_theory_from_file(path.to_str().unwrap()); 122 | let expected = run(&theory, &RelPreProcessor::new(false)) 123 | .into_iter() 124 | .map(|m| format!("{:?}", m)) 125 | .collect::>(); 126 | let result = run(&theory, &RelPreProcessor::new(true)) 127 | .into_iter() 128 | .map(|m| format!("{:?}", m)) 129 | .collect::>(); 130 | assert_eq!( 131 | expected, 132 | result, 133 | "invalid result for theory {}", 134 | path.with_extension("").to_str().unwrap() 135 | ); 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/attribute.rs: -------------------------------------------------------------------------------- 1 | use super::{constants::*, Error}; 2 | use itertools::Itertools; 3 | use razor_fol::{syntax, transform::Relational}; 4 | use std::convert::TryFrom; 5 | use std::ops::Deref; 6 | use std::str::FromStr; 7 | use Variant::*; 8 | 9 | /// Is the variants of attributes. 10 | #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 11 | pub(super) enum Variant { 12 | /// Universal attribute from a range restricted universally quantified variable. 13 | Universal, 14 | 15 | /// Existential attribute from an existential variable. 16 | Existential, 17 | 18 | /// Equational attribute, introduced by expanding equations. 19 | Equational, 20 | } 21 | 22 | /// In the context of a relational expression, a variable is refered to as an `Attribute`. 23 | /// 24 | /// **Note**: 25 | /// More accurately, an attribute is identified as a position on a relational formula. 26 | /// However, because we work with formulae where every position is occupied by a unique 27 | /// variable, attributes can be identified by variables. 28 | #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 29 | pub(super) struct Attribute { 30 | /// Is the attribute's name. 31 | name: String, 32 | 33 | /// Is the variation of the attribute. 34 | variant: Variant, 35 | } 36 | 37 | impl Attribute { 38 | /// Returns `true` if the attribute is universal. 39 | #[inline(always)] 40 | pub fn is_universal(&self) -> bool { 41 | matches!(self.variant, Universal) 42 | } 43 | 44 | /// Returns `true` if the attribute is existential. 45 | #[inline(always)] 46 | pub fn is_existential(&self) -> bool { 47 | matches!(self.variant, Existential) 48 | } 49 | } 50 | 51 | impl TryFrom<&syntax::Var> for Attribute { 52 | type Error = Error; 53 | 54 | fn try_from(value: &syntax::Var) -> Result { 55 | value.name().parse() 56 | } 57 | } 58 | 59 | impl From<&Attribute> for syntax::Var { 60 | fn from(attr: &Attribute) -> Self { 61 | let name = &attr.name; 62 | syntax::Var::from(name) 63 | } 64 | } 65 | 66 | impl FromStr for Attribute { 67 | type Err = Error; 68 | 69 | fn from_str(s: &str) -> Result { 70 | let variant = if s.starts_with(EXISTENTIAL_PREFIX) { 71 | Some(Existential) 72 | } else if s.starts_with(EQUATIONAL_PREFIX) { 73 | if s.contains(SEPERATOR) { 74 | Some(Equational) 75 | } else { 76 | None 77 | } 78 | } else { 79 | Some(Universal) 80 | }; 81 | 82 | let name = s.into(); 83 | if let Some(variant) = variant { 84 | Ok(Self { name, variant }) 85 | } else { 86 | Err(Error::BadAttributeName { name }) 87 | } 88 | } 89 | } 90 | 91 | /// Represents the list of attributes of a relational expression. 92 | #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 93 | pub(super) struct AttributeList(Vec); 94 | 95 | impl AttributeList { 96 | /// Creates a new instance from an iterator of [`Attribute`]s. 97 | pub fn new>(attributes: I) -> Self { 98 | Self(attributes.into_iter().map(Into::into).collect()) 99 | } 100 | 101 | /// Creates an empty list of attributes. 102 | pub fn empty() -> Self { 103 | Self::new(vec![]) 104 | } 105 | 106 | /// Returns the set union of the attributes in `self` and those in `other`. 107 | pub fn union(&self, other: &[Attribute]) -> AttributeList { 108 | Self::new( 109 | self.iter() 110 | .chain(other.iter().filter(|v| !self.contains(v))) 111 | .cloned() 112 | .collect_vec(), 113 | ) 114 | } 115 | 116 | /// Returns the attributes that are present in both `self` and `other`. 117 | pub fn intersect(&self, other: &[Attribute]) -> AttributeList { 118 | Self::new( 119 | self.iter() 120 | .filter(|v| other.contains(v)) 121 | .cloned() 122 | .collect_vec(), 123 | ) 124 | } 125 | 126 | /// Returns a new `AttributeList` containing the universal attributes in `self`. 127 | pub fn universals(&self) -> AttributeList { 128 | Self::new( 129 | self.attributes() 130 | .iter() 131 | .filter(|a| a.is_universal()) 132 | .cloned(), 133 | ) 134 | } 135 | 136 | /// Returns the list of attributes wrapped inside `self`. 137 | pub fn attributes(&self) -> &[Attribute] { 138 | &self.0 139 | } 140 | } 141 | 142 | impl Deref for AttributeList { 143 | type Target = [Attribute]; 144 | 145 | fn deref(&self) -> &Self::Target { 146 | &self.0 147 | } 148 | } 149 | 150 | impl TryFrom<&Relational> for AttributeList { 151 | type Error = Error; 152 | 153 | fn try_from(value: &Relational) -> Result { 154 | use razor_fol::syntax::Formula; 155 | 156 | let attributes = value 157 | .free_vars() 158 | .into_iter() 159 | .map(Attribute::try_from) 160 | .collect::, Error>>(); 161 | 162 | attributes.map(Self) 163 | } 164 | } 165 | 166 | impl From<&AttributeList> for Vec { 167 | fn from(attrs: &AttributeList) -> Self { 168 | attrs.iter().map(syntax::Var::from).collect() 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/constants.rs: -------------------------------------------------------------------------------- 1 | use razor_fol::syntax; 2 | 3 | // The naming prefix of existential attributes and variables in relational formulae. 4 | pub(super) const EXISTENTIAL_PREFIX: &str = "?"; 5 | 6 | // The naming prefix of equational attributes and variables in relational formulae. 7 | pub(super) const EQUATIONAL_PREFIX: &str = "~"; 8 | 9 | // The naming prefix of constant predicates created by relationalization 10 | pub(super) const CONSTANT_PREDICATE_PREFIX: &str = "@"; 11 | 12 | // The naming prefix of functional predicates created by relationalization 13 | pub(super) const FUNCTIONAL_PREDICATE_PREFIX: &str = "$"; 14 | 15 | // Seperates the different parts of attribute and variable names. 16 | pub(super) const SEPERATOR: &str = ":"; 17 | 18 | // Is the name of the database instance that stores the domain of elements. 19 | pub(super) const DOMAIN: &str = "$$domain"; 20 | 21 | // Is the name of the database instance for the equality relation. 22 | pub(super) const EQUALITY: &str = razor_fol::syntax::EQ_SYM; 23 | 24 | // Is the prefix associated to constants generated by Skolemization. 25 | pub(super) const SKOLEM_CONST_PREFIX: &str = "c#"; 26 | 27 | // Is the prefix associated to functions generated by Skolemization. 28 | pub(super) const SKOLEM_FN_PREFIX: &str = "f#"; 29 | 30 | // Creates the database instance name for the given constant. 31 | #[inline] 32 | pub(super) fn constant_instance_name(c: &syntax::Const) -> String { 33 | format!("@{}", c.name()) 34 | } 35 | 36 | // Creates the database instance name for the given function symbol. 37 | #[inline] 38 | pub(super) fn function_instance_name(f: &syntax::Func) -> String { 39 | format!("${}", f.name()) 40 | } 41 | 42 | // Creates the database instance name for the given predicate. 43 | #[inline] 44 | pub(super) fn predicate_instance_name(p: &syntax::Pred) -> String { 45 | p.to_string() 46 | } 47 | 48 | // Creates names for constants introduced by Skolemization. 49 | #[inline] 50 | pub(super) fn skolem_constant_name(index: u32) -> String { 51 | format!("{}{}", SKOLEM_CONST_PREFIX, index) 52 | } 53 | 54 | // Creates names for functions introduced by Skolemization. 55 | #[inline] 56 | pub(super) fn skolem_function_name(index: u32) -> String { 57 | format!("{}{}", SKOLEM_FN_PREFIX, index) 58 | } 59 | 60 | // Creates names for existential variables. 61 | #[inline] 62 | pub(super) fn existential_variable_name(index: u32) -> String { 63 | format!("{}{}", EXISTENTIAL_PREFIX, index) 64 | } 65 | 66 | // Creates names for existential variables used for linearization. 67 | #[inline] 68 | pub(super) fn linear_variable_name(name: &str, index: u32) -> String { 69 | format!("{}{}{}{}", EQUATIONAL_PREFIX, name, SEPERATOR, index) 70 | } 71 | 72 | // Returns true if a flat (variable) term corresponds to an existential quantifier or the 73 | // result of a function application. 74 | pub(super) fn is_existential_variable(name: &str) -> bool { 75 | name.starts_with(EXISTENTIAL_PREFIX) 76 | } 77 | 78 | // Returns true if the relational predicate corresponds to a function (or a constant) 79 | pub(super) fn is_function_predicate(name: &str) -> bool { 80 | name.starts_with(CONSTANT_PREDICATE_PREFIX) || name.starts_with(FUNCTIONAL_PREDICATE_PREFIX) 81 | } 82 | 83 | // Returns true if the relational predicate corresponds to a function (or a constant) 84 | #[allow(dead_code)] 85 | pub(super) fn is_skolem_predicate(name: &str) -> bool { 86 | assert!(!name.is_empty()); 87 | let postfix = &name[1..]; 88 | is_function_predicate(name) 89 | && (postfix.starts_with(SKOLEM_CONST_PREFIX) || postfix.starts_with(SKOLEM_FN_PREFIX)) 90 | } 91 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/evaluator.rs: -------------------------------------------------------------------------------- 1 | use crate::chase::{Bounder, EvaluateResult, Evaluator, Strategy}; 2 | 3 | use super::{ 4 | empty_named_tuple, 5 | model::RelModel, 6 | sequent::{Branch, RelSequent, SymbolicAtom}, 7 | symbol::Symbol, 8 | Error, NamedTuple, Tuple, 9 | }; 10 | use std::collections::HashMap; 11 | 12 | pub struct RelEvaluator; 13 | 14 | impl<'s, Stg: Strategy<&'s RelSequent>, B: Bounder> Evaluator<'s, Stg, B> for RelEvaluator { 15 | type Sequent = RelSequent; 16 | type Model = RelModel; 17 | 18 | fn evaluate( 19 | &self, 20 | model: &Self::Model, 21 | strategy: &mut Stg, 22 | bounder: Option<&B>, 23 | ) -> Option> { 24 | let next_sequent = next_sequent(strategy, model); 25 | if next_sequent.is_none() { 26 | return Some(EvaluateResult::new()); 27 | } 28 | let (sequent, tuples) = next_sequent.unwrap(); 29 | 30 | if sequent.branches().is_empty() { 31 | return None; // chase step fails 32 | } 33 | 34 | let mut open = vec![model.clone()]; 35 | let mut bounded = Vec::new(); 36 | 37 | for tuple in tuples { 38 | open = open 39 | .into_iter() 40 | .flat_map(|m| { 41 | info!(event = crate::trace::EVALUATE, sequent = %sequent, mapping = ?tuple); 42 | let (os, bs) = balance(&tuple, sequent.branches(), &m, bounder) 43 | .unwrap() 44 | .into_models(); 45 | bounded.extend(bs); 46 | os 47 | }) 48 | .collect(); 49 | } 50 | 51 | Some(EvaluateResult::from((open, bounded))) 52 | } 53 | } 54 | 55 | #[inline(always)] 56 | fn next_sequent<'s, S: Strategy<&'s RelSequent>>( 57 | strategy: &mut S, 58 | model: &RelModel, 59 | ) -> Option<(&'s RelSequent, Vec>)> { 60 | strategy.find_map(|s| { 61 | let ts = model.evaluate(s); 62 | if ts.is_empty() { 63 | None 64 | } else { 65 | Some((s, ts)) 66 | } 67 | }) 68 | } 69 | 70 | #[inline(always)] 71 | fn balance( 72 | tuple: &NamedTuple, 73 | branches: &[Branch], 74 | parent: &RelModel, 75 | bounder: Option<&B>, 76 | ) -> Result, Error> { 77 | let mut result = EvaluateResult::new(); 78 | 79 | for branch in branches.iter() { 80 | let mut model = parent.clone(); 81 | let mut symbol_tuples = HashMap::<&Symbol, Vec>::new(); 82 | let mut existentials = empty_named_tuple(); 83 | let mut bounded = false; 84 | for atom in branch { 85 | let new_tuple = { 86 | let (t, b) = balance_atom(&mut model, atom, tuple, &mut existentials, bounder)?; 87 | bounded |= b; 88 | t 89 | }; 90 | symbol_tuples 91 | .entry(atom.symbol()) 92 | .or_insert_with(Vec::new) 93 | .push(new_tuple); 94 | } 95 | 96 | for atom in branch { 97 | let symbol = atom.symbol(); 98 | if let Some(ts) = symbol_tuples.remove(symbol) { 99 | model.insert(symbol, ts.into()).unwrap(); 100 | } 101 | } 102 | model 103 | .insert( 104 | &Symbol::Domain, 105 | existentials.values().map(|x| vec![*x]).into(), 106 | ) 107 | .unwrap(); 108 | 109 | model 110 | .insert( 111 | &Symbol::Equality, 112 | existentials.into_iter().map(|(_, x)| vec![x, x]).into(), 113 | ) 114 | .unwrap(); 115 | 116 | if bounded { 117 | result.append_bounded_model(model); 118 | } else { 119 | result.append_open_model(model); 120 | } 121 | } 122 | 123 | Ok(result) 124 | } 125 | 126 | #[inline(always)] 127 | fn balance_atom<'t, B: Bounder>( 128 | model: &mut RelModel, 129 | atom: &'t SymbolicAtom, 130 | tuple: &NamedTuple, 131 | existentials: &mut NamedTuple<'t>, 132 | bounder: Option<&B>, 133 | ) -> Result<(Tuple, bool), Error> { 134 | let mut new_tuple = Vec::new(); 135 | let mut bounded = false; 136 | for attr in atom.attributes().iter() { 137 | if attr.is_existential() { 138 | let element = if let Some(element) = existentials.get(attr) { 139 | *element 140 | } else { 141 | let witness = atom.symbol().witness(&new_tuple)?; 142 | let element = model.record(witness); 143 | existentials.insert(attr, element); 144 | element 145 | }; 146 | new_tuple.push(element); 147 | } else { 148 | new_tuple.push(*tuple.get(attr).unwrap()); 149 | } 150 | } 151 | 152 | if let Some(bounder) = bounder { 153 | if let Some(obs) = atom.symbol().observation(&new_tuple) { 154 | if bounder.bound(model, &obs) { 155 | bounded = true; 156 | } 157 | } 158 | } 159 | 160 | Ok((new_tuple, bounded)) 161 | } 162 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/pre_processor.rs: -------------------------------------------------------------------------------- 1 | use super::{constants::*, expression::Convertor, model::RelModel, sequent::RelSequent}; 2 | use crate::chase::PreProcessor; 3 | use itertools::Itertools; 4 | use razor_fol::{ 5 | syntax::{formula::*, term::Complex, Sig, Theory, Var, FOF}, 6 | transform::{PcfSet, GNF, PCF}, 7 | }; 8 | 9 | /// Is a [`PreProcessor`] instance that converts the input theory to a vector of [`Sequent`]. 10 | /// This is done by the standard conversion to geometric normal form followed by relationalization. 11 | /// The empty [`Model`] instance is created by adding all signature symbols to its underlying database. 12 | /// 13 | /// [`Sequent`]: crate::chase::impl::relational::RelSequent 14 | /// [`Model`]: crate::chase::impl::relational::RelModel 15 | /// [`PreProcessor`]: crate::chase::PreProcessor 16 | pub struct RelPreProcessor { 17 | memoize: bool, 18 | } 19 | 20 | impl RelPreProcessor { 21 | pub fn new(memoize: bool) -> Self { 22 | Self { memoize } 23 | } 24 | } 25 | 26 | impl PreProcessor for RelPreProcessor { 27 | type Sequent = RelSequent; 28 | type Model = RelModel; 29 | 30 | fn pre_process(&self, theory: &Theory) -> (Vec, Self::Model) { 31 | use razor_fol::transform::ToGNF; 32 | use razor_fol::transform::ToSNF; 33 | 34 | let mut c_counter: u32 = 0; 35 | let mut f_counter: u32 = 0; 36 | let mut const_generator = || { 37 | let name = skolem_constant_name(c_counter); 38 | c_counter += 1; 39 | name.into() 40 | }; 41 | let mut fn_generator = || { 42 | let name = skolem_function_name(f_counter); 43 | f_counter += 1; 44 | name.into() 45 | }; 46 | let mut geo_theory: Theory<_> = theory 47 | .iter() 48 | .map(|f| f.snf_with(&mut const_generator, &mut fn_generator)) 49 | .flat_map(|f| f.gnf()) 50 | .collect(); 51 | 52 | geo_theory.extend(equality_axioms()); 53 | let sig = geo_theory.signature().expect("invalid theory signature"); 54 | geo_theory.extend(integrity_axioms(&sig)); 55 | 56 | let mut model = RelModel::new(&sig); 57 | let mut convertor = if self.memoize { 58 | Convertor::memoizing(model.database_mut()) 59 | } else { 60 | Convertor::new() 61 | }; 62 | 63 | let sequents = geo_theory 64 | .formulae() 65 | .iter() 66 | .map(|fmla| RelSequent::new(fmla, &mut convertor).unwrap()) 67 | .collect(); 68 | 69 | (sequents, model) 70 | } 71 | } 72 | 73 | fn equality_axioms() -> Vec { 74 | use razor_fol::{fof, transform::ToGNF}; 75 | 76 | // reflexive (not needed - automatically added for new elements): 77 | // fof!(['|'] -> [(x) = (x)]), 78 | // symmetric (not needed): 79 | // fof!([(x) = (y)] -> [(y) = (x)]), 80 | // transitive: 81 | fof!({[(x) = (y)] & [(y) = (z)]} -> {(x) = (z)}).gnf() 82 | } 83 | 84 | // Function integrity axioms in the form of: 85 | // 1) 'c = x & 'c = y -> x = y 86 | // 2) (f(x1, ..., xn) = x) & (f(y1, ..., yn) = y) & x1 = y1 & ... & xn = yn -> x = y 87 | fn integrity_axioms(sig: &Sig) -> Vec { 88 | use razor_fol::term; 89 | 90 | let mut result = Vec::new(); 91 | for c in sig.constants() { 92 | let c_x: Atomic<_> = Equals { 93 | left: c.clone().into(), 94 | right: term!(x), 95 | } 96 | .into(); 97 | let c_y: Atomic<_> = Equals { 98 | left: c.clone().into(), 99 | right: term!(y), 100 | } 101 | .into(); 102 | let x_y: Atomic<_> = Equals { 103 | left: term!(x), 104 | right: term!(y), 105 | } 106 | .into(); 107 | 108 | let gnf: GNF = (PCF::from(vec![c_x, c_y]), PcfSet::from(PCF::from(x_y))).into(); 109 | result.push(gnf); 110 | } 111 | 112 | for f in sig.functions().values() { 113 | let left = { 114 | let xs = (0..f.arity) 115 | .map(|n| Complex::from(Var::from(format!("x{}", n)))) 116 | .collect_vec(); 117 | let ys = (0..f.arity) 118 | .map(|n| Complex::from(Var::from(format!("y{}", n)))) 119 | .collect_vec(); 120 | 121 | let f_xs: Atomic<_> = Equals { 122 | left: f.symbol.clone().app(xs.clone()), 123 | right: term!(x), 124 | } 125 | .into(); 126 | let f_ys: Atomic<_> = Equals { 127 | left: f.symbol.clone().app(ys.clone()), 128 | right: term!(y), 129 | } 130 | .into(); 131 | 132 | let xs_ys = xs 133 | .into_iter() 134 | .zip(ys.into_iter()) 135 | .map(|(x, y)| Atomic::from(Equals { left: x, right: y })); 136 | 137 | let mut atomics = vec![f_xs, f_ys]; 138 | atomics.extend(xs_ys); 139 | atomics 140 | }; 141 | let right: Atomic<_> = Equals { 142 | left: term!(x), 143 | right: term!(y), 144 | } 145 | .into(); 146 | 147 | let gnf: GNF = (PCF::from(left), PcfSet::from(PCF::from(right))).into(); 148 | result.push(gnf); 149 | } 150 | 151 | result 152 | } 153 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/rewrite.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, hash::Hash}; 2 | 3 | /// Implements a simple algorithm for reasoning about equality of elements by rewriting 4 | /// equal elements to the same representative element of their equivalence class. 5 | pub(super) struct Rewrite 6 | where 7 | T: PartialEq + Eq + Clone + Hash, 8 | { 9 | /// Captures the set of rules in the form of mapping a key to a value element. 10 | rules: HashMap, 11 | } 12 | 13 | impl Rewrite 14 | where 15 | T: PartialEq + Eq + Clone + Hash, 16 | { 17 | /// Creates a new `Rewrite` instance. 18 | pub fn new() -> Self { 19 | Self { 20 | rules: HashMap::new(), 21 | } 22 | } 23 | 24 | /// Adds a new equation as a rewrite rule from `left` to `right`. 25 | pub fn rewrite(&mut self, left: &T, right: &T) { 26 | if self.equals(left, right) == Some(true) { 27 | return; 28 | } 29 | let left = self 30 | .rules 31 | .entry(left.clone()) 32 | .or_insert_with(|| right.clone()) 33 | .clone(); 34 | let right = self 35 | .rules 36 | .entry(right.clone()) 37 | .or_insert_with(|| right.clone()) 38 | .clone(); 39 | 40 | for v in self.rules.values_mut() { 41 | if *v == left { 42 | *v = right.clone() 43 | } 44 | } 45 | } 46 | 47 | /// Returns a `Some(true)` if `left` and `right` have the same normal form and `Some(false)` if different. 48 | /// It returns `None` if either of `left` or `right` does not have a normal form in the current set of rules. 49 | pub fn equals(&self, left: &T, right: &T) -> Option { 50 | let left = self.rules.get(left)?; 51 | let right = self.rules.get(right)?; 52 | 53 | Some(left == right) 54 | } 55 | 56 | /// Returns the normal form of `item` in the existing set of rules if it exists. 57 | pub fn normalize(&self, item: &T) -> Option<&T> { 58 | self.rules.get(item) 59 | } 60 | 61 | /// Returns a vector of all normal forms in the existing set of rules. 62 | pub fn normal_forms(&self) -> Vec<&T> { 63 | use itertools::Itertools; 64 | self.rules.values().unique().collect() 65 | } 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | 72 | #[test] 73 | fn test_basic() { 74 | let mut rewrite = Rewrite::new(); 75 | rewrite.rewrite(&1, &11); 76 | rewrite.rewrite(&2, &22); 77 | 78 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 79 | assert_eq!(Some(false), rewrite.equals(&2, &11)); 80 | assert_eq!(None, rewrite.equals(&2, &3)); 81 | 82 | assert_eq!(Some(&11), rewrite.normalize(&1)); 83 | assert_eq!(Some(&22), rewrite.normalize(&22)); 84 | assert_eq!(None, rewrite.normalize(&3)); 85 | } 86 | 87 | #[test] 88 | fn test_merge_classes() { 89 | { 90 | let mut rewrite = Rewrite::new(); 91 | rewrite.rewrite(&1, &11); 92 | rewrite.rewrite(&1, &111); 93 | 94 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 95 | assert_eq!(Some(true), rewrite.equals(&1, &111)); 96 | assert_eq!(Some(true), rewrite.equals(&11, &111)); 97 | 98 | assert_eq!(Some(&111), rewrite.normalize(&1)); 99 | assert_eq!(Some(&111), rewrite.normalize(&11)); 100 | assert_eq!(Some(&111), rewrite.normalize(&111)); 101 | } 102 | { 103 | let mut rewrite = Rewrite::new(); 104 | rewrite.rewrite(&1, &11); 105 | rewrite.rewrite(&11, &111); 106 | 107 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 108 | assert_eq!(Some(true), rewrite.equals(&1, &111)); 109 | assert_eq!(Some(true), rewrite.equals(&11, &111)); 110 | 111 | assert_eq!(Some(&111), rewrite.normalize(&1)); 112 | assert_eq!(Some(&111), rewrite.normalize(&11)); 113 | assert_eq!(Some(&111), rewrite.normalize(&111)); 114 | } 115 | { 116 | let mut rewrite = Rewrite::new(); 117 | rewrite.rewrite(&1, &11); 118 | rewrite.rewrite(&2, &22); 119 | rewrite.rewrite(&11, &111); 120 | 121 | assert_eq!(Some(false), rewrite.equals(&1, &22)); 122 | assert_eq!(Some(false), rewrite.equals(&11, &22)); 123 | assert_eq!(Some(false), rewrite.equals(&11, &22)); 124 | } 125 | } 126 | 127 | #[test] 128 | fn test_merge_multiple_classes() { 129 | { 130 | let mut rewrite = Rewrite::new(); 131 | rewrite.rewrite(&1, &11); 132 | rewrite.rewrite(&2, &22); 133 | rewrite.rewrite(&1, &2); 134 | 135 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 136 | assert_eq!(Some(true), rewrite.equals(&1, &22)); 137 | assert_eq!(Some(true), rewrite.equals(&2, &11)); 138 | assert_eq!(Some(true), rewrite.equals(&2, &22)); 139 | assert_eq!(Some(true), rewrite.equals(&1, &2)); 140 | assert_eq!(Some(true), rewrite.equals(&11, &22)); 141 | } 142 | { 143 | let mut rewrite = Rewrite::new(); 144 | rewrite.rewrite(&1, &11); 145 | rewrite.rewrite(&2, &22); 146 | rewrite.rewrite(&1, &22); 147 | 148 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 149 | assert_eq!(Some(true), rewrite.equals(&1, &22)); 150 | assert_eq!(Some(true), rewrite.equals(&2, &11)); 151 | assert_eq!(Some(true), rewrite.equals(&2, &22)); 152 | assert_eq!(Some(true), rewrite.equals(&1, &2)); 153 | assert_eq!(Some(true), rewrite.equals(&11, &22)); 154 | } 155 | { 156 | let mut rewrite = Rewrite::new(); 157 | rewrite.rewrite(&1, &11); 158 | rewrite.rewrite(&2, &22); 159 | rewrite.rewrite(&2, &1); 160 | 161 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 162 | assert_eq!(Some(true), rewrite.equals(&1, &22)); 163 | assert_eq!(Some(true), rewrite.equals(&2, &11)); 164 | assert_eq!(Some(true), rewrite.equals(&2, &22)); 165 | assert_eq!(Some(true), rewrite.equals(&1, &2)); 166 | assert_eq!(Some(true), rewrite.equals(&11, &22)); 167 | } 168 | { 169 | let mut rewrite = Rewrite::new(); 170 | rewrite.rewrite(&1, &11); 171 | rewrite.rewrite(&2, &22); 172 | rewrite.rewrite(&2, &11); 173 | 174 | assert_eq!(Some(true), rewrite.equals(&1, &11)); 175 | assert_eq!(Some(true), rewrite.equals(&1, &22)); 176 | assert_eq!(Some(true), rewrite.equals(&2, &11)); 177 | assert_eq!(Some(true), rewrite.equals(&2, &22)); 178 | assert_eq!(Some(true), rewrite.equals(&1, &2)); 179 | assert_eq!(Some(true), rewrite.equals(&11, &22)); 180 | } 181 | { 182 | let mut rewrite = Rewrite::new(); 183 | rewrite.rewrite(&1, &11); 184 | rewrite.rewrite(&2, &22); 185 | rewrite.rewrite(&11, &111); 186 | rewrite.rewrite(&2, &222); 187 | rewrite.rewrite(&1, &2); 188 | 189 | assert_eq!(Some(true), rewrite.equals(&1, &2)); 190 | assert_eq!(Some(true), rewrite.equals(&11, &22)); 191 | assert_eq!(Some(true), rewrite.equals(&111, &222)); 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /razor-chase/src/chase/impl/relational/symbol.rs: -------------------------------------------------------------------------------- 1 | use super::Error; 2 | use crate::chase::{r#impl::basic::BasicWitnessTerm, Observation, Rel, WitnessTerm, E}; 3 | use razor_fol::syntax; 4 | 5 | /// Is the symbol associated to a relational instance. 6 | #[derive(Hash, PartialEq, Eq, Clone, PartialOrd, Ord, Debug)] 7 | pub(super) enum Symbol { 8 | /// Constant symbol 9 | Const(syntax::Const), 10 | 11 | /// Function symbol 12 | Func { symbol: syntax::Func, arity: u8 }, 13 | 14 | /// Predicate symbol 15 | Pred { symbol: syntax::Pred, arity: u8 }, 16 | 17 | /// Equality symbol 18 | Equality, 19 | 20 | /// Domain of elements 21 | Domain, 22 | } 23 | 24 | impl Symbol { 25 | /// Creates a witness term from `self`, given a list of arguments `E`. 26 | pub fn witness(&self, args: &[E]) -> Result { 27 | match self { 28 | Symbol::Const(symbol) => { 29 | assert!(args.is_empty()); 30 | Ok(symbol.clone().into()) 31 | } 32 | Symbol::Func { symbol, arity } => { 33 | assert_eq!(args.len() as u8, *arity); 34 | 35 | let witness = BasicWitnessTerm::apply( 36 | symbol.clone(), 37 | args.iter().map(|e| e.into()).collect(), 38 | ); 39 | Ok(witness) 40 | } 41 | _ => Err(Error::BadWitnessTerm { 42 | symbol: self.to_string(), 43 | }), 44 | } 45 | } 46 | 47 | /// Creates an observation from `self` with a slice of `E` as arguments. 48 | pub fn observation(&self, args: &[E]) -> Option> { 49 | match self { 50 | Symbol::Pred { symbol, arity } => { 51 | assert_eq!(args.len() as u8, *arity); 52 | Some( 53 | Rel::from(symbol.name()) 54 | .app(args.iter().map(|e| BasicWitnessTerm::from(*e)).collect()), 55 | ) 56 | } 57 | Symbol::Equality => { 58 | assert_eq!(args.len(), 2); 59 | Some(BasicWitnessTerm::from(args[0]).equals(args[1].into())) 60 | } 61 | Symbol::Const(c) => { 62 | assert_eq!(args.len(), 1); 63 | Some(BasicWitnessTerm::from(c.clone()).equals(BasicWitnessTerm::from(args[0]))) 64 | } 65 | Symbol::Func { symbol, ref arity } => { 66 | assert_eq!(args.len() as u8, arity + 1); 67 | let last = args[*arity as usize]; 68 | let app = BasicWitnessTerm::apply( 69 | symbol.clone(), 70 | args[0..(*arity as usize)] 71 | .iter() 72 | .map(BasicWitnessTerm::from) 73 | .collect(), 74 | ); 75 | Some(app.equals(last.into())) 76 | } 77 | Symbol::Domain => None, // the Domain instance is used only for book-keeping 78 | } 79 | } 80 | } 81 | 82 | impl std::fmt::Display for Symbol { 83 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 84 | let display = match self { 85 | Symbol::Const(c) => format!("constant {}", c.to_string()), 86 | Symbol::Func { symbol, arity } => { 87 | format!("function {}, arity {}", symbol.to_string(), arity) 88 | } 89 | Symbol::Pred { symbol, arity } => { 90 | format!("predicate {}, arity {}", symbol.to_string(), arity) 91 | } 92 | Symbol::Equality => "equality (=)".into(), 93 | Symbol::Domain => "domain".into(), 94 | }; 95 | write!(f, "{}", display) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /razor-chase/src/chase/scheduler.rs: -------------------------------------------------------------------------------- 1 | //! Implements various schedulers for scheduling branches of the chase. 2 | //! 3 | //! The schedulers are instances of [`Scheduler`]. 4 | //! 5 | //! [`Scheduler`]: crate::chase::Scheduler 6 | use crate::chase::{Model, Scheduler, Sequent, Strategy}; 7 | use std::collections::VecDeque; 8 | use std::marker::PhantomData; 9 | 10 | /// Is a wrapper around other implementations of scheduler, preferred over a trait object that may 11 | /// contain different implementations where a choice among schedulers is desirable. 12 | pub enum Dispatch> { 13 | /// Wraps a [`FIFO`] scheduler, implementing a first-in-first-out algorithm. 14 | FIFO { scheduler: FIFO }, 15 | 16 | /// Wraps a [`LIFO`] scheduler, implementing a last-in-first-out algorithm. 17 | LIFO { 18 | scheduler: LIFO, 19 | _marker: PhantomData, 20 | }, 21 | } 22 | 23 | impl Dispatch 24 | where 25 | S: Sequent, 26 | M: Model, 27 | Stg: Strategy, 28 | { 29 | /// Returns a [`FIFO`], wrapped in a `Dispatch` scheduler. 30 | pub fn new_fifo() -> Self { 31 | Self::FIFO { 32 | scheduler: FIFO::new(), 33 | } 34 | } 35 | 36 | /// Returns a [`LIFO`], wrapped in a `Dispatch` scheduler. 37 | pub fn new_lifo() -> Self { 38 | Self::LIFO { 39 | scheduler: LIFO::new(), 40 | _marker: PhantomData, 41 | } 42 | } 43 | } 44 | 45 | impl Scheduler for Dispatch 46 | where 47 | S: Sequent, 48 | M: Model, 49 | Stg: Strategy, 50 | { 51 | fn empty(&self) -> bool { 52 | match self { 53 | Self::FIFO { scheduler } => scheduler.empty(), 54 | Self::LIFO { 55 | scheduler, 56 | _marker: PhantomData, 57 | } => scheduler.empty(), 58 | } 59 | } 60 | 61 | fn add(&mut self, model: M, strategy: Stg) { 62 | match self { 63 | Self::FIFO { scheduler } => scheduler.add(model, strategy), 64 | Self::LIFO { 65 | scheduler, 66 | _marker: PhantomData, 67 | } => scheduler.add(model, strategy), 68 | } 69 | } 70 | 71 | fn remove(&mut self) -> Option<(M, Stg)> { 72 | match self { 73 | Dispatch::FIFO { scheduler } => scheduler.remove(), 74 | Dispatch::LIFO { 75 | scheduler, 76 | _marker: PhantomData, 77 | } => scheduler.remove(), 78 | } 79 | } 80 | } 81 | 82 | /// Schedules branches of the chase in a first-in-first-out manner. 83 | pub struct FIFO> { 84 | /// Is a queue to store the chase branches (a [model] together with a [strategy]) in a 85 | /// first-in-first-out fashion. 86 | /// 87 | /// [model]: crate::chase::Model 88 | /// [strategy]: crate::chase::Strategy 89 | queue: VecDeque<(M, Stg)>, 90 | _marker: PhantomData, 91 | } 92 | 93 | impl FIFO 94 | where 95 | S: Sequent, 96 | M: Model, 97 | Stg: Strategy, 98 | { 99 | pub fn new() -> Self { 100 | FIFO { 101 | queue: VecDeque::new(), 102 | _marker: PhantomData, 103 | } 104 | } 105 | } 106 | 107 | impl Default for FIFO 108 | where 109 | S: Sequent, 110 | M: Model, 111 | Stg: Strategy, 112 | { 113 | fn default() -> Self { 114 | Self::new() 115 | } 116 | } 117 | 118 | impl Scheduler for FIFO 119 | where 120 | S: Sequent, 121 | M: Model, 122 | Stg: Strategy, 123 | { 124 | fn empty(&self) -> bool { 125 | self.queue.is_empty() 126 | } 127 | 128 | fn add(&mut self, model: M, strategy: Stg) { 129 | self.queue.push_back((model, strategy)) 130 | } 131 | 132 | fn remove(&mut self) -> Option<(M, Stg)> { 133 | self.queue.pop_front() 134 | } 135 | } 136 | 137 | /// Schedules branches of the chase in a last-in-first-out manner. 138 | pub struct LIFO> { 139 | /// Is a queue to store the chase branches (a [model] together with a [strategy]) in a 140 | /// last-in-first-out fashion. 141 | /// 142 | /// [model]: crate::chase::Model 143 | /// [strategy]: crate::chase::Strategy 144 | queue: VecDeque<(M, Stg)>, 145 | _marker: PhantomData, 146 | } 147 | 148 | impl LIFO 149 | where 150 | S: Sequent, 151 | M: Model, 152 | Stg: Strategy, 153 | { 154 | pub fn new() -> Self { 155 | LIFO { 156 | queue: VecDeque::new(), 157 | _marker: PhantomData, 158 | } 159 | } 160 | } 161 | 162 | impl Default for LIFO 163 | where 164 | S: Sequent, 165 | M: Model, 166 | Stg: Strategy, 167 | { 168 | fn default() -> Self { 169 | Self::new() 170 | } 171 | } 172 | 173 | impl Scheduler for LIFO 174 | where 175 | S: Sequent, 176 | M: Model, 177 | Stg: Strategy, 178 | { 179 | fn empty(&self) -> bool { 180 | self.queue.is_empty() 181 | } 182 | 183 | fn add(&mut self, model: M, strategy: Stg) { 184 | self.queue.push_front((model, strategy)) 185 | } 186 | 187 | fn remove(&mut self) -> Option<(M, Stg)> { 188 | self.queue.pop_front() 189 | } 190 | } 191 | 192 | #[cfg(test)] 193 | mod test_lifo { 194 | use crate::chase::scheduler::LIFO; 195 | use crate::chase::{ 196 | bounder::DomainSize, 197 | chase_all, 198 | r#impl::basic::{BasicEvaluator, BasicModel, BasicPreProcessor}, 199 | strategy::Linear, 200 | PreProcessor, Scheduler, 201 | }; 202 | use crate::test_prelude::*; 203 | use razor_fol::syntax::{Theory, FOF}; 204 | use std::collections::HashSet; 205 | 206 | pub fn run(theory: &Theory) -> Vec { 207 | let pre_processor = BasicPreProcessor; 208 | let (sequents, init_model) = pre_processor.pre_process(theory); 209 | let evaluator = BasicEvaluator; 210 | let strategy: Linear<_> = sequents.iter().collect(); 211 | let mut scheduler = LIFO::new(); 212 | let bounder: Option<&DomainSize> = None; 213 | scheduler.add(init_model, strategy); 214 | chase_all(&mut scheduler, &evaluator, bounder) 215 | } 216 | 217 | #[test] 218 | fn test() { 219 | std::fs::read_dir("../theories/core") 220 | .unwrap() 221 | .map(|item| item.unwrap().path()) 222 | .filter(|path| path.ends_with(".raz")) 223 | .for_each(|path| { 224 | let theory = read_theory_from_file(path.to_str().unwrap()); 225 | let expected: HashSet = 226 | read_file(path.with_extension("model").to_str().unwrap()) 227 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 228 | .filter(|s| !s.is_empty()) 229 | .map(Into::into) 230 | .collect(); 231 | let result: HashSet<_> = run(&theory) 232 | .into_iter() 233 | .map(|m| print_basic_model(m)) 234 | .collect(); 235 | assert_eq!( 236 | expected, 237 | result, 238 | "invalid result for theory {}", 239 | path.with_extension("").to_str().unwrap() 240 | ); 241 | }); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /razor-chase/src/chase/strategy.rs: -------------------------------------------------------------------------------- 1 | //! Implements various strategies for selecting sequents in a chase-step. 2 | //! 3 | //! The strategies are instances of [`Strategy`]. 4 | //! 5 | //! [`Strategy`]: crate::chase::Strategy 6 | use crate::chase::{Sequent, Strategy}; 7 | use razor_fol::syntax::{Formula, FOF}; 8 | use std::iter::FromIterator; 9 | 10 | /// Starting from the first [sequent] returns the next sequent every time `Iterator::next()` is 11 | /// called on this strategy. 12 | /// 13 | /// [sequent]: crate::chase::Sequent 14 | pub struct Linear { 15 | /// Is the list of all [sequents] in this strategy. 16 | /// 17 | /// [sequents]: crate::chase::Sequent 18 | sequents: Vec, 19 | 20 | /// Is an internal index, keeping track of the next sequent to return. 21 | index: usize, 22 | } 23 | 24 | impl<'s, S: Sequent> Linear { 25 | #[inline(always)] 26 | fn reset(&mut self) { 27 | self.index = 0; 28 | } 29 | } 30 | 31 | impl Iterator for Linear { 32 | type Item = S; 33 | 34 | fn next(&mut self) -> Option { 35 | self.index += 1; 36 | self.sequents.get(self.index - 1).cloned() 37 | } 38 | } 39 | 40 | impl<'s, S: Sequent> Clone for Linear { 41 | fn clone(&self) -> Self { 42 | Self { 43 | sequents: self.sequents.clone(), 44 | index: 0, 45 | } 46 | } 47 | } 48 | 49 | impl FromIterator for Linear { 50 | fn from_iter>(iter: T) -> Self { 51 | Linear { 52 | sequents: iter.into_iter().collect(), 53 | index: 0, 54 | } 55 | } 56 | } 57 | 58 | /// Avoids starving [sequents] by doing a round robin. The internal state of the 59 | /// strategy is preserved when cloned; thus, the cloned strategy preserves fairness. 60 | /// 61 | /// [sequents]: crate::chase::Sequent 62 | pub struct Fair<'s, S: Sequent> { 63 | /// Is the list of all [sequents] in this strategy. 64 | /// 65 | /// [sequents]: crate::chase::Strategy 66 | sequents: Vec<&'s S>, 67 | 68 | /// Is an internal index, keeping track of the next sequent to return. 69 | index: usize, 70 | 71 | /// For this instance (clone) of strategy, keeps track of the index of the first sequent that 72 | /// was returned. 73 | start: usize, 74 | 75 | /// Is an internal flag to keep track of whether all sequents have been visited or not. 76 | full_circle: bool, 77 | } 78 | 79 | impl<'s, S: Sequent> FromIterator<&'s S> for Fair<'s, S> { 80 | fn from_iter>(iter: T) -> Self { 81 | Fair { 82 | sequents: iter.into_iter().collect(), 83 | index: 0, 84 | start: 0, 85 | full_circle: false, 86 | } 87 | } 88 | } 89 | 90 | impl<'s, S: Sequent> Iterator for Fair<'s, S> { 91 | type Item = &'s S; 92 | 93 | fn next(&mut self) -> Option<&'s S> { 94 | if self.sequents.is_empty() || (self.index == self.start && self.full_circle) { 95 | return None; 96 | } 97 | self.full_circle = true; 98 | let result = self.sequents.get(self.index).cloned(); 99 | self.index = (self.index + 1) % self.sequents.len(); 100 | result 101 | } 102 | } 103 | 104 | impl<'s, S: Sequent> Clone for Fair<'s, S> { 105 | fn clone(&self) -> Self { 106 | Self { 107 | sequents: self.sequents.clone(), 108 | index: self.index, 109 | start: self.index, 110 | full_circle: false, 111 | } 112 | } 113 | } 114 | 115 | /// Behaves like the [strategy] that it wraps but chooses the initial [sequents] (sequents with 116 | /// empty body) only once at the beginning. 117 | /// 118 | /// [strategy]: crate::chase::Strategy 119 | /// [sequents]: crate::chase::Sequent 120 | #[derive(Clone)] 121 | pub struct Bootstrap> { 122 | /// Is the list of initial [sequents] (sequents with empty body). 123 | /// 124 | /// [sequents]: crate::chase::Sequent 125 | initial_sequents: Vec, 126 | 127 | /// Is the underlying [strategy] wrapped by this instance. 128 | /// 129 | /// [strategy]: crate::chase::Strategy 130 | strategy: Stg, 131 | } 132 | 133 | impl> FromIterator for Bootstrap { 134 | fn from_iter>(iter: T) -> Self { 135 | let (initial_sequents, rest) = iter 136 | .into_iter() 137 | .partition(|s| s.body() == FOF::Top && s.head().free_vars().is_empty()); 138 | 139 | Bootstrap { 140 | initial_sequents, 141 | strategy: rest.into_iter().collect(), 142 | } 143 | } 144 | } 145 | 146 | impl> Iterator for Bootstrap { 147 | type Item = S; 148 | 149 | fn next(&mut self) -> Option { 150 | if !self.initial_sequents.is_empty() { 151 | Some(self.initial_sequents.remove(0)) 152 | } else { 153 | self.strategy.next() 154 | } 155 | } 156 | } 157 | 158 | /// Behaves like its underlying [strategy] but prioritizes all failing [sequents] (sequents with 159 | /// empty head) before returning the next (non-failing) sequent. By using this strategy, the chase 160 | /// would drop an inconsistent model as soon as possible. 161 | /// 162 | /// [strategy]: crate::chase::Strategy 163 | /// [sequents]: crate::chase::Sequent 164 | #[derive(Clone)] 165 | pub struct FailFast> { 166 | /// Keeps track of failing [sequents] (sequents with empty head) by a [`Linear`] strategy. 167 | /// 168 | /// [sequents]: crate::chase::Sequent 169 | fail_strategy: Linear, 170 | 171 | /// Is the underlying [strategy] wrapped by this instance. 172 | /// 173 | /// [strategy]: crate::chase::Strategy 174 | strategy: Stg, 175 | } 176 | 177 | impl> FromIterator for FailFast { 178 | fn from_iter>(iter: T) -> Self { 179 | let (fail_sequents, rest): (Vec<_>, _) = 180 | iter.into_iter().partition(|s| s.head() == FOF::Bottom); 181 | 182 | Self { 183 | fail_strategy: fail_sequents.into_iter().collect(), 184 | strategy: rest.into_iter().collect(), 185 | } 186 | } 187 | } 188 | 189 | impl> Iterator for FailFast { 190 | type Item = S; 191 | 192 | fn next(&mut self) -> Option { 193 | self.fail_strategy.next().or_else(|| { 194 | self.fail_strategy.reset(); 195 | self.strategy.next() 196 | }) 197 | } 198 | } 199 | 200 | #[cfg(test)] 201 | mod test_fair { 202 | use crate::chase::{ 203 | bounder::DomainSize, 204 | chase_all, 205 | r#impl::basic::{BasicEvaluator, BasicModel, BasicPreProcessor}, 206 | scheduler::FIFO, 207 | strategy::Fair, 208 | PreProcessor, Scheduler, 209 | }; 210 | use crate::test_prelude::{print_basic_model, read_file, read_theory_from_file}; 211 | use razor_fol::syntax::{Theory, FOF}; 212 | use std::collections::HashSet; 213 | 214 | fn run(theory: &Theory) -> Vec { 215 | let preprocessor = BasicPreProcessor; 216 | let (sequents, init_model) = preprocessor.pre_process(theory); 217 | let evaluator = BasicEvaluator; 218 | let strategy: Fair<_> = sequents.iter().collect(); 219 | let mut scheduler = FIFO::new(); 220 | let bounder: Option<&DomainSize> = None; 221 | scheduler.add(init_model, strategy); 222 | chase_all(&mut scheduler, &evaluator, bounder) 223 | } 224 | 225 | #[test] 226 | fn test() { 227 | std::fs::read_dir("../theories/core") 228 | .unwrap() 229 | .map(|item| item.unwrap().path()) 230 | .filter(|path| path.ends_with(".raz")) 231 | .for_each(|path| { 232 | let theory = read_theory_from_file(path.to_str().unwrap()); 233 | let expected: HashSet = 234 | read_file(path.with_extension("model").to_str().unwrap()) 235 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 236 | .filter(|s| !s.is_empty()) 237 | .map(Into::into) 238 | .collect(); 239 | let result: HashSet<_> = run(&theory) 240 | .into_iter() 241 | .map(|m| print_basic_model(m)) 242 | .collect(); 243 | assert_eq!( 244 | expected, 245 | result, 246 | "invalid result for theory {}", 247 | path.with_extension("").to_str().unwrap() 248 | ); 249 | }); 250 | } 251 | } 252 | 253 | #[cfg(test)] 254 | mod test_bootstrap { 255 | use crate::chase::scheduler::FIFO; 256 | use crate::chase::{ 257 | bounder::DomainSize, 258 | chase_all, 259 | r#impl::basic::{BasicEvaluator, BasicModel, BasicPreProcessor}, 260 | strategy::{Bootstrap, Fair}, 261 | PreProcessor, Scheduler, 262 | }; 263 | use crate::test_prelude::*; 264 | use razor_fol::syntax::{Theory, FOF}; 265 | use std::collections::HashSet; 266 | 267 | fn run(theory: &Theory) -> Vec { 268 | let pre_processor = BasicPreProcessor; 269 | let (sequents, init_model) = pre_processor.pre_process(theory); 270 | let evaluator = BasicEvaluator; 271 | let strategy: Bootstrap<_, Fair<_>> = sequents.iter().collect(); 272 | let mut scheduler = FIFO::new(); 273 | let bounder: Option<&DomainSize> = None; 274 | scheduler.add(init_model, strategy); 275 | chase_all(&mut scheduler, &evaluator, bounder) 276 | } 277 | 278 | #[test] 279 | fn test() { 280 | std::fs::read_dir("../theories/core") 281 | .unwrap() 282 | .map(|item| item.unwrap().path()) 283 | .filter(|path| path.ends_with(".raz")) 284 | .for_each(|path| { 285 | let theory = read_theory_from_file(path.to_str().unwrap()); 286 | let expected: HashSet = 287 | read_file(path.with_extension("model").to_str().unwrap()) 288 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 289 | .filter(|s| !s.is_empty()) 290 | .map(Into::into) 291 | .collect(); 292 | let result: HashSet<_> = run(&theory) 293 | .into_iter() 294 | .map(|m| print_basic_model(m)) 295 | .collect(); 296 | assert_eq!( 297 | expected, 298 | result, 299 | "invalid result for theory {}", 300 | path.with_extension("").to_str().unwrap() 301 | ); 302 | }); 303 | } 304 | } 305 | 306 | #[cfg(test)] 307 | mod test_fail_fast { 308 | use crate::chase::scheduler::FIFO; 309 | use crate::chase::{ 310 | bounder::DomainSize, 311 | chase_all, 312 | r#impl::basic::{BasicEvaluator, BasicModel, BasicPreProcessor}, 313 | strategy::{FailFast, Fair}, 314 | PreProcessor, Scheduler, 315 | }; 316 | use crate::test_prelude::*; 317 | use razor_fol::syntax::{Theory, FOF}; 318 | use std::collections::HashSet; 319 | 320 | fn run(theory: &Theory) -> Vec { 321 | let pre_processor = BasicPreProcessor; 322 | let (sequents, init_model) = pre_processor.pre_process(theory); 323 | let evaluator = BasicEvaluator; 324 | let strategy: FailFast<_, Fair<_>> = sequents.iter().collect(); 325 | let mut scheduler = FIFO::new(); 326 | let bounder: Option<&DomainSize> = None; 327 | scheduler.add(init_model, strategy); 328 | chase_all(&mut scheduler, &evaluator, bounder) 329 | } 330 | 331 | #[test] 332 | fn test() { 333 | std::fs::read_dir("../theories/core") 334 | .unwrap() 335 | .map(|item| item.unwrap().path()) 336 | .filter(|path| path.ends_with(".raz")) 337 | .for_each(|path| { 338 | let theory = read_theory_from_file(path.to_str().unwrap()); 339 | let expected: HashSet = 340 | read_file(path.with_extension("model").to_str().unwrap()) 341 | .split("\n-- -- -- -- -- -- -- -- -- --\n") 342 | .filter(|s| !s.is_empty()) 343 | .map(Into::into) 344 | .collect(); 345 | let result: HashSet<_> = run(&theory) 346 | .into_iter() 347 | .map(|m| print_basic_model(m)) 348 | .collect(); 349 | assert_eq!( 350 | expected, 351 | result, 352 | "invalid result for theory {}", 353 | path.with_extension("").to_str().unwrap() 354 | ); 355 | }); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /razor-chase/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! Rusty Razor is a tool for constructing finite models for first-order theories with equality. 2 | 3 | The model-finding algorithm is inspired by [the chase](https://en.wikipedia.org/wiki/Chase_(algorithm)) 4 | in database systems. Given an input first-order theory, Razor constructs a set of homomorphically 5 | minimal models that satisfy theory. 6 | 7 | To learn more about the theoretical foundation of Razor, check out my 8 | [PhD dissertation](https://digitalcommons.wpi.edu/etd-dissertations/458/). */ 9 | 10 | #![doc(issue_tracker_base_url = "https://github.com/salmans/rusty-razor/issues")] 11 | pub mod chase; 12 | pub mod trace; 13 | 14 | #[cfg(test)] 15 | mod test_prelude; 16 | 17 | #[macro_use] 18 | extern crate tracing; 19 | -------------------------------------------------------------------------------- /razor-chase/src/test_prelude.rs: -------------------------------------------------------------------------------- 1 | use crate::chase::{r#impl::basic, r#impl::collapse, *}; 2 | use itertools::Itertools; 3 | use razor_fol::syntax::{term::Complex, Const, Func, Pred, Theory, Var, FOF}; 4 | use std::{fmt, fs::File, io::Read}; 5 | 6 | pub fn equal_sets(first: &[T], second: &[T]) -> bool { 7 | first.iter().all(|e| second.contains(e)) && second.iter().all(|e| first.contains(e)) 8 | } 9 | 10 | // Variables 11 | pub fn _u() -> Var { 12 | Var::from("u") 13 | } 14 | 15 | pub fn _v() -> Var { 16 | Var::from("v") 17 | } 18 | 19 | pub fn _w() -> Var { 20 | Var::from("w") 21 | } 22 | 23 | pub fn _x() -> Var { 24 | Var::from("x") 25 | } 26 | 27 | pub fn _x_1() -> Var { 28 | Var::from("x`") 29 | } 30 | 31 | pub fn _y() -> Var { 32 | Var::from("y") 33 | } 34 | 35 | pub fn _z() -> Var { 36 | Var::from("z") 37 | } 38 | 39 | #[allow(dead_code)] 40 | pub fn u() -> Complex { 41 | Var::from("u").into() 42 | } 43 | 44 | #[allow(dead_code)] 45 | pub fn v() -> Complex { 46 | Var::from("v").into() 47 | } 48 | 49 | #[allow(dead_code)] 50 | pub fn w() -> Complex { 51 | Var::from("w").into() 52 | } 53 | 54 | #[allow(dead_code)] 55 | pub fn x() -> Complex { 56 | Var::from("x").into() 57 | } 58 | 59 | #[allow(dead_code)] 60 | pub fn x_1() -> Complex { 61 | Var::from("x`").into() 62 | } 63 | 64 | #[allow(dead_code)] 65 | pub fn y() -> Complex { 66 | Var::from("y").into() 67 | } 68 | 69 | #[allow(dead_code)] 70 | pub fn z() -> Complex { 71 | Var::from("z").into() 72 | } 73 | 74 | // Functions 75 | pub fn f() -> Func { 76 | Func::from("f") 77 | } 78 | 79 | pub fn g() -> Func { 80 | Func::from("g") 81 | } 82 | 83 | #[allow(dead_code)] 84 | pub fn h() -> Func { 85 | Func::from("h") 86 | } 87 | 88 | // Constants 89 | pub fn _a() -> Const { 90 | Const::from("a") 91 | } 92 | 93 | pub fn _b() -> Const { 94 | Const::from("b") 95 | } 96 | 97 | pub fn _c() -> Const { 98 | Const::from("c") 99 | } 100 | 101 | pub fn _d() -> Const { 102 | Const::from("d") 103 | } 104 | 105 | #[allow(dead_code)] 106 | pub fn a() -> Complex { 107 | Const::from("a").into() 108 | } 109 | 110 | #[allow(dead_code)] 111 | pub fn b() -> Complex { 112 | Const::from("b").into() 113 | } 114 | 115 | #[allow(dead_code)] 116 | pub fn c() -> Complex { 117 | Const::from("c").into() 118 | } 119 | 120 | // Elements 121 | pub fn e_0() -> E { 122 | E::from(0) 123 | } 124 | 125 | pub fn e_1() -> E { 126 | E::from(1) 127 | } 128 | 129 | pub fn e_2() -> E { 130 | E::from(2) 131 | } 132 | 133 | pub fn e_3() -> E { 134 | E::from(3) 135 | } 136 | 137 | pub fn e_4() -> E { 138 | E::from(4) 139 | } 140 | 141 | // Predicates 142 | #[allow(dead_code)] 143 | #[allow(non_snake_case)] 144 | pub fn P() -> Pred { 145 | Pred::from("P") 146 | } 147 | 148 | #[allow(dead_code)] 149 | #[allow(non_snake_case)] 150 | pub fn Q() -> Pred { 151 | Pred::from("Q") 152 | } 153 | 154 | #[allow(non_snake_case)] 155 | pub fn R() -> Pred { 156 | Pred::from("R") 157 | } 158 | 159 | // Relations 160 | #[allow(non_snake_case)] 161 | pub fn _P_() -> Rel { 162 | Rel::from("P") 163 | } 164 | 165 | #[allow(non_snake_case)] 166 | pub fn _Q_() -> Rel { 167 | Rel::from("Q") 168 | } 169 | 170 | #[allow(non_snake_case)] 171 | pub fn _R_() -> Rel { 172 | Rel::from("R") 173 | } 174 | 175 | #[allow(non_snake_case)] 176 | pub fn _S_() -> Rel { 177 | Rel::from("S") 178 | } 179 | 180 | impl fmt::Debug for basic::BasicSequent { 181 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 182 | write!(f, "{}", self.to_string()) 183 | } 184 | } 185 | 186 | #[allow(dead_code)] 187 | pub fn assert_eq_vectors(first: &Vec, second: &Vec) -> () { 188 | assert!(first.iter().sorted() == second.iter().sorted()); 189 | } 190 | 191 | pub fn assert_eq_sets(first: &Vec, second: &Vec) -> () { 192 | assert!(equal_sets(first, second)); 193 | } 194 | 195 | pub fn assert_debug_string(expected: &str, value: T) { 196 | debug_assert_eq!(expected, format!("{:?}", value)); 197 | } 198 | 199 | #[allow(dead_code)] 200 | pub fn assert_debug_strings(expected: &str, value: Vec) { 201 | let mut strings = value.iter().map(|v| format!("{:?}", v)); 202 | debug_assert_eq!(expected, strings.join("\n")); 203 | } 204 | 205 | pub fn read_file(filename: &str) -> String { 206 | let mut f = File::open(filename).expect("file not found"); 207 | 208 | let mut content = String::new(); 209 | f.read_to_string(&mut content) 210 | .expect("something went wrong reading the file"); 211 | content 212 | } 213 | 214 | pub fn read_theory_from_file(filename: &str) -> Theory { 215 | let content = read_file(filename); 216 | content.parse().unwrap() 217 | } 218 | 219 | pub fn print_basic_model(model: basic::BasicModel) -> String { 220 | let elements: Vec = model 221 | .domain() 222 | .iter() 223 | .sorted() 224 | .iter() 225 | .map(|e| { 226 | let witnesses: Vec = model.witness(e).iter().map(|w| w.to_string()).collect(); 227 | let witnesses = witnesses.into_iter().sorted(); 228 | format!("{} -> {}", witnesses.into_iter().sorted().join(", "), e) 229 | }) 230 | .collect(); 231 | let domain: Vec = model.domain().iter().map(|e| e.to_string()).collect(); 232 | let facts: Vec = model.facts().iter().map(|e| e.to_string()).collect(); 233 | format!( 234 | "Domain: {{{}}}\nFacts: {}\n{}", 235 | domain.into_iter().sorted().join(", "), 236 | facts.into_iter().sorted().join(", "), 237 | elements.join("\n") 238 | ) 239 | } 240 | 241 | pub fn print_collapse_model(model: collapse::ColModel) -> String { 242 | let elements: Vec = model 243 | .domain() 244 | .iter() 245 | .sorted() 246 | .iter() 247 | .map(|e| { 248 | let witnesses: Vec = model.witness(e).iter().map(|w| w.to_string()).collect(); 249 | let witnesses = witnesses.into_iter().sorted(); 250 | format!( 251 | "{} -> {}", 252 | witnesses.into_iter().sorted().join(", "), 253 | e.get() 254 | ) 255 | }) 256 | .collect(); 257 | let domain: Vec = model.domain().iter().map(|e| e.get().to_string()).collect(); 258 | let facts: Vec = model.facts().iter().map(|e| e.to_string()).collect(); 259 | format!( 260 | "Domain: {{{}}}\nFacts: {}\n{}", 261 | domain.into_iter().sorted().join(", "), 262 | facts.into_iter().sorted().join(", "), 263 | elements.join("\n") 264 | ) 265 | } 266 | -------------------------------------------------------------------------------- /razor-chase/src/trace.rs: -------------------------------------------------------------------------------- 1 | pub mod subscriber; 2 | 3 | pub const DEFAULT_JSON_LOG_FILE: &str = "log.json"; 4 | 5 | // model log record fields: 6 | pub const EVENT_FILED: &str = "event"; 7 | pub const MODEL_ID_FIELD: &str = "model_id"; 8 | pub const PARENT_FIELD: &str = "parent"; 9 | pub const MODEL_FIELD: &str = "model"; 10 | 11 | // chase step evaluation log fields: 12 | pub const SEQUENT_FIELD: &str = "sequent"; 13 | pub const MAPPING_FIELD: &str = "mapping"; 14 | 15 | // log span types: 16 | /// Inside a chase step 17 | pub const CHASE_STEP: &str = "@chase_step"; 18 | 19 | // log event types: 20 | /// New model found. 21 | pub const MODEL: &str = "@model"; 22 | 23 | /// The model is expanded by the chase step. 24 | pub const EXTEND: &str = "@extend"; 25 | 26 | /// The chase step failed. The model will be discarded. 27 | pub const FAIL: &str = "@fail"; 28 | 29 | /// A model bound is reached. The model will be discarded. 30 | pub const BOUND: &str = "@bound"; 31 | 32 | /// A sequent was successfully evaluated by the chase step. 33 | pub const EVALUATE: &str = "@evaluate"; 34 | -------------------------------------------------------------------------------- /razor-chase/src/trace/subscriber.rs: -------------------------------------------------------------------------------- 1 | use serde_derive::{Deserialize, Serialize}; 2 | use std::{fmt, fs::File, io::Write, sync::Mutex}; 3 | use tracing::*; 4 | 5 | /// Thread safe json logger that rights records of `ChaseStepRecord` into a given log file. 6 | pub struct JsonLogger { 7 | log_file: Mutex, 8 | chase_step_record: Mutex, 9 | } 10 | 11 | impl JsonLogger { 12 | pub fn new(log_file: File) -> Self { 13 | Self { 14 | log_file: Mutex::new(log_file), 15 | chase_step_record: Mutex::new(ChaseStepRecord::new()), 16 | } 17 | } 18 | } 19 | 20 | impl subscriber::Subscriber for JsonLogger { 21 | fn enabled(&self, _: &Metadata) -> bool { 22 | true // for now 23 | } 24 | 25 | fn new_span(&self, span: &span::Attributes) -> Id { 26 | let mut record = Recorder::new(); 27 | span.record(&mut record); 28 | Id::from_u64(record.model_id.unwrap_or(1)) 29 | } 30 | 31 | fn record(&self, _span: &Id, _values: &span::Record) {} 32 | 33 | fn record_follows_from(&self, _span: &Id, _follows: &Id) {} 34 | 35 | fn event(&self, event: &Event) { 36 | let mut event_record = Recorder::new(); 37 | event.record(&mut event_record); 38 | 39 | if let Some(event_type) = &event_record.event { 40 | match event_type.as_ref() { 41 | super::EXTEND | super::MODEL | super::FAIL | super::BOUND => { 42 | let record = self.chase_step_record.lock().map(|mut rec| { 43 | rec.set_model(ModelRecord::try_from(event_record).ok()); 44 | rec 45 | }); 46 | std::mem::drop(record); 47 | } 48 | super::EVALUATE => { 49 | let mut evaluate_record = Recorder::new(); 50 | event.record(&mut evaluate_record); 51 | let record = self.chase_step_record.lock().map(|mut rec| { 52 | rec.set_evaluate(EvaluateRecord::try_from(event_record).ok()); 53 | rec 54 | }); 55 | std::mem::drop(record); 56 | } 57 | _ => (), 58 | } 59 | } 60 | } 61 | 62 | fn enter(&self, _span: &Id) {} 63 | 64 | fn exit(&self, _span: &Id) { 65 | let record = self.chase_step_record.lock().unwrap(); 66 | self.log_file 67 | .lock() 68 | .unwrap() 69 | .write_all(serde_json::to_string_pretty(&*record).unwrap().as_bytes()) 70 | .expect("unable to write data"); 71 | } 72 | } 73 | 74 | /// Log information associated to a chase step, including the extended model after the step and 75 | /// the sequent and its triggering mapping. 76 | #[derive(Serialize)] 77 | struct ChaseStepRecord { 78 | #[serde(rename = "evaluate")] 79 | evaluate_record: Option, 80 | #[serde(rename = "model")] 81 | model_record: Option, 82 | } 83 | 84 | impl ChaseStepRecord { 85 | fn new() -> Self { 86 | Self { 87 | evaluate_record: None, 88 | model_record: None, 89 | } 90 | } 91 | 92 | /// Set the `EvaluateRecord` of the step, triggered by an EVALUATE event. 93 | fn set_evaluate(&mut self, evaluate_record: Option) { 94 | self.evaluate_record = evaluate_record; 95 | } 96 | 97 | /// Set the `ModelRecord` of the step, triggered by EXTEND, FAIL, MODEL and BOUND events. 98 | fn set_model(&mut self, model_record: Option) { 99 | self.model_record = model_record; 100 | } 101 | } 102 | 103 | /// A record, containing information about a model as it is being extended by the chase or when it 104 | /// is done, a bound is reached or it fails. 105 | #[derive(Serialize, Deserialize)] 106 | struct ModelRecord { 107 | event: String, 108 | model_id: u64, 109 | parent: Option, 110 | model: String, 111 | } 112 | 113 | impl ModelRecord { 114 | fn try_from(value: Recorder) -> Result { 115 | if value.event.is_none() | value.model_id.is_none() | value.model.is_none() { 116 | Err(()) 117 | } else { 118 | Ok(ModelRecord { 119 | event: value.event.unwrap(), 120 | model_id: value.model_id.unwrap(), 121 | parent: value.parent, 122 | model: value.model.unwrap(), 123 | }) 124 | } 125 | } 126 | } 127 | 128 | /// A record, containing information about the sequent that is processed by a chase step and the 129 | /// mapping that triggers it. 130 | #[derive(Serialize, Deserialize)] 131 | struct EvaluateRecord { 132 | sequent: String, 133 | mapping: String, 134 | } 135 | 136 | impl EvaluateRecord { 137 | fn try_from(value: Recorder) -> Result { 138 | if value.sequent.is_none() { 139 | Err(()) 140 | } else { 141 | Ok(EvaluateRecord { 142 | sequent: value.sequent.unwrap(), 143 | mapping: value.mapping.unwrap_or_else(|| "".into()), 144 | }) 145 | } 146 | } 147 | } 148 | 149 | /// Generic trace visitor to collect as many fields as it can. Based on the triggering vent, 150 | /// `Recorder` will be converted to its corresponding log record. 151 | struct Recorder { 152 | event: Option, 153 | model_id: Option, 154 | parent: Option, 155 | model: Option, 156 | sequent: Option, 157 | mapping: Option, 158 | } 159 | 160 | impl Recorder { 161 | fn new() -> Recorder { 162 | Recorder { 163 | event: None, 164 | model_id: None, 165 | parent: None, 166 | model: None, 167 | sequent: None, 168 | mapping: None, 169 | } 170 | } 171 | } 172 | 173 | impl field::Visit for Recorder { 174 | fn record_u64(&mut self, field: &field::Field, value: u64) { 175 | match field.name() { 176 | super::MODEL_ID_FIELD => self.model_id = Some(value), 177 | super::PARENT_FIELD => self.parent = Some(value), 178 | _ => (), 179 | } 180 | } 181 | 182 | fn record_str(&mut self, field: &field::Field, value: &str) { 183 | if let super::EVENT_FILED = field.name() { 184 | self.event = Some(value.to_owned()) 185 | } 186 | } 187 | 188 | fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { 189 | match field.name() { 190 | super::MODEL_FIELD => self.model = Some(format!("{:?}", value)), 191 | super::SEQUENT_FIELD => self.sequent = Some(format!("{:?}", value)), 192 | super::MAPPING_FIELD => self.mapping = Some(format!("{:?}", value)), 193 | _ => (), 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /razor-fol/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "razor-fol" 3 | version = "0.1.0" 4 | authors = ["Salman Saghafi "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "razor-fol is a library for parsing and syntactic manipulation of first-order theories." 8 | documentation = "https://salmans.github.io/rusty-razor/intro.html" 9 | homepage = "https://github.com/salmans/rusty-razor" 10 | repository = "https://github.com/salmans/rusty-razor" 11 | keywords = ["razor", "first-order", "logic", "parser", "geometric"] 12 | categories = ["parser-implementations", "mathematics"] 13 | build = "build.rs" 14 | 15 | [dependencies] 16 | itertools = "^0.7" 17 | thiserror = "^1.0" 18 | lalrpop-util = "^0.19" 19 | 20 | [build-dependencies] 21 | lalrpop = "^0.19" 22 | regex = "1" 23 | 24 | [lib] 25 | name = "razor_fol" 26 | path = "src/lib.rs" -------------------------------------------------------------------------------- /razor-fol/build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /razor-fol/src/grammar.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::syntax::{*, term::Complex, FOF::*, Theory}; 2 | use lalrpop_util::ParseError; 3 | use super::Error; 4 | 5 | grammar; 6 | 7 | extern { 8 | type Error = Error; 9 | } 10 | 11 | match { 12 | r"\s*" => { }, // Skip whitespace 13 | r"//[^\n\r]*[\n\r]*" => { }, // Skip `// comments` 14 | r"/[*]([^*]|([*][^/]))*[*]/" => {}, // Skip `/* comments */` 15 | 16 | "," => _COMMA_, 17 | "." => _DOT_, 18 | ";" => _SEMICOLON_, 19 | 20 | "(" => _LPAREN_, 21 | ")" => _RPAREN_, 22 | 23 | r"=" => _EQUAL_, 24 | 25 | r"true|⊤|'\|'" => _TRUE_, 26 | r"false|⟘|_\|_" => _FALSE_, 27 | 28 | r"not|¬|~" => _NOT_, 29 | 30 | r"and|∧|&" => _AND_, 31 | r"or|∨|\|" => _OR_, 32 | 33 | r"implies|→|->" => _IMPLIES_, 34 | r"iff|⇔|<=>" => _IFF_, 35 | 36 | r"forall|∀|!" => _FORALL_, 37 | r"exists|∃|\?" => _EXISTS_, 38 | } else { 39 | // identifiers 40 | r"[[[:lower:]]_][[:word:]]*" => _LOWER_, 41 | r"[[:upper:]][[:word:]]*" => _UPPER_, 42 | r"'[[[:lower:]]_][[:word:]]*" => _CONST_, 43 | } 44 | 45 | // macros 46 | Comma: Vec = { 47 | _COMMA_)*> => match e { 48 | None => v, 49 | Some(e) => { 50 | v.push(e); 51 | v 52 | } 53 | } 54 | }; 55 | 56 | Parens: T = _LPAREN_ _RPAREN_; 57 | 58 | pub Lower: &'input str = <_LOWER_>; 59 | 60 | pub Upper: &'input str = <_UPPER_>; 61 | 62 | pub Var: Var = 63 | => <>.into(); 64 | 65 | pub Vars = Comma; 66 | 67 | pub Const: Const = 68 | => s[1..].into(); 69 | 70 | pub Func: Func = 71 | => <>.into(); 72 | 73 | pub Term: Complex = { 74 | >> => f.app(ts), 75 | => <>.into(), 76 | => <>.into(), 77 | }; 78 | 79 | pub Equal: FOF = 80 | _EQUAL_ => l.equals(r); 81 | 82 | pub Pred: Pred = 83 | => <>.into(); 84 | 85 | pub Atom: FOF = { 86 | _TRUE_ => Top, 87 | _FALSE_ => Bottom, 88 | , 89 | >> => p.app(ts).into(), 90 | }; 91 | 92 | Unit = { 93 | Atom, 94 | Parens<>, 95 | }; 96 | 97 | Unitary = { 98 | Quant, 99 | Unit, 100 | }; 101 | 102 | Not: FOF = { 103 | _NOT_ => FOF::not(<>), 104 | Unitary, 105 | }; 106 | 107 | UnitNot: FOF = { 108 | _NOT_ => FOF::not(<>), 109 | Unit, 110 | }; 111 | 112 | And: FOF = { 113 | _AND_ => l.and(r), 114 | Not, 115 | }; 116 | 117 | UnitAnd: FOF = { 118 | _AND_ => l.and(r), 119 | UnitNot, 120 | }; 121 | 122 | Or: FOF = { 123 | _OR_ => l.or(r), 124 | And, 125 | }; 126 | 127 | UnitOr: FOF = { 128 | _OR_ => l.or(r), 129 | UnitAnd, 130 | }; 131 | 132 | Quant: FOF = { 133 | _FORALL_ _DOT_ => FOF::forall(<>), 134 | _EXISTS_ _DOT_ => FOF::exists(<>), 135 | }; 136 | 137 | pub Formula: FOF = { 138 | _IMPLIES_ => l.implies(r), 139 | _IFF_ => l.iff(r), 140 | Or, 141 | }; 142 | 143 | pub Theory: Theory = 144 | ( _SEMICOLON_)* =>? { 145 | let theory: Theory<_> = <>.into_iter().collect(); 146 | theory.signature().map_err(|e| ParseError::User { 147 | error: Error::Syntax { 148 | source: e, 149 | } 150 | })?; // validating theory's signature 151 | Ok(theory) 152 | }; -------------------------------------------------------------------------------- /razor-fol/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! Provides a set of tools for parsing and applying common logical transformations on first-order 2 | formulae in Razor's syntax. */ 3 | #[macro_use] 4 | extern crate lalrpop_util; 5 | 6 | pub mod parser; 7 | pub mod syntax; 8 | pub mod transform; 9 | 10 | #[cfg(test)] 11 | pub mod test_macros; 12 | -------------------------------------------------------------------------------- /razor-fol/src/syntax.rs: -------------------------------------------------------------------------------- 1 | /*! Defines an abstract syntax tree (AST) for first-order terms and formulae with equality. */ 2 | pub mod formula; 3 | mod macros; 4 | pub mod signature; 5 | pub mod symbol; 6 | pub mod term; 7 | mod theory; 8 | 9 | pub use formula::{fof::FOF, Formula}; 10 | pub use signature::Sig; 11 | pub use symbol::{Const, Func, Pred, Var, EQ_SYM}; 12 | pub use term::Term; 13 | pub use theory::Theory; 14 | use thiserror::Error; 15 | 16 | /// Is the type of errors arising from inconsistencies in the syntax of formulae. 17 | #[derive(Error, PartialEq, Debug)] 18 | pub enum Error { 19 | /// Is returned when an unsupported operation is performed on an expression. 20 | #[error("inconsistent predicate in theory signature `{}` and `{}`", .this.to_string(), .other.to_string())] 21 | InconsistentPredSig { 22 | this: signature::PredSig, 23 | other: signature::PredSig, 24 | }, 25 | 26 | #[error("inconsistent function in theory signature `{}` and `{}`", .this.to_string(), .other.to_string())] 27 | InconsistentFuncSig { 28 | this: signature::FuncSig, 29 | other: signature::FuncSig, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /razor-fol/src/syntax/symbol.rs: -------------------------------------------------------------------------------- 1 | /*! Defines the symbols of ['Var'], ['Const'], ['Func'] and ['Pred'] for making terms 2 | and formulae. 3 | 4 | ['Var']: crate::syntax::Var 5 | ['Const']: crate::syntax::Const 6 | ['Func']: crate::syntax::Func 7 | ['Pred']: crate::syntax::Pred 8 | */ 9 | 10 | use super::{ 11 | formula::Atom, 12 | term::{Complex, Term}, 13 | }; 14 | use std::fmt; 15 | 16 | /// Represents an uninterpreted function symbol with a given name. 17 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 18 | pub struct Func(String); 19 | 20 | impl Func { 21 | /// Returns the function name. 22 | #[inline(always)] 23 | pub fn name(&self) -> &str { 24 | &self.0 25 | } 26 | 27 | /// Applies `self` on a list of terms. The length of `terms` must be equal to 28 | /// the (assumed) arity of the function. 29 | /// 30 | /// **Note**: the definition of [`Func`] does not impose restriction on the 31 | /// arity of the function. The user is expected to assume the arity of the function. 32 | /// 33 | /// [`Func`]: crate::syntax::Func 34 | pub fn app(self, args: Vec) -> Complex { 35 | Complex::apply(self, args) 36 | } 37 | } 38 | 39 | impl> From for Func { 40 | fn from(name: S) -> Self { 41 | Self(name.into()) 42 | } 43 | } 44 | 45 | impl fmt::Display for Func { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 47 | write!(f, "{}", self.0) 48 | } 49 | } 50 | 51 | impl fmt::Debug for Func { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | write!(f, "{}", self.to_string()) 54 | } 55 | } 56 | 57 | /// Represents a variable symbol with a given name. 58 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] 59 | pub struct Var(String); 60 | 61 | impl Var { 62 | /// Returns the variable name. 63 | #[inline(always)] 64 | pub fn name(&self) -> &str { 65 | &self.0 66 | } 67 | } 68 | 69 | impl> From for Var { 70 | fn from(name: S) -> Self { 71 | Self(name.into()) 72 | } 73 | } 74 | 75 | impl fmt::Display for Var { 76 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 77 | write!(f, "{}", self.0) 78 | } 79 | } 80 | 81 | impl fmt::Debug for Var { 82 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 83 | write!(f, "{}", self.to_string()) 84 | } 85 | } 86 | 87 | /// Represents a constant symbol with a given name. 88 | /// 89 | /// **Note**: Although it is possible to treat nullary functions as constants, we distinguish 90 | /// the two at a syntactic level. 91 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] 92 | pub struct Const(String); 93 | 94 | impl Const { 95 | /// Returns the constant name. 96 | #[inline(always)] 97 | pub fn name(&self) -> &str { 98 | &self.0 99 | } 100 | } 101 | 102 | impl> From for Const { 103 | fn from(name: S) -> Self { 104 | Self(name.into()) 105 | } 106 | } 107 | 108 | impl fmt::Display for Const { 109 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 110 | write!(f, "'{}", self.0) 111 | } 112 | } 113 | 114 | impl fmt::Debug for Const { 115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 116 | write!(f, "{}", self.to_string()) 117 | } 118 | } 119 | 120 | /// Represents a predicate symbol with a given name. 121 | #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] 122 | pub struct Pred(String); 123 | 124 | impl Pred { 125 | /// Returns the predicate name. 126 | #[inline(always)] 127 | pub fn name(&self) -> &str { 128 | &self.0 129 | } 130 | 131 | /// Applies `self` on a list of arguments. The length of `terms` must be equal to 132 | /// the (assumed) arity of the predicate. 133 | /// 134 | /// **Note**: the definition of [`Pred`] does not impose restriction on the arity 135 | /// of the predicate. The user is expected to assume the arity of the predicate. 136 | /// 137 | /// [`Pred`]: crate::syntax::Pred 138 | pub fn app(self, terms: Vec) -> Atom { 139 | Atom { 140 | predicate: self, 141 | terms, 142 | } 143 | } 144 | } 145 | 146 | impl> From for Pred { 147 | fn from(name: S) -> Self { 148 | Self(name.into()) 149 | } 150 | } 151 | 152 | impl fmt::Display for Pred { 153 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 154 | write!(f, "{}", self.0) 155 | } 156 | } 157 | 158 | impl fmt::Debug for Pred { 159 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 160 | write!(f, "{}", self.to_string()) 161 | } 162 | } 163 | 164 | /// Predicate symbol to represent the signature of equality. 165 | pub const EQ_SYM: &str = "="; 166 | 167 | #[cfg(test)] 168 | mod tests { 169 | use crate::{c, f, pred, v}; 170 | 171 | #[test] 172 | fn test_var_to_string() { 173 | assert_eq!("x", v!(x).to_string()); 174 | assert_eq!("y", v!(y).to_string()); 175 | } 176 | 177 | #[test] 178 | fn test_func_to_string() { 179 | assert_eq!("f", f!(f).to_string()); 180 | assert_eq!("g", f!(g).to_string()); 181 | } 182 | 183 | #[test] 184 | fn test_const_to_string() { 185 | assert_eq!("'a", c!(a).to_string()); 186 | assert_eq!("'b", c!(b).to_string()); 187 | } 188 | 189 | #[test] 190 | fn test_pred_to_string() { 191 | assert_eq!("P", pred!(P).to_string()); 192 | assert_eq!("Q", pred!(Q).to_string()); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /razor-fol/src/syntax/theory.rs: -------------------------------------------------------------------------------- 1 | /*! Defines theories of formulae. */ 2 | 3 | use super::{Error, Formula, Sig}; 4 | use std::{fmt, iter::FromIterator, ops::Deref}; 5 | 6 | /// is a first-order theory, containing a set of first-order formulae. 7 | #[derive(PartialEq, Clone, Debug)] 8 | pub struct Theory(Vec); 9 | 10 | impl Theory { 11 | /// Returns the formulae of this theory. 12 | pub fn formulae(&self) -> &[T] { 13 | &self.0 14 | } 15 | 16 | /// Extends this theory with additional formulae. It fails if the signature of the 17 | /// new formulae is inconsistent with the signature of this theory. 18 | pub fn extend>(&mut self, iter: I) { 19 | self.0.extend(iter); 20 | } 21 | } 22 | 23 | impl FromIterator for Theory { 24 | fn from_iter>(iter: I) -> Self { 25 | Self(iter.into_iter().collect()) 26 | } 27 | } 28 | 29 | impl Deref for Theory { 30 | type Target = [T]; 31 | 32 | fn deref(&self) -> &Self::Target { 33 | &self.0 34 | } 35 | } 36 | 37 | impl IntoIterator for Theory { 38 | type Item = T; 39 | 40 | type IntoIter = std::vec::IntoIter; 41 | 42 | fn into_iter(self) -> Self::IntoIter { 43 | self.0.into_iter() 44 | } 45 | } 46 | 47 | impl Formula for Theory { 48 | type Term = T::Term; 49 | 50 | fn signature(&self) -> Result { 51 | Sig::from_signatures( 52 | self.iter() 53 | .map(|c| c.signature()) 54 | .collect::, _>>()?, 55 | ) 56 | } 57 | 58 | fn free_vars(&self) -> Vec<&super::Var> { 59 | use itertools::Itertools; 60 | 61 | self.iter() 62 | .flat_map(|lit| lit.free_vars()) 63 | .unique() 64 | .collect() 65 | } 66 | 67 | fn transform_term(&self, f: &impl Fn(&Self::Term) -> Self::Term) -> Self { 68 | Self(self.iter().map(|lit| lit.transform_term(f)).collect()) 69 | } 70 | } 71 | 72 | impl fmt::Display for Theory { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 74 | let fs: Vec = self.iter().map(|t| t.to_string()).collect(); 75 | write!(f, "{}", fs.join("\n")) 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::*; 82 | use crate::fof; 83 | 84 | #[test] 85 | fn theory_to_string() { 86 | let formulae = vec![ 87 | fof!(!x. ((x) = (x))), 88 | fof!(!x, y. (((x) = (y)) -> ((y) = (x)))), 89 | fof!(!x, y, z. ((((x) = (y)) & ((y) = (z))) -> ((x) = (z)))), 90 | ]; 91 | 92 | let expected_sig = Sig::from_signatures( 93 | formulae 94 | .iter() 95 | .map(|f| f.signature()) 96 | .collect::, _>>() 97 | .unwrap(), 98 | ) 99 | .unwrap(); 100 | let expected = "∀ x. x = x\n\ 101 | ∀ x, y. (x = y → y = x)\n\ 102 | ∀ x, y, z. ((x = y ∧ y = z) → x = z)"; 103 | 104 | let theory: Theory<_> = formulae.into_iter().collect(); 105 | assert_eq!(expected, &theory.to_string()); 106 | assert_eq!(expected_sig, theory.signature().unwrap()); 107 | } 108 | 109 | #[test] 110 | fn theory_extend() { 111 | { 112 | let mut theory: Theory<_> = vec![fof!(P(f(@c)))].into_iter().collect(); 113 | let formulae = vec![fof!(Q(x))]; 114 | theory.extend(formulae); 115 | 116 | assert_eq!( 117 | "P(f(\'c))\n\ 118 | Q(x)", 119 | &theory.to_string() 120 | ); 121 | let signature = theory.signature().unwrap(); 122 | assert_eq!(1, signature.constants().len()); 123 | assert_eq!(1, signature.functions().len()); 124 | assert_eq!(2, signature.predicates().len()); 125 | } 126 | { 127 | let mut theory: Theory<_> = vec![fof!(P(f(@c)))].into_iter().collect(); 128 | let formulae = vec![fof!(P(x))]; 129 | theory.extend(formulae); 130 | 131 | assert_eq!( 132 | "P(f(\'c))\n\ 133 | P(x)", 134 | &theory.to_string() 135 | ); 136 | let signature = theory.signature().unwrap(); 137 | assert_eq!(1, signature.constants().len()); 138 | assert_eq!(1, signature.functions().len()); 139 | assert_eq!(1, signature.predicates().len()); 140 | } 141 | { 142 | let mut theory: Theory<_> = vec![fof!(P(f(@c)))].into_iter().collect(); 143 | let formulae = vec![fof!(P(x, y))]; 144 | theory.extend(formulae); 145 | 146 | assert!(theory.signature().is_err()); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /razor-fol/src/test_macros.rs: -------------------------------------------------------------------------------- 1 | /*! Provides a number of macros for testing purposes. */ 2 | 3 | // Creates a variable with the given name followed by one (otherwise illegal) '`'. 4 | // Internal variable renaming in PNF transformation appends '`' to variables. 5 | #[macro_export] 6 | macro_rules! v_1 { 7 | ($v:ident) => {{ 8 | let var = format!("{}`", stringify!($v)); 9 | $crate::syntax::Var::from(var) 10 | }}; 11 | } 12 | 13 | // Creates a variable with the given name followed by two '`'. 14 | #[macro_export] 15 | macro_rules! v_2 { 16 | ($v:ident) => {{ 17 | let var = format!("{}``", stringify!($v)); 18 | $crate::syntax::Var::from(var) 19 | }}; 20 | } 21 | 22 | // Creates a variable with the given name followed by three '`'. 23 | #[macro_export] 24 | macro_rules! v_3 { 25 | ($v:ident) => {{ 26 | let var = format!("{}```", stringify!($v)); 27 | $crate::syntax::Var::from(var) 28 | }}; 29 | } 30 | 31 | // Asserts equality between two vectors as multisets. 32 | #[macro_export] 33 | macro_rules! assert_eq_sorted_vecs { 34 | ($left:expr, $right:expr) => {{ 35 | match (&$left, &$right) { 36 | (left_val, right_val) => { 37 | let mut l = left_val.to_vec(); 38 | let mut r = right_val.to_vec(); 39 | l.sort(); 40 | r.sort(); 41 | assert_eq!(l, r) 42 | } 43 | } 44 | }}; 45 | ($left:expr, $right:expr ,) => { 46 | $crate::assert_eq_sorted_vecs!($left, $right) 47 | }; 48 | } 49 | 50 | // Asserts that the debug representation of `value` is equal to `expected`. 51 | #[macro_export] 52 | macro_rules! assert_debug_string { 53 | ($expected:expr, $value:expr) => {{ 54 | match (&$expected, &$value) { 55 | (expected_val, val) => assert_eq!(*expected_val, format!("{:?}", val)), 56 | } 57 | }}; 58 | ($expected:expr, $value:expr ,) => { 59 | $crate::assert_debug_string!($expected, $value) 60 | }; 61 | } 62 | 63 | // Asserts that the debug representation of the items in an input iterator `value` 64 | // separated by newline is equal to `expected`. 65 | #[macro_export] 66 | macro_rules! assert_debug_strings { 67 | ($expected:expr, $value:expr) => {{ 68 | match (&$expected, &$value) { 69 | (expected_val, val) => { 70 | let mut strings = val.iter().map(|v| format!("{:?}", v)); 71 | assert_eq!(*expected_val, strings.join("\n")) 72 | } 73 | } 74 | }}; 75 | ($expected:expr, $value:expr ,) => { 76 | $crate::assert_debug_strings!($expected, $value) 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /razor-fol/src/transform.rs: -------------------------------------------------------------------------------- 1 | /*! Implements a number of common transformations on first-order terms and formulae. */ 2 | mod cnf; 3 | mod dnf; 4 | mod gnf; 5 | mod linear; 6 | mod nnf; 7 | mod pnf; 8 | mod range_restrict; 9 | mod relational; 10 | mod simplify; 11 | mod snf; 12 | 13 | pub use cnf::{ToCNF, CNF}; 14 | pub use dnf::{ToDNF, DNF}; 15 | pub use gnf::{PcfSet, ToGNF, GNF, PCF}; 16 | pub use nnf::{ToNNF, NNF}; 17 | pub use pnf::{ToPNF, PNF}; 18 | pub use relational::{FlatClause, Relational, ToRelational}; 19 | pub use snf::{ToSNF, SNF}; 20 | 21 | use crate::syntax::FOF; 22 | use thiserror::Error; 23 | 24 | /// Is the type of errors arising from inconsistencies when transforming formula types. 25 | #[derive(Error, Debug)] 26 | pub enum Error { 27 | /// Is returned when a [`FOF`] cannot be forced into a [`GNF`]. 28 | #[error("formula `{}` cannot be forced into a GNF", .formula.to_string())] 29 | FofToGnf { formula: FOF }, 30 | } 31 | -------------------------------------------------------------------------------- /razor-fol/src/transform/linear.rs: -------------------------------------------------------------------------------- 1 | //! Implements an algorithm for linearizing [`Relational`] formulae. 2 | 3 | use super::Relational; 4 | use crate::syntax::{formula::*, term::Variable, Var}; 5 | use std::collections::HashMap; 6 | 7 | impl Relational { 8 | /// Is similar to [`Relational::linear`] but uses a custom closure to create new variable 9 | /// terms, resulting from removing variables that appear in multiple positions of `self`. 10 | /// 11 | /// **Example**: 12 | /// ```rust 13 | /// # use razor_fol::syntax::FOF; 14 | /// # use std::convert::TryFrom; 15 | /// use razor_fol::transform::{GNF, ToRelational}; 16 | /// 17 | /// let fof = "P(x) -> P(f(x)) & Q('c)".parse::().unwrap(); 18 | /// let gnf = GNF::try_from(fof).unwrap(); 19 | /// let gnf_head = gnf.head(); 20 | /// let relational = gnf_head.relational(); 21 | /// let mut generator = |name: &str, count| format!("V:{}:{}", name, count).into(); 22 | /// let linear = relational.linear_with(&mut generator); 23 | /// assert_eq!( 24 | /// r"(((($f(x, ?0) ∧ ?0 = V:?0:0) ∧ P(V:?0:0)) ∧ @c(?1)) ∧ ?1 = V:?1:0) ∧ Q(V:?1:0)", 25 | /// linear.to_string() 26 | /// ); 27 | /// ``` 28 | pub fn linear_with(&self, generator: &mut G) -> Relational 29 | where 30 | G: FnMut(&str, u32) -> Var, 31 | { 32 | linearize(self, generator) 33 | .into_clauses() 34 | .into_iter() 35 | .map(|clause| { 36 | clause 37 | .into_literals() 38 | .into_iter() 39 | // remove reflexive equations: 40 | .filter(|atomic| { 41 | if let Atomic::Equals(this) = atomic { 42 | this.left != this.right 43 | } else { 44 | true 45 | } 46 | }) 47 | .collect() 48 | }) 49 | .collect() 50 | } 51 | 52 | /// Returns a new [`Relational`] instance, resulting from replacing any varialbe `v` that 53 | /// appears in more than one position of `self` with a fresh variable `y` and 54 | /// conjoined with an equation `v = y`. 55 | /// 56 | /// **Example**: 57 | /// ```rust 58 | /// # use razor_fol::syntax::FOF; 59 | /// # use std::convert::TryFrom; 60 | /// use razor_fol::transform::{GNF, ToRelational}; 61 | /// 62 | /// let fof = "P(x) -> P(f(x)) & Q('c)".parse::().unwrap(); 63 | /// let gnf = GNF::try_from(fof).unwrap(); 64 | /// let gnf_head = gnf.head(); 65 | /// let relational = gnf_head.relational(); 66 | /// let linear = relational.linear(); 67 | /// assert_eq!( 68 | /// r"(((($f(x, ?0) ∧ ?0 = ~?0:0) ∧ P(~?0:0)) ∧ @c(?1)) ∧ ?1 = ~?1:0) ∧ Q(~?1:0)", 69 | /// linear.to_string() 70 | /// ); 71 | /// ``` 72 | pub fn linear(&self) -> Relational { 73 | let mut generator = |name: &str, count| format!("~{}:{}", name, count).into(); 74 | self.linear_with(&mut generator) 75 | } 76 | } 77 | 78 | // A helper to generate new fresh variable terms and equations to extend the original formula. 79 | fn make_equations( 80 | vars: &mut HashMap, 81 | terms: &[Variable], 82 | generator: &mut G, 83 | ) -> (Vec>, Vec) 84 | where 85 | G: FnMut(&str, u32) -> Var, 86 | { 87 | let mut equations = Vec::>::new(); 88 | let mut new_terms = Vec::new(); 89 | for variable in terms { 90 | vars.entry(variable.symbol().clone()) 91 | .and_modify(|count| { 92 | let new_var: Var = generator(variable.name(), *count); 93 | 94 | let left = variable.clone(); 95 | let right = new_var.clone().into(); 96 | 97 | equations.push(Equals { left, right }.into()); 98 | new_terms.push(new_var.into()); 99 | *count += 1; 100 | }) 101 | .or_insert_with(|| { 102 | new_terms.push(variable.clone()); 103 | 0 104 | }); 105 | } 106 | (equations, new_terms) 107 | } 108 | 109 | fn linearize(rel: &Relational, generator: &mut G) -> Relational 110 | where 111 | G: FnMut(&str, u32) -> Var, 112 | { 113 | let mut vars = HashMap::::new(); 114 | rel.iter() 115 | .map(|clause| { 116 | clause 117 | .iter() 118 | .flat_map(|atomic| match atomic { 119 | Atomic::Atom(this) => { 120 | let (mut equations, new_terms) = 121 | make_equations(&mut vars, &this.terms, generator); 122 | equations.push( 123 | Atom { 124 | predicate: this.predicate.clone(), 125 | terms: new_terms, 126 | } 127 | .into(), 128 | ); 129 | equations 130 | } 131 | Atomic::Equals(this) => { 132 | // left at index 0 and right at index 1: 133 | let (mut equations, mut new_terms) = make_equations( 134 | &mut vars, 135 | &[this.left.clone(), this.right.clone()], 136 | generator, 137 | ); 138 | assert_eq!(2, new_terms.len()); 139 | 140 | equations.push( 141 | Equals { 142 | left: new_terms.remove(0), 143 | right: new_terms.remove(0), 144 | } 145 | .into(), 146 | ); 147 | equations 148 | } 149 | }) 150 | .collect() 151 | }) 152 | .collect() 153 | } 154 | 155 | #[cfg(test)] 156 | mod tests { 157 | use crate::{fof, syntax::FOF, transform::PcfSet}; 158 | 159 | // Assumes the input in GNF 160 | fn clause_set(fof: FOF) -> PcfSet { 161 | use std::convert::TryFrom; 162 | 163 | PcfSet::try_from(fof).unwrap() 164 | } 165 | 166 | fn linear(fof: FOF) -> String { 167 | use crate::transform::ToRelational; 168 | 169 | let rels = clause_set(fof) 170 | .iter() 171 | .map(|f| f.relational().linear()) 172 | .map(FOF::from) 173 | .collect::>(); 174 | format!("{:?}", rels) 175 | } 176 | 177 | #[test] 178 | fn test_linear() { 179 | assert_eq!("[true]", linear(fof!('|'))); 180 | assert_eq!("[]", linear(fof!(_|_))); 181 | assert_eq!("[P()]", linear(fof!(P()))); 182 | assert_eq!("[P(x, y)]", linear(fof!(P(x, y)))); 183 | assert_eq!("[x = ~x:0 & P(x, ~x:0)]", linear(fof!(P(x, x)))); 184 | assert_eq!( 185 | "[(P(x, y) & x = ~x:0) & Q(~x:0)]", 186 | linear(fof!([P(x, y)] & [Q(x)])) 187 | ); 188 | assert_eq!( 189 | "[((P(x, y) & x = ~x:0) & y = ~y:0) & Q(~x:0, ~y:0)]", 190 | linear(fof!([P(x, y)] & [Q(x, y)])) 191 | ); 192 | assert_eq!( 193 | "[((P(x) & x = ~x:0) & y = ~y:0) & Q(y, ~x:0, ~y:0)]", 194 | linear(fof!([P(x)] & [Q(y, x, y)])) 195 | ); 196 | assert_eq!( 197 | "[(((P(x) & x = ~x:0) & Q(~x:0)) & x = ~x:1) & R(~x:1)]", 198 | linear(fof!({ [P(x)] & [Q(x)] } & { R(x) })) 199 | ); 200 | assert_eq!("[P(x, y), Q(x)]", linear(fof!([P(x, y)] | [Q(x)]))); 201 | assert_eq!("[P(x, y), Q(x, y)]", linear(fof!([P(x, y)] | [Q(x, y)]))); 202 | assert_eq!( 203 | "[P(x), y = ~y:0 & Q(y, x, ~y:0)]", 204 | linear(fof!([P(x)] | [Q(y, x, y)])) 205 | ); 206 | assert_eq!( 207 | "[P(x), Q(x), R(x)]", 208 | linear(fof!({ [P(x)] | [Q(x)] } | { R(x) })) 209 | ); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /razor-fol/src/transform/range_restrict.rs: -------------------------------------------------------------------------------- 1 | //! Implements a range-restriction algorithm for [`Relational`] formulae. 2 | use super::{FlatClause, Relational}; 3 | use crate::syntax::{formula::*, Var}; 4 | 5 | impl Relational { 6 | /// Given a list of variables `range`, ensures that every variable in `range` appears at 7 | /// least once in the formula represented by `self`. This is done by conjoining 8 | /// atomic formulae in the form of `RR(v)` where `RR` is a "range-restriction" predicate 9 | /// with `symbol` as the predicate symbol. 10 | /// 11 | /// **Note**: the term "range-restricted" is borrowed from the [Alice's book] of 12 | /// database literature. 13 | /// 14 | /// **Example**: 15 | /// ```rust 16 | /// # use std::convert::TryFrom; 17 | /// use razor_fol::{syntax::FOF, transform::{GNF, ToRelational}, v}; 18 | /// 19 | /// let fof = "R(x, y) -> P(x) & Q(y)".parse::().unwrap(); 20 | /// let gnf = GNF::try_from(fof).unwrap(); 21 | /// let gnf_head = gnf.head(); 22 | /// let relational = gnf_head.relational(); 23 | /// let range_restricted = relational.range_restrict(&vec![v!(x), v!(z)], "RR"); 24 | /// assert_eq!(r"(P(x) ∧ Q(y)) ∧ RR(z)", range_restricted.to_string()); 25 | /// ``` 26 | /// 27 | /// [Alice's book]: http://webdam.inria.fr/Alice/ 28 | pub fn range_restrict(&self, range: &[Var], symbol: &str) -> Relational { 29 | self.iter() 30 | .map(|clause| { 31 | let free = clause.free_vars(); 32 | let mut range = Vec::from(range); 33 | range.retain(|x| !free.contains(&x)); 34 | let mut literals = clause.literals().to_vec(); 35 | literals.extend(restrict(&range, symbol).into_literals()); 36 | literals.into() 37 | }) 38 | .collect() 39 | } 40 | } 41 | 42 | // Is a helper for `range_restrict` to build range_restriction conjuncts. 43 | #[inline(always)] 44 | fn restrict(range: &[Var], symbol: &str) -> FlatClause { 45 | let mut result = Vec::new(); 46 | for v in range { 47 | result.push( 48 | Atom { 49 | predicate: symbol.into(), 50 | terms: vec![v.clone().into()], 51 | } 52 | .into(), 53 | ); 54 | } 55 | result.into() 56 | } 57 | 58 | #[cfg(test)] 59 | mod test { 60 | use crate::{ 61 | fof, 62 | syntax::{Var, FOF}, 63 | transform::PcfSet, 64 | v, 65 | }; 66 | 67 | // Assumes the input in GNF 68 | fn clause_set(fof: FOF) -> PcfSet { 69 | use std::convert::TryFrom; 70 | 71 | PcfSet::try_from(fof).unwrap() 72 | } 73 | 74 | fn rr(fof: FOF, range: &[Var]) -> String { 75 | use crate::transform::ToRelational; 76 | 77 | let rels = clause_set(fof) 78 | .iter() 79 | .map(|f| f.relational().range_restrict(range, "RR")) 80 | .map(FOF::from) 81 | .collect::>(); 82 | format!("{:?}", rels) 83 | } 84 | 85 | #[test] 86 | fn test_range_restrict() { 87 | assert_eq!("[true]", rr(fof!('|'), &vec![])); 88 | assert_eq!("[RR(x) & RR(y)]", rr(fof!('|'), &vec![v!(x), v!(y)])); 89 | assert_eq!("[]", rr(fof!(_|_), &vec![])); 90 | 91 | assert_eq!("[P(x)]", rr(fof!(P(x)), &vec![])); 92 | assert_eq!( 93 | "[P(w, x, y) & RR(z)]", 94 | rr(fof!(P(w, x, y)), &vec![v!(x), v!(y), v!(z)]) 95 | ); 96 | 97 | assert_eq!("[P(x) & Q(y)]", rr(fof!([P(x)] & [Q(y)]), &vec![])); 98 | assert_eq!( 99 | "[((P(v, w) & Q(x)) & RR(y)) & RR(z)]", 100 | rr( 101 | fof!([P(v, w)] & [Q(x)]), 102 | &vec![v!(v), v!(w), v!(x), v!(y), v!(z)], 103 | ) 104 | .to_string() 105 | ); 106 | 107 | assert_eq!("[P(x), Q(y)]", rr(fof!([P(x)] | [Q(y)]), &vec![])); 108 | 109 | assert_eq!( 110 | "[P(x) & RR(y), Q(y) & RR(x)]", 111 | rr(fof!([P(x)] | [Q(y)]), &vec![v!(x), v!(y)]) 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /razor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "razor" 3 | version = "0.1.0" 4 | authors = ["Salman Saghafi "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "razor is a command-line tool for constructing finite models for first-order theories." 8 | documentation = "https://salmans.github.io/rusty-razor/intro.html" 9 | homepage = "https://github.com/salmans/rusty-razor" 10 | repository = "https://github.com/salmans/rusty-razor" 11 | keywords = ["razor", "chase", "logic", "model-finder"] 12 | categories = ["mathematics", "command-line-utilities"] 13 | 14 | [dependencies] 15 | razor-fol = { path = "../razor-fol", version = "0.1.0" } 16 | razor-chase = { path = "../razor-chase", version = "0.1.0" } 17 | itertools = "0.7" 18 | anyhow = "^1.0.31" 19 | structopt = "0.2" 20 | term = "0.2" 21 | tracing = "0.1" 22 | 23 | [[bin]] 24 | bench = false 25 | path = "src/main.rs" 26 | name = "razor" -------------------------------------------------------------------------------- /razor/src/command.rs: -------------------------------------------------------------------------------- 1 | use crate::terminal::Stylus; 2 | use crate::{constants::*, utils::*}; 3 | use anyhow::Error; 4 | use razor_chase::{ 5 | chase::{ 6 | bounder::DomainSize, 7 | chase_step, 8 | r#impl::relational::{RelEvaluator, RelPreProcessor}, 9 | scheduler::Dispatch, 10 | strategy::{Bootstrap, FailFast, Fair}, 11 | Model, PreProcessor, Scheduler, 12 | }, 13 | trace::{subscriber::JsonLogger, DEFAULT_JSON_LOG_FILE, EXTEND}, 14 | }; 15 | use std::fs; 16 | use structopt::StructOpt; 17 | 18 | #[derive(StructOpt)] 19 | enum BoundCommand { 20 | #[structopt(about = "Bound models by their domain size.")] 21 | Domain { size: usize }, 22 | } 23 | 24 | impl std::str::FromStr for BoundCommand { 25 | type Err = String; 26 | 27 | fn from_str(s: &str) -> Result { 28 | let domain_str: &'static str = "domain="; 29 | if s.to_lowercase().starts_with(&domain_str) { 30 | let size_str = &s[domain_str.len()..]; 31 | if let Ok(size) = size_str.parse::() { 32 | Ok(BoundCommand::Domain { size }) 33 | } else { 34 | Err(format!("invalid bound size '{}'", &size_str)) 35 | } 36 | } else { 37 | Err(format!("invalid bound '{}'", s)) 38 | } 39 | } 40 | } 41 | 42 | #[derive(StructOpt)] 43 | enum SchedulerOption { 44 | #[structopt(about = "When branching, process new models first.")] 45 | Lifo, 46 | #[structopt(about = "When branching, process new models last.")] 47 | Fifo, 48 | } 49 | 50 | impl std::str::FromStr for SchedulerOption { 51 | type Err = &'static str; 52 | 53 | fn from_str(s: &str) -> Result { 54 | if &s.to_lowercase() == "lifo" { 55 | Ok(SchedulerOption::Lifo) 56 | } else if &s.to_lowercase() == "fifo" { 57 | Ok(SchedulerOption::Fifo) 58 | } else { 59 | Err("invalid scheduler") 60 | } 61 | } 62 | } 63 | 64 | #[derive(StructOpt)] 65 | enum ProcessCommand { 66 | #[structopt(name = "solve", about = "Find models for the input theory")] 67 | Solve { 68 | #[structopt( 69 | short = "i", 70 | long = "input", 71 | parse(from_os_str), 72 | help = "Path to the input theory file" 73 | )] 74 | input: Option, 75 | #[structopt(long = "count", help = "Number of models to return")] 76 | count: Option, 77 | #[structopt( 78 | short = "b", 79 | long = "bound", 80 | name = "bound", 81 | help = "Bound the size of models." 82 | )] 83 | bound: Option, 84 | #[structopt( 85 | long = "show-incomplete", 86 | help = "Show incomplete models.", 87 | parse(try_from_str), 88 | default_value = "true" 89 | )] 90 | show_incomplete: bool, 91 | #[structopt(short = "s", long = "scheduler", default_value = "fifo")] 92 | scheduler: SchedulerOption, 93 | }, 94 | } 95 | 96 | impl ProcessCommand { 97 | fn run(self, stylus: &Stylus) -> Result<(), Error> { 98 | match self { 99 | ProcessCommand::Solve { 100 | input, 101 | count, 102 | bound, 103 | show_incomplete, 104 | scheduler, 105 | } => { 106 | let theory = if let Some(input) = input { 107 | read_theory_from_file(input.to_str().unwrap_or("."))? 108 | } else { 109 | read_theory_from_stdin()? 110 | }; 111 | 112 | stylus.set(STYLE_INFO); 113 | println!("Finding models for theory:"); 114 | 115 | stylus.set(STYLE_THEORY); 116 | theory.formulae().iter().for_each(|f| println!("{}", f)); 117 | 118 | println!(); 119 | println!(); 120 | 121 | let pre_processor = RelPreProcessor::new(true); 122 | let (sequents, init_model) = pre_processor.pre_process(&theory); 123 | 124 | let evaluator = RelEvaluator; 125 | let strategy: Bootstrap<_, FailFast<_, Fair<_>>> = sequents.iter().collect(); 126 | let bounder = bound.map(|b| match b { 127 | BoundCommand::Domain { size } => DomainSize::from(size), 128 | }); 129 | let mut complete_count = 0; 130 | let mut incomplete_count = 0; 131 | 132 | let mut scheduler = match scheduler { 133 | SchedulerOption::Fifo => Dispatch::new_fifo(), 134 | SchedulerOption::Lifo => Dispatch::new_lifo(), 135 | }; 136 | 137 | info!( 138 | event = EXTEND, 139 | model_id = &init_model.get_id(), 140 | model = ?init_model, 141 | ); 142 | scheduler.add(init_model, strategy); 143 | while !scheduler.empty() { 144 | if count.is_some() && complete_count >= count.unwrap() { 145 | break; 146 | } 147 | chase_step( 148 | &mut scheduler, 149 | &evaluator, 150 | bounder.as_ref(), 151 | |m| print_model(m.finalize(), stylus, &mut complete_count), 152 | |m| { 153 | if show_incomplete { 154 | print_model(m.finalize(), stylus, &mut incomplete_count); 155 | } 156 | }, 157 | ) 158 | } 159 | 160 | println!(); 161 | 162 | stylus.set(STYLE_INFO); 163 | println!( 164 | "{} complete and {} incomplete models were found.", 165 | complete_count, incomplete_count 166 | ); 167 | println!(); 168 | Ok(()) 169 | } 170 | } 171 | } 172 | } 173 | 174 | #[derive(StructOpt)] 175 | #[structopt( 176 | name = "Rusty Razor", 177 | about = "A tool for exploring finite models of first-order theories" 178 | )] 179 | #[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))] 180 | pub(super) struct Command { 181 | #[structopt(subcommand, name = "command")] 182 | command: ProcessCommand, 183 | #[structopt(long = "no-color", help = "Disable colored output.")] 184 | no_color: bool, 185 | #[structopt( 186 | short = "l", 187 | long = "log", 188 | parse(from_os_str), 189 | help = "Path to the log file." 190 | )] 191 | log: Option, 192 | } 193 | 194 | impl Command { 195 | pub fn run(self) -> Result<(), Error> { 196 | let process = self.command; 197 | let stylus = stylus(!self.no_color); 198 | 199 | let log = self 200 | .log 201 | .map(|l| l.to_str().unwrap_or(DEFAULT_JSON_LOG_FILE).to_owned()); 202 | 203 | if !self.no_color { 204 | stylus.set(STYLE_LOGO); 205 | println!("{}", ASCII_ART); 206 | } 207 | 208 | let run = || process.run(&stylus); 209 | 210 | if let Some(log) = log { 211 | let log = fs::File::create(log).expect("cannot create the log file"); 212 | let logger = JsonLogger::new(log); 213 | tracing::subscriber::with_default(logger, run) 214 | } else { 215 | run() 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /razor/src/constants.rs: -------------------------------------------------------------------------------- 1 | use crate::terminal::StyleId; 2 | 3 | pub(crate) const ASCII_ART: &str = r#" 4 | ─────────────────────────────── 5 | ███████████████████████████████ 6 | ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 7 | █▀▀█ ██▀▀█████▀▀▀█████▀▀█ ██▀▀█ 8 | █ █ 9 | █▄▄█ ██▄▄█████▄▄▄█████▄▄█ ██▄▄█ 10 | ██████ rusty razor 1.0 ███████ 11 | ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 12 | ─────────────────────────────── 13 | "#; 14 | pub(crate) const STYLE_LOGO: StyleId = 0; 15 | pub(crate) const STYLE_INFO: StyleId = 1; 16 | pub(crate) const STYLE_THEORY: StyleId = 2; 17 | pub(crate) const STYLE_MODEL_DOMAIN: StyleId = 3; 18 | pub(crate) const STYLE_MODEL_FACTS: StyleId = 4; 19 | -------------------------------------------------------------------------------- /razor/src/main.rs: -------------------------------------------------------------------------------- 1 | mod command; 2 | mod constants; 3 | mod terminal; 4 | mod utils; 5 | 6 | #[macro_use] 7 | extern crate tracing; 8 | 9 | use anyhow::Error; 10 | use command::Command; 11 | 12 | fn main() -> Result<(), Error> { 13 | use structopt::StructOpt; 14 | Command::from_args().run() 15 | } 16 | -------------------------------------------------------------------------------- /razor/src/terminal.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub type StyleId = u8; 4 | 5 | #[derive(Clone)] 6 | pub struct Stylus { 7 | styles: HashMap, 8 | } 9 | 10 | impl Stylus { 11 | pub fn new() -> Self { 12 | Self { 13 | styles: HashMap::new(), 14 | } 15 | } 16 | 17 | pub fn insert_style(&mut self, id: StyleId, style: Style) { 18 | self.styles.insert(id, style); 19 | } 20 | 21 | #[allow(dead_code)] 22 | pub fn delete_style(&mut self, id: StyleId) { 23 | self.styles.remove(&id); 24 | } 25 | 26 | pub fn set(&self, id: StyleId) { 27 | if let Some(s) = self.styles.get(&id) { 28 | self.set_style(s) 29 | } 30 | } 31 | 32 | pub fn set_style(&self, style: &Style) { 33 | self.clear(); 34 | Self::apply(style); 35 | } 36 | 37 | pub fn clear(&self) { 38 | term::stdout().as_mut().unwrap().reset().unwrap(); 39 | } 40 | 41 | fn apply(style: &Style) { 42 | let mut term = term::stdout(); 43 | style.color.map(|c| term.as_mut().unwrap().fg(c).unwrap()); 44 | style.attr.map(|a| term.as_mut().unwrap().attr(a).unwrap()); 45 | } 46 | } 47 | 48 | impl Drop for Stylus { 49 | fn drop(&mut self) { 50 | self.clear(); 51 | } 52 | } 53 | 54 | #[derive(Clone)] 55 | pub struct Style { 56 | color: Option, 57 | attr: Option, 58 | } 59 | 60 | impl Style { 61 | pub fn new() -> Self { 62 | Self { 63 | color: None, 64 | attr: None, 65 | } 66 | } 67 | 68 | pub fn foreground(self, color: term::color::Color) -> Self { 69 | Self { 70 | color: Some(color), 71 | ..self 72 | } 73 | } 74 | 75 | pub fn attribute(self, attr: term::Attr) -> Self { 76 | Self { 77 | attr: Some(attr), 78 | ..self 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /razor/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | constants::*, 3 | terminal::{Style, Stylus}, 4 | }; 5 | use anyhow::Error; 6 | use itertools::Itertools; 7 | use razor_chase::chase::{r#impl::relational::RelModel, Model, Observation}; 8 | use razor_fol::syntax::{Theory, FOF}; 9 | use std::{ 10 | fs, 11 | io::{stdin, Read}, 12 | }; 13 | 14 | pub(crate) fn stylus(color: bool) -> Stylus { 15 | let mut stylus = Stylus::new(); 16 | if color { 17 | stylus.insert_style( 18 | STYLE_LOGO, 19 | Style::new().foreground(59).attribute(term::Attr::Dim), 20 | ); 21 | stylus.insert_style( 22 | STYLE_INFO, 23 | Style::new().foreground(59).attribute(term::Attr::Bold), 24 | ); 25 | stylus.insert_style(STYLE_THEORY, Style::new().foreground(252)); 26 | stylus.insert_style( 27 | STYLE_MODEL_DOMAIN, 28 | Style::new().foreground(252).attribute(term::Attr::Bold), 29 | ); 30 | stylus.insert_style(STYLE_MODEL_FACTS, Style::new().foreground(252)); 31 | } 32 | 33 | stylus 34 | } 35 | 36 | pub(crate) fn read_theory_from_file(filename: &str) -> Result, Error> { 37 | let mut f = fs::File::open(filename) 38 | .map_err(|e| Error::new(e).context("failed to find the input file"))?; 39 | 40 | let mut contents = String::new(); 41 | f.read_to_string(&mut contents) 42 | .map_err(|e| Error::new(e).context("failed to read the input file"))?; 43 | 44 | contents 45 | .parse() 46 | .map_err(|e| Error::new(e).context("failed to parse the input theory")) 47 | } 48 | 49 | pub(crate) fn read_theory_from_stdin() -> Result, Error> { 50 | let mut buf: Vec = Vec::new(); 51 | stdin().read_to_end(&mut buf)?; 52 | let s = String::from_utf8(buf)?; 53 | let theory = s.parse()?; 54 | Ok(theory) 55 | } 56 | 57 | pub(crate) fn print_model(model: RelModel, stylus: &Stylus, count: &mut i32) { 58 | *count += 1; 59 | 60 | stylus.set(STYLE_INFO); 61 | println!("{}.\n", count); 62 | println!("Domain:"); 63 | 64 | { 65 | let domain = model.domain().iter().map(|e| e.to_string()).collect_vec(); 66 | // print_list(color, MODEL_DOMAIN_COLOR, &domain); 67 | stylus.set(STYLE_MODEL_DOMAIN); 68 | print!("{}", domain.join(", ")); 69 | println!("\n"); 70 | } 71 | 72 | { 73 | let facts: Vec = model 74 | .facts() 75 | .iter() 76 | .map(|f| match f { 77 | Observation::Fact { relation, terms } => { 78 | let ts: Vec = terms.iter().map(|t| t.to_string()).collect(); 79 | format!("{}({})", relation, ts.join(", ")) 80 | } 81 | Observation::Identity { left, right } => format!("{} = {}", left, right), 82 | }) 83 | .sorted(); 84 | 85 | stylus.set(STYLE_INFO); 86 | println!("Facts:"); 87 | stylus.set(STYLE_MODEL_FACTS); 88 | print!("{}", facts.join("\n")); 89 | } 90 | 91 | stylus.set(STYLE_INFO); 92 | println!("\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); 93 | } 94 | -------------------------------------------------------------------------------- /theories/bounded/thy0.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy0.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , 3 | 'a -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 -------------------------------------------------------------------------------- /theories/bounded/thy0.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) implies P(f(x)); -------------------------------------------------------------------------------- /theories/bounded/thy1.config: -------------------------------------------------------------------------------- 1 | 20 -------------------------------------------------------------------------------- /theories/bounded/thy1.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#11, e#13, e#15, e#16, e#20, e#21, e#25, e#26, e#3, e#30, e#31, e#35, e#38, e#39, e#43, e#44, e#6, e#7} 2 | Facts: 3 | 'c#0, 'e, f(e#0, e#0), f(e#0, e#3) -> e#0 4 | f(e#3, e#0), i(e#0) -> e#3 5 | f(e#3, e#3), f(e#6, e#0) -> e#6 6 | f(e#3, e#6), f(e#6, e#3), f(e#7, e#0) -> e#7 7 | f(e#0, e#7), f(e#11, e#0) -> e#11 8 | f(e#0, e#11), f(e#13, e#0) -> e#13 9 | f(e#15, e#0), f(e#6, e#6) -> e#15 10 | f(e#15, e#6), f(e#16, e#0), f(e#35, e#3), f(e#6, e#15) -> e#16 11 | f(e#11, e#11), f(e#20, e#0) -> e#20 12 | f(e#11, e#20), f(e#20, e#11), f(e#21, e#0) -> e#21 13 | f(e#20, e#20), f(e#25, e#0) -> e#25 14 | f(e#20, e#25), f(e#25, e#20), f(e#26, e#0) -> e#26 15 | f(e#16, e#16), f(e#30, e#0) -> e#30 16 | f(e#16, e#30), f(e#30, e#16), f(e#31, e#0) -> e#31 17 | f(e#15, e#3), f(e#35, e#0) -> e#35 18 | f(e#21, e#21), f(e#38, e#0) -> e#38 19 | f(e#21, e#38), f(e#38, e#21), f(e#39, e#0) -> e#39 20 | f(e#25, e#25), f(e#43, e#0) -> e#43 21 | f(e#25, e#43), f(e#43, e#25), f(e#44, e#0) -> e#44 -------------------------------------------------------------------------------- /theories/bounded/thy1.raz: -------------------------------------------------------------------------------- 1 | // Group Axioms (also see core/thy42.raz) 2 | exists x . x = 'e; 3 | f(x, 'e) = x; 4 | f(f(x, y), z) = f(x, f(y,z)); 5 | f(x, i(x)) = 'e; 6 | i(i(x)) = x; -------------------------------------------------------------------------------- /theories/bounded/thy2.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy2.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , 3 | 'a -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 -------------------------------------------------------------------------------- /theories/bounded/thy2.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(f(x)); -------------------------------------------------------------------------------- /theories/bounded/thy3.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy3.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'c#0 -> e#0 -------------------------------------------------------------------------------- /theories/bounded/thy3.raz: -------------------------------------------------------------------------------- 1 | ?x . P(x) & Q(x); -------------------------------------------------------------------------------- /theories/bounded/thy4.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy4.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'a -> e#0 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {e#0, e#1} 6 | Facts: , , 7 | 'a -> e#0 8 | f#0(e#0) -> e#1 9 | -- -- -- -- -- -- -- -- -- -- 10 | Domain: {e#0, e#1, e#2} 11 | Facts: , , , 12 | 'a -> e#0 13 | f#0(e#0) -> e#1 14 | f#0(e#1) -> e#2 15 | -- -- -- -- -- -- -- -- -- -- 16 | Domain: {e#0, e#1, e#2, e#3} 17 | Facts: , , , , 18 | 'a -> e#0 19 | f#0(e#0) -> e#1 20 | f#0(e#1) -> e#2 21 | f#0(e#2) -> e#3 22 | -- -- -- -- -- -- -- -- -- -- 23 | Domain: {e#0, e#1, e#2, e#3, e#4} 24 | Facts: , , , , , 25 | 'a -> e#0 26 | f#0(e#0) -> e#1 27 | f#0(e#1) -> e#2 28 | f#0(e#2) -> e#3 29 | f#0(e#3) -> e#4 30 | -- -- -- -- -- -- -- -- -- -- 31 | Domain: {e#0, e#1, e#2, e#3, e#4} 32 | Facts: , , , , 33 | 'a -> e#0 34 | f#0(e#0) -> e#1 35 | f#0(e#1) -> e#2 36 | f#0(e#2) -> e#3 37 | f#0(e#3) -> e#4 -------------------------------------------------------------------------------- /theories/bounded/thy4.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) -> exists y. P(y) | Q(x); -------------------------------------------------------------------------------- /theories/bounded/thy5.config: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /theories/bounded/thy5.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'c#0, 'e, f(e#0, e#0), i(e#0) -> e#0 -------------------------------------------------------------------------------- /theories/bounded/thy5.raz: -------------------------------------------------------------------------------- 1 | // Group Axioms (also see bounded/thy1.raz) 2 | exists x . x = 'e; 3 | f(x, 'e) = x; 4 | f(x, y) = w1 and f(y, x) = w2 implies f(w1, z) = f(x, w2); 5 | i(x) = y implies f(x, y) = 'e; 6 | i(i(x)) = x; -------------------------------------------------------------------------------- /theories/bounded/thy6.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy6.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , , , , 3 | 'c#0 -> e#0 4 | f#0(e#0, e#0) -> e#1 5 | f#1(e#0) -> e#2 6 | f#0(e#2, e#2) -> e#3 7 | f#1(e#2) -> e#4 8 | -- -- -- -- -- -- -- -- -- -- 9 | Domain: {e#0, e#1, e#2, e#3, e#4} 10 | Facts: , , , , , , , , 11 | 'c#0 -> e#0 12 | f#0(e#0, e#0) -> e#1 13 | f#1(e#0) -> e#2 14 | f#0(e#2, e#2) -> e#3 15 | f#1(e#2) -> e#4 16 | -- -- -- -- -- -- -- -- -- -- 17 | Domain: {e#0, e#1, e#2, e#3, e#4} 18 | Facts: , , , , , , , , 19 | 'c#0 -> e#0 20 | f#0(e#0, e#0) -> e#1 21 | f#1(e#0) -> e#2 22 | f#0(e#2, e#2) -> e#3 23 | f#1(e#2) -> e#4 24 | -- -- -- -- -- -- -- -- -- -- 25 | Domain: {e#0, e#1, e#2, e#3, e#4} 26 | Facts: , , , , , , , , 27 | 'c#0 -> e#0 28 | f#0(e#0, e#0) -> e#1 29 | f#1(e#0) -> e#2 30 | f#0(e#2, e#2) -> e#3 31 | f#1(e#2) -> e#4 32 | -- -- -- -- -- -- -- -- -- -- 33 | Domain: {e#0, e#1, e#2, e#3, e#4} 34 | Facts: , , , , , , , , , 35 | 'c#0 -> e#0 36 | f#0(e#0, e#0) -> e#1 37 | f#1(e#0) -> e#2 38 | f#0(e#2, e#2) -> e#3 39 | f#1(e#2) -> e#4 40 | -- -- -- -- -- -- -- -- -- -- 41 | Domain: {e#0, e#1, e#2, e#3, e#4} 42 | Facts: , , , , , , , , , 43 | 'c#0 -> e#0 44 | f#0(e#0, e#0) -> e#1 45 | f#1(e#0) -> e#2 46 | f#0(e#2, e#2) -> e#3 47 | f#1(e#2) -> e#4 48 | -- -- -- -- -- -- -- -- -- -- 49 | Domain: {e#0, e#1, e#2, e#3, e#4} 50 | Facts: , , , , , , , , , 51 | 'c#0 -> e#0 52 | f#0(e#0, e#0) -> e#1 53 | f#1(e#0) -> e#2 54 | f#0(e#2, e#2) -> e#3 55 | f#1(e#2) -> e#4 56 | -- -- -- -- -- -- -- -- -- -- 57 | Domain: {e#0, e#1, e#2, e#3, e#4} 58 | Facts: , , , , , , , , , , 59 | 'c#0 -> e#0 60 | f#0(e#0, e#0) -> e#1 61 | f#1(e#0) -> e#2 62 | f#0(e#2, e#2) -> e#3 63 | f#1(e#2) -> e#4 -------------------------------------------------------------------------------- /theories/bounded/thy6.raz: -------------------------------------------------------------------------------- 1 | true -> exists a . Q(a); 2 | Q(x) -> R(x,x) | S(x); 3 | Q(x) -> R(x,x); 4 | R(x,y) -> exists b . S(b); 5 | R(x,x) -> exists e . Q(e) & R(e,x); -------------------------------------------------------------------------------- /theories/bounded/thy7.config: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /theories/bounded/thy7.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , 3 | 'a -> e#0 4 | f#0(e#0) -> e#1 5 | f#0(e#1) -> e#2 6 | f#0(e#2) -> e#3 7 | f#0(e#3) -> e#4 -------------------------------------------------------------------------------- /theories/bounded/thy7.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) implies exists y . P(y); -------------------------------------------------------------------------------- /theories/core/thy0.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'a -> e#0 -------------------------------------------------------------------------------- /theories/core/thy0.raz: -------------------------------------------------------------------------------- 1 | P('a); -------------------------------------------------------------------------------- /theories/core/thy1.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: , 3 | 'a -> e#0 4 | 'b -> e#1 -------------------------------------------------------------------------------- /theories/core/thy1.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P('b); -------------------------------------------------------------------------------- /theories/core/thy10.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'a -> e#0 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {e#0} 6 | Facts: , 7 | 'b -> e#0 -------------------------------------------------------------------------------- /theories/core/thy10.raz: -------------------------------------------------------------------------------- 1 | P('a) or Q('b); 2 | P(x) implies R(x); 3 | Q(x) implies S(x); -------------------------------------------------------------------------------- /theories/core/thy11.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: 3 | -------------------------------------------------------------------------------- /theories/core/thy11.raz: -------------------------------------------------------------------------------- 1 | true implies true; -------------------------------------------------------------------------------- /theories/core/thy12.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: 3 | -------------------------------------------------------------------------------- /theories/core/thy12.raz: -------------------------------------------------------------------------------- 1 | P(x) implies Q(x); -------------------------------------------------------------------------------- /theories/core/thy13.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy13.model -------------------------------------------------------------------------------- /theories/core/thy13.raz: -------------------------------------------------------------------------------- 1 | true implies false; -------------------------------------------------------------------------------- /theories/core/thy14.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'b -> e#0 -------------------------------------------------------------------------------- /theories/core/thy14.raz: -------------------------------------------------------------------------------- 1 | P('a) or Q('b); 2 | P(x) implies false; -------------------------------------------------------------------------------- /theories/core/thy15.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy15.model -------------------------------------------------------------------------------- /theories/core/thy15.raz: -------------------------------------------------------------------------------- 1 | P('a) or Q('b); 2 | P(x) implies false; 3 | Q('b) implies false; -------------------------------------------------------------------------------- /theories/core/thy16.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'c -> e#0 -------------------------------------------------------------------------------- /theories/core/thy16.raz: -------------------------------------------------------------------------------- 1 | P('c, 'c); 2 | P(x, x) implies Q(x); -------------------------------------------------------------------------------- /theories/core/thy17.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2} 2 | Facts: , , 3 | 'c -> e#0 4 | 'a -> e#1 5 | 'b -> e#2 -------------------------------------------------------------------------------- /theories/core/thy17.raz: -------------------------------------------------------------------------------- 1 | P('c, 'c); 2 | P('a, 'b); 3 | P(x, x) implies Q(x); -------------------------------------------------------------------------------- /theories/core/thy18.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2} 2 | Facts: , , 3 | 'a -> e#0 4 | 'b -> e#1 5 | 'c -> e#2 -------------------------------------------------------------------------------- /theories/core/thy18.raz: -------------------------------------------------------------------------------- 1 | P('a, 'b); 2 | P('c, 'c); 3 | P(x, x) implies Q(x); -------------------------------------------------------------------------------- /theories/core/thy19.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#10, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 2 | Facts: 3 | 'a -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 8 | f(e#4) -> e#5 9 | f(e#5) -> e#6 10 | f(e#6) -> e#7 11 | f(e#7) -> e#8 12 | f(e#8) -> e#9 13 | 'b, f(e#9) -> e#10 -------------------------------------------------------------------------------- /theories/core/thy19.raz: -------------------------------------------------------------------------------- 1 | f(f(f(f(f(f(f(f(f(f('a)))))))))) = 'b; -------------------------------------------------------------------------------- /theories/core/thy2.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'a -> e#0 -------------------------------------------------------------------------------- /theories/core/thy2.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) implies Q(x); -------------------------------------------------------------------------------- /theories/core/thy20.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#10, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 2 | Facts: , , , , , , , , , 3 | 'a -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 8 | f(e#4) -> e#5 9 | f(e#5) -> e#6 10 | f(e#6) -> e#7 11 | f(e#7) -> e#8 12 | f(e#8) -> e#9 13 | 'b, f(e#9) -> e#10 -------------------------------------------------------------------------------- /theories/core/thy20.raz: -------------------------------------------------------------------------------- 1 | f(f(f(f(f(f(f(f(f(f('a)))))))))) = 'b; 2 | f(x) = y implies P(x); -------------------------------------------------------------------------------- /theories/core/thy21.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#10, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 2 | Facts: , , , , , , , , 3 | 'a -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 8 | f(e#4) -> e#5 9 | f(e#5) -> e#6 10 | f(e#6) -> e#7 11 | f(e#7) -> e#8 12 | f(e#8) -> e#9 13 | 'b, f(e#9) -> e#10 -------------------------------------------------------------------------------- /theories/core/thy21.raz: -------------------------------------------------------------------------------- 1 | f(f(f(f(f(f(f(f(f(f('a)))))))))) = 'b; 2 | f(x0) = x1 and f(x1) = x2 implies P(x0); -------------------------------------------------------------------------------- /theories/core/thy22.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , 3 | 'a -> e#0 -------------------------------------------------------------------------------- /theories/core/thy22.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | Q('a); 3 | P(x) and Q(x) implies R(x); -------------------------------------------------------------------------------- /theories/core/thy23.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , , 3 | 'c#0, 'c#1, 'c#2 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy23.raz: -------------------------------------------------------------------------------- 1 | exists x . P(x); 2 | exists y . Q(y); 3 | exists z . R(z); 4 | 5 | P(x) & R(x) -> S(x); 6 | P(x) & Q(y) -> x = y; 7 | Q(y) & R(z) -> y = z; -------------------------------------------------------------------------------- /theories/core/thy24.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , , , 3 | 'c#0, 'c#1, 'c#2, 'c#3 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy24.raz: -------------------------------------------------------------------------------- 1 | exists w, x, y, z . P(w) & Q(x) & R(y) & S(z); 2 | 3 | P(x) & S(x) -> T(x); 4 | P(x) & Q(y) -> x = y; 5 | R(x) & S(y) -> x = y; 6 | Q(x) & R(y) -> x = y; -------------------------------------------------------------------------------- /theories/core/thy25.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3} 2 | Facts: , , , 3 | 'c#0 -> e#0 4 | 'c#1 -> e#1 5 | 'c#2 -> e#2 6 | 'c#3 -> e#3 -------------------------------------------------------------------------------- /theories/core/thy25.raz: -------------------------------------------------------------------------------- 1 | exists w, x, y, z . P(w) & Q(x) & R(y) & S(z); -------------------------------------------------------------------------------- /theories/core/thy26.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'c#0 -> e#0 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {e#0} 6 | Facts: 7 | 'c#1 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy26.raz: -------------------------------------------------------------------------------- 1 | exists x . P(x) | exists x . P(x); -------------------------------------------------------------------------------- /theories/core/thy27.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy27.model -------------------------------------------------------------------------------- /theories/core/thy27.raz: -------------------------------------------------------------------------------- 1 | exists x . P(x); 2 | exists y . Q(y); 3 | P(x) & Q(y) -> x = y; 4 | P(x) -> R(f(x)); 5 | Q(x) -> S(f(x)); 6 | R(x) & S(x) -> false; -------------------------------------------------------------------------------- /theories/core/thy28.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: , 3 | 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {} 6 | Facts: , 7 | 8 | -- -- -- -- -- -- -- -- -- -- 9 | Domain: {} 10 | Facts: , , 11 | 12 | -- -- -- -- -- -- -- -- -- -- 13 | Domain: {} 14 | Facts: , , 15 | -------------------------------------------------------------------------------- /theories/core/thy28.raz: -------------------------------------------------------------------------------- 1 | T() and V() or U() and V(); -------------------------------------------------------------------------------- /theories/core/thy29.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: 3 | 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {} 6 | Facts: , , 7 | 8 | -- -- -- -- -- -- -- -- -- -- 9 | Domain: {} 10 | Facts: , , 11 | 12 | -- -- -- -- -- -- -- -- -- -- 13 | Domain: {} 14 | Facts: , , 15 | 16 | -- -- -- -- -- -- -- -- -- -- 17 | Domain: {} 18 | Facts: , , , 19 | 20 | -- -- -- -- -- -- -- -- -- -- 21 | Domain: {} 22 | Facts: , , , 23 | 24 | -- -- -- -- -- -- -- -- -- -- 25 | Domain: {} 26 | Facts: , , , , 27 | 28 | -- -- -- -- -- -- -- -- -- -- 29 | Domain: {} 30 | Facts: , , , , 31 | -------------------------------------------------------------------------------- /theories/core/thy29.raz: -------------------------------------------------------------------------------- 1 | P() or Q(); 2 | Q() -> R() or S(); 3 | R() -> T() and V() or U() and V(); 4 | S() -> W() or X() or Y(); 5 | -------------------------------------------------------------------------------- /theories/core/thy3.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: 3 | 'c#0 -> e#0 4 | 'c#1 -> e#1 -------------------------------------------------------------------------------- /theories/core/thy3.raz: -------------------------------------------------------------------------------- 1 | true implies exists y . exists z. R(y,z); -------------------------------------------------------------------------------- /theories/core/thy30.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy30.model -------------------------------------------------------------------------------- /theories/core/thy30.raz: -------------------------------------------------------------------------------- 1 | 'b0 = 'c0 and 'b1 = 'c1 and 'b2 = 'c2 and 'b3 = 'c3; 2 | f('b0) = 'd0 and g('b0, 'b1) = 'd1 and h('b0, 'b1, 'b2) = 'd2 and i('b0, 'b1, 'b2, 'b3) = 'd3; 3 | f('c0) = 'd0 and g('c0, 'c1) = 'd1 and h('c0, 'c1, 'c2) = 'd2 and i('c0, 'c1, 'c2, 'c3) = 'd3 implies false; -------------------------------------------------------------------------------- /theories/core/thy31.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , 3 | 'c#0 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy31.raz: -------------------------------------------------------------------------------- 1 | true -> exists y . R(y) & U(y); 2 | U(u) & R(x) -> exists z . Q(x,u); -------------------------------------------------------------------------------- /theories/core/thy32.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: , 3 | 'c#0 -> e#0 4 | f#0(e#0) -> e#1 -------------------------------------------------------------------------------- /theories/core/thy32.raz: -------------------------------------------------------------------------------- 1 | true -> exists y . R(y); 2 | R(x) -> exists w . Q(x,w); -------------------------------------------------------------------------------- /theories/core/thy33.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: 3 | -------------------------------------------------------------------------------- /theories/core/thy33.raz: -------------------------------------------------------------------------------- 1 | // exists x. next('test) = x & P(x); 2 | // P(x) -> exists y . next('test) = y & P(y); 3 | -------------------------------------------------------------------------------- /theories/core/thy35.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , 3 | 'c#0 -> e#0 4 | f#0(e#0) -> e#1 5 | f#2(e#1) -> e#2 6 | f#6(e#2) -> e#3 7 | f#14(e#3) -> e#4 8 | -- -- -- -- -- -- -- -- -- -- 9 | Domain: {e#0, e#1, e#2, e#3, e#4} 10 | Facts: , , , , 11 | 'c#0 -> e#0 12 | f#0(e#0) -> e#1 13 | f#2(e#1) -> e#2 14 | f#6(e#2) -> e#3 15 | f#14(e#3) -> e#4 16 | -- -- -- -- -- -- -- -- -- -- 17 | Domain: {e#0, e#1, e#2, e#3, e#4} 18 | Facts: , , , , 19 | 'c#0 -> e#0 20 | f#0(e#0) -> e#1 21 | f#2(e#1) -> e#2 22 | f#6(e#2) -> e#3 23 | f#16(e#3) -> e#4 24 | -- -- -- -- -- -- -- -- -- -- 25 | Domain: {e#0, e#1, e#2, e#3, e#4} 26 | Facts: , , , , 27 | 'c#0 -> e#0 28 | f#0(e#0) -> e#1 29 | f#2(e#1) -> e#2 30 | f#6(e#2) -> e#3 31 | f#16(e#3) -> e#4 32 | -- -- -- -- -- -- -- -- -- -- 33 | Domain: {e#0, e#1, e#2, e#3, e#4} 34 | Facts: , , , , 35 | 'c#0 -> e#0 36 | f#0(e#0) -> e#1 37 | f#2(e#1) -> e#2 38 | f#8(e#2) -> e#3 39 | f#18(e#3) -> e#4 40 | -- -- -- -- -- -- -- -- -- -- 41 | Domain: {e#0, e#1, e#2, e#3, e#4} 42 | Facts: , , , , 43 | 'c#0 -> e#0 44 | f#0(e#0) -> e#1 45 | f#2(e#1) -> e#2 46 | f#8(e#2) -> e#3 47 | f#18(e#3) -> e#4 48 | -- -- -- -- -- -- -- -- -- -- 49 | Domain: {e#0, e#1, e#2, e#3, e#4} 50 | Facts: , , , , 51 | 'c#0 -> e#0 52 | f#0(e#0) -> e#1 53 | f#2(e#1) -> e#2 54 | f#8(e#2) -> e#3 55 | f#20(e#3) -> e#4 56 | -- -- -- -- -- -- -- -- -- -- 57 | Domain: {e#0, e#1, e#2, e#3, e#4} 58 | Facts: , , , , 59 | 'c#0 -> e#0 60 | f#0(e#0) -> e#1 61 | f#2(e#1) -> e#2 62 | f#8(e#2) -> e#3 63 | f#20(e#3) -> e#4 64 | -- -- -- -- -- -- -- -- -- -- 65 | Domain: {e#0, e#1, e#2, e#3, e#4} 66 | Facts: , , , , 67 | 'c#0 -> e#0 68 | f#0(e#0) -> e#1 69 | f#4(e#1) -> e#2 70 | f#10(e#2) -> e#3 71 | f#22(e#3) -> e#4 72 | -- -- -- -- -- -- -- -- -- -- 73 | Domain: {e#0, e#1, e#2, e#3, e#4} 74 | Facts: , , , , 75 | 'c#0 -> e#0 76 | f#0(e#0) -> e#1 77 | f#4(e#1) -> e#2 78 | f#10(e#2) -> e#3 79 | f#22(e#3) -> e#4 80 | -- -- -- -- -- -- -- -- -- -- 81 | Domain: {e#0, e#1, e#2, e#3, e#4} 82 | Facts: , , , , 83 | 'c#0 -> e#0 84 | f#0(e#0) -> e#1 85 | f#4(e#1) -> e#2 86 | f#10(e#2) -> e#3 87 | f#24(e#3) -> e#4 88 | -- -- -- -- -- -- -- -- -- -- 89 | Domain: {e#0, e#1, e#2, e#3, e#4} 90 | Facts: , , , , 91 | 'c#0 -> e#0 92 | f#0(e#0) -> e#1 93 | f#4(e#1) -> e#2 94 | f#10(e#2) -> e#3 95 | f#24(e#3) -> e#4 96 | -- -- -- -- -- -- -- -- -- -- 97 | Domain: {e#0, e#1, e#2, e#3, e#4} 98 | Facts: , , , , 99 | 'c#0 -> e#0 100 | f#0(e#0) -> e#1 101 | f#4(e#1) -> e#2 102 | f#12(e#2) -> e#3 103 | f#26(e#3) -> e#4 104 | -- -- -- -- -- -- -- -- -- -- 105 | Domain: {e#0, e#1, e#2, e#3, e#4} 106 | Facts: , , , , 107 | 'c#0 -> e#0 108 | f#0(e#0) -> e#1 109 | f#4(e#1) -> e#2 110 | f#12(e#2) -> e#3 111 | f#26(e#3) -> e#4 112 | -- -- -- -- -- -- -- -- -- -- 113 | Domain: {e#0, e#1, e#2, e#3, e#4} 114 | Facts: , , , , 115 | 'c#0 -> e#0 116 | f#0(e#0) -> e#1 117 | f#4(e#1) -> e#2 118 | f#12(e#2) -> e#3 119 | f#28(e#3) -> e#4 120 | -- -- -- -- -- -- -- -- -- -- 121 | Domain: {e#0, e#1, e#2, e#3, e#4} 122 | Facts: , , , , 123 | 'c#0 -> e#0 124 | f#0(e#0) -> e#1 125 | f#4(e#1) -> e#2 126 | f#12(e#2) -> e#3 127 | f#28(e#3) -> e#4 -------------------------------------------------------------------------------- /theories/core/thy35.raz: -------------------------------------------------------------------------------- 1 | true -> (exists x . P(x)); 2 | P(x) -> (exists x . P1(x) ) | (exists x . P2(x)); 3 | 4 | P1(x) -> (exists x . P11(x) ) | (exists x . P12(x)); 5 | P2(x) -> (exists x . P21(x) ) | (exists x . P22(x)); 6 | 7 | P11(x) -> (exists x . P111(x) ) | (exists x . P112(x)); 8 | P12(x) -> (exists x . P121(x) ) | (exists x . P122(x)); 9 | P21(x) -> (exists x . P211(x) ) | (exists x . P212(x)); 10 | P22(x) -> (exists x . P221(x) ) | (exists x . P222(x)); 11 | 12 | P111(x) -> (exists x . P1111(x) ) | (exists x . P1112(x)); 13 | P112(x) -> (exists x . P1121(x) ) | (exists x . P1122(x)); 14 | P121(x) -> (exists x . P1211(x) ) | (exists x . P1212(x)); 15 | P122(x) -> (exists x . P1221(x) ) | (exists x . P1222(x)); 16 | 17 | P211(x) -> (exists x . P2111(x) ) | (exists x . P2112(x)); 18 | P212(x) -> (exists x . P2121(x) ) | (exists x . P2122(x)); 19 | P221(x) -> (exists x . P2211(x) ) | (exists x . P2212(x)); 20 | P222(x) -> (exists x . P2221(x) ) | (exists x . P2222(x)); -------------------------------------------------------------------------------- /theories/core/thy36.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 2 | Facts: , , , , 3 | 'c#0 -> e#0 4 | 'c#1 -> e#1 5 | f#0(e#0, e#1) -> e#2 6 | f#1(e#0, e#1) -> e#3 7 | f#4(e#2, e#3) -> e#4 8 | f#5(e#2, e#3) -> e#5 9 | f#12(e#4, e#5) -> e#6 10 | f#13(e#4, e#5) -> e#7 11 | f#28(e#6, e#7) -> e#8 12 | f#29(e#6, e#7) -> e#9 13 | -- -- -- -- -- -- -- -- -- -- 14 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 15 | Facts: , , , , 16 | 'c#0 -> e#0 17 | 'c#1 -> e#1 18 | f#0(e#0, e#1) -> e#2 19 | f#1(e#0, e#1) -> e#3 20 | f#4(e#2, e#3) -> e#4 21 | f#5(e#2, e#3) -> e#5 22 | f#12(e#4, e#5) -> e#6 23 | f#13(e#4, e#5) -> e#7 24 | f#28(e#6, e#7) -> e#8 25 | f#29(e#6, e#7) -> e#9 26 | -- -- -- -- -- -- -- -- -- -- 27 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 28 | Facts: , , , , 29 | 'c#0 -> e#0 30 | 'c#1 -> e#1 31 | f#0(e#0, e#1) -> e#2 32 | f#1(e#0, e#1) -> e#3 33 | f#4(e#2, e#3) -> e#4 34 | f#5(e#2, e#3) -> e#5 35 | f#12(e#4, e#5) -> e#6 36 | f#13(e#4, e#5) -> e#7 37 | f#32(e#6, e#7) -> e#8 38 | f#33(e#6, e#7) -> e#9 39 | -- -- -- -- -- -- -- -- -- -- 40 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 41 | Facts: , , , , 42 | 'c#0 -> e#0 43 | 'c#1 -> e#1 44 | f#0(e#0, e#1) -> e#2 45 | f#1(e#0, e#1) -> e#3 46 | f#4(e#2, e#3) -> e#4 47 | f#5(e#2, e#3) -> e#5 48 | f#12(e#4, e#5) -> e#6 49 | f#13(e#4, e#5) -> e#7 50 | f#32(e#6, e#7) -> e#8 51 | f#33(e#6, e#7) -> e#9 52 | -- -- -- -- -- -- -- -- -- -- 53 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 54 | Facts: , , , , 55 | 'c#0 -> e#0 56 | 'c#1 -> e#1 57 | f#0(e#0, e#1) -> e#2 58 | f#1(e#0, e#1) -> e#3 59 | f#4(e#2, e#3) -> e#4 60 | f#5(e#2, e#3) -> e#5 61 | f#16(e#4, e#5) -> e#6 62 | f#17(e#4, e#5) -> e#7 63 | f#36(e#6, e#7) -> e#8 64 | f#37(e#6, e#7) -> e#9 65 | -- -- -- -- -- -- -- -- -- -- 66 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 67 | Facts: , , , , 68 | 'c#0 -> e#0 69 | 'c#1 -> e#1 70 | f#0(e#0, e#1) -> e#2 71 | f#1(e#0, e#1) -> e#3 72 | f#4(e#2, e#3) -> e#4 73 | f#5(e#2, e#3) -> e#5 74 | f#16(e#4, e#5) -> e#6 75 | f#17(e#4, e#5) -> e#7 76 | f#36(e#6, e#7) -> e#8 77 | f#37(e#6, e#7) -> e#9 78 | -- -- -- -- -- -- -- -- -- -- 79 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 80 | Facts: , , , , 81 | 'c#0 -> e#0 82 | 'c#1 -> e#1 83 | f#0(e#0, e#1) -> e#2 84 | f#1(e#0, e#1) -> e#3 85 | f#4(e#2, e#3) -> e#4 86 | f#5(e#2, e#3) -> e#5 87 | f#16(e#4, e#5) -> e#6 88 | f#17(e#4, e#5) -> e#7 89 | f#40(e#6, e#7) -> e#8 90 | f#41(e#6, e#7) -> e#9 91 | -- -- -- -- -- -- -- -- -- -- 92 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 93 | Facts: , , , , 94 | 'c#0 -> e#0 95 | 'c#1 -> e#1 96 | f#0(e#0, e#1) -> e#2 97 | f#1(e#0, e#1) -> e#3 98 | f#4(e#2, e#3) -> e#4 99 | f#5(e#2, e#3) -> e#5 100 | f#16(e#4, e#5) -> e#6 101 | f#17(e#4, e#5) -> e#7 102 | f#40(e#6, e#7) -> e#8 103 | f#41(e#6, e#7) -> e#9 104 | -- -- -- -- -- -- -- -- -- -- 105 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 106 | Facts: , , , , 107 | 'c#0 -> e#0 108 | 'c#1 -> e#1 109 | f#0(e#0, e#1) -> e#2 110 | f#1(e#0, e#1) -> e#3 111 | f#8(e#2, e#3) -> e#4 112 | f#9(e#2, e#3) -> e#5 113 | f#20(e#4, e#5) -> e#6 114 | f#21(e#4, e#5) -> e#7 115 | f#44(e#6, e#7) -> e#8 116 | f#45(e#6, e#7) -> e#9 117 | -- -- -- -- -- -- -- -- -- -- 118 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 119 | Facts: , , , , 120 | 'c#0 -> e#0 121 | 'c#1 -> e#1 122 | f#0(e#0, e#1) -> e#2 123 | f#1(e#0, e#1) -> e#3 124 | f#8(e#2, e#3) -> e#4 125 | f#9(e#2, e#3) -> e#5 126 | f#20(e#4, e#5) -> e#6 127 | f#21(e#4, e#5) -> e#7 128 | f#44(e#6, e#7) -> e#8 129 | f#45(e#6, e#7) -> e#9 130 | -- -- -- -- -- -- -- -- -- -- 131 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 132 | Facts: , , , , 133 | 'c#0 -> e#0 134 | 'c#1 -> e#1 135 | f#0(e#0, e#1) -> e#2 136 | f#1(e#0, e#1) -> e#3 137 | f#8(e#2, e#3) -> e#4 138 | f#9(e#2, e#3) -> e#5 139 | f#20(e#4, e#5) -> e#6 140 | f#21(e#4, e#5) -> e#7 141 | f#48(e#6, e#7) -> e#8 142 | f#49(e#6, e#7) -> e#9 143 | -- -- -- -- -- -- -- -- -- -- 144 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 145 | Facts: , , , , 146 | 'c#0 -> e#0 147 | 'c#1 -> e#1 148 | f#0(e#0, e#1) -> e#2 149 | f#1(e#0, e#1) -> e#3 150 | f#8(e#2, e#3) -> e#4 151 | f#9(e#2, e#3) -> e#5 152 | f#20(e#4, e#5) -> e#6 153 | f#21(e#4, e#5) -> e#7 154 | f#48(e#6, e#7) -> e#8 155 | f#49(e#6, e#7) -> e#9 156 | -- -- -- -- -- -- -- -- -- -- 157 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 158 | Facts: , , , , 159 | 'c#0 -> e#0 160 | 'c#1 -> e#1 161 | f#0(e#0, e#1) -> e#2 162 | f#1(e#0, e#1) -> e#3 163 | f#8(e#2, e#3) -> e#4 164 | f#9(e#2, e#3) -> e#5 165 | f#24(e#4, e#5) -> e#6 166 | f#25(e#4, e#5) -> e#7 167 | f#52(e#6, e#7) -> e#8 168 | f#53(e#6, e#7) -> e#9 169 | -- -- -- -- -- -- -- -- -- -- 170 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 171 | Facts: , , , , 172 | 'c#0 -> e#0 173 | 'c#1 -> e#1 174 | f#0(e#0, e#1) -> e#2 175 | f#1(e#0, e#1) -> e#3 176 | f#8(e#2, e#3) -> e#4 177 | f#9(e#2, e#3) -> e#5 178 | f#24(e#4, e#5) -> e#6 179 | f#25(e#4, e#5) -> e#7 180 | f#52(e#6, e#7) -> e#8 181 | f#53(e#6, e#7) -> e#9 182 | -- -- -- -- -- -- -- -- -- -- 183 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 184 | Facts: , , , , 185 | 'c#0 -> e#0 186 | 'c#1 -> e#1 187 | f#0(e#0, e#1) -> e#2 188 | f#1(e#0, e#1) -> e#3 189 | f#8(e#2, e#3) -> e#4 190 | f#9(e#2, e#3) -> e#5 191 | f#24(e#4, e#5) -> e#6 192 | f#25(e#4, e#5) -> e#7 193 | f#56(e#6, e#7) -> e#8 194 | f#57(e#6, e#7) -> e#9 195 | -- -- -- -- -- -- -- -- -- -- 196 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6, e#7, e#8, e#9} 197 | Facts: , , , , 198 | 'c#0 -> e#0 199 | 'c#1 -> e#1 200 | f#0(e#0, e#1) -> e#2 201 | f#1(e#0, e#1) -> e#3 202 | f#8(e#2, e#3) -> e#4 203 | f#9(e#2, e#3) -> e#5 204 | f#24(e#4, e#5) -> e#6 205 | f#25(e#4, e#5) -> e#7 206 | f#56(e#6, e#7) -> e#8 207 | f#57(e#6, e#7) -> e#9 -------------------------------------------------------------------------------- /theories/core/thy36.raz: -------------------------------------------------------------------------------- 1 | true -> (exists x, y . Q(x,y)); 2 | Q(x,y) -> (exists x, y . Q1(x,y) ) | (exists x, y . Q2(x,y)); 3 | 4 | 5 | Q1(x,y) -> (exists x, y . Q11(x,y) ) | (exists x, y . Q12(x,y)); 6 | Q2(x,y) -> (exists x, y . Q21(x,y) ) | (exists x, y . Q22(x,y)); 7 | 8 | Q11(x,y) -> (exists x, y . Q111(x,y) ) | (exists x, y . Q112(x,y)); 9 | Q12(x,y) -> (exists x, y . Q121(x,y) ) | (exists x, y . Q122(x,y)); 10 | Q21(x,y) -> (exists x, y . Q211(x,y) ) | (exists x, y . Q212(x,y)); 11 | Q22(x,y) -> (exists x, y . Q221(x,y) ) | (exists x, y . Q222(x,y)); 12 | 13 | Q111(x,y) -> (exists x, y . Q1111(x,y) ) | (exists x, y . Q1112(x,y)); 14 | Q112(x,y) -> (exists x, y . Q1121(x,y) ) | (exists x, y . Q1122(x,y)); 15 | Q121(x,y) -> (exists x, y . Q1211(x,y) ) | (exists x, y . Q1212(x,y)); 16 | Q122(x,y) -> (exists x, y . Q1221(x,y) ) | (exists x, y . Q1222(x,y)); 17 | 18 | Q211(x,y) -> (exists x, y . Q2111(x,y) ) | (exists x, y . Q2112(x,y)); 19 | Q212(x,y) -> (exists x, y . Q2121(x,y) ) | (exists x, y . Q2122(x,y)); 20 | Q221(x,y) -> (exists x, y . Q2211(x,y) ) | (exists x, y . Q2212(x,y)); 21 | Q222(x,y) -> (exists x, y . Q2221(x,y) ) | (exists x, y . Q2222(x,y)); -------------------------------------------------------------------------------- /theories/core/thy37.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy37.model -------------------------------------------------------------------------------- /theories/core/thy37.raz: -------------------------------------------------------------------------------- 1 | R6(fv6) -> R3(fv6); 2 | R0(c1()) -> exists bv0. exists bv1. R8(c4(),bv1,bv1); 3 | R4(fv7,fv6) -> (false & exists bv0. R3(fv6)); 4 | true -> R6(c4()); 5 | R3(fv5) -> (R4(c6(),fv5) & R9(fv5)); 6 | R8(c3(),c9(),c9()) -> R7(c4(),c6()); -------------------------------------------------------------------------------- /theories/core/thy38.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'c#0, 'c#1, 'c#2 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy38.raz: -------------------------------------------------------------------------------- 1 | exists x. exists y. exists z. R(x,y,z); 2 | a = b; -------------------------------------------------------------------------------- /theories/core/thy39.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4, e#5, e#6} 2 | Facts: , 3 | 'c#0 -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f(e#3) -> e#4 8 | f(e#4) -> e#5 9 | f(e#5) -> e#6 -------------------------------------------------------------------------------- /theories/core/thy39.raz: -------------------------------------------------------------------------------- 1 | exists x . Q(f(x)); 2 | Q(x) -> R(x, f(f(f(f(f(x)))))); -------------------------------------------------------------------------------- /theories/core/thy4.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'a, 'b -> e#0 -------------------------------------------------------------------------------- /theories/core/thy4.raz: -------------------------------------------------------------------------------- 1 | 'a = 'b; -------------------------------------------------------------------------------- /theories/core/thy40.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1, e#2, e#3, e#4} 2 | Facts: , , , , 3 | 'c#0 -> e#0 4 | f(e#0) -> e#1 5 | f(e#1) -> e#2 6 | f(e#2) -> e#3 7 | f#0(e#1) -> e#4 -------------------------------------------------------------------------------- /theories/core/thy40.raz: -------------------------------------------------------------------------------- 1 | true -> exists a . Q(f(a)); 2 | Q(f(x)) -> R(x,f(x)); 3 | Q(x) -> R(x, f(f(x))); 4 | R(x, f(f(x))) -> exists b . S(b) & P(x); -------------------------------------------------------------------------------- /theories/core/thy41.model: -------------------------------------------------------------------------------- 1 | Domain: {} 2 | Facts: 3 | -------------------------------------------------------------------------------- /theories/core/thy41.raz: -------------------------------------------------------------------------------- 1 | // This produces an empty model (no formulae) -------------------------------------------------------------------------------- /theories/core/thy42.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salmans/rusty-razor/193cca120cad69aebacf50c46d956a045c66f338/theories/core/thy42.model -------------------------------------------------------------------------------- /theories/core/thy42.raz: -------------------------------------------------------------------------------- 1 | // This theory ensures that integrity axioms for functions are respected. 2 | exists x, y, z . P(x, y) & f(x, y) = z; 3 | exists x, y, z . Q(x, y) & f(x, y) = z; 4 | P(x1, y1) & Q(x2, y2) -> x1 = x2 & y1 = y2; 5 | P(x1, y1) & Q(x2, y2) & f(x1, y1) = f(x2, y2) -> false; 6 | -------------------------------------------------------------------------------- /theories/core/thy43.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: , , , 3 | 'a -> e#0 4 | 'b -> e#1 -------------------------------------------------------------------------------- /theories/core/thy43.raz: -------------------------------------------------------------------------------- 1 | P('a) and Q('b); 2 | P(x) and Q(y) iff P(y) and Q(x); -------------------------------------------------------------------------------- /theories/core/thy44.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'a -> e#0 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {e#0} 6 | Facts: , 7 | 'a -> e#0 -------------------------------------------------------------------------------- /theories/core/thy44.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) or Q(y) iff Q(x) or R(y); -------------------------------------------------------------------------------- /theories/core/thy45.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: , , 3 | 'a -> e#0 4 | 'b -> e#1 5 | -- -- -- -- -- -- -- -- -- -- 6 | Domain: {e#0} 7 | Facts: , 8 | 'a, 'b -> e#0 -------------------------------------------------------------------------------- /theories/core/thy45.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | Q('b); 3 | P(x) & Q(y) -> x = y | R(x, y); -------------------------------------------------------------------------------- /theories/core/thy46.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , 3 | 'c#0, 'c#1 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy46.raz: -------------------------------------------------------------------------------- 1 | ?x . P(x); 2 | ?x . Q(x); 3 | 4 | P(x) & Q(y) -> x = y & R(x, y); -------------------------------------------------------------------------------- /theories/core/thy47.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , , , 3 | 'c#0, 'c#1, 'c#2, 'c#3 -> e#0 -------------------------------------------------------------------------------- /theories/core/thy47.raz: -------------------------------------------------------------------------------- 1 | ?x . O(x); 2 | ?x . P(x); 3 | ?x . Q(x); 4 | ?x . R(x); 5 | 6 | O(w) & P(x) & Q(y) & R(z) -> w = x & x = y & w = z & S(w, x, y, z); -------------------------------------------------------------------------------- /theories/core/thy5.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: 3 | 'a -> e#0 4 | 'b -> e#1 -------------------------------------------------------------------------------- /theories/core/thy5.raz: -------------------------------------------------------------------------------- 1 | true implies P('a, 'b); -------------------------------------------------------------------------------- /theories/core/thy6.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0, e#1} 2 | Facts: 3 | 'a -> e#0 4 | f(e#0) -> e#1 -------------------------------------------------------------------------------- /theories/core/thy6.raz: -------------------------------------------------------------------------------- 1 | P(f('a)); -------------------------------------------------------------------------------- /theories/core/thy7.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , , 3 | 'a -> e#0 -------------------------------------------------------------------------------- /theories/core/thy7.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | P(x) implies Q(x); 3 | Q(x) implies R(x); -------------------------------------------------------------------------------- /theories/core/thy8.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: 3 | 'a -> e#0 4 | -- -- -- -- -- -- -- -- -- -- 5 | Domain: {e#0} 6 | Facts: 7 | 'b -> e#0 8 | -- -- -- -- -- -- -- -- -- -- 9 | Domain: {e#0} 10 | Facts: 11 | 'c -> e#0 -------------------------------------------------------------------------------- /theories/core/thy8.raz: -------------------------------------------------------------------------------- 1 | P('a) or Q('b) or R('c); -------------------------------------------------------------------------------- /theories/core/thy9.model: -------------------------------------------------------------------------------- 1 | Domain: {e#0} 2 | Facts: , 3 | 'a, 'b -> e#0 -------------------------------------------------------------------------------- /theories/core/thy9.raz: -------------------------------------------------------------------------------- 1 | P('a); 2 | Q('b); 3 | P(x) and Q(y) implies x = y; -------------------------------------------------------------------------------- /theories/examples/golden-lion.raz: -------------------------------------------------------------------------------- 1 | // A person `x` cannot be both "black of hair" and "golden head" 2 | ~(BlackOfHair(x) & GoldenHead(x)); 3 | 4 | // Traditionally, a Baratheon child `y` inherited his/her father's (`x`'s) family name 5 | Baratheon(x) & father(y) = x -> Baratheon(y); 6 | 7 | // King Robert Baratheon is black of hair 8 | Baratheon('robert) & BlackOfHair('robert); 9 | 10 | // King Robert is Joffrey's father 11 | father('joffrey) = 'robert; 12 | 13 | // Joffrey has golden hair 14 | GoldenHead('joffrey); 15 | 16 | // Ned Stark's discovery (every Baratheon `x` is black of hair) 17 | Baratheon(x) -> BlackOfHair(x); -------------------------------------------------------------------------------- /theories/examples/grandpa.raz: -------------------------------------------------------------------------------- 1 | // Source: Alloy Analyzer Examples 2 | Man(p) implies Person(p); 3 | Woman(p) implies Person(p); 4 | 5 | Man(p) and Woman(p) implies false; 6 | 7 | mother(p) = w implies Person(p) and Woman(w); 8 | father(p) = m implies Person(p) and Man(m); 9 | 10 | wife(m) = w implies Man(m) and Woman(w); 11 | husband(w) = m implies Man(m) and Woman(w); 12 | 13 | mother(p) = m implies Ancestor(p, m); 14 | father(p) = f implies Ancestor(p, f); 15 | 16 | // Biology: 17 | Ancestor(p, a) and father(a) = f implies Ancestor(p, f); 18 | Ancestor(p, a) and mother(a) = m implies Ancestor(p, m); 19 | 20 | Ancestor(p, p) implies false; 21 | 22 | 23 | // Terminology: 24 | wife(m) = w implies husband(w) = m; 25 | husband(w) = m implies wife(m) = w; 26 | 27 | // Social Convention: 28 | wife(p) = w and Ancestor(p, w) implies false; 29 | husband(p) = h and Ancestor(p, h) implies false; 30 | 31 | father(p) = f implies Parent(p, f); 32 | mother(p) = m implies Parent(p, m); 33 | father(p) = f and wife(f) = w implies Parent(p, w); 34 | mother(p) = m and husband(m) = h implies Parent(p, h); 35 | 36 | Grandpas(p, g) implies exists p_ . Parent(p_, g) and Parent(p, p_) and Man(g); 37 | Parent(p, p_) and Parent(p_, g) and Man(g) implies Grandpas(p, g); 38 | 39 | Parent(p, p_) implies father(p) = p_ or mother(p) = p_ or (exists f . father(p) = f and wife(f) = p_) or (exists m . mother(p) = m and husband(m) = p_); 40 | 41 | 42 | // Own Grandpa 43 | exists p. Grandpas(p, p); -------------------------------------------------------------------------------- /theories/examples/hodor-linear.raz: -------------------------------------------------------------------------------- 1 | // Wyllis hears "Hold the Door" (at time `t`), then he becomes Hodor in the next point of time 2 | HoldTheDoor(t) -> Hodor(next(t)); 3 | 4 | // Hodor, after turning into Hodor at time "t", holds the Door at some time "tt" in future ("tt > t") 5 | Hodor(t) -> ? tt . HoldTheDoor(tt) & After(t, tt); 6 | 7 | // These are the rules by which time progresses linearly: 8 | // (1) a point of time "t1" that is the next of "t0" (i.e., "next(t0)") is a point of 9 | // time after "t0" ("t1 > t0") 10 | next(t0) = t1 -> After(t0, t1); 11 | 12 | // (2) if a point of time "t1" is after "t0", it is either immediately after "t0" (i.e., "next(t0)") 13 | // or there is some point of time "t2" that is immediately after "t0" and before "t1". 14 | After(t0, t1) -> next(t0) = t1 | ? t2 . next(t0) = t2 & After(t2, t1); 15 | 16 | // And we know at some point of time (namely "'t_hodor"), Wyllis became Hodor 17 | Hodor('t_hodor); -------------------------------------------------------------------------------- /theories/examples/hodor-time-loop.raz: -------------------------------------------------------------------------------- 1 | HoldTheDoor(t) -> Hodor(next(t)); 2 | 3 | Hodor(t) -> ? tt . HoldTheDoor(tt) & After(t, tt); 4 | 5 | next(t0) = t1 -> After(t0, t1); 6 | After(t0, t1) -> (next(t0) = t1) | ? t2 . next(t0) = t2 & After(t2, t1); 7 | 8 | // Hold the door moment only happens at 't_hodor 9 | HoldTheDoor(t) -> t = 't_hodor; 10 | 11 | Hodor('t_hodor); -------------------------------------------------------------------------------- /theories/examples/lannisters.raz: -------------------------------------------------------------------------------- 1 | Lannister(x) and Father(x, y) implies Lannister(y); 2 | Debt(x, y) implies Pay(x, y) or not Lannister(x); 3 | 4 | Lannister('tywin) and Father('tywin, 'tyrion) and Debt('tyrion, 'coin); 5 | 6 | // Verify 7 | not Pay('tyrion, 'coin); -------------------------------------------------------------------------------- /theories/examples/toy.raz: -------------------------------------------------------------------------------- 1 | // All cats love all toys: 2 | forall c, t . (Cat(c) and Toy(t) implies Loves(c, t)); 3 | 4 | // All squishies are toys: 5 | forall s . (Squishy(s) implies Toy(s)); 6 | 7 | Cat('duchess); // Duchess is a cat 8 | Squishy('ducky); // Ducky is a squishy -------------------------------------------------------------------------------- /theories/examples/valar-morghulis.raz: -------------------------------------------------------------------------------- 1 | // All men must die: 2 | forall x. (Man(x) implies MustDie(x)); 3 | 4 | // Ser Gregor is a man: 5 | Man('gregor); 6 | 7 | // Razor thinks Ser Gregor must die! --------------------------------------------------------------------------------