l3lab/ntp-mathlib huggingface dataset:
/-- Equivalence $(M/W)^* \approx \operatorname{ann}(W)$. That is, there is a one-to-one
correspondence between the dual of `M ⧸ W` and those elements of the dual of `M` that
vanish on `W`.

The inverse of this is `Submodule.dualCopairing`. -/
def dualQuotEquivDualAnnihilator (W : Submodule R M) :
    Module.Dual R (M ⧸ W) ≃ₗ[R] W.dualAnnihilator :=
  LinearEquiv.ofLinear
    (W.mkQ.dualMap.codRestrict W.dualAnnihilator fun φ =>
-- Porting note: broken dot notation lean4#1910 LinearMap.mem_range_self
      W.range_dualMap_mkQ_eq ▸ LinearMap.mem_range_self W.mkQ.dualMap φ)
    W.dualCopairing (by ext; 

Proof state:
case h.a.h
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
W : Submodule R M
x✝¹ : ↥(dualAnnihilator W)
x✝ : M
⊢ ↑((LinearMap.comp
            (LinearMap.codRestrict (dualAnnihilator W) (LinearMap.dualMap (mkQ W))
              (_ : ∀ (φ : Dual R (M ⧸ W)), (LinearMap.dualMap (mkQ W)) φ ∈ dualAnnihilator W))
            (dualCopairing W))
          x✝¹)
      x✝ =
    ↑(LinearMap.id x✝¹) x✝

Next tactic:
rfl
/-- Equivalence $(M/W)^* \approx \operatorname{ann}(W)$. That is, there is a one-to-one
correspondence between the dual of `M ⧸ W` and those elements of the dual of `M` that
vanish on `W`.

The inverse of this is `Submodule.dualCopairing`. -/
def dualQuotEquivDualAnnihilator (W : Submodule R M) :
    Module.Dual R (M ⧸ W) ≃ₗ[R] W.dualAnnihilator :=
  LinearEquiv.ofLinear
    (W.mkQ.dualMap.codRestrict W.dualAnnihilator fun φ =>
-- Porting note: broken dot notation lean4#1910 LinearMap.mem_range_self
      W.range_dualMap_mkQ_eq ▸ LinearMap.mem_range_self W.mkQ.dualMap φ)
    W.dualCopairing (by ext; rfl) (by 

Proof state:
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
W : Submodule R M
⊢ LinearMap.comp (dualCopairing W)
      (LinearMap.codRestrict (dualAnnihilator W) (LinearMap.dualMap (mkQ W))
        (_ : ∀ (φ : Dual R (M ⧸ W)), (LinearMap.dualMap (mkQ W)) φ ∈ dualAnnihilator W)) =
    LinearMap.id

Next tactic:
ext
/-- Equivalence $(M/W)^* \approx \operatorname{ann}(W)$. That is, there is a one-to-one
correspondence between the dual of `M ⧸ W` and those elements of the dual of `M` that
vanish on `W`.

The inverse of this is `Submodule.dualCopairing`. -/
def dualQuotEquivDualAnnihilator (W : Submodule R M) :
    Module.Dual R (M ⧸ W) ≃ₗ[R] W.dualAnnihilator :=
  LinearEquiv.ofLinear
    (W.mkQ.dualMap.codRestrict W.dualAnnihilator fun φ =>
-- Porting note: broken dot notation lean4#1910 LinearMap.mem_range_self
      W.range_dualMap_mkQ_eq ▸ LinearMap.mem_range_self W.mkQ.dualMap φ)
    W.dualCopairing (by ext; rfl) (by ext; 

Proof state:
case h.h.h
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
W : Submodule R M
x✝¹ : Dual R (M ⧸ W)
x✝ : M
⊢ ((LinearMap.comp (dualCopairing W)
            (LinearMap.codRestrict (dualAnnihilator W) (LinearMap.dualMap (mkQ W))
              (_ : ∀ (φ : Dual R (M ⧸ W)), (LinearMap.dualMap (mkQ W)) φ ∈ dualAnnihilator W)))
          x✝¹ ∘ₗ
        mkQ W)
      x✝ =
    (LinearMap.id x✝¹ ∘ₗ mkQ W) x✝

Next tactic:
rfl
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  

Proof state:
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
⊢ range (dualMap f) = dualAnnihilator (ker f)

Next tactic:
have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    

Proof state:
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
⊢ Function.Surjective ⇑(rangeRestrict f)

Next tactic:
rw [← range_eq_top, range_rangeRestrict]
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  

Proof state:
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
⊢ range (dualMap f) = dualAnnihilator (ker f)

Next tactic:
have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  

Proof state:
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ range (dualMap f) = dualAnnihilator (ker f)

Next tactic:
convert this using 1
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  convert this using 1
  -- Porting note: broken dot notation lean4#1910
  · 

Proof state:
case h.e'_2
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ range (dualMap f) = range (dualMap (rangeRestrict f))

Next tactic:
change range ((range f).subtype.comp f.rangeRestrict).dualMap = _
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  convert this using 1
  -- Porting note: broken dot notation lean4#1910
  · change range ((range f).subtype.comp f.rangeRestrict).dualMap = _
    

Proof state:
case h.e'_2
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ range (dualMap (Submodule.subtype (range f) ∘ₗ rangeRestrict f)) = range (dualMap (rangeRestrict f))

Next tactic:
rw [← dualMap_comp_dualMap, range_comp_of_range_eq_top]
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  convert this using 1
  -- Porting note: broken dot notation lean4#1910
  · change range ((range f).subtype.comp f.rangeRestrict).dualMap = _
    rw [← dualMap_comp_dualMap, range_comp_of_range_eq_top]
    

Proof state:
case h.e'_2.hf
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ range (dualMap (Submodule.subtype (range f))) = ⊤

Next tactic:
rwa [range_eq_top]
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  convert this using 1
  -- Porting note: broken dot notation lean4#1910
  · change range ((range f).subtype.comp f.rangeRestrict).dualMap = _
    rw [← dualMap_comp_dualMap, range_comp_of_range_eq_top]
    rwa [range_eq_top]
  · 

Proof state:
case h.e'_3
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ dualAnnihilator (ker f) = dualAnnihilator (ker (rangeRestrict f))

Next tactic:
apply congr_arg
theorem range_dualMap_eq_dualAnnihilator_ker_of_subtype_range_surjective (f : M →ₗ[R] M')
    (hf : Function.Surjective f.range.subtype.dualMap) :
    LinearMap.range f.dualMap = f.ker.dualAnnihilator := by
  have rr_surj : Function.Surjective f.rangeRestrict := by
    rw [← range_eq_top, range_rangeRestrict]
  have := range_dualMap_eq_dualAnnihilator_ker_of_surjective f.rangeRestrict rr_surj
  convert this using 1
  -- Porting note: broken dot notation lean4#1910
  · change range ((range f).subtype.comp f.rangeRestrict).dualMap = _
    rw [← dualMap_comp_dualMap, range_comp_of_range_eq_top]
    rwa [range_eq_top]
  · apply congr_arg
    

Proof state:
case h.e'_3.h
R : Type u_1
M : Type u_2
M' : Type u_3
inst✝⁴ : CommRing R
inst✝³ : AddCommGroup M
inst✝² : Module R M
inst✝¹ : AddCommGroup M'
inst✝ : Module R M'
f : M →ₗ[R] M'
hf : Function.Surjective ⇑(dualMap (Submodule.subtype (range f)))
rr_surj : Function.Surjective ⇑(rangeRestrict f)
this : range (dualMap (rangeRestrict f)) = dualAnnihilator (ker (rangeRestrict f))
⊢ ker f = ker (rangeRestrict f)

Next tactic:
exact (ker_rangeRestrict f).symm
/-- For vector spaces, `f.dualMap` is surjective if and only if `f` is injective -/
@[simp]
theorem dualMap_surjective_iff {f : V₁ →ₗ[K] V₂} :
    Function.Surjective f.dualMap ↔ Function.Injective f := by
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
f : V₁ →ₗ[K] V₂
⊢ Function.Surjective ⇑(dualMap f) ↔ Function.Injective ⇑f

Next tactic:
rw [← LinearMap.range_eq_top, range_dualMap_eq_dualAnnihilator_ker,
      ← Submodule.dualAnnihilator_bot, Subspace.dualAnnihilator_inj, LinearMap.ker_eq_bot]
theorem dualPairing_eq (W : Subspace K V₁) :
    W.dualPairing = W.quotAnnihilatorEquiv.toLinearMap := by
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ Submodule.dualPairing W = ↑(quotAnnihilatorEquiv W)

Next tactic:
ext
theorem dualPairing_eq (W : Subspace K V₁) :
    W.dualPairing = W.quotAnnihilatorEquiv.toLinearMap := by
  ext
  

Proof state:
case h.h.h
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
x✝¹ : Dual K V₁
x✝ : ↥W
⊢ ((Submodule.dualPairing W ∘ₗ mkQ (dualAnnihilator W)) x✝¹) x✝ =
    ((↑(quotAnnihilatorEquiv W) ∘ₗ mkQ (dualAnnihilator W)) x✝¹) x✝

Next tactic:
rfl
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.Nondegenerate (Submodule.dualPairing W)

Next tactic:
constructor
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · 

Proof state:
case left
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.SeparatingLeft (Submodule.dualPairing W)

Next tactic:
rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
    

Proof state:
case left
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.ker ↑(quotAnnihilatorEquiv W) = ⊥

Next tactic:
apply LinearEquiv.ker
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
    apply LinearEquiv.ker
  · 

Proof state:
case right
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.SeparatingRight (Submodule.dualPairing W)

Next tactic:
intro x h
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
    apply LinearEquiv.ker
  · intro x h
    

Proof state:
case right
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
x : ↥W
h : ∀ (x_1 : Dual K V₁ ⧸ dualAnnihilator W), ((Submodule.dualPairing W) x_1) x = 0
⊢ x = 0

Next tactic:
rw [← forall_dual_apply_eq_zero_iff K x]
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
    apply LinearEquiv.ker
  · intro x h
    rw [← forall_dual_apply_eq_zero_iff K x]
    

Proof state:
case right
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
x : ↥W
h : ∀ (x_1 : Dual K V₁ ⧸ dualAnnihilator W), ((Submodule.dualPairing W) x_1) x = 0
⊢ ∀ (φ : Dual K ↥W), φ x = 0

Next tactic:
intro φ
theorem dualPairing_nondegenerate (W : Subspace K V₁) : W.dualPairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualPairing_eq]
    apply LinearEquiv.ker
  · intro x h
    rw [← forall_dual_apply_eq_zero_iff K x]
    intro φ
    

Proof state:
case right
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
x : ↥W
h : ∀ (x_1 : Dual K V₁ ⧸ dualAnnihilator W), ((Submodule.dualPairing W) x_1) x = 0
φ : Dual K ↥W
⊢ φ x = 0

Next tactic:
simpa only [Submodule.dualPairing_apply, dualLift_of_subtype] using
      h (Submodule.Quotient.mk (W.dualLift φ))
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.Nondegenerate (dualCopairing W)

Next tactic:
constructor
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · 

Proof state:
case left
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.SeparatingLeft (dualCopairing W)

Next tactic:
rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
    

Proof state:
case left
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.ker ↑(LinearEquiv.symm (dualQuotEquivDualAnnihilator W)) = ⊥

Next tactic:
apply LinearEquiv.ker
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
    apply LinearEquiv.ker
  · 

Proof state:
case right
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
⊢ LinearMap.SeparatingRight (dualCopairing W)

Next tactic:
rintro ⟨x⟩
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
    apply LinearEquiv.ker
  · rintro ⟨x⟩
    

Proof state:
case right.mk
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
y✝ : V₁ ⧸ W
x : V₁
⊢ (∀ (x_1 : ↥(dualAnnihilator W)), ((dualCopairing W) x_1) (Quot.mk Setoid.r x) = 0) → Quot.mk Setoid.r x = 0

Next tactic:
simp only [Quotient.quot_mk_eq_mk, dualCopairing_apply, Quotient.mk_eq_zero]
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
    apply LinearEquiv.ker
  · rintro ⟨x⟩
    simp only [Quotient.quot_mk_eq_mk, dualCopairing_apply, Quotient.mk_eq_zero]
    

Proof state:
case right.mk
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
y✝ : V₁ ⧸ W
x : V₁
⊢ (∀ (x_1 : ↥(dualAnnihilator W)), x_1 x = 0) → x ∈ W

Next tactic:
rw [← forall_mem_dualAnnihilator_apply_eq_zero_iff, SetLike.forall]
theorem dualCopairing_nondegenerate (W : Subspace K V₁) : W.dualCopairing.Nondegenerate := by
  constructor
  · rw [LinearMap.separatingLeft_iff_ker_eq_bot, dualCopairing_eq]
    apply LinearEquiv.ker
  · rintro ⟨x⟩
    simp only [Quotient.quot_mk_eq_mk, dualCopairing_apply, Quotient.mk_eq_zero]
    rw [← forall_mem_dualAnnihilator_apply_eq_zero_iff, SetLike.forall]
    

Proof state:
case right.mk
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W : Subspace K V₁
y✝ : V₁ ⧸ W
x : V₁
⊢ (∀ (x_1 : Dual K V₁) (h : x_1 ∈ dualAnnihilator W), { val := x_1, property := h } x = 0) →
    ∀ φ ∈ dualAnnihilator W, φ x = 0

Next tactic:
exact id
theorem dualAnnihilator_inf_eq (W W' : Subspace K V₁) :
    (W ⊓ W').dualAnnihilator = W.dualAnnihilator ⊔ W'.dualAnnihilator := by
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W W' : Subspace K V₁
⊢ dualAnnihilator (W ⊓ W') = dualAnnihilator W ⊔ dualAnnihilator W'

Next tactic:
refine' le_antisymm _ (sup_dualAnnihilator_le_inf W W')
theorem dualAnnihilator_inf_eq (W W' : Subspace K V₁) :
    (W ⊓ W').dualAnnihilator = W.dualAnnihilator ⊔ W'.dualAnnihilator := by
  refine' le_antisymm _ (sup_dualAnnihilator_le_inf W W')
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W W' : Subspace K V₁
⊢ dualAnnihilator (W ⊓ W') ≤ dualAnnihilator W ⊔ dualAnnihilator W'

Next tactic:
let F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := (Submodule.mkQ W).prod (Submodule.mkQ W')
theorem dualAnnihilator_inf_eq (W W' : Subspace K V₁) :
    (W ⊓ W').dualAnnihilator = W.dualAnnihilator ⊔ W'.dualAnnihilator := by
  refine' le_antisymm _ (sup_dualAnnihilator_le_inf W W')
  let F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := (Submodule.mkQ W).prod (Submodule.mkQ W')
  -- Porting note: broken dot notation lean4#1910 LinearMap.ker
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W W' : Subspace K V₁
F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := LinearMap.prod (mkQ W) (mkQ W')
⊢ dualAnnihilator (W ⊓ W') ≤ dualAnnihilator W ⊔ dualAnnihilator W'

Next tactic:
have : LinearMap.ker F = W ⊓ W' := by simp only [LinearMap.ker_prod, ker_mkQ]
theorem dualAnnihilator_inf_eq (W W' : Subspace K V₁) :
    (W ⊓ W').dualAnnihilator = W.dualAnnihilator ⊔ W'.dualAnnihilator := by
  refine' le_antisymm _ (sup_dualAnnihilator_le_inf W W')
  let F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := (Submodule.mkQ W).prod (Submodule.mkQ W')
  -- Porting note: broken dot notation lean4#1910 LinearMap.ker
  have : LinearMap.ker F = W ⊓ W' := by 

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W W' : Subspace K V₁
F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := LinearMap.prod (mkQ W) (mkQ W')
⊢ LinearMap.ker F = W ⊓ W'

Next tactic:
simp only [LinearMap.ker_prod, ker_mkQ]
theorem dualAnnihilator_inf_eq (W W' : Subspace K V₁) :
    (W ⊓ W').dualAnnihilator = W.dualAnnihilator ⊔ W'.dualAnnihilator := by
  refine' le_antisymm _ (sup_dualAnnihilator_le_inf W W')
  let F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := (Submodule.mkQ W).prod (Submodule.mkQ W')
  -- Porting note: broken dot notation lean4#1910 LinearMap.ker
  have : LinearMap.ker F = W ⊓ W' := by simp only [LinearMap.ker_prod, ker_mkQ]
  

Proof state:
K : Type uK
inst✝⁴ : Field K
V₁ : Type uV₁
V₂ : Type uV₂
inst✝³ : AddCommGroup V₁
inst✝² : Module K V₁
inst✝¹ : AddCommGroup V₂
inst✝ : Module K V₂
W W' : Subspace K V₁
F : V₁ →ₗ[K] (V₁ ⧸ W) × V₁ ⧸ W' := LinearMap.prod (mkQ W) (mkQ W')
this : LinearMap.ker F = W ⊓ W'
⊢ dualAnnihilator (W ⊓ W') ≤ dualAnnihilator W ⊔ dualAnnihilator W'

Next tactic:
rw [← this, ← LinearMap.range_dualMap_eq_dualAnnihilator_ker]