Skip to content

Commit

Permalink
sagemathgh-38167: New methods for sparse graphs backend to enumerate …
Browse files Browse the repository at this point in the history
…neighbors in linear time

    
<!-- ^ 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 sagemath#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 sagemath#12345". -->

As noted in issue sagemath#37642, enumerating the neighbors of a vertex for a
graph that use the sparse backend (the default in Sage) is **not**
linear on the number of neighbors (there is an extra log factor).
The goal of this PR is to fix this.

The problem is that to enumerate the neighbors of a vertex, the current
code calls iteratively _next_neighbor_unsafe (or one of its out or in
variant). But this method return an int corresponding to a neighbor, so
to go to the next neighbor, it was first necessary to retrieve this
vertex* in the structure storing all the neighbors. As it is stored in a
sorted binary tree, the cost of retrieving is O(log(#neighbors)).

*(or a close one if it was deleted in the meantime)

To obtain a linear complexity, I wrote a new method that do a simple
tree traversal which is linear in the number of nodes in the tree (i.e.,
the number of neighbors). As this new method does not have a similar
interface as the previous _next_neighbor_unsafe, some more rewriting was
necessary to use it in other parts of the code.

1. I wrote new methods for the SparseGraph class: out_neighbors_unsafe
and in_neighbors_unsafe. They overwrite the ones from the base class and
rely on the new methods _neighbors_unsafe and _neighbors_BTNode_unsafe.
The _neighbors_BTNode_unsafe is a low-level method enumerating the
neighbors in linear time.

2. I rewrote the two methods out_neighbors_BTNode_unsafe and
in_neighbors_BTNode_unsafe to use the new _neighbors_BTNode_unsafe.

3. I wrote a new method _iterator_edges method for SparseGraphBackend to
overwrite the one from the base class
  CGraphBackend, in order to expose the low-level code to the Graph
class.



Question:
During the writing of this PR, I notice that the methods
out_neighbors_BTNode_unsafe and in_neighbors_BTNode_unsafe are only
defined for the sparse backend and are not used anywhere in the code.
Can I remove them ? Or should they be kept for compatibility ?

### 📝 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,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#38167
Reported by: cyrilbouvier
Reviewer(s): cyrilbouvier, David Coudert
  • Loading branch information
Release Manager committed Jun 16, 2024
2 parents 66b3512 + b7e644e commit cee5890
Show file tree
Hide file tree
Showing 3 changed files with 307 additions and 81 deletions.
17 changes: 8 additions & 9 deletions src/sage/graphs/base/c_graph.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5022,8 +5022,8 @@ cdef inline bint _reorganize_edge(object v, object u, const int modus) noexcept:
- ``modus`` -- integer representing the modus of the iterator:
- ``0`` -- outgoing edges
- ``1`` -- ingoing edges
- ``3`` -- unsorted edges of an undirected graph
- ``4`` -- sorted edges of an undirected graph
- ``2`` -- unsorted edges of an undirected graph
- ``3`` -- sorted edges of an undirected graph
OUTPUT: Boolean according the modus:
Expand All @@ -5034,12 +5034,11 @@ cdef inline bint _reorganize_edge(object v, object u, const int modus) noexcept:
"""
if modus == 0:
return False
if modus == 1 or modus == 2:
return True
elif modus == 3:
try:
if v <= u:
return False
except TypeError:
pass

try:
if v <= u:
return False
except TypeError:
pass
return True
19 changes: 17 additions & 2 deletions src/sage/graphs/base/sparse_graph.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ cdef class SparseGraph(CGraph):
cpdef int out_degree(self, int u) noexcept
cpdef int in_degree(self, int u) noexcept

cdef int out_neighbors_BTNode_unsafe(self, int u, SparseGraphBTNode *** p_pointers) noexcept
cdef int in_neighbors_BTNode_unsafe(self, int u, SparseGraphBTNode *** p_pointers) noexcept
cdef inline int _neighbors_unsafe (self, int u, bint out, int *neighbors, int size) except -2

cdef inline int _neighbors_BTNode_unsafe (self, int u, bint out, SparseGraphBTNode **res, int size) except -2

cdef inline SparseGraphBTNode* next_out_neighbor_BTNode_unsafe(self, int u, int v) noexcept:
"""
Expand All @@ -50,6 +51,13 @@ cdef class SparseGraph(CGraph):
If ``v`` is ``-1`` return the first neighbor of ``u``.
Return ``NULL`` in case there does not exist such an out-neighbor.
.. WARNING::
Repeated calls to this function until NULL is returned DOES NOT
yield a linear time algorithm in the number of neighbors of u.
To list the neighbors of a vertex in linear time, one should use
_neighbors_BTNode_unsafe.
"""
return self.next_neighbor_BTNode_unsafe(self.vertices, u, v)

Expand All @@ -60,6 +68,13 @@ cdef class SparseGraph(CGraph):
If ``u`` is ``-1`` return the first neighbor of ``v``.
Return ``NULL`` in case there does not exist such an in-neighbor.
.. WARNING::
Repeated calls to this function until NULL is returned DOES NOT
yield a linear time algorithm in the number of neighbors of u.
To list the neighbors of a vertex in linear time, one should use
_neighbors_BTNode_unsafe.
"""
return self.next_neighbor_BTNode_unsafe(self.vertices_rev, v, u)

Expand Down
Loading

0 comments on commit cee5890

Please sign in to comment.