{"slug": "reading-proof-objects-and-completed-rewrite-systems-from-eprover-into", "title": "Reading Proof Objects and Completed Rewrite Systems from eprover into Knuckledragger", "summary": "E-prover, a superposition theorem prover, can be used as a Knuth-Bendix completion engine to generate oriented rewrite systems from equational axioms. The author demonstrates this by completing group axioms into a saturated set of rewrite rules, which can be parsed back into Z3py via Scryer Prolog. This approach enables using E-prover as an egraph modulo theory engine for automated reasoning.", "body_md": "# Reading Proof Objects and Completed Rewrite Systems from eprover into Knuckledragger\n\nAutomated reasoning is fun.\n\nE-prover [https://github.com/eprover/eprover](https://github.com/eprover/eprover) is a pure C [superposition](https://en.wikipedia.org/wiki/Superposition_calculus) theorem prover. It’s quite nice. [Superposition](https://www.tcs.ifi.lmu.de/teaching/courses-ws-2024-25/automated-theorem-proving/slides12-superposition.pdf) provers has strengths and weaknesses that complement those of SMT solvers.\n\nI was poking around at exporting problems from z3py by printing cnf formulas and reading back in the results using scryerpy, some light bindings I’ve made for [scryer prolog](https://github.com/mthom/scryer-prolog), a embeddable rust prolog I’ve made available at `pip install kdrag-scryer`\n\n[https://github.com/philzook58/scryerpy](https://github.com/philzook58/scryerpy) . This paper pointed out that post processing TPTP stuff via prolog is nice [https://arxiv.org/abs/2602.18844](https://arxiv.org/abs/2602.18844) . It is kind of overkill, but it works.\n\n# Completion via Eprover\n\nAs time has gone on, I’ve come to understand some of the knobs. It is particularly interesting to me to `--print-saturated`\n\nas this can be used an off the shelf [knuth bendix completion](https://en.wikipedia.org/wiki/Knuth%E2%80%93Bendix_completion_algorithm) engine.\n\nThe equations can be printed in oriented form using the recently added option `--print-oriented-eqlits-as-rules`\n\n. You can also pick the weights `--order-weights`\n\n, precedence of symbols `--precedence`\n\n, and LPO vs KBO term ordering `--term-ordering`\n\nvia command line options.\n\nKnuth bendix completion can be used as a egraph engine [https://www.philipzucker.com/egraph2024_talk/](https://www.philipzucker.com/egraph2024_talk/) or egraph modulo theory prototyping engine, so that’s pretty intriguing too. The completion of ground equations *is* an egraph. Throwing some rules on top of your ground equations with the right ordering can sometimes get you an egraph modulo that theory. eprover has has some special smarts for AC. I don’t know that anyone has thoroughly investigated using this.\n\nHere is the classic example of completing the rewrites describing a group\n\n```\n%%file /tmp/group.p\n\ncnf(mul_assoc, axiom, mul(X, mul(Y,Z)) = mul(mul(X,Y), Z)).\ncnf(mul_id_left, axiom, mul(id,X) = X).\ncnf(mul_inv, axiom, mul(inv(X), X) = id).\nWriting /tmp/group.p\n```\n\nWith the following command line, we print the completed saturated set as oriented rewrites\n\n```\n! eprover-ho --auto --silent --print-saturated --print-oriented-eqlits-as-rules /tmp/group.p\n% Preprocessing class: FSSSSMSSSSSNFFN.\n% Configuration: G-E--_302_C18_F1_URBAN_RG_S04BN\n% (lift_lambdas = 1, lambda_to_forall = 1,unroll_only_formulas = 1, sine = Auto)\n% No SInE strategy applied\n% Search class: FUUPS-FFSF21-SFFFFFNN\n% Configuration: G-E--_208_C12_00_F1_SE_CS_PI_SP_PS_S5PRR_RG_S04AN\n% Presaturation interreduction done\n\n% No proof found!\n% SZS status Satisfiable\n% Processed positive unit clauses:\ncnf(i_0_5, plain, (mul(id,X1)->X1)).\ncnf(i_0_6, plain, (mul(inv(X1),X1)->id)).\ncnf(i_0_4, plain, (mul(mul(X1,X2),X3)->mul(X1,mul(X2,X3)))).\ncnf(i_0_8, plain, (mul(inv(X1),mul(X1,X2))->X2)).\ncnf(i_0_23, plain, (inv(id)->id)).\ncnf(i_0_29, plain, (inv(inv(X1))->X1)).\ncnf(i_0_12, plain, (mul(X1,id)->X1)).\ncnf(i_0_32, plain, (mul(X1,inv(X1))->id)).\ncnf(i_0_33, plain, (mul(X1,mul(inv(X1),X2))->X2)).\ncnf(i_0_75, plain, (inv(mul(X1,X2))->mul(inv(X2),inv(X1)))).\n\n% Processed negative unit clauses:\n\n% Processed non-unit clauses:\n\n% Unprocessed positive unit clauses:\n\n% Unprocessed negative unit clauses:\n\n% Unprocessed non-unit clauses:\n```\n\nIt is jumping through hoops, but the full loop from z3py expressions into a CNF file, and then parsing back using scyrer prolog and turning back into z3py expressions is in this code [https://github.com/philzook58/knuckledragger/blob/101dbcef06fbcc214b90615be752ee070b23b1f0/src/kdrag/solvers/scryer.py](https://github.com/philzook58/knuckledragger/blob/101dbcef06fbcc214b90615be752ee070b23b1f0/src/kdrag/solvers/scryer.py) .\n\nI used the operator precedences file from leancop as prelude, as prolog by default does not parse some tptp operators.\n\nScryer is kind of overkill for this, but if it works it works. I may want to do some light elabrational theorem proving inside scryer to help me reconstruct certificates.\n\nOne tricky bit is coming from untyped prolog back into typed z3. I can store all the funcdecls, which helps a lot with the type inference problem. I did kind of super lite bidirectional typing in the form of an optional `sort=None`\n\nkeyword paramater for what sort is expected. This helps propagate sort information downwards. Generally speaking, this was simple, but a more declarative or constraint producing approach will be able to infer types for more expressions.\n\n``` python\nfrom kdrag.all import *\nimport kdrag.solvers.scryer as scryer_\nimport scryer \nimport kdrag.printers.tptp as tptp\nimport subprocess\n\ntptp_prelude = \"\"\"\n% TPTP syntax\n:- op(1130, xfy, <~>).  % negated equivalence\n:- op(1110, xfy, <=).   % implication\n:- op(1100, xfy, '|').  % disjunction\n:- op(1100, xfy, '~|'). % negated disjunction\n:- op(1000, xfy, &).    % conjunction\n:- op(1000, xfy, ~&).   % negated conjunction\n:- op( 500, fy, !).     % universal quantifier\n:- op( 500, fy, ?).     % existential quantifier\n:- op( 400, xfx, =).    % equality\n:- op( 300, xf, !).     % negated equality (for !=) Very annoying\n:- op( 299, fx, $).     % for $true/$false\n\"\"\"\n\ndef complete(eqs : list[smt.BoolRef]):\n    res = subprocess.run([\"eprover-ho\", \"--auto\", \"--soft-cpu-limit=1\", \"--silent\", \"--print-saturated\"], capture_output=True, input=tptp.cnf_file(eqs,nnf=True).encode())\n    if len(res.stderr) > 0:\n        raise Exception(res.stderr.decode())\n    m = scryer.Machine()\n    m.load_module_string(\"mymodule\", tptp_prelude + res.stdout.decode())\n    rules = []\n    decls = {d.name(): d for d in kd.utils.decls(smt.And(eqs))}\n    for res in m.query(\"cnf(Id, Status, Formula).\"):\n        rules.append(scryer_.from_scryer(res[\"Formula\"], decls))\n    return rules\n\nT = smt.DeclareSort(\"AbstractGroup\")\nx,y,z = smt.Consts(\"x y z\", T)\ne = smt.Const(\"e\", T)\ninv = smt.Function(\"inv\", T, T)\nmul = smt.Function(\"mul\", T, T, T)\nkd.notation.mul.register(T, mul)\nkd.notation.invert.register(T, inv)\neqs = [\n    axiom(ForAll([x], e * x == x)),\n    axiom(ForAll([x], inv(x) * x == e)),\n    axiom(ForAll([x,y,z], (x * y) * z == x * (y * z))),\n]\n\ncomplete([eq.thm for eq in eqs])\n[([_A], mul(e, _A) == _A),\n ([_A], mul(inv(_A), _A) == e),\n ([_A, _B, _C], mul(mul(_A, _B), _C) == mul(_A, mul(_B, _C))),\n ([_A, _B], mul(inv(_A), mul(_A, _B)) == _B),\n ([], inv(e) == e),\n ([_A], inv(inv(_A)) == _A),\n ([_A], mul(_A, e) == _A),\n ([_A], mul(_A, inv(_A)) == e),\n ([_A, _B], mul(_A, mul(inv(_A), _B)) == _B),\n ([_A, _B], inv(mul(_A, _B)) == mul(inv(_B), inv(_A)))]\n```\n\nNote this is outrageuosly faster than my homegrown KB in python [https://www.philipzucker.com/knuth_bendix_knuck/](https://www.philipzucker.com/knuth_bendix_knuck/) . This problem is baby shit, but even for this my solver took about 1s after I tuned quite a bit. eprover eats it instantaneously. Probably all the time is in making a subprocess etc.\n\n# Replaying Proof Objects\n\nIn Knuckederagger, I maintain the right to just trust eprover as an oracle.\n\nBut it is cool to reconstruct the proof object in order to reduce reliance on trust in my tptp serializers and deserializers. I try to code defensively, but they are definitely buggy. There is probably also some semantics gap between what even in principle what TPTP thinks is true and SMT/z3. I’d also like to work towards converting these proofs (and mine) into exportable metamath 0. Graham’s been firing off on that [https://grahamlk.me/Aufbau/#hilbert](https://grahamlk.me/Aufbau/#hilbert)\n\nHere is a problem proving the identity right from the left identity law for a group.\n\n```\n%%file /tmp/group.p\n\ncnf(mul_assoc, axiom, mul(X, mul(Y,Z)) = mul(mul(X,Y), Z)).\ncnf(mul_id_left, axiom, mul(id,X) = X).\ncnf(mul_inv, axiom, mul(inv(X), X) = id).\ncnf(goal, negated_conjecture, mul(x, id) = x).\nOverwriting /tmp/group.p\n```\n\nIt is similar to the above. `--proof-object`\n\noutputs a proof object.\n\nThe proof object is basically a listing of intermediate provable formulas. The inference rules in the fourth field don’t seem worth trying to take much out of except just the references to the previous theorems.\n\n```\n! eprover-ho --auto --silent --proof-object /tmp/group.p\n% Preprocessing class: FSSSSMSSSSSNFFN.\n% Configuration: G-E--_302_C18_F1_URBAN_RG_S04BN\n% (lift_lambdas = 1, lambda_to_forall = 1,unroll_only_formulas = 1, sine = Auto)\n% No SInE strategy applied\n% Search class: FUUPS-FFSF21-SFFFFFNN\n% Configuration: G-E--_208_C12_00_F1_SE_CS_PI_SP_PS_S5PRR_RG_S04AN\n% Presaturation interreduction done\n\n% No proof found!\n% SZS status Satisfiable\n% SZS output start Saturation\ncnf(mul_assoc, axiom, (mul(X1,mul(X2,X3))=mul(mul(X1,X2),X3)), file('/tmp/group.p', mul_assoc)).\ncnf(mul_inv, axiom, (mul(inv(X1),X1)=id), file('/tmp/group.p', mul_inv)).\ncnf(mul_id_left, axiom, (mul(id,X1)=X1), file('/tmp/group.p', mul_id_left)).\ncnf(c_0_3, axiom, (mul(X1,mul(X2,X3))=mul(mul(X1,X2),X3)), mul_assoc, ['final']).\ncnf(c_0_4, axiom, (mul(inv(X1),X1)=id), mul_inv, ['final']).\ncnf(c_0_5, axiom, (mul(id,X1)=X1), mul_id_left, ['final']).\ncnf(c_0_6, plain, (mul(inv(X1),mul(X1,X2))=X2), inference(rw,[status(thm)],[inference(spm,[status(thm)],[c_0_3, c_0_4]), c_0_5]), ['final']).\ncnf(c_0_7, plain, (mul(inv(inv(X1)),id)=X1), inference(spm,[status(thm)],[c_0_6, c_0_4])).\ncnf(c_0_8, plain, (mul(inv(inv(inv(X1))),X1)=id), inference(spm,[status(thm)],[c_0_6, c_0_7])).\ncnf(c_0_9, plain, (inv(inv(X1))=X1), inference(rw,[status(thm)],[inference(spm,[status(thm)],[c_0_6, c_0_8]), c_0_7]), ['final']).\ncnf(c_0_10, plain, (mul(X1,inv(X1))=id), inference(spm,[status(thm)],[c_0_4, c_0_9]), ['final']).\ncnf(c_0_11, plain, (mul(X1,mul(X2,inv(mul(X1,X2))))=id), inference(spm,[status(thm)],[c_0_3, c_0_10])).\ncnf(c_0_12, plain, (mul(X1,id)=X1), inference(rw,[status(thm)],[c_0_7, c_0_9]), ['final']).\ncnf(c_0_13, plain, (mul(inv(id),X1)=X1), inference(spm,[status(thm)],[c_0_6, c_0_5])).\ncnf(c_0_14, plain, (mul(X1,inv(mul(X2,X1)))=inv(X2)), inference(rw,[status(thm)],[inference(spm,[status(thm)],[c_0_6, c_0_11]), c_0_12])).\ncnf(c_0_15, plain, (mul(inv(inv(id)),X1)=X1), inference(spm,[status(thm)],[c_0_6, c_0_13])).\ncnf(c_0_16, plain, (inv(mul(X1,X2))=mul(inv(X2),inv(X1))), inference(spm,[status(thm)],[c_0_6, c_0_14]), ['final']).\ncnf(c_0_17, plain, (mul(X1,mul(inv(X1),X2))=X2), inference(spm,[status(thm)],[c_0_6, c_0_9]), ['final']).\ncnf(c_0_18, plain, (inv(id)=id), inference(spm,[status(thm)],[c_0_4, c_0_15]), ['final']).\n% SZS output end Saturation\n```\n\nSo really the proof object from my perspective is just a DAG of formulas which point back to previous nodes.\n\nReconstructing this take a few moments thought because the goal really has a different status than the `by`\n\ninformation (being currently unproven). What I need to do is weaken the incoming axioms to `goal |= ax`\n\nor really `|= goal -> ax`\n\n. Then I can have a reflexive proof object for the goal as `goal |= goal`\n\n. As a final step once I have `goal |= false`\n\n, I can turn this into `|= not goal`\n\nIt is important to skolemize yourself before handing it off to the prover. I think there is some format for getting information about how the prover skolemized, but skoemization isn’t a theorem, it’s a equisatisfiability / conservative extension thing.\n\n`|= exists a, foo(a)`\n\ndoes not imply by logical reasoning that `|= foo(biz)`\n\n. It’s kind of a meta move that you can create a new constant and assert as a new axiom that `foo(biz)`\n\nholds. This is different from `|= p /\\ q`\n\nactually can derive `|= p`\n\nwithout any meta playing or `|= forall a, foo(a)`\n\ncan derive `|= foo(bar)`\n\nwithout any metaplay.\n\n``` php\ndef eprove(goal : smt.BoolRef, by : list[kd.Proof] =[]) -> kd.Proof:\n    prob = [pf.thm for pf in by] + [goal]\n    res = subprocess.run([\"eprover-ho\", \"--auto\", \"--soft-cpu-limit=1\", \"--silent\", \"--proof-object\"], capture_output=True, input=tptp.cnf_file(prob,nnf=True).encode())\n    if len(res.stderr) > 0:\n        raise Exception(res.stderr.decode())\n    m = scryer.Machine()\n    m.load_module_string(\"mymodule\", tptp_prelude + res.stdout.decode())\n    #print(res.stdout.decode())\n    decls = {d.name(): d for d in kd.utils.decls(smt.And(prob))}\n    proof = {scryer.Term.Integer(i) : prove(Implies(goal, pf.thm),by=[pf]) for i,pf in enumerate(by)}\n    proof[scryer.Term.Integer(len(by)+1)] = prove(Implies(goal,goal))\n    for step in m.query(\"cnf(Id, Status, Formula, Reason) ; cnf(Id, Status, Formula, Reason, ['proof']) .\"):\n        if step[\"Id\"] in proof:\n            continue\n        vs, body = scryer_.from_scryer(step[\"Formula\"], decls)\n        if len(vs) == 0:\n            thm = body\n        else:\n            thm = ForAll(vs, body)\n        by1 = [proof[r] for r in scryer_.subterms(step[\"Reason\"]) if r in proof]\n        proof[step[\"Id\"]] = prove(Implies(goal, thm), by=by1)\n    return prove(Not(goal), by=list(proof.values()))\n\neprove(x * inv(x)!= e, by=eqs)\n```\n\n⊨Not(mul(x, inv(x)) != e)\n\nIf I give the same problem to z3 raw, it does not solve, so we are getting some delta here. The incomplete e-matching is not sufficient for this problem. It needs the clue terms in the eprover proof trace or a more fine grained manual proof.\n\n```\nprove(x * inv(x) == e, by=eqs)\n---------------------------------------------------------------------------\n\nLemmaError                                Traceback (most recent call last)\n\nFile ~/vibe_coding/knuck_anal/knuckledragger/src/kdrag/tactics.py:315, in prove(thm, fixes, assumes, by, admit, timeout, dump, solver, contracts, unfold)\n    314 try:\n--> 315     pf = kd.kernel.prove(\n    316         thm, by=by_list, timeout=timeout, dump=dump, solver=solver, admit=admit\n    317     )\n    318     if fixes:\n\nFile ~/vibe_coding/knuck_anal/knuckledragger/src/kdrag/kernel.py:181, in prove(thm, by, admit, timeout, dump, solver, fvs)\n    180         smtfile.write(s.sexpr())\n--> 181     raise LemmaError(\"prove\", thm, res, smtfile.name)\n    182 else:\n\nLemmaError: ('prove', mul(x, inv(x)) == e, unknown, '/tmp/tmp7u4jhr_k.smt2')\n\nDuring handling of the above exception, another exception occurred:\n\nTimeoutError                              Traceback (most recent call last)\n\nCell In[26], line 1\n----> 1 prove(x * inv(x) == e, by=eqs)\n\nFile ~/vibe_coding/knuck_anal/knuckledragger/src/kdrag/tactics.py:324, in prove(thm, fixes, assumes, by, admit, timeout, dump, solver, contracts, unfold)\n    322 except kd.kernel.LemmaError as e:\n    323     if time.perf_counter() - start_time > timeout / 1000:\n--> 324         raise TimeoutError(\n    325             \"Timeout. Maybe you have given `prove` too many or not enough lemmas?\"\n    326         )\n    327     elif isinstance(thm, smt.QuantifierRef):\n    328         while isinstance(thm, smt.QuantifierRef) and thm.is_forall():\n\nTimeoutError: Timeout. Maybe you have given `prove` too many or not enough lemmas?\n```\n\n# Bits and Bobbles\n\nVampire and eprover are two top tier automated theorem provers.\n\nShould I being called eprover “The Equational Theorem Prover E”? I think it’s name is E, but I call it eprover.\n\nIsabelle’s sledgehammer is crazy good. I should try to understand it better. A funny thing is basically you need a native automated theorem prover in order to digest proofs from others systems. They’ll only ever really give you breadcrumbs. I may want to use scryer to make some leantap or leancop variants to assist proof reconstruction [https://formal.kastel.kit.edu/beckert/leantap/](https://formal.kastel.kit.edu/beckert/leantap/) [https://jens-otten.de/tutorial_tableaux19/](https://jens-otten.de/tutorial_tableaux19/) knuth bendix in prolog [https://www.metalevel.at/trs/](https://www.metalevel.at/trs/) . I tried getting saturate running [https://resources.mpi-inf.mpg.de/SATURATE/Saturate.html](https://resources.mpi-inf.mpg.de/SATURATE/Saturate.html) . It’s hopeless. Too fancy, too bit rotted.\n\nI also think it’s interesting to explore proof by saturation / inductionless induction in eprover. The mere fact of saturation proves consistency of a statement and therefore it is true in the initial term model or something like that. I dunno how I’d even replay those proofs. I think eprover could be used for unbounded bitvector proofs in the manner.\n\nsick [https://vprover.github.io/vampireGuide/](https://vprover.github.io/vampireGuide/) vampire’s getting good docs. Getting out vampire proof objects should be quite similar. Vampire seems less straightforward to understand it’s saturated objects.\n\nSomeone is making rust bindings to vampire [https://docs.rs/vampire-sys/latest/vampire_sys/](https://docs.rs/vampire-sys/latest/vampire_sys/) ? Hellllllo nurse.\n\nI’ve been playing with eprover again recently because of my increased understanding of the concepts behind it’s command line knobs and the new `--print-eqlists-as-oriented-rules`\n\nflag.\n\nKnuth bendix and e-graphs are intimately related.\n\nIn this vampire for agda paper [https://arxiv.org/abs/2602.18844](https://arxiv.org/abs/2602.18844) , they suggest that one should use a prolog to read out the proof objects. Indeed, the TPTP format is prolog syntax, which is useful for those using prolog and non-useful for everyone else.\n\nI made some simple python bindings to scryer-prolog, a rust based prolog. My impression is that scryer is conceptually tighter that swi, but swi is much more feature rich. swi already has python bindings in the form of pyswip [https://github.com/yuce/pyswip](https://github.com/yuce/pyswip) and `janus-swi`\n\npackage. However, it’s not quite self contained enough to install these as python packages. You need to install swi separately to get these to work. As stupid as that sounds, that is important to me. Giants stumbling on pebbles.\n\nI also do not like how janus is set up. I really wanted it to be my solution. It clearly to my mind has the python access of prolog as a second class concern. My big gripe is basically how shipping terms back over with variables in them is a no go. You need to ground them first on the prolog side. For my preferences, prolog is the second class citizen. I want prolog for specialized solver-like situations and do not consider it a good general purpose programming language replacement. This is partially due to my relative familiarity, but not entirely.\n\nNo one has a gun to my head for many of the things I do, so the approach needs to spark joy in me.\n\nIn a strange way, despite the CNF format seeming so primitive, it is the better one to be using for some purposes. It is “closer to the metal” in some respects and kind of more constructive. The very helpful skolemization and normal form routines make getting a proof object out more confusing.\n\nI have found e-prover’s knobs to be less confusing if I am not after refutational theorem proving.\n\n```\n%%file /tmp/path.p\n\ncnf(edge_path, axiom, path(X,Y) | ~edge(X,Y)).\ncnf(path_trans, axiom, path(X,Z) | ~edge(X,Y) | ~path(Y,Z)).\ncnf(edge12, axiom, edge(a,b)).\ncnf(edge23, axiom, edge(b,c)).\ncnf(edge23, axiom, edge(c,d)).\nfof(query, conjecture, ?[X,Y] : path(X,Y)).\nOverwriting /tmp/path.p\n! eprover-ho /tmp/path.p --auto --silent --answers=5 --conjectures-are-questions\n% Preprocessing class: FSSSSMSSSSSNFFN.\n% Configuration: G-E--_302_C18_F1_URBAN_RG_S04BN\n% (lift_lambdas = 1, lambda_to_forall = 1,unroll_only_formulas = 1, sine = Auto)\n% No SInE strategy applied\n% Search class: FHUNF-FFSS22-SFFFFFNN\n% partial match(1): FGUNF-FFSS22-SFFFFFNN\n% Configuration: SAT001_MinMin_p005000_rr_RG\n% Presaturation interreduction done\n% SZS status Theorem\n% SZS answers Tuple [[c, d]|_]\n% SZS answers Tuple [[b, c]|_]\n% SZS answers Tuple [[a, b]|_]\n% SZS answers Tuple [[a, c]|_]\n% SZS answers Tuple [[b, d]|_]\n\n% Proof found!\n%%file /tmp/path.p\n\ncnf(edge_path, axiom, path(X,Y) | ~edge(X,Y)).\ncnf(path_trans, axiom, path(X,Z) | ~edge(X,Y) | ~path(Y,Z)).\ncnf(edge12, axiom, edge(a,b)).\ncnf(edge23, axiom, edge(b,c)).\ncnf(edge23, axiom, edge(c,d)).\nOverwriting /tmp/path.p\n```\n\na little sad from a datalog perspective, but it ain’t wrong. I haven’t really figured out how to\n\n```\n! eprover-ho --print-saturated  --silent --auto /tmp/path.p\n% Preprocessing class: FSSSSMSSSSSNFFN.\n% Configuration: G-E--_302_C18_F1_URBAN_RG_S04BN\n% (lift_lambdas = 1, lambda_to_forall = 1,unroll_only_formulas = 1, sine = Auto)\n% No SInE strategy applied\n% Search class: FHUNF-FFSS00-SFFFFFNN\n% Configuration: SAT001_MinMin_p005000_rr_RG\n% Presaturation interreduction done\n\n% No proof found!\n% SZS status Satisfiable\n% Processed positive unit clauses:\ncnf(i_0_8, plain, (edge(a,b))).\ncnf(i_0_9, plain, (edge(b,c))).\ncnf(i_0_10, plain, (edge(c,d))).\n\n% Processed negative unit clauses:\n\n% Processed non-unit clauses:\ncnf(i_0_6, plain, (path(X1,X2)|~edge(X1,X2))).\ncnf(i_0_7, plain, (path(X1,X2)|~edge(X1,X3)|~path(X3,X2))).\ncnf(i_0_11, plain, (path(c,X1)|~path(d,X1))).\ncnf(i_0_12, plain, (path(b,X1)|~path(c,X1))).\ncnf(i_0_13, plain, (path(a,X1)|~path(b,X1))).\ncnf(i_0_14, plain, (path(c,X1)|~edge(d,X1))).\ncnf(i_0_15, plain, (path(b,X1)|~edge(c,X1))).\ncnf(i_0_16, plain, (path(b,X1)|~edge(d,X1))).\n\n% Unprocessed positive unit clauses:\n\n% Unprocessed negative unit clauses:\n\n% Unprocessed non-unit clauses:\npython\ndef question(query, axioms):\npython\ndef question_answer(query, axioms): ...\n    \n\ndef to_prolog(vs : list[smt.ExprRef], e : smt.ExprRef):\n    if smt.is_eq(e):\n        return f\"{to_prolog(vs,e.arg(0))} = {to_prolog(vs, e.arg(1))}\"\n    elif smt.is_const(e):\n        if\n        return e.decl().name()\n    elif smt.is_app(e):\n        return f\"{e.decl().name()}({', '.join(to_prolog(a) for a in e.args())})\"\n\ndef prolog(query, rules):\nfrom kdrag.all import *\n\nT = DeclareSort(\"T\")\nmul = Function(\"mul\", T, T, T)\nzero = Const(\"zero\", T)\nid = Const(\"id\", T)\nx,y,z = Consts(\"x y z\", T)\nimport kdrag.printers.tptp as tptp\n\ntptp.expr_to_cnf(ForAll([x], mul(zero,x) == zero))\ndir(tptp)\ntptp.expr_to_tptp(ForAll([x], mul(zero,x) == zero), format=\"cnf\")\n#help(tptp.expr_to_tptp)\n\ne = ForAll([x], mul(zero,x) == zero)\nTactic(\"nnf\")(e)\nTactic(\"tseitin-cnf\")(e)\n#With(Tactic(\"nnf\"), skolem=True)(e)\n\ndef cnf_file(axioms, nnf=False):\n    if nnf:\n        g = Goal()\n        g.add(axioms)\n        axioms = Tactic(\"nnf\")(g)[0]\n    res = []\n    for i, axiom in enumerate(axioms):\n        res.append(f\"cnf({i},axiom,{tptp.expr_to_cnf(axiom)}).\")\n    return \"\\n\".join(res)\n\naxs = [\n    ForAll([x,y,z], mul(x, mul(y,z)) == mul(mul(x,y), z)),\n    ForAll([x], mul(id,x) == x),\n    ForAll([x], mul(x,id) == x),\n    ForAll([x,y], mul(x,y) == mul(y,x)),\n    mul(id,x) != x\n]\n\ng = Goal()\ng.add(axs)\nlist(Tactic(\"nnf\")(g)[0])\nprint(cnf_file(axs, nnf=True))\ntptp_prelude = \"\"\"\n% TPTP syntax\n:- op(1130, xfy, <~>).  % negated equivalence\n:- op(1110, xfy, <=).   % implication\n:- op(1100, xfy, '|').  % disjunction\n:- op(1100, xfy, '~|'). % negated disjunction\n:- op(1000, xfy, &).    % conjunction\n:- op(1000, xfy, ~&).   % negated conjunction\n:- op( 500, fy, !).     % universal quantifier\n:- op( 500, fy, ?).     % existential quantifier\n:- op( 400, xfx, =).    % equality\n:- op( 300, xf, !).     % negated equality (for !=) Very annoying\n:- op( 299, fx, $).     % for $true/$false\n\"\"\"\nimport subprocess\ndef _from_scryer(term : scryer.Term, decls : dict[str, smt.FuncDeclRef], vars, sort=None):\n    match term:\n        case scryer.Term.Var(name):\n            if name in vars:\n                if sort is not None:\n                    assert vars[name].sort() == sort\n                return vars[name]\n            else:\n                assert sort is not None, f\"Variable sort is not inferrable {name}\"\n                v = smt.Const(name, sort)\n                vars[name] = v\n                return v\n        case scryer.Term.Atom(name):\n            if name in decls:\n                return decls[name]()\n            else:\n                assert sort is not None\n                return smt.Const(name, sort)\n        case scryer.Term.Compound(\"=\", [scryer.Term.Compound(\"!\", [lhs]), rhs]):\n            lhs = _from_scryer(lhs, decls, vars)\n            return lhs != _from_scryer(rhs, decls, vars, sort=lhs.sort())\n        case scryer.Term.Compound(\"$\", [t]):\n            match t:\n                case scryer.Term.Atom(\"true\"):\n                    return smt.BoolVal(True)\n                case scryer.Term.Atom(\"false\"):\n                    return smt.BoolVal(False)\n                case _:\n                    raise Exception(f\"Untranslatable term: {term}\")\n        case scryer.Term.Compound(name, args):\n            if name == \"=\":\n                assert len(args) == 2\n                lhs = _from_scryer(args[0], decls, vars)\n                return lhs == _from_scryer(args[1], decls, vars, sort=lhs.sort())\n            if name in decls:\n                decl = decls[name]\n                args = [_from_scryer(arg, decls, vars, sort=decl.domain(i)) for i, arg in enumerate(args)]\n                return decl(*args)\n        case _:\n            raise Exception(f\"Untranslatable constructor: {term}\")\n        #case scryer.Term.List(values):\n\ndef from_scryer(term, decls):\n    vars = {}\n    res = _from_scryer(term, decls, vars)\n    if len(vars) == 0:\n        return res\n    else:\n        return smt.ForAll(list(vars.values()), res)\n\ndef in_term(term, tags):\n    todo = [term]\n    res = set()\n    while todo:\n        term = todo.pop()\n        if term in tags:\n            res.add(term)\n        if isinstance(term, scryer.Term.Compound):\n            todo.extend(term.args)\n        elif isinstance(term, scryer.Term.List):\n            todo.extend(term.values)\n    return res\n\ndef eprover(axs):\n    res = subprocess.run([\"eprover-ho\", \"--auto\", \"--cpu-limit=1\", \"--silent\", \"--proof-object\"], capture_output=True, input=cnf_file(axs,nnf=True).encode())\n    if len(res.stderr) > 0:\n        raise Exception(res.stderr.decode())\n    output = res.stdout.decode()\n    if \"SZS status Unsatisfiable\" in output:\n        m = scryer.Machine()\n        m.load_module_string(\"proof\", tptp_prelude + output)\n        proof = m.query(\"cnf(Id, Status, Formula, Reason) ; cnf(Id, Status, Formula, Reason, ['proof']) .\")\n        tags = set()\n        res = []\n        decls = {d.name(): d for d in kd.utils.decls(smt.And(axs))}\n        print(decls)\n        for step in proof:\n            res.append((step[\"Id\"], from_scryer(step[\"Formula\"], decls) , in_term(step[\"Reason\"], tags)))#, str(step[\"Formula\"])))\n            #print(tags)\n            tags.add(step[\"Id\"])\n        return res\nimport pprint\npprint.pprint(eprover(axs))\n# a more iterative fixpoint thing\n    from_scryer(term0, decls):\n    terms = [(None, t) for t in subterms(term0)]\n    todo = []\n    trans = {}\n    while True:\n        for sort, term in terms:\n            if term in trans:\n                continue\n            match term:\n                case scryer.Term.Atom(name):\n                    if name in decls:\n                        trans[term] = decls[name]()\n                    elif sort is not None:\n                        trans[term] = smt.Const(name, sort)\n                    else:\n                        todo.append((None, term))\n                case scryer.Term.Compound(\"=\", [scryer.Term.Compound(\"!\", [lhs]), rhs]):\n                    if (None, lhs) in trans and (None\n\n        terms = todo\n    return trans[term0]\n```\n\n[https://docs.rs/vampire-prover/latest/vampire_prover/](https://docs.rs/vampire-prover/latest/vampire_prover/) whoa\n\n```\n%%file /tmp/test.p\n\n%--------------------------------------------------------------------------\n% File     : SYN075-1 : TPTP v9.2.1. Released v1.0.0.\n% Domain   : Syntactic\n% Problem  : Pelletier Problem 52\n% Version  : Especial.\n% English  :\n\n% Refs     : [Pel86] Pelletier (1986), Seventy-five Problems for Testing Au\n% Source   : [Pel86]\n% Names    : Pelletier 52 [Pel86]\n\n% Status   : Unsatisfiable\n% Rating   : 0.05 v9.0.0, 0.10 v8.1.0, 0.00 v7.5.0, 0.05 v7.4.0, 0.06 v7.3.0, 0.08 v7.1.0, 0.00 v7.0.0, 0.13 v6.4.0, 0.07 v6.3.0, 0.09 v6.2.0, 0.00 v6.1.0, 0.07 v6.0.0, 0.00 v5.5.0, 0.20 v5.3.0, 0.22 v5.2.0, 0.12 v5.1.0, 0.06 v5.0.0, 0.07 v4.1.0, 0.08 v4.0.1, 0.18 v4.0.0, 0.09 v3.7.0, 0.00 v3.3.0, 0.14 v3.2.0, 0.08 v3.1.0, 0.09 v2.7.0, 0.08 v2.6.0, 0.00 v2.5.0, 0.08 v2.4.0, 0.11 v2.2.1, 0.11 v2.2.0, 0.22 v2.1.0, 0.33 v2.0.0\n% Syntax   : Number of clauses     :   10 (   0 unt;   4 nHn;   8 RR)\n%            Number of literals    :   31 (  17 equ;  17 neg)\n%            Maximal clause size   :    4 (   3 avg)\n%            Maximal term depth    :    2 (   1 avg)\n%            Number of predicates  :    2 (   1 usr;   0 prp; 2-2 aty)\n%            Number of functors    :    5 (   5 usr;   2 con; 0-2 aty)\n%            Number of variables   :   23 (   2 sgn)\n% SPC      : CNF_UNS_RFO_SEQ_NHN\n\n% Comments :\n%--------------------------------------------------------------------------\ncnf(clause_1,axiom,\n    ( ~ big_f(X,Y)\n    | X = a ) ).\n\ncnf(clause_2,axiom,\n    ( ~ big_f(X,Y)\n    | Y = b ) ).\n\ncnf(clause_3,axiom,\n    ( X != a\n    | Y != b\n    | big_f(X,Y) ) ).\n\ncnf(clause_4,negated_conjecture,\n    ( ~ big_f(Y,f(X))\n    | Y != g(X)\n    | f(X) = X ) ).\n\ncnf(clause_5,negated_conjecture,\n    ( ~ big_f(Y,f(X))\n    | Y = g(X)\n    | big_f(h(X,Z),f(X))\n    | ~ big_f(h(X,Z),f(X)) ) ).\n\ncnf(clause_6,negated_conjecture,\n    ( Y != g(X)\n    | big_f(Y,f(X))\n    | f(X) = X ) ).\n\ncnf(clause_7,negated_conjecture,\n    ( Y != g(X)\n    | big_f(Y,f(X))\n    | big_f(h(X,Z),f(X))\n    | h(X,Z) = Z ) ).\n\ncnf(clause_8,negated_conjecture,\n    ( Y != g(X)\n    | big_f(Y,f(X))\n    | h(X,Z) != Z\n    | ~ big_f(h(X,Z),f(X)) ) ).\n\ncnf(clause_9,negated_conjecture,\n    ( f(X) != X\n    | big_f(h(X,Z),f(X))\n    | h(X,Z) = Z ) ).\n\ncnf(clause_10,negated_conjecture,\n    ( f(X) != X\n    | h(X,Z) != Z\n    | ~ big_f(h(X,Z),f(X)) ) ).\n\n%--------------------------------------------------------------------------\nOverwriting /tmp/test.p\n! vampire --mode casc -t 1 --input_syntax tptp /tmp/test.p\n% Input is clausal, will run a generic CNF schedule.\n% WARNING: time unlimited strategy and instruction limiting not in place - attempting to translate instructions to time\n% dis+1002_3:1_sil=8000:urr=ec_only:flr=on:random_seed=1107239751:st=5:i=104:sd=1:ep=RST:ss=axioms_1 on test for (1ds/104Mi)\n% Refutation not found, incomplete strategy\n% ------------------------------\n% Version: Vampire 5.0.1 (Release build, commit 1b13eaf on 2026-01-18 12:14:50 +0000)\n% Linked with Z3 4.14.0.0 3c47fd96cf5645d0c42b2c819d9e9a84380aa721 NOTFOUND\n% CaDiCaL version: 2.1.3\n% Termination reason: Refutation not found, incomplete strategy\n% Time elapsed: 0.008 s\n% Peak memory usage: 11 MB\n% ------------------------------\n% ------------------------------\n% WARNING: time unlimited strategy and instruction limiting not in place - attempting to translate instructions to time\n% lrs-1002_1_sil=2000:random_seed=3682962797:i=118:sd=20:ss=axioms:sgt=8_1 on test for (1ds/118Mi)\n% Refutation not found, incomplete strategy\n% ------------------------------\n% Version: Vampire 5.0.1 (Release build, commit 1b13eaf on 2026-01-18 12:14:50 +0000)\n% Linked with Z3 4.14.0.0 3c47fd96cf5645d0c42b2c819d9e9a84380aa721 NOTFOUND\n% CaDiCaL version: 2.1.3\n% Termination reason: Refutation not found, incomplete strategy\n% Time elapsed: 0.001 s\n% Peak memory usage: 11 MB\n% ------------------------------\n% ------------------------------\n% WARNING: time unlimited strategy and instruction limiting not in place - attempting to translate instructions to time\n% dis+10_2:3_to=lpo:sil=2000:sp=unary_first:acc=on:urr=ec_only:fd=off:sac=on:random_seed=803463828:i=132:fgj=on:fsr=off_1 on test for (1ds/132Mi)\n% Solution written to \"/tmp/vampire-proof-1368719\"\n% Refutation found. Thanks to Tanya!\n% SZS status Unsatisfiable for test\n% SZS output start Proof for test\nfof(f1,axiom,(\n  ( ! [X0,X1] : (~big_f(X0,X1) | X0 = a) )),\n  file('/tmp/test.p',unknown)).\nfof(f2,plain,(\n  ( ! [X0,X1] : (~big_f(X0,X1) | a = X0) )),\n  inference(reorient_equations,[],[f1])).\nfof(f5,axiom,(\n  ( ! [X0,X1] : (X0 != a | X1 != b | big_f(X0,X1)) )),\n  file('/tmp/test.p',unknown)).\nfof(f6,plain,(\n  ( ! [X0,X1] : (a != X0 | b != X1 | big_f(X0,X1)) )),\n  inference(reorient_equations,[],[f5])).\nfof(f7,negated_conjecture,(\n  ( ! [X0,X1] : (~big_f(X0,f(X1)) | X0 != g(X1) | f(X1) = X1) )),\n  file('/tmp/test.p',unknown)).\nfof(f8,plain,(\n  ( ! [X0,X1] : (~big_f(X0,f(X1)) | g(X1) != X0 | f(X1) = X1) )),\n  inference(reorient_equations,[],[f7])).\nfof(f11,negated_conjecture,(\n  ( ! [X0,X1] : (X0 != g(X1) | big_f(X0,f(X1)) | f(X1) = X1) )),\n  file('/tmp/test.p',unknown)).\nfof(f12,plain,(\n  ( ! [X0,X1] : (g(X1) != X0 | big_f(X0,f(X1)) | f(X1) = X1) )),\n  inference(reorient_equations,[],[f11])).\nfof(f17,negated_conjecture,(\n  ( ! [X0,X1] : (f(X0) != X0 | big_f(h(X0,X1),f(X0)) | h(X0,X1) = X1) )),\n  file('/tmp/test.p',unknown)).\nfof(f18,negated_conjecture,(\n  ( ! [X0,X1] : (h(X0,X1) != X1 | f(X0) != X0 | ~big_f(h(X0,X1),f(X0))) )),\n  file('/tmp/test.p',unknown)).\nfof(f19,plain,(\n  ( ! [X1] : (b != X1 | big_f(a,X1)) )),\n  inference(equality_resolution,[],[f6])).\nfof(f20,plain,(\n  big_f(a,b)),\n  inference(equality_resolution,[],[f19])).\nfof(f21,plain,(\n  ( ! [X1] : (~big_f(g(X1),f(X1)) | f(X1) = X1) )),\n  inference(equality_resolution,[],[f8])).\nfof(f22,plain,(\n  ( ! [X1] : (big_f(g(X1),f(X1)) | f(X1) = X1) )),\n  inference(equality_resolution,[],[f12])).\nfof(f25,plain,(\n  ( ! [X0] : (f(X0) = X0 | f(X0) = X0) )),\n  inference(resolution,[],[f22,f21])).\nfof(f26,plain,(\n  ( ! [X0] : (f(X0) = X0) )),\n  inference(duplicate_literal_removal,[],[f25])).\nfof(f27,plain,(\n  ( ! [X0,X1] : (X0 != X0 | big_f(h(X0,X1),X0) | h(X0,X1) = X1) )),\n  inference(superposition,[],[f17,f26])).\nfof(f28,plain,(\n  ( ! [X0,X1] : (big_f(h(X0,X1),X0) | h(X0,X1) = X1) )),\n  inference(trivial_inequality_removal,[],[f27])).\nfof(f33,definition,(\n  spl0_1 <=> big_f(a,b)),\n  introduced(definition,[new_symbols(naming,[spl0_1])],[avatar_definition])).\nfof(f35,plain,(\n  big_f(a,b) | ~spl0_1),\n  inference(avatar_component_clause,[],[f33])).\nfof(f36,plain,(\n  spl0_1),\n  inference(avatar_split_clause,[],[f20,f33])).\nfof(f39,plain,(\n  ( ! [X0,X1] : (a = h(X0,X1) | h(X0,X1) = X1) )),\n  inference(resolution,[],[f2,f28])).\nfof(f75,plain,(\n  ( ! [X0,X1] : (a != X1 | h(X0,X1) = X1) )),\n  inference(equality_factoring,[],[f39])).\nfof(f84,plain,(\n  ( ! [X0] : (a = h(X0,a)) )),\n  inference(equality_resolution,[],[f75])).\nfof(f85,plain,(\n  ( ! [X0] : (a != a | f(X0) != X0 | ~big_f(a,f(X0))) )),\n  inference(superposition,[],[f18,f84])).\nfof(f88,plain,(\n  ( ! [X0] : (f(X0) != X0 | ~big_f(a,f(X0))) )),\n  inference(trivial_inequality_removal,[],[f85])).\nfof(f91,plain,(\n  ( ! [X0] : (X0 != X0 | ~big_f(a,X0)) )),\n  inference(superposition,[],[f88,f26])).\nfof(f92,plain,(\n  ( ! [X0] : (~big_f(a,X0)) )),\n  inference(trivial_inequality_removal,[],[f91])).\nfof(f93,plain,(\n  $false | ~spl0_1),\n  inference(unit_resulting_resolution,[],[f35,f92])).\nfof(f96,plain,(\n  ~spl0_1),\n  inference(avatar_contradiction_clause,[],[f93])).\ncnf(s1, plain, spl0_1, inference(sat_conversion,[],[f36])).\ncnf(s3, plain, ~spl0_1, inference(sat_conversion,[],[f96])).\ncnf(s4, plain, $false, inference(rat,[],[s1,s3])).\nfof(f97,plain,(\n  $false),\n  inference(avatar_sat_refutation,[],[s4])).\n% SZS output end Proof for test\n% ------------------------------\n% Version: Vampire 5.0.1 (Release build, commit 1b13eaf on 2026-01-18 12:14:50 +0000)\n% Linked with Z3 4.14.0.0 3c47fd96cf5645d0c42b2c819d9e9a84380aa721 NOTFOUND\n% CaDiCaL version: 2.1.3\n% Termination reason: Refutation\n% Time elapsed: 0.005 s\n% Peak memory usage: 12 MB\n% ------------------------------\n% ------------------------------\n% Success in time 0.036 s\npython\nimport scryer\nm = scryer.Machine()\nm.load_module_string(\"test\",\n\"\"\"\n% (lift_lambdas = 1, lambda_to_forall = 1,unroll_only_formulas = 1, sine = (null))\n\n% No proof found!\n% SZS status Satisfiable\n% Processed positive unit clauses:\ncnf(i_0_16, plain, (zero->e2)).\ncnf(i_0_12, plain, (e0->l(e2))).\ncnf(i_0_14, plain, (mul(id,id)->e1)).\ncnf(i_0_15, plain, (mul(e2,e2)->e2)).\ncnf(i_0_11, plain, (mul(id,l(e2))->l(e2))).\ncnf(i_0_20, plain, (mul(l(X1),l(X2))->l(mul(X1,X2)))).\ncnf(i_0_17, plain, (mul(l(e2),id)->l(e2))).\ncnf(i_0_18, plain, (mul(e1,l(e2))->l(e2))).\n\n\"\"\"\n)\nm.query(\"cnf(X,Y,Z).\")\n[{'Y': Atom { value: \"plain\" },\n  'X': Atom { value: \"i_0_16\" },\n  'Z': Compound { functor: \"->\", args: [Atom { value: \"zero\" }, Atom { value: \"e2\" }] }},\n {'Y': Atom { value: \"plain\" },\n  'X': Atom { value: \"i_0_12\" },\n  'Z': Compound { functor: \"->\", args: [Atom { value: \"e0\" }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] }},\n {'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Atom { value: \"id\" }, Atom { value: \"id\" }] }, Atom { value: \"e1\" }] },\n  'Y': Atom { value: \"plain\" },\n  'X': Atom { value: \"i_0_14\" }},\n {'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Atom { value: \"e2\" }, Atom { value: \"e2\" }] }, Atom { value: \"e2\" }] },\n  'Y': Atom { value: \"plain\" },\n  'X': Atom { value: \"i_0_15\" }},\n {'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Atom { value: \"id\" }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] },\n  'X': Atom { value: \"i_0_11\" },\n  'Y': Atom { value: \"plain\" }},\n {'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Compound { functor: \"l\", args: [Var { name: \"_A\" }] }, Compound { functor: \"l\", args: [Var { name: \"_B\" }] }] }, Compound { functor: \"l\", args: [Compound { functor: \"mul\", args: [Var { name: \"_A\" }, Var { name: \"_B\" }] }] }] },\n  'X': Atom { value: \"i_0_20\" },\n  'Y': Atom { value: \"plain\" }},\n {'X': Atom { value: \"i_0_17\" },\n  'Y': Atom { value: \"plain\" },\n  'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }, Atom { value: \"id\" }] }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] }},\n {'X': Atom { value: \"i_0_18\" },\n  'Y': Atom { value: \"plain\" },\n  'Z': Compound { functor: \"->\", args: [Compound { functor: \"mul\", args: [Atom { value: \"e1\" }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] }, Compound { functor: \"l\", args: [Atom { value: \"e2\" }] }] }}]\npython\nfrom kdrag.all import *\nfrom IPython.display import display\nx,y,z = smt.Ints(\"x y z\")\n\npprint(If(x > 17, And(x > 7, ForAll(x, x + y >= y)), If(False, True, False)))\n\nl = kd.Lemma(ForAll(x, x > 17, y > 24, And(x > 7, ForAll(x, x + y >= y))))\nl.fix()\nl.intros()\nl.split(at=-1)\n#l.intros()\ndisplay(pprint(l))\n#l\n```\n\n$\\displaystyle \\begin{array}{ll} \\text{fix} & x_{125} : \\mathsf{Int} \\ h_{0} & x_{125} > 17 \\ h_{1} & y > 24 \\ \\hline \\vdash & (x_{125} > 7) \\wedge (\\forall x. x + y \\geq y) \\end{array}$\n\n``` python\nfrom kdrag.all import *\nfrom IPython.display import display\n\nlatex.to_latex(kd.seq(1,2,3))\npprint(kd.seq(1,2,3))\n\neps = Real(\"eps\")\npprint(eps + 1)\n\nx = smt.BitVec(\"x\", 16)\npprint(x + 7)\n#latex.to_latex(x + 7)\npprint(Concat(x,x,x))\npprint(Extract(6,5, x) + 2)\npprint(Implies(x > 7, x > 4))\n```\n\n$\\displaystyle (\\text{x} >_s \\texttt{0x0007}) \\rightarrow (\\text{x} >_s \\texttt{0x0004})$", "url": "https://wpnews.pro/news/reading-proof-objects-and-completed-rewrite-systems-from-eprover-into", "canonical_source": "https://www.philipzucker.com/proof_processing/", "published_at": "2026-05-17 00:00:00+00:00", "updated_at": "2026-06-14 11:16:58.459840+00:00", "lang": "en", "topics": ["ai-research", "ai-tools", "developer-tools"], "entities": ["E-prover", "Knuckledragger", "Z3py", "Scryer Prolog", "Knuth-Bendix completion", "TPTP"], "alternates": {"html": "https://wpnews.pro/news/reading-proof-objects-and-completed-rewrite-systems-from-eprover-into", "markdown": "https://wpnews.pro/news/reading-proof-objects-and-completed-rewrite-systems-from-eprover-into.md", "text": "https://wpnews.pro/news/reading-proof-objects-and-completed-rewrite-systems-from-eprover-into.txt", "jsonld": "https://wpnews.pro/news/reading-proof-objects-and-completed-rewrite-systems-from-eprover-into.jsonld"}}