Zulip Chat Archive

Stream: lean4

Topic: Priority for simp theorems


Tomas Skrivan (Nov 15 2021 at 20:29):

It seems that priority can be assigned to theorems marked with simp. Is this documented somewhere? I have an example of a theorems can easily get into an infinite loop unless used carefully. Setting priority solves the problem and I would like to understand how priority for simp works.

Consider this example which uses simp to invert a function. There is a general theorem inverse_of_comp_parm how to invert composition of function with an additional parameter and then there is a specific inverse of addition operation inverse_of_add_1

variable {α β γ δ} [Inhabited α] [Inhabited β]

constant inverse : (α  β)  (β  α)

@[simp 10000]
def inverse_of_comp_parm (f : β  δ  γ) (g : α  β) (d : δ)
      : inverse (λ a => f (g a) d) = (inverse g)  (inverse (λ b => f b d)) := sorry

variable [Add α] [Sub α]

@[simp]
def inverse_of_add_1 (y : α)
    : inverse (λ x => x + y) = (λ x => x - y) := sorry


example (a b : α) : inverse (λ x => (x + a) + b) = (λ x => (x - b) - a) :=
by
  simp

When the theorem inverse_of_comp_parm is just marked with @[simp] then simp in the example gets into an infinite loop.

Tomas Skrivan (Nov 15 2021 at 20:34):

In ideal world, I do not want to set the priority but I want simp to be smart enough to realize that constantly applying inverse_of_comp_parm is not getting anywhere and stop the infinite loop.

Tomas Skrivan (Nov 15 2021 at 21:01):

I find the behavior of simp bit odd. Consider this example, a slight modification of the above that gets into an infinite loop:

variable {α β γ δ} [Inhabited α] [Inhabited β]

constant inverse : (α  β)  (β  α)

@[simp 100000]
def inverse_of_comp_parm (f : β  δ  γ) (g : α  β) (d : δ)
      : inverse (λ a => f (g a) d) = (inverse g)  (inverse (λ b => f b d)) := sorry

@[simp]
def inverse_of_id
    : inverse (λ x : α => x) = (λ x => x) := sorry

@[simp]
def comp_id (f : α  β)
    : f  (λ x => x) = f := sorry

variable [Add α] [Sub α]

set_option trace.Meta.Tactic.simp true

example (a b : α) : inverse (λ x => (x + a) + b) = (λ x => (x - b) - a) :=
by
  simp
  -- rw [inverse_of_comp_parm]
  -- rw [inverse_of_comp_parm]
  -- rw [inverse_of_id]
  -- rw [comp_id]
  -- rw [inverse_of_comp_parm]
  -- rw [inverse_of_id]
  -- rw [comp_id]

Looking at the trace log it looks like that simp goes through these rewrites:

1. inverse_of_comp_parm
2. inverse_of_comp_parm
3. inverse_of_id
4. inverse_of_comp_parm
5. inverse_of_comp_parm
6. ....

However I would expect these rewrites to be:

1. inverse_of_comp_parm
2. inverse_of_comp_parm
3. inverse_of_id
4. comp_id
5. inverse_of_comp_parm
6. inverse_of_id
7. ...

With the expected order or rewrites the goal after the step 1. and 4. are identical and a loop can be detected.

Gabriel Ebner (Nov 15 2021 at 21:09):

This comes up every once in a while: https://leanprover.zulipchat.com/#narrow/search/simplifier.20priority

Gabriel Ebner (Nov 15 2021 at 21:11):

Priorities don't work the way you think they do. The priority of a simp lemma only affects the choice of simp lemmas for the same subterm. If you have a term f (g a) d and simp lemmas g a = ... and g x = ..., then you can use priorities to specify which one of them to use.

Gabriel Ebner (Nov 15 2021 at 21:12):

But if you have a lemma f (g a) d = ... then this will always be applied after the lemmas for g a because simp first processes the subterms, no matter the priorities.

Tomas Skrivan (Nov 15 2021 at 21:20):

Thanks! Now the behavior makes sense. It looks like that I have to ditch the inverse_of_comp_parm and rewrite inverse_of_add_1 to the form

@[simp]
def inverse_of_add_1 (y : β) (f : α  β)
    : inverse (λ x => (f x) + y) = (inverse f)  (λ x => x - y) := sorry

Last updated: Dec 20 2023 at 11:08 UTC