Zulip Chat Archive

Stream: mathlib4

Topic: Qq error in MOP update


Patrick Massot (Sep 29 2023 at 13:33):

If any meta-expert want to help bumping @Heather Macbeth's book, the first challenge is to fix:

import Mathlib.Data.Int.Basic
import Mathlib.Tactic.Ring
import Mathlib.Tactic.NormNum
import Mathlib.Tactic.Linarith

open Lean hiding Rat mkRat
open Lean.Meta Qq Lean.Elab Term
open Lean.Parser.Tactic Mathlib.Meta.NormNum

/-- Two integers are congruent modulo `n`, if their difference is a multiple of `n`. -/
def Int.ModEq (n a b : ) : Prop := n  a - b

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

namespace Mathlib.Meta.NormNum

theorem isInt_ModEq_true : {a b a' b' n : }  IsInt a a'  IsInt b b'  decide (a' = b') = true 
    Int.ModEq n a b
  | _, _, a', b', n, rfl⟩, rfl⟩, hab =>
    by
      dsimp
      replace hab := of_decide_eq_true hab
      rw [hab]
      use 0
      ring

theorem isInt_ModEq_false : {a b a' b' n : }  IsInt a a'  IsInt b b'  decide (0 < n) = true 
    decide (a' < n) = true  decide (b' < n) = true  decide (0  a') = true 
    decide (0  b') = true  decide (a'  b') = true  ¬ Int.ModEq n a b
  | _, _, a', b', n, rfl⟩, rfl⟩, hn, han, hbn, ha, hb, hab =>
    by
      dsimp
      change ¬ n  _
      replace hn := of_decide_eq_true hn
      replace han := of_decide_eq_true han
      replace hbn := of_decide_eq_true hbn
      replace ha := of_decide_eq_true ha
      replace hb := of_decide_eq_true hb
      replace hab := of_decide_eq_true hab
      rw [ Int.exists_lt_and_lt_iff_not_dvd _ hn]
      cases' lt_or_gt_of_ne hab with hab hab
      · exact -1, by linarith, by linarith
      · exact 0, by linarith, by linarith

end Mathlib.Meta.NormNum

/-- The `norm_num` extension which identifies expressions of the form `a ≡ b [ZMOD n]`,
such that `norm_num` successfully recognises both `a` and `b` and they are small compared to `n`. -/
@[norm_num Int.ModEq _ _ _] def evalModEq : NormNumExt where eval (e : Q(Prop)) := do
  let .app (.app (.app f (n : Q())) (a : Q())) (b : Q())  whnfR e | failure
  guard <|← withNewMCtxDepth <| isDefEq f q(Int.ModEq)
  let ra : Result a  derive a
  let rb : Result b  derive b
  let rn : Result q($n)  derive n
  let i : Q(Ring ) := q(Int.instRingInt)
  let za, na, pa  ra.toInt
  let zb, nb, pb  rb.toInt
  let zn, _, _  rn.toInt i
  if za = zb then
    -- reduce `a ≡ b [ZMOD n]` to `true` if `a` and `b` reduce to the same integer
    let pab : Q(decide ($na = $nb) = true) := (q(Eq.refl true) : Expr)
    let r : Q(Int.ModEq $n $a $b) := q(isInt_ModEq_true $pa $pb $pab)
    return (.isTrue r : Result q(Int.ModEq $n $a $b))
  else
    -- reduce `a ≡ b [ZMOD n]` to `false` if `0 < n`, `a` reduces to `a'` with `0 ≤ a' < n`,
    -- and `b` reduces to `b'` with `0 ≤ b' < n`
    let pab : Q(decide ($na  $nb) = true) := (q(Eq.refl true) : Expr)
    if zn = 0 then failure
    let pn : Q(decide (0 < $n) = true) := (q(Eq.refl true) : Expr)
    if zn  za then failure
    let pan : Q(decide ($na < $n) = true) := (q(Eq.refl true) : Expr)
    if zn  zb then failure
    let pbn : Q(decide ($nb < $n) = true) := (q(Eq.refl true) : Expr)
    if za < 0 then failure
    let pa0 : Q(decide (0  $na) = true) := (q(Eq.refl true) : Expr)
    if zb < 0 then failure
    let pb0 : Q(decide (0  $nb) = true) := (q(Eq.refl true) : Expr)
    let r : Q(¬Int.ModEq $n $a $b) := q(isInt_ModEq_false $pa $pb $pn $pan $pbn $pa0 $pb0 $pab)
    return (.isFalse r : Result q(¬Int.ModEq $n $a $b))

Eric Wieser (Sep 29 2023 at 13:33):

I'll take a quick look

Eric Wieser (Sep 29 2023 at 13:34):

It would be great if CI were enabled for that repo so that I could see the erro in the PR

Bhavik Mehta (Sep 29 2023 at 13:35):

More generally, it might be nice if there were a CI template made explicitly somewhere that projects could copy / adapt, to ease the setup for people making Lean 4 projects

Eric Wieser (Sep 29 2023 at 13:36):

I think you can just copy the one in mathlib?

Patrick Massot (Sep 29 2023 at 13:39):

Maybe it isn't clear but the snippet I pasted above is a mwe.

Patrick Massot (Sep 29 2023 at 13:39):

You can paste it into a scratch file in current mathlib.

Eric Wieser (Sep 29 2023 at 13:50):

Adding

  let pa' : Q(IsInt «$a» «$na») := pa
  let pb' : Q(IsInt «$b» «$nb») := pb

seems to fix it

Eric Wieser (Sep 29 2023 at 13:50):

(and using the primed versions later)

Eric Wieser (Sep 29 2023 at 13:50):

The infoview can't be trusted to show the types that Qq actually sees

Eric Wieser (Sep 29 2023 at 13:52):

Hmm, actually somehow that makes an error appear on an earlier line

Eric Wieser (Sep 29 2023 at 13:52):

Working:

/-- The `norm_num` extension which identifies expressions of the form `a ≡ b [ZMOD n]`,
such that `norm_num` successfully recognises both `a` and `b` and they are small compared to `n`. -/
@[norm_num Int.ModEq _ _ _] def evalModEq : NormNumExt where eval (e : Q(Prop)) := do
  let .app (.app (.app f (n : Q())) (a : Q())) (b : Q())  whnfR e | failure
  guard <|← withNewMCtxDepth <| isDefEq f q(Int.ModEq)
  let ra : Result a  derive a
  let rb : Result b  derive b
  let rn : Result n  derive n
  let i : Q(Ring ) := q(Int.instRingInt)
  let za, na, pa  ra.toInt
  let zb, nb, pb  rb.toInt
  let pa' : Q(IsInt «$a» «$na») := pa
  let pb' : Q(IsInt «$b» «$nb») := pb
  let zn, _, _  rn.toInt i
  if za = zb then
    -- reduce `a ≡ b [ZMOD n]` to `true` if `a` and `b` reduce to the same integer
    let pab : Q(decide ($na = $nb) = true) := (q(Eq.refl true) : Expr)
    let r : Q($a  $b [ZMOD $n]) := q(isInt_ModEq_true $pa' $pb' $pab)
    return (.isTrue r : Result q(Int.ModEq $n $a $b))
  else
    -- reduce `a ≡ b [ZMOD n]` to `false` if `0 < n`, `a` reduces to `a'` with `0 ≤ a' < n`,
    -- and `b` reduces to `b'` with `0 ≤ b' < n`
    let pab : Q(decide ($na  $nb) = true) := (q(Eq.refl true) : Expr)
    if zn = 0 then failure
    let pn : Q(decide (0 < $n) = true) := (q(Eq.refl true) : Expr)
    if zn  za then failure
    let pan : Q(decide ($na < $n) = true) := (q(Eq.refl true) : Expr)
    if zn  zb then failure
    let pbn : Q(decide ($nb < $n) = true) := (q(Eq.refl true) : Expr)
    if za < 0 then failure
    let pa0 : Q(decide (0  $na) = true) := (q(Eq.refl true) : Expr)
    if zb < 0 then failure
    let pb0 : Q(decide (0  $nb) = true) := (q(Eq.refl true) : Expr)
    let r : Q(¬Int.ModEq $n $a $b) := q(isInt_ModEq_false $pa' $pb' $pn $pan $pbn $pa0 $pb0 $pab)
    return (.isFalse r : Result q(¬Int.ModEq $n $a $b))

Patrick Massot (Sep 29 2023 at 13:53):

Thanks. Any idea what was the explanation?

Eric Wieser (Sep 29 2023 at 13:55):

let rn : Result q($n) ← derive n was redundant and I guess confused Qq

Eric Wieser (Sep 29 2023 at 13:55):

Otherwise, the problem is that the destructuring let binding doesn't actually record the type information in a way that Qq can see it

Eric Wieser (Sep 29 2023 at 13:55):

As a general strategy, if something fails in Qq, try adding a let binding with an explicit type somewhere; even if the goal view says Lean already knows the type

Eric Wieser (Sep 29 2023 at 13:56):

I think Qq elaborates the terms before the goal view does, and so gets them in a less-elaborated state

Patrick Massot (Sep 29 2023 at 14:00):

It doesn't really explained why it worked before, but thanks.

Eric Wieser (Sep 29 2023 at 14:01):

Eric Wieser said:

It would be great if CI were enabled for that repo so that I could see the erro in the PR

Looking at the repo again, I think the answer is "this is a generated repo that is committed to by a script, the real repo is somewhere secret and may or may not have CI"

Heather Macbeth (Sep 29 2023 at 14:10):

@Patrick Massot I'm sorry you went to all this work, I actually have a bump in the private version which covers the Qq changes, but I didn't want to change the book version mid-semester.

Heather Macbeth (Sep 29 2023 at 14:11):

I'll add you to the private version.

Patrick Massot (Sep 29 2023 at 14:11):

Are you currently using the book? I thought that would happen next semester.

Patrick Massot (Sep 29 2023 at 14:11):

I pushed a fully fixed Library folder in my PR.

Heather Macbeth (Sep 29 2023 at 14:11):

Yes, I am teaching the couse three semesters in a row.

Yaël Dillies (Sep 29 2023 at 16:57):

Tangentially, how does one debug Qq troubles?


Last updated: Dec 20 2023 at 11:08 UTC