Zulip Chat Archive

Stream: new members

Topic: Need help understanding ring_nf


Kevin Cheung (Feb 04 2025 at 18:17):

I think I am lacking understanding on how Floats and Real work in Mathlib. In particular, I have a hard time seeing how ring_nf works for foo but not bar in the following:

import Mathlib.Tactic

theorem foo : (0.27 : ) = 27 / 100 := by ring_nf
theorem bar : 0.27 = 27 / 100 := by ring_nf -- why doesn't this work?

How can one find out how Lean interpreted the constants 0.27 and 27 / 100 in bar? I suppose Lean automatically treats 27 / 100 as in foo. Is that right? Also, is bar actually provable? Any help is greatly appreciated.

Aaron Liu (Feb 04 2025 at 18:22):

Float is not a docs#CommSemiring, so ring_nf will not work.

Kevin Cheung (Feb 04 2025 at 18:23):

So, in bar, Lean is treating everything as Float? In this case, is it provable?

Aaron Liu (Feb 04 2025 at 18:24):

I don't think it is provable, since floats are opaque.

Kevin Cheung (Feb 04 2025 at 18:29):

It's impossible to prove statements involving floats? For example, the following is unprovable?

example : 0.2 > 0.1 := by sorry

Walter Moreira (Feb 04 2025 at 19:54):

Kevin Cheung said:

...
How can one find out how Lean interpreted the constants 0.27 and 27 / 100 in bar?
...

For future situations, if you mouse over the term 0.27 in the Lean Infoview, you'll see the way that Lean interprets the literal:

@OfScientific.ofScientific Float instOfScientificFloat 27 true 2 : Float

Does anyone know the programmatic way to obtain what the Infoview is showing? I browsed the metaprogramming book but couldn't find it.

Aaron Liu (Feb 04 2025 at 19:58):

What do you mean by programmatic?

Aaron Liu (Feb 04 2025 at 20:15):

Kevin Cheung said:

It's impossible to prove statements involving floats? For example, the following is unprovable?

example : 0.2 > 0.1 := by sorry

This seems to be unprovable.

example : 0.2 > 0.1 := by
  unfold GT.gt LT.lt instLTFloat Float.lt
  unfold OfScientific.ofScientific
  unfold instOfScientificFloat
  show
    FloatSpec.lt
      floatSpec
      (Float.val (Float.ofScientific (nat_lit 1) true (nat_lit 1)))
      (Float.val (Float.ofScientific (nat_lit 2) true (nat_lit 1)))
  -- no further reduction is possible
  sorry

-- `1`, `2`, and `true` are fully reduced terms

-- `floatSpec` and `Float.ofScientific` are opaque constants with no definition

-- `FloatSpec.lt` is a projection coming from the type `FloatSpec`

/--
info: structure FloatSpec : Type 1
number of parameters: 0
fields:
  FloatSpec.float : Type
  FloatSpec.val : self.float
  FloatSpec.lt : self.float → self.float → Prop
  FloatSpec.le : self.float → self.float → Prop
  FloatSpec.decLt : DecidableRel self.lt
  FloatSpec.decLe : DecidableRel self.le
constructor:
  FloatSpec.mk (float : Type) (val : float) (lt le : float → float → Prop) (decLt : DecidableRel lt)
    (decLe : DecidableRel le) : FloatSpec
-/
#guard_msgs in
#print FloatSpec

-- `Float.val` is a projection coming from the type `Float`

/--
info: structure Float : Type
number of parameters: 0
fields:
  Float.val : floatSpec.float
constructor:
  Float.mk (val : floatSpec.float) : Float
-/
#guard_msgs in
#print Float

-- however,
example : 0.2 > 0.1 := by native_decide
-- '_example' depends on axioms: [propext, Classical.choice, Lean.ofReduceBool]

Vlad Tsyrklevich (Feb 04 2025 at 20:37):

Walter Moreira said:

Does anyone know the programmatic way to obtain what the Infoview is showing?

#check 0.27 ?

Walter Moreira (Feb 04 2025 at 20:40):

Aaron Liu said:

What do you mean by programmatic?

I was too vague. I would like to find a function or command (similar to #print) that shows the term with the the coercions that Lean is applying.

Kevin Cheung (Feb 04 2025 at 20:41):

Walter Moreira said:

Aaron Liu said:

What do you mean by programmatic?

I was too vague. I would like to find a function or command (similar to #print) that shows the term with the the coercions that Lean is applying.

I have also been trying to find such a command as well to no avail.

Kevin Cheung (Feb 04 2025 at 20:43):

Looking at native_decide, it seems that it takes computations from outside the trusted kernel. Is this the only way one can deal with proofs involving Float?

Walter Moreira (Feb 04 2025 at 20:43):

Vlad Tsyrklevich said:

#check 0.27 ?

Yes, that definitely shows Float as the type of the literal. Is it always guaranteed that will be the coercion in an arbitrary expression (like bar above)?

Aaron Liu (Feb 04 2025 at 20:45):

Walter Moreira said:

Aaron Liu said:

What do you mean by programmatic?

I was too vague. I would like to find a function or command (similar to #print) that shows the term with the the coercions that Lean is applying.

#print is a command. Is that no good? You can also #check.

Kevin Cheung (Feb 04 2025 at 20:48):

In the following, #check just shows Prop and #print fails

#check (0.27 > 1 / 5)
#print (0.27 > 1 / 5)

Kevin Cheung (Feb 04 2025 at 20:49):

But one can hover over the numbers in Infoview to see the full picture for #check. So that works.

Aaron Liu (Feb 04 2025 at 20:49):

Kevin Cheung said:

But one can hover over the numbers in Infoview to see the full picture for #check. So that works.

Yes, that's what I meant. Is this no good?

Aaron Liu (Feb 04 2025 at 20:50):

Or do you want to print it without having to hover?

Luigi Massacci (Feb 04 2025 at 20:50):

Kevin Cheung said:

Looking at native_decide, it seems that it takes computations from outside the trusted kernel. Is this the only way one can deal with proofs involving Float?

Broadly speaking yes, because of what was said above about opaqueness. Having said that, are you sure you want to work with floats as opposed to rationals? floats are really nasty to prove anything about even on paper: addition and multiplication are not associative, do not distribute over each other, etc

Kevin Cheung (Feb 04 2025 at 20:50):

Yes. Print without hovering, if possible.

Walter Moreira (Feb 04 2025 at 20:51):

Aaron Liu said:

Kevin Cheung said:

But one can hover over the numbers in Infoview to see the full picture for #check. So that works.

Yes, that's what I meant. Is this no good?

We were hoping to get the more informative term: @OfScientific.ofScientific Float instOfScientificFloat 27 true 2 : Float

Kevin Cheung (Feb 04 2025 at 20:51):

Luigi Massacci said:

Kevin Cheung said:

Looking at native_decide, it seems that it takes computations from outside the trusted kernel. Is this the only way one can deal with proofs involving Float?

Broadly speaking yes, because of what was said above about opaqueness. Having said that, are you sure you want to work with floats as opposed to rationals? floats are really nasty to prove anything about even on paper: addition and multiplication are not associative, do not distribute over each other, etc

I'm not at the point of doing the following but what if I want to prove some properties about numerical algorithms that work with Float? This kind of work is unavoidable, I think, in numerical analysis.

Luigi Massacci (Feb 04 2025 at 20:54):

I mean, pen and paper correctness proofs for numerical analysis quite often pretend to work with the reals anyway, which is more than fine in most cases

Luigi Massacci (Feb 04 2025 at 20:56):

(and given how little numerical stuff there is in lean, right now that would already be a solid project, without worrying about verifying the correctness of an actually runnable implementation)

Luigi Massacci (Feb 04 2025 at 20:59):

also, for actual software, native_decide is not bad at all

Luigi Massacci (Feb 04 2025 at 21:01):

If you want examples of working with Float though, you can look at SciLean by @Tomas Skrivan

Tomas Skrivan (Feb 04 2025 at 21:05):

In SciLean, I have a class RealScalar R(extends RCLike R) that says R behaves like a real number and provides all the basic functions like exp, sin etc. Most of the definitions and theorems are then parameterized by this type Rand instance of RealScalar R. When it comes to actually running the programs I just provide inconsistent instance RealScalar Float.

Aaron Liu (Feb 04 2025 at 21:19):

I was able to write this (it doesn't handle corner cases), still can't find a builtin command to do it.

import Lean

open Lean Elab Command Meta

syntax (name := see) "#see" term : command

@[command_elab see]
def elabShow : CommandElab
  | `(#see%$tk $term) => withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_show do
    -- -- show signature for `#check id`/`#check @id`
    -- if let `($id:ident) := term then
    --   try
    --     for c in (← realizeGlobalConstWithInfos term) do
    --       addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
    --       logInfoAt tk <| .signature c
    --       return
    --   catch _ => pure ()  -- identifier might not be a constant but constant + projection
    let e  Term.elabTerm term none
    Term.synthesizeSyntheticMVarsNoPostponing (ignoreStuckTC := true)
    -- Users might be testing out buggy elaborators. Let's typecheck before proceeding:
    withRef tk <| Meta.check e
    let e  Term.levelMVarToParam ( instantiateMVars e)
    if e.isSyntheticSorry then
      return
    let type  inferType e
    let str : MessageData  e.withApp fun f xs 
      return xs.foldl (fun m n  m ++ m!" {n}") m!"{f}"
    logInfoAt tk <| str ++ m!" : {type}"
  | _ => throwUnsupportedSyntax

/--
info: @OfScientific.ofScientific Float instOfScientificFloat 27 true 2 : Float
-/
#guard_msgs in
#see 0.27

I did find that norm_num has a command form #norm_num

Tomas Skrivan (Feb 04 2025 at 21:19):

I would recommend formalizing numerical algorithms something like this. Example for Lagrange interpolating polynomial:

import Mathlib

open Filter Set

-- specification over reals
def lagrangeInterpolSpec (n : ) (f :   ) (x : ) :  := sorry

-- executable program
def lagrangeInterpol {R} [Add R] [Mul R] [Zero R] [One R] (n : ) (f : R  R) (x : R) : R := sorry

-- exacutable program satisfies the spec
theorem lagrangeInterpol_spec (n : ) (f :   ) (x : ) :
  lagrangeInterpol n f x = lagrangeInterpolSpec n f x := sorry

-- Lagrange interpolating polynom is actually converging to the function under some assumptions
theorem lagrangeInterpolSpec_limit (f :   ) (hf : ContDiff   f) (x : ) :
   x  Icc 0 1, f x = lim (( : Filter ).map (lagrangeInterpolSpec · f x)) := sorry

Kevin Cheung (Feb 05 2025 at 12:16):

Aaron Liu said:

I was able to write this (it doesn't handle corner cases), still can't find a builtin command to do it.

import Lean

open Lean Elab Command Meta

syntax (name := see) "#see" term : command

@[command_elab see]
def elabShow : CommandElab
  | `(#see%$tk $term) => withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_show do
    -- -- show signature for `#check id`/`#check @id`
    -- if let `($id:ident) := term then
    --   try
    --     for c in (← realizeGlobalConstWithInfos term) do
    --       addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
    --       logInfoAt tk <| .signature c
    --       return
    --   catch _ => pure ()  -- identifier might not be a constant but constant + projection
    let e  Term.elabTerm term none
    Term.synthesizeSyntheticMVarsNoPostponing (ignoreStuckTC := true)
    -- Users might be testing out buggy elaborators. Let's typecheck before proceeding:
    withRef tk <| Meta.check e
    let e  Term.levelMVarToParam ( instantiateMVars e)
    if e.isSyntheticSorry then
      return
    let type  inferType e
    let str : MessageData  e.withApp fun f xs 
      return xs.foldl (fun m n  m ++ m!" {n}") m!"{f}"
    logInfoAt tk <| str ++ m!" : {type}"
  | _ => throwUnsupportedSyntax

/--
info: @OfScientific.ofScientific Float instOfScientificFloat 27 true 2 : Float
-/
#guard_msgs in
#see 0.27

I did find that norm_num has a command form #norm_num

I don't quite understand why without #guard_msgs, #see 0.27 would produce an error. I'm not very familiar with metaprogramming so pardon my ignorance.

Aaron Liu (Feb 05 2025 at 12:19):

If you comment out #guard_msgs, the docComment gets attached to #see, which is illegal.

Kevin Cheung (Feb 05 2025 at 12:30):

I see. I guess I don't quite know how docComment works.


Last updated: May 02 2025 at 03:31 UTC