def test_union_idempotent(populated_digraph): g1 = populated_digraph g2 = g1.copy() gu = sn.union(g1, g2) assert gu.get_nodes() == g1.get_nodes() assert gu.get_nodes() == g2.get_nodes() assert gu.get_edges() == g1.get_edges() assert gu.get_edges() == g2.get_edges()
def diff(A, B, context=False, mods=False): '''Given two graphs A and B, where it is generally assumed that B is a "newer" version of A, returns a new graph which captures information about which nodes and edges of A were removed, added, and remain the same in B. Specifically, it returns A ∪ B, such that: 1. Nodes in A - B are given the "diffstatus" attribute "removed" 2. Nodes in B - A are given the "diffstatus" attribute "added" 3. Nodes in A ∩ B are given the "diffstatus" attribute "same" Notice that the union of 1 - 3 equals A ∪ B. The optional parameter context, when true, will prune the graph so that nodes/edges which are the same are only present in the diff graph if: 1. An edge incident on/to/from it has been changed, or 2. it is connected to a changed node. The optional parameter mods, when true, will check for attribute modifications on nodes and edges, in addition to new/removed nodes. Any nodes/edges that have had their attributes changed between A and B are marked with the "diffstatus" attribute as "modified." WARNING: Currently, this method only works if both A and B were generated with unique IDs in a deterministic fashion; i.e., two identical nodes are given the same ID at both points in time. This means that diff() will not work on graphs which were generated with automatic random UUIDs. ''' # must take their union first, then mark appropriate nodes/edges AB = sn.union(A, B) # any edges incident on, to, or from the removed nodes will not be in the removed graph, # since we cannot have edges incident on, to, or from non-existent nodes removed = sn.difference(A, B) _mark_nodes_edges_as(AB, removed, 'removed') _mark_incident_edges_as(AB, removed, 'removed') added = sn.difference(B, A) _mark_nodes_edges_as(AB, added, 'added') _mark_incident_edges_as(AB, added, 'added') same = sn.intersection(B, A) _mark_nodes_edges_as(AB, same, 'same') _check_changed_edges(A, B, AB, same) if mods: _check_mods(A, B, AB, same) if context: _clear_clutter(AB) return AB
def test_union_partial(populated_digraph): g1 = populated_digraph g2 = sn.DiGraph() a = g2.add_node({"type": "A"}, '3caaa8c09148493dbdf02c574b95526c') b = g2.add_node({"type": "B"}, '2cdfebf3bf9547f19f0412ccdfbe03b7') d = g2.add_node({"type": "D"}) ab = g2.add_edge(a, b, {"type": "normal"}, '5f5f44ec7c0144e29c5b7d513f92d9ab') bd = g2.add_edge(b, d, {"type": "irregular"}) gu = sn.union(g1, g2) correct_nodes = { a: { 'id': a, 'type': 'A' }, b: { 'id': b, 'type': 'B' }, uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'): { 'id': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'C' }, d: { 'id': d, 'type': 'D' } } assert gu.get_nodes() == correct_nodes correct_edges = { ab: { 'id': ab, 'src': a, 'dst': b, 'type': 'normal' }, uuid.UUID('f3674fcc691848ebbd478b1bfb3e84c3'): { 'id': uuid.UUID('f3674fcc691848ebbd478b1bfb3e84c3'), 'src': b, 'dst': a, 'type': 'normal' }, uuid.UUID('7eb91be54d3746b89a61a282bcc207bb'): { 'id': uuid.UUID('7eb91be54d3746b89a61a282bcc207bb'), 'src': a, 'dst': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'normal' }, uuid.UUID('c172a3599b7d4ef3bbb688277276b763'): { 'id': uuid.UUID('c172a3599b7d4ef3bbb688277276b763'), 'src': uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'), 'dst': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'irregular' }, bd: { 'id': bd, 'src': b, 'dst': d, 'type': 'irregular' } } gu.get_nodes() == correct_nodes
def test_union_disjoint(populated_digraph): g1 = populated_digraph g2 = sn.DiGraph() a = g2.add_node({"label" : "A"}, 'a') b = g2.add_node({"label" : "B"}, 'b') c = g2.add_node({"label" : "C"}, 'c') ab = g2.add_edge(a, b, {"type" : "belongs"}, 'belongs') bc = g2.add_edge(b, c, {"type" : "owns"}, 'owns') ca = g2.add_edge(c, a, {"type" : "has"}, 'has') gu = sn.union(g1, g2) correct_nodes = { uuid.UUID('3caaa8c09148493dbdf02c574b95526c'): { 'id': uuid.UUID('3caaa8c09148493dbdf02c574b95526c'), 'type': 'A' }, uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'): { 'id': uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'), 'type': 'B' }, uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'): { 'id': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'C' }, 'a': { 'id': 'a', 'label': 'A' }, 'b': { 'id': 'b', 'label': 'B' }, 'c': { 'id': 'c', 'label': 'C' } } assert gu.get_nodes() == correct_nodes correct_edges = { uuid.UUID('5f5f44ec7c0144e29c5b7d513f92d9ab'): { 'id': uuid.UUID('5f5f44ec7c0144e29c5b7d513f92d9ab'), 'src': uuid.UUID('3caaa8c09148493dbdf02c574b95526c'), 'dst': uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'), 'type': 'normal' }, uuid.UUID('f3674fcc691848ebbd478b1bfb3e84c3'): { 'id': uuid.UUID('f3674fcc691848ebbd478b1bfb3e84c3'), 'src': uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'), 'dst': uuid.UUID('3caaa8c09148493dbdf02c574b95526c'), 'type': 'normal' }, uuid.UUID('7eb91be54d3746b89a61a282bcc207bb'): { 'id': uuid.UUID('7eb91be54d3746b89a61a282bcc207bb'), 'src': uuid.UUID('3caaa8c09148493dbdf02c574b95526c'), 'dst': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'normal' }, uuid.UUID('c172a3599b7d4ef3bbb688277276b763'): { 'id': uuid.UUID('c172a3599b7d4ef3bbb688277276b763'), 'src': uuid.UUID('2cdfebf3bf9547f19f0412ccdfbe03b7'), 'dst': uuid.UUID('3cd197c2cf5e42dc9ccd0c2adcaf4bc2'), 'type': 'irregular' }, ab: { 'id': ab, 'src': a, 'dst': b, 'type': 'belongs' }, bc: { 'id': bc, 'src': b, 'dst': c, 'type': 'owns' }, ca: { 'id': ca, 'src': c, 'dst': a, 'type': 'has' } } assert gu.get_edges() == correct_edges