Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - fix: correct bad handling of rat literals in ring_nf #8836

Closed
wants to merge 10 commits into from

Conversation

eric-wieser
Copy link
Member

@eric-wieser eric-wieser commented Dec 6, 2023

Reported on Zulip:

import Mathlib.Data.Real.Basic

local macro_rules | `($x ^ $y) => `(HPow.hPow $x $y) -- Porting note: See issue lean4#2220

example {n : ℝ} : (n + 1 / 2) ^ 2 * (n + 1 + 1 / 3) ≤ (n + 1 / 3) * (n + 1) ^ 2 := by
  ring_nf

After the ring_nf call, the goal looks like:

 ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 19) / ↑12) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3 ≤
  ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 5) / ↑3) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3

This removes the unwanted Int.ofNat and coercions.

The docstring was apparently incorrect on Rat.rawCast (according to @digama0).
With that in mind, it's just a case of fixing the local lemmas used to clean up, to reflect the actual invariant instead of the previously-documented one.

This also fixes a hang when using mode = .raw, by using only a single pass to prevent 1 recursively expanding to Nat.rawCast 1 + 0.


Open in Gitpod

@eric-wieser eric-wieser added bug Something isn't working awaiting-review t-meta Tactics, attributes or user commands labels Dec 6, 2023
eric-wieser and others added 2 commits December 6, 2023 01:48
Co-authored-by: Mario Carneiro <mcarneir@andrew.cmu.edu>
@leanprover-community leanprover-community deleted a comment from github-actions bot Dec 6, 2023
@leanprover-community leanprover-community deleted a comment from github-actions bot Dec 6, 2023
Comment on lines 122 to 125
theorem int_rawCast_pos {R} [Ring R] : (Int.rawCast (.ofNat n) : R) = Nat.rawCast n := by simp
theorem int_rawCast_neg {R} [Ring R] : (Int.rawCast (.negOfNat n) : R) = -Nat.rawCast n := by simp
theorem rat_rawCast {R} [DivisionRing R] :
(Rat.rawCast n d : R) = Int.rawCast n / Nat.rawCast d := by simp
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring in the other file says that Int.rawCast (.ofNat n) is not allowed to ever appear; so arguably rat_rawCast is a bad lemma because it introduces this illegal form, even if only temporarily.

I think the version I had that preserved the invariant was a little clearer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're taking things out of raw cast form here, so it's fine to break those invariants. (More precisely we are temporarily weakening one of the invariants to allow the Int.rawCast (.ofNat n) form.) The main reason I like this version better is that it's more orthogonal, we only need one case each for negation and division and the int cast. Otherwise we need a cartesian product of cases for all the ways that you can build literals.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for comparison, my proposal is

Suggested change
theorem int_rawCast_pos {R} [Ring R] : (Int.rawCast (.ofNat n) : R) = Nat.rawCast n := by simp
theorem int_rawCast_neg {R} [Ring R] : (Int.rawCast (.negOfNat n) : R) = -Nat.rawCast n := by simp
theorem rat_rawCast {R} [DivisionRing R] :
(Rat.rawCast n d : R) = Int.rawCast n / Nat.rawCast d := by simp
theorem int_rawCast_neg {R} [Ring R] : (Int.rawCast (.negOfNat n) : R) = -Nat.rawCast n := by simp
theorem rat_rawCast_pos {R} [DivisionRing R] :
(Rat.rawCast (.ofNat n) d : R) = Nat.rawCast n / Nat.rawCast d := by simp
theorem rat_rawCast_neg {R} [DivisionRing R] :
(Rat.rawCast (.negOfNat n) d : R) = Int.rawCast (.negOfNat n) / Nat.rawCast d := by simp

which has two benefits:

  • It never transitions through an invalid use of Int.rawCast
  • It doesn't accept invalid uses of Rat.rawCast

This is hardly a cartesian explosion, since we only have three types here anyway.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bhavik posted the following example on Zulip:

import Mathlib.Data.Real.Basic
example {n : ℝ} : (n + 1 / 2) ^ 2 * (n + 1 + 1 / 3) ≤ (n + 1 / 3) * (n + 1) ^ 2 := by ring_nf; sorry

On 86251e6, the remaining goal displays as ⊢ 1 / 3 + n * (OfNat.ofNat 19 / 12) + n ^ 2 * (OfNat.ofNat 7 / 3) + n ^ 3 ≤ 1 / 3 + n * (OfNat.ofNat 5 / 3) + n ^ 2 * (OfNat.ofNat 7 / 3) + n ^ 3 in the infoview. Applying Eric's suggestion, it is ⊢ 1 / 3 + n * (19 / 12) + n ^ 2 * (7 / 3) + n ^ 3 ≤ 1 / 3 + n * (5 / 3) + n ^ 2 * (7 / 3) + n ^ 3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note, regarding the two simp lemma strategies, I don't really care that much about it. Use whichever one you prefer.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@collares, do you have any ideas for how to write a test for that? It seems the one I added doesn't spot the issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using guard_mgs with trace_state

test/ring.lean Outdated
example {n : ℝ} (hn : 0 ≤ n) : (n + 1 / 2) ^ 2 * (n + 1 + 1 / 3) ≤ (n + 1 / 3) * (n + 1) ^ 2 := by
ring_nf
trace_state
sorry
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sorry
exact test_sorry

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_sorry has the interesting effect of complaining about unused hypotheses

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you will have to use _hn etc as well. The reason is because the linter doesn't fire on proofs that contain sorry.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I've already pushed such a change.

-- `conv_lhs` prevents `ring_nf` picking a bad normalization for both sides.
conv_lhs => ring_nf

-- We can't use `guard_target =ₛ` here, as while it does detect stray `OfNat`s, it also complains
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it sounds like we need a guard_target mode for pretty printed output rather than expr equality

Copy link
Member Author

@eric-wieser eric-wieser Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or some intermediate "syntactically equal except for instances implicit arguments"

@digama0
Copy link
Member

digama0 commented Dec 11, 2023

bors r+

@github-actions github-actions bot added ready-to-merge This PR has been sent to bors. and removed awaiting-review labels Dec 11, 2023
mathlib-bors bot pushed a commit that referenced this pull request Dec 11, 2023
Reported [on Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/ring_nf.20returns.20ugly.20literals/near/400988184):

> ```lean
> import Mathlib.Data.Real.Basic
> 
> local macro_rules | `($x ^ $y) => `(HPow.hPow $x $y) -- Porting note: See issue lean4#2220
> 
> example {n : ℝ} : (n + 1 / 2) ^ 2 * (n + 1 + 1 / 3) ≤ (n + 1 / 3) * (n + 1) ^ 2 := by
>   ring_nf
> ```
> After the `ring_nf` call, the goal looks like:
> ```lean
>  ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 19) / ↑12) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3 ≤
>   ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 5) / ↑3) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3
> ```

This removes the unwanted `Int.ofNat` and coercions.

The docstring was apparently incorrect on `Rat.rawCast` (according to @digama0).
With that in mind, it's just a case of fixing the local lemmas used to clean up, to reflect the actual invariant instead of the previously-documented one.

This also fixes a hang when using `mode = .raw`, by using only a single pass to prevent `1` recursively expanding to `Nat.rawCast 1 + 0`.



Co-authored-by: Mario Carneiro <di.gama@gmail.com>
@mathlib-bors
Copy link
Contributor

mathlib-bors bot commented Dec 11, 2023

Pull request successfully merged into master.

Build succeeded:

@mathlib-bors mathlib-bors bot changed the title fix: correct bad handling of rat literals in ring_nf [Merged by Bors] - fix: correct bad handling of rat literals in ring_nf Dec 11, 2023
@mathlib-bors mathlib-bors bot closed this Dec 11, 2023
@mathlib-bors mathlib-bors bot deleted the eric-wieser/fix-ring_nf branch December 11, 2023 12:14
awueth pushed a commit that referenced this pull request Dec 19, 2023
Reported [on Zulip](https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/ring_nf.20returns.20ugly.20literals/near/400988184):

> ```lean
> import Mathlib.Data.Real.Basic
> 
> local macro_rules | `($x ^ $y) => `(HPow.hPow $x $y) -- Porting note: See issue lean4#2220
> 
> example {n : ℝ} : (n + 1 / 2) ^ 2 * (n + 1 + 1 / 3) ≤ (n + 1 / 3) * (n + 1) ^ 2 := by
>   ring_nf
> ```
> After the `ring_nf` call, the goal looks like:
> ```lean
>  ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 19) / ↑12) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3 ≤
>   ↑(Int.ofNat 1) / ↑3 + n * (↑(Int.ofNat 5) / ↑3) + n ^ 2 * (↑(Int.ofNat 7) / ↑3) + n ^ 3
> ```

This removes the unwanted `Int.ofNat` and coercions.

The docstring was apparently incorrect on `Rat.rawCast` (according to @digama0).
With that in mind, it's just a case of fixing the local lemmas used to clean up, to reflect the actual invariant instead of the previously-documented one.

This also fixes a hang when using `mode = .raw`, by using only a single pass to prevent `1` recursively expanding to `Nat.rawCast 1 + 0`.



Co-authored-by: Mario Carneiro <di.gama@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working ready-to-merge This PR has been sent to bors. t-meta Tactics, attributes or user commands
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants