Skip to content

feat: graph definitions (#Attempts 3) - #810

Open
sorrachai wants to merge 37 commits into
leanprover:mainfrom
sorrachai:graph_def
Open

feat: graph definitions (#Attempts 3)#810
sorrachai wants to merge 37 commits into
leanprover:mainfrom
sorrachai:graph_def

Conversation

@sorrachai

@sorrachai sorrachai commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

This PR introduces basic graph definitions: Graph, Digraph, SimpleGraph, and SimpleDiGraph.

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 Mathlib Graph α β, adding a typeclass to keep track of endpoints explicitly.
  • MultiDigraph α β: a directed multigraph; the directed counterpart of Mathlib's Graph α β with explicit endpoints.
  • SimpleGraph α: a simple graph with a vertex set, extending Mathlib's SimpleGraph α.
  • SimpleDigraph α: a loopless directed graph with adjacency Adj : α → α → Prop and a
    vertex 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 of Mathlib.Combinatorics.Graph.

Zulip discussion: Follow at
https://leanprover.zulipchat.com/#narrow/channel/252551-graph-theory/topic/A.20Set-based.20Multigraph.20Definition/with/616948793.

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated

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 α β

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand having this abbrev if they are both called Graph, and are defined exactly the same way.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to rename things for consistency. Now, it is renamed as MultiGraph.

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
/-- 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 α)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@ctchou ctchou Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's do it.

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
/-- 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 (α × α)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about partial functions as above.

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need := by grind. There is nothing to prove here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's some confusion here: when declaring a structure/typeclass this is how you specify a default tactic to run.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I didn't know that. The syntax is not helpful here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Just to add the default tactic to prove the goal)

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need := by grind. There is nothing to prove here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Just to add the default tactic to prove the goal)

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated

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

@ctchou ctchou Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Rename it to MultiDigraph and from Graph to MultiGraph.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Quiver is not quite what we want; they generalize V -> V -> Prop to V -> V -> Type, which means no global edge labeling.)

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
sorrachai and others added 8 commits August 18, 2026 09:00
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>
@SnirBroshi

Copy link
Copy Markdown

Can you replace all mentions of "arc" with "edge"?

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
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 α :=

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

α, β -> V, E

Comment thread Cslib/Algorithms/Lean/Graph/Basic.lean Outdated
Comment on lines +86 to +103
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

@ctchou ctchou Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Added isLink_imp_left_mem_vertexSet and isLink_imp_right_mem_vertexSet.

Comment on lines +66 to +75
/-- 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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@sorrachai sorrachai Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@sorrachai

Copy link
Copy Markdown
Collaborator Author

I did everything I could. Let me know if you need anything else.

@ctchou ctchou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @sorrachai's assessment.

@Shreyas4991

Shreyas4991 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

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.

@sorrachai

Copy link
Copy Markdown
Collaborator Author

@chenson2018 Any thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants