Zulip Chat Archive

Stream: mathlib4

Topic: Prime ZMod leads to `decide` complaining of free variables


Mauricio Collares (May 24 2024 at 10:12):

The following example, adapted from the Exponential Ramsey project, doesn't work in Lean 4:

set_option pp.all true in
example [AAAAAAAAA : Fact (Nat.Prime 5)] :  (a : ZMod 5), a = 2  a = 3  False := by
  decide

It fails with

expected type must not contain free or meta variables
   (a : ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))),
    And
        (@Eq.{1} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) a
          (@OfNat.ofNat.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) 2
            (@instOfNat.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) 2
              (@Semiring.toNatCast.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                (@DivisionSemiring.toSemiring.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                  (@Semifield.toDivisionSemiring.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                    (@Field.toSemifield.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                      (@ZMod.instFieldZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)) AAAAAAAAA)))))
              (@instNatAtLeastTwo (@OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))))
        (@Eq.{1} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) a
          (@OfNat.ofNat.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) 3
            (@instOfNat.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5))) 3
              (@Semiring.toNatCast.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                (@DivisionSemiring.toSemiring.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                  (@Semifield.toDivisionSemiring.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                    (@Field.toSemifield.{0} (ZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)))
                      (@ZMod.instFieldZMod (@OfNat.ofNat.{0} Nat 5 (instOfNatNat 5)) AAAAAAAAA)))))
              (@instNatAtLeastTwo (@OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))) 
      False

The AAAAAAAAA instance clearly appears in the target, but I think that was also the case in Lean 3 and dec_trivial worked then. Is this expected?

Alex J. Best (May 25 2024 at 22:24):

it would certainly be nice to have decide be a bit more user friendly in such situations, but if you put

  revert AAAAAAAAA

before decide it works fine. The error message is complaining that AA..A is free in the goal, revert fixes that

Kim Morrison (May 26 2024 at 09:02):

Do we have an issue tracking this? In my understanding, we all agree that the user facing decide should determine what needs to be reverted automatically, it's just no one has implemented this.

Mauricio Collares (Nov 07 2024 at 18:51):

I think @Kyle Miller just fixed this (or rather added an option to do so) in lean4#5999, but I never used decide! or dec_trivial in Lean 3 so I don't know what should be in scope for decide vs decide +revert in Lean 4

Mauricio Collares (Nov 07 2024 at 18:53):

cc @Bhavik Mehta, who had to work around this in ExponentialRamsey (compare with Lean 3)

Bhavik Mehta (Nov 07 2024 at 19:11):

Will take a look later, but I don't think the revert was the difference here; note I also had to revert in Lean 3. Instead the regression for the project was that I had to make it a separate lemma, and the decide failed (including with the revert) if I didn't.

Bhavik Mehta (Nov 07 2024 at 19:34):

Oh oops, the issue was indeed the revert! In the Lean 3 version I didn't need to revert the instance, but I needed to revert something else. Whereas in Lean 4, I need to revert both. So indeed Kim's suggested solution would fix this in my example.

Bhavik Mehta (Nov 07 2024 at 19:44):

Thinking about this more, another possible diagnosis is that in Lean 3, AAAAAAAAA didn't appear in the goal at all, and so didn't need to be reverted.
For instance,

set_option pp.all true in
example :  (a : ZMod 5), a = 2  a = 3  False := by
  decide

succeeds. So instead of this being viewed as a breaking change in decide, it could also be viewed as a breaking change in typeclass synthesis

Bhavik Mehta (Nov 07 2024 at 19:56):

Huh. In fact the Lean 3 version does have AAAAAAAAA in the goal, and the dec_trivial works fine there.

Mauricio Collares (Nov 07 2024 at 20:04):

Is this related to https://github.com/leanprover-community/mathlib3/pull/3875 ?

Bhavik Mehta (Nov 07 2024 at 20:09):

I don't believe so - the change there is to make the tactic dec_trivial! do reverts, but the Lean 3 tactic dec_trivial succeeds already, without doing reverts

Bhavik Mehta (Nov 07 2024 at 20:25):

My guess as to what's actually causing this is that while the Fact (Nat.Prime 5) instance is present, the goal is definitionally equal to a goal where that instance is missing: it's well-defined without that instance, and even though it's used to show that ZMod 5 is a field, all that's needed here is that it's a semiring. Lean 3's dec_trivial was smart enough to see that the instance wasn't actually used, but Lean 4's decide isn't. And so the revert becomes needed in Lean 4 where it wasn't in Lean 3.

Mauricio Collares (Nov 07 2024 at 20:26):

By the way: I just tested, and decide +revert works to replace the auxiliary lemma (paley_five_bound_aux) on paley_five_bound but not the one (paley_seventeen_helper') on paley_seventeen_bound. It complains about forall_prop_decidable being classical.

Bhavik Mehta (Nov 07 2024 at 20:29):

Note that seventeen_helper and five_bound_aux aren't analogous; seventeen_bound uses seventeen_helper and a decide call, and in Lean 4 the decide call needed to be separated out to seventeen_helper'. It's this latter one that's analogous to five_bound_aux. I hope you can excuse my awful naming given that these should all be private anyway ;)

Mauricio Collares (Nov 07 2024 at 20:34):

No, no, the naming's fine! I just made a typo, I indeed meant paley_seventeen_helper'. Edited.

Mauricio Collares (Nov 07 2024 at 20:39):

In the latter case, it reverted too many things and I think it is trying to use choice for the two Exists propositions:

tactic 'decide' failed for proposition
   (this : Fact (Nat.Prime 17)),
    ( m c, (paleyLabelling (ZMod 17)).MonochromaticOf (m) c  ![4, 4] c = #m) 
      ( m c, (paleyLabelling (ZMod 17)).MonochromaticOf (m) c  4 = #m) 
         (a b : ZMod 17), a = 2  a = 9  a = 16  b = 2  b = 9  b = 16  ¬a = b  IsSquare (b - a)  False

Mauricio Collares (Nov 07 2024 at 20:40):

Ah, that's not the case. Even clearing the corresponding variables, I get

tactic 'decide' failed for proposition
   (this : Fact (Nat.Prime 17)) (a b : ZMod 17),
    a = 2  a = 9  a = 16  b = 2  b = 9  b = 16  ¬a = b  IsSquare (b - a)  False
since its 'Decidable' instance
  forall_prop_decidable fun this =>
     (a b : ZMod 17), a = 2  a = 9  a = 16  b = 2  b = 9  b = 16  ¬a = b  IsSquare (b - a)  False
did not reduce to 'isTrue' or 'isFalse'.

Bhavik Mehta (Nov 07 2024 at 20:40):

Huh, this is a really interesting observation. So what's going on here is that reverting the instance is requiring it to revert the other two hypotheses that are still around (these ones do actually need that instance), and those ones use choice in their definitions (see classical on the second line of the proof). But since Lean 3 never needed to revert the instance, these hypotheses don't even get seen by dec_trivial, and so the proof goes through

Bhavik Mehta (Nov 07 2024 at 20:42):

Curiously on 4.13.0 (I haven't bumped ExponentialRamsey to 4.14.0-rc locally yet), clearing the variables gives me a recursion depth error instead!

Bhavik Mehta (Nov 07 2024 at 20:45):

(For some broader context for those following along at home, this one is part of the proof that R(4) = 18, which is the highest known diagonal Ramsey number, and up until ITP this year was the joint largest formally verified Ramsey number.)

Mauricio Collares (Nov 07 2024 at 21:04):

Ah, the constructive decidability instance in the second case is too big (whatever that means), and then the classical instance kicks in. So clearing h and this and doing set_option synthInstance.maxSize 1024 makes decide +revert work for paley_seventeen_bound as well.


Last updated: May 02 2025 at 03:31 UTC