packages feed

GenZ-0.1.0.0: README.md

# GenZ - A Generic Sequent Calculus Prover using the Zipper

Covering the following logics:

- Propositional: CPL, IPL
- Modal: K, K4, K45, T, D, D4, D45, S4, GL

## Web interface

You can use the prover online at <https://tools.malv.in/genz-web/>.

## Formula Syntax

Both ASCII and Unicode symbols are allowed. Here are some example formulas:

- `[](p -> []p) -> []p`
- `[]p & []q <-> []p | []q` which unfolds to `((☐p ∧ ☐q) → (☐p v ☐q)) ∧ ((☐p v ☐q) → (☐p ∧ ☐q))`.
- `<><>p -> <>p` which unfolds to `(☐((☐(p → ⊥) → ⊥) → ⊥) → ⊥) → (☐(p → ⊥) → ⊥)`

| Symbols                  | Meaning                 | Note                             |
|--------------------------|-------------------------|----------------------------------|
| `p`, `q`, `bla`, ...     | atomic propositions     |                                  |
| `true`, ⊤                | top (constant true)     |                                  |
| `false`, ⊥               | bottom (constant false) |                                  |
| `!`, `~`, `¬`            | negation (not)          | abreviation for `... -> false`   |
| `&`                      | conjunction (and)       | primitive                        |
| `\|`, `v`                | disjunction (or)        | primitive                        |
| `->`, `-->`, `=>`, `→`,  | implication (if-then)   | primitive                        |
| `<->`, `<-->`, `<=>`,`↔` | bi-implication (iff)    | abbreviation using `->` and `&`. |
| `<>`, `<a>`, `◇`         | diamond (possible)      | abbreviation for `~ [] ~ ...`    |
| `[]`, `[a]`, `☐`, `◻`    | box (necessary)         | primitive                        |

## Building

You should have the Haskell build tool `stack` installed, via [ghcup](https://www.haskell.org/ghcup/).
For proof visualization, optonally you may want to install [`graphviz`](https://graphviz.org/).

To build the project run `stack build`.

## CLI program

To install the `genz` and the `genz-web` binaries, just run `stack install` in this repository.

### CLI examples

We can prove a formula given directly with `-F` or from a file with `-f` or from standard input with `--stdin`.
By default the modal logic `K` is used.

```
$ genz -F "[]p -> [][]p"
False

$ echo "p | ~p" > example.txt
$ genz -f example.txt
True
$ genz -f example.txt --logic IPL
False

$ echo "[][]p -> [][][][]p" | genz --stdin --logic S4
True
```

Get help:

```
$ genz --help
genz - a generic sequent calculus prover with zippers

Usage: genz ((-F|--formula FORMULA) | (-f|--file FILE) | (-s|--stdin))
            [-i|--input INPUT] [-t|--tree] [-n|--negate] [-d|--debug]
            [-p|--proofFormat FORMAT] [-l|--logic LOGIC]

  Prove the given FORMULA or the formula in FILE or STDIN.

Available options:
  -F,--formula FORMULA     Formula
  -f,--file FILE           Input file
  -s,--stdin               Read from stdin
  -i,--input INPUT         Input format: single, tptp (default: single)
  -t,--tree                Use standard trees (default is to use zippers).
  -n,--negate              Negate the input formula.
  -d,--debug               Print additional debug information.
  -p,--proofFormat FORMAT  Proof format: none, plain, buss, size (default: none)
  -l,--logic LOGIC         Logic to use: CPL, IPL, D, D4, D45, GL, K, K4, K45,
                           S4, T (default: K)
  -h,--help                Show this help text
```

Benchmarking zipper vs tree representation (for an unprovable formula):
```
$ /usr/bin/time -f%E genz -F "[]([]p->p) -> [][][][][][][][][][][][][][][][][][]p" --logic K4
False
0:00.01
$ /usr/bin/time -f%E genz -F "[]([]p->p) -> [][][][][][][][][][][][][][][][][][]p" --logic K4 --tree
False
0:03.44
```

## Haskell examples

You can use `stack ghci` to run examples like this:

    stack ghci lib/Logic/Modal/K.hs lib/FormM.hs

    ghci> FormM.multiVerK 3
    (☐(3 → (4 → (5 → 1))) → (☐3 → (☐4 → (☐5 → ☐1))))
    ghci> FormM.extraAtK 3
    (☐(3 → (4 → (5 → (2 → 1)))) → (☐3 → (☐4 → (☐5 → ☐1))))

    ghci> isProvableT k (FormM.multiVerK 3)
    True
    ghci> isProvableZ k (FormM.multiVerK 3)
    True
    ghci> isProvableT k (FormM.extraAtK 3)
    False
    ghci> isProvableZ k (FormM.extraAtK 3)
    False

In the above `k` is `Logic.Modal.K.k :: Logic`, i.e. the proof system.

## Defining Your Own Logic

A `Logic` in GenZ is a collection of **safeRules** (invertible, applied greedily) and **unsafeRules** (non-invertible, requiring backtracking and possibly loopchecks):
```haskell
data Logic f = Log { name :: String
                   , safeRules :: [Rule f]
                   , unsafeRules :: [Rule f] }
```

Each rule takes a history, the current sequent, and a principal formula, and returns possibly multiple branches:
```haskell
type Rule f = History f -> Sequent f -> Either f f -> [(RuleName, [Sequent f])]
```

As an example, CPL has only safe rules:
```haskell
classical :: Logic FormP
classical = Log { name = "CPL"
                , safeRules   = [leftBot, isAxiom, replaceRule safeCPL]
                , unsafeRules = []
                }

safeCPL :: Either FormP FormP -> [(RuleName,[Sequent FormP])]
safeCPL (Left (ConP f g))   = [("∧L", [Set.fromList [Left g, Left f]])]
safeCPL (Left (DisP f g))   = [("vL", [Set.singleton (Left f), Set.singleton (Left g)])]
safeCPL (Left (ImpP f g))   = [("→L", [Set.singleton (Right f), Set.singleton (Left g)])]
safeCPL (Right (ConP f g))  = [("∧R", [Set.singleton (Right f), Set.singleton (Right g)])]
safeCPL (Right (DisP f g))  = [("vR", [Set.fromList [Right g, Right f]])]
safeCPL (Right (ImpP f g))  = [("→R", [Set.fromList [Right g, Left f]])]
safeCPL _                   = []
```

For logics that require backtracking (e.g. IPL, modal logics), place non-invertible rules in `unsafeRules`. See `lib/Logic/` for all implemented calculi.
Implementation for loopchecks can be found in `lib/Logic/Propositional/IPL.hs` for reference.

To try your own logic, create a new module under `lib/Logic/`, following the structure above. Then load it in ghci. Below is an example of a modal logic. You can also define your own language and your own formula examples.
```
$ stack ghci lib/General.hs lib/FormM.hs lib/Logic/YourLogic.hs
ghci> isProvableZ yourLogic (ImpM (AtM '1') (AtM '1'))
True
```

## LaTeX output

The prover can generate code for [bussproofs](https://ctan.org/pkg/bussproofs).

For example

    stack exec genz -- -F "□(□p → p) → □p" -l GL -p buss

will print the LaTeX code for the following proof:

![](doc/GL-example.png)

When using *ghci* instead of the `genz` executable, the following
command can be used to write the code into `temp.tex` and then run
`pdflatex` on it directly:

    stack ghci lib/Logic/Modal/GL.hs lib/FormM.hs

    ghci> texFile . head . proveZ gl $ ImpM (Box (ImpM (Box (AtM "p")) (AtM "p"))) (Box (AtM "p"))

## The Zipper Data Structure

GenZ uses the **zipper**, a data structure introduced by [Huet (1997)](https://doi.org/10.1017/S0956796897002864), to efficiently navigate and modify proof trees during proof search. The idea is to split a tree into a **focus** (the subtree being worked on) and a **path** (everything above and around it), enabling constant-time navigation and modification without reconstructing the entire tree.

A standard tree and its zipper in Haskell:
```haskell
data Tree a    = Node a [Tree a]
data ZipTree a = ZT (Tree a) (Path a)
data Path a    = Top | Step a (Path a) [Tree a] [Tree a]
```

A `ZipTree` focuses on a subtree, while `Path` records the parent node, the path further up, and the left/right siblings. For example, in this tree with focus at node `2`:
```
      0
    / | \ \
   1 [2] 3  4
     |      / \
     5     6   7

ZT (Node 2 [Node 5 []])
   (Step 0 Top [Node 1 []] [Node 3 [], Node 4 [Node 6 [], Node 7 []]])
```

GenZ applies this to proof search trees via `ZipProof` and `ZipPath`:
```haskell
data ZipProof f = ZP (Proof f) (ZipPath f)
data ZipPath  f = Top | Step (Sequent f) RuleName (ZipPath f) [Proof f] [Proof f]
```

This makes backtracking efficient: when a rule application fails, GenZ moves the focus back up and tries a different rule without rebuilding the tree.

## Tests

To run all tests locally, run `stack test`.
This should not take more than five minutes.

The tests are also run automatically for each commit,
see <https://github.com/XiaoshuangYang999/GenZ/actions> for results.

## LWB and ILTP Benchmarks

See the `benchmarks` folder for bash scripts to run GenZ on these benchmarks.

## Custom Benchmarks (`bench` folder)

You should have LaTeX and [pandoc](https://pandoc.org/) installed.

To run the benchmarks for a small selection of formulas,
run `make bench/runtime.pdf` and `make bench/memory.pdf`.
The runtime benchmark will take around 30 minutes, the memory benchmark less than one minute.

To run benchmarks on a larger set of formulas,
run `make bench/runtime-all.pdf` and `make bench/memory-all.pdf`.
Note: this runtime benchmark will take multiple hours.

Example results are available at
<https://github.com/XiaoshuangYang999/GenZ/releases>.

## References

The code in this repository was originally developed as part of the following master's thesis:

- Xiaoshuang Yang: *Sequent Calculus with Zippers*.
  University of Amsterdam, 2024.
  <https://eprints.illc.uva.nl/id/eprint/2354>

The original code from the thesis can be found in the `thesis-version` branch.
Ongoing updates and improvements are included in the `main` branch.