Zulip Chat Archive

Stream: Is there code for X?

Topic: Dealing with `Path.cast`


Sebastian Kumar (Aug 09 2025 at 15:49):

I was formalizing some algebraic topology and I felt I needed lemmas like:

import Mathlib

variable {X : Type*} [TopologicalSpace X]

namespace Path

theorem cast_trans_trans_cast_eq {x₀ x₁ x₂ x₃ x₄ x₅ : X} (h₀ : x₂ = x₁) (h₁ : x₃ = x₄)
    (p₀ : Path x₀ x₁) (p₁ : Path x₂ x₃) (p₂ : Path x₄ x₅) :
    ((p₀.cast rfl h₀).trans p₁).trans (p₂.cast h₁ rfl) =
      (p₀.trans (p₁.cast h₀.symm h₁.symm)).trans p₂ := by
  ext s
  simp [trans_apply]

namespace Homotopic

theorem cast_homotopic_iff_homotopic_cast {x₀ x₁ x₀' x₁' : X} {p : Path x₀ x₁} {p' : Path x₀' x₁'}
    {h₀ : x₀' = x₀} {h₁ : x₁' = x₁} :
    (p.cast h₀ h₁).Homotopic p'  p.Homotopic (p'.cast h₀.symm h₁.symm) := id, id

However, these seem unreasonably messy. I was wondering if there was a smarter way to do this. Are there any clever dependent type theory constructions to deal with casting objects between different types? My intuition is that Path.trans should only depend on the underlying path and so should be flexible with respect to casting, but I don't know how to formalize this in lean (besides what I gave above).

Aaron Liu (Aug 09 2025 at 15:51):

try rewriting?

Aaron Liu (Aug 09 2025 at 15:51):

with the dependent rewrite tactic

Aaron Liu (Aug 09 2025 at 15:52):

you can also congr!

Aaron Liu (Aug 09 2025 at 15:57):

import Mathlib

variable {X : Type*} [TopologicalSpace X]

namespace Path

@[simp]
theorem cast_rfl_rfl {x₀ x₁ : X} (p : Path x₀ x₁) : p.cast rfl rfl = p := rfl

theorem cast_trans_trans_cast_eq {x₀ x₁ x₂ x₃ x₄ x₅ : X} (h₀ : x₂ = x₁) (h₁ : x₃ = x₄)
    (p₀ : Path x₀ x₁) (p₁ : Path x₂ x₃) (p₂ : Path x₄ x₅) :
    ((p₀.cast rfl h₀).trans p₁).trans (p₂.cast h₁ rfl) =
      (p₀.trans (p₁.cast h₀.symm h₁.symm)).trans p₂ := by
  rewrite! (castMode := .all) [h₀, h₁]
  dsimp

namespace Homotopic

theorem cast_homotopic_iff_homotopic_cast {x₀ x₁ x₀' x₁' : X} {p : Path x₀ x₁} {p' : Path x₀' x₁'}
    {h₀ : x₀' = x₀} {h₁ : x₁' = x₁} :
    (p.cast h₀ h₁).Homotopic p'  p.Homotopic (p'.cast h₀.symm h₁.symm) := by
  rewrite! (castMode := .all) [h₀, h₁]
  dsimp
  rfl

Sebastian Kumar (Aug 09 2025 at 20:28):

Thanks for the suggestion. I wasn't familiar with the dependent rewrite tactic. So could something along the lines of the following be added to Mathlib? The example at the end is (more or less) what I am trying to use this for.

namespace Path

variable {X : Type*} [TopologicalSpace X] {x₀ x₀' x₁ x₁' : X}

@[simp] theorem cast_rfl_rfl (p : Path x₀ x₁) : p.cast rfl rfl = p := rfl

theorem cast_eq (h₀ : x₀' = x₀) (h₁ : x₁' = x₁) (p : Path x₀ x₁) :
    p.cast h₀ h₁ = h₀  h₁  p  := by
  rewrite! [h₀, h₁]
  rfl

theorem Homotopic.cast_iff (h₀ : x₀' = x₀) (h₁ : x₁' = x₁)
    (p : Path x₀ x₁) (p' : Path x₀' x₁') : (p.cast h₀ h₁).Homotopic p' 
      p.Homotopic (p'.cast h₀.symm h₁.symm) := by
  rewrite! (castMode := .all) [h₀, h₁]
  rfl

example {x : X} (h₀ : x₀ = x) (h₁ : x₁ = x) (p : Path x₀ x₁) (q : Path x x)
    (h : p.Homotopic (q.cast h₀ h₁)) :
    ((((refl x).cast rfl h₀).trans p).trans ((Path.refl x).cast h₁ rfl)).Homotopic q := by
  rewrite! (castMode := .all) [h₀, h₁]
  dsimp
  apply Homotopic.trans (Homotopic.trans Homotopy.transRefl _⟩ Homotopy.reflTrans _⟩)
  rw [ cast_eq h₀.symm h₁.symm, Homotopic.cast_iff]
  exact h

end Path

Another issue I ran into is that, in the code I was working with, instead of h₀ I have a proof of the form ht₀ ▸ γ.source. For some reason the tactic rewrite! (castMode := .all) [ht₀ ▸ γ.source] doesn't work and I instead have to do have := ht₀ ▸ γ.source; rewrite! (castMode := .all) [this]. Why is this?

Aaron Liu (Aug 09 2025 at 20:29):

there might be bugs

Aaron Liu (Aug 09 2025 at 20:29):

what is the error message you got?

Aaron Liu (Aug 09 2025 at 20:30):

can you provide a #mwe?

Aaron Liu (Aug 09 2025 at 20:31):

oh btw you can also just cases h₀; cases h₁ since you have a free variable on one side

Aaron Liu (Aug 09 2025 at 20:39):

import Mathlib

namespace Path

variable {X : Type*} [TopologicalSpace X] {x₀ x₀' x₁ x₁' : X}

@[simp] theorem cast_rfl_rfl (p : Path x₀ x₁) : p.cast rfl rfl = p := rfl

theorem cast_eq (h₀ : x₀' = x₀) (h₁ : x₁' = x₁) (p : Path x₀ x₁) :
    p.cast h₀ h₁ = h₀  h₁  p := by
  cases h₀; cases h₁; rfl

theorem Homotopic.cast_iff (h₀ : x₀' = x₀) (h₁ : x₁' = x₁)
    (p : Path x₀ x₁) (p' : Path x₀' x₁') : (p.cast h₀ h₁).Homotopic p' 
      p.Homotopic (p'.cast h₀.symm h₁.symm) := by
  cases h₀; cases h₁; simp

@[simp]
theorem Homotopic.trans_refl_left_iff (p q : Path x₀ x₁) :
    (p.trans (Path.refl x₁)).Homotopic q  p.Homotopic q :=
  ⟨.trans (Homotopy.transRefl p).symm, .trans Homotopy.transRefl p⟩⟩

@[simp]
theorem Homotopic.refl_trans_left_iff (p q : Path x₀ x₁) :
    ((Path.refl x₀).trans p).Homotopic q  p.Homotopic q :=
  ⟨.trans (Homotopy.reflTrans p).symm, .trans Homotopy.reflTrans p⟩⟩

@[simp]
theorem Homotopic.trans_refl_right_iff (p q : Path x₀ x₁) :
    p.Homotopic (q.trans (Path.refl x₁))  p.Homotopic q :=
  fun h => h.trans Homotopy.transRefl q, fun h => h.trans (Homotopy.transRefl q).symm⟩⟩

@[simp]
theorem Homotopic.refl_trans_right_iff (p q : Path x₀ x₁) :
    p.Homotopic ((Path.refl x₀).trans q)  p.Homotopic q :=
  fun h => h.trans Homotopy.reflTrans q, fun h => h.trans (Homotopy.reflTrans q).symm⟩⟩

example {x : X} (h₀ : x₀ = x) (h₁ : x₁ = x) (p : Path x₀ x₁) (q : Path x x)
    (h : p.Homotopic (q.cast h₀ h₁)) :
    ((((refl x).cast rfl h₀).trans p).trans ((Path.refl x).cast h₁ rfl)).Homotopic q := by
  cases h₀; cases h₁; simpa using h

end Path

Sebastian Kumar (Aug 09 2025 at 20:48):

Here is an example for the error I was talking about. For me, the first example compiles but the second gives the error tactic 'depRewrite' failed, equality or iff proof expected.

import Mathlib

variable {α : Type*} {a₀ a₁ : α} (ha : a₀ = a₁)
         {X : Type*} [TopologicalSpace X] {x y : X}
         (f : α  X) (hfa₁ : f a₁ = x) (p : Path x y)

example : p.cast (ha  hfa₁) rfl = (ha  hfa₁)  p := by
  have := ha  hfa₁
  rewrite! [this]
  rfl

example : p.cast (ha  hfa₁) rfl = (ha  hfa₁)  p := by
  rewrite! [ha  hfa₁]

Aaron Liu (Aug 09 2025 at 20:56):

regular rewrite also fails here so it's not dependent rewrite's fault

Aaron Liu (Aug 09 2025 at 20:56):

maybe it's something about the elaboration

Aaron Liu (Aug 09 2025 at 20:58):

this works

import Mathlib

variable {α : Type*} {a₀ a₁ : α} (ha : a₀ = a₁)
         {X : Type*} [TopologicalSpace X] {x y : X}
         (f : α  X) (hfa₁ : f a₁ = x) (p : Path x y)

example : p.cast (ha  hfa₁) rfl = (ha  hfa₁)  p := by
  rewrite! [(ha  hfa₁ :)]

Sebastian Kumar (Aug 09 2025 at 21:31):

thanks for the fix

Sebastian Kumar (Aug 09 2025 at 21:48):

So should I make a pull request to Mathlib for cast_rfl_rfl, and one for the trans_refl_left_iff lemmas? I am new to Mathilb so I am not quite sure: what would be the convention for giving credit in this case?

Aaron Liu (Aug 09 2025 at 21:50):

yeah sure, unless you want me to do it

Aaron Liu (Aug 09 2025 at 21:50):

you can just mention me in the PR description

Sebastian Kumar (Aug 09 2025 at 22:01):

Okay, thanks for all the help. I should be able to make the prs tomorrow. My end goal is contributing that the n-sphere is simply connected for n > 1, and now I'm getting rather close :)

Aaron Liu (Aug 09 2025 at 22:03):

ooohhh I'm interested

Wuyang (Aug 11 2025 at 19:12):

That’s a nice refinement of the casting lemmas: your approach plus cases/rewrite! makes things much cleaner. Looking forward to seeing the PRs!
(P.S. If you want to explore related lemmas or prior work, try LeanFinder at www.leanfinder.org.)

Wuyang (Aug 11 2025 at 19:12):

@leanfinder casting lemmas for Path and Homotopic in Lean with rewrite! and cases tactics

leanfinder (Aug 11 2025 at 19:12):

Here’s what I found:

Matthew Ballard (Aug 11 2025 at 19:19):

@Wuyang please stop spamming topics with leanfinder.

Wuyang (Aug 11 2025 at 19:26):

Matthew Ballard said:

Wuyang please stop spamming topics with leanfinder.

@Matthew Ballard Thank you! Sorry for sending many messages! Our team is building basic tools to help mathematicians. Hope these tools could be useful.

Matthew Ballard (Aug 11 2025 at 19:29):

That is great to hear. Please make a post on #general discussing this so people can interact in a more contained manner and use the tools themselves if they wish.

Wuyang (Aug 11 2025 at 19:44):

Matthew Ballard said:

That is great to hear. Please make a post on #general discussing this so people can interact in a more contained manner and use the tools themselves if they wish.

@Matthew Ballard Thanks! Will do!

Sebastian Kumar (Aug 11 2025 at 21:58):

By the way, here is the pull request for the sphere being simply connected #28246. My final approach for dealing with Path.cast was to use cases in the lemma cast_trans_trans_homotopic_of_homotopic_cast and combine that with a dependent rewrite in exists_loops_homotopic_foldTrans_of_open_cover (both in the file SimplyConnectedSphere). I hope that is a sensible approach, but I am of course happy to change it if not.

Kim Morrison (Aug 11 2025 at 23:22):

Matthew Ballard said:

That is great to hear. Please make a post on #general discussing this so people can interact in a more contained manner and use the tools themselves if they wish.

My suggestion is that you (or any else writing future bots) only ever summon it in "someone else's thread" if you then follow up the bot's response with something useful/constructive/introspective (e.g. "hmm, that wasn't a great answer", or "it looks to me like suggestion 3 was what you're looking for"). Putting in this effort will lessen the impression that you are summoning the bot merely to advertise it.


Last updated: Dec 20 2025 at 21:32 UTC