Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions pydatastructs/graphs/_backend/cpp/Algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,135 @@ static PyObject* minimum_spanning_tree_prim_adjacency_list(PyObject* self, PyObj
}
return reinterpret_cast<PyObject*>(mst);
}

static PyObject* shortest_paths_dijkstra_adjacency_list(PyObject* self, PyObject* args, PyObject* kwargs) {
PyObject* graph_obj;
const char* source_name;
const char* target_name = "";

static const char* kwlist[] = {"graph", "source_node", "target_node", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!s|s", const_cast<char**>(kwlist),
&AdjacencyListGraphType, &graph_obj,
&source_name, &target_name)) {
return nullptr;
}

AdjacencyListGraph* graph = reinterpret_cast<AdjacencyListGraph*>(graph_obj);

const size_t V = graph->node_map.size();

std::unordered_map<std::string, double> dist;
std::unordered_map<std::string, std::string> pred;

for (const auto& [name, node] : graph->node_map) {
dist[name] = std::numeric_limits<double>::infinity();
pred[name] = "";
}
dist[source_name] = 0.0;

using PQEntry = std::pair<double, std::string>;
std::priority_queue<PQEntry, std::vector<PQEntry>, std::greater<>> pq;
pq.push({0.0, source_name});

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<double>(std::get<int64_t>(edge->value));
else if (edge->value_type == DataType::Double)
weight = std::get<double>(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});
}
}
}

PyObject* dist_dict = PyDict_New();
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) {
Py_XDECREF(dval);
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}
Py_DECREF(dval);
}

for (const auto& [v, p] : pred) {
PyObject* py_pred;
if (p.empty()) {
Py_INCREF(Py_None);
py_pred = Py_None;
} else {
py_pred = PyUnicode_FromString(p.c_str());
if (!py_pred) {
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}
}

if (PyDict_SetItemString(pred_dict, v.c_str(), py_pred) < 0) {
Py_DECREF(py_pred);
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}
Py_DECREF(py_pred);
}

if (strlen(target_name) > 0) {
PyObject* out = PyTuple_New(2);
if (!out) {
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}

PyObject* dist_val = PyFloat_FromDouble(dist[target_name]);
if (!dist_val) {
Py_DECREF(out);
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}

PyTuple_SetItem(out, 0, dist_val);
PyTuple_SetItem(out, 1, pred_dict);
Py_DECREF(dist_dict);
return out;
}

PyObject* result = PyTuple_New(2);
if (!result) {
Py_DECREF(dist_dict);
Py_DECREF(pred_dict);
return nullptr;
}

PyTuple_SetItem(result, 0, dist_dict);
PyTuple_SetItem(result, 1, pred_dict);
return result;
}
1 change: 1 addition & 0 deletions pydatastructs/graphs/_backend/cpp/algorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ static PyMethodDef AlgorithmsMethods[] = {
{"bfs_adjacency_list", (PyCFunction)breadth_first_search_adjacency_list, METH_VARARGS | METH_KEYWORDS, "Run BFS on adjacency list with callback"},
{"bfs_adjacency_matrix", (PyCFunction)breadth_first_search_adjacency_matrix, METH_VARARGS | METH_KEYWORDS, "Run BFS on adjacency matrix with callback"},
{"minimum_spanning_tree_prim_adjacency_list", (PyCFunction)minimum_spanning_tree_prim_adjacency_list, METH_VARARGS | METH_KEYWORDS, "Run Prim's algorithm on adjacency list"},
{"shortest_paths_dijkstra_adjacency_list", (PyCFunction)shortest_paths_dijkstra_adjacency_list, METH_VARARGS | METH_KEYWORDS, "Dijkstra's algorithm for adjacency list graphs"},
{NULL, NULL, 0, NULL}
};

Expand Down
22 changes: 13 additions & 9 deletions pydatastructs/graphs/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,15 +804,19 @@ def shortest_paths(graph: Graph, algorithm: str,
.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
.. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
"""
raise_if_backend_is_not_python(
shortest_paths, kwargs.get('backend', Backend.PYTHON))
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
raise NotImplementedError(
"Currently %s algorithm isn't implemented for "
"finding shortest paths in graphs."%(algorithm))
return getattr(algorithms, func)(graph, source, target)
backend = kwargs.get('backend', Backend.PYTHON)
if (backend == Backend.PYTHON):
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
raise NotImplementedError(
"Currently %s algorithm isn't implemented for "
"finding shortest paths in graphs."%(algorithm))
return getattr(algorithms, func)(graph, source, target)
else:
from pydatastructs.graphs._backend.cpp._algorithms import shortest_paths_dijkstra_adjacency_list
if graph._impl == "adjacency_list" and algorithm == 'dijkstra':
return shortest_paths_dijkstra_adjacency_list(graph, source, target)

def _bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple:
distances, predecessor, visited, cnts = {}, {}, {}, {}
Expand Down
19 changes: 19 additions & 0 deletions pydatastructs/graphs/tests/test_algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ def _test_shortest_paths_positive_edges(ds, algorithm):
graph.add_edge('D', 'SLC', -10)
assert raises(ValueError, lambda: shortest_paths(graph, 'bellman_ford', 'SLC'))

if (ds == 'List' and algorithm == 'dijkstra'):
vertices2 = [AdjacencyListGraphNode('S', 0, backend = Backend.CPP), AdjacencyListGraphNode('C', 0, backend = Backend.CPP),
AdjacencyListGraphNode('SLC', 0, backend = Backend.CPP), AdjacencyListGraphNode('SF', 0, backend = Backend.CPP),
AdjacencyListGraphNode('D', 0, backend = Backend.CPP)]
graph2 = Graph(*vertices2, backend = Backend.CPP)
graph2.add_edge('S', 'SLC', 2)
graph2.add_edge('C', 'S', 4)
graph2.add_edge('C', 'D', 2)
graph2.add_edge('SLC', 'C', 2)
graph2.add_edge('SLC', 'D', 3)
graph2.add_edge('SF', 'SLC', 2)
graph2.add_edge('SF', 'S', 2)
graph2.add_edge('D', 'SF', 3)
(dist2, pred2) = shortest_paths(graph2, algorithm, 'SLC', backend = Backend.CPP)
assert dist2 == {'S': 6, 'C': 2, 'SLC': 0, 'SF': 6, 'D': 3}
assert pred2 == {'S': 'C', 'C': 'SLC', 'SLC': None, 'SF': 'D', 'D': 'SLC'}



def _test_shortest_paths_negative_edges(ds, algorithm):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
Expand Down
Loading