| |
| |
|
|
| 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 |