diff --git a/pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp b/pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp index 9bcb4ee9..a25274eb 100644 --- a/pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp +++ b/pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp @@ -19,6 +19,10 @@ typedef struct { std::vector nodes; std::unordered_map edges; std::unordered_map node_map; + std::unordered_map id_map; + std::unordered_map id_to_name; + std::unordered_map name_to_id; + int next_id; } AdjacencyListGraph; @@ -34,6 +38,9 @@ static void AdjacencyListGraph_dealloc(AdjacencyListGraph* self) { self->edges.clear(); self->node_map.clear(); + self->id_map.clear(); + self->id_to_name.clear(); + self->name_to_id.clear(); Py_TYPE(self)->tp_free(reinterpret_cast(self)); } @@ -46,17 +53,17 @@ static PyObject* AdjacencyListGraph_new(PyTypeObject* type, PyObject* args, PyOb new (&self->nodes) std::vector(); new (&self->edges) std::unordered_map(); new (&self->node_map) std::unordered_map(); + new (&self->id_map) std::unordered_map(); + new (&self->id_to_name) std::unordered_map(); + new (&self->name_to_id) std::unordered_map(); + + self->next_id = 0; Py_ssize_t num_args = PyTuple_Size(args); for (Py_ssize_t i = 0; i < num_args; ++i) { PyObject* node_obj = PyTuple_GetItem(args, i); if (!PyObject_IsInstance(node_obj, (PyObject*)&AdjacencyListGraphNodeType)) { PyErr_SetString(PyExc_TypeError, "All arguments must be AdjacencyListGraphNode instances"); - - self->nodes.~vector(); - self->edges.~unordered_map(); - self->node_map.~unordered_map(); - Py_TYPE(self)->tp_free(reinterpret_cast(self)); return NULL; } @@ -64,20 +71,20 @@ static PyObject* AdjacencyListGraph_new(PyTypeObject* type, PyObject* args, PyOb if (self->node_map.find(node->name) != self->node_map.end()) { PyErr_Format(PyExc_ValueError, "Duplicate node with name '%s'", node->name.c_str()); - - self->nodes.~vector(); - self->edges.~unordered_map(); - self->node_map.~unordered_map(); - Py_TYPE(self)->tp_free(reinterpret_cast(self)); return NULL; } + node->internal_id = self->next_id++; + Py_INCREF(node); self->nodes.push_back(node); self->node_map[node->name] = node; + self->id_map[node->internal_id] = node; + self->id_to_name[node->internal_id] = node->name; + self->name_to_id[node->name] = node->internal_id; } - PyObject* impl_str = PyUnicode_FromString("adjacency_list"); + PyObject* impl_str = PyUnicode_FromString("adjacency_list"); if (PyObject_SetAttrString(reinterpret_cast(self), "_impl", impl_str) < 0) { Py_DECREF(impl_str); PyErr_SetString(PyExc_RuntimeError, "Failed to set _impl attribute"); @@ -112,9 +119,14 @@ static PyObject* AdjacencyListGraph_add_vertex(AdjacencyListGraph* self, PyObjec return NULL; } + node->internal_id = self->next_id++; + Py_INCREF(node); self->nodes.push_back(node); self->node_map[node->name] = node; + self->id_map[node->internal_id] = node; + self->id_to_name[node->internal_id] = node->name; + self->name_to_id[node->name] = node->internal_id; Py_RETURN_NONE; } @@ -126,17 +138,15 @@ static PyObject* AdjacencyListGraph_is_adjacent(AdjacencyListGraph* self, PyObje if (!PyArg_ParseTuple(args, "ss", &node1_name_c, &node2_name_c)) return NULL; - std::string node1_name(node1_name_c); - std::string node2_name(node2_name_c); - - auto it1 = self->node_map.find(node1_name); - if (it1 == self->node_map.end()) { + auto it1 = self->name_to_id.find(node1_name_c); + if (it1 == self->name_to_id.end()) { PyErr_SetString(PyExc_KeyError, "node1 not found"); return NULL; } - AdjacencyListGraphNode* node1 = it1->second; + int id1 = it1->second; + AdjacencyListGraphNode* node1 = self->id_map[id1]; - if (node1->adjacent.find(node2_name) != node1->adjacent.end()) { + if (node1->adjacent.find(node2_name_c) != node1->adjacent.end()) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; @@ -156,13 +166,13 @@ static PyObject* AdjacencyListGraph_neighbors(AdjacencyListGraph* self, PyObject if (!PyArg_ParseTuple(args, "s", &node_name_c)) return NULL; - std::string node_name(node_name_c); - auto it = self->node_map.find(node_name); - if (it == self->node_map.end()) { + auto it = self->name_to_id.find(node_name_c); + if (it == self->name_to_id.end()) { PyErr_SetString(PyExc_KeyError, "Node not found"); return NULL; } - AdjacencyListGraphNode* node = it->second; + int id = it->second; + AdjacencyListGraphNode* node = self->id_map[id]; PyObject* neighbors_list = PyList_New(0); if (!neighbors_list) return NULL; @@ -180,23 +190,27 @@ static PyObject* AdjacencyListGraph_remove_vertex(AdjacencyListGraph* self, PyOb if (!PyArg_ParseTuple(args, "s", &name_c)) return NULL; - std::string name(name_c); - auto it = self->node_map.find(name); - if (it == self->node_map.end()) { + auto it = self->name_to_id.find(name_c); + if (it == self->name_to_id.end()) { PyErr_SetString(PyExc_KeyError, "Node not found"); return NULL; } - AdjacencyListGraphNode* node_to_remove = it->second; + int id = it->second; + AdjacencyListGraphNode* node_to_remove = self->id_map[id]; + std::string name = node_to_remove->name; auto& nodes_vec = self->nodes; nodes_vec.erase(std::remove(nodes_vec.begin(), nodes_vec.end(), node_to_remove), nodes_vec.end()); - self->node_map.erase(it); + self->node_map.erase(name); + self->id_map.erase(id); + self->name_to_id.erase(name); + self->id_to_name.erase(id); Py_XDECREF(node_to_remove); - for (auto& node_pair : self->node_map) { + for (auto& node_pair : self->id_map) { AdjacencyListGraphNode* node = node_pair.second; auto adj_it = node->adjacent.find(name); if (adj_it != node->adjacent.end()) { @@ -207,17 +221,11 @@ static PyObject* AdjacencyListGraph_remove_vertex(AdjacencyListGraph* self, PyOb for (auto it = self->edges.begin(); it != self->edges.end(); ) { const std::string& key = it->first; - - bool involves_node = false; size_t underscore = key.find('_'); - if (underscore != std::string::npos) { - std::string source = key.substr(0, underscore); - std::string target = key.substr(underscore + 1); - if (source == name || target == name) - involves_node = true; - } + std::string src = key.substr(0, underscore); + std::string dst = key.substr(underscore + 1); - if (involves_node) { + if (src == name || dst == name) { Py_XDECREF(it->second); it = self->edges.erase(it); } else { diff --git a/pydatastructs/graphs/_backend/cpp/Algorithms.hpp b/pydatastructs/graphs/_backend/cpp/Algorithms.hpp index 08279375..b260ff43 100644 --- a/pydatastructs/graphs/_backend/cpp/Algorithms.hpp +++ b/pydatastructs/graphs/_backend/cpp/Algorithms.hpp @@ -156,7 +156,6 @@ static PyObject* breadth_first_search_adjacency_matrix(PyObject* self, PyObject* } static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObject* args, PyObject* kwargs) { - PyObject* graph_obj; static const char* kwlist[] = {"graph", nullptr}; @@ -168,8 +167,8 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj AdjacencyListGraph* graph = reinterpret_cast(graph_obj); struct EdgeTuple { - std::string source; - std::string target; + int source_id; + int target_id; std::variant value; DataType value_type; @@ -187,26 +186,32 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj }; std::priority_queue, std::greater<>> pq; - std::unordered_set visited; + std::unordered_set visited; PyObject* mst_graph = PyObject_CallObject(reinterpret_cast(&AdjacencyListGraphType), nullptr); AdjacencyListGraph* mst = reinterpret_cast(mst_graph); - std::string start = graph->node_map.begin()->first; - visited.insert(start); + int start_id = graph->nodes[0]->internal_id; + visited.insert(start_id); - AdjacencyListGraphNode* start_node = graph->node_map[start]; + AdjacencyListGraphNode* start_node = graph->id_map[start_id]; + std::string start_name = graph->id_to_name[start_id]; Py_INCREF(start_node); mst->nodes.push_back(start_node); - mst->node_map[start] = start_node; + mst->node_map[start_name] = start_node; + mst->id_map[start_id] = start_node; + mst->id_to_name[start_id] = start_name; + mst->name_to_id[start_name] = start_id; for (const auto& [adj_name, _] : start_node->adjacent) { - std::string key = make_edge_key(start, adj_name); + int adj_id = graph->name_to_id[adj_name]; + std::string key = make_edge_key(start_name, adj_name); GraphEdge* edge = graph->edges[key]; + EdgeTuple et; - et.source = start; - et.target = adj_name; + et.source_id = start_id; + et.target_id = adj_id; et.value_type = edge->value_type; switch (edge->value_type) { @@ -230,27 +235,35 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj EdgeTuple edge = pq.top(); pq.pop(); - if (visited.count(edge.target)) continue; - visited.insert(edge.target); + if (visited.count(edge.target_id)) continue; + visited.insert(edge.target_id); + + int u_id = edge.source_id; + int v_id = edge.target_id; + std::string u_name = graph->id_to_name[u_id]; + std::string v_name = graph->id_to_name[v_id]; - for (const std::string& name : {edge.source, edge.target}) { - if (!mst->node_map.count(name)) { - AdjacencyListGraphNode* node = graph->node_map[name]; + for (auto [id, name] : std::vector>{{u_id, u_name}, {v_id, v_name}}) { + if (!mst->id_map.count(id)) { + AdjacencyListGraphNode* node = graph->id_map[id]; Py_INCREF(node); mst->nodes.push_back(node); mst->node_map[name] = node; + mst->id_map[id] = node; + mst->id_to_name[id] = name; + mst->name_to_id[name] = id; } } - AdjacencyListGraphNode* u = mst->node_map[edge.source]; - AdjacencyListGraphNode* v = mst->node_map[edge.target]; + AdjacencyListGraphNode* u = mst->id_map[u_id]; + AdjacencyListGraphNode* v = mst->id_map[v_id]; Py_INCREF(v); Py_INCREF(u); - u->adjacent[edge.target] = reinterpret_cast(v); - v->adjacent[edge.source] = reinterpret_cast(u); + u->adjacent[v_name] = reinterpret_cast(v); + v->adjacent[u_name] = reinterpret_cast(u); - std::string key_uv = make_edge_key(edge.source, edge.target); + std::string key_uv = make_edge_key(u_name, v_name); GraphEdge* new_edge = PyObject_New(GraphEdge, &GraphEdgeType); PyObject_Init(reinterpret_cast(new_edge), &GraphEdgeType); new (&new_edge->value) std::variant(edge.value); @@ -261,26 +274,28 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj new_edge->target = reinterpret_cast(v); mst->edges[key_uv] = new_edge; - std::string key_vu = make_edge_key(edge.target, edge.source); + std::string key_vu = make_edge_key(v_name, u_name); GraphEdge* new_edge_rev = PyObject_New(GraphEdge, &GraphEdgeType); PyObject_Init(reinterpret_cast(new_edge_rev), &GraphEdgeType); new (&new_edge_rev->value) std::variant(edge.value); new_edge_rev->value_type = edge.value_type; Py_INCREF(u); Py_INCREF(v); - new_edge_rev->source = reinterpret_cast(v); + new_edge_rev->source = reinterpret_cast(v); new_edge_rev->target = reinterpret_cast(u); mst->edges[key_vu] = new_edge_rev; - AdjacencyListGraphNode* next_node = graph->node_map[edge.target]; - + AdjacencyListGraphNode* next_node = graph->id_map[v_id]; for (const auto& [adj_name, _] : next_node->adjacent) { - if (visited.count(adj_name)) continue; - std::string key = make_edge_key(edge.target, adj_name); + int adj_id = graph->name_to_id[adj_name]; + if (visited.count(adj_id)) continue; + + std::string key = make_edge_key(v_name, adj_name); GraphEdge* adj_edge = graph->edges[key]; + EdgeTuple adj_et; - adj_et.source = edge.target; - adj_et.target = adj_name; + adj_et.source_id = v_id; + adj_et.target_id = adj_id; adj_et.value_type = adj_edge->value_type; switch (adj_edge->value_type) { @@ -300,6 +315,7 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj pq.push(adj_et); } } + return reinterpret_cast(mst); } @@ -316,49 +332,48 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject } AdjacencyListGraph* graph = reinterpret_cast(graph_obj); - const size_t V = graph->node_map.size(); - std::unordered_map dist; - std::unordered_map pred; - - for (const auto& [name, node] : graph->node_map) { - dist[name] = std::numeric_limits::infinity(); - pred[name] = ""; + std::vector>> adj(V); + for (const auto& [edge_key, edge] : graph->edges) { + size_t delim = edge_key.find('_'); + std::string u_name = edge_key.substr(0, delim); + std::string v_name = edge_key.substr(delim + 1); + int u = graph->name_to_id[u_name]; + int v = graph->name_to_id[v_name]; + + double weight = 0.0; + if (edge->value_type == DataType::Int) + weight = static_cast(std::get(edge->value)); + else if (edge->value_type == DataType::Double) + weight = std::get(edge->value); + else + continue; + + if (weight < 0) continue; + adj[u].emplace_back(v, weight); } - dist[source_name] = 0.0; - using PQEntry = std::pair; + std::vector dist(V, std::numeric_limits::infinity()); + std::vector pred(V, -1); + using PQEntry = std::pair; std::priority_queue, std::greater<>> pq; - pq.push({0.0, source_name}); + + int source_id = graph->name_to_id[source_name]; + dist[source_id] = 0.0; + pq.push({0.0, source_id}); while (!pq.empty()) { - auto [u_dist, u_name] = pq.top(); pq.pop(); - - if (u_dist > dist[u_name]) continue; - - AdjacencyListGraphNode* u = graph->node_map[u_name]; - for (const auto& [v_name, _] : u->adjacent) { - std::string edge_key = make_edge_key(u_name, v_name); - auto edge_it = graph->edges.find(edge_key); - if (edge_it == graph->edges.end()) continue; - - GraphEdge* edge = edge_it->second; - double weight = 0.0; - if (edge->value_type == DataType::Int) - weight = static_cast(std::get(edge->value)); - else if (edge->value_type == DataType::Double) - weight = std::get(edge->value); - else - continue; - - if (weight < 0) continue; - - double new_dist = dist[u_name] + weight; - if (new_dist < dist[v_name]) { - dist[v_name] = new_dist; - pred[v_name] = u_name; - pq.push({new_dist, v_name}); + auto [u_dist, u_id] = pq.top(); pq.pop(); + if (u_dist > dist[u_id]) continue; + + for (const auto& [v_id, weight] : adj[u_id]) { + double new_dist = dist[u_id] + weight; + if (new_dist < dist[v_id]) { + dist[v_id] = new_dist; + pred[v_id] = u_id; + pq.push({new_dist, v_id}); + } } } @@ -367,9 +382,10 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject PyObject* pred_dict = PyDict_New(); if (!dist_dict || !pred_dict) return nullptr; - for (const auto& [v, d] : dist) { - PyObject* dval = PyFloat_FromDouble(d); - if (!dval || PyDict_SetItemString(dist_dict, v.c_str(), dval) < 0) { + for (int id = 0; id < static_cast(V); ++id) { + const std::string& name = graph->id_to_name[id]; + PyObject* dval = PyFloat_FromDouble(dist[id]); + if (!dval || PyDict_SetItemString(dist_dict, name.c_str(), dval) < 0) { Py_XDECREF(dval); Py_DECREF(dist_dict); Py_DECREF(pred_dict); @@ -378,13 +394,14 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject Py_DECREF(dval); } - for (const auto& [v, p] : pred) { + for (int id = 0; id < static_cast(V); ++id) { + const std::string& name = graph->id_to_name[id]; PyObject* py_pred; - if (p.empty()) { + if (pred[id] == -1) { Py_INCREF(Py_None); py_pred = Py_None; } else { - py_pred = PyUnicode_FromString(p.c_str()); + py_pred = PyUnicode_FromString(graph->id_to_name[pred[id]].c_str()); if (!py_pred) { Py_DECREF(dist_dict); Py_DECREF(pred_dict); @@ -392,7 +409,8 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject } } - if (PyDict_SetItemString(pred_dict, v.c_str(), py_pred) < 0) { + if (PyDict_SetItemString(pred_dict, name.c_str(), py_pred) < 0) { + Py_DECREF(py_pred); Py_DECREF(dist_dict); Py_DECREF(pred_dict); @@ -402,6 +420,7 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject } if (strlen(target_name) > 0) { + int target_id = graph->name_to_id[target_name]; PyObject* out = PyTuple_New(2); if (!out) { Py_DECREF(dist_dict); @@ -409,7 +428,7 @@ static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject return nullptr; } - PyObject* dist_val = PyFloat_FromDouble(dist[target_name]); + PyObject* dist_val = PyFloat_FromDouble(dist[target_id]); if (!dist_val) { Py_DECREF(out); Py_DECREF(dist_dict); diff --git a/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp b/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp index 8a48566d..b09ccd34 100644 --- a/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp +++ b/pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp @@ -13,7 +13,8 @@ extern PyTypeObject AdjacencyListGraphNodeType; typedef struct { PyObject_HEAD std::string name; - std::variant data; + int internal_id; + std::variant data; DataType data_type; std::unordered_map adjacent; } AdjacencyListGraphNode; @@ -38,6 +39,7 @@ static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args, new (&self->data) std::variant(); self->data_type = DataType::None; self->data = std::monostate{}; + self->internal_id = -1; static char* kwlist[] = { "name", "data", "adjacency_list", NULL }; const char* name; @@ -260,7 +262,7 @@ inline PyTypeObject AdjacencyListGraphNodeType = { /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, - /* tp_str */ 0, + /* tp_str */ (reprfunc)GraphNode_str, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ 0, diff --git a/pydatastructs/utils/_backend/cpp/GraphNode.hpp b/pydatastructs/utils/_backend/cpp/GraphNode.hpp index 8921dc76..ad065720 100644 --- a/pydatastructs/utils/_backend/cpp/GraphNode.hpp +++ b/pydatastructs/utils/_backend/cpp/GraphNode.hpp @@ -17,7 +17,8 @@ enum class DataType { typedef struct { PyObject_HEAD std::string name; - std::variant data; + int internal_id; + std::variant data; DataType data_type; } GraphNode; @@ -46,21 +47,30 @@ static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwd } self->name = std::string(name); - - if (data == Py_None) { + self->internal_id = -1; + if (data == Py_None) + { self->data = std::monostate{}; self->data_type = DataType::None; - } else if (PyLong_Check(data)) { + } + else if (PyLong_Check(data)) + { self->data = static_cast(PyLong_AsLongLong(data)); self->data_type = DataType::Int; - } else if (PyFloat_Check(data)) { + } + else if (PyFloat_Check(data)) + { self->data = PyFloat_AsDouble(data); self->data_type = DataType::Double; - } else if (PyUnicode_Check(data)) { + } + else if (PyUnicode_Check(data)) + { const char* s = PyUnicode_AsUTF8(data); self->data = std::string(s); self->data_type = DataType::String; - } else { + } + else + { self->data = data; self->data_type = DataType::PyObject; Py_INCREF(data);