hylogic 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hylogic-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Marko Manninen
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.
hylogic-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,352 @@
1
+ Metadata-Version: 2.4
2
+ Name: hylogic
3
+ Version: 1.0.0
4
+ Summary: Propositional and first-order logic in Hy: truth tables, arguments, models and a resolution prover
5
+ Author-email: Marko Manninen <elonmedia@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/markomanninen/hylogic
8
+ Keywords: hylang,lisp,logic,propositional logic,first-order logic,truth table,theorem prover,resolution,unification
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Education
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Lisp
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: hy>=1.0
20
+ Provides-Extra: test
21
+ Requires-Dist: pytest>=7; extra == "test"
22
+ Requires-Dist: ipython; extra == "test"
23
+ Requires-Dist: ipykernel; extra == "test"
24
+ Requires-Dist: nbclient; extra == "test"
25
+ Requires-Dist: nbformat; extra == "test"
26
+ Requires-Dist: mcp<2,>=1.2; python_version >= "3.10" and extra == "test"
27
+ Provides-Extra: notebook
28
+ Requires-Dist: ipython; extra == "notebook"
29
+ Provides-Extra: mcp
30
+ Requires-Dist: mcp<2,>=1.2; extra == "mcp"
31
+ Provides-Extra: z3
32
+ Requires-Dist: z3-solver; extra == "z3"
33
+ Dynamic: license-file
34
+
35
+ # Hylogic
36
+
37
+ Propositional and first-order logic in [Hy](https://hylang.org), a Lisp that
38
+ compiles to Python. Write formulas as Lisp code with the usual logical
39
+ symbols, then check them with truth tables, evaluate them in a finite model,
40
+ test whether an argument is valid, or prove them with a small resolution
41
+ prover that prints its proofs.
42
+
43
+ ```hy
44
+ (import hylogic [truth-table prove])
45
+
46
+ (print (truth-table '(→ (∧ P (→ P Q)) Q)))
47
+ ```
48
+
49
+ ```
50
+ P │ Q │ (P ∧ (P → Q)) → Q
51
+ ──┼───┼──────────────────
52
+ ⊤ │ ⊤ │ ⊤
53
+ ⊤ │ ⊥ │ ⊤
54
+ ⊥ │ ⊤ │ ⊤
55
+ ⊥ │ ⊥ │ ⊤
56
+ ```
57
+
58
+ Hylogic is meant for learning and experimenting: the code is short enough to
59
+ read, and every step of a proof can be printed. For serious proofs, use
60
+ [Lean](https://lean-lang.org/) or an automated prover such as Vampire or E.
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install hylogic
66
+ ```
67
+
68
+ Hylogic needs Python 3.9 or newer and Hy 1.0 or newer. To run the tests and
69
+ the tour from a clone:
70
+
71
+ ```bash
72
+ pip install -e ".[test]"
73
+ pytest
74
+ hy examples/tour.hy
75
+ ```
76
+
77
+ ## Formulas
78
+
79
+ A formula is quoted Hy code. Atoms are propositions (`P`) or predicates
80
+ applied to terms (`(Loves x (mother x))`). Every connective has a symbol and
81
+ an ASCII name, and both can be mixed freely:
82
+
83
+ | Symbol | ASCII | Name | Arguments |
84
+ |---|---|---|---|
85
+ | `¬` | `not` | Negation | 1 |
86
+ | `∧` | `and` | Conjunction | any number |
87
+ | `∨` | `or` | Disjunction | any number |
88
+ | `↑` | `nand` | Nonconjunction | any number |
89
+ | `↓` | `nor` | Nondisjunction | any number |
90
+ | `⊕` `↮` | `xor` | Exclusive or, true for an odd number of true arguments | any number |
91
+ | `≡` | `eqv` | Equivalence, true when all arguments agree | any number |
92
+ | `≢` | `neqv` | Nonequivalence | any number |
93
+ | `↔` | `<=>` | Biconditional | 2 |
94
+ | `→` | `=>` | Material implication | 2 |
95
+ | `←` | `<=` | Converse implication | 2 |
96
+ | `↛` | `nimp` | Material nonimplication | 2 |
97
+ | `↚` | `cnimp` | Converse nonimplication | 2 |
98
+ | `∀` | `forall` | Universal quantifier, `(∀ [x y] body)` | |
99
+ | `∃` | `exists` | Existential quantifier | |
100
+ | `⊤` `⊥` | `true` `false` | The constants | |
101
+
102
+ `pretty` prints a formula in the usual notation:
103
+
104
+ ```hy
105
+ (import hylogic [pretty])
106
+ (pretty '(forall [x] (=> (Man x) (Mortal x)))) ; "∀x ((Man x) → (Mortal x))"
107
+ ```
108
+
109
+ ## Truth tables
110
+
111
+ ```hy
112
+ (import hylogic.truth [truth-table tautology? satisfiable? equivalent? entails? counterexample])
113
+
114
+ (tautology? '(=> (=> (=> p q) p) p)) ; Peirce's law: True
115
+ (equivalent? '(¬ (∧ P Q)) '(∨ (¬ P) (¬ Q))) ; De Morgan: True
116
+ (entails? ['(→ P Q) '(¬ Q)] '(¬ P)) ; modus tollens: True
117
+ (counterexample ['(→ P Q) 'Q] 'P) ; {"P" False "Q" True}
118
+ ```
119
+
120
+ `truth-table` takes several formulas and shows them side by side. In a
121
+ Jupyter notebook it renders as an HTML table. `(print (connective-tables))`
122
+ prints the tables of all thirteen connectives.
123
+
124
+ ## Arguments
125
+
126
+ `defargument` takes premises and a conclusion as written, without quoting,
127
+ and decides validity: by truth table when the argument is propositional, and
128
+ with the prover when it has quantifiers or predicates.
129
+
130
+ ```hy
131
+ (require hylogic.argument [defargument])
132
+
133
+ (defargument modus-tollens
134
+ :premises [(→ P Q) (¬ Q)]
135
+ :conclusion (¬ P)
136
+ :meanings {P "It rains" Q "The street is wet"})
137
+
138
+ (print (.explain modus-tollens))
139
+ ```
140
+
141
+ ```
142
+ P → Q
143
+ ¬Q
144
+ ───────
145
+ ∴ ¬P
146
+
147
+ P: It rains
148
+ Q: The street is wet
149
+
150
+ Valid, by truth table.
151
+ ```
152
+
153
+ An invalid argument comes with a counterexample, and `Argument` builds the
154
+ same object from quoted formulas:
155
+
156
+ ```hy
157
+ (import hylogic [Argument])
158
+ (.explain (Argument ['(→ P Q) 'Q] 'P))
159
+ ; Invalid, by truth table.
160
+ ; Counterexample: P = ⊥, Q = ⊤
161
+ ```
162
+
163
+ ## Models
164
+
165
+ `evaluate` finds the truth value of a formula in a model. Predicates are
166
+ Python functions or collections of the tuples they hold for, and quantifiers
167
+ range over a finite domain:
168
+
169
+ ```hy
170
+ (import hylogic [evaluate])
171
+
172
+ (setv model {"Man" #{"socrates" "plato"}
173
+ "Mortal" #{"socrates" "plato" "fido"}})
174
+ (evaluate '(∀ [x] (→ (Man x) (Mortal x))) model ["socrates" "plato" "fido"]) ; True
175
+ (evaluate '(∀ [x] (→ (Mortal x) (Man x))) model ["socrates" "plato" "fido"]) ; False
176
+ ```
177
+
178
+ For quantifiers over ordinary Python data, there are macros:
179
+
180
+ ```hy
181
+ (require hylogic.quantifiers [∀ ∃])
182
+ (∀ [x (range 1 10)] (> x 0)) ; True
183
+ (∃ [x [1 2] y [-1 -2]] (= (+ x y) 0)) ; True, with x = 1, y = -1
184
+ ```
185
+
186
+ ## Proofs
187
+
188
+ `prove` refutes the negated goal together with the axioms by resolution, and
189
+ returns a result whose `proof` lists every step:
190
+
191
+ ```hy
192
+ (import hylogic [prove])
193
+
194
+ (setv result (prove '(Kills curiosity tuna)
195
+ ['(∃ [x] (∧ (Dog x) (Owns jack x)))
196
+ '(∀ [x] (→ (∃ [y] (∧ (Dog y) (Owns x y))) (AnimalLover x)))
197
+ '(∀ [x] (→ (AnimalLover x) (∀ [y] (→ (Animal y) (¬ (Kills x y))))))
198
+ '(∨ (Kills jack tuna) (Kills curiosity tuna))
199
+ '(Cat tuna)
200
+ '(∀ [x] (→ (Cat x) (Animal x)))]))
201
+ (for [line (.proof result)] (print line))
202
+ ```
203
+
204
+ ```
205
+ 1. (Dog sk1) axiom
206
+ ...
207
+ 8. (not (Kills curiosity tuna)) negated goal
208
+ 10. (Kills jack tuna) resolve 5, 8
209
+ 13. (AnimalLover jack) resolve 12, 1
210
+ 21. (not (AnimalLover jack)) resolve 16, 9
211
+ 26. □ resolve 21, 13
212
+ ```
213
+
214
+ `result.status` is `proved`, `saturated` (the goal does not follow) or
215
+ `gave-up` (the clause limit, `:limit 5000` by default, was reached). The
216
+ prover proves Pelletier's problems 1 to 24 in milliseconds and problem 34
217
+ (Andrews' challenge) in about five seconds. [doc/prover.md](doc/prover.md)
218
+ explains how it works, step by step, with exercises.
219
+
220
+ Term rewriting is included too:
221
+
222
+ ```hy
223
+ (require hylogic.rewrite [defrules])
224
+ (import hylogic [normalize])
225
+
226
+ (defrules PEANO
227
+ (+ 0 ?y) -> ?y
228
+ (+ (s ?x) ?y) -> (s (+ ?x ?y)))
229
+
230
+ (get (normalize '(+ (s (s 0)) (s 0)) PEANO) 0) ; #("s" #("s" #("s" 0)))
231
+ ```
232
+
233
+ ## Teaching and exercises
234
+
235
+ `hylogic.exercises` checks the two classic kinds of exercise and explains a
236
+ wrong answer in the words of the exercise. A formalization is correct when it
237
+ is logically equivalent to the reference, however it is written. A wrong one
238
+ gets a situation where the two come apart:
239
+
240
+ ```hy
241
+ (import hylogic.exercise-sets [check-answer])
242
+
243
+ (check-answer "F2" '(→ P Q)) ; "The street is wet only if it rains."
244
+ ; Not quite. Suppose “it rains” is false and “the street is wet” is true.
245
+ ; Then your formula is true, but the sentence is false.
246
+
247
+ (check-answer "V9" True) ; "Everybody loves somebody. So there is somebody whom everybody loves."
248
+ ; Not quite. The argument is invalid. In this world the premises are true and the conclusion is false:
249
+ ; Domain: d1, d2
250
+ ; Loves holds for: (d1 d1), (d2 d2)
251
+ ```
252
+
253
+ The exercise set has 16 sentences to formalize, propositional and
254
+ first-order, and 10 arguments to judge. `check-formalization` and
255
+ `check-validity-claim` work with your own exercises too, and a claim that an
256
+ argument is invalid can come with a counterexample, which is checked.
257
+
258
+ [Hylogic tutorial.ipynb](Hylogic%20tutorial.ipynb) walks through truth
259
+ tables, arguments, exercises, models and proofs. It runs on the ordinary
260
+ Python kernel with `%load_ext hylogic.ipython`, which adds a `%%hy` cell
261
+ magic.
262
+
263
+ ## Larger problems, countermodels and other solvers
264
+
265
+ Truth tables double with every proposition. For larger propositional
266
+ problems, `satisfiable` and `valid?` in `hylogic.sat` use the Tseitin
267
+ transformation and the DPLL algorithm, which stay in milliseconds up to about
268
+ 40 variables. Arguments switch to it on their own above 10 propositions.
269
+
270
+ ```hy
271
+ (import hylogic.sat [satisfiable valid?])
272
+ (satisfiable '(∧ (∨ P Q) (¬ P))) ; {"P" False "Q" True}
273
+ ```
274
+
275
+ When a first-order argument is invalid, `countermodel` looks for a small
276
+ world that shows it, by grounding the formulas over a domain of up to four
277
+ elements and solving the result with DPLL:
278
+
279
+ ```hy
280
+ (import hylogic [countermodel])
281
+ (print (countermodel ['(∀ [x] (∃ [y] (Loves x y)))] '(∃ [y] (∀ [x] (Loves x y)))))
282
+ ```
283
+
284
+ ```
285
+ Domain: d1, d2
286
+ Loves holds for: (d1 d1), (d2 d2)
287
+ ```
288
+
289
+ Everybody loves somebody, here themselves, but nobody is loved by everybody.
290
+
291
+ For problems beyond hylogic's reach, `to-smtlib` and `to-tptp` write the same
292
+ question for real solvers such as Z3 and cvc5, or provers such as Vampire and
293
+ E, and `to-dimacs` writes the SAT clauses for any SAT solver. With
294
+ `pip install "hylogic[z3]"`, `check-with-z3` runs Z3 directly.
295
+ [benchmarks/](benchmarks/) compares the four methods on the same problems.
296
+
297
+ ## For AI agents
298
+
299
+ `hylogic-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io)
300
+ server with one tool, `check_argument`. An agent formalizes an argument,
301
+ calls the tool, and gets the verdict with a counterexample or countermodel
302
+ when the argument is invalid. Formulas can be s-expression strings or JSON
303
+ arrays, since models write JSON most reliably.
304
+
305
+ ```bash
306
+ pip install "hylogic[mcp]"
307
+ claude mcp add hylogic -- hylogic-mcp
308
+ ```
309
+
310
+ ```
311
+ check_argument(premises=["(=> P Q)", "Q"], conclusion="P",
312
+ meanings={"P": "it rains", "Q": "the street is wet"})
313
+
314
+ INVALID (checked by truth table)
315
+ Read as: P → Q; Q ⊢ P
316
+ Counterexample: “it rains” is false and “the street is wet” is true. Then every premise is true and the conclusion is false.
317
+ ```
318
+
319
+ [benchmarks/field_test/](benchmarks/field_test/) measures whether the tool
320
+ helps agents judge arguments, and what it costs.
321
+
322
+ ## From Python
323
+
324
+ Formulas are nested tuples of strings, and the Hy names that end in `?` have
325
+ Python aliases:
326
+
327
+ ```python
328
+ import hylogic
329
+
330
+ hylogic.is_tautology(("or", "P", ("not", "P"))) # True
331
+ hylogic.entails([("=>", "P", "Q"), "P"], "Q") # True
332
+ hylogic.prove(("=>", ("forall", ("x",), ("P", "x")), ("P", "a"))).proved # True
333
+ ```
334
+
335
+ ## Tests
336
+
337
+ The tests check every connective against its definition, compare the
338
+ extended connectives with their rewritten forms on every assignment, and run
339
+ the prover on 300 random propositional formulas, where it must agree with
340
+ the truth tables. The first-order problems were also checked with the Z3
341
+ solver during development.
342
+
343
+ ## History
344
+
345
+ Hylogic began in 2017 as a propositional logic evaluator for Hy 0.12 with an
346
+ infix notation. Version 1.0 is a rewrite for Hy 1.x: the infix notation is
347
+ gone, formulas are data, and the prover, models and arguments are new. The
348
+ 2017 version is tagged `v0.1.0`.
349
+
350
+ ## License
351
+
352
+ MIT
@@ -0,0 +1,318 @@
1
+ # Hylogic
2
+
3
+ Propositional and first-order logic in [Hy](https://hylang.org), a Lisp that
4
+ compiles to Python. Write formulas as Lisp code with the usual logical
5
+ symbols, then check them with truth tables, evaluate them in a finite model,
6
+ test whether an argument is valid, or prove them with a small resolution
7
+ prover that prints its proofs.
8
+
9
+ ```hy
10
+ (import hylogic [truth-table prove])
11
+
12
+ (print (truth-table '(→ (∧ P (→ P Q)) Q)))
13
+ ```
14
+
15
+ ```
16
+ P │ Q │ (P ∧ (P → Q)) → Q
17
+ ──┼───┼──────────────────
18
+ ⊤ │ ⊤ │ ⊤
19
+ ⊤ │ ⊥ │ ⊤
20
+ ⊥ │ ⊤ │ ⊤
21
+ ⊥ │ ⊥ │ ⊤
22
+ ```
23
+
24
+ Hylogic is meant for learning and experimenting: the code is short enough to
25
+ read, and every step of a proof can be printed. For serious proofs, use
26
+ [Lean](https://lean-lang.org/) or an automated prover such as Vampire or E.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install hylogic
32
+ ```
33
+
34
+ Hylogic needs Python 3.9 or newer and Hy 1.0 or newer. To run the tests and
35
+ the tour from a clone:
36
+
37
+ ```bash
38
+ pip install -e ".[test]"
39
+ pytest
40
+ hy examples/tour.hy
41
+ ```
42
+
43
+ ## Formulas
44
+
45
+ A formula is quoted Hy code. Atoms are propositions (`P`) or predicates
46
+ applied to terms (`(Loves x (mother x))`). Every connective has a symbol and
47
+ an ASCII name, and both can be mixed freely:
48
+
49
+ | Symbol | ASCII | Name | Arguments |
50
+ |---|---|---|---|
51
+ | `¬` | `not` | Negation | 1 |
52
+ | `∧` | `and` | Conjunction | any number |
53
+ | `∨` | `or` | Disjunction | any number |
54
+ | `↑` | `nand` | Nonconjunction | any number |
55
+ | `↓` | `nor` | Nondisjunction | any number |
56
+ | `⊕` `↮` | `xor` | Exclusive or, true for an odd number of true arguments | any number |
57
+ | `≡` | `eqv` | Equivalence, true when all arguments agree | any number |
58
+ | `≢` | `neqv` | Nonequivalence | any number |
59
+ | `↔` | `<=>` | Biconditional | 2 |
60
+ | `→` | `=>` | Material implication | 2 |
61
+ | `←` | `<=` | Converse implication | 2 |
62
+ | `↛` | `nimp` | Material nonimplication | 2 |
63
+ | `↚` | `cnimp` | Converse nonimplication | 2 |
64
+ | `∀` | `forall` | Universal quantifier, `(∀ [x y] body)` | |
65
+ | `∃` | `exists` | Existential quantifier | |
66
+ | `⊤` `⊥` | `true` `false` | The constants | |
67
+
68
+ `pretty` prints a formula in the usual notation:
69
+
70
+ ```hy
71
+ (import hylogic [pretty])
72
+ (pretty '(forall [x] (=> (Man x) (Mortal x)))) ; "∀x ((Man x) → (Mortal x))"
73
+ ```
74
+
75
+ ## Truth tables
76
+
77
+ ```hy
78
+ (import hylogic.truth [truth-table tautology? satisfiable? equivalent? entails? counterexample])
79
+
80
+ (tautology? '(=> (=> (=> p q) p) p)) ; Peirce's law: True
81
+ (equivalent? '(¬ (∧ P Q)) '(∨ (¬ P) (¬ Q))) ; De Morgan: True
82
+ (entails? ['(→ P Q) '(¬ Q)] '(¬ P)) ; modus tollens: True
83
+ (counterexample ['(→ P Q) 'Q] 'P) ; {"P" False "Q" True}
84
+ ```
85
+
86
+ `truth-table` takes several formulas and shows them side by side. In a
87
+ Jupyter notebook it renders as an HTML table. `(print (connective-tables))`
88
+ prints the tables of all thirteen connectives.
89
+
90
+ ## Arguments
91
+
92
+ `defargument` takes premises and a conclusion as written, without quoting,
93
+ and decides validity: by truth table when the argument is propositional, and
94
+ with the prover when it has quantifiers or predicates.
95
+
96
+ ```hy
97
+ (require hylogic.argument [defargument])
98
+
99
+ (defargument modus-tollens
100
+ :premises [(→ P Q) (¬ Q)]
101
+ :conclusion (¬ P)
102
+ :meanings {P "It rains" Q "The street is wet"})
103
+
104
+ (print (.explain modus-tollens))
105
+ ```
106
+
107
+ ```
108
+ P → Q
109
+ ¬Q
110
+ ───────
111
+ ∴ ¬P
112
+
113
+ P: It rains
114
+ Q: The street is wet
115
+
116
+ Valid, by truth table.
117
+ ```
118
+
119
+ An invalid argument comes with a counterexample, and `Argument` builds the
120
+ same object from quoted formulas:
121
+
122
+ ```hy
123
+ (import hylogic [Argument])
124
+ (.explain (Argument ['(→ P Q) 'Q] 'P))
125
+ ; Invalid, by truth table.
126
+ ; Counterexample: P = ⊥, Q = ⊤
127
+ ```
128
+
129
+ ## Models
130
+
131
+ `evaluate` finds the truth value of a formula in a model. Predicates are
132
+ Python functions or collections of the tuples they hold for, and quantifiers
133
+ range over a finite domain:
134
+
135
+ ```hy
136
+ (import hylogic [evaluate])
137
+
138
+ (setv model {"Man" #{"socrates" "plato"}
139
+ "Mortal" #{"socrates" "plato" "fido"}})
140
+ (evaluate '(∀ [x] (→ (Man x) (Mortal x))) model ["socrates" "plato" "fido"]) ; True
141
+ (evaluate '(∀ [x] (→ (Mortal x) (Man x))) model ["socrates" "plato" "fido"]) ; False
142
+ ```
143
+
144
+ For quantifiers over ordinary Python data, there are macros:
145
+
146
+ ```hy
147
+ (require hylogic.quantifiers [∀ ∃])
148
+ (∀ [x (range 1 10)] (> x 0)) ; True
149
+ (∃ [x [1 2] y [-1 -2]] (= (+ x y) 0)) ; True, with x = 1, y = -1
150
+ ```
151
+
152
+ ## Proofs
153
+
154
+ `prove` refutes the negated goal together with the axioms by resolution, and
155
+ returns a result whose `proof` lists every step:
156
+
157
+ ```hy
158
+ (import hylogic [prove])
159
+
160
+ (setv result (prove '(Kills curiosity tuna)
161
+ ['(∃ [x] (∧ (Dog x) (Owns jack x)))
162
+ '(∀ [x] (→ (∃ [y] (∧ (Dog y) (Owns x y))) (AnimalLover x)))
163
+ '(∀ [x] (→ (AnimalLover x) (∀ [y] (→ (Animal y) (¬ (Kills x y))))))
164
+ '(∨ (Kills jack tuna) (Kills curiosity tuna))
165
+ '(Cat tuna)
166
+ '(∀ [x] (→ (Cat x) (Animal x)))]))
167
+ (for [line (.proof result)] (print line))
168
+ ```
169
+
170
+ ```
171
+ 1. (Dog sk1) axiom
172
+ ...
173
+ 8. (not (Kills curiosity tuna)) negated goal
174
+ 10. (Kills jack tuna) resolve 5, 8
175
+ 13. (AnimalLover jack) resolve 12, 1
176
+ 21. (not (AnimalLover jack)) resolve 16, 9
177
+ 26. □ resolve 21, 13
178
+ ```
179
+
180
+ `result.status` is `proved`, `saturated` (the goal does not follow) or
181
+ `gave-up` (the clause limit, `:limit 5000` by default, was reached). The
182
+ prover proves Pelletier's problems 1 to 24 in milliseconds and problem 34
183
+ (Andrews' challenge) in about five seconds. [doc/prover.md](doc/prover.md)
184
+ explains how it works, step by step, with exercises.
185
+
186
+ Term rewriting is included too:
187
+
188
+ ```hy
189
+ (require hylogic.rewrite [defrules])
190
+ (import hylogic [normalize])
191
+
192
+ (defrules PEANO
193
+ (+ 0 ?y) -> ?y
194
+ (+ (s ?x) ?y) -> (s (+ ?x ?y)))
195
+
196
+ (get (normalize '(+ (s (s 0)) (s 0)) PEANO) 0) ; #("s" #("s" #("s" 0)))
197
+ ```
198
+
199
+ ## Teaching and exercises
200
+
201
+ `hylogic.exercises` checks the two classic kinds of exercise and explains a
202
+ wrong answer in the words of the exercise. A formalization is correct when it
203
+ is logically equivalent to the reference, however it is written. A wrong one
204
+ gets a situation where the two come apart:
205
+
206
+ ```hy
207
+ (import hylogic.exercise-sets [check-answer])
208
+
209
+ (check-answer "F2" '(→ P Q)) ; "The street is wet only if it rains."
210
+ ; Not quite. Suppose “it rains” is false and “the street is wet” is true.
211
+ ; Then your formula is true, but the sentence is false.
212
+
213
+ (check-answer "V9" True) ; "Everybody loves somebody. So there is somebody whom everybody loves."
214
+ ; Not quite. The argument is invalid. In this world the premises are true and the conclusion is false:
215
+ ; Domain: d1, d2
216
+ ; Loves holds for: (d1 d1), (d2 d2)
217
+ ```
218
+
219
+ The exercise set has 16 sentences to formalize, propositional and
220
+ first-order, and 10 arguments to judge. `check-formalization` and
221
+ `check-validity-claim` work with your own exercises too, and a claim that an
222
+ argument is invalid can come with a counterexample, which is checked.
223
+
224
+ [Hylogic tutorial.ipynb](Hylogic%20tutorial.ipynb) walks through truth
225
+ tables, arguments, exercises, models and proofs. It runs on the ordinary
226
+ Python kernel with `%load_ext hylogic.ipython`, which adds a `%%hy` cell
227
+ magic.
228
+
229
+ ## Larger problems, countermodels and other solvers
230
+
231
+ Truth tables double with every proposition. For larger propositional
232
+ problems, `satisfiable` and `valid?` in `hylogic.sat` use the Tseitin
233
+ transformation and the DPLL algorithm, which stay in milliseconds up to about
234
+ 40 variables. Arguments switch to it on their own above 10 propositions.
235
+
236
+ ```hy
237
+ (import hylogic.sat [satisfiable valid?])
238
+ (satisfiable '(∧ (∨ P Q) (¬ P))) ; {"P" False "Q" True}
239
+ ```
240
+
241
+ When a first-order argument is invalid, `countermodel` looks for a small
242
+ world that shows it, by grounding the formulas over a domain of up to four
243
+ elements and solving the result with DPLL:
244
+
245
+ ```hy
246
+ (import hylogic [countermodel])
247
+ (print (countermodel ['(∀ [x] (∃ [y] (Loves x y)))] '(∃ [y] (∀ [x] (Loves x y)))))
248
+ ```
249
+
250
+ ```
251
+ Domain: d1, d2
252
+ Loves holds for: (d1 d1), (d2 d2)
253
+ ```
254
+
255
+ Everybody loves somebody, here themselves, but nobody is loved by everybody.
256
+
257
+ For problems beyond hylogic's reach, `to-smtlib` and `to-tptp` write the same
258
+ question for real solvers such as Z3 and cvc5, or provers such as Vampire and
259
+ E, and `to-dimacs` writes the SAT clauses for any SAT solver. With
260
+ `pip install "hylogic[z3]"`, `check-with-z3` runs Z3 directly.
261
+ [benchmarks/](benchmarks/) compares the four methods on the same problems.
262
+
263
+ ## For AI agents
264
+
265
+ `hylogic-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io)
266
+ server with one tool, `check_argument`. An agent formalizes an argument,
267
+ calls the tool, and gets the verdict with a counterexample or countermodel
268
+ when the argument is invalid. Formulas can be s-expression strings or JSON
269
+ arrays, since models write JSON most reliably.
270
+
271
+ ```bash
272
+ pip install "hylogic[mcp]"
273
+ claude mcp add hylogic -- hylogic-mcp
274
+ ```
275
+
276
+ ```
277
+ check_argument(premises=["(=> P Q)", "Q"], conclusion="P",
278
+ meanings={"P": "it rains", "Q": "the street is wet"})
279
+
280
+ INVALID (checked by truth table)
281
+ Read as: P → Q; Q ⊢ P
282
+ Counterexample: “it rains” is false and “the street is wet” is true. Then every premise is true and the conclusion is false.
283
+ ```
284
+
285
+ [benchmarks/field_test/](benchmarks/field_test/) measures whether the tool
286
+ helps agents judge arguments, and what it costs.
287
+
288
+ ## From Python
289
+
290
+ Formulas are nested tuples of strings, and the Hy names that end in `?` have
291
+ Python aliases:
292
+
293
+ ```python
294
+ import hylogic
295
+
296
+ hylogic.is_tautology(("or", "P", ("not", "P"))) # True
297
+ hylogic.entails([("=>", "P", "Q"), "P"], "Q") # True
298
+ hylogic.prove(("=>", ("forall", ("x",), ("P", "x")), ("P", "a"))).proved # True
299
+ ```
300
+
301
+ ## Tests
302
+
303
+ The tests check every connective against its definition, compare the
304
+ extended connectives with their rewritten forms on every assignment, and run
305
+ the prover on 300 random propositional formulas, where it must agree with
306
+ the truth tables. The first-order problems were also checked with the Z3
307
+ solver during development.
308
+
309
+ ## History
310
+
311
+ Hylogic began in 2017 as a propositional logic evaluator for Hy 0.12 with an
312
+ infix notation. Version 1.0 is a rewrite for Hy 1.x: the infix notation is
313
+ gone, formulas are data, and the prover, models and arguments are new. The
314
+ 2017 version is tagged `v0.1.0`.
315
+
316
+ ## License
317
+
318
+ MIT