Skip to content

Commit 080cebe

Browse files
author
Release Manager
committed
gh-38198: Improve complexity comments for graphs <!-- ^ Please provide a concise and informative title. --> <!-- ^ Don't put issue numbers in the title, do this in the PR description below. --> <!-- ^ For example, instead of "Fixes #12345" use "Introduce new method to calculate 1 + 2". --> <!-- v Describe your changes below in detail. --> <!-- v Why is this change required? What problem does it solve? --> <!-- v If this PR resolves an open issue, please link to it here. For example, "Fixes #12345". --> As noted in #37642, there is a difference in the complexity of enumerating neighbors or edges between sparse graphs and dense graphs. This PR aims at making the comments about the time complexity of algorithms clearer in regard of this difference. Most of the changes I made in this PR are due to the fact that the function `init_short_digraph` have the following time complexity: - `O(n+m)` when the input graph has a sparse representation and sort_neighbors is False - `O(n+m log(m))` when the input graph has a sparse representation and sort_neighbors is True - `O(n^2)` when the input graph has a dense representation and sort_neighbors is False - `O(n^2 log(m))` when the input graph has a dense representation and sort_neighbors is True with `m` the number of edges and `n` the number of vertices of the graph. I spotted 5 additional complexity claims in the comments that I was not able to verify: 1. for strong_orientations_iterator in orientations.py 2. for yen_k_shortest_simple_paths in path_enumeration.pyx 3. for feng_k_shortest_simple_paths in path_enumeration.pyx 4. for edge_disjoint_spanning_trees in spanning_tree.pyx (there is loop over the sorted edges of the graphs, which should imply a complexity of at least `n+m log(m)` for sparse graphs and `O(n^2 log(m))` for dense graphs 5. for is_partial_cube in partial_cube.py (there is at least one enumeration of neighbors of a vertex (via calling `contracted[root]`, line 318) so `m` should appear in the complexity) ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #38198 Reported by: cyrilbouvier Reviewer(s): cyrilbouvier, David Coudert
2 parents a43e74a + c6f9a50 commit 080cebe

File tree

9 files changed

+73
-24
lines changed

9 files changed

+73
-24
lines changed

src/sage/graphs/base/dense_graph.pyx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ from ``CGraph`` (for explanation, refer to the documentation there)::
9797
It also contains the following variables::
9898
9999
cdef binary_matrix_t edges
100+
101+
.. NOTE::
102+
103+
As the edges are stored as the adjacency matrix of the graph, enumerating
104+
the edges of the graph has complexity `O(n^2)` and enumerating the neighbors
105+
of a vertex has complexity `O(n)` (where `n` in the size of the bitset
106+
active_vertices).
107+
So, the class ``DenseGraph`` should be used for graphs such that the number
108+
of edges is close to the square of the number of vertices.
109+
100110
"""
101111

102112
# ****************************************************************************

src/sage/graphs/base/static_sparse_graph.pyx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,13 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled=False,
231231
complexity of some methods. More precisely:
232232
233233
- When set to ``True``, the time complexity for initializing ``g`` is in
234-
`O(m + m\log{m})` and deciding if ``g`` has edge `(u, v)` can be done in
234+
`O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})` for
235+
``DenseGraph``, and deciding if ``g`` has edge `(u, v)` can be done in
235236
time `O(\log{m})` using binary search.
236237
237238
- When set to ``False``, the time complexity for initializing ``g`` is
238-
reduced to `O(n + m)` but the time complexity for deciding if ``g`` has
239+
reduced to `O(n + m)` for ``SparseGraph`` and `O(n^2)` for
240+
``DenseGraph``, but the time complexity for deciding if ``g`` has
239241
edge `(u, v)` increases to `O(m)`.
240242
"""
241243
g.edge_labels = NULL

src/sage/graphs/convexity_properties.pyx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,11 @@ def geodetic_closure(G, S):
507507
each vertex `u \in S`, the algorithm first performs a breadth first search
508508
from `u` to get distances, and then identifies the vertices of `G` lying on
509509
a shortest path from `u` to any `v\in S` using a reversal traversal from
510-
vertices in `S`. This algorithm has time complexity in `O(|S|(n + m))` and
511-
space complexity in `O(n + m)`.
510+
vertices in `S`. This algorithm has time complexity in
511+
`O(|S|(n + m) + (n + m\log{m}))` for ``SparseGraph``,
512+
`O(|S|(n + m) + n^2\log{m})` for ``DenseGraph`` and space complexity in
513+
`O(n + m)` (the extra `\log` factor is due to ``init_short_digraph`` being
514+
called with ``sort_neighbors=True``).
512515
513516
INPUT:
514517
@@ -678,10 +681,10 @@ def is_geodetic(G):
678681
Check whether the input (di)graph is geodetic.
679682
680683
A graph `G` is *geodetic* if there exists only one shortest path between
681-
every pair of its vertices. This can be checked in time `O(nm)` in
682-
unweighted (di)graphs with `n` nodes and `m` edges. Examples of geodetic
683-
graphs are trees, cliques and odd cycles. See the
684-
:wikipedia:`Geodetic_graph` for more details.
684+
every pair of its vertices. This can be checked in time `O(nm)` for
685+
``SparseGraph`` and `O(nm+n^2)` for ``DenseGraph`` in unweighted (di)graphs
686+
with `n` nodes and `m` edges. Examples of geodetic graphs are trees, cliques
687+
and odd cycles. See the :wikipedia:`Geodetic_graph` for more details.
685688
686689
(Di)graphs with multiple edges are not considered geodetic.
687690

src/sage/graphs/distances_all_pairs.pyx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,11 @@ def diameter(G, algorithm=None, source=None):
17501750
error if the initial vertex is not in `G`. This parameter is not used
17511751
when ``algorithm=='standard'``.
17521752
1753+
.. NOTE::
1754+
As the graph is first converted to a short_digraph, all complexity
1755+
have an extra `O(m+n)` for ``SparseGraph`` and `O(n^2)` for
1756+
``DenseGraph``.
1757+
17531758
EXAMPLES::
17541759
17551760
sage: from sage.graphs.distances_all_pairs import diameter
@@ -2278,6 +2283,14 @@ def szeged_index(G, algorithm=None):
22782283
By default (``None``), the ``"low"`` algorithm is used for graphs and the
22792284
``"high"`` algorithm for digraphs.
22802285
2286+
.. NOTE::
2287+
As the graph is converted to a short_digraph, the complexity for the
2288+
case ``algorithm == "high"`` has an extra `O(m+n)` for ``SparseGraph``
2289+
and `O(n^2)` for ``DenseGraph``. If ``algorithm == "low"``, the extra
2290+
complexity is `O(n + m\log{m})` for ``SparseGraph`` and `O(n^2\log{m})`
2291+
for ``DenseGraph`` (because ``init_short_digraph`` is called with
2292+
``sort_neighbors=True``).
2293+
22812294
EXAMPLES:
22822295
22832296
True for any connected graph [KRG1996]_::

src/sage/graphs/generic_graph.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4572,8 +4572,9 @@ def eulerian_orientation(self):
45724572
has no non-oriented edge (this vertex must have odd degree), the walk
45734573
resumes at another vertex of odd degree, if any.
45744574

4575-
This algorithm has complexity `O(m)`, where `m` is the number of edges
4576-
in the graph.
4575+
This algorithm has complexity `O(n+m)` for ``SparseGraph`` and `O(n^2)`
4576+
for ``DenseGraph``, where `m` is the number of edges in the graph and
4577+
`n` is the number of vertices in the graph.
45774578

45784579
EXAMPLES:
45794580

@@ -14905,7 +14906,8 @@ def is_chordal(self, certificate=False, algorithm="B"):
1490514906
ALGORITHM:
1490614907

1490714908
This method implements the algorithm proposed in [RT1975]_ for the
14908-
recognition of chordal graphs with time complexity in `O(m)`. The
14909+
recognition of chordal graphs. The time complexity of this algorithm is
14910+
`O(n+m)` for ``SparseGraph`` and `O(n^2)` for ``DenseGraph``. The
1490914911
algorithm works through computing a Lex BFS on the graph, then checking
1491014912
whether the order is a Perfect Elimination Order by computing for each
1491114913
vertex `v` the subgraph induced by its non-deleted neighbors, then

src/sage/graphs/graph.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3047,7 +3047,8 @@ def strong_orientation(self):
30473047
.. NOTE::
30483048
30493049
- This method assumes the graph is connected.
3050-
- This algorithm works in O(m).
3050+
- This time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)`
3051+
for ``DenseGraph`` .
30513052
30523053
.. SEEALSO::
30533054

src/sage/graphs/graph_decompositions/clique_separators.pyx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal
172172
:meth:`~sage.graphs.traversals.maximum_cardinality_search_M` graph traversal
173173
and has time complexity in `O(|V|\cdot|E|)`.
174174
175+
.. NOTE::
176+
As the graph is converted to a short_digraph (with
177+
``sort_neighbors=True``), the complexity has an extra
178+
`O(|V|+|E|\log{|E|})` for ``SparseGraph`` and `O(|V|^2\log{|E|})` for
179+
``DenseGraph``.
180+
175181
If the graph is not connected, we insert empty separators between the lists
176182
of separators of each connected components. See the examples below for more
177183
details.

src/sage/graphs/traversals.pyx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast")
256256
- ``"fast"`` -- This algorithm uses the notion of *slices* to refine the
257257
position of the vertices in the ordering. The time complexity of this
258258
algorithm is in `O(n + m)`, and our implementation follows that
259-
complexity. See [HMPV2000]_ and next section for more details.
259+
complexity for ``SparseGraph``. For ``DenseGraph``, the complexity is
260+
`O(n^2)`. See [HMPV2000]_ and next section for more details.
260261
261262
ALGORITHM:
262263
@@ -505,8 +506,9 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None):
505506
appended to the codes of all neighbors of the selected vertex that are left
506507
in the graph.
507508
508-
Time complexity is `O(n+m)` where `n` is the number of vertices and `m` is
509-
the number of edges.
509+
Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for
510+
``DenseGraph`` where `n` is the number of vertices and `m` is the number of
511+
edges.
510512
511513
See [Mil2017]_ for more details on the algorithm.
512514
@@ -677,8 +679,9 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None):
677679
codes are updated. Lex DFS differs from Lex BFS only in the way codes are
678680
updated after each iteration.
679681
680-
Time complexity is `O(n+m)` where `n` is the number of vertices and `m` is
681-
the number of edges.
682+
Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for
683+
``DenseGraph`` where `n` is the number of vertices and `m` is the number of
684+
edges.
682685
683686
See [CK2008]_ for more details on the algorithm.
684687
@@ -851,8 +854,9 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None):
851854
prepended to the codes of all neighbors of the selected vertex that are left
852855
in the graph.
853856
854-
Time complexity is `O(n+m)` where `n` is the number of vertices and `m` is
855-
the number of edges.
857+
Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for
858+
``DenseGraph`` where `n` is the number of vertices and `m` is the number of
859+
edges.
856860
857861
See [Mil2017]_ for more details on the algorithm.
858862
@@ -1582,6 +1586,10 @@ def maximum_cardinality_search(G, reverse=False, tree=False, initial_vertex=None
15821586
chosen at each step `i` to be placed in position `n - i` in `\alpha`. This
15831587
ordering can be computed in time `O(n + m)`.
15841588
1589+
Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for
1590+
``DenseGraph`` where `n` is the number of vertices and `m` is the number of
1591+
edges.
1592+
15851593
When the graph is chordal, the ordering returned by MCS is a *perfect
15861594
elimination ordering*, like :meth:`~sage.graphs.traversals.lex_BFS`. So
15871595
this ordering can be used to recognize chordal graphs. See [He2006]_ for

src/sage/graphs/weakly_chordal.pyx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ def is_long_hole_free(g, certificate=False):
163163
This is done through a depth-first-search. For efficiency, the auxiliary
164164
graph is constructed on-the-fly and never stored in memory.
165165
166-
The run time of this algorithm is `O(m^2)` [NP2007]_ ( where
167-
`m` is the number of edges of the graph ) .
166+
The run time of this algorithm is `O(n+m^2)` for ``SparseGraph`` and
167+
`O(n^2 + m^2)` for ``DenseGraph`` [NP2007]_ (where `n` is the number of
168+
vertices and `m` is the number of edges of the graph).
168169
169170
EXAMPLES:
170171
@@ -393,8 +394,9 @@ def is_long_antihole_free(g, certificate=False):
393394
This is done through a depth-first-search. For efficiency, the auxiliary
394395
graph is constructed on-the-fly and never stored in memory.
395396
396-
The run time of this algorithm is `O(m^2)` [NP2007]_ (where
397-
`m` is the number of edges of the graph).
397+
The run time of this algorithm is `O(n+m^2)` for ``SparseGraph`` and
398+
`O(n^2\log{m} + m^2)` for ``DenseGraph`` [NP2007]_ (where `n` is the number
399+
of vertices and `m` is the number of edges of the graph).
398400
399401
EXAMPLES:
400402
@@ -526,7 +528,9 @@ def is_weakly_chordal(g, certificate=False):
526528
contain an induced cycle of length at least 5.
527529
528530
Using is_long_hole_free() and is_long_antihole_free() yields a run time
529-
of `O(m^2)` (where `m` is the number of edges of the graph).
531+
of `O(n+m^2)` for ``SparseGraph`` and `O(n^2\log{m} + m^2)` for
532+
``DenseGraph`` (where `n` is the number of vertices and `m` is the number of
533+
edges of the graph).
530534
531535
EXAMPLES:
532536

0 commit comments

Comments
 (0)