|
| 1 | +#include "ego_graph.h" |
| 2 | +#include "subgraph.h" |
| 3 | +#include "../../classes/graph.h" |
| 4 | +#include "../../classes/directed_graph.h" |
| 5 | +#include "../../classes/csr_graph.h" |
| 6 | +#include "../../common/utils.h" |
| 7 | +#include "../../classes/linkgraph.h" |
| 8 | +#include "../path/path.h" |
| 9 | +#include <algorithm> |
| 10 | + |
| 11 | + |
| 12 | +struct _EgoGraphCore { |
| 13 | + Graph_L G_l; |
| 14 | + std::vector<node_t> node_ids; |
| 15 | + std::unordered_map<node_t, int> node_to_idx; |
| 16 | + py::dict id_to_node_py; |
| 17 | + bool has_error = false; |
| 18 | +}; |
| 19 | + |
| 20 | + |
| 21 | +static _EgoGraphCore _cpp_ego_graph_compute_impl( |
| 22 | + Graph& G_, |
| 23 | + py::object n, |
| 24 | + double radius_val, |
| 25 | + bool center_val, |
| 26 | + bool undirected_val, |
| 27 | + bool is_directed, |
| 28 | + const std::string& weight_key) { |
| 29 | + |
| 30 | + _EgoGraphCore out; |
| 31 | + |
| 32 | + if (G_.node_to_id.contains(n) == 0) { |
| 33 | + PyErr_Format(PyExc_KeyError, "Node %R is not in the graph.", n.ptr()); |
| 34 | + out.has_error = true; |
| 35 | + return out; |
| 36 | + } |
| 37 | + |
| 38 | + node_t center_id = G_.node_to_id[n].cast<node_t>(); |
| 39 | + out.id_to_node_py = G_.id_to_node; |
| 40 | + |
| 41 | + if (G_.linkgraph_dirty || G_.linkgraph_structure.max_deg == -1) { |
| 42 | + if (undirected_val) { |
| 43 | + out.G_l = graph_to_linkgraph(G_, false, weight_key, true, false); |
| 44 | + } else { |
| 45 | + out.G_l = graph_to_linkgraph(G_, is_directed, weight_key, true, false); |
| 46 | + } |
| 47 | + G_.linkgraph_dirty = false; |
| 48 | + G_.linkgraph_structure = out.G_l; // also cache the freshly built linkgraph |
| 49 | + } else { |
| 50 | + out.G_l = G_.linkgraph_structure; |
| 51 | + } |
| 52 | + |
| 53 | + std::vector<double> dist = _dijkstra(out.G_l, center_id, -1); |
| 54 | + int N = out.G_l.n; |
| 55 | + for (int i = 1; i <= N; i++) { |
| 56 | + if (dist[i] > radius_val) continue; |
| 57 | + if (!center_val && i == (int)center_id) continue; |
| 58 | + out.node_to_idx[i] = (int)out.node_ids.size(); |
| 59 | + out.node_ids.push_back(i); |
| 60 | + } |
| 61 | + return out; |
| 62 | +} |
| 63 | + |
| 64 | + |
| 65 | +static _EgoGraphCore _cpp_ego_graph_compute( |
| 66 | + py::object G, |
| 67 | + py::object n, |
| 68 | + py::object radius, |
| 69 | + py::object center, |
| 70 | + py::object undirected, |
| 71 | + py::object distance) { |
| 72 | + |
| 73 | + bool is_directed = G.attr("is_directed")().cast<bool>(); |
| 74 | + bool center_val = center.cast<bool>(); |
| 75 | + bool undirected_val = undirected.cast<bool>(); |
| 76 | + double radius_val = radius.cast<double>(); |
| 77 | + std::string weight_key = weight_to_string(distance); |
| 78 | + |
| 79 | + if (is_directed) { |
| 80 | + DiGraph& G_ = G.cast<DiGraph&>(); |
| 81 | + return _cpp_ego_graph_compute_impl( |
| 82 | + G_, n, radius_val, center_val, undirected_val, is_directed, weight_key); |
| 83 | + } else { |
| 84 | + Graph& G_ = G.cast<Graph&>(); |
| 85 | + return _cpp_ego_graph_compute_impl( |
| 86 | + G_, n, radius_val, center_val, undirected_val, is_directed, weight_key); |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | + |
| 91 | +py::object cpp_ego_graph( |
| 92 | + py::object G, |
| 93 | + py::object n, |
| 94 | + py::object radius, |
| 95 | + py::object center, |
| 96 | + py::object undirected, |
| 97 | + py::object distance) { |
| 98 | + |
| 99 | + _EgoGraphCore core = _cpp_ego_graph_compute(G, n, radius, center, undirected, distance); |
| 100 | + if (core.has_error) return py::none(); |
| 101 | + |
| 102 | + return nodes_subgraph_cpp(G, core.node_ids); |
| 103 | +} |
| 104 | + |
| 105 | + |
| 106 | +py::object cpp_ego_graph_csr( |
| 107 | + py::object G, |
| 108 | + py::object n, |
| 109 | + py::object radius, |
| 110 | + py::object center, |
| 111 | + py::object undirected, |
| 112 | + py::object distance) { |
| 113 | + |
| 114 | + _EgoGraphCore core = _cpp_ego_graph_compute(G, n, radius, center, undirected, distance); |
| 115 | + if (core.has_error) return py::none(); |
| 116 | + |
| 117 | + int n_nodes = (int)core.node_ids.size(); |
| 118 | + if (n_nodes == 0) { |
| 119 | + return py::dict(); |
| 120 | + } |
| 121 | + |
| 122 | + std::vector<int> V(n_nodes + 1, 0); |
| 123 | + std::vector<int> E; |
| 124 | + std::vector<double> W; |
| 125 | + |
| 126 | + for (int new_u = 0; new_u < n_nodes; new_u++) { |
| 127 | + node_t u = core.node_ids[new_u]; |
| 128 | + V[new_u + 1] = V[new_u]; |
| 129 | + |
| 130 | + int edge_idx = core.G_l.head[u]; |
| 131 | + while (edge_idx != -1 && edge_idx < core.G_l.e) { |
| 132 | + node_t v = core.G_l.edges[edge_idx].to; |
| 133 | + auto it = core.node_to_idx.find(v); |
| 134 | + if (it != core.node_to_idx.end()) { |
| 135 | + E.push_back(it->second); |
| 136 | + W.push_back(core.G_l.edges[edge_idx].w); |
| 137 | + V[new_u + 1]++; |
| 138 | + } |
| 139 | + edge_idx = core.G_l.edges[edge_idx].next; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + py::list original_nodes; |
| 144 | + for (node_t internal_id : core.node_ids) { |
| 145 | + original_nodes.append(core.id_to_node_py[py::cast(internal_id)]); |
| 146 | + } |
| 147 | + |
| 148 | + py::dict result; |
| 149 | + result["nodes"] = original_nodes; |
| 150 | + result["V"] = py::cast(V); |
| 151 | + result["E"] = py::cast(E); |
| 152 | + result["W"] = py::cast(W); |
| 153 | + |
| 154 | + return result; |
| 155 | +} |
0 commit comments