def test_edgeview(self) -> None: mg = MultiGraph([(1, 1), (1, 1), (3, 2), (2, 3)]) loops: MultiEdge = mg.get_edge(1, 1) loop_view: EdgeConnectionView[MultiVertex, MultiEdge] = loops.connections()[0] assert loop_view.is_loop(), "edge connection should be a loop" assert loop_view.label == "(1, 1)", "label should be '(1, 1)'" assert (str(loop_view) == "EdgeConnectionView(1, 1)" ), "__str__() should return '<class name><connection label>'" assert loop_view.vertex1 == 1, "vertex1 should have label 1" assert loop_view.vertex2 == 1, "vertex2 should have label 1" assert loop_view.weight == edge_module.DEFAULT_WEIGHT, "edge should have default weight" assert not loop_view.has_attributes_dict( ), "edge attr dict should not be instantiated" loop_view["color"] = "blue" assert loop_view[ "color"] == "blue", "connection should have 'color' attribute" assert loop_view.attr[ "color"] == "blue", "connection should have 'color' attribute" assert ( loop_view.has_attributes_dict() ), "edge attr dictionary should be instantiated after using property accessor" with pytest.raises(KeyError): _ = loop_view["unknown_key"] multiedge = mg.get_edge(2, 3) view = multiedge.connections()[0] assert view.vertex1 == mg[3], "vertex1 should be vertex 2" assert view.vertex2 == mg[2], "vertex2 should be vertex 3" assert view.label == "(2, 3)", "label should be '(2, 3)'"
def test_loop(self) -> None: g = MultiGraph([(1, 1), (1, 1)]) assert g.get_edge( 1, 1).is_loop(), "edge (1, 1) should self identify as a loop" assert g.get_edge(1, 1).vertex1 == 1, "loop edge vertex1 should be 1" assert g.get_edge(1, 1).vertex2 == 1, "loop edge vertex2 should be 1" assert (g.get_edge(1, 1).multiplicity == 2 ), "there should be two parallel loop edge connections"
def test_issubclass_and_isinstance(self) -> None: g = MultiGraph() v1 = g.add_vertex(1) assert isinstance( v1, vertex_module.VertexBase ), "v1 should be an instance of superclass VertexBase" assert isinstance(v1, MultiVertex), "v1 should be a MultiVertex instance" assert issubclass(MultiVertex, vertex_module.VertexBase ), "MultiVertex should be VertexBase subclass"
def test_issubclass_and_isinstance(self) -> None: g = MultiGraph() edge: MultiEdge = g.add_edge(1, 2) assert isinstance( edge, edge_module.MultiEdgeBase ), "edge should be an instance of superclass MultiEdgeBase" assert isinstance(edge, MultiEdge), "edge should be an MultiEdge instance" assert issubclass(MultiEdge, edge_module.MultiEdgeBase ), "MultiEdge should be MultiEdgeBase subclass"
def test_weight(self) -> None: g = MultiGraph([(1, 2), (1, 2), (3, 4, 9.5), (3, 4, 0.5)]) assert ( g.get_edge(1, 2).weight == 2 * edge_module.DEFAULT_WEIGHT ), "each parallel connection should default to edge_module.DEFAULT_WEIGHT" assert (g.get_edge(3, 4).weight == 10.0 ), "multiedge weight should be cumulative of all connections" assert ( g.get_edge(1, 2).connections()[0].weight == edge_module.DEFAULT_WEIGHT ), "Individual connections should default to edge_module.DEFAULT_WEIGHT"
def test_connections_and_connection_items(self) -> None: g = MultiGraph([(1, 2), (1, 2), (2, 2), (2, 2), (2, 2)]) c12 = list(g.get_edge(1, 2).connections()) assert len(c12) == 2, "edge (1, 2) should have 2 connections" keys = set() for key, connection in g.get_edge(2, 2).connection_items(): assert not connection.has_attributes_dict( ), "loop connection should not have attr dict" keys.add(key) assert len( keys) == 3, "edge (2, 2) should have 3 parallel loop connections"
def test_contract(self) -> None: g = MultiGraph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 2), (2, 2)]) assert g[1].adj_vertices() == { g[2], g[3], }, "vertex 1 should be adjacent to vertices 2 and 3" assert ( g.get_edge(1, 3).multiplicity == 1 ), "before edge contraction, edge (1, 3) should have multiplicity 1" assert g.get_edge( 2, 2).multiplicity == 2, "vertex 2 should have two loops" g.get_edge(1, 2).contract(remove_loops=False) assert g[1].adj_vertices() == { g[1], g[3], g[4], }, "after edge contraction, vertex 1 should be adjacent to vertices 1, 3, and 4" assert ( g.get_edge(1, 3).multiplicity == 2 ), "after edge contraction, edge (1, 3) should have multiplicity 2" assert g[1].loop_edge, "vertex 1 should have loop edge (1, 1)" print(f"\nDEBUG: g[1].loop_edge => {g[1].loop_edge}\n") assert g[ 1].loop_edge.multiplicity == 3, "loop edge should have multiplicity 3" assert not g.has_vertex( 2), "after edge contraction, vertex 2 should be removed" g2 = MultiDiGraph([(1, 2), (2, 4), (2, 2), (2, 2)]) g2.get_edge(1, 2).contract(remove_loops=True) assert not g2[ 1].loop_edge, "loop edge should be removed after edge contraction"
def test_repr_str_and_label(self) -> None: mg = MultiGraph([(2, 1), (1, 2)]) assert mg.get_edge( 2, 1).label == "(1, 2)", "multiedge label should be (1, 2)" assert (mg.get_edge(2, 1).__str__() == mg.get_edge( 2, 1).__repr__()), "edge __repr__ should equal __str__" assert (mg.get_edge(2, 1).__str__() == "(1, 2), (1, 2)" ), "edge __str__ should be '(1, 2), (1, 2)'" mg.add_edge(3, 4, weight=1.5) assert mg.get_edge( 2, 1).label == "(1, 2)", "multiedge label should be (1, 2)" assert ( mg.get_edge(2, 1).__str__() == "(1, 2, 1.0), (1, 2, 1.0)" ), "edge __str__ should be '(1, 2, 1.0), (1, 2, 1.0)' after adding weighted edge to graph"
def test_equality_operator(self) -> None: mg = MultiGraph([(1, 2), (2, 1), (3, 4), (3, 4, 1.5), (6, 7, 9.5, { "k": "v1" })]) mg2 = MultiGraph([(2, 1), (2, 1), (3, 4), (3, 4, 5.5), (6, 7, 9.5, { "k": "v2" })]) assert mg.get_edge(1, 2) == mg2.get_edge( 2, 1), "multiedges (1, 2) should be equal" assert mg.get_edge(3, 4) != mg2.get_edge( 3, 4 ), "multiedges (3, 4) should not be equal due to different weights" assert mg.get_edge(6, 7) == mg2.get_edge( 6, 7 ), "multiedges (6, 7) should be equal (attributes of parallel connections not checked)"
def test_adj_vertices(self) -> None: g = MultiGraph([(1, 1), (1, 1), (2, 3), (2, 3)]) assert g[1].adj_vertices() == { g[1] }, "vertex with self-loops should be adjacent to itself" assert g[2].adj_vertices() == { g[3] }, "vertex 2 (with parallel connections) should be adjacent to vertex 3"
def test_add_remove_get_connection(self) -> None: g = MultiGraph([(1, 2)]) assert g.get_edge( 1, 2).multiplicity == 1, "edge (1, 2) should have multiplicity 1" g.get_edge(1, 2).add_connection(weight=3.5, key="connection2", color="blue") assert ( g.get_edge(1, 2).multiplicity == 2 ), "edge (1, 2) should have multiplicity 2 after adding parallel connection" connection = g.get_edge(1, 2).get_connection("connection2") assert connection[ "color"] == "blue", "new connection should have color attribute 'blue'" assert connection.weight == 3.5, "new connection should have weight 3.5" g.get_edge(1, 2).remove_connection("connection2") assert ( g.get_edge(1, 2).multiplicity == 1 ), "edge (1, 2) should have multiplicity 1 after removing parallel connection"
def test_is_vertex_type(self) -> None: g = Graph() v: Vertex = g.add_vertex(1) assert vertex_module.is_vertex_type( v), "Vertex object should be a VertexType" g2 = DiGraph() di_v: DiVertex = g2.add_vertex(1) assert vertex_module.is_vertex_type( di_v), "DiVertex object should be a VertexType" g3 = MultiGraph() multi_v: MultiVertex = g3.add_vertex(1) assert vertex_module.is_vertex_type( multi_v), "MultiVertex object should be a VertexType" g4 = MultiDiGraph() multi_di_v: MultiDiVertex = g4.add_vertex(1) assert vertex_module.is_vertex_type( multi_di_v), "MultiDiVertex should be a VertexType" assert vertex_module.is_vertex_type( 10), "int vertex label should be a VertexType" assert vertex_module.is_vertex_type( "s"), "str vertex label should be a VertexType" assert vertex_module.is_vertex_type(("s", { "color": "blue" })), "vertex tuple should be a VertexType" assert not vertex_module.is_vertex_type( 10.99), "float should not be a VertexType" assert not vertex_module.is_vertex_type( ("s", "t")), "edge tuple should not be a VertexType" assert not vertex_module.is_vertex_type( ("s", "t", 4.5)), "edge tuple with edge weight should not be a VertexType" g.add_edge("s", "t") assert not vertex_module.is_vertex_type(g.get_edge( "s", "t")), "edge object should not be a VertexType"
def test_parse_edge_type(self) -> None: g = Graph() g.add_edge(1, 2) edge_data1 = pp_module.parse_edge_type(g.get_edge(1, 2)) assert edge_data1.vertex1.label == "1" assert edge_data1.vertex2.label == "2" mg = MultiGraph([(3, 4), (3, 4)]) edge_data2 = pp_module.parse_edge_type(mg.get_edge(3, 4)) assert edge_data2.vertex1.label == "3" assert edge_data2.vertex2.label == "4" edge_tuple = (1, 2) edge_data3 = pp_module.parse_edge_type(edge_tuple) assert edge_data3.vertex1.label == "1" assert edge_data3.vertex2.label == "2" edge_tuple_weighted = (3, 4, 3.5) edge_data4 = pp_module.parse_edge_type(edge_tuple_weighted) assert edge_data4.vertex1.label == "3" assert edge_data4.vertex2.label == "4" assert edge_data4.weight == 3.5 edge_tuple_attr = (4, 5, {"color": "blue"}) edge_data5 = pp_module.parse_edge_type(edge_tuple_attr) assert edge_data5.vertex1.label == "4" assert edge_data5.vertex2.label == "5" assert edge_data5.weight == edge_module.DEFAULT_WEIGHT assert edge_data5.attr["color"] == "blue" edge_tuple_weighted_attr = (6, 7, 9.5, {"k": "v"}) edge_data6 = pp_module.parse_edge_type(edge_tuple_weighted_attr) assert edge_data6.vertex1.label == "6" assert edge_data6.vertex2.label == "7" assert edge_data6.weight == 9.5 assert edge_data6.attr["k"] == "v"
def test_incident_edges_and_loop_edge(self) -> None: g = MultiGraph() g.add_vertex(0) assert not g[0].loop_edge, "vertex 0 should not have a loop edge" g.add_edges_from([(1, 1), (1, 1), (2, 3), (2, 3)]) assert g[1].incident_edges() == {g.get_edge( 1, 1)}, "vertex 1 should have self loop as incident edge" assert g[1].loop_edge, "vertex 1 should have a self loop" assert len(g[2].incident_edges() ) == 1, "vertex 2 should have one incident multiedge" assert (next(iter(g[2].incident_edges())).multiplicity == 2 ), "multiedge (2, 3) should have multiplicity 2"
def test_parse_vertex_type(self) -> None: g = Graph() g.add_vertex(1, mass=5.5) vertex_data1 = pp_module.parse_vertex_type(g[1]) assert vertex_data1.label == "1" assert vertex_data1.attr["mass"] == 5.5 mg = MultiGraph([(3, 4), (3, 4)]) vertex_data2 = pp_module.parse_vertex_type(mg[4]) assert vertex_data2.label == "4" vertex_data3 = pp_module.parse_vertex_type(3) assert vertex_data3.label == "3" vertex_data4 = pp_module.parse_vertex_type("s") assert vertex_data4.label == "s" vertex_data5 = pp_module.parse_vertex_type(("t", {"mass": 42})) assert vertex_data5.label == "t" assert vertex_data5.attr["mass"] == 42.0
def test_degree(self) -> None: g = MultiGraph() v0 = g.add_vertex(0) assert v0.degree == 0, "vertex 0 should have degree 0" g.add_edge(1, 1) assert g[1].degree == 2, "vertex with self loop should have degree 2" g.add_edge(1, 1) assert g[ 1].degree == 4, "vertex with two parallel self loops should have degree 4" g.add_edge(2, 3) assert g[2].degree == 1, "vertex 2 should have degree 1" g.add_edge(2, 3) assert g[ 2].degree == 2, "vertex 2 with two parallel edge connections should have degree 2"
def test_remove(self) -> None: g = MultiGraph([(1, 2), (1, 2), (2, 3)]) assert g.has_edge(1, 2), "prior to removal, graph should have edge (1, 2)" assert not g[1].is_isolated( ), "vertex 1 should not be isolated prior to edge removal" g.get_edge(1, 2).remove(remove_semi_isolated_vertices=False) assert not g.has_edge( 1, 2), "after removal, graph should not have edge (1, 2)" assert g.has_vertex( 1 ), "after edge removal, isolated vertex 1 should still be in graph" assert g[1].is_isolated( ), "vertex 1 should be isolated after edge removal" g2 = MultiGraph([(1, 2), (1, 2), (1, 1), (1, 1), (2, 3)]) g2.get_edge(1, 2).remove(remove_semi_isolated_vertices=True) assert not g2.has_vertex( 1 ), "after edge removal, semi-isolated vertex 1 should have been removed"
def test_vertex1_vertex2(self) -> None: g = MultiGraph([(2, 1), (1, 2)]) assert g.get_edge( 1, 2).multiplicity == 2, "multiedge should have multiplicity 2" assert g.get_edge(1, 2).vertex1 == 2, "vertex1 should be 2" assert g.get_edge(1, 2).vertex2 == 1, "vertex2 should be 1"