Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: fuse nested mkCongrArg calls (leanprover#3203)
Encouraged by the performance gains from making `rewrite` produce smaller proof objects (leanprover#3121) I am here looking for low-hanging fruit in `simp`. Consider this typical example: ``` set_option pp.explicit true theorem test (a : Nat) (b : Nat) (c : Nat) (heq : a = b) (h : (c.add (c.add ((c.add b).add c))).add c = c) : (c.add (c.add ((c.add a).add c))).add c = c ``` We get a rather nice proof term when using ``` := by rw [heq]; assumption ``` namely ``` theorem test : ∀ (a b c : Nat), @eq Nat a b → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c := fun a b c heq h => @Eq.mpr (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c) (@congrArg Nat Prop a b (fun _a => @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c _a) c))) c) c) heq) h ``` (this is with leanprover#3121). But with `by simp only [heq]; assumption`, it looks rather different: ``` theorem test : ∀ (a b c : Nat), @eq Nat a b → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c := fun a b c heq h => @Eq.mpr (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c) (@id (@eq Prop (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c)) (@congrFun Nat (fun a => Prop) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c)) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c)) (@congrArg Nat (Nat → Prop) (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) (@eq Nat) (@congrFun Nat (fun a => Nat) (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c)))) (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c)))) (@congrArg Nat (Nat → Nat) (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) Nat.add (@congrArg Nat Nat (Nat.add c (Nat.add (Nat.add c a) c)) (Nat.add c (Nat.add (Nat.add c b) c)) (Nat.add c) (@congrArg Nat Nat (Nat.add (Nat.add c a) c) (Nat.add (Nat.add c b) c) (Nat.add c) (@congrFun Nat (fun a => Nat) (Nat.add (Nat.add c a)) (Nat.add (Nat.add c b)) (@congrArg Nat (Nat → Nat) (Nat.add c a) (Nat.add c b) Nat.add (@congrArg Nat Nat a b (Nat.add c) heq)) c)))) c)) c)) h ``` Since simp uses only single-step `congrArg`/`congrFun` congruence lemmas here, the proof term grows very large, likely quadratic in this case. Can we do better? Every nesting of `congrArg` (and it's little brother `congrFun`) can be turned into a single `congrArg` call. In this PR I make making the smart app builders `Meta.mkCongrArg` and `Meta.mkCongrFun` a bit smarter and not only fuse with `Eq.refl`, but also with `congrArg`/`congrFun`. Now we get, in this simple example, ``` theorem test : ∀ (a b c : Nat), @eq Nat a b → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c → @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c := fun a b c heq h => @Eq.mpr (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c a) c))) c) c) (@eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c b) c))) c) c) (@congrArg Nat Prop a b (fun x => @eq Nat (Nat.add (Nat.add c (Nat.add c (Nat.add (Nat.add c x) c))) c) c) heq) h ``` Let’s see if it works and how much we gain.
- Loading branch information