Skip to content

Commit

Permalink
Raise IndexError when accessing non-existing nodes/edges
Browse files Browse the repository at this point in the history
  • Loading branch information
funkey committed Oct 3, 2024
1 parent c98dbbd commit 7dfc13b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 10 deletions.
7 changes: 5 additions & 2 deletions spatial_graph/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,18 @@ def __new__(

wrapper = witty.compile_module(
str(wrapper_template),
extra_compile_args=["-O3", "-std=c++17"],
source_files=[str(src_dir / "src" / "graph_lite.h")],
extra_compile_args=["-O3", "-std=c++20"],
include_dirs=[str(src_dir)],
language="c++",
quiet=True,
)
GraphType = type(cls.__name__, (cls, wrapper.Graph), {})
return wrapper.Graph.__new__(GraphType)

def __init__(self, node_dtype, node_attr_dtypes=None, edge_attr_dtypes=None, directed=False):
def __init__(
self, node_dtype, node_attr_dtypes=None, edge_attr_dtypes=None, directed=False
):
if node_attr_dtypes is None:
node_attr_dtypes = {}
if edge_attr_dtypes is None:
Expand Down
21 changes: 17 additions & 4 deletions spatial_graph/graph/src/graph_lite.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <type_traits>
#include <cassert>
#include <iostream>
#include <sstream>

// container spec
namespace graph_lite {
Expand Down Expand Up @@ -411,6 +412,16 @@ namespace graph_lite::detail {
const NodePropType& node_prop(const T& node_iv) const {
const auto* self = static_cast<const GType*>(this);
auto pos = self->find_by_iter_or_by_value(node_iv);
if (pos == self->adj_list.end()) {
if constexpr(GType::template is_iterator<T>()) {
throw std::out_of_range("Requested node does not exist");
} else {
static_assert(GType::template can_construct_node<T>);
std::ostringstream msg_stream;
msg_stream << "Node " << typename GType::node_type{node_iv} << " does not exist";
throw std::out_of_range(msg_stream.str());
}
}
return pos->second.prop;
}
template<typename T>
Expand Down Expand Up @@ -468,8 +479,6 @@ namespace graph_lite::detail {
template<typename U, typename V>
const EdgePropType& edge_prop(U&& source_iv, V&& target_iv) const {
const auto* self = static_cast<const GType*>(this);
const char* src_not_found_msg = "source is not found";
const char* tgt_not_found_msg = "target is not found";
auto find_neighbor_iv = [self, &source_iv, &target_iv]() {
auto&& tgt_val = self->unwrap_by_iter_or_by_value(std::forward<V>(target_iv));
if constexpr(GType::DIRECTION==EdgeDirection::UNDIRECTED) {
Expand All @@ -490,14 +499,18 @@ namespace graph_lite::detail {
if constexpr(GType::template is_iterator<U>()) { // I & V or I & I
auto [found, pos] = find_neighbor_iv();
if (not found) {
throw std::runtime_error(tgt_not_found_msg);
std::ostringstream msg_stream;
msg_stream << "Edge (" << typename GType::node_type{source_iv} << ", " << typename GType::node_type{target_iv} << ") does not exist";
throw std::out_of_range(msg_stream.str());
}
return pos->second.prop();
} else {
// V & I or V & V
auto [found, pos] = find_neighbor_vi();
if (not found) {
throw std::runtime_error(src_not_found_msg);
std::ostringstream msg_stream;
msg_stream << "Edge (" << typename GType::node_type{source_iv} << ", " << typename GType::node_type{target_iv} << ") does not exist";
throw std::out_of_range(msg_stream.str());
}
return pos->second.prop();
}
Expand Down
4 changes: 2 additions & 2 deletions spatial_graph/graph/wrapper_template.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ cdef extern from *:

int add_edge_with_prop(NodeType& source, NodeType& target, EdgeData& prop)

NodeData& node_prop[T](T& node)
NodeData& node_prop[T](T& node) except +

EdgeData& edge_prop[T](T& u, T& v)
EdgeData& edge_prop[T](T& u, T& v) except +

%if $directed
pair[NeighborsIterator, NeighborsIterator] out_neighbors(Iterator& node)
Expand Down
41 changes: 39 additions & 2 deletions tests/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def test_operations(directed):
continue
if not directed and u > v:
continue
num_added += graph.add_edge(np.array([u, v], dtype="uint64"), score=u * 100 + v)
num_added += graph.add_edge(
np.array([u, v], dtype="uint64"), score=u * 100 + v
)

assert graph.num_edges() == num_added

Expand Down Expand Up @@ -76,7 +78,6 @@ def test_operations(directed):


def test_directed_edges():

graph = sg.Graph("uint64", directed=True)
graph.add_nodes(np.array([0, 1, 2], dtype="uint64"))
graph.add_edges(np.array([[0, 1], [1, 2], [2, 0]], dtype="uint64"))
Expand Down Expand Up @@ -105,6 +106,7 @@ def test_directed_edges():
out_edges_01 = graph.out_edges_by_nodes(np.array([0, 1], dtype="uint64"))
np.testing.assert_array_equal(out_edges_01, [[0, 1], [1, 2]])


def test_attribute_modification():
graph = sg.Graph(
"uint64",
Expand Down Expand Up @@ -154,3 +156,38 @@ def test_attribute_modification():
assert graph.node_attrs[2].attr2 == 40
assert graph.node_attrs[3].attr2 == 60
assert graph.node_attrs[4].attr2 == 80


def test_missing_nodes_edges():
graph = sg.Graph(
"uint64", {"node_attr": "float32"}, {"edge_attr": "float32"}, directed=False
)
graph.add_nodes(
np.array([1, 2, 3, 4, 5], dtype="uint64"),
node_attr=np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype="float32"),
)
graph.add_edges(
np.array([[1, 2], [3, 4], [5, 1]], dtype="uint64"),
edge_attr=np.array([0.1, 0.2, 0.3], dtype="float32"),
)

with pytest.raises(IndexError):
graph.node_attrs[6].node_attr

with pytest.raises(IndexError):
graph.node_attrs[[4, 5, 6]].node_attr

with pytest.raises(IndexError):
graph.edge_attrs[(1, 3)].edge_attr

with pytest.raises(IndexError):
graph.edge_attrs[[(1, 2), (2, 4), (5, 1)]].edge_attr


def test_missing_attribute():
graph = sg.Graph("uint64", directed=False)
graph.add_nodes(np.array([1, 2, 3, 4, 5], dtype="uint64"))
graph.add_edges(np.array([[1, 2], [3, 4], [5, 1]], dtype="uint64"))

with pytest.raises(AttributeError):
graph.node_attrs[5].doesntexist

0 comments on commit 7dfc13b

Please sign in to comment.