Zulip Chat Archive
Stream: new members
Topic: graph theory project
Lode Vermeulen (Apr 04 2024 at 13:45):
Hi! The coming months I will be working on a project where I am looking to formalize some graph theory proofs in Lean. Currently, I am still getting to know the Lean language by looking at the existing SimpleGraph directory in Mathlib. Some of these are quite intimidating, so it would really help me if someone could answer some of my questions. Should I start a conversation for my project in #graph theory ?
Damiano Testa (Apr 04 2024 at 13:46):
I think that a new stream for each question in #new members is probably better.
Lode Vermeulen (Apr 04 2024 at 14:14):
Damiano Testa said:
I think that a new stream for each question in #new members is probably better.
In that case, I have a question about the first lemma I am working on, about the union of 2 perfect matchings being a disjoint union of cycles. I have the formalization, but I am getting stuck on trying to show that the resulting graph is 2-regular. Also, I am not sure how I would then translate this into it being a cycle.
/- Two disjoint perfect matchings -/
def IsDisjointPerfectMatchingPair (M₁ M₂ : Subgraph G) : Prop :=
M₁.IsPerfectMatching ∧ M₂.IsPerfectMatching ∧ M₁.edgeSet ∩ M₂.edgeSet = ∅
/- A graph with exclusively disjoint perfect matchings -/
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop :=
∀ (M₁ M₂ : Subgraph G), IsDisjointPerfectMatchingPair M₁ M₂
/- A graph that consists of a disjoint union of cycles -/
def IsCyclic (G : SimpleGraph V) : Prop := ∀ ⦃v : V⦄ (c : G.Walk v v), c.IsCycle
/- Two disjoint PMs form a union of cycles -/
lemma disjoint_PMs_form_union_of_cycles (M₁ M₂ : Subgraph G)
(hm : IsDisjointPerfectMatchingPair M₁ M₂) (hg : IsExclusivelyDisjointPMGraph G) :
IsCyclic (fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)) := by
have hd : G.IsRegularOfDegree 2 := by sorry
sorry
Yaël Dillies (Apr 04 2024 at 16:16):
Note you could define
def IsDisjointPerfectMatchingPair (M₁ M₂ : Subgraph G) : Prop :=
M₁.IsPerfectMatching ∧ M₂.IsPerfectMatching ∧ Disjoint M₁ M₂
Yaël Dillies (Apr 04 2024 at 16:18):
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop :=
∀ (M₁ M₂ : Subgraph G), IsDisjointPerfectMatchingPair M₁ M₂
is (almost) always false since
- Most simple graphs have subgraphs which are not perfect matchings (eg the empty subgraph)
- You don't forbid
M₁ = M₂
Yaël Dillies (Apr 04 2024 at 16:19):
What you want is something like
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop :=
{M : Subgraph G | M.IsPerfectMatching}.PairwiseDisjoint id
Mitchell Lee (Apr 04 2024 at 21:52):
If you want to write the condition that the graph G
is the union of the two disjoint perfect matchings M₁ M₂ : Subgraph G
, that would be M₁.IsPerfectMatching ∧ M₂.IsPerfectMatching ∧ IsCompl M₁ M₂
. (See docs#IsCompl.) It might not be worth introducing a new definition for that.
Mitchell Lee (Apr 04 2024 at 21:57):
(Of course, you should use three separate hypotheses of types M₁.IsPerfectMatching
, M₂.IsPerfectMatching
, and IsCompl M₁ M₂
rather than one hypothesis of type M₁.IsPerfectMatching ∧ M₂.IsPerfectMatching ∧ IsCompl M₁ M₂
.)
Mitchell Lee (Apr 04 2024 at 22:00):
I think your definition of IsCyclic
is wrong. It states that every walk from a vertex to itself is a cycle. This isn't possible if the graph has any edges, because you can walk back and forth along that edge several times.
Mitchell Lee (Apr 04 2024 at 22:21):
Yaël Dillies said:
Note you could define
def IsDisjointPerfectMatchingPair (M₁ M₂ : Subgraph G) : Prop := M₁.IsPerfectMatching ∧ M₂.IsPerfectMatching ∧ Disjoint M₁ M₂
Unfortunately, for M₁ M₂ : Subgraph G
, I think that Disjoint M₁ M₂
means that M₁
and M₂
don't share any vertices, not that they don't share any edges. So this isn't what you want.
(In a perfect world, you could get around this problem by taking M₁
and M₂
to be of type SimpleGraph V
instead. Then Disjoint M₁ M₂
would mean that M₁
and M₂
don't share any edges. But then you wouldn't have IsMatching
or IsPerfectMatching
. I think it would be nice to be able to use IsMatching
on SimpleGraph V
in addition to Subgraph G
. See #11911.)
For now, you can use M₁.edgeSet ∩ M₂.edgeSet = ∅
or Disjoint M₁.edgeSet M₂.edgeSet
.
Lode Vermeulen (Apr 05 2024 at 12:06):
Yaël Dillies said:
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop := ∀ (M₁ M₂ : Subgraph G), IsDisjointPerfectMatchingPair M₁ M₂
is (almost) always false since
- Most simple graphs have subgraphs which are not perfect matchings (eg the empty subgraph)
- You don't forbid
M₁ = M₂
I see, thanks for the correction
Lode Vermeulen (Apr 05 2024 at 12:08):
Mitchell Lee said:
I think your definition of
IsCyclic
is wrong. It states that every walk from a vertex to itself is a cycle. This isn't possible if the graph has any edges, because you can walk back and forth along that edge several times.
Right, that makes sense. I have adjusted it to impose that the walk should be a trail first:
/- A graph that consists of a disjoint union of cycles -/
def IsCyclic (G : SimpleGraph V) : Prop :=
∀ ⦃v : V⦄ (w : G.Walk v v), w.IsTrail → w.IsCycle
Lode Vermeulen (Apr 05 2024 at 12:14):
How I see it then, is that I have to show the following statements:
- That a trail exists
- The trail is nonempty
- The trail starts and ends at the same vertex.
- The only repeating vertex is the first one.
Because point 2, 3 and 4 are the only differences between a trail and a cycle.
Does anyone know of any examples I can use in order to show these points?
Yaël Dillies (Apr 05 2024 at 12:21):
Lode Vermeulen said:
I have adjusted it to impose that the walk should be a trail first:
/- A graph that consists of a disjoint union of cycles -/ def IsCyclic (G : SimpleGraph V) : Prop := ∀ ⦃v : V⦄ (w : G.Walk v v), w.IsTrail → w.IsCycle
You also need to assume w ≠ nil
. nil
is a trail but not a cycle.
Lode Vermeulen (Apr 05 2024 at 12:46):
Yaël Dillies said:
≠ nil
That makes sense. I guess that also removes one thing that needs to be shown from the lemma. Is there any way you could show me how to start proving this? I have tried looking at a bunch of similar Lean code, but I can't find a way to attack this.
Rida Hamadani (Apr 05 2024 at 12:52):
Can you please provide an #mwe? I couldn't figure out what import
s, open
s, variable
s, etc. you're using.
Yaël Dillies (Apr 05 2024 at 12:55):
Sorry, I don't have much time right now, but I will remark three more things
fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)
is justM₁ ⊔ M₂
IsCyclic
is still wrongly stated because it currently allows vertices that don't lie on any cycle. I believe what you want is
def IsCyclic (G : SimpleGraph V) : Prop := ∀ ⦃v : V⦄, (∃ w : G.Walk v v, w ≠ nil) ∧ ∀ (w : G.Walk v v), w.IsTrail → w.IsCycle
- Your theorem is wrong because you haven't assumed
V
is finite. Without that assumption, you can at least prove thatM₁ ⊔ M₂
is a disjoint union of cycles/two-sided infinite paths.
Lode Vermeulen (Apr 05 2024 at 12:59):
Rida Hamadani said:
Can you please provide an #mwe? I couldn't figure out what
import
s,open
s,variable
s, etc. you're using.
Yes, my apologies, here is an instance of the lean web editor with all my code so far.
Rida Hamadani (Apr 05 2024 at 14:33):
The first thing that would come to mind is a plan like this:
lemma disjoint_PMs_form_union_of_cycles (M₁ M₂ : Subgraph G)
(hm : IsDisjointPerfectMatchingPair M₁ M₂) : IsCyclic (fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)) :=
fun v => by
refine ⟨?_, ?_⟩
· obtain ⟨hm, -⟩ := hm
rw [isPerfectMatching_iff] at hm
obtain ⟨w, hw, -⟩ := hm v
sorry
· sorry
Now you have to find a way to use docs#SimpleGraph.Adj.toWalk with hw
to construct the walk, and docs#SimpleGraph.Adj.ne to get v ≠ w
, and finishing the first goal with docs#SimpleGraph.Walk.not_nil_of_ne.
P.S. I'm using @Yaël Dillies's definition of IsCyclic, but I couldn't replace fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)
with M₁ ⊔ M₂
.
Rida Hamadani (Apr 05 2024 at 14:37):
I couldn't really figure out how to apply SimpleGraph
theorems to Subgraph
, I suppose there should be an easy way to do that?
Also by the way, in your link, you can remove import Mathlib.Combinatorics.SimpleGraph.Subgraph
, since it is already included in SimpleGraph.Matching
.
Yaël Dillies (Apr 05 2024 at 14:47):
Rida Hamadani said:
I couldn't replace
fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)
withM₁ ⊔ M₂
.
Hint: You want to use docs#SimpleGraph.fromEdgeSet_sup, docs#SimpleGraph.fromEdgeSet_edgeSet and some glue between SimpleGraph.edgeSet
and SimpleGraph.Subgraph.edgeSet
Mitchell Lee (Apr 05 2024 at 19:38):
Hi, I think that [LocallyFinite G]
isn't enough; the vertex set V
actually needs to be finite.
Mitchell Lee (Apr 05 2024 at 20:28):
Another note: the definition of IsCyclic G
you have is now equivalent to the statement that G
is a disjoint union of cycles. However, you said that your goal is to prove that G
is a disjoint union of cycles, not just to prove that G
has a property that's equivalent to being a disjoint union of cycles. (After all, if you just wanted to do the latter, you could stop at proving that G
is regular of degree 2.)
Here's one way of expressing it.
/- The graph `G` is a disjoint union of cycles -/
def IsDisjointUnionOfCycles (G : SimpleGraph V) : Prop :=
∃ P : Set (Subgraph G), -- There exists a set `P` of subgraphs of `G`, such that:
P.Pairwise Disjoint -- The subgraphs are pairwise disjoint,
∧ sSup P = ⊤ -- The union of all of the subgraphs is the whole graph, and
∧ ∀ H ∈ P, ∃ (v : V) (c : G.Walk v v), c.IsCycle ∧ H = c.toSubgraph /- every subgraph `H ∈ P`
consists of the vertices and edges of some cycle of `G`. -/
Another way to express this, which is less direct but probably easier for proofs, is to state that the induced subgraph on each connected component of G
consists of the vertices and edges of a cycle of G
.
/- The graph `G` is a disjoint union of cycles -/
def IsDisjointUnionOfCycles' (G : SimpleGraph V) : Prop :=
∀ H : G.ConnectedComponent, ∃ (v : V) (c : G.Walk v v), c.IsCycle ∧ Subgraph.induce ⊤ H = c.toSubgraph
Lode Vermeulen (Apr 08 2024 at 15:09):
Rida Hamadani said:
The first thing that would come to mind is a plan like this:...
Thanks a lot for this start! This helps a lot with my understanding of Lean as well :grinning_face_with_smiling_eyes: . After playing around with the code, I do have a some questions which I hope you could speak to and which would help me with understanding basic proofs in Lean:
- The proof starts with a lambda function. Is this just to address that the following should hold for every
v
? - The documentation of
refine
states that it behaves likeexact
. In my experience, theexact
tactic solves a goal if the types match. From what I can tell in this example, refine splits the goal into two subgoals. Furthermore, the⟨⟩
brackets stand for pattern matching right? Which in this case is done without assigning a name, hence the anonymous 'dot notation'? - The
obtain
tactic seems to be used as a destructuring function in this example. Especially the second time it is used is confusing to me. Here, we somehow gethw: M₁.Adj v w
fromhm
, which states that that shouldn't exist. - To address the first
sorry
, I tried, as you suggested, applyingAdj.toWalk hw
. From looking at the code forAdj.toWalk
, this should work, as it is expecting an argument of typeG.Adj u v
, but it throws the errorargument hw has type M₁.Adj v w : Prop but is expected to have type ?m.671.Adj ?m.672 ?m.673 : Prop
. The only difference seems likeM₁
is a SubGraph, but I am not sure whether this is the problem. - To try to address the second sorry, I tried
fun w =>
, but Lean says it is expecting a tactic. Do I not have to loop over all walks, like we also do withv
?
Lode Vermeulen (Apr 08 2024 at 15:09):
Yaël Dillies said:
Rida Hamadani said:
I couldn't replace
fromEdgeSet (M₁.edgeSet ∪ M₂.edgeSet)
withM₁ ⊔ M₂
.Hint: You want to use docs#SimpleGraph.fromEdgeSet_sup, docs#SimpleGraph.fromEdgeSet_edgeSet and some glue between
SimpleGraph.edgeSet
andSimpleGraph.Subgraph.edgeSet
Is there a benefit of using ⊔
over ∪
? It seems quite a hassle at this point, and, in my opinion, is little less readable.
Lode Vermeulen (Apr 08 2024 at 15:10):
Mitchell Lee said:
/- The graph `G` is a disjoint union of cycles -/ def IsDisjointUnionOfCycles (G : SimpleGraph V) : Prop := ∃ P : Set (Subgraph G), -- There exists a set `P` of subgraphs of `G`, such that: P.Pairwise Disjoint -- The subgraphs are pairwise disjoint, ∧ sSup P = ⊤ -- The union of all of the subgraphs is the whole graph, and ∧ ∀ H ∈ P, ∃ (v : V) (c : G.Walk v v), c.IsCycle ∧ H = c.toSubgraph /- every subgraph `H ∈ P` consists of the vertices and edges of some cycle of `G`. -/
Thank you, this looks like a really robust way to define it! It might be beneficial to use this in the long run as well, as I intent to show the matchings form a Hamiltonian cycle when the graph is IsExclusivelyDisjointPMGraph
(which I hope could then be as simple as proving Card P = 1
).
Mitchell Lee (Apr 08 2024 at 15:33):
What do you mean by IsExclusivelyDisjointPMGraph
?
Lode Vermeulen (Apr 08 2024 at 15:35):
Mitchell Lee said:
What do you mean by
IsExclusivelyDisjointPMGraph
?
It is defined at the top of this thread (note Yaël's correction as well).
Mitchell Lee (Apr 08 2024 at 15:40):
Can you describe it without using code? Currently the definition is expressing a property that's equivalent to the graph having at most one perfect matching. But I am not sure whether this is what you want because I can't figure out what "A graph with exclusively disjoint perfect matchings" means.
Lode Vermeulen (Apr 08 2024 at 15:44):
Maybe the code is not right, but it is supposed to represent the property that a graph has only disjoint perfect matchings. So a graph with 2 disjoint PMs and no other (non-disjoint) PMs would satisfy this property.
Mitchell Lee (Apr 08 2024 at 15:55):
I see. That shouldn't be necessary as a hypothesis of your theorem disjoint_PMs_form_union_of_cycles
.
Mitchell Lee (Apr 08 2024 at 15:56):
Also, please see this MathOverflow page: https://mathoverflow.net/questions/267002/graphs-with-only-disjoint-perfect-matchings
Lode Vermeulen (Apr 08 2024 at 16:00):
Haha, this is actually the post which my project is based upon. See the following document where I worked out the math more formally.Bogdanov_s_lemma.pdf
Lode Vermeulen (Apr 09 2024 at 13:41):
Lode Vermeulen said:
Rida Hamadani said:
The first thing that would come to mind is a plan like this:...
Thanks a lot for this start! This helps a lot with my understanding of Lean as well :grinning_face_with_smiling_eyes: . After playing around with the code, I do have a some questions which I hope you could speak to and which would help me with understanding basic proofs in Lean:
- The proof starts with a lambda function. Is this just to address that the following should hold for every
v
?- The documentation of
refine
states that it behaves likeexact
. In my experience, theexact
tactic solves a goal if the types match. From what I can tell in this example, refine splits the goal into two subgoals. Furthermore, the⟨⟩
brackets stand for pattern matching right? Which in this case is done without assigning a name, hence the anonymous 'dot notation'?- The
obtain
tactic seems to be used as a destructuring function in this example. Especially the second time it is used is confusing to me. Here, we somehow gethw: M₁.Adj v w
fromhm
, which states that that shouldn't exist.- To address the first
sorry
, I tried, as you suggested, applyingAdj.toWalk hw
. From looking at the code forAdj.toWalk
, this should work, as it is expecting an argument of typeG.Adj u v
, but it throws the errorargument hw has type M₁.Adj v w : Prop but is expected to have type ?m.671.Adj ?m.672 ?m.673 : Prop
. The only difference seems likeM₁
is a SubGraph, but I am not sure whether this is the problem.- To try to address the second sorry, I tried
fun w =>
, but Lean says it is expecting a tactic. Do I not have to loop over all walks, like we also do withv
?
Bumping this because it might have gotten buried.
Rida Hamadani (Apr 09 2024 at 13:54):
Lode Vermeulen said:
Bumping this because it might have gotten buried.
I've been away from my computer, I'll try to reply tonight, sorry for the late response.
Lode Vermeulen (Apr 09 2024 at 13:56):
Rida Hamadani said:
I've been away from my computer, I'll try to reply tonight, sorry for the late response.
That is completely fine, no rush, I was just worried that it had gotten buried.
Mark Fischer (Apr 09 2024 at 19:18):
I'm just starting out too! I'm sure you'll get more detailed answers still.
Re: question 1. I think you have it right.
Consider that:
λv ↦ by
refine ...
Is very similar to
by
intro v
refine ...
Function abstraction is the term-level way to introduce an assumption or hypothesis, so you can use a tactic like intro, or just use function abstraction directly.
If the term introduced is part of a dependent type, then you sometimes say that what follows holds ∀ v
import Mathlib
example: (n : ℕ) → n < 1 → n = 0 := by
intro n h
linarith
example: (n : ℕ) → n < 1 → n = 0 :=
λ n h ↦ by linarith
example: ∀n : ℕ, n < 1 → n = 0 := by
intro n h
linarith
example: ∀n : ℕ, n < 1 → n = 0 :=
λ n h ↦ by linarith
though in cases like this, it's probably more common to see n
and h
as parameters already:
example (n : ℕ) (h : n < 1) : n = 0 := by linarith
Rida Hamadani (Apr 09 2024 at 22:53):
Lode Vermeulen said:
I do have a some questions:
- Yes.
- Yes, but I'm not sure about assigning names, never saw it in action. A quick test shows that you can assign a name and use dots anyways.
- Yes. I think you confused "shouldn't exist" with "there exists a unique", because in programming languages
!
stands for negation, but in math∃!
stands for existence with uniqueness. - You can use
have hvw : G.Adj v w := hw.adj_sub
to getG.Adj v w
, however I've noticed now that my plan was wrong, because the walk that you need to show is notnil
must be of the typew : Walk (fromEdgeSet (edgeSet M₁ ∪ edgeSet M₂)) v v
. I didn't notice this at first. - Lean has a "term mode", and a "tactic mode".
fun w =>
is a term, whileintro w
is a tactic.
Note that there was a job posting here years ago for formalizing what you are working on, check this, so I wonder if anybody ended up taking the offer and formalizing this.
Mario Krenn (Apr 10 2024 at 09:33):
Rida Hamadani said:
Note that there was a job posting here years ago for formalizing what you are working on, check this, so I wonder if anybody ended up taking the offer and formalizing this.
Indeed @Yaël Dillies with help of @Bhavik Mehta worked on this when i posted it. Time ran out at the time (mainly due to time-constraints of the fundings from my side), and it was not finished. But i shared all of the codes from that time with Lode.
Yaël Dillies (Apr 11 2024 at 08:33):
The biggest issue was that mathlib didn't have a proper notion of multigraphs at the time, so I worked a lot on figuring this out but nothing ended up in mathlib
Rida Hamadani (Apr 11 2024 at 08:34):
I think having multigraphs in mathlib would be neat, what are the challenges of porting them?
Rida Hamadani (Apr 11 2024 at 08:34):
I think having multigraphs in mathlib would be neat, what are the challenges of porting them?
(This message got double sent for some weird reason, sorry!)
Lode Vermeulen (Apr 12 2024 at 13:14):
Rida Hamadani said:
- Yes....
Thanks a lot for these insights, they are really useful!
Lode Vermeulen (Apr 12 2024 at 13:41):
As the previous approach did not really work out, I have been looking into proving the lemma using the following definition:
Mitchell Lee said:
/- The graph `G` is a disjoint union of cycles -/ def IsDisjointUnionOfCycles (G : SimpleGraph V) : Prop := ∃ P : Set (Subgraph G), -- There exists a set `P` of subgraphs of `G`, such that: P.Pairwise Disjoint -- The subgraphs are pairwise disjoint, ∧ sSup P = ⊤ -- The union of all of the subgraphs is the whole graph, and ∧ ∀ H ∈ P, ∃ (v : V) (c : G.Walk v v), c.IsCycle ∧ H = c.toSubgraph /- every subgraph `H ∈ P` consists of the vertices and edges of some cycle of `G`. -/
The first step of proving that a graph complies to this definition seems to me to be the most important. Constructing a set of subgraphs by alternating the edges added to a subgraph between the two PMs would naturally mean the subgraphs are disjoint and cycles.
See this document as well for more info about constructing these subgraphs (Lemma 3.2):
Bogdanov_s_lemma.pdf
Doing this in Python code would look something like this:
#Assumption: G is made with 2 disjoint PMs -> G is 2-regular
sub_graphs = []
for edge in G.edgeSet:
edge_added = False
for sub_graph in sub_graphs:
if hasVertexInCommon(sub_graph, edge):
sub_graph.append(edge)
edge_added = True
break
if edge_added:
continue
subGraphs.append([edge])
My question: is it possible to define something similar in Lean? And could this actually lead to a simpler proof in Lean?
Lode Vermeulen (Apr 29 2024 at 09:12):
Yaël Dillies said:
What you want is something like
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop := {M : Subgraph G | M.IsPerfectMatching}.PairwiseDisjoint id
I am finally getting around to using this definition, but I am struggling to see why this definition does not also say that the vertex sets of the Subgraphs are disjoint. How does Lean know that we are talking about the disjointness of the edgesets, rather than the vertex sets?
Yaël Dillies (Apr 29 2024 at 09:14):
As Mitchell mentioned above, this definition is indeed wrong because it should only require that the edge sets are disjoint. The correct definition is
def IsExclusivelyDisjointPMGraph (G : SimpleGraph V) : Prop :=
{M : Subgraph G | M.IsPerfectMatching}.PairwiseDisjoint Subgraph.edgeSet
Last updated: May 02 2025 at 03:31 UTC