Zulip Chat Archive

Stream: maths

Topic: Construction of structures


Antoine Chambert-Loir (Sep 25 2022 at 21:53):

The following code defines an equivalence :
fin_oplus_fin_equiv_fin {m n a : ℕ} (ham : a ≤ m): (fin m) ⊕ (fin n) ≃ fin (m + n)
that inserts the fin n after place a of the given fin m.
However, to prove the right_inv property, I was forced to define the to_fun and inv_fun parts
outside of the definition of the equiv, because otherwise, Lean wouldn't simp the inner argument.
I tried some other ways, but this is the only thing I could do…

Any advice ?

def aux_i {m n a : } (ham : a  m) : (fin m)  (fin n)  fin (m + n) := λ xy,
begin
  induction xy with x y,
  { by_cases hx : (x : ) < a,
    exact fin.cast_le (le_self_add) x,
    exact fin.add_nat n x, },
  { exact fin.cast_le (add_le_add_right ham n) (fin.nat_add a y), },
end

def aux_j {m n a : } (ham : a  m): fin (m + n)  (fin m)  (fin n) := λ z,
begin
  by_cases hz : (z : ) < a,
  exact sum.inl (fin.cast_le ham z, hz⟩),
  by_cases hz' : (z : ) < a + n,
  exact sum.inr (⟨z - a,
  begin
    rw [lt_iff_not_le, nat.le_sub_iff_right (not_lt.mp hz),  lt_iff_not_le],
    rw add_comm n a, exact hz',
  end⟩),
  exact sum.inl (⟨(z : ) - n,
  begin
    rw [lt_iff_not_le, nat.le_sub_iff_right (le_trans (le_add_self) (not_lt.mp hz')),  lt_iff_not_le],
    exact z.prop,
  end⟩),
end

def fin_oplus_fin_equiv_fin {m n a : } (ham : a  m): (fin m)  (fin n)  fin (m + n) := {
to_fun := aux_i ham,
inv_fun := aux_j ham,
left_inv := λ xy,
begin
  simp only [aux_j, aux_i],
  induction xy with x y,
  { simp only [not_lt, dite_eq_ite, fin.cast_le_mk],
    split_ifs with hx,
    { simp only [fin.insert_fin, hx],
      simp only [not_lt, if_true, fin.coe_cast_le, fin.eta, dite_eq_left_iff],
      intro h, exfalso, exact not_le.mpr hx h, },
    { simp only [hx, if_false, fin.coe_add_nat, add_lt_add_iff_right, not_false_iff,
      add_tsub_cancel_right, fin.eta, dif_neg, dite_eq_right_iff],
      intro h, exfalso, apply hx, apply lt_of_le_of_lt (le_self_add) h, }, },
  { simp only [fin.coe_nat_add, fin.cast_le_mk],
    split_ifs with hy hy',
    { apply not_le.mpr hy, exact le_self_add, },
    { simp only [fin.coe_cast_le, fin.coe_nat_add, add_tsub_cancel_left, fin.eta], },
    { simp only [fin.coe_cast_le, fin.coe_nat_add, add_lt_add_iff_left] at hy',
      exact hy' y.prop, }, },
end,
right_inv := λ z,
begin
  simp only [aux_j],
  by_cases hz : (z : ) < a,
  { simp only [hz, fin.cast_le_mk, dif_pos],
    simp only [aux_i, hz, fin.coe_mk, fin.cast_le_mk, fin.eta, dite_eq_ite, if_true], },
  { by_cases hz' : (z : ) < a + n,
    { rw  subtype.coe_inj,
      simp only [hz, hz', aux_i, not_false_iff, fin.cast_le_mk, dif_pos, dif_neg, fin.nat_add_mk, fin.coe_mk],
      rw add_comm a _,
      refine nat.sub_add_cancel (not_lt.mp hz), },
    { rw  subtype.coe_inj,
      simp only [hz, hz', aux_i, not_false_iff, fin.cast_le_mk, dif_neg, fin.coe_mk, fin.add_nat_mk, dite_eq_ite],
      suffices hz'' : ¬((z : ) - n < a),
      simp only [hz'', if_false, fin.coe_mk],
      apply nat.sub_add_cancel,
      exact le_trans (le_add_self) (not_lt.mp hz'),
      intro hz'', apply hz', exact lt_add_of_tsub_lt_right hz'', }, },
end }

Antoine Chambert-Loir (Sep 25 2022 at 21:54):

(A related question would be how, in the definition of a structure, to access some already defined fields.)

Bob Bass (Sep 25 2022 at 22:02):

(deleted)

Kyle Miller (Sep 25 2022 at 22:07):

Found the mathlib definition, if you want to take a look how it was done: docs#fin_sum_fin_equiv

Junyan Xu (Sep 25 2022 at 22:21):

docs#fin_sum_fin_equiv doesn't have the flexibility of inserting at any place though. If you inline aux_i and aux_j, you just need a dsimp only to achieve (almost) the same effect as simp only [aux_i, aux_j]. I think you may also consider using docs#list.split_at, docs#list.append, docs#list.nth_le to construct the backward direction.

Eric Wieser (Sep 25 2022 at 22:31):

Inserting at any place can be achieved by composing docs#equiv.add_left on the input and output appropriately, right?

Junyan Xu (Sep 25 2022 at 22:38):

rather, docs#fin_rotate should work better (no need of abusing defeq between zmod and fin, and works for n=0)

Kyle Miller (Sep 26 2022 at 01:04):

Ah, I didn't read what the description of the needed equivalence well enough.

Kyle Miller (Sep 26 2022 at 01:04):

There are enough equivalences in mathlib to create this equivalence without needing to descend to terms:

import logic.equiv.fin
import tactic.zify
import tactic.ring

def fin_oplus_fin_equiv_fin {m n a : } (ham : a  m) :
  fin m  fin n  fin (m + n) :=
calc fin m  fin n  fin (a + (m - a))  fin n :
    equiv.sum_congr (fin.cast (by { zify [ham], ring})).to_equiv (equiv.refl _)
  ...  (fin a  fin (m - a))  fin n :
    equiv.sum_congr fin_sum_fin_equiv.symm (equiv.refl _)
  ...  fin a  (fin (m - a)  fin n) : equiv.sum_assoc _ _ _
  ...  fin a  (fin n  fin (m - a)) : equiv.sum_congr (equiv.refl _) (equiv.sum_comm _ _)
  ...  fin a  fin (n + (m - a)) :
    equiv.sum_congr (equiv.refl _) fin_sum_fin_equiv
  ...  fin (a + (n + (m - a))) : fin_sum_fin_equiv
  ...  fin (m + n) : (fin.cast (by { zify [ham], ring })).to_equiv

Ruben Van de Velde (Sep 26 2022 at 05:41):

Neat. Do you also have a proof that it is the equivalence Antoine was looking for? :)

Antoine Chambert-Loir (Sep 26 2022 at 09:46):

I am impressed by Kyle's alternative definition (in fact, calc is a tactic I'm not enough used to),.

But this does not really answer my question which was that without an external definition of aux_i, everything was expanded too soon and the compiler wasn't able to rewrite something at the required depth.
Something I would like to be able to do is to rewrite right_inv as having to proveλ z, to_fun (inv_fun z) = z, and then expanding inv_fun, etc.

Floris van Doorn (Sep 26 2022 at 11:08):

You can do something like this:

import logic.equiv.fin

def fin_oplus_fin_equiv_fin {m n a : } (ham : a  m): (fin m)  (fin n)  fin (m + n) :=
let to_fun : (fin m)  (fin n)  fin (m + n) := λ xy,
begin
  sorry
end in
let inv_fun : fin (m + n)  (fin m)  (fin n) := λ z,
begin
  sorry
end in
{
to_fun := to_fun,
inv_fun := inv_fun,
left_inv := λ xy,
begin
  sorry -- inv_fun (to_fun xy) = xy
end,
right_inv := λ z,
begin
  -- to_fun (inv_fun z) = z
  simp only [inv_fun],
  -- to_fun (expanded version) = z
  sorry
end }

Ruben Van de Velde (Sep 26 2022 at 13:38):

I nerd-sniped myself and got lost in a maze of fin casting functions, but came out with a proof that Kyle's construction works: https://gist.github.com/Ruben-VandeVelde/9af0d23e0909719f7247e740bf3efbdf


Last updated: Dec 20 2023 at 11:08 UTC