feat: graph definitions (#Attempts 3) - #810
Conversation
|
|
||
| This is Mathlib's `Graph α β` — so parallel edges and loops are permitted, and both the | ||
| vertex and edge sets may be infinite. -/ | ||
| abbrev Graph (α β : Type*) := _root_.Graph α β |
There was a problem hiding this comment.
I would suggest replacing α and β by V and E. When reading the code below, I have to constantly remind myself that α and β are the carrier types for vertices and edges. Using V and E will make this transparent.
There was a problem hiding this comment.
I don't understand having this abbrev if they are both called Graph, and are defined exactly the same way.
There was a problem hiding this comment.
Just to rename things for consistency. Now, it is renamed as MultiGraph.
| /-- A computable map from an edge label of `G` to its ends. -/ | ||
| class Graph.HasEndpoints {α β : Type*} (G : Graph α β) where | ||
| /-- The ends of the edge labelled `e`, or `none` if `e` is not an edge of `G`. -/ | ||
| endpoints : β → Option (Sym2 α) |
There was a problem hiding this comment.
There is already a notion of partial functons in mathlib:
https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/PFun.html
which has a lot of API's.
There was a problem hiding this comment.
I tried a bit, but I found that it allows even non-computable Prop. It would be overkill to use it, especially since we want to keep everything computable.
There was a problem hiding this comment.
That a non-computable Prop is allowed doesn't mean you have to use a non-computable Prop. It just means that Lean will not insists that the Prop is computable. Note that you can build a Set from a non-computable Prop as well. Do you want to replace Set by Finset everywhere in your code?
The main reason for my suggesting PFun is that it already has a lot of API support, including ones that work via Option.
There was a problem hiding this comment.
Sure, let's do it.
| /-- A computable map from an edge label of `G` to its ends. -/ | ||
| class Digraph.HasEndpoints {α β : Type*} (G : Digraph α β) where | ||
| /-- The ends of the edge labelled `e`, or `none` if `e` is not an edge of `G`. -/ | ||
| endpoints : β → Option (α × α) |
There was a problem hiding this comment.
Same comment about partial functions as above.
| vertexSet : Set α | ||
| /-- The left end of every adjacent pair is a vertex. The right end then follows by | ||
| symmetry of `Adj`. -/ | ||
| left_incidence : ∀ ⦃x y⦄, Adj x y → x ∈ vertexSet := by grind |
There was a problem hiding this comment.
You don't need := by grind. There is nothing to prove here.
There was a problem hiding this comment.
I think there's some confusion here: when declaring a structure/typeclass this is how you specify a default tactic to run.
There was a problem hiding this comment.
OK. I didn't know that. The syntax is not helpful here.
There was a problem hiding this comment.
(Just to add the default tactic to prove the goal)
| loopless : Std.Irrefl Adj | ||
| /-- Both ends of every adjacent pair are vertices. Unlike `SimpleGraph`, `Adj` is not | ||
| symmetric, so neither direction follows from the other. -/ | ||
| incidence : ∀ ⦃x y⦄, Adj x y → x ∈ vertexSet ∧ y ∈ vertexSet := by grind |
There was a problem hiding this comment.
You don't need := by grind. There is nothing to prove here.
There was a problem hiding this comment.
(Just to add the default tactic to prove the goal)
|
|
||
| The directed counterpart of Mathlib's `Graph α β`, which has no Mathlib counterpart to | ||
| extend; the field layout mirrors it, with symmetry dropped. -/ | ||
| structure Digraph (α β : Type*) where |
There was a problem hiding this comment.
Since you are making this definition from scratch, why not state it using endpoints, rather than adapting mathlib's Graph? You should also consider if mathlib's Quiver is applicable here.
Also, since mathlib already has a Digraph and this definition is not an extension of it, I would suggest calling it something else. Perhaps DirectedMultigraph?
There was a problem hiding this comment.
Good point. Rename it to MultiDigraph and from Graph to MultiGraph.
There was a problem hiding this comment.
(Quiver is not quite what we want; they generalize V -> V -> Prop to V -> V -> Type, which means no global edge labeling.)
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
Co-authored-by: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com>
|
Can you replace all mentions of "arc" with "edge"? |
| endpoints_spec : ∀ e x y, G.IsLink e x y ↔ s(x, y) ∈ endpoints e | ||
|
|
||
| /-- The ends of `e` in `G`; undefined when `e ∉ E(G)`. -/ | ||
| def MultiGraph.endpoints (G : MultiGraph α β) [inst : G.HasEndpoints] : β →. Sym2 α := |
| structure MultiDigraph (V E : Type*) where | ||
| /-- The set of vertices. -/ | ||
| vertexSet : Set V | ||
| /-- The incidence predicate: `IsLink e x y` states that the edge labelled `e` runs from | ||
| `x` to `y`. -/ | ||
| IsLink : E → V → V → Prop | ||
| /-- The ends of the edge labelled `e`, or `none` if `e` is not an edge of `G`. -/ | ||
| endpoints : E →. (V × V) | ||
| /-- `endpoints` computes `IsLink`. -/ | ||
| endpoints_spec : ∀ e x y, IsLink e x y ↔ (x, y) ∈ endpoints e | ||
| /-- Both ends of every edge are vertices. `IsLink` is not symmetric, so neither direction | ||
| follows from the other. -/ | ||
| isLink_imp_left_mem_vertexSet : ∀ ⦃e x y⦄, IsLink e x y → x ∈ vertexSet := by grind | ||
| isLink_imp_right_mem_vertexSet : ∀ ⦃e x y⦄, IsLink e x y → y ∈ vertexSet := by grind | ||
| /-- The set of edge labels. -/ | ||
| edgeSet : Set E := { e | ∃ x y, IsLink e x y} | ||
| /-- A label lies in `edgeSet` exactly when it is used by some edge. -/ | ||
| edge_mem_iff_exists_IsLink (e) : e ∈ edgeSet ↔ ∃ x y, IsLink e x y := by exact fun _ ↦ Iff.rfl |
There was a problem hiding this comment.
I don't think you need both IsLink and endpoints to be in the structure MultiDigraph, because endpoints_spec can serve as the definition of IsLink in terms of endpoints. The contents of isLink_imp_{left,right}_mem_vertexSet can be expressed in terms of endpoints directly: namely, the PFun.image of endpoints projected to a Set V is a subset of vertexSet. Then isLink_imp_{left,right}_mem_vertexSet can be derived as theorems. Going the other way (namely, defining endpoints in terms of IsLink) is also possible, but then you'll need an analogue of mathlib's Graph. eq_or_eq_of_isLink_of_isLink to prevent the same edge label to be applied to multiple edges. In either case, that analogue should probably be stated and proved/assumed.
Also, stylistically, I would prefer edgeSet and edge_mem_iff_exists_IsLink to become a separate definition and theorem outside the structure.
There was a problem hiding this comment.
Thanks for the comment. Indeed, these two fields are redundant. The question is which one should be in the field (and which should be derived). I would prefer endpoints because they explicitly provide a vertex pair. The reason for the IsLink relation was to avoid quotient types (Sym2)#CSLib > New graph definitions @ 💬 in the case of an undirected multigraph. This is no longer the case for directed graphs.
There was a problem hiding this comment.
I think the following two Props should suffice to imply isLink_imp_{left,right}_mem_vertexSet:
left_endpoint_mem_vertexSet : endpoints.ran.MapsTo Prod.fst vertexSet
right_endpoint_mem_vertexSet : endpoints.ran.MapsTo Prod.snd vertexSet
There was a problem hiding this comment.
Indeed. Added isLink_imp_left_mem_vertexSet and isLink_imp_right_mem_vertexSet.
| /-- A map from an edge label of `G` to its ends. -/ | ||
| class MultiGraph.HasEndpoints {V E : Type*} (G : MultiGraph V E) where | ||
| /-- The ends of the edge labelled `e`, or `none` if `e` is not an edge of `G`. -/ | ||
| endpoints : E →. (Sym2 V) | ||
| /-- `endpoints` computes `Graph.IsLink`. -/ | ||
| endpoints_spec : ∀ e x y, G.IsLink e x y ↔ s(x, y) ∈ endpoints e | ||
|
|
||
| /-- The ends of `e` in `G`; undefined when `e ∉ E(G)`. -/ | ||
| def MultiGraph.endpoints (G : MultiGraph V E) [inst : G.HasEndpoints] : E →. Sym2 V := | ||
| inst.endpoints |
There was a problem hiding this comment.
I wonder if it is possible to define MultiGraph.endpoints directly. Unfortunately, all definitions I can think of uses Classical.choose somewhere and hence must be marked as noncomputable. But if we accept that, then we can get rid of the assumption [G.HasEndpoints], which currently needs to be assumed whenever MultiGraph.endpoints is used. Perhaps that can be considered an improvement and a small price to pay for using mathlib's Graph.
There was a problem hiding this comment.
Alternatively, one could extend Mathlib's Graph with field endpoints and endpoints_spec, but this seems redundant, since it subsumes IsLink. I am happy with the current price to pay if we cannot define MultiGraph ourselves.
|
I did everything I could. Let me know if you need anything else. |
ctchou
left a comment
There was a problem hiding this comment.
I agree with @sorrachai's assessment.
|
I am sorry but I maintain as pointed out by others on Zulip that this PR still fractures and duplicates the Graph API. For instance, the correct step for "SimpleDigraph" is to adopt leanprover-community/mathlib4#33466 and get it merged. Especially (but not limited to) the situation where one accepts the review in the immediately preceding set of review comments that using noncommputable is fine. I personally do not find the existing mathlib API lacking for basic graph algorithms, apart from vertex sets in SimpleGraph, which is being addressed in Mathlib through the ongoing refactors. I propose first making PRs to add graph algorithms (such as my #804 and/or #805 ) and then, through those PRs, surfacing any deficiencies in mathlib's graph API. When they are found, they can be backfilled through mathlib PRs. Such an incremental process will also help mathlib graph developers and avoid fracturing and duplicating API for graph theory. |
|
@chenson2018 Any thoughts? |
This PR introduces basic graph definitions:
Graph,Digraph,SimpleGraph, andSimpleDiGraph.We use the following definition of a multigraph.
A multigraph is a triple (V,E,f) where V is a vertex set, E is an edge set,
and f is a function from an edge to an (ordered/unordered) pair of vertices.
Preferably, f is a computable function. We reuse the definitions from Mathlib whenever possible.
MultiGraph α β: abbrev for MathlibGraph α β, adding a typeclass to keep track of endpoints explicitly.MultiDigraph α β: a directed multigraph; the directed counterpart of Mathlib'sGraph α βwith explicit endpoints.SimpleGraph α: a simple graph with a vertex set, extending Mathlib'sSimpleGraph α.SimpleDigraph α: a loopless directed graph with adjacencyAdj : α → α → Propand avertex set, extending Mathlib's
Digraph α.Comparison to PR #503 (#503): This version reuses Mathlib as much as possible. All vertex sets are
Set-valued, following the design ofMathlib.Combinatorics.Graph.Zulip discussion: Follow at
https://leanprover.zulipchat.com/#narrow/channel/252551-graph-theory/topic/A.20Set-based.20Multigraph.20Definition/with/616948793.