|
| 1 | +from graphblas import Matrix, Vector, binary |
| 2 | +from graphblas.select import offdiag |
| 3 | +from graphblas.semiring import any_plus |
| 4 | + |
| 5 | +__all__ = ["floyd_warshall"] |
| 6 | + |
| 7 | + |
| 8 | +def floyd_warshall(G, is_weighted=False): |
| 9 | + # By using `offdiag` instead of `G._A`, we ensure that D will not become dense. |
| 10 | + # Dense D may be better at times, but not including the diagonal will result in less work. |
| 11 | + # Typically, Floyd-Warshall algorithms sets the diagonal of D to 0 at the beginning. |
| 12 | + # This is unnecessary with sparse matrices, and we set the diagonal to 0 at the end. |
| 13 | + # We also don't iterate over index `i` if either row i or column i are empty. |
| 14 | + if G.is_directed(): |
| 15 | + A, row_degrees, column_degrees = G.get_properties("offdiag row_degrees- column_degrees-") |
| 16 | + nonempty_nodes = binary.pair(row_degrees & column_degrees).new(name="nonempty_nodes") |
| 17 | + else: |
| 18 | + A, nonempty_nodes = G.get_properties("offdiag degrees-") |
| 19 | + |
| 20 | + if A.dtype == bool or not is_weighted: |
| 21 | + dtype = int |
| 22 | + else: |
| 23 | + dtype = A.dtype |
| 24 | + n = A.nrows |
| 25 | + D = Matrix(dtype, nrows=n, ncols=n, name="floyd_warshall") |
| 26 | + if is_weighted: |
| 27 | + D << A |
| 28 | + else: |
| 29 | + D(A.S) << 1 # Like `D << unary.one[int](A)` |
| 30 | + del A |
| 31 | + |
| 32 | + Row = Matrix(dtype, nrows=1, ncols=n, name="Row") |
| 33 | + Col = Matrix(dtype, nrows=n, ncols=1, name="Col") |
| 34 | + Outer = Matrix(dtype, nrows=n, ncols=n, name="Outer") |
| 35 | + for i in nonempty_nodes: |
| 36 | + Col << D[:, [i]] |
| 37 | + Row << D[[i], :] |
| 38 | + Outer << any_plus(Col @ Row) # Like `col.outer(row, binary.plus)` |
| 39 | + D(binary.min) << offdiag(Outer) |
| 40 | + |
| 41 | + # Set diagonal values to 0 (this way seems fast). |
| 42 | + # The missing values are implied to be infinity, so we set diagonals explicitly to 0. |
| 43 | + mask = Vector(bool, size=n, name="mask") |
| 44 | + mask << True |
| 45 | + Mask = mask.diag(name="Mask") |
| 46 | + D(Mask.S) << 0 |
| 47 | + return D |
0 commit comments