CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
graph.py163 linesDownload Raw Back to utils
1"""Graph utilities and algorithms."""
2
3# Authors: The scikit-learn developers
4# SPDX-License-Identifier: BSD-3-Clause
5
6import numpy as np
7from scipy import sparse
8
9from ..metrics.pairwise import pairwise_distances
10from ._param_validation import Integral, Interval, validate_params
11
12
13###############################################################################
14# Path and connected component analysis.
15# Code adapted from networkx
16@validate_params(
17    {
18        "graph": ["array-like", "sparse matrix"],
19        "source": [Interval(Integral, 0, None, closed="left")],
20        "cutoff": [Interval(Integral, 0, None, closed="left"), None],
21    },
22    prefer_skip_nested_validation=True,
23)
24def single_source_shortest_path_length(graph, source, *, cutoff=None):
25    """Return the length of the shortest path from source to all reachable nodes.
26
27    Parameters
28    ----------
29    graph : {array-like, sparse matrix} of shape (n_nodes, n_nodes)
30        Adjacency matrix of the graph. Sparse matrix of format LIL is
31        preferred.
32
33    source : int
34       Start node for path.
35
36    cutoff : int, default=None
37        Depth to stop the search - only paths of length <= cutoff are returned.
38
39    Returns
40    -------
41    paths : dict
42        Reachable end nodes mapped to length of path from source,
43        i.e. `{end: path_length}`.
44
45    Examples
46    --------
47    >>> from sklearn.utils.graph import single_source_shortest_path_length
48    >>> import numpy as np
49    >>> graph = np.array([[ 0, 1, 0, 0],
50    ...                   [ 1, 0, 1, 0],
51    ...                   [ 0, 1, 0, 0],
52    ...                   [ 0, 0, 0, 0]])
53    >>> single_source_shortest_path_length(graph, 0)
54    {0: 0, 1: 1, 2: 2}
55    >>> graph = np.ones((6, 6))
56    >>> sorted(single_source_shortest_path_length(graph, 2).items())
57    [(0, 1), (1, 1), (2, 0), (3, 1), (4, 1), (5, 1)]
58    """
59    if sparse.issparse(graph):
60        graph = graph.tolil()
61    else:
62        graph = sparse.lil_matrix(graph)
63    seen = {}  # level (number of hops) when seen in BFS
64    level = 0  # the current level
65    next_level = [source]  # dict of nodes to check at next level
66    while next_level:
67        this_level = next_level  # advance to next level
68        next_level = set()  # and start a new list (fringe)
69        for v in this_level:
70            if v not in seen:
71                seen[v] = level  # set the level of vertex v
72                next_level.update(graph.rows[v])
73        if cutoff is not None and cutoff <= level:
74            break
75        level += 1
76    return seen  # return all path lengths as dictionary
77
78
79def _fix_connected_components(
80    X,
81    graph,
82    n_connected_components,
83    component_labels,
84    mode="distance",
85    metric="euclidean",
86    **kwargs,
87):
88    """Add connections to sparse graph to connect unconnected components.
89
90    For each pair of unconnected components, compute all pairwise distances
91    from one component to the other, and add a connection on the closest pair
92    of samples. This is a hacky way to get a graph with a single connected
93    component, which is necessary for example to compute a shortest path
94    between all pairs of samples in the graph.
95
96    Parameters
97    ----------
98    X : array of shape (n_samples, n_features) or (n_samples, n_samples)
99        Features to compute the pairwise distances. If `metric =
100        "precomputed"`, X is the matrix of pairwise distances.
101
102    graph : sparse matrix of shape (n_samples, n_samples)
103        Graph of connection between samples.
104
105    n_connected_components : int
106        Number of connected components, as computed by
107        `scipy.sparse.csgraph.connected_components`.
108
109    component_labels : array of shape (n_samples)
110        Labels of connected components, as computed by
111        `scipy.sparse.csgraph.connected_components`.
112
113    mode : {'connectivity', 'distance'}, default='distance'
114        Type of graph matrix: 'connectivity' corresponds to the connectivity
115        matrix with ones and zeros, and 'distance' corresponds to the distances
116        between neighbors according to the given metric.
117
118    metric : str
119        Metric used in `sklearn.metrics.pairwise.pairwise_distances`.
120
121    kwargs : kwargs
122        Keyword arguments passed to
123        `sklearn.metrics.pairwise.pairwise_distances`.
124
125    Returns
126    -------
127    graph : sparse matrix of shape (n_samples, n_samples)
128        Graph of connection between samples, with a single connected component.
129    """
130    if metric == "precomputed" and sparse.issparse(X):
131        raise RuntimeError(
132            "_fix_connected_components with metric='precomputed' requires the "
133            "full distance matrix in X, and does not work with a sparse "
134            "neighbors graph."
135        )
136
137    for i in range(n_connected_components):
138        idx_i = np.flatnonzero(component_labels == i)
139        Xi = X[idx_i]
140        for j in range(i):
141            idx_j = np.flatnonzero(component_labels == j)
142            Xj = X[idx_j]
143
144            if metric == "precomputed":
145                D = X[np.ix_(idx_i, idx_j)]
146            else:
147                D = pairwise_distances(Xi, Xj, metric=metric, **kwargs)
148
149            ii, jj = np.unravel_index(D.argmin(axis=None), D.shape)
150            if mode == "connectivity":
151                graph[idx_i[ii], idx_j[jj]] = 1
152                graph[idx_j[jj], idx_i[ii]] = 1
153            elif mode == "distance":
154                graph[idx_i[ii], idx_j[jj]] = D[ii, jj]
155                graph[idx_j[jj], idx_i[ii]] = D[ii, jj]
156            else:
157                raise ValueError(
158                    "Unknown mode=%r, should be one of ['connectivity', 'distance']."
159                    % mode
160                )
161
162    return graph
163 
Aluode/PerceptionLabPortable · CoolFace