From 27994efded5373762f82d1109025508ca4e0cc75 Mon Sep 17 00:00:00 2001 From: oortsang Date: Sun, 10 Jan 2021 21:51:58 -0600 Subject: [PATCH 1/4] sequence support in two characters! --- tinygraph/tinygraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinygraph/tinygraph.py b/tinygraph/tinygraph.py index 0f1a6f6..4401b48 100644 --- a/tinygraph/tinygraph.py +++ b/tinygraph/tinygraph.py @@ -363,7 +363,7 @@ def __getitem__(self, key): raise KeyError("Expecting exactly two endpoints.") elif len(key) != 2: raise KeyError("Expecting exactly two endpoints.") - return self.adjacency[key[0]][key[1]] + return self.adjacency[key[0], key[1]] def copy(self): """ From 8a41b76f9e3d1d411e6e469bd7be097640c9d7de Mon Sep 17 00:00:00 2001 From: oortsang Date: Sun, 10 Jan 2021 21:57:22 -0600 Subject: [PATCH 2/4] Modified the copy test suite to check for changes to vertex properties --- tests/test_lib_basics.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/test_lib_basics.py b/tests/test_lib_basics.py index 5c92f04..192d31b 100644 --- a/tests/test_lib_basics.py +++ b/tests/test_lib_basics.py @@ -207,7 +207,7 @@ def test_graph_props(): Simple tests of per-graph properties """ - + g1 = tg.TinyGraph(10) g1.props['foo'] = 'bar' @@ -217,9 +217,9 @@ def test_graph_props(): assert tg.util.graph_equality(g1, g2) g2.props['baz'] = 7 - + assert not tg.util.graph_equality(g1, g2) - + @pytest.mark.parametrize("test_name", [k for k in suite.keys()]) def test_copy_suite(test_name): @@ -228,5 +228,10 @@ def test_copy_suite(test_name): """ for g in suite[test_name]: g1 = g.copy() - assert tg.util.graph_equality(g1, g) - + assert tg.util.graph_equality(g, g1) + g1.add_vert_prop('useless_vertex_property', np.bool) + assert not tg.util.graph_equality(g, g1) + g2 = g1.copy() + g2.remove_vert_prop('useless_vertex_property') + assert not tg.util.graph_equality(g, g1) + assert tg.util.graph_equality(g, g2) From 9e4655467e6477533b381543e570179960e49a1d Mon Sep 17 00:00:00 2001 From: oortsang Date: Sun, 10 Jan 2021 23:41:26 -0600 Subject: [PATCH 3/4] things seem to be working --- tests/test_api_enchance.py | 8 +- tests/test_lib_basics.py | 99 ++++++++++++++++++++++++- tinygraph/tinygraph.py | 145 +++++++++++++++++++++++++------------ 3 files changed, 198 insertions(+), 54 deletions(-) diff --git a/tests/test_api_enchance.py b/tests/test_api_enchance.py index f4e7cc0..18dc91e 100644 --- a/tests/test_api_enchance.py +++ b/tests/test_api_enchance.py @@ -74,14 +74,14 @@ def test_remove_edge(): g.e['color'][4,0] = 1 g[0,4] = 0 g[1,0] = 0 - with pytest.raises(IndexError, match='No such edge'): + with pytest.raises(IndexError, match='Missing at least one edge.'): g.e['color'][1,4] = 6 - with pytest.raises(IndexError, match='No such edge.'): + with pytest.raises(IndexError, match='Missing at least one edge.'): g.e['color'][0,1] assert g.e['color'][1,2] == 4 assert g.e['color'][2,3] == 5 assert g.e['color'][3,4] == 6 - with pytest.raises(IndexError, match='No such edge.'): + with pytest.raises(IndexError, match='Missing at least one edge.'): g.e['color'][4,0] def test_permute(): @@ -110,4 +110,4 @@ def test_permute(): assert not graph_equality(g2, pG11) assert graph_equality(g2, pG12) - assert graph_equality(g1, pG13) \ No newline at end of file + assert graph_equality(g1, pG13) diff --git a/tests/test_lib_basics.py b/tests/test_lib_basics.py index 192d31b..512018f 100644 --- a/tests/test_lib_basics.py +++ b/tests/test_lib_basics.py @@ -229,9 +229,100 @@ def test_copy_suite(test_name): for g in suite[test_name]: g1 = g.copy() assert tg.util.graph_equality(g, g1) + + # Change g1 but not g g1.add_vert_prop('useless_vertex_property', np.bool) assert not tg.util.graph_equality(g, g1) - g2 = g1.copy() - g2.remove_vert_prop('useless_vertex_property') - assert not tg.util.graph_equality(g, g1) - assert tg.util.graph_equality(g, g2) + + +def test_sequence_adjacency(): + """ + Ensure that sequences can be used to modify the adjacency matrix compactly + """ + g = tg.TinyGraph(3, np.int) + # Edges: + g[[0, 1, 2], [1, 2, 0]] = np.array([1, 2, 3]) + + # Desired effect on adjacency matrix + assert np.array_equal(g.adjacency, np.array([[0, 1, 3], [1, 0, 2], [3, 2, 0]])) + + # And __getitem__ fetches just a 3-item array rather than something weird + assert np.array_equal(g[[0, 1, 2], [1, 2, 0]], [1, 2, 3]) + + # Conflicting writes are dealt with + g[[0, 1], [1, 0]] = [7, 8] + # still symmetric! + assert np.array_equal(g.adjacency, g.adjacency.T) + + # Add an edge from 0 to 2 + g[range(1), range(2, 3)] = 13 + assert g.adjacency[0, 2] == 13 + + # zero-out everything + g[[0, 1, 2], [1, 2, 0]] = 0 + assert np.all(g.adjacency == 0) + +def test_sequence_adjacency_errors(): + """ + Test the error-handling for poorly setting adjacency matrix values + """ + g = tg.TinyGraph(3, np.int) + with pytest.raises(KeyError): + g[[0, 0, 0], [1, 2]] = 1 + + with pytest.raises(IndexError): + # Attempt to set self-edges + g[[0, 1], [0, 1]] = 5 + + with pytest.raises(ValueError): + # length mismatch -> numpy shape mismatch + g[[0, 1], [1, 2]] = [5, 2, 6] + +def test_sequence_vprop(): + """ + Test that sequences can be used for assigning vertex properties + """ + g = tg.TinyGraph(3, vp_types={'name':np.dtype(' 1: + reorder = lambda x, y: (x, y) if x 1: + reorder = lambda x, y: (x, y) if x Date: Mon, 11 Jan 2021 00:07:22 -0600 Subject: [PATCH 4/4] caught a broadcasting error --- tests/test_lib_basics.py | 15 +++++++++++++-- tinygraph/tinygraph.py | 15 ++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/test_lib_basics.py b/tests/test_lib_basics.py index 512018f..dc42892 100644 --- a/tests/test_lib_basics.py +++ b/tests/test_lib_basics.py @@ -258,10 +258,20 @@ def test_sequence_adjacency(): g[range(1), range(2, 3)] = 13 assert g.adjacency[0, 2] == 13 - # zero-out everything + # Zero-out everything g[[0, 1, 2], [1, 2, 0]] = 0 assert np.all(g.adjacency == 0) + # Ensure tuples work + g[(0,), (1,)] = 1 + assert g.adjacency[1, 0] == 1 + g[(0, 1), (1, 2)] = 2 + assert g.adjacency[1, 0] == 2 + assert g.adjacency[2, 1] == 2 + g[(0, 1), (1, 2)] = (3, 5) + assert g.adjacency[1, 0] == 3 + assert g.adjacency[2, 1] == 5 + def test_sequence_adjacency_errors(): """ Test the error-handling for poorly setting adjacency matrix values @@ -307,8 +317,9 @@ def test_sequence_eprop(): assert np.array_equal(g.e_p['edgecolor'], g.e_p['edgecolor'].T) # Edge deletion results in property deletion - g[0, 1] = 0 + g[[0, 1], [1, 2]] = (0, 1) assert g.e_p['edgecolor'][1, 0] == 0 + assert g.e_p['edgecolor'][1, 2] == 2 def test_sequence_eprop_errors(): """ diff --git a/tinygraph/tinygraph.py b/tinygraph/tinygraph.py index 91e54a9..d2319c5 100644 --- a/tinygraph/tinygraph.py +++ b/tinygraph/tinygraph.py @@ -343,7 +343,7 @@ def remove_node(self, n): # Update the node count self.__node_N -= 1 - def __setitem__(self, key, newValue): + def __setitem__(self, key, new_value): """ Create an edge or change the weight of an existing edge. This operation is fast. Edges are undirected. If an existing edge is set to its zero @@ -354,7 +354,7 @@ def __setitem__(self, key, newValue): Also accepts sequences (iterable) in place of ints, numpy-style (usage: ([s1, s2, ...], [t1, t2, ...]) to modify edges (s1, t1), (s2, t2), ...) value (dtype): Value to set edge property to. - newValue (adj_type): Weight of edge. + new_value (adj_type): Weight of edge. Outputs: None - modifications are made in place. @@ -364,6 +364,7 @@ def __setitem__(self, key, newValue): elif len(key) != 2: raise KeyError("Expecting exactly two endpoints.") e1, e2 = key + lenv = getattr(new_value, '__len__', lambda: 1)() len1 = getattr(e1, '__len__', lambda: 1)() len2 = getattr(e2, '__len__', lambda: 1)() @@ -385,17 +386,17 @@ def __setitem__(self, key, newValue): for i in range(len1): e1[i], e2[i] = reorder(e1[i], e2[i]) - self.adjacency[e1, e2] = newValue - self.adjacency[e2, e1] = newValue + self.adjacency[e1, e2] = new_value + self.adjacency[e2, e1] = new_value # Zero-out properties when edges are deleted - if len1 == 1: - if newValue == default_zero(self.adjacency.dtype): + if lenv == 1: + if new_value == default_zero(self.adjacency.dtype): for k, prop in self.e_p.items(): self.e_p[k][e1, e2] = default_zero(prop.dtype) self.e_p[k][e2, e1] = default_zero(prop.dtype) else: - del_edges = newValue == default_zero(self.adjacency.dtype) + del_edges = np.array(list(new_value)) == default_zero(self.adjacency.dtype) d1 = e1[del_edges] d2 = e2[del_edges] for k, prop in self.e_p.items():