Zulip Chat Archive

Stream: mathlib4

Topic: New `solve_by_elim`


Heather Macbeth (Jan 09 2023 at 17:54):

@Scott Morrison I was experimenting with the refactored solve_by_elim, particularly with the new functionality to throw in symms of hypotheses. I expected the following to work; can you tell me if I am doing something wrong?

import Mathlib.Data.Int.ModEq

example {a : } (ha : a  2 [ZMOD 4]) : 2  a [ZMOD 4] := by
  apply_rules only [ha] -- doesn't resolve the goal

The goal state after this is

a : ℤ
ha : a ≡ 2 [ZMOD 4]
ha_symm : 2 ≡ a [ZMOD 4]
⊢ 2 ≡ a [ZMOD 4]

so at least it correctly formed the symm of ha.

Heather Macbeth (Jan 09 2023 at 18:01):

I'm also curious why this one doesn't work:

import Mathlib.Data.Int.ModEq

example {a b : } (ha : a  2 [ZMOD 4]) :
    a * b ^ 2 + a ^ 2 * b  2 * b ^ 2 + 2 ^ 2 * b [ZMOD 4] := by
  apply_rules only [ha, Int.ModEq.add, Int.ModEq.mul,
    Int.ModEq.pow, Int.ModEq.rfl] -- doesn't resolve the goal

After it runs there is one goal left which ought to be doable by Int.ModEq.rfl:

a b : ℤ
ha : a ≡ 2 [ZMOD 4]
ha_symm : 2 ≡ a [ZMOD 4]
⊢ b ≡ b [ZMOD 4]

and it's strange because presumably it already resolved another b ≡ b [ZMOD 4] goal.

Scott Morrison (Jan 09 2023 at 20:35):

The first one is presumably a bug/feature that only doesn't pick up the symm version of that named lemma. I'll have to think about how to allow that.

Heather Macbeth (Jan 09 2023 at 20:42):

If it would be easier, we could have the syntax apply_rules only [←ha]

Scott Morrison (Jan 15 2023 at 21:26):

apply_rules only [←ha] seems a bit superfluous given apply_rules only [ha.symm] works.

Scott Morrison (Jan 15 2023 at 21:27):

I can change the behaviour of only so it automatically includes all the symmetric versions, but then I worry someone will want only only.

Scott Morrison (Jan 15 2023 at 21:34):

Regarding the second example,

example {a b : } (ha : a  2 [ZMOD 4]) :
    a * b ^ 2 + a ^ 2 * b  2 * b ^ 2 + 2 ^ 2 * b [ZMOD 4] := by
  apply_rules (config := {maxDepth := 13}) only [ha, Int.ModEq.add, Int.ModEq.mul,
    Int.ModEq.pow, Int.ModEq.rfl]

suffices.

Scott Morrison (Jan 15 2023 at 21:34):

We should probably increase the default maxDepth for apply_rules.

Scott Morrison (Jan 15 2023 at 21:34):

Reordering the lemmas as

example {a b : } (ha : a  2 [ZMOD 4]) :
    a * b ^ 2 + a ^ 2 * b  2 * b ^ 2 + 2 ^ 2 * b [ZMOD 4] := by
  apply_rules only [ha, Int.ModEq.rfl, Int.ModEq.add, Int.ModEq.mul,
    Int.ModEq.pow]

also works.

Scott Morrison (Jan 15 2023 at 21:35):

i.e. so it tries rfl on things before decomposing products and powers that match to begin with. I'm not sure if there is a sensible way to make these tactics try to find better orderings themselves. That's probably out of scope for now.

Heather Macbeth (Jan 15 2023 at 21:50):

Is there a reason to have a maxDepth for apply_rules at all? It seems like with a sensible rule set (no symms) there's no way it would loop.

Scott Morrison (Jan 15 2023 at 22:52):

Actually, given that we already have the config := { symm := false } option, maybe apply_rules only [ha] could reasonably use ha.symm as well, and you have to write apply_rules (config := {symm := false}) only [ha] for the only only behaviour!

Scott Morrison (Jan 15 2023 at 22:54):

It would be nice if we could have different defaults: default to symm := true normally, but default to symm := false when called with only. Then users could call apply_rules (config := { symm := true }) only [ha] if they wanted the "weird" behaviour of only + symm.

Scott Morrison (Jan 15 2023 at 23:11):

Heather Macbeth said:

Is there a reason to have a maxDepth for apply_rules at all? It seems like with a sensible rule set (no symms) there's no way it would loop.

Well, at some point we need a maxDepth, if only for implementation reasons. mathlib#1580 changes the default to 50. @Heather Macbeth, feel free to merge as is or change the default to anything else that suits you.

Heather Macbeth (Jan 15 2023 at 23:43):

mathlib4#1580

Heather Macbeth (Jan 16 2023 at 00:02):

From the solve_by_elim source:

(In mathlib3 we could also pass attributes, and all declarations with that attribute were included. This has not been implemented here.)

@Scott Morrison Would it be difficult to implement that functionality? It was very convenient in mathlib3.

To #xy a bit, a typical use case for me would be to implement a tactic

mod_rw [h1, h2]

which is a wrapper for

apply_rules only [h1, h2] using mod_rules

where mod_rules is an attribute which I have tagged Int.ModEq.add, Int.ModEq.mul, Int.ModEq.pow ... with. I'm not sure how to do this without the attribute functionality.

Scott Morrison (Jan 16 2023 at 00:11):

It's the usual problem, that there isn't a "standard" for looking up everything with an attribute. Presumably it would suffice if this worked with something created via register_simp_attr?

Scott Morrison (Jan 16 2023 at 00:12):

(Or alternatively some relative of this, like register_apply_attr that we make up? It would also potentially be useful for aesop.)

Heather Macbeth (Jan 16 2023 at 00:16):

I don't think the lemmas I will be tagging are technically suitable as simp-lemmas. But I'm happy to "abuse" the functionality in this way if it will work.

Heather Macbeth (Jan 16 2023 at 00:17):

Actually, can we just re-use the declare_aesop_rule_sets [mod_rules] functionality?

Heather Macbeth (Jan 16 2023 at 00:19):

Presumably that sets up an attribute which has most of the code needed already written.

Scott Morrison (Jan 16 2023 at 00:48):

aesop rule sets are much more complicated

Heather Macbeth (Jan 16 2023 at 01:31):

Do you have any ideas for a hack that could make my desired syntax

mod_rw [h1, h2]

work without the attribute functionality? Some kind of mini-tactic that

  • appends a hard-coded list of lemmas to [h1, h2]
  • runs apply_rules only on the resulting list

?

Scott Morrison (Jan 16 2023 at 01:57):

I'm writing register_tag_attr, for attributes which have no function except being able to look up everything so tagged.

Heather Macbeth (Jan 16 2023 at 02:18):

Scott Morrison said:

Reordering the lemmas also works.

By the way, when it comes to using register_tag_attr in apply_rules, it sounds like there will be a question of the relative priority of attribute-tagged lemmas vs lemmas listed individually by the user -- the tactic tries the lemmas in a fixed order, only trying a given lemma if all the previous lemmas failed.

In my own use cases it is better that the attribute-tagged lemmas be given lower priority than the individually-listed lemmas. In fact I am going to be bold and claim that this is better in most use cases.

Scott Morrison (Jan 16 2023 at 03:01):

Happily, this is what I implemented before reading this message. :-)

Scott Morrison (Jan 16 2023 at 03:01):

mathlib4#1581

Scott Morrison (Jan 16 2023 at 03:05):

@Heather Macbeth, there's a very simple test case

@[dummy_tag_attr] axiom foo : 1 = 2

example : 1 = 2 := by
  fail_if_success solve_by_elim
  solve_by_elim using dummy_tag_attr

but I haven't tested it any beyond that. Please let me know how it goes. :-)

Heather Macbeth (Jan 16 2023 at 08:28):

As far as I can tell, the attribute setup is working correctly. Thank you @Scott Morrison!

I did discover a separate weird behaviour, which I suspect might be because the mathlib3 option for a transparency setting is not implemented yet.

import Mathlib.Init.Data.Int.Basic
import Mathlib.Tactic.SolveByElim

def Int.ModEq (n a b : ) := a % n = b % n

notation:50 a " ≡ " b " [ZMOD " n "]" => Int.ModEq n a b

theorem Int.ModEq.add (h₁ : a  b [ZMOD n]) (h₂ : c  d [ZMOD n]) :
    a + c  b + d [ZMOD n] :=
  sorry

example {a b : } : a + b  a + a [ZMOD 4] := by
  apply_rules [Int.ModEq.add]

Heather Macbeth (Jan 16 2023 at 08:28):

In mathlib3, apply_rules does what I want here, which is to leave the following goal state:

2 goals
a b : ℤ
⊢ a ≡ a [ZMOD 4]

a b : ℤ
⊢ b ≡ a [ZMOD 4]

In the mathlib4 implementation the goal state after the apply_rules is

a b : 
 HAdd.hAdd = fun {a b} => a + a

However, if I mark def Int.ModEq as @[irreducible], then I get back the mathlib3 behaviour. So I'm guessing that if apply_rules { md := reducible } were implemented then the mathlib4 version would do what I want.

Heather Macbeth (Jan 16 2023 at 08:30):

(In the mathlib3 apply_rules I always set { md := reducible }.)

Heather Macbeth (Jan 16 2023 at 08:40):

Hmm, actually it can't just be transparency level, because the discrepancy in behaviour persists even without any definitions or notations. Compare mathlib4

import Mathlib.Init.Data.Int.Basic
import Mathlib.Tactic.SolveByElim

variable {a b c d n : }

theorem Int.ModEq.add (h₁ : a % n = b % n) (h₂ : c % n = d % n) :
    (a + c) % n = (b + d) % n :=
  sorry

example : (a + b) % 4 = (a + a) % 4 := by
  apply_rules [Int.ModEq.add]

with mathlib3

import tactic.interactive

variables {a b c d n : }

theorem Int.ModEq.add (h₁ : a % n = b % n) (h₂ : c % n = d % n) :
    (a + c) % n = (b + d) % n :=
  sorry

example {a b : } : (a + b) % 4 = (a + a) % 4 :=
begin
  apply_rules [Int.ModEq.add],

end

Heather Macbeth (Jan 16 2023 at 22:52):

Note that in this example apply_rules [Int.ModEq.add] actually produces a different goal list than apply Int.ModEq.add.

Heather Macbeth (Jan 16 2023 at 23:50):

@Scott Morrison Aha! I think the change in behaviour is that apply_rules now has the following feature, from being implemented on top of solve_by_elim: per the source,

mkAssumptionSet builds a collection of lemmas for use in the backtracking search in solve_by_elim.

  • By default, it includes all local hypotheses, along with rfl, trivial, congrFun and congrArg.

Heather Macbeth (Jan 16 2023 at 23:50):

Indeed, the term proof produced by the version which makes the weird goals

example : (a + b) % 4 = (a + a) % 4 := by
  apply_rules [Int.ModEq.add]
  sorry

includes congrFun and congrArg:

congrFun (congrArg HMod.hMod (congrFun (congrFun (sorryAx (HAdd.hAdd = fun {a b} => a + a)) a) b)) 4

Whereas with the only flag (which your docs say will omit those default lemmas from the applyset), i.e. running

example : (a + b) % 4 = (a + a) % 4 := by
  apply_rules only [Int.ModEq.add]
  . sorry
  . sorry

I get the goals I expected, and the term proof indeed has no congrArg or congrFun:

Int.ModEq.add (sorryAx (a % 4 = a % 4)) (sorryAx (b % 4 = a % 4))

Heather Macbeth (Jan 16 2023 at 23:53):

What would you think about changing the order of the collection of lemmas built by mkAssumptionSet, so that the default lemmas rfl, trivial, congrFun and congrArg are tried last (after lemmas provided by the user, local hypotheses, and lemmas tagged with listed attributes)? It seems like that would prevent the behaviour I observed here.

Scott Morrison (Jan 17 2023 at 00:33):

This change will conflict slightly with mathlib4#1581 which hasn't been merged yet, so I propose making this change part of that PR.

Scott Morrison (Jan 17 2023 at 00:35):

Done.

Heather Macbeth (Jan 17 2023 at 00:35):

Thank you!

Heather Macbeth (Jan 17 2023 at 05:37):

Scott Morrison said:

Heather Macbeth, there's a very simple test case

@[dummy_tag_attr] axiom foo : 1 = 2

example : 1 = 2 := by
  fail_if_success solve_by_elim
  solve_by_elim using dummy_tag_attr

but I haven't tested it any beyond that. Please let me know how it goes. :-)

@Scott Morrison OK, testing the attribute functionality a little more. How would I use an attribute in a macro, like the last line of the apply_rules macro? I tried writing

liftMetaTactic fun g => solveByElim.processSyntax cfg true star add remove #[`mod_rules] [g]

but it complains that mod_rules is a Name, not an Ident.

Heather Macbeth (Jan 17 2023 at 05:37):

(I can provide the full macro if needed, but it's pretty close to the apply_rules macro except that I want to hard-code a choice of attribute.)

Thomas Murrills (Jan 20 2023 at 01:06):

Was scrolling through old messages and saw this, so it might be resolved by now, but if that's the error, does mkIdent `mod_rules work?

Mario Carneiro (Jan 20 2023 at 01:06):

it does (solved in PMs)


Last updated: Dec 20 2023 at 11:08 UTC