Zulip Chat Archive

Stream: general

Topic: norm_num


Sebastien Gouezel (Dec 03 2018 at 20:19):

I was under the impression that norm_num was always successful in its range of expertise. Here is a real-life counterexample:

lemma crazy (k l : ) (h : l  k): ((2:real)⁻¹)^k  (2⁻¹)^l :=
begin
  apply pow_le_pow_of_le_one _ _ h,
  norm_num,
  -- goal is 2⁻¹ ≤ 1
  norm_num
end

The last norm_num call gives rise to a deterministic timeout. Replacing it with show 2⁻¹ ≤ (1 : real), by norm_num works fine, so this is not really a problem for me, but still surprising.

Kevin Buzzard (Dec 03 2018 at 20:27):

import data.real.basic tactic.norm_num

lemma crazy (k l : ) (h : l  k): ((2:real)⁻¹)^k  (2⁻¹)^l :=
begin
  apply pow_le_pow_of_le_one _ _ h,
    norm_num,
  -- goal is 2⁻¹ ≤ 1
  show (2⁻¹ : )  1,
  norm_num,
end

I've seen this before, but don't remember why it's happening.

Mario Carneiro (Dec 03 2018 at 20:28):

norm_num is fighting with simp. Compare with norm_num1, simp, norm_num1, simp,

Mario Carneiro (Dec 03 2018 at 20:28):

which is basically what norm_num does

Sebastien Gouezel (Dec 03 2018 at 20:33):

Does it mean that it would be more safe for norm_num to call simp [-one_div_eq_inv]? By the way, I am not convinced that one_div_eq_inv is a good simp rule, what do you think?

Patrick Massot (Dec 03 2018 at 20:34):

It's the same family that turns every a-b into a+-b that I hate

Patrick Massot (Dec 03 2018 at 20:34):

and it's in core lib :sad:

Mario Carneiro (Dec 03 2018 at 20:43):

a better question is why norm_num1 isn't solving the goal

Mario Carneiro (Dec 03 2018 at 20:43):

since it does solve the goal if you canonize the instances by restating the goal as you did

Mario Carneiro (Dec 03 2018 at 20:44):

I think this one is a result of my relying on the C++ norm_num implementation, which lags behind the mathlib implementation quite a bit

Mario Carneiro (Dec 03 2018 at 20:45):

I use it for doing + - * / on rings and fields, and do everything else in lean

Mario Carneiro (Dec 03 2018 at 20:46):

but I have seen it get confused with weird instances before (i.e. simp wants to prove that (0 : int) != (1 : multiplicative int) even though it's false)

Sebastien Gouezel (Dec 03 2018 at 20:52):

It is definitely confused by the instances. With pp.all, the goal it can not solve is

@has_le.le.{0} real
    (@preorder.to_has_le.{0} real
       (@partial_order.to_preorder.{0} real
          (@ordered_comm_monoid.to_partial_order.{0} real
             (@ordered_cancel_comm_monoid.to_ordered_comm_monoid.{0} real
                (@ordered_semiring.to_ordered_cancel_comm_monoid.{0} real
                   (@linear_ordered_semiring.to_ordered_semiring.{0} real real.linear_ordered_semiring))))))
    (@has_inv.inv.{0} real (@division_ring.to_has_inv.{0} real real.division_ring)
       (@bit0.{0} real
          (@distrib.to_has_add.{0} real
             (@ring.to_distrib.{0} real
                (@normed_ring.to_ring.{0} real (@normed_field.to_normed_ring.{0} real real.normed_field))))
          (@has_one.one.{0} real
             (@zero_ne_one_class.to_has_one.{0} real (@domain.to_zero_ne_one_class.{0} real real.domain)))))
    (@has_one.one.{0} real
       (@monoid.to_has_one.{0} real
          (@semiring.to_monoid.{0} real
             (@ordered_semiring.to_semiring.{0} real
                (@linear_ordered_semiring.to_ordered_semiring.{0} real real.linear_ordered_semiring)))))

while the "right" instance is the much nicer

@has_le.le.{0} real real.has_le
    (@has_inv.inv.{0} real (@division_ring.to_has_inv.{0} real real.division_ring)
       (@bit0.{0} real
          (@distrib.to_has_add.{0} real
             (@ring.to_distrib.{0} real
                (@normed_ring.to_ring.{0} real (@normed_field.to_normed_ring.{0} real real.normed_field))))
          (@has_one.one.{0} real
             (@zero_ne_one_class.to_has_one.{0} real (@domain.to_zero_ne_one_class.{0} real real.domain)))))
    (@has_one.one.{0} real (@zero_ne_one_class.to_has_one.{0} real (@domain.to_zero_ne_one_class.{0} real real.domain)))

Rob Lewis (Dec 03 2018 at 21:14):

Something in norm_num, either in C++ or in mathlib, is creating some metavars and leaving them uninstantiated. Adding e₂ ← instantiate_mvars e₂, before the guard at https://github.com/leanprover/mathlib/blob/master/tactic/norm_num.lean#L162 allows norm_num1 to kill the goal.

Rob Lewis (Dec 03 2018 at 21:15):

I haven't tracked down where the uninstantiated metavar is coming from yet.

Luca Gerolla (Dec 03 2018 at 21:36):

20180814_193304.jpg

Luca Gerolla (Dec 03 2018 at 21:36):

I also bumped into deterministic timeout few times when solving various inequality; the common situation was that I had goals involving some "variable" real number (i.e. not explicit - like the 'k' and 'l' in the example) and norm_num got in a loop (often involving 'one_div_eq_inv')

Rob Lewis (Dec 03 2018 at 21:42):

Hmm. I think the C++ norm_num instantiates mvars as a side effect, but only sometimes. It doesn't do it when it when the term is already in normal form.

Rob Lewis (Dec 03 2018 at 21:43):

Simple fix: e ← instantiate_mvars e, at norm_num.lean:468

Mario Carneiro (Dec 03 2018 at 22:01):

468?

Mario Carneiro (Dec 03 2018 at 22:01):

https://github.com/leanprover/mathlib/blob/master/tactic/norm_num.lean#L468

Mario Carneiro (Dec 03 2018 at 22:02):

you mean before the ext_simplify_core?

Patrick Massot (Dec 03 2018 at 22:02):

Booooo! Rob is not running up to date mathlib

Mario Carneiro (Dec 03 2018 at 22:03):

lol, you don't want to know what I'm running

Rob Lewis (Dec 03 2018 at 22:04):

Nope, the argument to derive should be instantiated before you do anything with it.

Rob Lewis (Dec 03 2018 at 22:04):

So make that the first line of the do block.

Mario Carneiro (Dec 03 2018 at 22:05):

why not 471/472? that's the first place after calling C norm_num

Mario Carneiro (Dec 03 2018 at 22:06):

or is the problem uninstantiated metavars in the user input

Rob Lewis (Dec 03 2018 at 22:06):

Yeah, exactly. At some point you compare the input with something that went through the C++ norm_num. The former has mvars, the latter doesn't.

Mario Carneiro (Dec 03 2018 at 22:09):

we could just check for unify instead of alpha equivalent

Mario Carneiro (Dec 03 2018 at 22:10):

eh, I guess that doesn't make sense here

Mario Carneiro (Dec 03 2018 at 22:11):

yeah okay, ship it

Kenny Lau (Dec 03 2018 at 22:17):

ship who?

Mario Carneiro (Dec 03 2018 at 23:22):

Not sure if this is a serious question, but this is an international forum, so... ship it

Yury G. Kudryashov (Mar 24 2020 at 18:53):

What is the purpose of algebra/norm_num in the Lean core library? Is it used for some tactic?

Mario Carneiro (Mar 24 2020 at 18:56):

It is used for the norm_num tactic

Yury G. Kudryashov (Mar 24 2020 at 19:02):

The norm_num tactic in mathlib?

Yury G. Kudryashov (Mar 24 2020 at 19:02):

I mean, can we move all these lemmas to mathlib?

Mario Carneiro (Mar 24 2020 at 19:03):

tactic.norm_num is in core, and norm_num in mathlib uses it

Mario Carneiro (Mar 24 2020 at 19:04):

we recently discussed having mathlib norm_num be implemented as standalone so that tactic.norm_num can be deleted

Yury G. Kudryashov (Mar 24 2020 at 19:06):

tactic.norm_num is a meta constant. Will it work if supporting lemmas will be in mathlib instead of stdlib?

Mario Carneiro (Mar 24 2020 at 19:07):

no, there are tests in the lean repo that would fail if you did that

Kevin Buzzard (Mar 24 2020 at 19:08):

move the tests to the mathlib repo?

Mario Carneiro (Mar 24 2020 at 19:09):

that's just breaking lean and pretending we didn't

Kevin Buzzard (Mar 24 2020 at 19:10):

move all of Lean into mathlib?

Simon Hudon (Mar 25 2020 at 00:35):

It's not really practical to move constants backed by C++ code outside of core. For now. In Lean 4 we could do something like that but not in Lean 3.

Bryan Gin-ge Chen (Mar 25 2020 at 00:40):

Out of curiosity, was the issue discussed at the start of this thread in Dec 2018 ever fixed? Yep, the example works in 3.7.2c and a recent mathlib.

Yury G. Kudryashov (Jun 28 2020 at 23:02):

Do we need init_/algebra/norm_num for some reason?

Yury G. Kudryashov (Jun 28 2020 at 23:03):

@Mario Carneiro :up:

Mario Carneiro (Jun 29 2020 at 02:38):

I think it is not imported by anything right now

Yury G. Kudryashov (Jun 29 2020 at 13:54):

I meant "it is not imported, may I remove it"?

Johan Commelin (Jun 29 2020 at 13:58):

Yup, I think it can go. I didn't pay close attention to what stuff was dead code when I ripped algebra out of core.

Johan Commelin (Jun 29 2020 at 13:58):

But since Mario rewrote norm_num, I think this is now a leaf and can be safely deleted.

Johan Commelin (Jun 29 2020 at 13:59):

(Unless you think there is something in there that is worth merging into the main tree.)

Mario Carneiro (Jun 29 2020 at 14:14):

When I did my second pass on Johan's import I specifically attended to making sure that everything in init_.algebra.norm_num that is needed by the new norm_num got moved where it needs to be, without importing it, exactly so that it could be deleted

Yaël Dillies (Jun 20 2022 at 13:09):

@Mario Carneiro, I broke norm_num in #14683. It does not infer the 0 < b argument to docs#norm_num.zpow_neg anymore. Do you think you could investigate?

Mario Carneiro (Jun 20 2022 at 15:06):

@Yaël Dillies fixed in #14852. The bug was there all along but it was causing a non-fatal error and simp used to find an alternative proof if you use norm_num instead of norm_num1.

Mario Carneiro (Jun 20 2022 at 15:27):

It appears the way this interacted with your PR is that simp would previously rewrite

  (@has_div.div real
     (@div_inv_monoid.to_has_div real
        (@division_ring.to_div_inv_monoid real
           (@field.to_division_ring real (@linear_ordered_field.to_field real real.linear_ordered_ring))))
     1
     9)
==>
  (@has_div.div real
     (@div_inv_monoid.to_has_div real
        (@field.to_div_inv_monoid real (@linear_ordered_field.to_field real real.linear_ordered_ring)))
     1
     9)

which would cause a failure in simp when applying transitivity with the bad proof in a place that would get ignored, and on your branch the term is

       (@has_div.div real
           (@div_inv_monoid.to_has_div real
              (@division_ring.to_div_inv_monoid real
                 (@field.to_division_ring real (@linear_ordered_field.to_field real real.linear_ordered_ring))))
           1
           9)

and simp is no longer interested in rewriting it, so the bad proof makes it to the top level and causes the failure. I guess you changed the instance search around division rings in some way?

Yaël Dillies (Jun 20 2022 at 15:40):

I added semifield and division_semiring under field and division_ring, so I had to change the inheritance around those parts.


Last updated: Dec 20 2023 at 11:08 UTC