Zulip Chat Archive

Stream: general

Topic: Working with `invertible`


Eric Wieser (May 31 2023 at 12:12):

Does anyone have any tips for working with invertible? I always find it really awkward to prove even the most basic facts, such as

import algebra.invertible

variables {R : Type*} [semiring R] (x y : R)

/-- `1 + ⅟x` is invertible if `x + 1` is -/
def invertible_one_add_inv_of [invertible x] [invertible (x + 1)] : invertible (1 + x) :=
begin
  suffices : invertible ((x + 1) * x),
  { refine this.copy _ _,
    rw [add_one_mul x, mul_inv_of_self], },
  letI : invertible ((x + 1) * x) := invertible_mul _ _,
  refine ((unit_of_invertible ((x + 1) * x)).copy _ rfl (x * (x + 1)) _).invertible,
  dsimp,
  rw [inv_of_mul, inv_of_inv_of],
end

lemma one_add_inv_of [invertible x] [invertible (x + 1)] [invertible (1 + x)] :
  (1 + x) = x*⅟(x + 1) :=
begin
  letI := invertible_one_add_inv_of x,
  convert (rfl : (1 + x) = _),
end

Going further and showing invertible (⅟x + 1) ≃ invertible (1 + x) (proof: it's just multiplication by the unit units_of_invertible x) is even more annoying

Reid Barton (May 31 2023 at 12:16):

Yeah type classes make things hard :shrug:

Eric Wieser (May 31 2023 at 12:17):

A slightly better statement might be

/-- `1 + u` is invertible if `u + 1` is -/
def units.invertible_one_add_inv_of (u : Rˣ) [invertible (u + 1 : R)] : invertible (1 + u⁻¹ : R) :=
begin
  suffices : invertible ((u + 1) * u⁻¹ : R),
  { refine this.copy _ _,
    rw [add_one_mul (u : R), units.mul_inv], },
  letI := (u⁻¹).invertible,
  letI : invertible ((u + 1) * u⁻¹ : R) := invertible_mul _ _,
  refine ((unit_of_invertible ((u + 1) * u⁻¹ : R)).copy _ rfl (u * (u + 1)) _).invertible,
  dsimp,
  rw [inv_of_mul, inv_of_units, inv_inv],
end

lemma units.one_add_inv_of (u : Rˣ) [invertible (u + 1 : R)] [invertible (1 + u⁻¹ : R)] :
  (1 + u⁻¹ : R) = u*⅟(u + 1) :=
begin
  letI := u.invertible_one_add_inv_of,
  convert (rfl : (1 + u⁻¹ : R) = _),
end

but I don't see an obvious trick to help the proof

Anne Baanen (May 31 2023 at 12:33):

I believe the recommendation is to work in units and only at the last stage convert back to invertible. Since units has much more API, would that help here?

Eric Wieser (May 31 2023 at 12:39):

If I work with units, I need a hypothesis u2 = u + 1

Eric Wieser (May 31 2023 at 12:41):

So my statement becomes something like

example (h1 : u2 = u + 1) (h2 : u3 = 1 + u⁻¹) : u3⁻¹ = u * u2⁻¹ := sorry

which no longer resembles ⅟(1 + ↑u⁻¹ : R) = u*⅟(u + 1) at all!

Anne Baanen (May 31 2023 at 12:45):

I see, and is_unit is not good if you want it to be computable.

Eric Wieser (May 31 2023 at 12:49):

That woul make the goal be ↑hu3.unit⁻¹ = ↑u * hu2.unit⁻¹ which seems almost as bad!

Eric Wieser (May 31 2023 at 12:49):

I guess you could prove it about ring.inverse; but then you need to separately prove that is_unit for the RHS implies is_unit for the LHS

Eric Wieser (May 31 2023 at 12:50):

The code around diagonal_invertible gives a demonstration of how invertible can be somewhat useful

Eric Wieser (May 31 2023 at 12:51):

In that it gives us is_unit (diagonal v) ↔ is_unit v and (diagonal v)⁻¹ = diagonal (ring.inverse v) mostly at the same time, without having to repeat the same algebra

Eric Wieser (May 31 2023 at 12:52):

#18849 is a somewhat nastier example

Ruben Van de Velde (May 31 2023 at 12:59):

Not sure if you like this better:

def invertible_one_add_inv_of [invertible x] [invertible (x + 1)] : invertible (1 + x) :=
begin
  refine x*⅟(x + 1), _, _; nontriviality R,
  { rw [ (unit_of_invertible x)⁻¹.mul_right_inj,  (unit_of_invertible x).mul_left_inj],
    simp only [coe_unit_of_invertible, mul_one, inv_of_mul_self, coe_inv_unit_of_invertible],
    calc  x * (x *  (x + 1) * (1 +  x)) * x
        = ( x * x) *  (x + 1) * (x +  x * x) : by noncomm_ring
    ... = 1 : by simp },
  { rw [ mul_assoc, add_mul],
    simp },
end

Kevin Buzzard (May 31 2023 at 13:10):

import algebra.invertible

variables {R : Type*} [semiring R] (x y : R)

/-- `1 + ⅟x` is invertible if `x + 1` is -/
def invertible_one_add_inv_of [invertible x] [invertible (x + 1)] : invertible (1 + x) :=
begin
  convert invertible_mul (x) (x + 1),
  simp [mul_add],
end

Eric Wieser (May 31 2023 at 13:13):

It doesn't count unless you also prove ⅟(1 + ⅟x) = x*⅟(x + 1)

Kevin Buzzard (May 31 2023 at 13:22):

lemma one_add_inv_of [invertible x] [invertible (x + 1)] [invertible (1 + x)] :
  (1 + x) = x*⅟(x + 1) :=
begin
  apply inv_of_eq_right_inv,
  simpa [add_mul] using mul_inv_of_self (x + 1),
end

Kevin Buzzard (May 31 2023 at 13:28):

a.k.a. "these things follow formally from properties of inverses"

Eric Wieser (May 31 2023 at 13:30):

I guess this works here because the proof is trivial in both cases, but in general you end up proving pretty much the same thing in the first and second declaration

Filippo A. E. Nuccio (May 31 2023 at 13:31):

Since we are discussing this, and since I have fought a bit with invertible, can I ask why being in Type rather than in Propmakes "computation easier"? Also, it seems to me tat the sentence in the doc "The intent of the typeclass is to provide a way to write e.g. ⅟2 in a ring like ℤ[1/2] where some inverses exist but there is no general ⁻¹ operator;" is a bit strange. is_unit does not need this inverse operator neither, is it an old piece of doc?

Kevin Buzzard (May 31 2023 at 13:32):

I think the point is that invertible actually carries around the inverse, even though mathematically this is daft because it's unique so you can just use classical.some

Kevin Buzzard (May 31 2023 at 13:32):

You never need to know what the inverse actually is in the calculations above, you are just formally using the fact that 1/x * x = 1

Filippo A. E. Nuccio (May 31 2023 at 13:33):

Ah, I see.

Eric Wieser (May 31 2023 at 13:33):

The benefit of carrying around the inverse is that you can prove it has an inverse, and what that inverse is all at once

Filippo A. E. Nuccio (May 31 2023 at 13:33):

OK ok, I see, it makes sense. And concerning the doc, do you agree that it is somewhat strange?

Eric Wieser (May 31 2023 at 13:34):

is_unit doesn't actually give you the inverse; are you thinking of docs#ring.inverse or docs#is_unit.unit?

Filippo A. E. Nuccio (May 31 2023 at 13:34):

At first reading one would imagine that unit or is_unit need an has_inv instance, but this is not the case.

Filippo A. E. Nuccio (May 31 2023 at 13:35):

Eric Wieser said:

is_unit doesn't actually give you the inverse; are you thinking of ring.inverse?

Well, as @Kevin Buzzard said it gives it through some, because it gives you a term of units M

Eric Wieser (May 31 2023 at 13:35):

Right, that's docs#is_unit.unit

Kevin Buzzard (May 31 2023 at 13:35):

The doc looks fine to me: what's the problem? classical.some + is_unit is arguably more inconvenient than invertible, just like we don't have an axiom in group theory in Lean saying "there exists an identity element", we have a field of the structure saying "this is the identity element".

Eric Wieser (May 31 2023 at 13:36):

Please don't use classical.some with is_unit, that's what docs#is_unit.unit is for! (edit: the latter has a nicer defeq that means you don't need some_spec)

Filippo A. E. Nuccio (May 31 2023 at 13:37):

Agreed, I simply want to say that is_unitseems to carry the inverse as well, because terms in units M do.

Jireh Loreaux (May 31 2023 at 13:39):

Eric, it seems to me like the solution to this:
Eric Wieser said:

I guess this works here because the proof is trivial in both cases, but in general you end up proving pretty much the same thing in the first and second declaration

might be something like an invertible.copy_inv where you provide an inverse and the proofs, and then another another inverse which you prove is equal to the first, then you put the second one as the data in the instance. Of course, you wouldn't actually be copying an existing invertible since its a class not a structure, but this seems like it addresses your "doing the same computation twice" complaint.

Jireh Loreaux (May 31 2023 at 13:40):

(Maybe copy_inv is a bad name)

Eric Wieser (May 31 2023 at 13:40):

Yes, I was leaning towards that too; indeed, that's why my proof goes through units.copy which was a way of doing that without writing more API

Jireh Loreaux (May 31 2023 at 13:41):

Meh, I think this is a reasonable constructor to have for invertible.

Eric Wieser (May 31 2023 at 13:41):

Maybe docs#invertible.copy is useless and should instead be the version you describe

Filippo A. E. Nuccio (May 31 2023 at 13:41):

Kevin Buzzard said:

The doc looks fine to me: what's the problem? classical.some + is_unit is arguably more inconvenient than invertible, just like we don't have an axiom in group theory in Lean saying "there exists an identity element", we have a field of the structure saying "this is the identity element".

My problem with the doc is probably that it makes no reference to docs#ring.inverse and it is therefore difficult to find a way through the several definitions (invertible, is_unit, unit, inverse, etc...)

Eric Wieser (May 31 2023 at 13:42):

@Filippo A. E. Nuccio, don't forget matrix.has_inv which is equal to ring.inverse everywhere but not defeq!

Jireh Loreaux (May 31 2023 at 13:42):

I don't think invertible.copy is useless. That allows you to transfer an invertible instance from one element to another, while preserving the inverse. The copy_inv proposal is different (because you aren't already given an invertible instance).

Eric Wieser (May 31 2023 at 13:45):

@Jireh Loreaux, do you mean this?

/-- This is defined such that `⅟y = yi` -/
def invertible.more_copy {M : Type*} [monoid M]
  (x : M) [invertible x] (y : M) (hxy : y = x) (yi : M) (hyi : yi = x) :
  invertible y :=
((unit_of_invertible x).copy _ hxy yi hyi).invertible

Filippo A. E. Nuccio (May 31 2023 at 13:46):

There is also a nice docs#mv_power_series.inv.aux that always creates an auxiliary (mv) power series depending of an element of the coefficient ring, and whenever this elements happens to be the inverse of the constant terms it really gives the inverse of the power series :eyes:

Jireh Loreaux (May 31 2023 at 13:53):

That's not what I meant, but maybe it would be handy if the existing docs#invertible.copy were changed to that (I'm not sure). What I meant was this (untested):

/-- This is defined so that `⅟x = xi'`. -/
def invertible.mk_inv {M : Type*} [monoid M]
  (x : M) (xi : M) (xi' : M) (hx : xi = xi') (hx_mul_inv : x * xi = 1) (hx_inv_mul : xi * x = 1) :
  invertible x :=
xi', hx  hx_mul_inv, hx  hx_inv_mul

But maybe I have misunderstood your original complaint?

Eric Wieser (Jun 06 2023 at 08:04):

I think that would be more convenient with x,xi, and the two hxs bundled... Which essentially gives you what I suggest above!

Eric Wieser (Jun 06 2023 at 08:30):

One thing I realized today is that invertible doesn't follow a pattern we've established elsewhere:

Existence Explicit existence sometimes-junk value never-junk value
summable f has_sum f a tsum f -
differentiable f has_deriv_at f f ' a deriv f a -
is_unit x invertible x ring.inverse x ⅟x (inv_of)

Where the last row is the clear odd-one-out

Eric Wieser (Jun 06 2023 at 08:34):

Frankly I've found both has_sum and inv_of pretty irritating; the former forces you to use convert all the time when the a term is wrong, while the latter has equalities that you can rewrite but you have to build a bunch of typeclasses up front to use it.

Kevin Buzzard (Jun 06 2023 at 08:59):

has_sum is super-useful in teaching, especially before you've proved the theorem that says that a series has at most one sum.

Eric Wieser (Jun 06 2023 at 09:16):

Oh I completely agree it's way better than not having it at all!

Eric Wieser (Jun 06 2023 at 09:18):

My comment is more that the design constraints feel the same in both places, yet we've come up with two different solutions; is there a hybrid that is better than both?

Kevin Buzzard (Jun 06 2023 at 12:49):

I guess another example of "never junk value" would be the division function which demands a proof that the denominator is nonzero, and we don't have that either AFAIK.

Jireh Loreaux (Jun 06 2023 at 12:53):

docs#divp?

Yury G. Kudryashov (Jun 06 2023 at 13:03):

About has_sum/has_deriv_at/...: should we add congr_right lemmas like has_sum f x -> x = y -> has_sum f y to avoid convert?

David Loeffler (Jun 06 2023 at 13:29):

Talking of "existence vs. explicit existence", I've often wondered whether the integration library might benefit from introducing has_integral f μ a, analogous to has_sum f a.

Eric Wieser (Jun 08 2023 at 13:42):

I'm coming to the conclusion that what we probably want is a has_inverse x x_inv proposition; the reason we don't have it already is probably that it's mostly useless in commutative setting where you can just write x * x_inv = 1

Jireh Loreaux (Jun 08 2023 at 14:03):

I think you're right. I ran into this kind of headache on the way to proving the Gelfand formula for the spectral radius, but at the time I didn't recognize that this missing API was the problem. I think I got around it by defining a new Units term and then using that, but the details are a bit murky.

Yury G. Kudryashov (Jun 08 2023 at 15:04):

Should we introduce something between DivInvMonoid (fixed) and DivisionMonoid instead assuming just ∀ a b, a * b = 1 → b * a = 1 → a⁻¹ = b?

Yury G. Kudryashov (Jun 08 2023 at 15:04):

And use a⁻¹ everywhere?

Eric Wieser (Jun 08 2023 at 15:05):

I think we'd want has_inverse first

Jireh Loreaux (Jun 08 2023 at 15:05):

What is a docs4#DIvInvMonoid ?

Eric Wieser (Jun 08 2023 at 15:06):

It's a monoid with the obvious link between div and inv and nothing else integer powers

Eric Wieser (Jun 08 2023 at 15:07):

docs4#DivInvMonoid

Yury G. Kudryashov (Jun 08 2023 at 15:11):

Another approach is to add this axiom to DivInvMonoid.

Yury G. Kudryashov (Jun 08 2023 at 15:11):

I guess, this is true in all instances.

Yaël Dillies (Jun 08 2023 at 15:36):

We already had this discussion, and the upshot is that it's not so obvious.

Yury G. Kudryashov (Jun 08 2023 at 15:53):

Could you give a link (or an example), please?

Kevin Buzzard (Jun 08 2023 at 22:24):

Were n*n matrices a problem?

Yury G. Kudryashov (Jun 08 2023 at 22:43):

What's wrong with matrices? The Inv.inv (with any reasonable definition) agrees with Unit.val/Unit.inv.

Eric Wieser (Jun 08 2023 at 23:08):

Yury, I suspect the answer is that your generalization can't actually be used in many existing lemmas because it doesn't specify the junk value for non-invertible elements

Yury G. Kudryashov (Jun 09 2023 at 00:24):

But it can be used in all lemmas that only deal with invertible elements (e.g., smoothness of Ring.inverse).

Alex J. Best (Jun 10 2023 at 09:36):

I sketched some of the basic theory of the left inverse implies right inverse typeclass here https://github.com/leanprover-community/mathlib/pull/11376 but it is quite old!


Last updated: Dec 20 2023 at 11:08 UTC