1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
# Felix
Felix is an experimental [proof assistant](https://en.wikipedia.org/wiki/Proof_assistant)
based on [set theory](https://en.wikipedia.org/wiki/Set_theory) (and [classical logic](https://en.wikipedia.org/wiki/Classical_logic)).
It uses a [controlled language](https://en.wikipedia.org/wiki/Controlled_natural_language)
embedded into subset of LaTeX as input format, supporting [literate formalization](https://en.wikipedia.org/wiki/Literate_programming).
## Example
```latex
\begin{theorem}[Cantor]\label{cantor}
There exists no surjection from $A$ to $\pow{A}$.
\end{theorem}
\begin{proof}
Suppose not.
Consider a surjection $f$ from $A$ to $\pow{A}$.
Let $B = \{a \in A \mid a\notin f(a)\}$.
Then $B\in\pow{A}$.
Take an element $a'$ of $A$ such that $f(a') = B$ by \cref{surj}.
Now $a' \in B$ iff $a' \notin f(a') = B$.
Contradiction.
\end{proof}
```
## Development
### Prerequisites
We rely on [Vampire](https://vprover.github.io/) to discharge proof tasks.
You may also need to install `zlib` on certain operating systems.
For example, on Ubuntu you can install the development version of
`zlib` by running `sudo apt install zlib1g-dev`.
#### Obtaining Vampire
Felix and the included library are tested with Vampire at
[commit 367732686](https://github.com/vprover/vampire/commit/3677326861181f990ce3ef461e90471ba9749225).
Its version output describes it as a “Release build”; that is the build mode,
not the tagged Git release.
Build that commit by following the instructions in Vampire’s `README.md` and make sure that the resulting binary is available as `vampire` on your `$PATH` (running `vampire --version` should identify the tested revision).
If you have multiple versions of Vampire installed, you can export the `$NAPROCHE_ZF_VAMPIRE` environment variable to choose a specific version,
e.g. by adding
```
export NAPROCHE_ZF_VAMPIRE="/absolute/path/to/vampire"
```
to your shell configuration.
### Building
This project uses [Stack](http://haskellstack.org/) to manage Haskell libraries and GHC.
You can install Stack and other Haskell tooling using [GHCup](https://www.haskell.org/ghcup/). Follow the GHCup install instructions and then use `ghcup tui` to install Stack.
Stack should install the correct GHC version when you first try to build the software. You can also use GHCup to install the Haskell Language Server, which enables IDE features in some text editors. For VS Code you also need to install the [Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell).
You can build the project using `stack build` in the root directory of this project.
Supported platforms are Linux x86-64 and macOS AArch64/x86-64.
Support for Windows is currently untested, but using [WSL2](https://learn.microsoft.com/en-us/windows/wsl/) may work.
### Checking individual files
After running `stack build` you can run the program with `stack exec zf -- <FILENAME>` in the root directory of this project.
The double hyphens `--` separate the arguments of `stack`
from the arguments of the proof checker. Here's an example invocation:
```
stack exec zf -- library/set.tex --log
```
Successful commands exit with status 0. A rejected verification exits with
status 1, while a prover or protocol failure exits with status 2. Diagnostics
are written to stderr. A successful run with `Omitted` proof steps reports
their locations as explicit proof gaps and includes them in its authorization
summary.
Verification always opens a disposable SQLite store. By default Felix uses
the platform's XDG cache directory; `--store PATH` selects an explicit database
file whose parent directory already exists, while `--fresh` uses a temporary
store that is removed after the command. A store is reusable only when its
two-field cache-epoch/theory identity matches this build. An incompatible or
damaged store is never migrated or silently rebuilt: use `--fresh`, select a
new path, or remove the disposable database and verify again.
The command model has three modes. `--version` prints the version,
`--parseonly` performs authority-free source discovery and parsing without
opening SQLite or invoking Vampire, and ordinary invocation verifies through
the typed checker. Both paths load the packaged final prelude's syntax; Verify
also acquires its sealed semantics. Ordinary modules therefore need no source
import for the prelude dependency.
Without `--jobs`, verification uses approximately one third of the detected
logical processor count: `max 1 ((detected + 1) div 3)`. A positive `--jobs N`
is an exact bound for live module checkers and Vampire invocations. Each
invocation runs a two-worker CASC portfolio. `-t` and `-m` bound each Vampire
request's time and memory.
Source fixity declarations are line-leading pragmas such as `%! infixl 4`.
User declarations may use literal levels 0 through 7; the two higher internal
levels are reserved. Verification keeps compact proof-independent theorem
authority separate from exact proof/declaration validation. Its authorization
summary lists only direct source axioms and syntactic `Omitted` sites, not
facts that merely inherit their safety.
`--html DIRECTORY` and `--dump DIRECTORY` observe the same verification.
Dump files are the exact Vampire requests that actually ran and the directory
must be absent or empty. HTML is uncached and is published sequentially in
source order only after complete semantic success; a later output failure
reports the already committed output prefix together with the successful
authorization summary.
Declarations append atomically and modules expose only sealed syntax and
semantic interfaces. After an ordinary later failure, the successfully checked
declaration prefix remains reusable but the incomplete module has no root.
Compatible stores can reuse rebound parsed artifacts, exact validation, and
complete module roots; source remains authoritative, so an edited proof or
interface is revalidated at the corresponding content boundary.
For a list of all options run `stack exec zf -- --help`.
### Checking the entire standard library
Run `make lib` to check the file `library/everything.tex`.
### Compiling the PDF of the standard library
```
cd latex && xelatex stdlib.tex
```
### Setting up other formalization environments
When looking for imported files, the following list of base directories is considered (in descending priority):
- the current working directory `.`
- the directory `./library`, which you can override with the environment variable `NAPROCHE_LIB`
- the directory `./debug`
### Running the tests
There are a few [golden tests](https://hackage.haskell.org/package/tasty-golden-2.3.4/docs/Test-Tasty-Golden.html)
that compare the output of the program to previously accepted output.
Run the non-accepting test suite with
```
make test
```
It fails if the output differs from the existing golden files. After reviewing
an intentional output change, explicitly update the golden files with
```
make golden
```
### Building Haskell documentation
Running `stack haddock` will build the project together with its documentation,
placing the rendered HTML files into `haddocks/`.
### Profiling
The following makes sure that stack uses a dedicated directory
to cache the profiled version of all dependencies. Otherwise
switching between profiled and unprofiled builds will cause
lots of recompilation.
```
stack --work-dir .stack-work-profile --profile build
```
Basic time profiling:
```
stack --work-dir .stack-work-profile exec --profile zf -- library/ordinal.tex +RTS -p
```
If you have [ghc-prof-flamegraph](https://hackage.haskell.org/package/ghc-prof-flamegraph) installed
(e.g. after running `cabal install ghc-prof-flamegraph`), you can generate an interactive `zf.prof.svg` from the `.prof` file
by running the following:
```
ghc-prof-flamegraph zf.prof
```
### Debugging
Logical rejections and indeterminate prover results include the failed TPTP task; protocol and process failures include protocol or process diagnostics.
Use `--dump` to write the exact requests sent to Vampire. A failed run retains
only the requests that were executed before the failure. The destination must
be absent or empty; Felix owns the complete directory for that run. `make dump`
creates and prints a fresh temporary destination unless `DUMP_DIR` is supplied.
```sh
dump_dir=$(mktemp -d "${TMPDIR:-/tmp}/felix-dump.XXXXXX")
printf 'Writing dump to %s\n' "$dump_dir"
stack exec zf -- file-that-fails.tex --dump "$dump_dir"
```
You can then run Vampire manually on one of those files.
```sh
vampire --input_syntax tptp --mode casc "$dump_dir/1.p"
```
|