├── Evaluation ├── Calc.lean ├── scaling-proof-length │ ├── .gitignore │ ├── Rplots.pdf │ ├── scaling-exponential.png │ ├── Scaling.v │ ├── scaling-exponential.csv │ ├── stats.csv │ ├── plotscaling.R │ ├── scaling-geometric.csv │ ├── Scaling.lean │ ├── scaling-exponential.py │ └── scaling-geometric.py ├── scaling-space-and-proof │ ├── .gitignore │ ├── Rplots.pdf │ ├── scaling-geometric.png │ ├── scaling-exponential.png │ ├── Scaling.v │ ├── scaling-exponential.csv │ ├── stats.csv │ ├── plotscaling.R │ ├── scaling-geometric.csv │ ├── Scaling.lean │ └── scaling-exponential.py ├── GeneralizedRewriting.lean ├── GroupsKnuthBendixAuto.v ├── Reviewer1 │ ├── counts.sh │ └── gen_count.py ├── scaling-search-space │ ├── plotscaling.R │ └── scaling-geometric.py ├── GroupsKnuthBendixSimp.lean ├── GroupsKnuthBendixAesop.lean ├── GroupsKnuthBendix.v ├── GroupsKnuthBendix.lean ├── FunctionalProgramming.lean └── GroupsKnuthBendixExploded.v ├── ForeignLean ├── .gitignore ├── Main.lean ├── Makefile └── myfuns.cpp ├── json-egg ├── json-inputs │ ├── egg.json │ ├── non-equal-test.json │ ├── transitive-test.json │ ├── transitive-rev-test.json │ ├── test-position.json │ ├── test-position-vars.json │ ├── group-test.json │ ├── group-test-ap.json │ ├── instantiation-test.json │ ├── group-inv-egg-cleaned-up.json │ ├── group-inv-simplified.json │ ├── group-inv-mul-working.json │ ├── group-inv-egg-not-cleaned-up.json │ ├── group-inv-inv-broken-rewrites.json │ ├── group-inv-inv-working-rewrites.json │ ├── group-inv-mul-slow-reduced.json │ ├── group-inv-mul-slow.json │ ├── test.json │ ├── group-inv-mul-cancel-left.json │ └── slow.json ├── .gitignore ├── solution.md ├── compare-json-reqs.py ├── Cargo.toml ├── src │ └── scheduler.rs └── Cargo.lock ├── lean-toolchain ├── EggTactic ├── Tracing.lean ├── Test.lean └── Egraph.lean ├── Main.lean ├── lake-manifest.json ├── .gitignore ├── ffi-egg ├── .gitignore ├── Cargo.toml └── src │ ├── main.rs │ ├── math.rs │ └── lib.rs ├── README.md ├── lakefile.lean ├── example.lean └── PrettyPrinter.lean /Evaluation/Calc.lean: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ForeignLean/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /json-egg/json-inputs/egg.json: -------------------------------------------------------------------------------- 1 | /tmp/egg.json -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | -------------------------------------------------------------------------------- /lean-toolchain: -------------------------------------------------------------------------------- 1 | leanprover/lean4:nightly-2023-03-31 2 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | -------------------------------------------------------------------------------- /json-egg/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /build 3 | /lean_packages 4 | tags 5 | -------------------------------------------------------------------------------- /EggTactic/Tracing.lean: -------------------------------------------------------------------------------- 1 | import Lean 2 | initialize Lean.registerTraceClass `EggTactic.egg 3 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/Rplots.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencompl/egg-tactic-code/HEAD/Evaluation/scaling-proof-length/Rplots.pdf -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/Rplots.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencompl/egg-tactic-code/HEAD/Evaluation/scaling-space-and-proof/Rplots.pdf -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/scaling-exponential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencompl/egg-tactic-code/HEAD/Evaluation/scaling-proof-length/scaling-exponential.png -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/scaling-geometric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencompl/egg-tactic-code/HEAD/Evaluation/scaling-space-and-proof/scaling-geometric.png -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/scaling-exponential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opencompl/egg-tactic-code/HEAD/Evaluation/scaling-space-and-proof/scaling-exponential.png -------------------------------------------------------------------------------- /Main.lean: -------------------------------------------------------------------------------- 1 | -- import PrettyPrinter 2 | import EggTactic 3 | import EggTactic.Test 4 | import EggTactic.Egraph 5 | import EggTactic.Sexp 6 | 7 | def main : IO Unit := 8 | IO.println "Hello, hello!" 9 | -------------------------------------------------------------------------------- /json-egg/json-inputs/non-equal-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "a", 4 | "target-rhs": "none", 5 | "rewrites": [ 6 | {"name": "a_eq_b", "lhs": "a" , "rhs": "b"} 7 | ] 8 | 9 | } 10 | -------------------------------------------------------------------------------- /lake-manifest.json: -------------------------------------------------------------------------------- 1 | {"version": 4, 2 | "packagesDir": "lake-packages", 3 | "packages": 4 | [{"git": 5 | {"url": "https://github.com/leanprover/std4", 6 | "subDir?": null, 7 | "rev": "529a6587a15f1a4ce22a96c422d90f5235b0fecd", 8 | "name": "Std", 9 | "inputRev?": "529a6"}}]} 10 | -------------------------------------------------------------------------------- /json-egg/json-inputs/transitive-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "a", 4 | "target-rhs": "c", 5 | "rewrites": [ 6 | {"name": "a_eq_b", "lhs": "a" , "rhs": "b"}, 7 | {"name": "b_eq_c", "lhs": "b" , "rhs": "c"} 8 | ] 9 | 10 | } 11 | -------------------------------------------------------------------------------- /json-egg/json-inputs/transitive-rev-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "c", 4 | "target-rhs": "a", 5 | "rewrites": [ 6 | {"name": "a_eq_b", "lhs": "a" , "rhs": "b"}, 7 | {"name": "b_eq_c", "lhs": "b" , "rhs": "c"} 8 | ] 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/Scaling.v: -------------------------------------------------------------------------------- 1 | Inductive B := 2 | | O | I. 3 | 4 | Theorem count_upward_v3: forall 5 | (count: B -> B -> B -> B) 6 | (count_0: forall (b2 b1: B), count b2 b1 O = count b2 b1 I) 7 | (count_1: forall (b2: B), count b2 O I = count b2 I O) 8 | (count_2: count O I I = count I O O), count I I I = count O O O. 9 | Proof. 10 | intros. 11 | congruence. 12 | Qed. 13 | 14 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/Scaling.v: -------------------------------------------------------------------------------- 1 | Inductive B := 2 | | O | I. 3 | 4 | Theorem count_upward_v3: forall 5 | (count: B -> B -> B -> B) 6 | (count_0: forall (b2 b1: B), count b2 b1 O = count b2 b1 I) 7 | (count_1: forall (b2: B), count b2 O I = count b2 I O) 8 | (count_2: count O I I = count I O O), count I I I = count O O O. 9 | Proof. 10 | intros. 11 | congruence. 12 | Qed. 13 | 14 | -------------------------------------------------------------------------------- /json-egg/json-inputs/test-position.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "(op x x x x x)", 4 | "target-rhs": "(op x x y x z)", 5 | "rewrites": [ 6 | { 7 | "name": "1", 8 | "lhs": "x", 9 | "rhs": "y" 10 | }, 11 | { 12 | "name": "0", 13 | "lhs": "x", 14 | "rhs": "z" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /json-egg/json-inputs/test-position-vars.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "(plus (plus (times x y) (times y x)) (plus (times x y) (times y x)))", 4 | "target-rhs": "(plus (plus (times y x) (times x y)) (plus (times x y) (times y x)))", 5 | "rewrites": [ 6 | { 7 | "name": "1", 8 | "lhs": "(plus ?a ?b)", 9 | "rhs": "(plus ?b ?a)" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.code-* 2 | /target 3 | /build 4 | /lean_packages 5 | tags 6 | *.code-workspace 7 | lake-packages/ 8 | lean_packages/ 9 | *~ 10 | \#* 11 | .#* 12 | *.lock 13 | build 14 | GPATH 15 | GRTAGS 16 | GSYMS 17 | GTAGS 18 | .projectile 19 | .lean_options 20 | .vs 21 | compile_commands.json 22 | *.idea 23 | tasks.json 24 | settings.json 25 | .gdb_history 26 | .vscode 27 | *.produced.out 28 | CMakeSettings.json 29 | CppProperties.json 30 | tags 31 | utils/ 32 | -------------------------------------------------------------------------------- /ffi-egg/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "(^-1 (^-1 a))", 4 | "target-rhs": "a", 5 | "timeout": 10, 6 | "dump-graph": false, 7 | "rewrites": [ 8 | {"name": "assoc-mul", "lhs": "(* ?a (* ?b ?c))" , "rhs": "(* (* ?a ?b) ?c)"}, 9 | {"name": "inv-left", "lhs": "(* (^-1 ?a) ?a)" , "rhs": "1"}, 10 | {"name": "mul-one'", "lhs": "?a" , "rhs": "(* ?a 1)" }, 11 | {"name": "inv-right-a", "lhs": "1" , "rhs": "(* a (^-1 a))"} 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-test-ap.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "(ap ^-1 (ap ^-1 a))", 4 | "target-rhs": "a", 5 | "rewrites": [ 6 | {"name": "assoc-mul", "lhs": "(ap (ap * ?a) (ap (ap * ?b) ?c))" , "rhs": "(ap (ap * (ap (ap * ?a) ?b)) ?c)"}, 7 | {"name": "inv-left", "lhs": "(ap (ap * (ap ^-1 ?a)) ?a)" , "rhs": "1"}, 8 | {"name": "mul-one'", "lhs": "?a" , "rhs": "(ap (ap * ?a) 1)" }, 9 | {"name": "inv-right-a", "lhs": "1" , "rhs": "(ap (ap * a) (ap ^-1 a))"} 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Evaluation/GeneralizedRewriting.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | 3 | inductive EQ {α : Type} (a : α) : α → Type where 4 | | refl : EQ a a 5 | 6 | def EQ.trans (h₁ : EQ a b) (h₂ : EQ b c) : EQ a c := by 7 | cases h₁; cases h₂; constructor 8 | 9 | instance : Trans (@EQ α) (@EQ α) (@EQ α) where 10 | trans := EQ.trans 11 | 12 | infix:50 " ≋ " => EQ 13 | 14 | example (h₁ : EQ a b) (h₂ : b = c) (h₃ : EQ c d) : EQ a d := by 15 | calc a ≋ b := h₁ 16 | _ = c := h₂ 17 | _ ≋ d := h₃ 18 | 19 | example (h₁ : EQ a b) (h₂ : b = c) (h₃ : EQ c d) : EQ a d := by 20 | eggxplosion [h₁, h₂, h₃] 21 | -------------------------------------------------------------------------------- /json-egg/solution.md: -------------------------------------------------------------------------------- 1 | - Create a separate e-graph with a term, then apply the rule to match against 2 | the e-graph. Then find the match at the head node. 3 | 4 | - If we have 1 = (mul (?a (inv ?a))), we can directly modify the e-graph and 5 | set up an equivalence between the LHS and the RHS. 6 | 7 | 8 | - Enumerate the e-class for LHS and RHS if we don't have the equality, then 9 | show the end-user the variants of the LHS and the RHS. 10 | 11 | - Generate lemmas for all the "obvious" equalities. 12 | 13 | - Sketches? Skip steps in a proof? 14 | 15 | - rule scheduling to decide when to run different rules. 16 | - iteration limit, node limit, time limit. 17 | -------------------------------------------------------------------------------- /json-egg/compare-json-reqs.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | import json 3 | from sys import argv 4 | 5 | def compare_rewrites(name1,name2,j1,j2): 6 | for rw in j1['rewrites']: 7 | found = False 8 | for rw_cmp in j2['rewrites']: 9 | if rw['lhs'] == rw_cmp['lhs'] and rw['rhs'] == rw_cmp['rhs']: 10 | found = True 11 | if not found: 12 | print(f"{name1} has rw: {rw} which is not in {name2}") 13 | 14 | with open(argv[1],'r') as f1: 15 | with open(argv[2],'r') as f2: 16 | j1 = json.load(f1) 17 | j2 = json.load(f2) 18 | compare_rewrites(argv[1],argv[2],j1,j2) 19 | compare_rewrites(argv[2],argv[1],j2,j1) 20 | -------------------------------------------------------------------------------- /ffi-egg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "egg-herbie" 3 | version = "0.2.2" 4 | authors = [ "Oliver Flatt ", "Max Willsey " ] 5 | edition = "2018" 6 | 7 | 8 | [dependencies] 9 | egg = "0.6" 10 | 11 | log = "0.4" 12 | indexmap = "1" 13 | libc = "0.2.71" 14 | smallvec = "1.4.0" 15 | 16 | num-rational = "0.3.0" 17 | num-integer = "0.1.42" 18 | num-bigint = "0.3.0" 19 | num-traits = "0.2.12" 20 | env_logger = { version = "0.7", default-features = false } 21 | 22 | # [features] 23 | # upward-merging = ["egg/upward-merging"] 24 | 25 | [lib] 26 | name = "egg_math" 27 | crate-type = ["rlib", "cdylib"] 28 | 29 | [profile.test] 30 | debug = true 31 | opt-level = 1 32 | 33 | [profile.release] 34 | debug = true 35 | lto = "fat" 36 | codegen-units = 1 37 | -------------------------------------------------------------------------------- /json-egg/json-inputs/instantiation-test.json: -------------------------------------------------------------------------------- 1 | { "request":"perform-rewrite", 2 | "target-lhs":"(\"HSub.hSub.{0 0 0} Int Int Int (instHSub.{0} Int Int.instSubInt) _uniq.13337 _uniq.13337\")", 3 | "target-rhs":"(\"HSub.hSub.{0 0 0} Int Int Int (instHSub.{0} Int Int.instSubInt) _uniq.13338 _uniq.13338\")", 4 | "rewrites":[{"name":"gk", 5 | "lhs":"(\"HSub.hSub.{0 0 0} Int Int Int (instHSub.{0} Int Int.instSubInt) _uniq.13338 _uniq.13338\")", 6 | "rhs":"(\"OfNat.ofNat.{0} Int 0 (Int.instOfNatInt 0)\")"}, 7 | { "name":"gh", 8 | "lhs":"(\"HSub.hSub.{0 0 0} Int Int Int (instHSub.{0} Int Int.instSubInt) _uniq.13337 _uniq.13337\")", 9 | "rhs":"(\"OfNat.ofNat.{0} Int 0 (Int.instOfNatInt 0)\")"} 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-egg-cleaned-up.json: -------------------------------------------------------------------------------- 1 | { 2 | "request":"perform-rewrite", 3 | "target-lhs":"(ap inv (ap inv x))", 4 | "timeout" : 30, 5 | "dump-graph" : false, 6 | "target-rhs":"x", 7 | "rewrites":[ 8 | { 9 | "name":"3", 10 | "lhs":"one", 11 | "rhs":"(ap (ap mul x) (ap inv x))" 12 | }, 13 | { 14 | "name":"2", 15 | "lhs":"?_uniq.562", 16 | "rhs":"(ap (ap mul ?_uniq.562) one)" 17 | }, 18 | { 19 | "name":"1", 20 | "lhs":"(ap (ap mul (ap inv ?_uniq.561)) ?_uniq.561)", 21 | "rhs":"one" 22 | }, 23 | { 24 | "name":"0", 25 | "lhs":"(ap (ap mul ?_uniq.558) (ap (ap mul ?_uniq.559) ?_uniq.560))", 26 | "rhs":"(ap (ap mul (ap (ap mul ?_uniq.558) ?_uniq.559)) ?_uniq.560)" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-simplified.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "perform-rewrite", 3 | "target-lhs": "(ap v7 (ap v7 v6))", 4 | "target-rhs": "v6", 5 | "rewrites": [ 6 | { 7 | "name": "3", 8 | "lhs": "v8", 9 | "rhs": "(ap (ap v9 v6) (ap v7 v6))" 10 | }, 11 | { 12 | "name": "2", 13 | "lhs": "?_uniq.562", 14 | "rhs": "(ap (ap v9 ?_uniq.562) v8)" 15 | }, 16 | { 17 | "name": "1", 18 | "lhs": "(ap (ap v9 (ap v7 ?_uniq.561)) ?_uniq.561)", 19 | "rhs": "v8" 20 | }, 21 | { 22 | "name": "0", 23 | "lhs": "(ap (ap v9 ?_uniq.558) (ap (ap v9 ?_uniq.559) ?_uniq.560))", 24 | "rhs": "(ap (ap v9 (ap (ap v9 ?_uniq.558) ?_uniq.559)) ?_uniq.560)" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /json-egg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "egg-herbie" 3 | version = "0.2.2" 4 | authors = [ "Oliver Flatt ", "Max Willsey " ] 5 | edition = "2018" 6 | 7 | 8 | [dependencies] 9 | egg = { git="https://github.com/egraphs-good/egg.git", rev="e19a18f", features=["serde-1"] } 10 | #egg = { version = "0.9", features=["serde-1"] } 11 | serde = { version = "1", features = ["derive"] } 12 | serde_json = "1" 13 | 14 | log = "0.4" 15 | indexmap = "1" 16 | libc = "0.2.71" 17 | smallvec = "1.4.0" 18 | symbolic_expressions = "5.0.3" 19 | 20 | num-rational = "0.3.0" 21 | num-integer = "0.1.42" 22 | num-bigint = "0.3.0" 23 | num-traits = "0.2.12" 24 | env_logger = { version = "0.7", default-features = false } 25 | 26 | [profile.test] 27 | debug = true 28 | opt-level = 1 29 | 30 | [profile.release] 31 | debug = true 32 | lto = "fat" 33 | codegen-units = 1 34 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/scaling-exponential.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,1,0.6255600559961749 3 | lean-simp,1,0.6359152189979795 4 | coq,1,0.12317631300538778 5 | lean-egg,2,0.656288433005102 6 | lean-simp,2,0.6216333779884735 7 | coq,2,0.1269918059988413 8 | lean-egg,3,0.6812429380079266 9 | lean-simp,3,ERR 10 | coq,3,0.13758863999100868 11 | lean-egg,4,0.6983315639954526 12 | lean-simp,4,ERR 13 | coq,4,0.4960235339967767 14 | lean-egg,5,0.713191539995023 15 | lean-simp,5,ERR 16 | coq,5,1.9209456329990644 17 | lean-egg,6,0.7455722380109364 18 | lean-simp,6,ERR 19 | coq,6,ERR 20 | lean-egg,7,0.7649258650053525 21 | lean-simp,7,ERR 22 | coq,7,ERR 23 | lean-egg,8,0.7567150990071241 24 | lean-simp,8,ERR 25 | coq,8,ERR 26 | lean-egg,9,0.7967491140007041 27 | lean-simp,9,ERR 28 | coq,9,ERR 29 | lean-egg,10,0.893793358001858 30 | lean-simp,10,ERR 31 | coq,10,ERR 32 | lean-egg,11,1.0878147110051941 33 | lean-simp,11,ERR 34 | coq,11,ERR 35 | -------------------------------------------------------------------------------- /ForeignLean/Main.lean: -------------------------------------------------------------------------------- 1 | constant SPointed : NonemptyType 2 | def S : Type := SPointed.type 3 | instance : Nonempty S := SPointed.property 4 | 5 | @[extern "lean_mk_S"] constant mkS (x y : UInt32) (s : @& String) : S 6 | @[extern "lean_S_add_x_y"] constant S.addXY (s : @& S) : UInt32 7 | @[extern "lean_S_string"] constant S.string (s : @& S) : String 8 | -- The following 3 externs have side effects. Thus, we put them in IO. 9 | @[extern "lean_S_global_append"] constant appendToGlobalS (str : @& String) : IO Unit 10 | @[extern "lean_S_global_string"] constant getGlobalString : IO String 11 | @[extern "lean_S_update_global"] constant updateGlobalS (s : @& S) : IO Unit 12 | 13 | 14 | def main : IO Unit := do 15 | IO.println (mkS 10 20 "hello").addXY 16 | IO.println (mkS 10 20 "hello").string 17 | appendToGlobalS "foo" 18 | appendToGlobalS "bla" 19 | getGlobalString >>= IO.println 20 | updateGlobalS (mkS 0 0 "world") 21 | getGlobalString >>= IO.println 22 | pure () 23 | 24 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/scaling-exponential.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,1,0.6796798589930404 3 | lean-simp,1,0.6796187160070986 4 | coq,1,0.12695215400890447 5 | lean-egg,2,0.6942399209947325 6 | lean-simp,2,0.6787896699970588 7 | coq,2,0.12700987100834027 8 | lean-egg,3,0.7095766180136707 9 | lean-simp,3,0.6720019589993171 10 | coq,3,0.13025340900640003 11 | lean-egg,4,0.7360819420136977 12 | lean-simp,4,0.6910068780125584 13 | coq,4,0.13695060898317024 14 | lean-egg,5,0.7895242430095095 15 | lean-simp,5,0.6884844560117926 16 | coq,5,0.14768485800595954 17 | lean-egg,6,0.9077971520018764 18 | lean-simp,6,0.7033233670226764 19 | coq,6,0.19974560601986013 20 | lean-egg,7,1.252460493997205 21 | lean-simp,7,0.7313971309922636 22 | coq,7,0.3409807049902156 23 | lean-egg,8,1.9782920159923378 24 | lean-simp,8,0.7678543670044746 25 | coq,8,1.1186473580019083 26 | lean-egg,9,4.127158749004593 27 | lean-simp,9,0.8613429969991557 28 | coq,9,3.8445353459974285 29 | lean-egg,10,11.058134030987276 30 | lean-simp,10,1.0793156619765796 31 | coq,10,ERR 32 | lean-egg,11,33.85495507597807 33 | lean-simp,11,1.5366898530046456 34 | coq,11,ERR 35 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendixAuto.v: -------------------------------------------------------------------------------- 1 | Parameter G : Type. 2 | Parameter inv: G -> G. 3 | Parameter mul: G -> G -> G. 4 | Parameter one: G. 5 | Axiom assocMul : forall (a b c: G), mul a (mul b c) = (mul (mul a b) c). 6 | Axiom invLeft: forall (a: G), mul (inv a) a = one. 7 | Axiom oneMul: forall (a: G), mul one a = a. 8 | Axiom mulOne: forall (a: G), mul a one = a. 9 | Axiom invRight: forall (a: G), mul a (inv a) = one. 10 | Global Hint Rewrite assocMul invLeft mulOne oneMul invRight : base0. 11 | 12 | Theorem inv_mul_cancel_left 13 | (x y : G) 14 | : (mul (inv x) (mul x y)) = y. 15 | Proof. 16 | try autorewrite with base0. reflexivity. Qed. 17 | 18 | Theorem mul_inv_cancel_left 19 | (x y : G) 20 | : (mul x (mul (inv x) y)) = y. 21 | Proof. 22 | try autorewrite with base0. reflexivity. Qed. 23 | 24 | Theorem inv_mul 25 | (x y : G) 26 | : (inv (mul x y)) = (mul (inv y) (inv x)). 27 | Proof. 28 | try autorewrite with base0. Admitted. 29 | 30 | Theorem one_inv 31 | (x y : G) 32 | : (inv one) = one. 33 | Proof. 34 | try autorewrite with base0. Admitted. 35 | 36 | Theorem inv_inv 37 | (x: G) 38 | : (inv (inv x) = x). 39 | Proof. 40 | try autorewrite with base0. Admitted. 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `egg` Tactic: E-Graphs in Lean 2 | =============================== 3 | 4 | This repository contains work-in-progress of a [Lean4](https://leanprover.github.io/) tactic to prove equalities, powered by E-Graphs and the [egg](https://egraphs-good.github.io/) project. 5 | 6 | # Requirements 7 | 8 | To use this tactic you will need a recent version of Lean4, ideally a nightly (see [here](https://leanprover.github.io/lean4/doc/quickstart.html) for installation instructions. 9 | 10 | You will also need an installation of [Rust](https://www.rust-lang.org/learn/get-started) to compile the egg integration. 11 | 12 | # Building 13 | 14 | To build this, just run the following in the `json-egg` directory: 15 | ``` 16 | cargo build --release 17 | ``` 18 | 19 | # Using 20 | 21 | Just open the `EggTactic/Test.lean` file in your favorite editor that supports Lean4 and you should be able to use the `rawEgg` tactic to prove your equalities. We have not yet packaged this to use directly in your own project. 22 | 23 | # Work in progress 24 | 25 | This project is work in progress and is not yet ready for everyday use. Don't worry however, you won't be able to prove anything incorrect with this. In the worst case, the tactic will fail when it shouldn't. 26 | 27 | 28 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/stats.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,1,0.20212429203093052 3 | lean-simp,1,0.16639754106290638 4 | coq,1,0.12816075002774596 5 | lean-egg,2,0.18037495901808143 6 | lean-simp,2,0.16827074997127056 7 | coq,2,0.1301167500205338 8 | lean-egg,3,0.17784237512387335 9 | lean-simp,3,0.16576374997384846 10 | coq,3,0.12497629201970994 11 | lean-egg,4,0.1851215830538422 12 | lean-simp,4,0.1674012909643352 13 | coq,4,0.12463958305306733 14 | lean-egg,5,0.20210062502883375 15 | lean-simp,5,0.191266915993765 16 | coq,5,0.12774595781229436 17 | lean-egg,6,0.23353520804084837 18 | lean-simp,6,0.1722347498871386 19 | coq,6,0.14017724990844727 20 | lean-egg,7,0.3019998329691589 21 | lean-simp,7,0.1788757499307394 22 | coq,7,0.1759405001066625 23 | lean-egg,8,0.5106685000937432 24 | lean-simp,8,0.18962095910683274 25 | coq,8,0.3737069161143154 26 | lean-egg,9,1.1413808749057353 27 | lean-simp,9,0.224318458000198 28 | coq,9,1.3377918750047684 29 | lean-egg,10,3.2249896249268204 30 | lean-simp,10,0.2845560829155147 31 | coq,10,ERR 32 | lean-egg,11,ERR 33 | lean-simp,11,0.3961865829769522 34 | coq,11,ERR 35 | lean-egg,12,ERR 36 | lean-simp,12,0.6667643329128623 37 | coq,12,ERR 38 | lean-egg,13,ERR 39 | lean-simp,13,ERR 40 | coq,13,ERR 41 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/stats.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,1,0.20212429203093052 3 | lean-simp,1,0.16639754106290638 4 | coq,1,0.12816075002774596 5 | lean-egg,2,0.18037495901808143 6 | lean-simp,2,0.16827074997127056 7 | coq,2,0.1301167500205338 8 | lean-egg,3,0.17784237512387335 9 | lean-simp,3,0.16576374997384846 10 | coq,3,0.12497629201970994 11 | lean-egg,4,0.1851215830538422 12 | lean-simp,4,0.1674012909643352 13 | coq,4,0.12463958305306733 14 | lean-egg,5,0.20210062502883375 15 | lean-simp,5,0.191266915993765 16 | coq,5,0.12774595781229436 17 | lean-egg,6,0.23353520804084837 18 | lean-simp,6,0.1722347498871386 19 | coq,6,0.14017724990844727 20 | lean-egg,7,0.3019998329691589 21 | lean-simp,7,0.1788757499307394 22 | coq,7,0.1759405001066625 23 | lean-egg,8,0.5106685000937432 24 | lean-simp,8,0.18962095910683274 25 | coq,8,0.3737069161143154 26 | lean-egg,9,1.1413808749057353 27 | lean-simp,9,0.224318458000198 28 | coq,9,1.3377918750047684 29 | lean-egg,10,3.2249896249268204 30 | lean-simp,10,0.2845560829155147 31 | coq,10,ERR 32 | lean-egg,11,ERR 33 | lean-simp,11,0.3961865829769522 34 | coq,11,ERR 35 | lean-egg,12,ERR 36 | lean-simp,12,0.6667643329128623 37 | coq,12,ERR 38 | lean-egg,13,ERR 39 | lean-simp,13,ERR 40 | coq,13,ERR 41 | -------------------------------------------------------------------------------- /ForeignLean/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= c++ 2 | CPPFLAGS = -O3 3 | include lean.mk 4 | 5 | CPP_SRCS = myfuns.cpp 6 | CPP_OBJS = $(addprefix $(OUT)/testcpp/,$(CPP_SRCS:.cpp=.o)) 7 | 8 | all: run_test run_interp 9 | 10 | $(OUT)/testcpp/%.o: %.cpp 11 | @mkdir -p "$(@D)" 12 | $(CXX) -std=c++14 -c -o $@ $< $(CPPFLAGS) `leanc --print-cflags` 13 | 14 | # to avoid conflicts between the system C++ stdlib needed by the above object file and the internal one used in the Lean runtime, 15 | # we need to dynamically link the Lean runtime. 16 | 17 | ifeq ($(OS),Windows_NT) 18 | # make S.so find testcpp.so 19 | export PATH := $(BIN_OUT):$(PATH) 20 | else 21 | # find libleanshared.so 22 | TEST_SHARED_LINK_FLAGS := -Wl,-rpath,`lean --print-prefix`/lib/lean 23 | endif 24 | 25 | $(BIN_OUT)/testcpp.so: $(CPP_OBJS) | $(BIN_OUT) 26 | $(CXX) -shared -o $@ $^ `leanc -shared --print-ldflags` 27 | 28 | $(BIN_OUT)/test: $(LIB_OUT)/libMain.a $(CPP_OBJS) | $(BIN_OUT) 29 | $(CXX) -o $@ $^ `leanc -shared --print-ldflags` -lleanshared $(TEST_SHARED_LINK_FLAGS) 30 | 31 | run_test: $(BIN_OUT)/test 32 | $^ 33 | 34 | # also test interpreter; see doc/dev/ffi.md 35 | $(BIN_OUT)/S.so: $(C_OUT)/Main/S.c $(BIN_OUT)/testcpp.so 36 | leanc -shared -o $@ $^ 37 | 38 | run_interp: $(BIN_OUT)/S.so 39 | lean --load-dynlib=$^ --run Main.lean 40 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-mul-working.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 3, 3 | "dump-graph" : false, 4 | "rewrites": [ 5 | { 6 | "rhs": "(ap (ap mul y) (ap inv y))", 7 | "lhs": "one", 8 | "name": "6" 9 | }, 10 | { 11 | "rhs": "(ap (ap mul x) (ap inv x))", 12 | "lhs": "one", 13 | "name": "5" 14 | }, 15 | { 16 | "rhs": "(ap (ap mul one) (ap inv one))", 17 | "lhs": "one", 18 | "name": "4" 19 | }, 20 | { 21 | "rhs": "?a", 22 | "lhs": "(ap (ap mul one) ?a)", 23 | "name": "3" 24 | }, 25 | { 26 | "rhs": "(ap (ap mul ?a) one)", 27 | "lhs": "?a", 28 | "name": "2" 29 | }, 30 | { 31 | "rhs": "one", 32 | "lhs": "(ap (ap mul (ap inv ?a)) ?a)", 33 | "name": "1" 34 | }, 35 | { 36 | "rhs": "(ap (ap mul (ap (ap mul ?a) ?b)) ?c)", 37 | "lhs": "(ap (ap mul ?a) (ap (ap mul ?b) ?c))", 38 | "name": "0" 39 | } 40 | ], 41 | "target-rhs": "(ap (ap mul (ap inv y)) (ap inv x))", 42 | "target-lhs": "(ap inv (ap (ap mul x) y))", 43 | "request": "perform-rewrite" 44 | } 45 | -------------------------------------------------------------------------------- /Evaluation/Reviewer1/counts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | header() { 6 | echo "rewrites|proof|time" 7 | } 8 | 9 | gen_count() { 10 | python3 ./gen_count.py "$@" 11 | } 12 | 13 | warmup() { 14 | for proof in autorewrite congruence simp; do 15 | gen_count --proof $proof --time 300 >/dev/null 16 | done 17 | } 18 | 19 | gen_data() { 20 | for proof in autorewrite congruence simp; do 21 | for n in $(seq 1 15) $(seq 16 2 24) $(seq 25 3 60); do 22 | out=$(gen_count --proof $proof --time $((n * n))) 23 | echo "$out" 24 | # if proof times out, don't try larger numbers 25 | if [[ "$out" =~ .*\|inf$ ]]; then 26 | break 27 | fi 28 | if [ "$n" -ge 10 ]; then 29 | sleep 2 30 | fi 31 | done 32 | # there are big jumps at powers of 2 33 | for n in $(seq 5 10); do 34 | gen_count --proof $proof --time $((2 ** n - 1)) 35 | gen_count --proof $proof --time $((2 ** n)) 36 | sleep 2 37 | done 38 | done 39 | } 40 | 41 | if [ "$#" -ge 0 ]; then 42 | out="$1" 43 | # avoid clobbering output in case run is cancelled 44 | temp=".$out.tmp" 45 | header | tee "$temp" 46 | warmup 47 | gen_data | tee -a "$temp" 48 | mv "$temp" "$out" 49 | else 50 | header 51 | warmup 52 | gen_data 53 | fi 54 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-egg-not-cleaned-up.json: -------------------------------------------------------------------------------- 1 | { 2 | "request":"perform-rewrite", 3 | "target-lhs":"(ap (fvar (num (str anonymous _uniq) 547)) (ap (fvar (num (str anonymous _uniq) 547)) (fvar (num (str anonymous _uniq) 550))))", 4 | "timeout" : 30, 5 | "dump-graph" : false, 6 | "target-rhs":"(fvar (num (str anonymous _uniq) 550))", 7 | "rewrites":[ 8 | { 9 | "name":"3", 10 | "lhs":"(fvar (num (str anonymous _uniq) 549))", 11 | "rhs":"(ap (ap (fvar (num (str anonymous _uniq) 548)) (fvar (num (str anonymous _uniq) 550))) (ap (fvar (num (str anonymous _uniq) 547)) (fvar (num (str anonymous _uniq) 550))))" 12 | }, 13 | { 14 | "name":"2", 15 | "lhs":"?_uniq.562", 16 | "rhs":"(ap (ap (fvar (num (str anonymous _uniq) 548)) ?_uniq.562) (fvar (num (str anonymous _uniq) 549)))" 17 | }, 18 | { 19 | "name":"1", 20 | "lhs":"(ap (ap (fvar (num (str anonymous _uniq) 548)) (ap (fvar (num (str anonymous _uniq) 547)) ?_uniq.561)) ?_uniq.561)", 21 | "rhs":"(fvar (num (str anonymous _uniq) 549))" 22 | }, 23 | { 24 | "name":"0", 25 | "lhs":"(ap (ap (fvar (num (str anonymous _uniq) 548)) ?_uniq.558) (ap (ap (fvar (num (str anonymous _uniq) 548)) ?_uniq.559) ?_uniq.560))", 26 | "rhs":"(ap (ap (fvar (num (str anonymous _uniq) 548)) (ap (ap (fvar (num (str anonymous _uniq) 548)) ?_uniq.558) ?_uniq.559)) ?_uniq.560)" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-inv-broken-rewrites.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 3, 3 | "rewrites": [ 4 | { 5 | "rhs": "v10", 6 | "lhs": "(ap (ap v11 ?_uniq.115) (ap v9 ?_uniq.115))", 7 | "name": "8" 8 | }, 9 | { 10 | "rhs": "v10", 11 | "lhs": "(ap (ap v11 v8) (ap v9 v8))", 12 | "name": "7" 13 | }, 14 | { 15 | "rhs": "v10", 16 | "lhs": "(ap (ap v11 v10) (ap v9 v10))", 17 | "name": "6" 18 | }, 19 | { 20 | "rhs": "?_uniq.113", 21 | "lhs": "(ap (ap v11 v10) ?_uniq.113)", 22 | "name": "5" 23 | }, 24 | { 25 | "rhs": "?_uniq.111", 26 | "lhs": "(ap (ap v11 ?_uniq.111) v10)", 27 | "name": "4" 28 | }, 29 | { 30 | "rhs": "v10", 31 | "lhs": "(ap (ap v11 (ap v9 ?_uniq.109)) ?_uniq.109)", 32 | "name": "3" 33 | }, 34 | { 35 | "rhs": "v10", 36 | "lhs": "(ap (ap v11 (ap v9 v8)) v8)", 37 | "name": "2" 38 | }, 39 | { 40 | "rhs": "v10", 41 | "lhs": "(ap (ap v11 (ap v9 v10)) v10)", 42 | "name": "1" 43 | }, 44 | { 45 | "rhs": "(ap (ap v11 (ap (ap v11 ?_uniq.75) ?_uniq.76)) ?_uniq.77)", 46 | "lhs": "(ap (ap v11 ?_uniq.75) (ap (ap v11 ?_uniq.76) ?_uniq.77))", 47 | "name": "0" 48 | } 49 | ], 50 | "target-rhs": "v8", 51 | "target-lhs": "(ap v9 (ap v9 v8))", 52 | "request": "perform-rewrite" 53 | } 54 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-inv-working-rewrites.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 30, 3 | "dump-graph" : false, 4 | "rewrites": [ 5 | { 6 | "rhs": "v10", 7 | "lhs": "(ap (ap v11 ?_uniq.115) (ap v9 ?_uniq.115))", 8 | "name": "8" 9 | }, 10 | { 11 | "rhs": "(ap (ap v11 v8) (ap v9 v8))", 12 | "lhs": "v10", 13 | "name": "7" 14 | }, 15 | { 16 | "rhs": "(ap (ap v11 v10) (ap v9 v10))", 17 | "lhs": "v10", 18 | "name": "6" 19 | }, 20 | { 21 | "rhs": "?_uniq.113", 22 | "lhs": "(ap (ap v11 v10) ?_uniq.113)", 23 | "name": "5" 24 | }, 25 | { 26 | "rhs": "(ap (ap v11 ?_uniq.111) v10)", 27 | "lhs": "?_uniq.111", 28 | "name": "4" 29 | }, 30 | { 31 | "rhs": "v10", 32 | "lhs": "(ap (ap v11 (ap v9 ?_uniq.109)) ?_uniq.109)", 33 | "name": "3" 34 | }, 35 | { 36 | "rhs": "v10", 37 | "lhs": "(ap (ap v11 (ap v9 v8)) v8)", 38 | "name": "2" 39 | }, 40 | { 41 | "rhs": "v10", 42 | "lhs": "(ap (ap v11 (ap v9 v10)) v10)", 43 | "name": "1" 44 | }, 45 | { 46 | "rhs": "(ap (ap v11 (ap (ap v11 ?_uniq.75) ?_uniq.76)) ?_uniq.77)", 47 | "lhs": "(ap (ap v11 ?_uniq.75) (ap (ap v11 ?_uniq.76) ?_uniq.77))", 48 | "name": "0" 49 | } 50 | ], 51 | "target-rhs": "v8", 52 | "target-lhs": "(ap v9 (ap v9 v8))", 53 | "request": "perform-rewrite" 54 | } 55 | -------------------------------------------------------------------------------- /lakefile.lean: -------------------------------------------------------------------------------- 1 | import Lake 2 | open Lake DSL 3 | 4 | require Std from git "https://github.com/leanprover/std4"@"529a6" 5 | 6 | package «egg-tactic» { 7 | 8 | } 9 | 10 | def compileCargo (name : String) (manifestFile : FilePath) 11 | (cargo : FilePath := "cargo") : LogIO Unit := do 12 | logInfo s!"Creating {name}" 13 | proc { 14 | cmd := cargo.toString 15 | args := #["build", "--release", "--manifest-path", manifestFile.toString] 16 | } 17 | 18 | def buildCargo (targetFile : FilePath) (manifestFile : FilePath) (targetDest : FilePath) 19 | (oFileJobs : Array (BuildJob FilePath)) : SchedulerM (BuildJob FilePath) := 20 | let name := targetFile.fileName.getD targetFile.toString 21 | buildFileAfterDepArray targetFile oFileJobs fun _ => do 22 | compileCargo name manifestFile 23 | -- hack (and not very portable) 24 | createParentDirs targetDest 25 | proc { 26 | cmd := "cp" 27 | args := #[targetFile.toString, targetDest.toString] 28 | } 29 | 30 | -- @[default_target] 31 | target «egg-herbie» (pkg : Package) : FilePath := do 32 | let buildDir := pkg.dir / "json-egg" 33 | let binFile := buildDir / "target" / "release" / "egg-herbie" 34 | let dest := pkg.dir / "utils" / "egg-herbie" 35 | let manifestFile := buildDir / "Cargo.toml" 36 | buildCargo binFile manifestFile dest #[] 37 | 38 | @[default_target] 39 | lean_lib EggTactic{ 40 | roots := #[`EggTactic] 41 | precompileModules := true 42 | } 43 | -- add configuration options here 44 | 45 | --require «aesop» from git "https://github.com/JLimperg/aesop" @ "3fb480b3d7b1e70e488e479e94875bb94d7c8ade" 46 | -- require smt from git "https://github.com/ufmg-smite/lean-smt.git"@"main" 47 | 48 | -- require mathlib from git "https://github.com/leanprover-community/mathlib4" @ "master" 49 | 50 | -------------------------------------------------------------------------------- /ForeignLean/myfuns.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct S { 6 | unsigned m_x; 7 | unsigned m_y; 8 | std::string m_s; 9 | S(unsigned x, unsigned y, char const * s):m_x(x), m_y(y), m_s(s) {} 10 | }; 11 | 12 | static void S_finalize(void * obj) { 13 | delete static_cast(obj); 14 | } 15 | 16 | static void S_foreach(void *, b_lean_obj_arg) { 17 | // do nothing since `S` does not contain nested Lean objects 18 | } 19 | 20 | static lean_external_class * g_S_class = nullptr; 21 | 22 | static inline lean_object * S_to_lean(S * s) { 23 | if (g_S_class == nullptr) { 24 | g_S_class = lean_register_external_class(S_finalize, S_foreach); 25 | } 26 | return lean_alloc_external(g_S_class, s); 27 | } 28 | 29 | static inline S const * to_S(b_lean_obj_arg s) { 30 | return static_cast(lean_get_external_data(s)); 31 | } 32 | 33 | extern "C" LEAN_EXPORT lean_object * lean_mk_S(uint32_t x, uint32_t y, b_lean_obj_arg s) { 34 | return S_to_lean(new S(x, y, lean_string_cstr(s))); 35 | } 36 | 37 | extern "C" LEAN_EXPORT uint32_t lean_S_add_x_y(b_lean_obj_arg s) { 38 | return to_S(s)->m_x + to_S(s)->m_y; 39 | } 40 | 41 | extern "C" LEAN_EXPORT lean_object * lean_S_string(b_lean_obj_arg s) { 42 | return lean_mk_string(to_S(s)->m_s.c_str()); 43 | } 44 | 45 | static S g_s(0, 0, ""); 46 | 47 | extern "C" LEAN_EXPORT lean_object * lean_S_global_append(b_lean_obj_arg str, lean_object /* w */) { 48 | g_s.m_s += lean_string_cstr(str); 49 | return lean_io_result_mk_ok(lean_box(0)); 50 | } 51 | 52 | extern "C" LEAN_EXPORT lean_object * lean_S_global_string(lean_object /* w */) { 53 | return lean_io_result_mk_ok(lean_mk_string(g_s.m_s.c_str())); 54 | } 55 | 56 | extern "C" LEAN_EXPORT lean_object * lean_S_update_global(b_lean_obj_arg s, lean_object /* w */) { 57 | g_s.m_x = to_S(s)->m_x; 58 | g_s.m_y = to_S(s)->m_y; 59 | g_s.m_s = to_S(s)->m_s; 60 | return lean_io_result_mk_ok(lean_box(0)); 61 | } 62 | -------------------------------------------------------------------------------- /Evaluation/scaling-search-space/plotscaling.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | library(tidyverse) 3 | library(readr) 4 | library(fs) 5 | library(RColorBrewer) 6 | library(tikzDevice) 7 | 8 | args = commandArgs(trailingOnly=TRUE) 9 | if (length(args)<1) { 10 | stop("expected CSV file path. usage: plotscaling.R ", call.=FALSE) 11 | } 12 | 13 | stats_raw <- read_csv(args[1], col_types = cols(time = col_double())) 14 | # sort: ours first (for color scheme) 15 | 16 | stats <- filter(stats_raw, problemsize < 999999) %>% 17 | transform(tool=ifelse(tool=="lean-egg","eggxplosion", ifelse(tool == "coq", "coq-congruence", tool))) %>% 18 | rowwise() %>% 19 | transform(count_to = problemsize) 20 | 21 | stats$tool <- factor(stats$tool,levels=c("eggxplosion", "coq-congruence", "lean-simp", "coq-autorewrite")) 22 | 23 | p <- ggplot(data =stats, mapping = aes(x=`count_to`, y =`time`, fill = `tool`)) + 24 | #geom_col(mapping = aes(fill = `tool`), position=position_dodge2()) + 25 | geom_point(mapping = aes(color = `tool`)) + 26 | geom_line(mapping = aes(color = `tool`)) + 27 | xlab("counter radix") + 28 | ylab("time [s] (log)") + 29 | #geom_text(mapping = aes(x = `problemsize`, y = 0.3, label = ifelse(is.na(`time`), "X", "")), position=position_dodge2()) 30 | #geom_point() + 31 | scale_y_log10(expand=expansion(mult=c(0,0.1))) + # No space below the bars but 10% above them; https://ggplot2.tidyverse.org/reference/expansion.html 32 | scale_x_sqrt(breaks = stats$count_to) + 33 | scale_fill_brewer(palette="Set2") + 34 | scale_color_brewer(palette="Set2") + 35 | theme_light() + 36 | theme(legend.position = c(0.1,0.84), 37 | legend.title = element_blank(), 38 | panel.grid.major.x = element_blank(), 39 | panel.grid.minor.x = element_blank(), 40 | legend.background=element_blank()) 41 | 42 | tikz(file = fs::path_ext_set(args[1], "tex"), standAlone = F, width=10,height=4) 43 | print(p) 44 | dev.off() 45 | 46 | p 47 | ggsave(fs::path_ext_set(args[1], "png")) 48 | #ggsave(fs::path_ext_set(args[1], "pdf")) 49 | 50 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/plotscaling.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | library(tidyverse) 3 | library(readr) 4 | library(fs) 5 | library(RColorBrewer) 6 | library(tikzDevice) 7 | 8 | args = commandArgs(trailingOnly=TRUE) 9 | if (length(args)<1) { 10 | stop("expected CSV file path. usage: plotscaling.R ", call.=FALSE) 11 | } 12 | 13 | stats_raw <- read_csv(args[1], col_types = cols(time = col_double())) 14 | # sort: ours first (for color scheme) 15 | 16 | stats <- filter(stats_raw, problemsize < 999999) %>% 17 | transform(tool=ifelse(tool=="lean-egg","eggxplosion", ifelse(tool == "coq", "coq-congruence", tool))) %>% 18 | rowwise() %>% 19 | transform(count_to = problemsize) 20 | 21 | stats$tool <- factor(stats$tool,levels=c("eggxplosion", "coq-congruence", "lean-simp", "coq-autorewrite")) 22 | 23 | p <- ggplot(data =stats, mapping = aes(x=`count_to`, y =`time`, fill = `tool`)) + 24 | #geom_col(mapping = aes(fill = `tool`), position=position_dodge2()) + 25 | geom_point(mapping = aes(color = `tool`)) + 26 | geom_line(mapping = aes(color = `tool`)) + 27 | xlab("counter radix") + 28 | ylab("time [s] (log)") + 29 | #geom_text(mapping = aes(x = `problemsize`, y = 0.3, label = ifelse(is.na(`time`), "X", "")), position=position_dodge2()) 30 | #geom_point() + 31 | scale_y_log10(expand=expansion(mult=c(0,0.1))) + # No space below the bars but 10% above them; https://ggplot2.tidyverse.org/reference/expansion.html 32 | scale_x_sqrt(breaks = stats$count_to) + 33 | scale_fill_brewer(palette="Set2") + 34 | scale_color_brewer(palette="Set2") + 35 | theme_light() + 36 | theme(legend.position = c(0.1,0.84), 37 | legend.title = element_blank(), 38 | panel.grid.major.x = element_blank(), 39 | panel.grid.minor.x = element_blank(), 40 | legend.background=element_blank()) 41 | 42 | tikz(file = fs::path_ext_set(args[1], "tex"), standAlone = F, width=10,height=4) 43 | print(p) 44 | dev.off() 45 | 46 | p 47 | ggsave(fs::path_ext_set(args[1], "png")) 48 | #ggsave(fs::path_ext_set(args[1], "pdf")) 49 | 50 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-mul-slow-reduced.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 9999999, 3 | "dump-graph": true, 4 | "rewrites": [ 5 | { 6 | "rhs": "one", 7 | "lhs": "(mul ?a (inv ?a))", 8 | "name": "19" 9 | }, 10 | { 11 | "rhs": "(mul y (inv y))", 12 | "lhs": "one", 13 | "name": "18" 14 | }, 15 | { 16 | "rhs": "(mul x (inv x))", 17 | "lhs": "one", 18 | "name": "16" 19 | }, 20 | { 21 | "rhs": "(mul one (inv one))", 22 | "lhs": "one", 23 | "name": "14" 24 | }, 25 | { 26 | "rhs": "(mul ?a one)", 27 | "lhs": "?a", 28 | "name": "12" 29 | }, 30 | { 31 | "rhs": "?a", 32 | "lhs": "(mul ?a one)", 33 | "name": "11" 34 | }, 35 | { 36 | "rhs": "(mul one ?a)", 37 | "lhs": "?a", 38 | "name": "10" 39 | }, 40 | { 41 | "rhs": "?a", 42 | "lhs": "(mul one ?a)", 43 | "name": "9" 44 | }, 45 | { 46 | "rhs": "one", 47 | "lhs": "(mul (inv ?a) ?a)", 48 | "name": "8" 49 | }, 50 | { 51 | "rhs": "(mul (inv y) y)", 52 | "lhs": "one", 53 | "name": "7" 54 | }, 55 | { 56 | "rhs": "(mul (inv x) x)", 57 | "lhs": "one", 58 | "name": "5" 59 | }, 60 | { 61 | "rhs": "(mul (inv one) one)", 62 | "lhs": "one", 63 | "name": "3" 64 | }, 65 | { 66 | "rhs": "(mul ?a (mul ?b ?c))", 67 | "lhs": "(mul (mul ?a ?b) ?c)", 68 | "name": "1" 69 | }, 70 | { 71 | "rhs": "(mul (mul ?a ?b) ?c)", 72 | "lhs": "(mul ?a (mul ?b ?c))", 73 | "name": "0" 74 | } 75 | ], 76 | "target-rhs": "(mul (inv y) (inv x))", 77 | "target-lhs": "(inv (mul x y))", 78 | "request": "perform-rewrite" 79 | } 80 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/plotscaling.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | library(tidyverse) 3 | library(readr) 4 | library(fs) 5 | library(RColorBrewer) 6 | library(tikzDevice) 7 | 8 | args = commandArgs(trailingOnly=TRUE) 9 | if (length(args)<1) { 10 | stop("expected CSV file path. usage: plotscaling.R ", call.=FALSE) 11 | } 12 | 13 | stats_raw <- read_csv(args[1], col_types = cols(time = col_double())) 14 | # sort: ours first (for color scheme) 15 | 16 | stats <- filter(stats_raw, problemsize < 999999) %>% 17 | transform(tool=ifelse(tool=="lean-egg","eggxplosion", ifelse(tool == "coq", "coq-congruence", tool))) %>% 18 | rowwise() %>% 19 | transform(count_to = ifelse(problemsize < 4, 2^problemsize - 1, problemsize * problemsize)) 20 | stats$tool <- factor(stats$tool,levels=c("eggxplosion", "coq-congruence", "lean-simp", "coq-autorewrite")) 21 | 22 | p <- ggplot(data =stats, mapping = aes(x=`count_to`, y =`time`, fill = `tool`)) + 23 | #geom_col(mapping = aes(fill = `tool`), position=position_dodge2()) + 24 | geom_point(mapping = aes(color = `tool`)) + 25 | geom_line(mapping = aes(color = `tool`)) + 26 | xlab("min. number of rewrites ($\\sqrt{\\phantom{x}}$)") + 27 | ylab("time [s] (log)") + 28 | #geom_text(mapping = aes(x = `problemsize`, y = 0.3, label = ifelse(is.na(`time`), "X", "")), position=position_dodge2()) 29 | #geom_point() + 30 | scale_y_log10(expand=expansion(mult=c(0,0.1))) + # No space below the bars but 10% above them; https://ggplot2.tidyverse.org/reference/expansion.html 31 | scale_x_sqrt(breaks = stats$count_to) + 32 | scale_fill_brewer(palette="Set2") + 33 | scale_color_brewer(palette="Set2") + 34 | theme_light() + 35 | theme(legend.position = c(0.1,0.84), 36 | legend.title = element_blank(), 37 | panel.grid.major.x = element_blank(), 38 | panel.grid.minor.x = element_blank(), 39 | legend.background=element_blank()) 40 | 41 | tikz(file = fs::path_ext_set(args[1], "tex"), standAlone = F, width=10,height=4) 42 | print(p) 43 | dev.off() 44 | 45 | p 46 | ggsave(fs::path_ext_set(args[1], "png")) 47 | #ggsave(fs::path_ext_set(args[1], "pdf")) 48 | 49 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendixSimp.lean: -------------------------------------------------------------------------------- 1 | theorem inv_inv 2 | {G: Type} 3 | (inv: G → G) 4 | (mul: G → G → G) 5 | (one: G) 6 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 7 | (invLeft: forall (a: G), mul (inv a) a = one) 8 | (mulOne: forall (a: G), mul a one = a) 9 | (oneMul: forall (a: G), mul one a = a) 10 | (invRight: forall (a: G), mul a (inv a) = one) 11 | (x: G) 12 | : (inv (inv x) = x) := by 13 | simp [assocMul, invLeft, mulOne, oneMul, invRight] 14 | 15 | theorem inv_mul_cancel_left 16 | {G: Type} 17 | (inv: G → G) 18 | (mul: G → G → G) 19 | (one: G) 20 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 21 | (invLeft: forall (a: G), mul (inv a) a = one) 22 | (mulOne: forall (a: G), mul a one = a) 23 | (oneMul: forall (a: G), mul one a = a) 24 | (invRight: forall (a: G), mul a (inv a) = one) 25 | (x y : G) 26 | : (mul (inv x) (mul x y)) = y := by 27 | simp [assocMul, invLeft, mulOne, oneMul, invRight] 28 | 29 | 30 | theorem mul_inv_cancel_left 31 | {G: Type} 32 | (inv: G → G) 33 | (mul: G → G → G) 34 | (one: G) 35 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 36 | (invLeft: forall (a: G), mul (inv a) a = one) 37 | (mulOne: forall (a: G), mul a one = a) 38 | (oneMul: forall (a: G), mul one a = a) 39 | (invRight: forall (a: G), mul a (inv a) = one) 40 | (x y : G) 41 | : (mul x (mul (inv x) y)) = y := by 42 | simp [assocMul, invLeft, mulOne, oneMul, invRight] 43 | 44 | 45 | theorem inv_mul 46 | {G: Type} 47 | (inv: G → G) 48 | (mul: G → G → G) 49 | (one: G) 50 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 51 | (invLeft: forall (a: G), mul (inv a) a = one) 52 | (mulOne: forall (a: G), mul a one = a) 53 | (oneMul: forall (a: G), mul one a = a) 54 | (invRight: forall (a: G), mul a (inv a) = one) 55 | (x y : G) 56 | : (inv (mul x y)) = (mul (inv y) (inv x)) := by 57 | simp [assocMul, invLeft, mulOne, oneMul, invRight] 58 | 59 | theorem one_inv 60 | {G: Type} 61 | (inv: G → G) 62 | (mul: G → G → G) 63 | (one: G) 64 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 65 | (invLeft: forall (a: G), mul (inv a) a = one) 66 | (mulOne: forall (a: G), mul a one = a) 67 | (oneMul: forall (a: G), mul one a = a) 68 | (invRight: forall (a: G), mul a (inv a) = one) 69 | (x y : G) 70 | : (inv one) = one := by 71 | simp [assocMul, invLeft, mulOne, oneMul, invRight] 72 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendixAesop.lean: -------------------------------------------------------------------------------- 1 | -- this only works with the nightly 2022-07-13, adding results as comments 2 | import Aesop 3 | 4 | theorem inv_inv 5 | {G: Type} 6 | (inv: G → G) 7 | (mul: G → G → G) 8 | (one: G) 9 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 10 | (invLeft: forall (a: G), mul (inv a) a = one) 11 | (mulOne: forall (a: G), mul a one = a) 12 | (oneMul: forall (a: G), mul one a = a) 13 | (invRight: forall (a: G), mul a (inv a) = one) 14 | (x: G) 15 | : (inv (inv x) = x) := by 16 | aesop -- aesop: failed to prove the goal after exhaustive search. 17 | 18 | theorem inv_mul_cancel_left 19 | {G: Type} 20 | (inv: G → G) 21 | (mul: G → G → G) 22 | (one: G) 23 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 24 | (invLeft: forall (a: G), mul (inv a) a = one) 25 | (mulOne: forall (a: G), mul a one = a) 26 | (oneMul: forall (a: G), mul one a = a) 27 | (invRight: forall (a: G), mul a (inv a) = one) 28 | (x y : G) 29 | : (mul (inv x) (mul x y)) = y := by 30 | aesop -- goals accomplished 31 | 32 | 33 | theorem mul_inv_cancel_left 34 | {G: Type} 35 | (inv: G → G) 36 | (mul: G → G → G) 37 | (one: G) 38 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 39 | (invLeft: forall (a: G), mul (inv a) a = one) 40 | (mulOne: forall (a: G), mul a one = a) 41 | (oneMul: forall (a: G), mul one a = a) 42 | (invRight: forall (a: G), mul a (inv a) = one) 43 | (x y : G) 44 | : (mul x (mul (inv x) y)) = y := by 45 | aesop -- goals accomplished 46 | 47 | theorem inv_mul 48 | {G: Type} 49 | (inv: G → G) 50 | (mul: G → G → G) 51 | (one: G) 52 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 53 | (invLeft: forall (a: G), mul (inv a) a = one) 54 | (mulOne: forall (a: G), mul a one = a) 55 | (oneMul: forall (a: G), mul one a = a) 56 | (invRight: forall (a: G), mul a (inv a) = one) 57 | (x y : G) 58 | : (inv (mul x y)) = (mul (inv y) (inv x)) := by 59 | aesop -- aesop: failed to prove the goal after exhaustive search. 60 | 61 | theorem one_inv 62 | {G: Type} 63 | (inv: G → G) 64 | (mul: G → G → G) 65 | (one: G) 66 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 67 | (invLeft: forall (a: G), mul (inv a) a = one) 68 | (mulOne: forall (a: G), mul a one = a) 69 | (oneMul: forall (a: G), mul one a = a) 70 | (invRight: forall (a: G), mul a (inv a) = one) 71 | (x y : G) 72 | : (inv one) = one := by 73 | aesop -- aesop: failed to prove the goal after exhaustive search. 74 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendix.v: -------------------------------------------------------------------------------- 1 | Theorem inv_inv 2 | {G: Type} 3 | (inv: G -> G) 4 | (mul: G -> G -> G) 5 | (one: G) 6 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 7 | (invLeft: forall (a: G), mul (inv a) a = one) 8 | (mulOne: forall (a: G), a = mul a one) 9 | (oneMul: forall (a: G), mul one a = a) 10 | (invRight: forall (a: G), one = mul a (inv a)) 11 | (x: G) 12 | : (inv (inv x) = x). 13 | Proof. 14 | try congruence with assocMul invLeft mulOne oneMul invRight. 15 | Admitted. 16 | 17 | Theorem inv_mul_cancel_left 18 | {G: Type} 19 | (inv: G -> G) 20 | (mul: G -> G -> G) 21 | (one: G) 22 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 23 | (invLeft: forall (a: G), mul (inv a) a = one) 24 | (mulOne: forall (a: G), a = mul a one) 25 | (oneMul: forall (a: G), mul one a = a) 26 | (invRight: forall (a: G), one = mul a (inv a)) 27 | (x y : G) 28 | : (mul (inv x) (mul x y)) = y. 29 | Proof. 30 | try congruence with assocMul invLeft mulOne oneMul invRight. 31 | Qed. 32 | 33 | Theorem mul_inv_cancel_left 34 | {G: Type} 35 | (inv: G -> G) 36 | (mul: G -> G -> G) 37 | (one: G) 38 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 39 | (invLeft: forall (a: G), mul (inv a) a = one) 40 | (mulOne: forall (a: G), a = mul a one) 41 | (oneMul: forall (a: G), mul one a = a) 42 | (invRight: forall (a: G), one = mul a (inv a)) 43 | (x y : G) 44 | : (mul x (mul (inv x) y)) = y. 45 | Proof. 46 | try congruence with assocMul invLeft mulOne oneMul invRight. 47 | Qed. 48 | 49 | Theorem inv_mul 50 | {G: Type} 51 | (inv: G -> G) 52 | (mul: G -> G -> G) 53 | (one: G) 54 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 55 | (invLeft: forall (a: G), mul (inv a) a = one) 56 | (mulOne: forall (a: G), a = mul a one) 57 | (oneMul: forall (a: G), mul one a = a) 58 | (invRight: forall (a: G), one = mul a (inv a)) 59 | (x y : G) 60 | : (inv (mul x y)) = (mul (inv y) (inv x)). 61 | Proof. 62 | try congruence with assocMul invLeft mulOne oneMul invRight. 63 | Admitted. 64 | 65 | Theorem one_inv 66 | {G: Type} 67 | (inv: G -> G) 68 | (mul: G -> G -> G) 69 | (one: G) 70 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 71 | (invLeft: forall (a: G), mul (inv a) a = one) 72 | (mulOne: forall (a: G), a = mul a one) 73 | (oneMul: forall (a: G), mul one a = a) 74 | (invRight: forall (a: G), one = mul a (inv a)) 75 | (x y : G) 76 | : (inv one) = one. 77 | Proof. 78 | try congruence with assocMul invLeft mulOne oneMul invRight. 79 | Qed. 80 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/scaling-geometric.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,1,0.19801983307115734 3 | lean-simp,1,0.18038966599851847 4 | coq-congruence,1,0.12607270805165172 5 | coq-autorewrite,1,0.12960916594602168 6 | lean-egg,2,0.1962639580015093 7 | lean-simp,2,0.18056670809164643 8 | coq-congruence,2,0.14623829117044806 9 | coq-autorewrite,2,0.14736224990338087 10 | lean-egg,3,0.20825674990192056 11 | lean-simp,3,0.18092333292588592 12 | coq-congruence,3,0.12776708300225437 13 | coq-autorewrite,3,0.12943204189650714 14 | lean-egg,4,0.20851083286106586 15 | lean-simp,4,0.17839004192501307 16 | coq-congruence,4,0.1289632918778807 17 | coq-autorewrite,4,0.13113083387725055 18 | lean-egg,5,0.21738104103133082 19 | lean-simp,5,0.1853819580283016 20 | coq-congruence,5,0.1327020840253681 21 | coq-autorewrite,5,0.1395785000640899 22 | lean-egg,6,0.2446490409784019 23 | lean-simp,6,0.18881799979135394 24 | coq-congruence,6,0.13883420918136835 25 | coq-autorewrite,6,0.15649683284573257 26 | lean-egg,7,0.28215333307161927 27 | lean-simp,7,0.1984272920526564 28 | coq-congruence,7,0.15479787508957088 29 | coq-autorewrite,7,0.19927487499080598 30 | lean-egg,8,0.4153181668370962 31 | lean-simp,8,0.2198717501014471 32 | coq-congruence,8,0.20072883390821517 33 | coq-autorewrite,8,0.2700894591398537 34 | lean-egg,9,0.37183816684409976 35 | lean-simp,9,0.2757890420034528 36 | coq-congruence,9,0.2507611659821123 37 | coq-autorewrite,9,0.43007533298805356 38 | lean-egg,10,0.4418881661258638 39 | lean-simp,10,0.38326829206198454 40 | coq-congruence,10,0.36599758290685713 41 | coq-autorewrite,10,0.7813528338447213 42 | lean-egg,11,0.5255341660231352 43 | lean-simp,11,0.6257025408558547 44 | coq-congruence,11,0.45593166700564325 45 | coq-autorewrite,11,1.5640232500154525 46 | lean-egg,12,0.6044175841379911 47 | lean-simp,12,1.0909463341813534 48 | coq-congruence,12,0.7149674999527633 49 | coq-autorewrite,12,3.222817540867254 50 | lean-egg,13,0.7386981248855591 51 | lean-simp,13,ERR 52 | coq-congruence,13,0.9734135000035167 53 | coq-autorewrite,13,7.060199625091627 54 | lean-egg,14,0.9146429998800159 55 | coq-congruence,14,1.72374412487261 56 | coq-autorewrite,14,ERR 57 | lean-egg,15,1.1333341251593083 58 | coq-congruence,15,2.059567374875769 59 | lean-egg,16,1.3777481249999255 60 | coq-congruence,16,3.66617816593498 61 | lean-egg,17,1.6875872497912496 62 | coq-congruence,17,4.113800040911883 63 | lean-egg,18,2.046454916941002 64 | coq-congruence,18,8.967847042018548 65 | lean-egg,19,2.485067417146638 66 | coq-congruence,19,8.338863499928266 67 | lean-egg,20,3.014344417024404 68 | coq-congruence,20,14.89962162496522 69 | -------------------------------------------------------------------------------- /example.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | namespace Egg 3 | 4 | -- From: lean4/tests/lean/run/alg.lean 5 | 6 | -- class Group (α : Type u) extends Mul α where 7 | -- one : α 8 | -- one_mul (a : α) : one * a = a 9 | -- mul_one (a : α) : a * one = a 10 | -- inv : α → α 11 | -- mul_assoc (a b c : α) : a * b * c = a * (b * c) 12 | -- mul_left_inv (a : α) : (inv a) * a = one 13 | -- 14 | -- instance [Group α] : OfNat α (nat_lit 1) where 15 | -- ofNat := Group.one 16 | 17 | -- Does not work with typeclasses (problem with metavariables) 18 | 19 | -- We'll just take the integers for now and not use any additional properties 20 | -- Copied form stdlib 21 | inductive G : Type where 22 | | ofNat : Nat → G 23 | | negSucc : Nat → G 24 | 25 | def negOfNat : Nat → G 26 | | 0 => .ofNat 0 27 | | .succ m => .negSucc m 28 | 29 | def neg (n : G) : G := 30 | match n with 31 | | .ofNat n => negOfNat n 32 | | .negSucc n => .ofNat $ Nat.succ n 33 | 34 | def subNatNat (m n : Nat) : G := 35 | match (n - m : Nat) with 36 | | 0 => G.ofNat (m - n) -- m ≥ n 37 | | (.succ k) => .negSucc k 38 | 39 | def add (m n : G) : G := 40 | match m, n with 41 | | .ofNat m, .ofNat n => .ofNat (m + n) 42 | | .ofNat m, .negSucc n => subNatNat m (Nat.succ n) 43 | | .negSucc m, .ofNat n => subNatNat n (Nat.succ m) 44 | | .negSucc m, .negSucc n => .negSucc (Nat.succ (m + n)) 45 | 46 | postfix:max "⁻¹" => neg 47 | infix:80 "∘" => add 48 | notation "e" => G.ofNat 0 49 | 50 | theorem one_mul (a : G) : e ∘ a = a := by sorry 51 | theorem mul_assoc (a b c : G) : (a ∘ b) ∘ c = a ∘ (b ∘ c) := by sorry 52 | theorem mul_one (a : G) : a ∘ e = a := by sorry 53 | theorem mul_left_inv (a : G) : a⁻¹ ∘ a = e by sorry 54 | theorem mul_right_inv (a : G) : a ∘ a⁻¹ = e by sorry 55 | 56 | theorem inv_mul_cancel_left (a b : G) : a⁻¹ ∘ (a ∘ b) = b := by 57 | try simp [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 58 | rawEgg [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 59 | 60 | theorem mul_inv_cancel_left : a ∘ (a⁻¹ ∘ b) = b := by 61 | try simp [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 62 | rawEgg [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 63 | 64 | theorem inv_mul : (a ∘ b)⁻¹ = b⁻¹ ∘ a⁻¹ := by 65 | try simp [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 66 | rawEgg [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 67 | 68 | theorem one_inv : e⁻¹ = e := by 69 | try simp [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 70 | rawEgg [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 71 | 72 | theorem inv_inv : a ⁻¹ ⁻¹ = a := by 73 | try simp [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 74 | rawEgg [mul_assoc,mul_left_inv,mul_assoc,one_mul,mul_one, mul_right_inv] 75 | 76 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/scaling-geometric.csv: -------------------------------------------------------------------------------- 1 | tool,problemsize,time 2 | lean-egg,5,0.6606982149969554 3 | lean-simp,5,ERR 4 | coq-congruence,5,0.1380389749974711 5 | coq-autorewrite,5,0.13357244001235813 6 | lean-egg,10,0.6879128000000492 7 | coq-congruence,10,0.15781702799722552 8 | coq-autorewrite,10,0.1523789030034095 9 | lean-egg,15,0.7254004330025055 10 | coq-congruence,15,0.20015423800214194 11 | coq-autorewrite,15,0.17083301801176276 12 | lean-egg,20,0.7826493350003147 13 | coq-congruence,20,0.24500175699358806 14 | coq-autorewrite,20,0.1834884290001355 15 | lean-egg,25,0.8936237009911565 16 | coq-congruence,25,0.3193078729964327 17 | coq-autorewrite,25,0.20789327699458227 18 | lean-egg,30,1.018859075004002 19 | coq-congruence,30,0.42048308900848497 20 | coq-autorewrite,30,0.219417489002808 21 | lean-egg,35,1.1596640799980378 22 | coq-congruence,35,0.5375996119983029 23 | coq-autorewrite,35,0.23936865699943155 24 | lean-egg,40,1.4069674749916885 25 | coq-congruence,40,0.5958961940050358 26 | coq-autorewrite,40,0.25959992299613077 27 | lean-egg,45,1.6323640089976834 28 | coq-congruence,45,0.7280822729953798 29 | coq-autorewrite,45,0.2800746849970892 30 | lean-egg,50,1.9807322129927343 31 | coq-congruence,50,0.8811358700040728 32 | coq-autorewrite,50,0.29869292200601194 33 | lean-egg,55,2.4428646360029234 34 | coq-congruence,55,1.0551086659979774 35 | coq-autorewrite,55,0.31531613699917216 36 | lean-egg,60,2.7387422080064425 37 | coq-congruence,60,1.3082496489951154 38 | coq-autorewrite,60,0.3317360520013608 39 | lean-egg,65,3.2482307570026023 40 | coq-congruence,65,1.4862908390059602 41 | coq-autorewrite,65,0.35668117299792357 42 | lean-egg,70,3.7227900279976893 43 | coq-congruence,70,1.7701356339966878 44 | coq-autorewrite,70,0.3594784000015352 45 | lean-egg,75,4.368286693003029 46 | coq-congruence,75,2.05526858799567 47 | coq-autorewrite,75,0.38890774999163114 48 | lean-egg,80,5.0438713030016515 49 | coq-congruence,80,2.441220107997651 50 | coq-autorewrite,80,0.4239808889979031 51 | lean-egg,85,5.869772862002719 52 | coq-congruence,85,2.8764944540016586 53 | coq-autorewrite,85,0.439586207998218 54 | lean-egg,90,6.782504778995644 55 | coq-congruence,90,3.318135624998831 56 | coq-autorewrite,90,0.45236927500809543 57 | lean-egg,95,7.787842264006031 58 | coq-congruence,95,3.7748769850004464 59 | coq-autorewrite,95,0.4696371640020516 60 | lean-egg,100,8.958623686005012 61 | coq-congruence,100,4.390673834001063 62 | coq-autorewrite,100,0.5007975759945111 63 | lean-egg,105,10.11039578000782 64 | coq-congruence,105,4.961962051005685 65 | coq-autorewrite,105,0.515772451995872 66 | lean-egg,110,11.236139283006196 67 | coq-congruence,110,5.559308344003512 68 | coq-autorewrite,110,0.5042435770010343 69 | lean-egg,115,13.263184415991418 70 | coq-congruence,115,6.5071321970026474 71 | coq-autorewrite,115,0.5486899120005546 72 | lean-egg,120,14.302797203010414 73 | coq-congruence,120,7.29515570399235 74 | coq-autorewrite,120,0.5436183249985334 75 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendix.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | 3 | set_option trace.EggTactic.egg true 4 | theorem inv_inv 5 | {G: Type} 6 | (inv: G → G) 7 | (mul: G → G → G) 8 | (one: G) 9 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 10 | (invLeft: forall (a: G), mul (inv a) a = one) 11 | (oneMul: forall (a: G), mul one a = a) 12 | (mulOne: forall (a: G), mul a one = a) 13 | (invRight: forall (a: G), mul a (inv a) = one) 14 | (x: G) 15 | : (inv (inv x) = x) := by 16 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] 17 | 18 | theorem inv_mul_cancel_left 19 | {G: Type} 20 | (inv: G → G) 21 | (mul: G → G → G) 22 | (one: G) 23 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 24 | (invLeft: forall (a: G), mul (inv a) a = one) 25 | (mulOne: forall (a: G), mul a one = a) 26 | (oneMul: forall (a: G), mul one a = a) 27 | (invRight: forall (a: G), mul a (inv a) = one) 28 | (x y : G) 29 | : (mul (inv x) (mul x y)) = y := by 30 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] 31 | 32 | 33 | theorem mul_inv_cancel_left 34 | {G: Type} 35 | (inv: G → G) 36 | (mul: G → G → G) 37 | (one: G) 38 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 39 | (invLeft: forall (a: G), mul (inv a) a = one) 40 | (mulOne: forall (a: G), a = mul a one) 41 | (oneMul: forall (a: G), mul one a = a) 42 | (invRight: forall (a: G), one = mul a (inv a)) 43 | (x y : G) 44 | : (mul x (mul (inv x) y)) = y := by 45 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] 46 | 47 | 48 | theorem inv_mul 49 | {G: Type} 50 | (inv: G → G) 51 | (mul: G → G → G) 52 | (one: G) 53 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 54 | (invLeft: forall (a: G), mul (inv a) a = one) 55 | (oneMul: forall (a: G), mul one a = a) 56 | (mulOne: forall (a: G), mul a one = a) 57 | (invRight: forall (a: G), mul a (inv a) = one) 58 | (x y : G) 59 | : (inv (mul x y)) = (mul (inv y) (inv x)) := by 60 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] (timeLimit := 5) 61 | 62 | #print inv_mul 63 | 64 | theorem one_inv 65 | {G: Type} 66 | (inv: G → G) 67 | (mul: G → G → G) 68 | (one: G) 69 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 70 | (invLeft: forall (a: G), mul (inv a) a = one) 71 | (mulOne: forall (a: G), a = mul a one) 72 | (oneMul: forall (a: G), mul one a = a) 73 | (invRight: forall (a: G), one = mul a (inv a)) 74 | (x y : G) 75 | : (inv one) = one := by 76 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] 77 | 78 | #print inv_mul_cancel_left 79 | 80 | /- 81 | theorem test_simplification 82 | {G: Type} 83 | (inv: G → G) 84 | (mul: G → G → G) 85 | (one: G) 86 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 87 | (invLeft: forall (a: G), mul (inv a) a = one) 88 | (mulOne: forall (a: G), a = mul a one) 89 | (oneMul: forall (a: G), mul one a = a) 90 | (invRight: forall (a: G), one = mul a (inv a)) 91 | (x y : G) 92 | -- not equal terms, but simplies 93 | : (inv (mul x (inv y))) = one := by 94 | eggxplosion [assocMul, invLeft, mulOne, oneMul, invRight] 95 | 96 | 97 | #print test_simplification 98 | -/ 99 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-mul-slow.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 30, 3 | "dump-graph" : false, 4 | "rewrites": [ 5 | { 6 | "rhs": "one", 7 | "lhs": "(ap (ap mul ?a) (ap inv ?a))", 8 | "name": "19" 9 | }, 10 | { 11 | "rhs": "(ap (ap mul y) (ap inv y))", 12 | "lhs": "one", 13 | "name": "18" 14 | }, 15 | { 16 | "rhs": "one", 17 | "lhs": "(ap (ap mul y) (ap inv y))", 18 | "name": "17" 19 | }, 20 | { 21 | "rhs": "(ap (ap mul x) (ap inv x))", 22 | "lhs": "one", 23 | "name": "16" 24 | }, 25 | { 26 | "rhs": "one", 27 | "lhs": "(ap (ap mul x) (ap inv x))", 28 | "name": "15" 29 | }, 30 | { 31 | "rhs": "(ap (ap mul one) (ap inv one))", 32 | "lhs": "one", 33 | "name": "14" 34 | }, 35 | { 36 | "rhs": "one", 37 | "lhs": "(ap (ap mul one) (ap inv one))", 38 | "name": "13" 39 | }, 40 | { 41 | "rhs": "(ap (ap mul ?a) one)", 42 | "lhs": "?a", 43 | "name": "12" 44 | }, 45 | { 46 | "rhs": "?a", 47 | "lhs": "(ap (ap mul ?a) one)", 48 | "name": "11" 49 | }, 50 | { 51 | "rhs": "(ap (ap mul one) ?a)", 52 | "lhs": "?a", 53 | "name": "10" 54 | }, 55 | { 56 | "rhs": "?a", 57 | "lhs": "(ap (ap mul one) ?a)", 58 | "name": "9" 59 | }, 60 | { 61 | "rhs": "one", 62 | "lhs": "(ap (ap mul (ap inv ?a)) ?a)", 63 | "name": "8" 64 | }, 65 | { 66 | "rhs": "(ap (ap mul (ap inv y)) y)", 67 | "lhs": "one", 68 | "name": "7" 69 | }, 70 | { 71 | "rhs": "one", 72 | "lhs": "(ap (ap mul (ap inv y)) y)", 73 | "name": "6" 74 | }, 75 | { 76 | "rhs": "(ap (ap mul (ap inv x)) x)", 77 | "lhs": "one", 78 | "name": "5" 79 | }, 80 | { 81 | "rhs": "one", 82 | "lhs": "(ap (ap mul (ap inv x)) x)", 83 | "name": "4" 84 | }, 85 | { 86 | "rhs": "(ap (ap mul (ap inv one)) one)", 87 | "lhs": "one", 88 | "name": "3" 89 | }, 90 | { 91 | "rhs": "one", 92 | "lhs": "(ap (ap mul (ap inv one)) one)", 93 | "name": "2" 94 | }, 95 | { 96 | "rhs": "(ap (ap mul ?a) (ap (ap mul ?b) ?c))", 97 | "lhs": "(ap (ap mul (ap (ap mul ?a) ?b)) ?c)", 98 | "name": "1" 99 | }, 100 | { 101 | "rhs": "(ap (ap mul (ap (ap mul ?a) ?b)) ?c)", 102 | "lhs": "(ap (ap mul ?a) (ap (ap mul ?b) ?c))", 103 | "name": "0" 104 | } 105 | ], 106 | "target-rhs": "(ap (ap mul (ap inv y)) (ap inv x))", 107 | "target-lhs": "(ap inv (ap (ap mul x) y))", 108 | "request": "perform-rewrite" 109 | } 110 | -------------------------------------------------------------------------------- /Evaluation/FunctionalProgramming.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | namespace Test 3 | 4 | 5 | inductive List α 6 | | nil : List α 7 | | cons : α → List α → List α 8 | 9 | 10 | notation a "::" b => List.cons a b 11 | notation "[" "]" => List.nil 12 | 13 | -- From init/data/list/basic.lean 14 | 15 | def append : List α → List α → List α 16 | | [], bs => bs 17 | | a::as, bs => a :: append as bs 18 | 19 | /- I should be able to derive these directly in the tactic... -/ 20 | def append_nil : ∀ (bs: List α), append ([] : List α) bs = bs := by 21 | intros 22 | rfl 23 | 24 | def append_cons : ∀ (a : α) (as bs : List α), append (a :: as) bs = a :: (append as bs) := by 25 | intros 26 | rfl 27 | 28 | instance {α : Type _} : HAppend (List α) (List α) (List α) where hAppend := append 29 | 30 | theorem append_assoc (as bs cs : List α) : (as ++ bs) ++ cs = as ++ (bs ++ cs) := by 31 | induction as with 32 | | nil => eggxplosion [append_nil, append_cons] 33 | | cons a as ih => eggxplosion [ih, append_nil, append_cons] -- could this also be automated? 34 | -- <;> eggxplosion 35 | 36 | 37 | def reverseAux : List α → List α → List α 38 | | [], r => r 39 | | a::l, r => reverseAux l (a::r) 40 | 41 | def List.reverse (as : List α) :List α := 42 | reverseAux as [] 43 | 44 | def reverseAux_nil : ∀ r : List α, reverseAux ([] : List α) r = r := by 45 | intros 46 | rfl 47 | 48 | def reverseAux_cons : ∀ (l r : List α) (a : α) , reverseAux (a :: l) r = reverseAux l (a :: r) := by 49 | intros 50 | rfl 51 | 52 | def reverse_def : ∀ l : List α, l.reverse = reverseAux l [] := by 53 | intros 54 | rfl 55 | 56 | theorem reverseAux_eq_append (as bs : List α) : reverseAux as bs = reverseAux as [] ++ bs := by 57 | induction as generalizing bs with 58 | | nil => eggxplosion [reverseAux_nil, reverseAux_cons] 59 | | cons a as ih => 60 | eggxplosion [reverseAux_nil, reverseAux_cons, append_assoc] 61 | -- <;> eggxplosion [reverseAux, append_assoc] 62 | 63 | theorem reverse_cons (a : α) (as : List α) : List.reverse (a :: as) = List.reverse as ++ (a :: List.nil) := by 64 | eggxplosion [reverse_def, reverseAux_nil, reverseAux_cons, reverseAux_eq_append] 65 | 66 | theorem reverse_append (as bs : List α) : (as ++ bs).reverse = bs.reverse ++ as.reverse := by 67 | induction as generalizing bs with 68 | | nil => eggxplosion [] 69 | | cons a as ih => eggxplosion [ih, append_assoc] 70 | -- <;> eggxplosion [append_assoc] 71 | 72 | def List.map : (α → β) → List α → List β 73 | | _, [] => [] 74 | | f, (a :: as) => (f a) :: (as.map f) 75 | 76 | def List.foldr (f : α → β → β) (init : β) : List α → β 77 | | [] => init 78 | | a :: l => f a (foldr f init l) 79 | 80 | -- A short cut to deforestation, Gill, Launchbury, Peyton Jones 81 | def all (p : α → Bool) (xs : List α) : Bool := (List.map p xs).foldr and true 82 | 83 | def all' : (α → Bool) → List α → Bool 84 | | _, [] => true 85 | | p, (x::xs) => (p x) && (all' p xs) 86 | 87 | theorem all_deforestation {α} : ∀ (p : α → Bool) (xs : List α), all p xs = all' p xs := by 88 | -- eggxplosion [all, all', map, foldr] 89 | intros p xs 90 | try induction xs with 91 | | nil => simp [all, all', List.foldr, List.map] 92 | | cons a as ih => simp [all, all', List.foldr, List.map, ih] 93 | eggxplosion [all, all', List.foldr, List.map] -- add induction explicitly (like in Agda?) 94 | -------------------------------------------------------------------------------- /Evaluation/GroupsKnuthBendixExploded.v: -------------------------------------------------------------------------------- 1 | Theorem inv_inv 2 | {G: Type} 3 | (inv: G -> G) 4 | (mul: G -> G -> G) 5 | (one: G) 6 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 7 | (assocMul': forall (a b c: G), (mul (mul a b) c) = mul a (mul b c)) 8 | (invLeft: forall (a: G), mul (inv a) a = one) 9 | (invLeft': forall (a: G), one = mul (inv a) a) 10 | (mulOne: forall (a: G), a = mul a one) 11 | (mulOne': forall (a: G), mul a one = a) 12 | (oneMul: forall (a: G), mul one a = a) 13 | (oneMul': forall (a: G), a = mul one a) 14 | (invRight: forall (a: G), one = mul a (inv a)) 15 | (invRight': forall (a: G), mul a (inv a) = one) 16 | (x: G) 17 | (invRightx : mul x (inv x) = one) 18 | (invRightOne : mul one (inv one) = one) 19 | (invRightx' : one = mul x (inv x)) 20 | (invRightOne' : one = mul one (inv one)) 21 | : (inv (inv x) = x). 22 | Proof. 23 | try congruence with assocMul assocMul' invLeft invLeft' mulOne mulOne' oneMul oneMul' invRight invRightx invRightx' invRightOne invRightOne'. 24 | Qed. 25 | 26 | Theorem inv_mul_cancel_left 27 | {G: Type} 28 | (inv: G -> G) 29 | (mul: G -> G -> G) 30 | (one: G) 31 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 32 | (invLeft: forall (a: G), mul (inv a) a = one) 33 | (mulOne: forall (a: G), a = mul a one) 34 | (oneMul: forall (a: G), mul one a = a) 35 | (invRight: forall (a: G), one = mul a (inv a)) 36 | (x y : G) 37 | : (mul (inv x) (mul x y)) = y. 38 | Proof. 39 | try congruence with assocMul invLeft mulOne oneMul invRight. 40 | Qed. 41 | 42 | Theorem mul_inv_cancel_left 43 | {G: Type} 44 | (inv: G -> G) 45 | (mul: G -> G -> G) 46 | (one: G) 47 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 48 | (invLeft: forall (a: G), mul (inv a) a = one) 49 | (mulOne: forall (a: G), a = mul a one) 50 | (oneMul: forall (a: G), mul one a = a) 51 | (invRight: forall (a: G), one = mul a (inv a)) 52 | (x y : G) 53 | : (mul x (mul (inv x) y)) = y. 54 | Proof. 55 | try congruence with assocMul invLeft mulOne oneMul invRight. 56 | Qed. 57 | 58 | Theorem inv_mul 59 | {G: Type} 60 | (inv: G -> G) 61 | (mul: G -> G -> G) 62 | (one: G) 63 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 64 | (invLeft: forall (a: G), mul (inv a) a = one) 65 | (mulOne: forall (a: G), a = mul a one) 66 | (oneMul: forall (a: G), mul one a = a) 67 | (invRight: forall (a: G), one = mul a (inv a)) 68 | (x y : G) 69 | (assocMul': forall (a b c: G), (mul (mul a b) c) = mul a (mul b c)) 70 | (invLeft': forall (a: G), one = mul (inv a) a) 71 | (mulOne': forall (a: G), mul a one = a) 72 | (oneMul': forall (a: G), a = mul one a) 73 | (invRight': forall (a: G), mul a (inv a) = one) 74 | (invRightx : mul x (inv x) = one) 75 | (invRightOne : mul one (inv one) = one) 76 | (invRightx' : one = mul x (inv x)) 77 | (invRightOne' : one = mul one (inv one)) 78 | (invRighty : mul y (inv y) = one) 79 | (invRighty' : one = mul y (inv y)) 80 | : (inv (mul x y)) = (mul (inv y) (inv x)). 81 | Proof. 82 | try congruence with assocMul assocMul' invLeft invLeft' mulOne mulOne' oneMul oneMul' invRight invRightx invRightx' invRightOne invRightOne' invRighty invRighty'. 83 | Admitted. 84 | 85 | Theorem one_inv 86 | {G: Type} 87 | (inv: G -> G) 88 | (mul: G -> G -> G) 89 | (one: G) 90 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 91 | (invLeft: forall (a: G), mul (inv a) a = one) 92 | (mulOne: forall (a: G), a = mul a one) 93 | (oneMul: forall (a: G), mul one a = a) 94 | (invRight: forall (a: G), one = mul a (inv a)) 95 | (x y : G) 96 | : (inv one) = one. 97 | Proof. 98 | try congruence with assocMul invLeft mulOne oneMul invRight. 99 | Qed. 100 | -------------------------------------------------------------------------------- /ffi-egg/src/main.rs: -------------------------------------------------------------------------------- 1 | // TODO: see https://github.com/yatima-inc/RustFFI.lean 2 | use egg::{rewrite as rw, *}; 3 | 4 | // #[derive(Debug)] 5 | // pub struct MatchAST { 6 | // target: L 7 | 8 | // } 9 | // impl CostFunction for MatchAST { 10 | // type Cost = usize; 11 | // fn cost(&mut self, enode: &L, mut costs: C) -> Self::Cost 12 | // where 13 | // C: FnMut(Id) -> Self::Cost, 14 | // { 15 | // if (enode.eq(&self.target)) { return 1; } else { return 100; } 16 | // } 17 | // } 18 | 19 | struct SillyCostFn; 20 | impl CostFunction for SillyCostFn { 21 | type Cost = f64; 22 | fn cost(&mut self, enode: &SymbolLang, mut costs: C) -> Self::Cost 23 | where 24 | C: FnMut(Id) -> Self::Cost 25 | { 26 | let op_cost = match enode.op.as_str() { 27 | "ANSWER" => 1.0, 28 | _ => 100.0 29 | }; 30 | enode.fold(op_cost, |sum, id| sum + costs(id)) 31 | } 32 | } 33 | 34 | #[derive(Debug)] 35 | pub struct AstSizeFive; 36 | impl CostFunction for AstSizeFive { 37 | type Cost = usize; 38 | fn cost(&mut self, enode: &L, mut costs: C) -> Self::Cost 39 | where 40 | C: FnMut(Id) -> >::Cost, 41 | { 42 | let tot_size = enode.fold(1, |sum, id| sum + costs(id)); 43 | println!("tot_size: {}",tot_size); 44 | if tot_size == 5 {1} else {100} 45 | } 46 | } 47 | 48 | fn mainGroupCheck() { 49 | 50 | let rules: &[Rewrite] = &[ 51 | rw!("assoc-mul"; "(* ?a (* ?b ?c))" => "(* (* ?a ?b) ?c)"), 52 | rw!("assoc-mul'"; "(* (* ?a ?b) ?c)" => "(* ?a (* ?b ?c))"), 53 | rw!("one-mul"; "(* 1 ?a)" => "?a"), 54 | rw!("one-mul'"; "?a" => "(* 1 ?a)"), 55 | rw!("inv-left"; "(* (^-1 ?a) ?a)" => "1"), 56 | rw!("inv-left'"; "1" => "(* (^-1 a) a)"), 57 | rw!("inv-left''"; "1" => "(* (^-1 b) b)"), 58 | rw!("mul-one"; "(* ?a 1)" => "?a"), 59 | rw!("mul-one'"; "?a" => "(* ?a 1)" ), 60 | rw!("inv-right"; "(* ?a (^-1 ?a))" => "1"), 61 | rw!("inv-right'"; "1" => "(* a (^-1 a))"), 62 | rw!("inv-right''"; "1" => "(* b (^-1 b))"), 63 | //rw!("anwser''"; "(* (^-1 b)(^-1 a))" => "ANSWER"), 64 | 65 | ]; 66 | 67 | // We can make our own Language later to work with other types. 68 | //(a * b) * (1⁻¹⁻¹ * b⁻¹ * ((a⁻¹ * a⁻¹⁻¹⁻¹) * a)) 69 | //let start = "(* (* a b)(* (^-1 (^-1 1)) (* (^-1 b) (* (* (^-1 a) (^-1 (^-1 (^-1 a)))) a))))".parse().unwrap(); 70 | 71 | // a * 1 -- won't work without 72 | //let start = "(* a 1)".parse().unwrap(); 73 | 74 | // a⁻¹⁻¹ = a 75 | let start = "(^-1 (^-1 a))".parse().unwrap(); 76 | 77 | // a⁻¹ * (a * b) = b 78 | //let start = "(* (^-1 a) (* a b))".parse().unwrap(); 79 | 80 | // a * (a⁻¹ * b) = b 81 | //let start = "(* a (* (^-1 a) b))".parse().unwrap(); 82 | 83 | //(a * b)⁻¹ = b⁻¹ * a⁻¹ 84 | // let start = "(^-1 (* a b))".parse().unwrap(); 85 | //let start = "(* 1 (* 1 1))".parse().unwrap(); 86 | //let start = "(* (^-1 b) (^-1 a))".parse().unwrap(); 87 | 88 | //(1 : G)⁻¹ = 1 89 | //let start = "(^-1 1)".parse().unwrap(); 90 | 91 | // That's it! We can run equality saturation now. 92 | let mut runner = Runner::default() 93 | //.with_node_limit(105) 94 | // .with_explanations_enabled() 95 | .with_expr(&start) 96 | .run(rules); 97 | 98 | // Dump 99 | // runner.egraph.dot().to_pdf("target/group.pdf").unwrap(); 100 | //runner.egraph.dot().to_pdf("target/group2.pdf").unwrap(); 101 | //println!("Debug: {:?} \n \n", runner.egraph.dump()); 102 | 103 | // Extractors can take a user-defined cost function, 104 | // we'll use the egg-provided AstSize for now 105 | let mut extractor = Extractor::new(&runner.egraph, AstSize); 106 | 107 | 108 | // We want to extract the best expression represented in the 109 | // same e-class as our initial expression, not from the whole e-graph. 110 | // Luckily the runner stores the eclass Id where we put the initial expression. 111 | let (best_cost, best_expr) = extractor.find_best(runner.roots[0]); 112 | 113 | 114 | println!("Best expr : {:?} -- cost: {}",best_expr, best_cost); 115 | // println!("{}", runner.explain_equivalence(&start, &best_expr).get_flat_string()); 116 | // we found the best thing, which is just "a" in this case 117 | } 118 | 119 | fn main() { 120 | mainGroupCheck(); 121 | } 122 | -------------------------------------------------------------------------------- /json-egg/json-inputs/test.json: -------------------------------------------------------------------------------- 1 | {"request":"perform-rewrite","target-lhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.396) _uniq.397))","target-rhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.396)) _uniq.397)","rewrites":[{"name":"27","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 ?_uniq.416) ?_uniq.417)) ?_uniq.418)","rhs":"(ap (ap _uniq.393 ?_uniq.416) (ap (ap _uniq.393 ?_uniq.417) ?_uniq.418))"},{"name":"26","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.397)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.397) _uniq.397))"},{"name":"25","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.397)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.397) _uniq.396))"},{"name":"24","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.397)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.397) _uniq.395))"},{"name":"23","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.396)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.396) _uniq.397))"},{"name":"22","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.396)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.396) _uniq.396))"},{"name":"21","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.396)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.396) _uniq.395))"},{"name":"20","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.395)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.395) _uniq.397))"},{"name":"19","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.395)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.395) _uniq.396))"},{"name":"18","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.397) _uniq.395)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.397) (ap (ap _uniq.393 _uniq.395) _uniq.395))"},{"name":"17","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.397)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.397) _uniq.397))"},{"name":"16","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.397)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.397) _uniq.396))"},{"name":"15","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.397)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.397) _uniq.395))"},{"name":"14","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.396)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.396) _uniq.397))"},{"name":"13","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.396)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.396) _uniq.396))"},{"name":"12","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.396)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.396) _uniq.395))"},{"name":"11","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.395)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.395) _uniq.397))"},{"name":"10","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.395)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.395) _uniq.396))"},{"name":"9","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.396) _uniq.395)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.396) (ap (ap _uniq.393 _uniq.395) _uniq.395))"},{"name":"8","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.397)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.397) _uniq.397))"},{"name":"7","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.397)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.397) _uniq.396))"},{"name":"6","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.397)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.397) _uniq.395))"},{"name":"5","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.396)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.396) _uniq.397))"},{"name":"4","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.396)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.396) _uniq.396))"},{"name":"3","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.396)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.396) _uniq.395))"},{"name":"2","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.395)) _uniq.397)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.395) _uniq.397))"},{"name":"1","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.395)) _uniq.396)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.395) _uniq.396))"},{"name":"0","lhs":"(ap (ap _uniq.393 (ap (ap _uniq.393 _uniq.395) _uniq.395)) _uniq.395)","rhs":"(ap (ap _uniq.393 _uniq.395) (ap (ap _uniq.393 _uniq.395) _uniq.395))"}]} 2 | 3 | -------------------------------------------------------------------------------- /PrettyPrinter.lean: -------------------------------------------------------------------------------- 1 | 2 | inductive AST : Type where 3 | | mul : AST → AST → AST 4 | | inv : AST → AST 5 | | symbol : String → AST 6 | | rewrite : String → AST → AST 7 | | seq : AST → AST → AST 8 | | const : Nat → AST 9 | 10 | declare_syntax_cat eggExpr 11 | syntax "(" "*" eggExpr eggExpr ")" : eggExpr 12 | syntax "(" "^-1" eggExpr ")" : eggExpr 13 | syntax "(" "Rewrite" "<=" ident eggExpr ")" : eggExpr 14 | syntax "(" "Rewrite" "=>" ident eggExpr ")" : eggExpr 15 | syntax eggExpr "," eggExpr : eggExpr 16 | -- syntax "(" eggExpr ")" : eggExpr 17 | syntax ident : eggExpr 18 | syntax num : eggExpr 19 | 20 | -- auxiliary notation for translating `eggExpr` into `term` 21 | syntax "`[Egg| " eggExpr "]" : term 22 | macro_rules 23 | | `(`[Egg| (* $x:eggExpr $y:eggExpr) ]) => `(AST.mul `[Egg| $x] `[Egg| $y]) 24 | | `(`[Egg| (^-1 $x:eggExpr) ]) => `(AST.inv `[Egg| $x]) 25 | -- | `(`[Egg| ( $x:eggExpr ) ]) => `[Egg| $x] 26 | | `(`[Egg| $x:ident ]) => `(AST.symbol $(Lean.quote (toString x.getId))) 27 | | `(`[Egg| (Rewrite<= $x:ident $y:eggExpr) ]) => `(AST.rewrite ("<=" ++ $(Lean.quote (toString x.getId))) `[Egg| $y]) 28 | | `(`[Egg| (Rewrite => $x:ident $y:eggExpr) ]) => `(AST.rewrite ("=>" ++ $(Lean.quote (toString x.getId))) `[Egg| $y]) 29 | | `(`[Egg| $x , $y ]) => `(AST.seq `[Egg| $x] `[Egg| $y]) 30 | | `(`[Egg| $num:numLit ]) => `(AST.const $num ) 31 | 32 | def pretty_print : AST → String 33 | | AST.mul x y => "(" ++ (pretty_print x) ++ ") * (" ++ (pretty_print y) ++ ")" 34 | | AST.inv x => "(" ++ (pretty_print x) ++ ")^{-1}" 35 | | AST.rewrite _ x => pretty_print x 36 | | AST.symbol x => x 37 | | AST.const x => toString x 38 | | AST.seq x y => (pretty_print x) ++ " → " ++ (pretty_print y) 39 | 40 | -- #check `[Egg| (* a b)] 41 | #eval pretty_print `[Egg| (* a b)] 42 | 43 | def rewrite : AST := `[Egg| (^-1 (* a b)) 44 | , (Rewrite=> one_mul' (* 1 (^-1 (* a b)))) 45 | , (* 1 (Rewrite=> one_mul' (* 1 (^-1 (* a b))))) 46 | , (Rewrite=> assoc_mul (* (* 1 1) (^-1 (* a b)))) 47 | , (* (Rewrite<= one_mul' 1) (^-1 (* a b))) 48 | , (* (Rewrite=> inv_left'' (* (^-1 b) b)) (^-1 (* a b))) 49 | , (Rewrite=> assoc_mul' (* (^-1 b) (* b (^-1 (* a b))))) 50 | , (* (Rewrite=> mul_one' (* (^-1 b) 1)) (* b (^-1 (* a b)))) 51 | , (Rewrite=> assoc_mul' (* (^-1 b) (* 1 (* b (^-1 (* a b)))))) 52 | , (* (^-1 b) (* (Rewrite=> inv_right'' (* b (^-1 b))) (* b (^-1 (* a b))))) 53 | , (* (^-1 b) (Rewrite<= assoc_mul (* b (* (^-1 b) (* b (^-1 (* a b))))))) 54 | , (* (^-1 b) (* b (Rewrite<= assoc_mul' (* (* (^-1 b) b) (^-1 (* a b)))))) 55 | , (* (^-1 b) (* b (* (Rewrite<= inv_left'' 1) (^-1 (* a b))))) 56 | , (* (^-1 b) (* b (* (Rewrite=> one_mul' (* 1 1)) (^-1 (* a b))))) 57 | , (* (^-1 b) (* b (Rewrite<= assoc_mul (* 1 (* 1 (^-1 (* a b))))))) 58 | , (* (^-1 b) (* b (* 1 (Rewrite<= one_mul' (^-1 (* a b)))))) 59 | , (* (^-1 b) (* b (Rewrite<= one_mul' (^-1 (* a b))))) 60 | , (* (^-1 b) (* (Rewrite=> one_mul' (* 1 b)) (^-1 (* a b)))) 61 | , (* (^-1 b) (* (* 1 (Rewrite=> one_mul' (* 1 b))) (^-1 (* a b)))) 62 | , (* (^-1 b) (* (Rewrite=> assoc_mul (* (* 1 1) b)) (^-1 (* a b)))) 63 | , (* (^-1 b) (* (* (Rewrite<= one_mul' 1) b) (^-1 (* a b)))) 64 | , (* (^-1 b) (* (* (Rewrite=> inv_left' (* (^-1 a) a)) b) (^-1 (* a b)))) 65 | , (* (^-1 b) (* (Rewrite=> assoc_mul' (* (^-1 a) (* a b))) (^-1 (* a b)))) 66 | , (* (^-1 b) (Rewrite=> assoc_mul' (* (^-1 a) (* (* a b) (^-1 (* a b)))))) 67 | , (* (^-1 b) (* (^-1 a) (Rewrite=> inv_right 1))) 68 | , (* (^-1 b) (Rewrite<= mul_one' (^-1 a))) 69 | ] 70 | 71 | def rewrite2 : AST := `[Egg| 72 | (^-1 (^-1 a)) 73 | ,(Rewrite=> mul_one' (* (^-1 (^-1 a)) 1)) 74 | ,(* (Rewrite=> mul_one' (* (^-1 (^-1 a)) 1)) 1) 75 | ,(Rewrite=> assoc_mul' (* (^-1 (^-1 a)) (* 1 1))) 76 | ,(* (Rewrite=> one_mul' (* 1 (^-1 (^-1 a)))) (* 1 1)) 77 | ,(* (* 1 (Rewrite=> one_mul' (* 1 (^-1 (^-1 a))))) (* 1 1)) 78 | ,(* (Rewrite=> assoc_mul (* (* 1 1) (^-1 (^-1 a)))) (* 1 1)) 79 | ,(* (* (Rewrite<= one_mul' 1) (^-1 (^-1 a))) (* 1 1)) 80 | ,(* (* (Rewrite=> inv_right' (* a (^-1 a))) (^-1 (^-1 a))) (* 1 1)) 81 | ,(* (Rewrite=> assoc_mul' (* a (* (^-1 a) (^-1 (^-1 a))))) (* 1 1)) 82 | ,(* (* a (* (^-1 a) (^-1 (^-1 a)))) (Rewrite<= one_mul' 1)) 83 | ,(Rewrite=> assoc_mul' (* a (* (* (^-1 a) (^-1 (^-1 a))) 1))) 84 | ,(* a (Rewrite<= assoc_mul (* (^-1 a) (* (^-1 (^-1 a)) 1)))) 85 | ,(* a (* (^-1 a) (Rewrite<= mul_one' (^-1 (^-1 a))))) 86 | ,(* a (Rewrite=> inv_right 1)) 87 | ,(* a (Rewrite=> one_mul' (* 1 1))) 88 | ,(Rewrite<= assoc_mul' (* (* a 1) 1)) 89 | ,(* (Rewrite<= mul_one' a) 1) 90 | ,(Rewrite<= mul_one' a) 91 | ] 92 | #eval pretty_print rewrite 93 | #eval pretty_print rewrite2 94 | 95 | class group (G : Type) extends has_mul G, 96 | has_one G, has_inv G := 97 | (mul_asoc : ∀ (a b c : G), 98 | a * b * c = a * (b * c)) 99 | (one_mul : ∀ (a : G), 1 * a = a) 100 | (mul_one : ∀ (a : G), a * 1 = a) 101 | (mul_left_inv : ∀ (a : G), a⁻¹ * a = 1) 102 | (mul_right_inv : ∀ (a : G), a * a⁻¹ = 1) 103 | -------------------------------------------------------------------------------- /EggTactic/Test.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | 3 | set_option trace.EggTactic.egg true 4 | -- TODO @andres: what egg gives us back is wrong. What we really need to know is the rewrite term that we need to use. 5 | -- This means we should build the sexpr ( ... ) and then use this to perform the rewrite. 6 | -- Instead, what `egg` gives us is the goal state after the rewrite. 7 | -- 8 | -- This is a problem because we need to know the rewrite term to perform the rewrite. 9 | -- We can't just use the goal state because the goal state is the result of the rewrite. 10 | 11 | 12 | theorem testSuccess0 (anat: Nat) (bnat: Nat) (H: anat = bnat): anat = bnat := by { 13 | intros; 14 | eggxplosion [H] 15 | } 16 | 17 | #print testSuccess0 18 | 19 | 20 | set_option pp.analyze true in 21 | theorem testSuccess (anat: Nat) (bint: Int) (cnat: Nat) 22 | (dint: Int) (eint: Int) (a_eq_a: anat = anat) (b_eq_d: bint = dint) (d_eq_e: dint = eint): bint = eint := by 23 | -- eggxplosion [not_rewrite] 24 | -- eggxplosion [rewrite_wrong_type] 25 | -- rewrite [b_eq_d] 26 | -- rewrite [d_eq_e] 27 | -- rfl 28 | eggxplosion [b_eq_d, d_eq_e] 29 | 30 | 31 | #print testSuccess 32 | 33 | 34 | theorem testSuccess0Symm (anat: Nat) (bnat: Nat) (H: bnat = anat): anat = bnat := by { 35 | intros; 36 | eggxplosion [H] 37 | } 38 | 39 | #print testSuccess0Symm 40 | 41 | -- elab "boiledEgg" "[" rewrites:ident,* "]" : tactic => withMainContext do 42 | 43 | -- | test that we can run rewrites 44 | 45 | 46 | 47 | -- | test that we can run theorems in reverse. 48 | theorem testSuccessRev : ∀ (anat: Nat) (bint: Int) (cnat: Nat) 49 | (dint: Int) (eint: Int) (a_eq_a: anat = anat) (b_eq_d: bint = dint) (d_eq_e: dint = eint), 50 | eint = bint := by 51 | intros a b c d e aeqa beqd deqe 52 | eggxplosion [beqd, deqe] 53 | 54 | #print testSuccessRev 55 | 56 | 57 | 58 | /- 59 | theorem testManualInstantiation 60 | (group_inv: forall (g: Int), g - g = 0) 61 | (h: Int) (k: Int): h - h = k - k := by 62 | have gh : h - h = 0 := group_inv h 63 | have gk : k - k = 0 := group_inv k 64 | -- TODO: figure out how to get these 65 | -- manually instantiated locals to work. 66 | -- @bollu's hypothesis is that we need to force 67 | -- metavariable resolution at the value and type level 68 | -- with a couple 'reduce's. 69 | eggxplosion [gh, gk] 70 | 71 | #print testManualInstantiation 72 | -/ 73 | 74 | 75 | theorem assoc_instantiate(G: Type) 76 | (mul: G → G → G) 77 | (assocMul: forall (a b c: G), (mul (mul a b) c) = mul a (mul b c)) 78 | (x y z: G) : mul x (mul y z) = mul (mul x y) z := by { 79 | eggxplosion [assocMul] 80 | } 81 | 82 | #print assoc_instantiate 83 | 84 | 85 | /- 86 | theorem testGoalNotEqualityMustFail : ∀ (a: Nat) (b: Int) (c: Nat) , Nat := by 87 | intros a b c 88 | eggxplosion [] 89 | sorry 90 | -/ 91 | 92 | 93 | 94 | /- 95 | rw!("assoc-mul"; "(* ?a (* ?b ?c))" => "(* (* ?a ?b) ?c)"), 96 | rw!("assoc-mul'"; "(* (* ?a ?b) ?c)" => "(* ?a (* ?b ?c))"), 97 | rw!("one-mul"; "(* 1 ?a)" => "?a"), 98 | rw!("one-mul'"; "?a" => "(* 1 ?a)"), 99 | rw!("inv-left"; "(* (^-1 ?a) ?a)" => "1"), 100 | rw!("inv-left'"; "1" => "(* (^-1 a) a)"), 101 | rw!("inv-left''"; "1" => "(* (^-1 b) b)"), 102 | rw!("mul-one"; "(* ?a 1)" => "?a"), 103 | rw!("mul-one'"; "?a" => "(* ?a 1)" ), 104 | rw!("inv-right"; "(* ?a (^-1 ?a))" => "1"), 105 | rw!("inv-right'"; "1" => "(* a (^-1 a))"), 106 | rw!("inv-right''"; "1" => "(* b (^-1 b))"), 107 | //rw!("anwser''"; "(* (^-1 b)(^-1 a))" => "ANSWER"), 108 | -/ 109 | theorem inv_mul_cancel_left (G: Type) 110 | (inv: G → G) 111 | (mul: G → G → G) 112 | (one: G) 113 | (x: G) 114 | (assocMul: forall (a b c: G), mul a (mul b c) = (mul (mul a b) c)) 115 | (invLeft: forall (a: G), mul (inv a) a = one) 116 | (mulOne: forall (a: G), a = mul a one) 117 | (invRight: forall (a: G), one = mul a (inv a)) 118 | --(invRightX: one = mul x (inv x)) 119 | : (inv (inv x) = x) := by 120 | eggxplosion [assocMul, invLeft, mulOne, invRight] (timeLimit := 3) 121 | 122 | #print inv_mul_cancel_left 123 | 124 | def all {α : Type} (p : α → Bool) (xs : List α) := List.and (List.map p xs) 125 | def all'{α : Type} (p : α → Bool) (xs : List α) := List.foldr (λ a b => b && (p a)) True xs 126 | 127 | theorem deforestation : ∀ (p : α → Bool) (xs : List α), all p xs = all' p xs := by 128 | intros p xs 129 | unfold all 130 | unfold all' 131 | unfold List.map 132 | unfold List.and 133 | unfold List.all 134 | induction xs with 135 | | nil => 136 | simp 137 | rfl 138 | | cons head tail ih => 139 | -- ⊢ List.foldr (fun a r => a && r) true (p head :: List.map p tail) = List.foldr (fun a b => b && p a) true (head :: tail) 140 | 141 | 142 | 143 | 144 | theorem testInstantiation 145 | -- TODO: need to change definitions to make all arguments implicit, since those are the only kinds of rewrites 146 | -- egg can cope with! 147 | (group_inv: forall {g: Int}, g - g = 0) 148 | (h: Int) (k: Int): h - h = k - k := by { 149 | -- expected no bound variables, we use locally nameless. 150 | -- @andres: I strongly suspect the toExplode 151 | -- array somehow leaking in `bvars` on accident. A cursory glance at it did not show me what it 152 | -- does when it doesn't explode a variable; I would have expected it to instantiate an `mvar`. 153 | eggxplosion [group_inv]; 154 | } 155 | 156 | 157 | #print testInstantiation 158 | -------------------------------------------------------------------------------- /json-egg/src/scheduler.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug, Formatter}; 2 | use log::*; 3 | use egg::*; 4 | use indexmap::IndexMap; 5 | 6 | // Based or egg's `BackoffScheduler` 7 | pub struct BoundedGraphScheduler { 8 | max_graph_size: usize, 9 | default_application_limit : usize, 10 | search_threshold : usize, 11 | stats: IndexMap, 12 | } 13 | 14 | #[derive(Debug, PartialEq)] 15 | enum RuleStatus { 16 | Available, 17 | Paused, 18 | Stopped 19 | } 20 | 21 | use RuleStatus::{Available,Paused,Stopped}; 22 | 23 | #[derive(Debug)] 24 | struct RuleStats { 25 | times_applied: usize, 26 | match_limit : usize, 27 | times_paused : usize, 28 | status: RuleStatus, 29 | } 30 | 31 | impl BoundedGraphScheduler { 32 | /// Set the initial application limit after which a rule will be banned. 33 | /// Default: 1,000 34 | pub fn with_initial_match_limit(mut self, limit: usize) -> Self { 35 | self.default_application_limit = limit; 36 | self 37 | } 38 | 39 | /// Set the initial ban length. 40 | /// Default: 5 iterations 41 | pub fn with_max_graph_size(mut self, graph_size: usize) -> Self { 42 | self.max_graph_size = graph_size; 43 | self 44 | } 45 | 46 | fn rule_stats(&mut self, name: Symbol) -> &mut RuleStats { 47 | if self.stats.contains_key(&name) { 48 | &mut self.stats[&name] 49 | } else { 50 | self.stats.entry(name).or_insert(RuleStats { 51 | times_applied: 0, 52 | times_paused: 0, 53 | match_limit: self.default_application_limit, 54 | status : Available 55 | }) 56 | } 57 | } 58 | 59 | /// Never ban a particular rule. 60 | pub fn do_not_ban(mut self, name: impl Into) -> Self { 61 | self.rule_stats(name.into()).match_limit = usize::MAX; 62 | self 63 | } 64 | 65 | /// Set the initial match limit for a rule. 66 | pub fn rule_match_limit(mut self, name: impl Into, limit: usize) -> Self { 67 | self.rule_stats(name.into()).match_limit = limit; 68 | self 69 | } 70 | 71 | } 72 | 73 | impl Default for BoundedGraphScheduler { 74 | fn default() -> Self { 75 | Self { 76 | stats: Default::default(), 77 | max_graph_size: 2_000, 78 | // TODO: add a function that computes this from the rewrites! 79 | search_threshold: 20, 80 | default_application_limit : 64, 81 | } 82 | } 83 | } 84 | 85 | impl RewriteScheduler for BoundedGraphScheduler 86 | where 87 | L: Language, 88 | { 89 | fn can_stop(&mut self, iteration: usize) -> bool { 90 | 91 | let n_stats = self.stats.len(); 92 | 93 | let mut paused: Vec<_> = self 94 | .stats 95 | .iter_mut() 96 | .filter(|(_, s)| s.status == Paused) 97 | .collect(); 98 | 99 | if paused.is_empty(){ 100 | true 101 | } else { 102 | let mut unpaused = vec![]; 103 | for (name, s) in &mut paused { 104 | s.status = Available; 105 | unpaused.push(name.as_str()); 106 | } 107 | 108 | assert!(!unpaused.is_empty()); 109 | info!( 110 | "unpaused {}", 111 | unpaused.join(", "), 112 | ); 113 | 114 | false 115 | } 116 | } 117 | 118 | fn search_rewrite<'a>( 119 | &mut self, 120 | iteration: usize, 121 | egraph: &EGraph, 122 | rewrite: &'a Rewrite, 123 | ) -> Vec> { 124 | let stats = self.rule_stats(rewrite.name); 125 | 126 | if stats.status == Paused { 127 | debug!( 128 | "Skipping {} ({}-{}), paused.", 129 | rewrite.name, stats.times_applied, stats.times_paused 130 | ); 131 | return vec![]; 132 | } 133 | 134 | let threshold = stats.match_limit << stats.times_paused; 135 | let matches = rewrite.search_with_limit(egraph, threshold + 1); 136 | let total_len: usize = matches.iter().map(|m| m.substs.len()).sum(); 137 | if total_len > threshold { 138 | stats.status = Paused; 139 | info!( 140 | "Banning {} ({}) : {} < {}", 141 | rewrite.name, 142 | stats.times_applied, 143 | threshold, 144 | total_len, 145 | ); 146 | vec![] 147 | } else { 148 | stats.times_applied += 1; 149 | matches 150 | } 151 | } 152 | fn apply_rewrite( 153 | &mut self, 154 | iteration: usize, 155 | egraph: &mut EGraph, 156 | rewrite: &Rewrite, 157 | matches: Vec>, 158 | ) -> usize { 159 | //println!("egraph size: {}", egraph.total_size()); 160 | if egraph.total_size() + self.search_threshold < self.max_graph_size{ 161 | rewrite.apply(egraph, &matches).len() 162 | } 163 | else{ 164 | // This is obviously super inefficient... 165 | // we should be able to try and reverse if we don't like it. 166 | let mut egraph_copy = egraph.clone(); 167 | rewrite.apply(&mut egraph_copy, &matches).len(); 168 | if egraph_copy.total_size() > self.max_graph_size{ 169 | let stats = self.rule_stats(rewrite.name); 170 | stats.status = Stopped; 171 | 0 172 | } 173 | else{ 174 | //just reapplying; could otherwise use the copy here 175 | //but then I'd have to think about the references... 176 | rewrite.apply(egraph, &matches).len() 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Evaluation/scaling-proof-length/Scaling.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | -- Rewrites that force a counter to count upward. 3 | 4 | inductive B where -- bit 5 | | O : B 6 | | I : B 7 | open B 8 | 9 | def count_upward_v3 10 | (count: B -> B -> B -> B) 11 | (count_0: ∀ (b2 b1: B), count b2 b1 O = count b2 b1 I) 12 | (count_1: ∀ (b2: B), count b2 O I = count b2 I O) 13 | (count_2: count O I I = count I O O): count I I I = count O O O := by { 14 | simp[count_0, count_1, count_2]; 15 | -- rawEgg[count_0, count_1, count_2]; 16 | } 17 | 18 | /- 19 | inductive N where -- unary encoding of natural numbers 20 | | Z : N 21 | | S : N -> N 22 | 23 | 24 | open N 25 | open B 26 | 27 | axiom BinNum : Type 28 | axiom BinNum.get: BinNum -> N -> B -- get Nth bit 29 | axiom counter: N -> BinNum -- counter value at nth step 30 | 31 | axiom counter_begin (ix: N): (counter Z).get ix = O 32 | 33 | -- 0 0 0 -> 0 0 1 34 | axiom axiom_000_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 35 | (counter (S n)).get (S (S i)) = O 36 | axiom axiom_000_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 37 | (counter (S n)).get (S i) = O 38 | axiom axiom_000_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 39 | (counter (S n)).get i = I 40 | 41 | -- 0 0 1 -> 0 1 0 42 | -- Ssn Sn n 43 | axiom axiom_001_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 44 | (counter (S n)).get (S (S i)) = O 45 | axiom axiom_001_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 46 | (counter (S n)).get (S i) = I 47 | axiom axiom_001_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 48 | (counter (S n)).get i = O 49 | 50 | -- 0 1 0 ->0 1 1 51 | -- Ssn Sn n 52 | axiom axiom_010_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 53 | (counter (S n)).get (S (S i)) = O 54 | axiom axiom_010_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 55 | (counter (S n)).get (S i) = I 56 | axiom axiom_010_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 57 | 58 | -- 0 1 1 -> 1 0 0 59 | axiom axiom_011_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 60 | (counter (S n)).get (S (S i)) = I 61 | axiom axiom_011_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 62 | (counter (S n)).get (S i) = O 63 | axiom axiom_011_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 64 | (counter (S n)).get i = O 65 | 66 | abbrev one : N := S Z 67 | abbrev two : N := S one 68 | abbrev three : N := S two 69 | abbrev four : N := S three 70 | abbrev five : N := S four 71 | abbrev six: N := S five 72 | abbrev seven : N := S six 73 | 74 | /- 75 | #print seven 76 | theorem count_upward_7_at_0: (counter seven).get Z = I := by { 77 | rawEgg [axiom_000_SSn, axiom_000_Sn, axiom_000_n 78 | , axiom_001_SSn, axiom_001_Sn, axiom_001_n 79 | , axiom_010_SSn, axiom_010_Sn, axiom_010_n 80 | , axiom_011_SSn, axiom_011_Sn, axiom_011_n] 81 | sorry 82 | -/ 83 | 84 | def foo 85 | (x: Int) 86 | (y: Nat): True := sorry 87 | 88 | def count_upward_7_at_0' 89 | -- 0 0 0 -> 0 0 1 90 | (axiom_000_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = O) 91 | (axiom_000_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = O) 92 | (axiom_000_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get i = I) 93 | -- 0 0 1 -> 0 1 0 94 | -- Ssn Sn n 95 | (axiom_001_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get (S (S i)) = O) 96 | (axiom_001_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get (S i) = I) 97 | (axiom_001_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get i = O) 98 | -- 0 1 0 ->0 1 1 99 | -- Ssn Sn n 100 | (axiom_010_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = O) 101 | (axiom_010_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = I) 102 | (axiom_010_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get i = I) 103 | -- 0 1 1 -> 1 0 0 104 | (axiom_011_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = I) 105 | (axiom_011_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = O) 106 | (axiom_011_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get i = O): 107 | (counter seven).get Z = I := by { 108 | rawEgg [axiom_000_SSn, axiom_000_Sn, axiom_000_n 109 | , axiom_001_SSn, axiom_001_Sn, axiom_001_n 110 | , axiom_010_SSn, axiom_010_Sn, axiom_010_n 111 | , axiom_011_SSn, axiom_011_Sn, axiom_011_n]; 112 | sorry 113 | 114 | } 115 | -/ 116 | 117 | -------------------------------------------------------------------------------- /Evaluation/scaling-space-and-proof/Scaling.lean: -------------------------------------------------------------------------------- 1 | import EggTactic 2 | -- Rewrites that force a counter to count upward. 3 | 4 | inductive B where -- bit 5 | | O : B 6 | | I : B 7 | open B 8 | 9 | def count_upward_v3 10 | (count: B -> B -> B -> B) 11 | (count_0: ∀ (b2 b1: B), count b2 b1 O = count b2 b1 I) 12 | (count_1: ∀ (b2: B), count b2 O I = count b2 I O) 13 | (count_2: count O I I = count I O O): count I I I = count O O O := by { 14 | simp[count_0, count_1, count_2]; 15 | -- rawEgg[count_0, count_1, count_2]; 16 | } 17 | 18 | /- 19 | inductive N where -- unary encoding of natural numbers 20 | | Z : N 21 | | S : N -> N 22 | 23 | 24 | open N 25 | open B 26 | 27 | axiom BinNum : Type 28 | axiom BinNum.get: BinNum -> N -> B -- get Nth bit 29 | axiom counter: N -> BinNum -- counter value at nth step 30 | 31 | axiom counter_begin (ix: N): (counter Z).get ix = O 32 | 33 | -- 0 0 0 -> 0 0 1 34 | axiom axiom_000_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 35 | (counter (S n)).get (S (S i)) = O 36 | axiom axiom_000_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 37 | (counter (S n)).get (S i) = O 38 | axiom axiom_000_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O): 39 | (counter (S n)).get i = I 40 | 41 | -- 0 0 1 -> 0 1 0 42 | -- Ssn Sn n 43 | axiom axiom_001_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 44 | (counter (S n)).get (S (S i)) = O 45 | axiom axiom_001_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 46 | (counter (S n)).get (S i) = I 47 | axiom axiom_001_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I): 48 | (counter (S n)).get i = O 49 | 50 | -- 0 1 0 ->0 1 1 51 | -- Ssn Sn n 52 | axiom axiom_010_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 53 | (counter (S n)).get (S (S i)) = O 54 | axiom axiom_010_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 55 | (counter (S n)).get (S i) = I 56 | axiom axiom_010_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 57 | 58 | -- 0 1 1 -> 1 0 0 59 | axiom axiom_011_SSn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 60 | (counter (S n)).get (S (S i)) = I 61 | axiom axiom_011_Sn (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 62 | (counter (S n)).get (S i) = O 63 | axiom axiom_011_n (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O): 64 | (counter (S n)).get i = O 65 | 66 | abbrev one : N := S Z 67 | abbrev two : N := S one 68 | abbrev three : N := S two 69 | abbrev four : N := S three 70 | abbrev five : N := S four 71 | abbrev six: N := S five 72 | abbrev seven : N := S six 73 | 74 | /- 75 | #print seven 76 | theorem count_upward_7_at_0: (counter seven).get Z = I := by { 77 | rawEgg [axiom_000_SSn, axiom_000_Sn, axiom_000_n 78 | , axiom_001_SSn, axiom_001_Sn, axiom_001_n 79 | , axiom_010_SSn, axiom_010_Sn, axiom_010_n 80 | , axiom_011_SSn, axiom_011_Sn, axiom_011_n] 81 | sorry 82 | -/ 83 | 84 | def foo 85 | (x: Int) 86 | (y: Nat): True := sorry 87 | 88 | def count_upward_7_at_0' 89 | -- 0 0 0 -> 0 0 1 90 | (axiom_000_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = O) 91 | (axiom_000_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = O) 92 | (axiom_000_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = O), (counter (S n)).get i = I) 93 | -- 0 0 1 -> 0 1 0 94 | -- Ssn Sn n 95 | (axiom_001_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get (S (S i)) = O) 96 | (axiom_001_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get (S i) = I) 97 | (axiom_001_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = O) (Xn: (counter n).get i = I), (counter (S n)).get i = O) 98 | -- 0 1 0 ->0 1 1 99 | -- Ssn Sn n 100 | (axiom_010_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = O) 101 | (axiom_010_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = I) 102 | (axiom_010_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get i = I) 103 | -- 0 1 1 -> 1 0 0 104 | (axiom_011_SSn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S (S i)) = I) 105 | (axiom_011_Sn: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get (S i) = O) 106 | (axiom_011_n: forall (n: N) (i: N) (Xssn: (counter n).get (S (S i)) = O) (Xsn: (counter n).get (S i) = I) (Xn: (counter n).get i = O), (counter (S n)).get i = O): 107 | (counter seven).get Z = I := by { 108 | rawEgg [axiom_000_SSn, axiom_000_Sn, axiom_000_n 109 | , axiom_001_SSn, axiom_001_Sn, axiom_001_n 110 | , axiom_010_SSn, axiom_010_Sn, axiom_010_n 111 | , axiom_011_SSn, axiom_011_Sn, axiom_011_n]; 112 | sorry 113 | 114 | } 115 | -/ 116 | 117 | -------------------------------------------------------------------------------- /json-egg/json-inputs/group-inv-mul-cancel-left.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 3, 3 | "rewrites": [ 4 | { 5 | "rhs": "(ap (ap mul y) (ap inv y))", 6 | "lhs": "one", 7 | "name": "42" 8 | }, 9 | { 10 | "rhs": "(ap (ap mul x) (ap inv x))", 11 | "lhs": "one", 12 | "name": "41" 13 | }, 14 | { 15 | "rhs": "(ap (ap mul one) (ap inv one))", 16 | "lhs": "one", 17 | "name": "40" 18 | }, 19 | { 20 | "rhs": "?_uniq.460", 21 | "lhs": "(ap (ap mul one) ?_uniq.460)", 22 | "name": "39" 23 | }, 24 | { 25 | "rhs": "y", 26 | "lhs": "(ap (ap mul one) y)", 27 | "name": "38" 28 | }, 29 | { 30 | "rhs": "x", 31 | "lhs": "(ap (ap mul one) x)", 32 | "name": "37" 33 | }, 34 | { 35 | "rhs": "one", 36 | "lhs": "(ap (ap mul one) one)", 37 | "name": "36" 38 | }, 39 | { 40 | "rhs": "(ap (ap mul ?_uniq.459) one)", 41 | "lhs": "?_uniq.459", 42 | "name": "35" 43 | }, 44 | { 45 | "rhs": "(ap (ap mul y) one)", 46 | "lhs": "y", 47 | "name": "34" 48 | }, 49 | { 50 | "rhs": "(ap (ap mul x) one)", 51 | "lhs": "x", 52 | "name": "33" 53 | }, 54 | { 55 | "rhs": "(ap (ap mul one) one)", 56 | "lhs": "one", 57 | "name": "32" 58 | }, 59 | { 60 | "rhs": "one", 61 | "lhs": "(ap (ap mul (ap inv ?_uniq.458)) ?_uniq.458)", 62 | "name": "31" 63 | }, 64 | { 65 | "rhs": "one", 66 | "lhs": "(ap (ap mul (ap inv y)) y)", 67 | "name": "30" 68 | }, 69 | { 70 | "rhs": "one", 71 | "lhs": "(ap (ap mul (ap inv x)) x)", 72 | "name": "29" 73 | }, 74 | { 75 | "rhs": "one", 76 | "lhs": "(ap (ap mul (ap inv one)) one)", 77 | "name": "28" 78 | }, 79 | { 80 | "rhs": "(ap (ap mul (ap (ap mul ?_uniq.455) ?_uniq.456)) ?_uniq.457)", 81 | "lhs": "(ap (ap mul ?_uniq.455) (ap (ap mul ?_uniq.456) ?_uniq.457))", 82 | "name": "27" 83 | }, 84 | { 85 | "rhs": "(ap (ap mul (ap (ap mul y) y)) y)", 86 | "lhs": "(ap (ap mul y) (ap (ap mul y) y))", 87 | "name": "26" 88 | }, 89 | { 90 | "rhs": "(ap (ap mul (ap (ap mul y) y)) x)", 91 | "lhs": "(ap (ap mul y) (ap (ap mul y) x))", 92 | "name": "25" 93 | }, 94 | { 95 | "rhs": "(ap (ap mul (ap (ap mul y) y)) one)", 96 | "lhs": "(ap (ap mul y) (ap (ap mul y) one))", 97 | "name": "24" 98 | }, 99 | { 100 | "rhs": "(ap (ap mul (ap (ap mul y) x)) y)", 101 | "lhs": "(ap (ap mul y) (ap (ap mul x) y))", 102 | "name": "23" 103 | }, 104 | { 105 | "rhs": "(ap (ap mul (ap (ap mul y) x)) x)", 106 | "lhs": "(ap (ap mul y) (ap (ap mul x) x))", 107 | "name": "22" 108 | }, 109 | { 110 | "rhs": "(ap (ap mul (ap (ap mul y) x)) one)", 111 | "lhs": "(ap (ap mul y) (ap (ap mul x) one))", 112 | "name": "21" 113 | }, 114 | { 115 | "rhs": "(ap (ap mul (ap (ap mul y) one)) y)", 116 | "lhs": "(ap (ap mul y) (ap (ap mul one) y))", 117 | "name": "20" 118 | }, 119 | { 120 | "rhs": "(ap (ap mul (ap (ap mul y) one)) x)", 121 | "lhs": "(ap (ap mul y) (ap (ap mul one) x))", 122 | "name": "19" 123 | }, 124 | { 125 | "rhs": "(ap (ap mul (ap (ap mul y) one)) one)", 126 | "lhs": "(ap (ap mul y) (ap (ap mul one) one))", 127 | "name": "18" 128 | }, 129 | { 130 | "rhs": "(ap (ap mul (ap (ap mul x) y)) y)", 131 | "lhs": "(ap (ap mul x) (ap (ap mul y) y))", 132 | "name": "17" 133 | }, 134 | { 135 | "rhs": "(ap (ap mul (ap (ap mul x) y)) x)", 136 | "lhs": "(ap (ap mul x) (ap (ap mul y) x))", 137 | "name": "16" 138 | }, 139 | { 140 | "rhs": "(ap (ap mul (ap (ap mul x) y)) one)", 141 | "lhs": "(ap (ap mul x) (ap (ap mul y) one))", 142 | "name": "15" 143 | }, 144 | { 145 | "rhs": "(ap (ap mul (ap (ap mul x) x)) y)", 146 | "lhs": "(ap (ap mul x) (ap (ap mul x) y))", 147 | "name": "14" 148 | }, 149 | { 150 | "rhs": "(ap (ap mul (ap (ap mul x) x)) x)", 151 | "lhs": "(ap (ap mul x) (ap (ap mul x) x))", 152 | "name": "13" 153 | }, 154 | { 155 | "rhs": "(ap (ap mul (ap (ap mul x) x)) one)", 156 | "lhs": "(ap (ap mul x) (ap (ap mul x) one))", 157 | "name": "12" 158 | }, 159 | { 160 | "rhs": "(ap (ap mul (ap (ap mul x) one)) y)", 161 | "lhs": "(ap (ap mul x) (ap (ap mul one) y))", 162 | "name": "11" 163 | }, 164 | { 165 | "rhs": "(ap (ap mul (ap (ap mul x) one)) x)", 166 | "lhs": "(ap (ap mul x) (ap (ap mul one) x))", 167 | "name": "10" 168 | }, 169 | { 170 | "rhs": "(ap (ap mul (ap (ap mul x) one)) one)", 171 | "lhs": "(ap (ap mul x) (ap (ap mul one) one))", 172 | "name": "9" 173 | }, 174 | { 175 | "rhs": "(ap (ap mul (ap (ap mul one) y)) y)", 176 | "lhs": "(ap (ap mul one) (ap (ap mul y) y))", 177 | "name": "8" 178 | }, 179 | { 180 | "rhs": "(ap (ap mul (ap (ap mul one) y)) x)", 181 | "lhs": "(ap (ap mul one) (ap (ap mul y) x))", 182 | "name": "7" 183 | }, 184 | { 185 | "rhs": "(ap (ap mul (ap (ap mul one) y)) one)", 186 | "lhs": "(ap (ap mul one) (ap (ap mul y) one))", 187 | "name": "6" 188 | }, 189 | { 190 | "rhs": "(ap (ap mul (ap (ap mul one) x)) y)", 191 | "lhs": "(ap (ap mul one) (ap (ap mul x) y))", 192 | "name": "5" 193 | }, 194 | { 195 | "rhs": "(ap (ap mul (ap (ap mul one) x)) x)", 196 | "lhs": "(ap (ap mul one) (ap (ap mul x) x))", 197 | "name": "4" 198 | }, 199 | { 200 | "rhs": "(ap (ap mul (ap (ap mul one) x)) one)", 201 | "lhs": "(ap (ap mul one) (ap (ap mul x) one))", 202 | "name": "3" 203 | }, 204 | { 205 | "rhs": "(ap (ap mul (ap (ap mul one) one)) y)", 206 | "lhs": "(ap (ap mul one) (ap (ap mul one) y))", 207 | "name": "2" 208 | }, 209 | { 210 | "rhs": "(ap (ap mul (ap (ap mul one) one)) x)", 211 | "lhs": "(ap (ap mul one) (ap (ap mul one) x))", 212 | "name": "1" 213 | }, 214 | { 215 | "rhs": "(ap (ap mul (ap (ap mul one) one)) one)", 216 | "lhs": "(ap (ap mul one) (ap (ap mul one) one))", 217 | "name": "0" 218 | } 219 | ], 220 | "target-rhs": "y", 221 | "target-lhs": "(ap (ap mul x) (ap (ap mul (ap inv x)) y))", 222 | "request": "perform-rewrite" 223 | } 224 | -------------------------------------------------------------------------------- /ffi-egg/src/math.rs: -------------------------------------------------------------------------------- 1 | use egg::*; 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | 5 | use num_bigint::BigInt; 6 | use num_integer::Integer; 7 | use num_rational::Ratio; 8 | use num_traits::{One, Pow, Signed, Zero}; 9 | 10 | pub type Constant = num_rational::BigRational; 11 | pub type RecExpr = egg::RecExpr; 12 | pub type Pattern = egg::Pattern; 13 | pub type EGraph = egg::EGraph; 14 | pub type Rewrite = egg::Rewrite; 15 | pub type Runner = egg::Runner; 16 | pub type Iteration = egg::Iteration; 17 | 18 | pub struct IterData { 19 | pub extracted: Vec<(Id, Extracted)>, 20 | } 21 | 22 | pub struct Extracted { 23 | pub best: RecExpr, 24 | pub cost: usize, 25 | } 26 | 27 | // cost function similar to AstSize except it will 28 | // penalize `(pow _ p)` where p is a fraction 29 | pub struct AltCost<'a> { 30 | pub egraph: &'a EGraph, 31 | } 32 | 33 | impl<'a> AltCost<'a> { 34 | pub fn new(egraph: &'a EGraph) -> Self { 35 | Self { egraph } 36 | } 37 | } 38 | 39 | impl<'a> CostFunction for AltCost<'a> { 40 | type Cost = usize; 41 | 42 | fn cost(&mut self, enode: &SymbolLang, mut costs: C) -> Self::Cost 43 | where 44 | C: FnMut(Id) -> Self::Cost, 45 | { 46 | if let SymbolLang::Pow([_, _, i]) = enode { 47 | if let Some(n) = &self.egraph[*i].data { 48 | if !n.denom().is_one() && n.denom().is_odd() { 49 | return usize::MAX; 50 | } 51 | } 52 | } 53 | 54 | enode.fold(1, |sum, id| usize::saturating_add(sum, costs(id))) 55 | } 56 | } 57 | 58 | impl IterationData for IterData { 59 | fn make(runner: &Runner) -> Self { 60 | let mut extractor = Extractor::new(&runner.egraph, AltCost::new(&runner.egraph)); 61 | let extracted = runner 62 | .roots 63 | .iter() 64 | .map(|&root| { 65 | let (cost, best) = extractor.find_best(root); 66 | let ext = Extracted { cost, best }; 67 | (root, ext) 68 | }) 69 | .collect(); 70 | Self { extracted } 71 | } 72 | } 73 | 74 | // operators from FPCore 75 | define_language! { 76 | pub enum SymbolLang { 77 | 78 | // constant-folding operators 79 | 80 | "+" = Add([Id; 3]), 81 | "-" = Sub([Id; 3]), 82 | "*" = Mul([Id; 3]), 83 | "/" = Div([Id; 3]), 84 | "pow" = Pow([Id; 3]), 85 | "neg" = Neg([Id; 2]), 86 | "sqrt" = Sqrt([Id; 2]), 87 | "fabs" = Fabs([Id; 2]), 88 | "ceil" = Ceil([Id; 2]), 89 | "floor" = Floor([Id; 2]), 90 | "round" = Round([Id; 2]), 91 | "log" = Log([Id; 2]), 92 | "cbrt" = Cbrt([Id; 2]), 93 | 94 | Constant(Constant), 95 | Symbol(egg::Symbol), 96 | Other(egg::Symbol, Vec), 97 | } 98 | } 99 | 100 | pub struct ConstantFold { 101 | pub unsound: AtomicBool, 102 | pub constant_fold: bool, 103 | pub prune: bool, 104 | } 105 | 106 | impl Default for ConstantFold { 107 | fn default() -> Self { 108 | Self { 109 | constant_fold: true, 110 | prune: true, 111 | unsound: AtomicBool::new(false), 112 | } 113 | } 114 | } 115 | 116 | impl Analysis for ConstantFold { 117 | type Data = Option; 118 | fn make(egraph: &EGraph, enode: &SymbolLang) -> Self::Data { 119 | if !egraph.analysis.constant_fold { 120 | return None; 121 | } 122 | 123 | let x = |id: &Id| egraph[*id].data.as_ref(); 124 | let is_zero = |id: &Id| { 125 | let data = egraph[*id].data.as_ref(); 126 | match data { 127 | Some(data) => data.is_zero(), 128 | None => false, 129 | } 130 | }; 131 | 132 | match enode { 133 | SymbolLang::Constant(c) => Some(c.clone()), 134 | 135 | // real 136 | SymbolLang::Add([_p, a, b]) => Some(x(a)? + x(b)?), 137 | SymbolLang::Sub([_p, a, b]) => Some(x(a)? - x(b)?), 138 | SymbolLang::Mul([_p, a, b]) => Some(x(a)? * x(b)?), 139 | SymbolLang::Div([_p, a, b]) => { 140 | if x(b)?.is_zero() { 141 | None 142 | } else { 143 | Some(x(a)? / x(b)?) 144 | } 145 | } 146 | SymbolLang::Neg([_p, a]) => Some(-x(a)?.clone()), 147 | SymbolLang::Pow([_p, a, b]) => { 148 | if is_zero(a) { 149 | if x(b)?.is_positive() { 150 | Some(Ratio::new(BigInt::from(0), BigInt::from(1))) 151 | } else { 152 | None 153 | } 154 | } else if is_zero(b) { 155 | Some(Ratio::new(BigInt::from(1), BigInt::from(1))) 156 | } else if x(b)?.is_integer() { 157 | Some(Pow::pow(x(a)?, x(b)?.to_integer())) 158 | } else { 159 | None 160 | } 161 | } 162 | SymbolLang::Sqrt([_p, a]) => { 163 | let a = x(a)?; 164 | if *a.numer() > BigInt::from(0) && *a.denom() > BigInt::from(0) { 165 | let s1 = a.numer().sqrt(); 166 | let s2 = a.denom().sqrt(); 167 | let is_perfect = &(&s1 * &s1) == a.numer() && &(&s2 * &s2) == a.denom(); 168 | if is_perfect { 169 | Some(Ratio::new(s1, s2)) 170 | } else { 171 | None 172 | } 173 | } else { 174 | None 175 | } 176 | } 177 | SymbolLang::Log([_p, a]) => { 178 | if x(a)? == &Ratio::new(BigInt::from(1), BigInt::from(1)) { 179 | Some(Ratio::new(BigInt::from(0), BigInt::from(1))) 180 | } else { 181 | None 182 | } 183 | } 184 | SymbolLang::Cbrt([_p, a]) => { 185 | if x(a)? == &Ratio::new(BigInt::from(1), BigInt::from(1)) { 186 | Some(Ratio::new(BigInt::from(1), BigInt::from(1))) 187 | } else { 188 | None 189 | } 190 | } 191 | SymbolLang::Fabs([_p, a]) => Some(x(a)?.clone().abs()), 192 | SymbolLang::Floor([_p, a]) => Some(x(a)?.floor()), 193 | SymbolLang::Ceil([_p, a]) => Some(x(a)?.ceil()), 194 | SymbolLang::Round([_p, a]) => Some(x(a)?.round()), 195 | 196 | _ => None, 197 | } 198 | } 199 | 200 | fn merge(&self, to: &mut Self::Data, from: Self::Data) -> bool { 201 | match (&to, from) { 202 | (None, None) => false, 203 | (Some(_), None) => false, // no update needed 204 | (None, Some(c)) => { 205 | *to = Some(c); 206 | true 207 | } 208 | (Some(a), Some(ref b)) => { 209 | if a != b && !self.unsound.swap(true, Ordering::SeqCst) { 210 | log::warn!("Bad merge detected: {} != {}", a, b); 211 | } 212 | false 213 | } 214 | } 215 | } 216 | 217 | fn modify(egraph: &mut EGraph, id: Id) { 218 | if let Some(constant) = egraph[id].data.clone() { 219 | let added = egraph.add(SymbolLang::Constant(constant)); 220 | let (id, _) = egraph.union(id, added); 221 | if egraph.analysis.prune { 222 | egraph[id].nodes.retain(|n| n.is_leaf()) 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /EggTactic/Egraph.lean: -------------------------------------------------------------------------------- 1 | import Std.Data.HashMap 2 | open Std HashMap 3 | 4 | namespace Egraph 5 | 6 | -- We key pointers with names instead of types so that 7 | -- we can have names of structures that does not create a cycle. 8 | -- When defining a structure, we are not allowed to use the 9 | -- type of the structure in the body of the structure. 10 | -- We dodge this restriction by converting to names. 11 | -- Convention: all pointer variables will have name starting 12 | -- with π. e.g. πx is a pointer to x. 13 | structure Ptr (_α : Name) where ix : UInt64 14 | deriving DecidableEq, Hashable,Inhabited 15 | 16 | -- typeclass that can dereference points of type 'o' named by 'α' 17 | class DerefM (m : Type → Type) (α : Name) (o : outParam Type) where 18 | deref : Ptr α -> m (Option o) 19 | set : Ptr α -> o -> m Unit 20 | malloc [Inhabited o] : m (Ptr α) -- allocate junk memory 21 | 22 | def DerefM.new [Monad m] [DerefM m α o] [Inhabited o] (v : o) : m (Ptr α) := do 23 | let ptr ← malloc 24 | set ptr v 25 | return ptr 26 | 27 | -- dereference pointer and run action. action must succeed. 28 | def Ptr.modifyM! [Monad m] [DerefM m α o] [Inhabited o] 29 | (f : o → m o) (p : Ptr α) : m Unit := do 30 | let v ← Option.get! <$> (DerefM.deref p) 31 | DerefM.set p (← f v) 32 | 33 | def Ptr.modify! [Monad m] [DerefM m α o] [Inhabited o] 34 | (f : o → o) (p : Ptr α) : m Unit := 35 | p.modifyM! (fun o => pure (f o)) 36 | 37 | def Ptr.deref [DerefM m α o] (p : Ptr α) : m (Option o) := DerefM.deref p 38 | def Ptr.deref! [Inhabited o] [Monad m] [DerefM m α o] (p : Ptr α) : m o:= 39 | Option.get! <$> DerefM.deref p 40 | 41 | abbrev Memory (α : Type) := HashMap UInt64 α 42 | notation "*?" x => DerefM.deref x -- dereference a pointer 43 | set_option quotPrecheck false in 44 | notation "*" x => (← Ptr.deref! x) -- dereference a pointer 45 | notation p " ^= " v => DerefM.set p v -- set a pointer to a value 46 | -- notation "^" x => Ptr `x 47 | 48 | -- terms. 49 | structure HashConsTerm (σ : Type) where 50 | head : σ 51 | args : Array (Ptr `Egraph.Eclass) 52 | deriving BEq, DecidableEq, Hashable, Inhabited 53 | 54 | -- equivalence class of terms. 55 | structure Eclass (σ : Type) where 56 | members : Array (HashConsTerm σ) 57 | -- TODO: cannot use (Ptr Eclass) here :( 58 | -- lmao, just use names! 59 | parentTerms : Array (HashConsTerm σ × Ptr `Egraph.Eclass) 60 | πcanon : Ptr `Egraph.Eclass -- pointer to canonical eclass represenative 61 | subtreeSize : UInt64 -- union find subtree size 62 | deriving BEq, DecidableEq, Hashable, Inhabited 63 | 64 | -- create a singleton e class. 65 | def Eclass.singleton (πcanon : Ptr ``Eclass) (member : HashConsTerm σ) : Eclass σ where 66 | members := #[member] 67 | parentTerms := {} 68 | πcanon := πcanon 69 | subtreeSize := 0 70 | 71 | -- add a term and the e-class that owns the term as the parent 72 | -- of this e-class. 73 | -- we add the parent e-class as well as the term since this informatoin 74 | -- is useful when moving terms around during the update. 75 | def Eclass.addParent (cls : Eclass σ) 76 | (tm : HashConsTerm σ) 77 | (πtm_cls : Ptr ``Eclass) := 78 | { cls with parentTerms := cls.parentTerms.push (tm, πtm_cls) } 79 | 80 | -- the data of the Egraph state. 81 | structure State (σ : Type) where 82 | σinhabited : Inhabited σ 83 | σbeq : DecidableEq σ 84 | σhashable : Hashable σ 85 | eclasses : Memory (Eclass σ) 86 | hashconsterms : Memory (HashConsTerm σ) 87 | term2classp : HashMap (HashConsTerm σ) (Ptr ``Eclass) 88 | 89 | def State.new [Inhabited σ] [DecidableEq σ] [Hashable σ] : State σ where 90 | σinhabited := inferInstance 91 | σbeq := inferInstance 92 | σhashable := inferInstance 93 | eclasses := {} 94 | hashconsterms := {} 95 | term2classp := {} 96 | 97 | abbrev M σ α := StateM (State σ) α 98 | 99 | -- deref an e-class 100 | instance : DerefM (M σ) ``Eclass (Eclass σ) where 101 | deref ptr := do 102 | return (← get).eclasses.find? ptr.ix 103 | set ptr v := modify (fun s => 104 | { s with eclasses := s.eclasses.insert ptr.ix v : State σ }) 105 | malloc := do 106 | let ptr := Ptr.mk <| UInt64.ofNat (← get).eclasses.size 107 | return ptr 108 | 109 | instance : DerefM (M σ) ``HashConsTerm (HashConsTerm σ) where 110 | deref ptr := do 111 | return (← get).hashconsterms.find? ptr.ix 112 | set ptr v := modify (fun s => 113 | { s with hashconsterms := s.hashconsterms.insert ptr.ix v : State σ }) 114 | malloc := do 115 | let ptr := Ptr.mk <| UInt64.ofNat (← get).eclasses.size 116 | return ptr 117 | 118 | class Canonicalize (σ : Type) (α : Type) where 119 | canonicalize : α → M σ α 120 | 121 | -- return canonical pointer 122 | notation "⟦" x "⟧" => Canonicalize.canonicalize x 123 | 124 | partial def canonicalizeClass (πcls : Ptr ``Eclass) : M σ (Ptr ``Eclass) := do 125 | let cls : (Eclass σ) ← πcls.deref! 126 | if cls.πcanon == πcls 127 | then return πcls 128 | else do 129 | let πcanon ← Egraph.canonicalizeClass πcls 130 | πcls ^= { cls with πcanon := πcanon } 131 | return πcanon 132 | 133 | instance : Canonicalize σ (Ptr ``Eclass) where 134 | canonicalize := canonicalizeClass 135 | 136 | partial def canonicalizeHashConsTerm (htm : HashConsTerm σ) : 137 | M σ (HashConsTerm σ) := do 138 | let _ : Inhabited σ := (← get).σinhabited 139 | let htm := { htm with args := (← htm.args.mapM canonicalizeClass) } 140 | return htm 141 | 142 | instance : Canonicalize σ (HashConsTerm σ) where 143 | canonicalize := canonicalizeHashConsTerm 144 | 145 | partial def HashConsTerm.find! (htm : HashConsTerm σ) : 146 | M σ (Ptr ``Eclass) := do 147 | match (← get).term2classp.find? (← ⟦htm⟧) with 148 | | .some cls => return cls 149 | | .none => panic! "unable to find e-class" 150 | 151 | partial def HashConsTerm.findOrAdd (htm : HashConsTerm σ) : 152 | M σ (Ptr ``Eclass) := do 153 | let htm ← ⟦htm⟧ 154 | let πhtm_cls ← 155 | match (← get).term2classp.find? htm with 156 | | .some x => pure x 157 | | .none => do 158 | let πhtm_cls ← DerefM.malloc (α := ``Eclass) 159 | modify (fun state => { state with term2classp := state.term2classp.insert htm πhtm_cls }) 160 | πhtm_cls ^= Eclass.singleton (πcanon := πhtm_cls) (member := htm) 161 | return πhtm_cls 162 | -- for each argument, update the parent e-elcass by adding a pointer to the 163 | -- e class of htm 164 | for πarg in htm.args do 165 | πarg.modify! (fun cls => cls.addParent htm πhtm_cls) 166 | return πhtm_cls 167 | 168 | mutual 169 | 170 | -- rebuild E-graph 171 | partial def Egraph.unite (πc πd : Ptr ``Eclass) : M σ (Ptr ``Eclass) := do 172 | let πc ← ⟦ πc ⟧ 173 | let πd ← ⟦ πd ⟧ 174 | if πc == πd then return πc 175 | -- attach root of lighter thing to root of heavier thing, so that there is 176 | -- a chance that depth does not go up. 177 | let (πparent, πchild) := 178 | if (← πc.deref!).subtreeSize >= (← πd.deref!).subtreeSize 179 | then (πc, πd) 180 | else (πd, πc) 181 | 182 | πchild.modify! (fun c => { c with πcanon := πparent }) 183 | πparent.modifyM! (fun c => do return { 184 | c with subtreeSize := c.subtreeSize + (← πchild.deref!).subtreeSize 185 | }) 186 | πparent.modifyM! (fun c => do return { 187 | c with 188 | members := c.members ++ (← πchild.deref!).members, 189 | parentTerms := c.parentTerms ++ (← πchild.deref!).parentTerms 190 | }) 191 | πchild.modify! (fun c => { c with members := #[], parentTerms := #[] }) 192 | Egraph.rebuild πparent 193 | return πparent 194 | 195 | -- rebuild E-class. 196 | partial def Egraph.rebuild (πc : Ptr ``Eclass) : M σ Unit := do 197 | let mut parents := #[] 198 | for (htm, πhtm_old_cls) in (← πc.deref!).parentTerms do 199 | let πhtm_new_cls ← htm.findOrAdd 200 | let πhtm_new_cls ← Egraph.unite πhtm_old_cls πhtm_new_cls 201 | modify (fun s => { s with term2classp := (s.term2classp.erase htm)} ) 202 | let htm ← ⟦ htm ⟧ 203 | modify (fun s => { s with term2classp := s.term2classp.insert htm πhtm_new_cls } ) 204 | parents := parents.push (htm, πhtm_new_cls) 205 | πc.modify! (fun c => { c with parentTerms := parents}) 206 | end 207 | -------------------------------------------------------------------------------- /Evaluation/Reviewer1/gen_count.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | import re 6 | import subprocess 7 | import sys 8 | import tempfile 9 | import time 10 | 11 | 12 | def coq_B(f): 13 | print("Inductive B := I | O.", file=f) 14 | 15 | 16 | def lean_B(f): 17 | print("inductive B", file=f) 18 | print("| I | O", file=f) 19 | print("open B", file=f) 20 | 21 | 22 | def count_sig(N, f): 23 | args = ["B"] * N 24 | args.append("B") # return type 25 | arg_s = " -> ".join(args) 26 | print(f"(count: {arg_s})", file=f) 27 | 28 | 29 | def count_lemma(N, n, f): 30 | free_vars = [] 31 | for i in reversed(range(n + 1, N + 1)): 32 | free_vars.append(f"b{i}") 33 | if free_vars: 34 | params = "forall {},".format(" ".join(free_vars)) 35 | else: 36 | params = "" 37 | args_l = " ".join(free_vars) 38 | args_r = " ".join(free_vars) 39 | # we have n arguments left 40 | args_l += " O" + " I" * (n - 1) 41 | args_r += " I" + " O" * (n - 1) 42 | print(f"(count_{n} : {params}", file=f) 43 | print(f" count {args_l} = count {args_r})", file=f) 44 | 45 | 46 | def hint_rewrite(N, f): 47 | count_lemmas = [f"count_{n}" for n in range(1, N + 1)] 48 | print("#[local] Hint Rewrite {} : count.".format(" ".join(count_lemmas)), file=f) 49 | 50 | 51 | def theorem_statement(rewrites, N, f): 52 | # strip the leading "0b" from the binary representation 53 | bin_num = bin(rewrites)[2:] 54 | if len(bin_num) < N: 55 | bin_num = "0" * (N - len(bin_num)) + bin_num 56 | count_args = bin_num.replace("0", "O ").replace("1", "I ") 57 | print(" count {} = count {}".format(count_args, "O " * N), file=f, end="") 58 | 59 | 60 | def coq_theorem(rewrites, N, f): 61 | print(f"(* {rewrites} rewrites *)", file=f) 62 | print("Theorem count_upward :", file=f) 63 | theorem_statement(rewrites, N, f) 64 | print(".", file=f) 65 | 66 | 67 | def coq_proof(rewrites, N, proof, f): 68 | print("Proof.", file=f) 69 | print(f' idtac "rewrites = {rewrites}".', file=f) 70 | if proof == "congruence": 71 | for n in range(1, N + 1): 72 | print(f"pose proof count_{n}.", file=f) 73 | # This number is the number of quantified hypotheses that can be added 74 | # to the goal. The default is 1000, which we increase here. Without this 75 | # parameter congruence would simply fail at some earlier point, but we 76 | # want to measure its performance a bit further. 77 | print('time "congruence" congruence 2000.', file=f) 78 | elif proof == "autorewrite": 79 | print(' time "autorewrite" autorewrite with count. reflexivity.', file=f) 80 | print("Qed.", file=f) 81 | 82 | 83 | def lean_proof(N, f): 84 | print("begin", file=f) 85 | count_lemmas = ", ".join([f"count_{n}" for n in range(1, N + 1)]) 86 | print(f" simp [{count_lemmas}],", file=f) 87 | print("end", file=f) 88 | 89 | 90 | def num_digits(rewrites): 91 | """Compute the number of digits required to represent a number of 92 | rewrites.""" 93 | return int(math.floor(math.log(rewrites, 2))) + 1 94 | 95 | 96 | def coq_file(args, f): 97 | rewrites = args.rewrites 98 | N = num_digits(rewrites) 99 | coq_B(f) 100 | print("Context", file=f) 101 | print(f"(* N = {N} *)", file=f) 102 | count_sig(N, f) 103 | for n in range(1, N + 1): 104 | count_lemma(N, n, f) 105 | print(".", file=f) 106 | print(file=f) 107 | hint_rewrite(N, f) 108 | print(file=f) 109 | coq_theorem(rewrites, N, f) 110 | coq_proof(rewrites, N, args.proof, f) 111 | 112 | 113 | def lean_file(args, f): 114 | rewrites = args.rewrites 115 | N = num_digits(rewrites) 116 | lean_B(f) 117 | print(file=f) 118 | print("theorem count_upward", file=f) 119 | count_sig(N, f) 120 | for n in range(1, N + 1): 121 | count_lemma(N, n, f) 122 | print(f" -- rewrites = {rewrites}", file=f) 123 | print(" :", end="", file=f) 124 | theorem_statement(rewrites, N, f) 125 | print(":=", file=f) 126 | lean_proof(N, f) 127 | 128 | 129 | def run_coqc(f): 130 | out = subprocess.run(["coqc", f], capture_output=True) 131 | if out.returncode != 0: 132 | if re.search("Tactic failure", out.stderr.decode("utf-8")): 133 | return None 134 | else: 135 | # something else seems to have gone wrong 136 | print(out.stderr, file=sys.stderr) 137 | print("Coq failed", file=sys.stderr) 138 | sys.exit(1) 139 | return out.stdout.decode("utf-8") 140 | 141 | 142 | def time_coqc(args, time_complete=True): 143 | """Time running coqc on a file generated from args. 144 | 145 | If time_complete is True, then time the entire run rather than just the 146 | autorewrite/congruence invocation. This includes the kernel checking the 147 | resulting proof. 148 | """ 149 | with tempfile.TemporaryDirectory() as tempd: 150 | fname = path.join(tempd, "count.v") 151 | with open(fname, "w") as f: 152 | coq_file(args, f) 153 | start = time.time() 154 | output = run_coqc(fname) 155 | if output is None: 156 | return float("inf") 157 | elapsed = time.time() - start 158 | if not time_complete: 159 | # replace elapsed with the tactic timing result (from the Coq output) 160 | m = re.search(r"""ran for (?P