Zulip Chat Archive
Stream: new members
Topic: Transpositions of adjacent elements
Zhangir Azerbayev (Aug 03 2020 at 13:42):
Every transposition of fin n
can be decomposed into transpositions of adjacent elements. @Kevin Buzzard pointed out to me that, in some sense, mathlib already knows this, since list.perm
has the constructor
| swap : ∀ {α : Type uu} (a a_1 : list α) (l : list α), a_1 :: a :: l ~ a :: a_1 :: l
I want to extract from this either
- A lemma that says any
equiv.swap i j
withi j : fin n
can be decomposed into a product of an odd number of elements of the formequiv.swap i (i+1)
, or - An induction principle similar to
equiv.perm.swap_induction_on
, which would say something like
∀ (i j : fin n), P 1 → (∀ (f : equiv.perm (fin n)) (i : fin n),
P f → P (equiv.swap i (i + 1) * f)) → P (equiv.swap i j)
or
∀ f : equiv.perm (fin n) , P 1 → (∀ (f : equiv.perm (fin n)) (i : fin n),
P f → P (equiv.swap i (i + 1) * f)) → P f
How could one prove this?
Kevin Buzzard (Aug 03 2020 at 13:44):
Your definition of swap quoted above looks funny -- a should have type alpha surely
Kevin Buzzard (Aug 03 2020 at 13:45):
swap says that [a,b,c,...] is related to [b,a,c,...]. It's part of the definition of a binary relation on lists
Kevin Buzzard (Aug 03 2020 at 13:46):
I don't think the questions you're asking typecheck
Kevin Buzzard (Aug 03 2020 at 13:47):
Oh sorry are there two swaps here?
Kevin Buzzard (Aug 03 2020 at 13:49):
You can prove 1, assuming I've understood it correctly, directly by writing it as an explicit product of something like 2(j-i-1)+1 adjacent swaps
Zhangir Azerbayev (Aug 03 2020 at 13:51):
The definition I wrote is wrong, it's actually
| swap : Π (x y : α) (l : list α), perm (y::x::l) (x::y::l)
The thing I wrote in my question is what shows up on the mathlib docs website (https://leanprover-community.github.io/mathlib_docs/data/list/perm.html#list.is_setoid), which is different from what's in source code.
Kevin Buzzard (Aug 03 2020 at 13:52):
Your induction principle might not quite be what you want because i+1 is random if n=i+1
Kevin Buzzard (Aug 03 2020 at 13:53):
@Kendall Frey you're not the only one doing permutations of fin n
Mario Carneiro (Aug 03 2020 at 14:10):
wait, don't tell me this is the next evolution stage of the a
bug
Reid Barton (Aug 03 2020 at 14:16):
The only real problem is the {α : Type uu}
part, right?
Mario Carneiro (Aug 03 2020 at 14:23):
no, the a
and a_1
binders have the wrong type
Mario Carneiro (Aug 03 2020 at 14:25):
which, incidentally, means we have found an entirely new kind of pollack inconsistency in lean
Reid Barton (Aug 03 2020 at 14:26):
oh neat
Mario Carneiro (Aug 03 2020 at 14:36):
Oh, the bug is in doc_gen
Mario Carneiro (Aug 03 2020 at 14:38):
Here's a standalone version of the bug:
import tactic.basic data.list.perm
open tactic
meta def expr.instantiate_pis : list expr → expr → expr
| (e'::es) (expr.pi n bi t e) := expr.instantiate_pis es (e.instantiate_var e')
| _ e := e
meta def mk_const_with_params (d : declaration) : expr :=
let lvls := d.univ_params.map level.param in
expr.const d.to_name lvls
#eval do
d ← get_decl ``list.perm,
t ← infer_type (mk_const_with_params d),
(locs, _) ← mk_local_pis t,
trace (list.map expr.local_type locs), -- [Type uu, list α, list α]
proj_tp ← mk_const ``list.perm.swap >>= infer_type,
t ← pis locs (proj_tp.instantiate_pis locs),
trace t -- ∀ {α : Type uu} (a a_1 : list α) (l : list α), a_1 :: a :: l ~ a :: a_1 :: l
Mario Carneiro (Aug 03 2020 at 14:40):
It looks like the code has assumed that the first three binders of list.perm.swap
have types [Type uu, list α, list α]
respectively
Mario Carneiro (Aug 03 2020 at 14:41):
in other words, it confused the indices of the inductive family inductive list.perm {α : Type uu} : list α → list α → Prop
for parameters
Kevin Buzzard (Aug 03 2020 at 18:13):
Back to the original point, Zhangir Chris and I knocked up the following in the live lean coding discord chat:
import data.equiv.basic
import group_theory.perm.sign
import tactic
open equiv
lemma zhangir1 (n : ℕ) (i j : fin n) (hij : i ≠ j) : ∃ L : list (perm (fin n)),
L.prod = equiv.swap i j ∧ ∀ l ∈ L, ∃ k : fin n, ∃ h : k.1 + 1 < n,
l = equiv.swap k ⟨k.1 + 1, h⟩ :=
begin
wlog h1 : i < j using [i j, j i],
{ rcases lt_trichotomy i j with h1 | rfl | h3,
{ left, assumption },
{ exfalso, cc },
{ right, assumption }
},
{ cases j with j hj,
cases i with i hi,
change i < j at h1,
clear hij,
revert hj,
refine nat.le_induction _ _ j (show i+1 ≤ j, from h1),
{ intro hj,
let L := [swap (⟨i, hi⟩ : fin n) ⟨i + 1, hj⟩],
use L,
split, simp,
intros l hl,
use i, exact hi,
use hj,
cases hl, exact hl,
cases hl },
{ clear h1 j,
intros j hj h hj',
have hjn : j < n := lt_trans (nat.lt_succ_self j) hj',
rcases h hjn with ⟨L, hL1, hL2⟩,
let M := [swap (⟨j, hjn⟩ : fin n) (⟨j+1, hj'⟩)] ++ L ++ [swap (⟨j, hjn⟩ : fin n) (⟨j+1, hj'⟩)],
use M,
split,
{ rw [list.prod_append, list.prod_append, hL1, list.prod_singleton],
rw swap_comm (fin.mk i _) (fin.mk (j+1) _),
apply perm.swap_mul_swap_mul_swap,
{ apply ne_of_lt, exact hj },
{ apply ne_of_lt, show i < j+1, linarith } },
{ intros l hl,
rw [list.mem_append, list.mem_append, list.mem_singleton] at hl,
rcases hl with ⟨rfl | hl⟩ | rfl,
{ use fin.mk j hjn, use hj' },
{ apply hL2, assumption, },
{ use fin.mk j hjn, use hj' } } } },
{ symmetry' at hij,
rw swap_comm,
exact this hij }
end
variables (α : Type*) [decidable_eq α]
@[elab_as_eliminator] lemma zhangir2 [fintype α] {P : perm α → Prop} (f : perm α) :
P 1 → (∀ f x y, x ≠ y → P f → P (f * swap x y)) → P f :=
begin
intros h1 IH,
let Q : perm α → Prop := λ f, P (f⁻¹),
suffices hQ : ∀ f, Q f,
{ convert hQ f⁻¹,
simp,
},
intro f,
apply perm.swap_induction_on,
{ exact h1 },
intros f x y h hQ,
show P (((swap x y) * f)⁻¹),
rw [mul_inv_rev, swap_inv],
apply IH,
exact h,
exact hQ,
end
Yakov Pechersky (Aug 03 2020 at 22:38):
Relatedly, why is equiv.swap_apply_of_ne_of_ne
not a simp lemma?
Scott Morrison (Aug 03 2020 at 22:54):
src#equiv.swap_apply_of_ne_of_ne
Scott Morrison (Aug 03 2020 at 22:55):
(Just using the linkifier so I can see what it says.)
Scott Morrison (Aug 03 2020 at 22:55):
Presumably because it seems unlikely the side conditions will be discharged by simp
, so we wouldn't gain much.
Scott Morrison (Aug 03 2020 at 22:55):
simp
is not much good at proving inequalities.
Yakov Pechersky (Aug 03 2020 at 22:59):
Here's a short example where, if tagged [simp]
, it's useful. But it's not that much of a hassle to just invoke it.
variables {R : Type*} {m n : ℕ} [field R]
variables (A : matrix (fin (n + 2)) (fin (m + 2)) R)
def swap_row (i j) : matrix (fin (n + 2)) (fin (m + 2)) R :=
A ∘ equiv.swap i j
@[simp] lemma swap_row_def (i j) :
swap_row A i j = A ∘ equiv.swap i j := rfl
lemma swap_row_symmetric (i j) :
swap_row A i j = swap_row A j i :=
begin
ext ix jx,
by_cases hj : (ix = j);
by_cases hi : (ix = i);
simp [hj, hi, equiv.swap_apply_of_ne_of_ne]
end
Scott Morrison (Aug 03 2020 at 23:10):
I think a general rule of thumb is that if in order to make simp
use some particular lemma, you're going to need to jump through hoops providing additional facts to simp so it can apply, then it doesn't merit joining the standard simp
set. Maybe another way to say the same thing is that you shouldn't have to "know about" individual simp
lemmas to successfully use simp
.
Last updated: Dec 20 2023 at 11:08 UTC