graph(3)
π
graph(3) β Erlang Module Definition π
okay so bestie, welcome to the graph(3) page! This is part 1, so buckle up. π
β¨ NAME β¨
graph β a functional implementation of labeled directed graphs. periodt. π
πΈ DESCRIPTION πΈ
okay so this module is closely modelled on the digraph and digraph_utils modules, which represent graphs using mutable ETS tables. This functional implementation is more lightweight and does not involve mutable state and table ownership, and makes it easy to keep multiple versions of a graph, but for large tables with a long lifetime, the ETS based implementation can be more suitable. In the context of this module, we only use the term "digraph" when referring to the digraph implementation, and not to directed graphs in general. π
When rewriting code from using digraph to using graph, keep in mind that:
- Graphs are immutable: each modifying operation returns the new graph, which needs to be saved in a variable and passed to the next operation, for example
G0 = graph:new(), G1 = graph:add_vertex(G0, v1), G2 = graph:add_vertex(G1, v2). - Graphs are garbage collected and do not need to be explicitly deleted.
- There are no protected or private options and no memory info key.
- Edges are not objects with identity and state. An edge is uniquely identified by the triple
{From, To, Label}where the default label is[]. There can be multiple edges with the sameFromandTobut only if they have different values forLabel. - Vertices, however, have a unique identifier just as in
digraph. The label of an existing vertex can be replaced by a new call toadd_vertex(Id, Label). The label defaults to[]. - The functions in
digraph_utilshave been included directly in thegraphmodule for simplicity.
Some graph theoretical definitions:
- directed graph (here simply called "graph") is a pair
(V, E)of a finite setVof vertices and a finite setEof directed edges (here simply called "edges"). The set of edgesEis a subset ofV Γ V(the Cartesian product ofVwith itself).
In this module, V is allowed to be empty. The so obtained unique graph is called the empty graph. Each vertex has a unique Erlang term as identifier. π¦
- Graphs can be annotated with more information. Such information can be attached to the vertices and to the edges of the graph. An annotated graph is called a labeled graph, and the information attached to a vertex or an edge is called a label. Labels are Erlang terms.
- An edge
e = (v, w)is said to emanate from vertexvand to be incident on vertexw. - The out-degree of a vertex is the number of edges emanating from that vertex.
- The in-degree of a vertex is the number of edges incident on that vertex.
- If an edge is emanating from
vand incident onw, thenwis said to be an out-neighbor ofv, andvis said to be an in-neighbor ofw. - subgraph
G'ofGis a graph whose vertices and edges form subsets of the vertices and edges ofG. G'is maximal with respect to a propertyPif all other subgraphs that include the vertices ofG'do not have propertyP.- path
Pfromv[1]tov[k]in a graph(V, E)is a non-empty sequencev[1], v[2], ..., v[k]of vertices inVsuch that there is an edge(v[i], v[i+1])inEfor1 <= i < k. - The length of path
Pisk-1. - Path
Pis simple if all vertices are distinct, except that the first and the last vertices can be the same. - Path
Pis a cycle if the length ofPis not zero andv[1] = v[k]. - loop is a cycle of length one.
- simple cycle is a path that is both a cycle and simple.
- An acyclic graph is a graph without cycles.
- tree is an acyclic non-empty graph such that there is a unique path between every pair of vertices, considering all edges undirected. In an undirected tree, any vertex can be used as root. Informally however, "tree" is often used to refer to mean an out-tree (arborescence), in particular in computer science.
- An arborescence or directed rooted tree or out-tree is an acyclic directed graph with a vertex
V, the root, such that there is a unique path fromVto every other vertex ofG. - forest is a disjoint union of trees.
- strongly connected component is a maximal subgraph such that there is a path between each pair of vertices.
- connected component is a maximal subgraph such that there is a path between each pair of vertices, considering all edges undirected.
This module also provides algorithms based on depth-first traversal of directed graphs.
- depth-first traversal of a directed graph can be viewed as a process that visits all vertices of the graph. Initially, all vertices are marked as unvisited. The traversal starts with an arbitrarily chosen vertex, which is marked as visited, and follows an edge to an unmarked vertex, marking that vertex. The search then proceeds from that vertex in the same fashion, until there is no edge leading to an unvisited vertex. At that point the process backtracks, and the traversal continues as long as there are unexamined edges. If unvisited vertices remain when all edges from the first vertex have been examined, some so far unvisited vertex is chosen, and the process is repeated.
- partial ordering of a set
Sis a transitive, antisymmetric, and reflexive relation between the objects ofS. - The problem of topological sorting is to find a total ordering of
Sthat is a superset of the partial ordering. A graphG = (V, E)is equivalent to a relationEonV(we neglect that the version of directed graphs provided by thedigraphmodule allows multiple edges between vertices). If the graph has no cycles of length two or more, the reflexive and transitive closure ofEis a partial ordering.
π DATA TYPES π
edge() :: {vertex(), vertex(), label()}.
edge_map() :: #{vertex() => ordsets:ordset(vertex())}.
graph() :: #graph{}.
graph_cyclicity() :: acyclic | cyclic.
graph_type() :: graph_cyclicity().
label() :: term().
vertex() :: term().
vertice_map() :: #{vertex() => label()}.
π FUNCTIONS π
add_edge(G, V1, V2, L) -> graph()
add_edge(G, V1, V2, L) -> graph()
when
G :: graph(),
V1 :: vertex(),
V2 :: vertex(),
L :: label().
Creates an edge {V1, V2, L} in graph G. β¨
The edge is emanating from V1 and incident on V2, and has label L. The edge is uniquely identified by this triple. A graph can have multiple edges between the same vertices V1 and V2 but only if the edges have different labels.
If G was created with option acyclic, then attempting to add an edge that would introduce a cycle will raise an error {bad_edge, {From, To}}. Note that checking for cyclicity slows down the adding of edges. π
add_vertex(graph()) -> {vertex(), graph()}
add_vertex(graph()) -> {vertex(), graph()}.
Adds a new vertex to graph G, returning the created vertex id.
The new vertex will have the empty list [] as label.
Note: Vertex ID:s are assigned as integers in increasing order starting from zero. If you use add_vertex/2 or add_vertex/3 to insert vertices with your own identifiers, this function could generate an ID that already exists in the graph. π
add_vertex(graph(), vertex(), label()) -> graph()
add_vertex(graph(), vertex(), label()) -> graph().
Creates or modifies vertex V of graph G, using L as the (new) label of the vertex.
arborescence_root(graph()) -> no | {yes, vertex()}
arborescence_root(graph()) -> no | {yes, vertex()}.
Returns {yes, V} if G is an arborescence (a directed tree) with vertex V as the root, otherwise no.
components(graph()) -> [[vertex()]]
components(graph()) -> [[vertex()]].
Returns a list of connected components. π«Ά
Each component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of graph G occurs in exactly one component.
condensation(graph()) -> graph()
condensation(graph()) -> graph().
Creates a graph where the vertices are the strongly connected components of G as returned by strong_components/1.
If X and Y are two different strongly connected components, and vertices x and y exist in X and Y, respectively, such that there is an edge emanating from x and incident on y, then an edge emanating from X and incident on Y is created.
The created graph has the same type as G. All vertices and edges have the default label [].
Each cycle is included in some strongly connected component, which implies that a topological ordering of the created graph always exists.
cyclic_strong_components(graph()) -> [[vertex()]]
cyclic_strong_components(graph()) -> [[vertex()]].
Returns a list of cyclic strongly connected components.
Each strongly component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Only vertices that are included in some cycle in G are returned, otherwise the returned list is equal to that returned by strong_components/1.
del_edge(G :: graph(), E :: edge()) -> graph()
del_edge(G :: graph(), E :: edge()) -> graph().
Deletes edge E from graph G.
del_edges(graph(), [edge()]) -> graph()
del_edges(graph(), [edge()]) -> graph().
Deletes the edges in list Es from graph G.
del_edges(graph(), vertex(), vertex()) -> graph()
del_edges(graph(), vertex(), vertex()) -> graph().
Deletes all edges from vertex V1 to vertex V2 in graph G.
del_path(graph(), vertex(), vertex()) -> graph()
del_path(graph(), vertex(), vertex()) -> graph().
Deletes edges from graph G until there are no paths from vertex V1 to vertex V2.
A sketch of the procedure employed:
- Find an arbitrary simple path
v[1], v[2], ..., v[k]fromV1toV2inG. - Remove all edges of
Gemanating fromv[i]and incident tov[i+1]for1 <= i < k(including multiple edges). - Repeat until there is no path between
V1andV2.
del_vertex(graph(), vertex()) -> graph()
del_vertex(graph(), vertex()) -> graph().
Deletes vertex V from graph G.
Any edges emanating from V or incident on V are also deleted.
del_vertices(G :: graph(), Vs :: [vertex()]) -> graph()
del_vertices(G :: graph(), Vs :: [vertex()]) -> graph().
Deletes the vertices in list Vs from graph G.
edges(G :: graph()) -> [edge()]
edges(G :: graph()) -> [edge()].
Returns a list of all edges of graph G, in some unspecified order.
edges(G :: graph(), V :: vertex()) -> [edge()]
edges(G :: graph(), V :: vertex()) -> [edge()].
Returns a list of all edges emanating from or incident on V of graph G, in some unspecified order.
Edges may occur twice in the list. Use ordsets:from_list/1 on the result if you need to remove duplicates.
edges(G :: graph(), V1 :: vertex(), V2 :: vertex()) -> ordsets:ordset(edge())
edges(G :: graph(), V1 :: vertex(), V2 :: vertex()) ->
ordsets:ordset(edge()).
fold_vertices(G, Fun, Acc) -> any(),when G :: graph(), Fun :: fun((vertex(), label(), any()) -> any()), Acc :: any().β foldFunover the vertices of graphG, in some unspecified order.get_cycle(graph(), vertex()) -> [vertex(), ...] | false.β tries to find a cycle inGthat includes vertexV. π
If a simple cycle of length two or more exists through vertex V, the cycle comes back as a list [V, ..., V] of vertices. If a loop through V exists, the loop comes back as a list [V]. And if there are genuinely no cycles through V? false, bestie. π
get_path(graph(), vertex(), vertex()) -> [vertex(), ...] | false.β tries to find a simple path from vertexV1to vertexV2of graphG.
Returns the path as a list [V1, ..., V2] of vertices, or false if no simple path from V1 to V2 of length one or more exists.
The graph gets walked in a depth-first kind of way, and the first path it finds is the one you get. periodt.
get_short_cycle(graph(), vertex()) -> [vertex(), ...] | false.β same energy asget_cycle/2, except a cycle of length one is preferred.
Tries to find an as short as possible simple cycle through vertex V of graph G. Returns the cycle as a list [V, ..., V] of vertices, or false if no simple cycle through V exists. And notice β a loop through V is returned as the list [V, V]. yes, really. π«Ά
get_short_path(graph(), vertex(), vertex()) -> [vertex(), ...] | false.β same asget_path/3, but it uses a breadth-first search to find a short path.
Tries to find an as short as possible simple path from vertex V1 to vertex V2 of graph G. Returns the path as a list [V1, ..., V2] of vertices, or false if no simple path from V1 to V2 of length one or more exists.
Graph G is traversed in a breadth-first manner, and the first found path is returned.
has_edge(G :: graph(), E :: edge()) -> boolean().β returnstrueif and only ifGcontains edgeE. π
bestie read this twice: the identity of an edge includes its label. So if you just want "is there some edge between these two vertices", use has_edge/3 instead.
has_edge(G :: graph(), V1 :: vertex(), V2 :: vertex()) -> boolean().β returnstrueif and only ifGcontains some edge fromV1toV2.has_path(G :: graph(), V1 :: vertex(), V2 :: vertex()) -> boolean().β returnstrueif and only if there is a path inGfrom vertexV1to vertexV2.has_vertex(G :: graph(), V :: vertex()) -> boolean().β returnstrueif and only ifGcontains vertexV.in_degree(G :: graph(), V :: vertex()) -> non_neg_integer().β returns the in-degree of vertexVof graphG.in_edges(G :: graph(), V :: vertex()) -> [edge()].β returns a list of all edges incident onVof graphG, in some unspecified order. β¨in_neighbours(G :: graph(), V :: vertex()) -> [vertex()].β returns a list of all in-neighbors ofVof graphG, in some unspecified order.info(graph()) -> [{cyclicity, graph_cyclicity()}].β returns a list of{Tag, Value}pairs describing graphG.
The following pairs are returned:
{cyclicity, Cyclicity}, whereCyclicityiscyclicoracyclic, according to the options given tonew.
is_acyclic(graph()) -> boolean().β returnstrueif and only if graphGis acyclic.is_arborescence(graph()) -> boolean().β returnstrueif and only if graphGis an arborescence (a directed tree with a unique root). πis_tree(graph()) -> boolean().β returnstrueif and only if graphGis a tree, considering all edges undirected.loop_vertices(graph()) -> [vertex()].β returns a list of all vertices ofGthat are included in some loop. πnew([graph_type()]) -> graph().β creates a new graph. β¨ iconic behaviour honestly.
Returns an empty graph with properties according to the options in Options:
cyclicβ allows cycles in the graph (this is the default).acyclicβ the graph is to be kept acyclic. Attempting to add an edge that would introduce a cycle will raise an error{bad_edge, {From, To}}. ngl this slows down the adding of edges. worth it tho.
If an unrecognized option is specified or Options is not a proper list, a badarg exception is raised.
no_edges(G :: graph()) -> non_neg_integer().β returns the number of edges of graphG.no_vertices(G :: graph()) -> non_neg_integer().β returns the number of vertices of graphG.out_degree(G :: graph(), V :: vertex()) -> non_neg_integer().β returns the out-degree of vertexVof graphG.out_edges(G :: graph(), V :: vertex()) -> [edge()].β returns a list of all edges emanating fromVof graphG, in some unspecified order.out_neighbours(G :: graph(), V :: vertex()) -> [vertex()].β returns a list of all out-neighbors ofVof graphG, in some unspecified order.postorder(graph(), [vertex()]) -> [vertex()].β returns the vertices of graphGreachable fromVs, listed in post-order.
The order is given by a depth-first traversal of the graph, collecting visited vertices in postorder. More precisely, the vertices visited while searching from an arbitrarily chosen vertex are collected in postorder, and all those collected vertices are placed before the subsequently visited vertices.
preorder(graph(), [vertex()]) -> [vertex()].β returns all vertices of graphGreachable fromVs, listed in pre-order.
The order is given by a depth-first traversal of the graph, collecting visited vertices in preorder.
reachable(graph(), [vertex()]) -> [vertex()].β returns an unsorted list of graph vertices such that for each vertex in the list, there is a path inGfrom some vertex ofVsto the vertex.
In particular, since paths can have length zero, the vertices of Vs are all included in the returned list. π«Ά
reachable_via_neighbours(graph(), [vertex()]) -> [vertex()].β returns an unsorted list of graph vertices such that for each vertex in the list, there is a path inGof length one or more from some vertex ofVsto the vertex.
Hence, vertices in Vs will only be included in the result if they are part of some cycle.
reaching(graph(), [vertex()]) -> [vertex()].β returns an unsorted list of graph vertices such that for each vertex in the list, there is a path from the vertex to some vertex ofVs.
In particular, since paths can have length zero, the vertices of Vs are all included in the returned list.
reaching_via_neighbours(graph(), [vertex()]) -> [vertex()].β returns an unsorted list of graph vertices such that for each vertex in the list, there is a path of length one or more from the vertex to some vertex ofVs.
Hence, vertices in Vs will only be included in the result if they are part of some cycle.
reverse_postorder(graph(), [vertex()]) -> [vertex()].β returns the vertices of graphGreachable fromVs, listed in reverse post-order.
This effectively performs a topological sort of the reachable nodes.
The graph is traversed as for postorder/2, but producing the result in reverse order.
roots(graph()) -> [vertex()].β returns a minimal list of vertices ofGfrom which all vertices ofGcan be reached. π±sink_vertices(G :: graph()) -> [vertex()].β returns a list of all vertices of graphGwith out-degree zero.source_vertices(G :: graph()) -> [vertex()].β returns a list of all vertices of graphGwith in-degree zero.strong_components(graph()) -> [[vertex()]].β returns a list of strongly connected components. πͺ
Each strongly component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of graph G occurs in exactly one strong component.
subgraph(graph(), [vertex()], Options) -> graph(),when Options :: [{type, SubgraphType} | {keep_labels, boolean()}], SubgraphType :: inherit | [graph_type()].β creates a maximal subgraph ofGrestricted to the vertices listed inVs.
If the value of option type is inherit, which is the default, the type of G is used for the subgraph as well (for example, whether the graph allows cycles). Otherwise the value of the type option is used as argument to new/1.
If the value of option keep_labels is true, which is the default, the labels of vertices and edges of G are used for the subgraph as well. If the value is false, the vertices and edges of the subgraph will have the default labels.
If any of the arguments are invalid, a badarg exception is raised. π
topsort(graph()) -> [vertex()].β returns a topological ordering of the vertices of graphGif such an ordering exists, otherwisefalse.
For each vertex in the returned list, no out-neighbors occur earlier in the list.
β οΈ heads up bestie: this is currently implemented simply as reverse_postorder(G), but this detail is subject to change and should not be relied on. don't build your whole personality on it.
vertex(G :: graph(), V :: vertex()) -> label().β returns the label of the vertexVof graphG.
An exception is raised if V does not exist in G. π
vertex(G :: graph(), V :: vertex(), Default :: label()) -> label().β returns the label of the vertexVof graphG, or returnsDefaultifVdoes not exist inG. much gentler honestly.vertices(G :: graph()) -> [vertex()].β returns a list of all vertices of graphG, in some unspecified order.vertices_with_labels(G :: graph()) -> [{vertex(), label()}].β returns a list of all pairs{V, L}of vertices of graphGand their respective labels, in some unspecified order. π¦
and that's the whole thing, babe. this page belongs to Ericsson AB, it's from stdlib 8.0.4, and it is, of course, graph(3). no cap, you now know more about Erlang graphs than most people ever will. slay. ππ
β¨
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms