# Zulip Chat Archive

## Stream: new members

### Topic: subtypes

#### Ashwin Iyengar (Feb 13 2021 at 16:00):

I'm a bit confused about how subtypes work. This `simp [h]`

doesn't close the subgoal:

```
import analysis.normed_space.basic
import ring_theory.power_series.basic
open filter
def mv_restricted_power_series (σ R : Type*) [normed_ring R] :=
{f : mv_power_series σ R // tendsto f cofinite (nhds 0)}
namespace mv_restricted_power_series
variables (σ R : Type*) [normed_ring R]
instance : has_zero (mv_restricted_power_series σ R) := {zero := ⟨mv_power_series.has_zero.zero, tendsto_const_nhds⟩}
theorem exists_max (f : mv_restricted_power_series σ R) : ∃ x, ∀ y, norm (f.val x) ≥ norm (f.val y) :=
begin
cases em (f = 0),
{
simp [h],
},
{}
end
```

But if I change `cases em (f = 0)`

to `cases em (f.val = 0)`

it does close the goal. I don't get why this makes such a big difference; wouldn't it be more natural to write `f = 0`

?

#### Kevin Buzzard (Feb 13 2021 at 16:06):

what imports do I need to make this a #mwe?

#### Ashwin Iyengar (Feb 13 2021 at 16:07):

Edited, added them.

#### Kevin Buzzard (Feb 13 2021 at 16:09):

I think there are some simp lemmas missing.

#### Ashwin Iyengar (Feb 13 2021 at 16:10):

Some simp lemmas that need to be written for `mv_restricted_power_series`

? Or things missing from one of the imports?

#### Kevin Buzzard (Feb 13 2021 at 16:11):

If `h : f = 0`

then `simp [h]`

takes you to `0.val x`

and then to `↑0 x`

and then it gets stuck

#### Ashwin Iyengar (Feb 13 2021 at 16:13):

If `h : f.val = 0`

then the simp does a `pi.zero_apply`

, which I guess it doesn't want to do if `h : f = 0`

#### Kevin Buzzard (Feb 13 2021 at 16:17):

```
instance : has_coe (mv_restricted_power_series σ R) (mv_power_series σ R) :=
⟨subtype.val⟩
@[simp, norm_cast] lemma coe_zero :
((0 : mv_restricted_power_series σ R) : mv_power_series σ R) = 0 := rfl
theorem exists_max (f : mv_restricted_power_series σ R) : ∃ x, ∀ y, norm (f.val x) ≥ norm (f.val y) :=
begin
cases em (f = 0),
{ simp [h] }, -- works now
{ sorry }
end
```

#### Ashwin Iyengar (Feb 13 2021 at 16:18):

ahh amazing, thanks!

#### Kevin Buzzard (Feb 13 2021 at 16:18):

`simp`

changes `f.val`

into `↑f`

it seems, so you need a simp lemma saying that the coerced 0 is still 0.

#### Ashwin Iyengar (Feb 13 2021 at 16:19):

Right ok that makes sense

#### Kevin Buzzard (Feb 13 2021 at 16:19):

The trick with these things is to look at the goal left by `simp`

(in this case one involving `↑0`

) and to ask "why did it not get any further than this?" and the answer is that the lemma you want to apply next (namely `↑0 = 0`

) is not there.

#### Kevin Buzzard (Feb 13 2021 at 16:20):

Making definitions is much harder than proving theorems, but after a while you just pick up the tricks of what needs to be done.

#### Kevin Buzzard (Feb 13 2021 at 16:32):

```
-- don't want them to be explicit for this next function
variables {σ R}
open mv_power_series
def coeff (f : mv_restricted_power_series σ R) (c : σ →₀ ℕ) : R :=
coeff R c (f : mv_power_series σ R)
@[ext] lemma ext (f g : mv_restricted_power_series σ R)
(h : ∀ (c : σ →₀ ℕ), coeff f c = coeff g c) : f = g := by {ext c, exact h c }
lemma ext_iff (f g : mv_restricted_power_series σ R) :
f = g ↔ ∀ (c : σ →₀ ℕ), coeff f c = coeff g c :=
⟨λ h _, h ▸ rfl, ext f g⟩
theorem exists_max (f : mv_restricted_power_series σ R) : ∃ x, ∀ y, norm (f.val x) ≥ norm (f.val y) :=
begin
cases em (f = 0),
{ simp [h] }, -- works now
{ rw ext_iff at h,
push_neg at h,
sorry }
end
```

@Ashwin Iyengar that's how I'd start. Any new type needs an extensionality lemma.

#### Ashwin Iyengar (Feb 13 2021 at 16:35):

Nice, thanks

#### Kevin Buzzard (Feb 13 2021 at 16:36):

In fact looking at `mv_power_series`

the `coeff`

function should be an R-module hom really.

#### Kevin Buzzard (Feb 13 2021 at 16:37):

so another thing you might want to do is to make `mv_restricted_power_series σ R`

into an `R`

-module and then beef up the definition of `coeff`

#### Kevin Buzzard (Feb 13 2021 at 16:41):

Of course it's entirely up to you how you do this. If you're thinking about a PR (and you perhaps should be) then you might want to look at the beginning of the power series file in mathlib, where they do exactly what I decided was the best idea -- give it an R-module structure and then define coeff and ext. They also defined monomial. These things will ultimately make your life easier in the end.

#### Ashwin Iyengar (Feb 13 2021 at 16:43):

Cool yeah that's basically what I was planning to do after getting to grips with the library a bit more

#### Kevin Buzzard (Feb 13 2021 at 16:44):

I know several cases of beginners who have started exactly this way -- made a PR to mathlib generalising something which was already there.

#### Kevin Buzzard (Feb 13 2021 at 16:44):

Ashvni (who you know), Amelia and Jason (both IC UGs) all started like this.

#### Kevin Buzzard (Feb 13 2021 at 16:45):

If you do nothing more than to make it a ring under the assumption of the ultrametric inequality then this is already really useful. Then you come back to look at it 3 months later and other random people have built on top of it. It's the way the library grows.

#### Ashwin Iyengar (Feb 15 2021 at 12:38):

I would expect that in my instantiation of an `add_comm_group`

, I should just be able to do `rfl`

for `add_assoc`

because I added a `coe_add`

but it doesn't work. Is there a simple way to prove `add_assoc`

which essentially just says "it's true because it's true for the supertype"?

```
import analysis.normed_space.basic
import ring_theory.power_series.basic
open filter
noncomputable theory
def mv_restricted_power_series (σ R : Type*) [normed_field R] :=
{f : mv_power_series σ R // tendsto f cofinite (nhds 0)}
namespace mv_restricted_power_series
variables {σ R : Type*} [normed_field R]
instance : has_coe (mv_restricted_power_series σ R) (mv_power_series σ R) :=
⟨subtype.val⟩
instance : has_zero (mv_restricted_power_series σ R) := {zero := ⟨mv_power_series.has_zero.zero, tendsto_const_nhds⟩}
@[simp, norm_cast] lemma coe_zero :
((0 : mv_restricted_power_series σ R) : mv_power_series σ R) = 0 := rfl
def add (f g : mv_restricted_power_series σ R) : mv_restricted_power_series σ R :=
⟨f.val + g.val, by simpa using tendsto.add f.2 g.2⟩
@[simp, norm_cast] lemma coe_add (a b : mv_restricted_power_series σ R) :
((a.add b) : mv_power_series σ R) = (a : mv_power_series σ R) + (b : mv_power_series σ R) := rfl
def neg (f : mv_restricted_power_series σ R) : mv_restricted_power_series σ R :=
⟨-f.val,
begin
have f_conv := metric.tendsto_nhds.1 f.2,
simp at f_conv,
rw metric.tendsto_nhds,
simp,
exact f_conv,
end⟩
instance : add_comm_group (mv_restricted_power_series σ R) :=
{
add := add,
add_assoc := rfl,
zero := 0,
zero_add := sorry,
add_zero := sorry,
neg := neg,
add_left_neg := sorry,
add_comm := sorry,
}
```

#### Ruben Van de Velde (Feb 15 2021 at 13:05):

One thing that seems to work is

```
add_assoc := by { rintros ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩, simp only [add, subtype.mk_eq_mk], apply add_assoc, },
```

#### Eric Wieser (Feb 15 2021 at 13:28):

`ext`

ought to help here too, after those rintros

#### Kevin Buzzard (Feb 15 2021 at 13:32):

` add_assoc := λ _ _ _, by {ext, simp only [add_assoc, coe_add]},`

would be the way I would prove it. `by {ext, simp [add_assoc]} works too, but is slower. Note that `

add_assoc` is not a `

simp` lemma so needs to be added explicitly.

#### Kevin Buzzard (Feb 15 2021 at 13:33):

`rfl`

means "this is true by definition, if you unfold all the definitions", so it won't work here, because if you unfold all the definitions you are reduced to associativity of addition for multivariable power series, which is true, but not true by definition -- it's true because of a theorem (in fact an axiom, namely associativity of addition on R).

#### Ashwin Iyengar (Feb 15 2021 at 13:37):

Thanks all! `ext`

was the key that I wasn't aware existed.

#### Kevin Buzzard (Feb 15 2021 at 13:48):

The `ext`

lemma we proved earlier was tagged with `@[ext]`

, which means that the `ext`

tactic will use it. But actually I guess the reason it's working without that ext lemma is that the `ext`

tactic is using `subtype.ext`

and then the `ext`

lemma for `mv_power_series`

.

Here's a proof of `neg`

which just says "f.2 says some filter tends to 0, so the corresponding filter for -f tends to -0".

```
def neg (f : mv_restricted_power_series σ R) : mv_restricted_power_series σ R :=
⟨-f.val,
begin
convert tendsto.comp continuous_at_neg f.2,
exact neg_zero.symm,
end⟩
```

#### Eric Wieser (Feb 15 2021 at 13:50):

The implementation for something like src#submonoid.to_monoid ought to show how `ext`

is normally used here

#### Kevin Buzzard (Feb 15 2021 at 13:50):

```
add_assoc := λ _ _ _, by {show_term{ext}, simp only [add_assoc, coe_add]},
```

shows that it's using subtype.ext and then the ext lemma for mv power series

#### Eric Wieser (Feb 15 2021 at 13:51):

Actually it seems like `ext`

isn't used at all, and the proofs are just transferred across the injectivity of `coe`

, using docs#function.injective.add_comm_monoid

#### Eric Wieser (Feb 15 2021 at 13:57):

```
lemma coe_injective : function.injective (coe : mv_restricted_power_series σ R → mv_power_series σ R) :=
subtype.coe_injective
instance : has_add (mv_restricted_power_series σ R) := ⟨add⟩
instance : has_neg (mv_restricted_power_series σ R) := ⟨neg⟩
instance : add_comm_group (mv_restricted_power_series σ R) :=
coe_injective.add_comm_group _ rfl (λ x y, rfl) (λ x, rfl)
```

#### Eric Wieser (Feb 15 2021 at 13:58):

There's the `rfl`

s you were hoping to see

#### Kevin Buzzard (Feb 15 2021 at 13:58):

How can this work? One has to check that the subtype condition is preserved by addition and negation.

#### Eric Wieser (Feb 15 2021 at 13:59):

That's why you have to define` add`

and `neg`

yourself

#### Kevin Buzzard (Feb 15 2021 at 13:59):

Oh I see, you use the contructions of add and neg

#### Eric Wieser (Feb 15 2021 at 14:00):

(deleted)

#### Kevin Buzzard (Feb 15 2021 at 14:02):

```
def neg (f : mv_restricted_power_series σ R) : mv_restricted_power_series σ R :=
⟨-f.val, by simpa using tendsto.comp continuous_at_neg f.2⟩
```

#### Kevin Buzzard (Feb 15 2021 at 14:03):

```
def neg (f : mv_restricted_power_series σ R) : mv_restricted_power_series σ R :=
⟨-f.val, by simpa using tendsto.neg f.2⟩
```

#### Kevin Buzzard (Feb 15 2021 at 14:04):

No need to ever translate into epsilons.

#### Ashwin Iyengar (Feb 15 2021 at 18:09):

Thanks. This code works:

```
instance : has_scalar R (mv_restricted_power_series σ R) :=
{smul := λ r f, ⟨r • f.val, by simpa using tendsto.comp ((mul_left_continuous r).tendsto 0) f.property⟩}
```

but now I want to use docs#function.injective.semimodule to show that restricted power series are an R-module. The problem is that it's not clear that `coe`

is an additive monoid homomorphism. My understanding is that morphisms should be bundled. So what's the best way of stating that `coe`

is a homomorphism? Then I can do something like

```
instance : semimodule R (mv_restricted_power_series σ R) :=
coe_injective.semimodule _ ???
```

#### Kevin Buzzard (Feb 15 2021 at 18:14):

```
def coe_hom : mv_restricted_power_series σ R →+ mv_power_series σ R :=
{ to_fun := coe,
map_zero' := coe_zero,
map_add' := coe_add }
```

#### Ashwin Iyengar (Feb 15 2021 at 18:15):

Ah ok so treat it as a separate thing. Thanks!

#### Kevin Buzzard (Feb 15 2021 at 18:15):

yeah it's kind of annoying. In Lean 4 we might be able to go back to the `instance : is_add_monoid_hom coe`

approach but in Lean 3 the separate thing approach turned out to be nicer.

#### Kevin Buzzard (Feb 15 2021 at 18:21):

```
instance : semimodule R (mv_restricted_power_series σ R) :=
function.injective.semimodule R coe_hom coe_hom_injective (λ c x, rfl)
```

works but I can't get `coe_hom_injective.semimodule ...`

to work.

#### Ashwin Iyengar (Feb 15 2021 at 18:23):

Yeah I guess `coe_injective`

doesn't know that `coe`

is a hom

#### Eric Wieser (Feb 15 2021 at 18:24):

Can you post an updated #mwe?

#### Ashwin Iyengar (Feb 15 2021 at 18:24):

```
import analysis.normed_space.basic
import ring_theory.power_series.basic
open filter
noncomputable theory
def mv_restricted_power_series (σ R : Type*) [normed_ring R] :=
{f : mv_power_series σ R // tendsto f cofinite (nhds 0)}
namespace mv_restricted_power_series
variables {σ R : Type*} [normed_field R]
instance : has_coe (mv_restricted_power_series σ R) (mv_power_series σ R) :=
⟨subtype.val⟩
lemma coe_injective : function.injective (coe : mv_restricted_power_series σ R → mv_power_series σ R) :=
subtype.coe_injective
instance : has_add (mv_restricted_power_series σ R) :=
⟨λ f g, ⟨f.val + g.val, by simpa using tendsto.add f.2 g.2⟩⟩
instance : has_zero (mv_restricted_power_series σ R) := ⟨⟨0, tendsto_const_nhds⟩⟩
instance : has_neg (mv_restricted_power_series σ R) :=
⟨λ f, ⟨-f.val, by simpa using tendsto.neg f.2⟩⟩
instance : has_scalar R (mv_restricted_power_series σ R) :=
{smul := λ r f, ⟨r • f.val, by simpa using tendsto.comp ((mul_left_continuous r).tendsto 0) f.property⟩}
instance : add_comm_group (mv_restricted_power_series σ R) :=
coe_injective.add_comm_group _ rfl (λ x y, rfl) (λ x, rfl)
def coe_hom : mv_restricted_power_series σ R →+ mv_power_series σ R :=
{ to_fun := coe,
map_zero' := rfl,
map_add' := λ _ _, rfl}
instance : semimodule R (mv_restricted_power_series σ R) :=
function.injective.semimodule R coe_hom coe_injective (λ _ _, rfl)
```

#### Kevin Buzzard (Feb 15 2021 at 18:28):

Hey, nice `has_scalar.smul`

proof :-) You can use `f.2`

for `f.property`

if you want (and `f.1`

for `f.val`

) -- but I'm not sure that there is any particular preference for one over the other, I'm just noting this general trick of X.1 and X.2 which works in large generality. Note that X.3 doesn't work :-( (in situations where it's meaningful, e.g. if X is a proof of P and Q and R) -- it's X.2.2.

#### Ashwin Iyengar (Feb 15 2021 at 18:32):

Ok, yeah either way I should probably pick a convention and stick to it

#### Ashwin Iyengar (Feb 15 2021 at 18:32):

I sort of like `f.val`

and `f.property`

because they're less ambiguous, but maybe that hasn't been the design principle?

#### Kevin Buzzard (Feb 15 2021 at 18:33):

No, I agree about the lack of ambiguity, stick with them, I was just pointing out a trick.

#### Kevin Buzzard (Feb 15 2021 at 18:34):

Honestly, the only reason I said it was that

```
{smul := λ r f, ⟨r • f.val, by simpa using tendsto.comp ((mul_left_continuous r).tendsto 0) f.property⟩}
```

is over 100 characters long, and there's a 100 character limit per line in mathlib, and this trick got it back down to under 100 characters :-)

#### Ashwin Iyengar (Feb 15 2021 at 18:35):

Ah good to know

#### Eric Wieser (Feb 15 2021 at 18:42):

Usually coercion is used as the simp-normal form in place of `.val`

or `.1`

#### Eric Wieser (Feb 15 2021 at 18:43):

That is,

```
instance : has_scalar R (mv_restricted_power_series σ R) :=
{smul := λ r f, ⟨r • f, by simpa using tendsto.comp ((mul_left_continuous r).tendsto 0) f.prop⟩}
```

#### Mario Carneiro (Feb 15 2021 at 18:46):

Note that X.3 doesn't work :-( (in situations where it's meaningful, e.g. if X is a proof of P and Q and R) -- it's X.2.2.

By the way, while I would be all for such an abbreviation, it makes X.2 ambiguous

#### Ashwin Iyengar (Feb 15 2021 at 19:44):

Also @Kevin Buzzard I would think that

```
@[ext] lemma ext (f g : mv_restricted_power_series σ R)
(h : ∀ (n : σ →₀ ℕ), f.val n = g.val n) : f = g := by {ext, exact h n}
```

is more natural than

```
@[ext] lemma ext (f g : mv_restricted_power_series σ R)
(h : ∀ (n : σ →₀ ℕ), coeff R n f = coeff R n g) : f = g := by {ext, exact h n}
```

but maybe I'm missing something? Is the idea that using `coeff`

is more elegant because it doesn't rely on the precise definition of `mv_power_series`

or `mv_restricted_power_series`

?

#### Kevin Buzzard (Feb 15 2021 at 19:52):

I am always a bit edgy about abuse of definitional equality. What you say is exactly what worries me -- but I know that some computer scientists would find your suggestion more elegant. Your code breaks if someone refactors mv_power_series to be e.g. a completion of k[sigma]/I^n with I the ideal generated by the sigma, whereas mine uses the interface so doesn't break. I've seen definitions change in the past in mathlib. On the other hand the current definition of power series seems to be one which makes everyone happy right now so maybe it's unlikely to change in future. Abuse of definitional equality is really handy when you're writing slick term mode proofs at the beginning of your API. After a while it basically becomes irrelevant though, because e.g. if you're trying to prove that Tate algebras in finitely many variables over a complete nonarchimedean normed field are Noetherian then these issues are not really the important ones -- you're going to write a long tactic proof there so who cares what the definition is under the hood.

#### Ashwin Iyengar (Feb 15 2021 at 19:55):

I see. Yeah it's the little things like this I get perplexed by, like e.g. right now I'm stuck trying to convert `hd: ¬⇑(coeff R d) f = 0`

to `hd: ¬∥f.val d∥ = 0`

#### Kevin Buzzard (Feb 15 2021 at 19:57):

You can probably `change ¬(f.val d = 0) at hd`

because these will be definitional.

#### Kevin Buzzard (Feb 15 2021 at 19:58):

But one thing you should definitely do is to decide once and for all whether you're going to refer to to the d'th coefficient of f via f.val d or coeff R d f or whatever, and then stick to your convention, and write simp lemmas changing every other convention to your convention.

#### Ashwin Iyengar (Feb 15 2021 at 20:01):

A tactic a day...

Last updated: Dec 20 2023 at 11:08 UTC