|
|
|
|
|
|
|
import cython |
|
|
|
cimport numpy |
|
from cython.parallel cimport parallel, prange |
|
|
|
import numpy as np |
|
|
|
|
|
|
|
UNREACHABLE_NODE_DISTANCE = 510 |
|
|
|
def floyd_warshall(adjacency_matrix): |
|
""" |
|
Applies the Floyd-Warshall algorithm to the adjacency matrix, to compute the |
|
shortest paths distance between all nodes, up to UNREACHABLE_NODE_DISTANCE. |
|
""" |
|
(nrows, ncols) = adjacency_matrix.shape |
|
assert nrows == ncols |
|
cdef unsigned int n = nrows |
|
|
|
adj_mat_copy = adjacency_matrix.astype(np.int32, order='C', casting='safe', copy=True) |
|
assert adj_mat_copy.flags['C_CONTIGUOUS'] |
|
cdef numpy.ndarray[numpy.int32_t, ndim=2, mode='c'] M = adj_mat_copy |
|
cdef numpy.ndarray[numpy.int32_t, ndim=2, mode='c'] path = -1 * np.ones([n, n], dtype=np.int32) |
|
|
|
cdef unsigned int i, j, k |
|
cdef numpy.int32_t M_ij, M_ik, cost_ikkj |
|
cdef numpy.int32_t* M_ptr = &M[0,0] |
|
cdef numpy.int32_t* M_i_ptr |
|
cdef numpy.int32_t* M_k_ptr |
|
|
|
|
|
for i in range(n): |
|
for j in range(n): |
|
if i == j: |
|
M[i][j] = 0 |
|
elif M[i][j] == 0: |
|
M[i][j] = UNREACHABLE_NODE_DISTANCE |
|
|
|
|
|
for k in range(n): |
|
M_k_ptr = M_ptr + n*k |
|
for i in range(n): |
|
M_i_ptr = M_ptr + n*i |
|
M_ik = M_i_ptr[k] |
|
for j in range(n): |
|
cost_ikkj = M_ik + M_k_ptr[j] |
|
M_ij = M_i_ptr[j] |
|
if M_ij > cost_ikkj: |
|
M_i_ptr[j] = cost_ikkj |
|
path[i][j] = k |
|
|
|
|
|
for i in range(n): |
|
for j in range(n): |
|
if M[i][j] >= UNREACHABLE_NODE_DISTANCE: |
|
path[i][j] = UNREACHABLE_NODE_DISTANCE |
|
M[i][j] = UNREACHABLE_NODE_DISTANCE |
|
|
|
return M, path |
|
|
|
|
|
def get_all_edges(path, i, j): |
|
""" |
|
Recursive function to compute all possible paths between two nodes from the graph adjacency matrix. |
|
""" |
|
cdef int k = path[i][j] |
|
if k == -1: |
|
return [] |
|
else: |
|
return get_all_edges(path, i, k) + [k] + get_all_edges(path, k, j) |
|
|
|
|
|
def gen_edge_input(max_dist, path, edge_feat): |
|
""" |
|
Generates the full edge feature and adjacency matrix. |
|
Shape: num_nodes * num_nodes * max_distance_between_nodes * num_edge_features |
|
Dim 1 is the input node, dim 2 the output node of the edge, dim 3 the depth of the edge, dim 4 the feature |
|
""" |
|
(nrows, ncols) = path.shape |
|
assert nrows == ncols |
|
cdef unsigned int n = nrows |
|
cdef unsigned int max_dist_copy = max_dist |
|
|
|
path_copy = path.astype(long, order='C', casting='safe', copy=True) |
|
edge_feat_copy = edge_feat.astype(long, order='C', casting='safe', copy=True) |
|
assert path_copy.flags['C_CONTIGUOUS'] |
|
assert edge_feat_copy.flags['C_CONTIGUOUS'] |
|
|
|
cdef numpy.ndarray[numpy.int32_t, ndim=4, mode='c'] edge_fea_all = -1 * np.ones([n, n, max_dist_copy, edge_feat.shape[-1]], dtype=np.int32) |
|
cdef unsigned int i, j, k, num_path, cur |
|
|
|
for i in range(n): |
|
for j in range(n): |
|
if i == j: |
|
continue |
|
if path_copy[i][j] == UNREACHABLE_NODE_DISTANCE: |
|
continue |
|
path = [i] + get_all_edges(path_copy, i, j) + [j] |
|
num_path = len(path) - 1 |
|
for k in range(num_path): |
|
edge_fea_all[i, j, k, :] = edge_feat_copy[path[k], path[k+1], :] |
|
|
|
return edge_fea_all |