11
$\begingroup$

Does Lean 4 have a good way of handling data type conversion (e.g. between the natural and real numbers)?

I am trying to solve the following property from the Mechanics of Proof book:

$\forall a \in Z, a ^ 2 - 5 * a + 5 ≤ -1 ↔ a = 2 ∨ a = 3$

In lean:

example {a : ℤ} : a ^ 2 - 5 * a + 5 ≤ -1 ↔ a = 2 ∨ a = 3 := by
  sorry

The arithmetic is simple enough if one views the number $a$ as a real or rational number, and that

$a ^ 2 - 5 * a + 5 = (a - 5/2) ^ 2 - 5/4$

Then, this translates to

$ (a - 5/2) ^ 2 \leq 1/4 $

or eventually to

$ 2 \leq a \leq 3$

If one views $a$ as integer again, $a = 2 \lor a = 3$ follows.

But how can I switch the context and use $a$ as an integer sometimes and sometimes as real numbers.

Directly using $a$ as a real number doesn't work. For example, I tried:

  have ha : a ^ 2 - 5 * a + 5 = (a - 5/2) ^ 2 - 5/4    := by ring

, and this gives an error:

ring failed, ring expressions not equal.

I then tried:

have ha : (a : ℝ) ^ 2 - 5 * a + 5 = (a - 5/2) ^ 2 - 5/4    := by ring

This seems to check. But the VSCode inforview shows the equation with an up-arrow prepended to the variable a.

ha : ↑a ^ 2 - 5 * ↑a + 5 = (↑a - 5 / 2) ^ 2 - 5 / 4

I am not sure what ↑a is, or how to use it in the proof that follows.

Hence, the question here.

In Lean 4, how can I convert between different number types during a proof?

Note:

I know that I can probably get away with a workaround by multiplication and bring everything back to integer:

  have ha : 4 * (a ^ 2 - 5 * a + 5 ) = (2 * a - 5) ^ 2 - 5    := by ring

But in general, there is no guarantee such tricks to bring things to integer would work. For example, what if the condition in question was:

a ^ 2 - 5 * a + 5 ≤ -0.9
$\endgroup$
2
  • 1
    $\begingroup$ I fixed my answer, including giving tactics for working with coercions. I think this should give you the information you need to work with your example and be a good resource to others. $\endgroup$ Commented Jun 23, 2024 at 19:53
  • $\begingroup$ Note that my answer may not be in the spirit of MiL. I haven't read the book, and I don't know the intended solution. $\endgroup$ Commented Jun 23, 2024 at 19:56

1 Answer 1

10
$\begingroup$

This is a reasonable approach. You prove this by "relaxing" a to be a real number, namely proving 2 <= (a : Real) <= 3 and then using that to deduce 2 <= (a : Int) <= 3. I'll let you handle how to prove 2 <= (a : Real) <= 3, but let's talk about converting between Int and Real, and more generally casting from one type to another.

Coercion basics

If a has type Int and you write a : Real, what you are telling Lean to do is coerce a from Int to Real. So a is replaced with ↑a. This uparrow symbol (written \u or \uparrow in the Lean vs code plugin) is shorthand for @Int.cast ℝ Real.instIntCast a. You can find this out by hovering over ↑a in the infoview. It also gives this helpful message:

↑x represents a coercion, which converts x of type α to type β, using typeclasses to resolve a suitable conversion function. You can often leave the off entirely, since coercion is triggered implicitly whenever there is a type error, but in ambiguous cases it can be useful to use to disambiguate between e.g. ↑x + ↑y and ↑(x + y).

As this message suggests, if Lean is expecting a real number, as in a < Real.pi, then a, ↑a, and (a : ℝ) are all the same.

#check ∀ a : ℤ, a < Real.pi        -- ∀ (a : ℤ), ↑a < Real.pi
#check ∀ a : ℤ, ↑a < Real.pi       -- ∀ (a : ℤ), ↑a < Real.pi
#check ∀ a : ℤ, (a : ℝ) < Real.pi  -- ∀ (a : ℤ), ↑a < Real.pi

One case where a alone wouldn't work is when the expression is valid as either an Int or a Real. In that case, you have to specify the coercion using (a : ℝ) in at least one place.

#check ∀ a : ℤ, ((a : ℝ) / 2) * 2 = a  -- ((a : ℝ) / 2) * 2 = a

It is not enough here to just use ↑a since Lean doesn't know what to coerce to. For example, this doesn't coerce at all:

#check ∀ a : ℤ, ↑a / 2 * 2 = a  -- ∀ (a : ℤ), a / 2 * 2 = a

Conversely, Lean seems to apply ↑a as far inside as possible. So sometimes one needs to explicitly use to specify where the conversion should be.

#check ∀ a : ℤ, ((a + a) : ℝ) = (a : ℝ) + (a : ℝ)
-- ∀ (a : ℤ), ↑a + ↑a = ↑a + ↑a
#check ∀ a : ℤ, (↑(a + a) : ℝ) = a + a
-- ∀ (a : ℤ), ↑(a + a) = ↑a + ↑a

Coersion can change the truth value of a theorem

The reason that

have ha : a ^ 2 - 5 * a + 5 = (a - 5/2) ^ 2 - 5/4    := by ring

failed, is that the theorem is false (and ring tells you this), because for the integers, 5/4 = 1 and 5/2 = 2.

#eval (5: ℤ ) / 4  -- 1
#eval (5: ℤ ) / 2  -- 2

(This is similar to say 5 // 4 and 5 // 2 in python if you are familiar with integer division in typical programming languages.)

So this brings us to an important point. Just because you prove a theorem for (a : ℝ) doesn't mean it still holds for a.

How to go back and forth between coercions?

It shouldn't be hard for you to prove 2 <= (a : Real) and (a : Real) <= 3, but that isn't your goal. You want 2 <= a and a <= 3 (which since a is an integer implies that a = 2 or a = 3).

This is a common case in coercions. While it isn't true in general that any theorem of (a : Real) is a theorem of a, it is true that many functions and relations are -invariant (meaning R_ℤ a b ↔ R_ℝ ↑a ↑b) and other functions are -equivariant (meaning ↑(f_ℤ a b) = f_ℝ ↑a ↑b).

These theorems should be readily available in Mathlib, but instead of looking them up, there are tactics to help.

norm_cast normalizes coercions (removing them if possible).

example (a : ℤ) (h : a < 9) : (a : ℝ) < 10 := by
  norm_cast -- normalizes coercions (removing them if possible) in goal
  /-
  a : ℤ
  h : a < 9
  ⊢ a < 10
  -/
  linarith [h]

example (a : ℤ) (h : (a : ℝ) < 9) : a < 10 := by
  norm_cast at h  -- normalizes coersions (removing them if possible) in the hypotheses `h`
  /-
  a : ℤ
  h : a < 9
  ⊢ a < 10
  -/
  linarith [h]

push_cast pushes coercions inward (again removing them if possible).

example (a b : ℤ) (h : a + b < 9) : ↑ (a + b) < 10 := by
  push_cast  -- pushes coercions inward (removing them if possible) in the goal
  /-
  a b : ℤ
  h : a + b < 9
  ⊢ a + b < 10
  -/
  linarith [h]

example (a b : ℤ) (h : ↑ (a + b) < 9) : a + b < 10 := by
  push_cast at h  -- pushes coercions inward (removing them if possible) in the hypothesis
  /-
  a b : ℤ
  h : a + b < 9
  ⊢ a + b < 10
  -/
  linarith [h]

exact_mod_cast and apply_mod_cast are versions of exact and apply which also normalizes coercions.

example (a : ℤ) (h : a < 10) : (a : ℝ) < 10 := by
  exact_mod_cast h  -- like `exact` but normalizes coercions first

example (a : ℤ) (h : (a : ℝ) < 10) : a < 10 := by
  exact_mod_cast h  -- like `exact` but normalizes coercions first

example (a : ℤ) (h : (a : ℝ) < 10) : a < 10 := by
  apply_mod_cast h -- like `apply` but normalizes coercions first

rify converts goals on , , , etc. to ℝ. There is also qify for rationals and zify for integers. The zify, qify, rify tactics require Mathlib, while the other ..._cast tactics are available in base Lean.

example (a : ℤ) (h : (a : ℝ) < 9) : a < 10 := by
  rify -- converts goal to reals (use `zify` for ℤ and `qify` for ℚ)
  /-
  a : ℤ
  h : ↑a < 9
  ⊢ ↑a < 10
  -/
  linarith [h]

example (a : ℤ) (h : (a : ℝ) < 10) : a < 10 := by
  rify [h]  -- `rify` using `h` as a simp lemma

Other coercions

There are a lot of coercions in Lean. They come up in natural settings where one wants to interpret one type as another because of some canonical embedding (usually a homomorphism).

There are coercions from ℕ to ℚ to ℝ to ℂ; coercions to and from Nat and Fin n; coercions from fields to rings to groups (the forgetful functor); coercions from ℕ to rings, and many more.

New coercions have to be registered so Lean knows about them. For more information see Coercions using Type Classes in Theorem Proving in Lean 4.

$\endgroup$
1
  • $\begingroup$ Thank you for this summary of coercions. Indeed, it will be a very useful resource, as coercions and surrounding issues are really annoying when one is getting started with Lean... $\endgroup$ Commented May 6 at 22:24

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.