Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
hello_build
test_2
# Temporary files
*~*
*#*
Expand Down
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
cmake_minimum_required(VERSION 3.15...3.29)

project(sp LANGUAGES CXX)

# Require C++14-compliant compiler; only available for CMake v. 3.1 and up
set(CMAKE_CXX_STANDARD 14)

cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

SET(CMAKE_COLOR_MAKEFILE ON)
SET(CMAKE_VERBOSE_MAKEFILE OFF)
Expand Down
6 changes: 5 additions & 1 deletion external/catch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2130,7 +2130,11 @@ namespace Catch{
__asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
: : : "memory","r0","r3","r4" ) /* NOLINT */
#else
#define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
#if defined(__i386__) || defined(__x86_64__)
#define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
#elif defined(__aarch64__)
#define CATCH_TRAP() __asm__(".inst 0xd4200000")
#endif
#endif

#elif defined(CATCH_PLATFORM_LINUX)
Expand Down
23 changes: 17 additions & 6 deletions include/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ struct ShortestPath {
// Iterate until source has been reached
while (destination != source) {
destination = parent.at(destination);
if (destination != source) path.emplace_back(destination);
if (destination != source)
path.emplace_back(destination);
}
// Reverse to arrange from source to destination
std::reverse(path.begin(), path.end());
Expand All @@ -54,7 +55,7 @@ struct ShortestPath {
//! \brief Graph class to store vertices and edge and compute shortest path
//! \details Graph class has Priority Queue Dijkstra algorithm for SSSP
class Graph {
public:
public:
//! Vertex id type
using vertex_t = int;
//! Weight type, that can be added with +
Expand Down Expand Up @@ -89,14 +90,24 @@ class Graph {
//! Generate a simple graph
void generate_simple_graph();

//! Create a graph based on edges from Pandas DataFrame
//! \param[in] edge_starts name of the dataframe column containing edge start
//! nodes \param[in] edge_ends name of the dataframe column containing edge
//! end nodes \param[in] edge_wghs name of the dataframe column containing
//! edge weights \param[in] nedges numbers of edges (number of rows in the
//! edges dataframe) \param[in] nvertices numbers of vertices (maximum of
//! start and end node ids in the edges dataframe)
void generate_graph(int *edge_starts, int *edge_ends, double *edge_wghs,
int nedges, int nvertices);

//! Read MatrixMarket graph file format
//! \param[in] filename Name of input MatrixMarket file
//! \retval status File read status
bool read_graph_matrix_market(const std::string& filename);
bool read_graph_matrix_market(const std::string &filename);

//! Write MatrixMarket graph file format
//! \param[in] filename Name of output MatrixMarket file
bool write_graph_matrix_market(const std::string& filename);
bool write_graph_matrix_market(const std::string &filename);

//! Compute the shortest path using priority queue
//! \param[in] source ID of source vertex1
Expand All @@ -105,7 +116,7 @@ class Graph {
ShortestPath dijkstra_priority_queue(vertex_t source,
vertex_t destination = -1);

private:
private:
//! Assign number of vertices
//! \param[in] nvertices Number of vertices in graph
void assign_nvertices(unsigned nvertices) { this->nvertices_ = nvertices; }
Expand All @@ -120,4 +131,4 @@ class Graph {
tsl::robin_map<vertex_t, std::vector<std::shared_ptr<Edge>>> vertex_edges_;
};

#endif // _GRAPH_H_
#endif // _GRAPH_H_
21 changes: 9 additions & 12 deletions include/unordered_map_tuple_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,33 @@ namespace {
// See Mike Seymour in magic-numbers-in-boosthash-combine:
// https://stackoverflow.com/questions/4948780

template <class T>
inline void hash_combine(std::size_t& seed, T const& v) {
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
static void apply(size_t& seed, Tuple const& tuple) {
static void apply(size_t &seed, Tuple const &tuple) {
HashValueImpl<Tuple, Index - 1>::apply(seed, tuple);
hash_combine(seed, get<Index>(tuple));
}
};

template <class Tuple>
struct HashValueImpl<Tuple, 0> {
static void apply(size_t& seed, Tuple const& tuple) {
template <class Tuple> struct HashValueImpl<Tuple, 0> {
static void apply(size_t &seed, Tuple const &tuple) {
hash_combine(seed, get<0>(tuple));
}
};
} // namespace
} // namespace

template <typename... TT>
struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const& tt) const {
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>::apply(seed, tt);
return seed;
}
};
} // namespace std
} // namespace std

#endif // _UNORDERED_MAP_TUPLE_HASH_H_
#endif // _UNORDERED_MAP_TUPLE_HASH_H_
70 changes: 65 additions & 5 deletions interface.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
from __future__ import print_function

import sys
from sys import argv
from ctypes import *
from pathlib import Path
from ctypes import c_bool, c_int, c_int32, c_double, cdll, byref, Structure, POINTER
from numpy.ctypeslib import ndpointer
import pandas as pd
import numpy as np

import os
absolute_path = os.path.dirname(os.path.abspath(__file__))

root = Path(absolute_path) / "build"

if sys.platform == "darwin":
ext = ".dylib"
elif sys.platform.startswith("win"):
ext = ".dll"
else:
ext = ".so"

libsp = cdll.LoadLibrary(str(root / f"liblsp{ext}"))

libsp = cdll.LoadLibrary("./build/liblsp.so")
libsp.distance.restype = c_double

class ShortestPath(Structure):
Expand Down Expand Up @@ -37,19 +55,34 @@ def writegraph(self, filename):

libsp.simplegraph.restype = POINTER(Graph)
libsp.readgraph.restype = POINTER(Graph)
libsp.creategraph.restype = POINTER(Graph)
libsp.creategraph.argtypes = [
ndpointer(c_int32, flags='C_CONTIGUOUS'),
ndpointer(c_int32, flags='C_CONTIGUOUS'),
ndpointer(c_double, flags='C_CONTIGUOUS'),
c_int, c_int, c_bool]

def simplegraph(directed=True):
return libsp.simplegraph(directed).contents


def readgraph(filename, directed=True):
return libsp.readgraph(filename, directed).contents

def from_dataframe(edges=None, start_node_col=None, end_node_col=None, weight_col=None, directed=True):
# print(np.max(edges[[start_node_col, end_node_col]].values))
return libsp.creategraph(
edges[start_node_col].values.astype(np.int32),
edges[end_node_col].values.astype(np.int32),
edges[weight_col].values,
edges.shape[0], ### the number of edges is computed from the edges dataframe, not an input to the python interface
np.max(edges[[start_node_col, end_node_col]].values), ### number of vertices is the maximum of the start or end node id in the edges dataframe
directed).contents


def test():
g = simplegraph()
#g = readgraph(b"../sf.mtx")
res = g.update_edge(1, 3, c_double(0.5))
g.update_edge(1, 3, c_double(0.5))
sp = g.dijkstra(1, -1)

print("origin:", sp.origin)
Expand All @@ -58,6 +91,33 @@ def test():
print(destination, sp.distance(destination))

print( " -> ".join("%s"%vertex[1] for vertex in sp.route(destination)) )
sp.clear()

def test_df():
print("test 1")
df = pd.DataFrame({'start':[0,1,2,3,4,5,6,7], 'end':[1,2,3,4,5,6,7,0], 'wgh':[0.1,0.5,1.9,1.1,1.2,1.5,1.6,1.9]})
g = from_dataframe(df, 'start', 'end', 'wgh')

origin, destin = 1, 5
sp = g.dijkstra(origin, destin)

print("origin is {}, destination is {}".format(sp.origin, destin))
print("path is {} --> ".format(sp.origin), " -> ".join("%s"%vertex[1] for vertex in sp.route(destin)) )
print("distance is ", sp.distance(destin))
sp.clear()

print("\ntest 2")
df = pd.DataFrame({'start':[0,2,4,10,12], 'end':[1,3,5,12,0], 'wgh':[0.1,0.5,1.9,1.1,1.2]})
# df = pd.DataFrame({'start':[0,1,2,3,4], 'end':[1,2,3,4,0], 'wgh':[0.1,0.5,1.9,1.1,1.2]})
g = from_dataframe(df, 'start', 'end', 'wgh')

origin, destin = 10,1 #0,4
sp = g.dijkstra(origin, destin)

print("origin is {}, destination is {}".format(sp.origin, destin))
print("path is {} --> ".format(sp.origin), " -> ".join("%s"%vertex[1] for vertex in sp.route(destin)) )
print("distance is ", sp.distance(destin))
sp.clear()

if __name__ == '__main__':
test()
test_df()
15 changes: 15 additions & 0 deletions src/graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,21 @@ void Graph::generate_simple_graph() {
this->add_edge(5, 6, 9.7);
}

// Create graph based on edges from Pandas DataFrame
void Graph::generate_graph(int* edge_starts, int* edge_ends, double* edge_wghs,
int nedges, int nvertices) {
// std::cout << nvertices << std::endl;
this->assign_nvertices(nvertices + 1);

for (unsigned int e = 0; e < nedges; e++) {
int v1 = edge_starts[e];
int v2 = edge_ends[e];
double weight = edge_wghs[e];
// std::cout << v1 << " " << v2 << " " << weight << std::endl;
this->add_edge(v1, v2, weight);
}
}

// Dijktra shortest paths from src to all other vertices
ShortestPath Graph::dijkstra_priority_queue(vertex_t source,
vertex_t destination) {
Expand Down
16 changes: 16 additions & 0 deletions src/py.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ Graph* readgraph(char* filename, bool directed) {
return graph;
}

//! Create a graph based on edges from Pandas DataFrame
//! \param[in] edge_starts name of the dataframe column containing edge start
//! nodes \param[in] edge_ends name of the dataframe column containing edge end
//! nodes \param[in] edge_wghs name of the dataframe column containing edge
//! weights \param[in] nedges numbers of edges (number of rows in the edges
//! dataframe) \param[in] nvertices numbers of vertices (maximum of start and
//! end node ids in the edges dataframe) \param[in] directed Boolean to
//! determine if a given graph is directed or not \retval graph Pointer to a
//! graph object
Graph* creategraph(int* edge_starts, int* edge_ends, double* edge_wghs,
int nedges, int nvertices, bool directed) {
Graph* graph = new Graph(directed);
graph->generate_graph(edge_starts, edge_ends, edge_wghs, nedges, nvertices);
return graph;
}

//! Write graph as MatrixMarket file
//! \param[in] filename Graph filename
//! \param[in] graph Graph object
Expand Down