From 50a891638de7a016a3ead10969fe6004ab572d39 Mon Sep 17 00:00:00 2001 From: Franck LECUYER Date: Wed, 21 Jun 2023 15:01:04 +0200 Subject: [PATCH] Traversal from terminal by depth or breadth first Signed-off-by: Franck LECUYER --- .../store/iidm/impl/BusBreakerViewImpl.java | 11 +- .../store/iidm/impl/NodeBreakerViewImpl.java | 127 +++++++++++++---- .../network/store/iidm/impl/TerminalImpl.java | 16 ++- .../TopologyTraverseDepthAndBreadthTest.java | 132 ++++++++++++++++++ .../traversalByDepthOrBreadthNetwork.svg | 4 + 5 files changed, 250 insertions(+), 40 deletions(-) create mode 100644 network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/TopologyTraverseDepthAndBreadthTest.java create mode 100644 network-store-iidm-impl/src/test/javadoc/com/powsybl/network/store/iidm/impl/doc-files/traversalByDepthOrBreadthNetwork.svg diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/BusBreakerViewImpl.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/BusBreakerViewImpl.java index fc7f9fc9d..2e32b3932 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/BusBreakerViewImpl.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/BusBreakerViewImpl.java @@ -8,6 +8,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; +import com.powsybl.math.graph.TraversalType; import com.powsybl.math.graph.TraverseResult; import com.powsybl.network.store.model.Resource; import com.powsybl.network.store.model.VoltageLevelAttributes; @@ -204,14 +205,14 @@ private Bus getOtherBus(String switchId, String busId) { } } - boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set traversedTerminals) { + boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set traversedTerminals, TraversalType traversalType) { checkNodeBreakerTopology(); Objects.requireNonNull(traverser); - return traverseFromBus(terminal.getBusBreakerView().getBus(), traverser, traversedTerminals, new HashSet<>()); + return traverseFromBus(terminal.getBusBreakerView().getBus(), traverser, traversedTerminals, new HashSet<>(), traversalType); } - private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, Set traversedTerminals, Set traversedBuses) { + private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, Set traversedTerminals, Set traversedBuses, TraversalType traversalType) { Objects.requireNonNull(bus); Objects.requireNonNull(traverser); @@ -232,7 +233,7 @@ private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, S } else if (result == TraverseResult.CONTINUE) { Set otherSideTerminals = ((TerminalImpl) terminal).getOtherSideTerminals(); for (Terminal otherSideTerminal : otherSideTerminals) { - if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals)) { + if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals, traversalType)) { return false; } } @@ -248,7 +249,7 @@ private boolean traverseFromBus(Bus bus, Terminal.TopologyTraverser traverser, S return false; } else if (result == TraverseResult.CONTINUE) { Bus otherBus = getOtherBus(s.getId(), bus.getId()); - if (!traverseFromBus(otherBus, traverser, traversedTerminals, traversedBuses)) { + if (!traverseFromBus(otherBus, traverser, traversedTerminals, traversedBuses, traversalType)) { return false; } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NodeBreakerViewImpl.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NodeBreakerViewImpl.java index 3f8eb33f6..e1b39b557 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NodeBreakerViewImpl.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/NodeBreakerViewImpl.java @@ -8,6 +8,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; +import com.powsybl.math.graph.TraversalType; import com.powsybl.math.graph.TraverseResult; import com.powsybl.network.store.model.*; import org.jgrapht.Graph; @@ -115,7 +116,7 @@ public void traverse(int[] nodes, VoltageLevel.NodeBreakerView.TopologyTraverser Graph graph = NodeBreakerTopology.INSTANCE.buildGraph(index, getVoltageLevelResource(), true, true); Set done = new HashSet<>(); for (int node : nodes) { - if (!traverseFromNode(graph, node, traverser, done)) { + if (!traverseFromNode(graph, node, TraversalType.DEPTH_FIRST, traverser, done)) { break; } } @@ -125,44 +126,110 @@ public void traverse(int[] nodes, VoltageLevel.NodeBreakerView.TopologyTraverser public void traverse(int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) { Objects.requireNonNull(traverser); checkBusBreakerTopology(); - traverseFromNode(node, traverser); + traverseFromNode(node, TraversalType.DEPTH_FIRST, traverser); } - boolean traverseFromNode(int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) { + boolean traverseFromNode(int node, TraversalType traversalType, VoltageLevel.NodeBreakerView.TopologyTraverser traverser) { Graph graph = NodeBreakerTopology.INSTANCE.buildGraph(index, getVoltageLevelResource(), true, true); Set done = new HashSet<>(); - return traverseFromNode(graph, node, traverser, done); + return traverseFromNode(graph, node, traversalType, traverser, done); } - private boolean traverseFromNode(Graph graph, int node, VoltageLevel.NodeBreakerView.TopologyTraverser traverser, + private boolean traverseFromNode(Graph graph, int node, TraversalType traversalType, VoltageLevel.NodeBreakerView.TopologyTraverser traverser, Set done) { - if (done.contains(node)) { - return true; - } - done.add(node); - - for (Edge edge : graph.edgesOf(node)) { - NodeBreakerBiConnectable biConnectable = edge.getBiConnectable(); - int nextNode = biConnectable.getNode1() == node ? biConnectable.getNode2() : biConnectable.getNode1(); - TraverseResult result; - if (done.contains(nextNode)) { - continue; - } - if (biConnectable instanceof SwitchAttributes) { - result = traverseSwitch(traverser, biConnectable, node, nextNode); - } else if (biConnectable instanceof InternalConnectionAttributes) { - result = traverser.traverse(node, null, nextNode); - } else { - throw new AssertionError(); + if (traversalType == TraversalType.DEPTH_FIRST) { // traversal by depth first + if (done.contains(node)) { + return true; } - if (result == TraverseResult.CONTINUE) { - if (!traverseFromNode(graph, nextNode, traverser, done)) { + done.add(node); + + for (Edge edge : graph.edgesOf(node)) { + NodeBreakerBiConnectable biConnectable = edge.getBiConnectable(); + int nextNode = biConnectable.getNode1() == node ? biConnectable.getNode2() : biConnectable.getNode1(); + TraverseResult result; + if (done.contains(nextNode)) { + continue; + } + if (biConnectable instanceof SwitchAttributes) { + result = traverseSwitch(traverser, biConnectable, node, nextNode); + } else if (biConnectable instanceof InternalConnectionAttributes) { + result = traverser.traverse(node, null, nextNode); + } else { + throw new AssertionError(); + } + if (result == TraverseResult.CONTINUE) { + if (!traverseFromNode(graph, nextNode, traversalType, traverser, done)) { + return false; + } + } else if (result == TraverseResult.TERMINATE_TRAVERSER) { return false; } - } else if (result == TraverseResult.TERMINATE_TRAVERSER) { - return false; } + } else { // traversal by breadth first + boolean keepGoing = true; + Set encounteredEdges = new HashSet<>(); + + LinkedList vertexToTraverse = new LinkedList<>(); + vertexToTraverse.offer(node); + while (!vertexToTraverse.isEmpty()) { + int firstV = vertexToTraverse.getFirst(); + vertexToTraverse.poll(); + if (done.contains(firstV)) { + continue; + } + done.add(firstV); + + Set adjacentEdges = graph.edgesOf(firstV); + + for (Edge edge : adjacentEdges) { + if (encounteredEdges.contains(edge)) { + continue; + } + encounteredEdges.add(edge); + + NodeBreakerBiConnectable biConnectable = edge.getBiConnectable(); + int node1 = biConnectable.getNode1(); + int node2 = biConnectable.getNode2(); + + TraverseResult traverserResult; + if (!done.contains(node1)) { + if (biConnectable instanceof SwitchAttributes) { + traverserResult = traverseSwitch(traverser, biConnectable, node2, node1); + } else if (biConnectable instanceof InternalConnectionAttributes) { + traverserResult = traverser.traverse(node2, null, node1); + } else { + throw new AssertionError(); + } + if (traverserResult == TraverseResult.CONTINUE) { + vertexToTraverse.offer(node1); + } else if (traverserResult == TraverseResult.TERMINATE_TRAVERSER) { + keepGoing = false; + } + } else if (!done.contains(node2)) { + if (biConnectable instanceof SwitchAttributes) { + traverserResult = traverseSwitch(traverser, biConnectable, node1, node2); + } else if (biConnectable instanceof InternalConnectionAttributes) { + traverserResult = traverser.traverse(node1, null, node2); + } else { + throw new AssertionError(); + } + if (traverserResult == TraverseResult.CONTINUE) { + vertexToTraverse.offer(node2); + } else if (traverserResult == TraverseResult.TERMINATE_TRAVERSER) { + keepGoing = false; + } + } + if (!keepGoing) { + break; + } + } + if (!keepGoing) { + break; + } + } + return keepGoing; } + return true; } @@ -175,12 +242,12 @@ private TraverseResult traverseSwitch(VoltageLevel.NodeBreakerView.TopologyTrave /** * This is the method called when we traverse the topology stating from a terminal. */ - boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set traversedTerminals) { + boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser traverser, Set traversedTerminals, TraversalType traversalType) { checkBusBreakerTopology(); Objects.requireNonNull(traverser); List nexTerminals = new ArrayList<>(); - if (!traverseFromNode(terminal.getNodeBreakerView().getNode(), (node1, sw, node2) -> { + if (!traverseFromNode(terminal.getNodeBreakerView().getNode(), traversalType, (node1, sw, node2) -> { if (sw != null) { TraverseResult result = traverser.traverse(sw); if (result != TraverseResult.CONTINUE) { @@ -204,7 +271,7 @@ boolean traverseFromTerminal(Terminal terminal, Terminal.TopologyTraverser trave } for (Terminal nextTerminal : nexTerminals) { - if (!((TerminalImpl) nextTerminal).traverse(traverser, traversedTerminals)) { + if (!((TerminalImpl) nextTerminal).traverse(traverser, traversedTerminals, traversalType)) { return false; } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/TerminalImpl.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/TerminalImpl.java index 49813ee9d..3453dc184 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/TerminalImpl.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/TerminalImpl.java @@ -9,6 +9,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; import com.powsybl.math.graph.TraverseResult; +import com.powsybl.math.graph.TraversalType; import com.powsybl.network.store.model.*; import org.jgrapht.Graph; import org.jgrapht.alg.connectivity.ConnectivityInspector; @@ -359,25 +360,30 @@ public String getMessageHeader() { @Override public void traverse(Terminal.TopologyTraverser traverser) { + traverse(traverser, TraversalType.DEPTH_FIRST); + } + + @Override + public void traverse(Terminal.TopologyTraverser traverser, TraversalType traversalType) { Set traversedTerminals = new HashSet<>(); if (getAbstractIdentifiable().getOptionalResource().isEmpty()) { throw new PowsyblException("Associated equipment is removed"); } // One side - if (!traverse(traverser, traversedTerminals)) { + if (!traverse(traverser, traversedTerminals, traversalType)) { return; } // Other sides for (Terminal otherSideTerminal : getOtherSideTerminals()) { - if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals)) { + if (!((TerminalImpl) otherSideTerminal).traverse(traverser, traversedTerminals, traversalType)) { return; } } } - boolean traverse(Terminal.TopologyTraverser traverser, Set traversedTerminals) { + boolean traverse(Terminal.TopologyTraverser traverser, Set traversedTerminals, TraversalType traversalType) { if (traversedTerminals.contains(this)) { return true; } @@ -393,9 +399,9 @@ boolean traverse(Terminal.TopologyTraverser traverser, Set traversedTe TopologyKind topologyKind = getTopologyKind(); switch (topologyKind) { case NODE_BREAKER: - return ((NodeBreakerViewImpl) voltageLevel.getNodeBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals); + return ((NodeBreakerViewImpl) voltageLevel.getNodeBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals, traversalType); case BUS_BREAKER: - return ((BusBreakerViewImpl) voltageLevel.getBusBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals); + return ((BusBreakerViewImpl) voltageLevel.getBusBreakerView()).traverseFromTerminal(this, traverser, traversedTerminals, traversalType); default: throw new IllegalStateException("Unknown topology kind: " + topologyKind); } diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/TopologyTraverseDepthAndBreadthTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/TopologyTraverseDepthAndBreadthTest.java new file mode 100644 index 000000000..3acaaaf11 --- /dev/null +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/TopologyTraverseDepthAndBreadthTest.java @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.iidm.impl; + +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.math.graph.TraversalType; +import com.powsybl.math.graph.TraverseResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Small network to test topology traversal by depth first or breadth first + * Below is a diagram of the network: + *
+ * + *
+ * + * @author Franck Lecuyer + */ +class TopologyTraverseDepthAndBreadthTest { + + private Network network; + + @BeforeEach + public void setUp() { + network = Network.create("test", "test"); + Substation s1 = network.newSubstation().setId("S1").setCountry(Country.FR).add(); + VoltageLevel vl1 = s1.newVoltageLevel().setId("VL1").setNominalV(400f).setTopologyKind(TopologyKind.NODE_BREAKER).add(); + vl1.getNodeBreakerView().newBusbarSection().setId("VL1_B1").setNode(0).add(); + vl1.getNodeBreakerView().newBusbarSection().setId("VL1_B2").setNode(1).add(); + vl1.newGenerator().setId("G1").setNode(2).setMinP(0).setMaxP(1).setTargetP(1).setTargetQ(0).setVoltageRegulatorOn(false).add(); + vl1.newGenerator().setId("G2").setNode(5).setMinP(0).setMaxP(1).setTargetP(1).setTargetQ(0).setVoltageRegulatorOn(false).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER3").setOpen(false).setNode1(3).setNode2(9).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER1").setOpen(false).setNode1(3).setNode2(4).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER2").setOpen(false).setNode1(9).setNode2(10).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER7").setOpen(false).setNode1(6).setNode2(12).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER5").setOpen(false).setNode1(6).setNode2(7).add(); + vl1.getNodeBreakerView().newBreaker().setId("VL1_BREAKER6").setOpen(false).setNode1(12).setNode2(13).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR1").setOpen(false).setNode1(2).setNode2(3).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR2").setOpen(false).setNode1(8).setNode2(9).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR3").setOpen(false).setNode1(1).setNode2(4).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR4").setOpen(false).setNode1(0).setNode2(10).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR5").setOpen(false).setNode1(5).setNode2(6).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR6").setOpen(false).setNode1(1).setNode2(7).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR7").setOpen(false).setNode1(11).setNode2(12).add(); + vl1.getNodeBreakerView().newDisconnector().setId("VL1_DISCONNECTOR8").setOpen(false).setNode1(0).setNode2(13).add(); + + Substation s2 = network.newSubstation().setId("S2").setCountry(Country.FR).add(); + VoltageLevel vl2 = s2.newVoltageLevel().setId("VL2").setNominalV(400f).setTopologyKind(TopologyKind.NODE_BREAKER).add(); + vl2.getNodeBreakerView().newBusbarSection().setId("VL2_B1").setNode(0).add(); + vl2.getNodeBreakerView().newBreaker().setId("VL2_BREAKER1").setOpen(false).setNode1(1).setNode2(2).add(); + vl2.getNodeBreakerView().newDisconnector().setId("VL2_DISCONNECTOR1").setOpen(false).setNode1(0).setNode2(2).add(); + + Substation s3 = network.newSubstation().setId("S3").setCountry(Country.FR).add(); + VoltageLevel vl3 = s3.newVoltageLevel().setId("VL3").setNominalV(400f).setTopologyKind(TopologyKind.NODE_BREAKER).add(); + vl3.getNodeBreakerView().newBusbarSection().setId("VL3_B1").setNode(0).add(); + vl3.getNodeBreakerView().newBreaker().setId("VL3_BREAKER1").setOpen(false).setNode1(1).setNode2(2).add(); + vl3.getNodeBreakerView().newDisconnector().setId("VL3_DISCONNECTOR1").setOpen(false).setNode1(0).setNode2(2).add(); + + network.newLine().setId("L1").setVoltageLevel1("VL1").setVoltageLevel2("VL2").setNode1(8).setNode2(1).setR(1).setX(1).setG1(0).setB1(0).setG2(0).setB2(0).add(); + network.newLine().setId("L2").setVoltageLevel1("VL1").setVoltageLevel2("VL3").setNode1(11).setNode2(1).setR(1).setX(1).setG1(0).setB1(0).setG2(0).setB2(0).add(); + } + + private static class BusbarSectionFinderTraverser implements Terminal.TopologyTraverser { + private final boolean onlyConnectedBbs; + private String firstTraversedBbsId; + + public BusbarSectionFinderTraverser(boolean onlyConnectedBbs) { + this.onlyConnectedBbs = onlyConnectedBbs; + } + + @Override + public TraverseResult traverse(Terminal terminal, boolean connected) { + if (terminal.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) { + firstTraversedBbsId = terminal.getConnectable().getId(); + return TraverseResult.TERMINATE_TRAVERSER; + } + return TraverseResult.CONTINUE; + } + + @Override + public TraverseResult traverse(Switch aSwitch) { + if (onlyConnectedBbs && aSwitch.isOpen()) { + return TraverseResult.TERMINATE_PATH; + } + return TraverseResult.CONTINUE; + } + + public String getFirstTraversedBbsId() { + return firstTraversedBbsId; + } + } + + private String getBusbarSectionId(Terminal terminal, TraversalType traversalType) { + BusbarSectionFinderTraverser connectedBusbarSectionFinder = new BusbarSectionFinderTraverser(terminal.isConnected()); + terminal.traverse(connectedBusbarSectionFinder, traversalType); + return connectedBusbarSectionFinder.getFirstTraversedBbsId(); + } + + @Test + void testTraverseDepthFirst() { + assertEquals("VL1_B1", getBusbarSectionId(network.getGenerator("G1").getTerminal(), TraversalType.DEPTH_FIRST)); + assertEquals("VL1_B1", getBusbarSectionId(network.getGenerator("G2").getTerminal(), TraversalType.DEPTH_FIRST)); + assertEquals("VL1_B2", getBusbarSectionId(network.getLine("L1").getTerminal("VL1"), TraversalType.DEPTH_FIRST)); + assertEquals("VL2_B1", getBusbarSectionId(network.getLine("L1").getTerminal("VL2"), TraversalType.DEPTH_FIRST)); + assertEquals("VL1_B2", getBusbarSectionId(network.getLine("L2").getTerminal("VL1"), TraversalType.DEPTH_FIRST)); + assertEquals("VL3_B1", getBusbarSectionId(network.getLine("L2").getTerminal("VL3"), TraversalType.DEPTH_FIRST)); + } + + @Test + void testTraverseBreadthFirst() { + assertEquals("VL1_B2", getBusbarSectionId(network.getGenerator("G1").getTerminal(), TraversalType.BREADTH_FIRST)); + assertEquals("VL1_B2", getBusbarSectionId(network.getGenerator("G2").getTerminal(), TraversalType.BREADTH_FIRST)); + assertEquals("VL1_B1", getBusbarSectionId(network.getLine("L1").getTerminal("VL1"), TraversalType.BREADTH_FIRST)); + assertEquals("VL2_B1", getBusbarSectionId(network.getLine("L1").getTerminal("VL2"), TraversalType.BREADTH_FIRST)); + assertEquals("VL1_B1", getBusbarSectionId(network.getLine("L2").getTerminal("VL1"), TraversalType.BREADTH_FIRST)); + assertEquals("VL3_B1", getBusbarSectionId(network.getLine("L2").getTerminal("VL3"), TraversalType.BREADTH_FIRST)); + } +} diff --git a/network-store-iidm-impl/src/test/javadoc/com/powsybl/network/store/iidm/impl/doc-files/traversalByDepthOrBreadthNetwork.svg b/network-store-iidm-impl/src/test/javadoc/com/powsybl/network/store/iidm/impl/doc-files/traversalByDepthOrBreadthNetwork.svg new file mode 100644 index 000000000..e1f9a4a9d --- /dev/null +++ b/network-store-iidm-impl/src/test/javadoc/com/powsybl/network/store/iidm/impl/doc-files/traversalByDepthOrBreadthNetwork.svg @@ -0,0 +1,4 @@ + + + +
G1
G1
G2
G2
L2
L2
VL1_B1
VL1_B1
VL1_B2
VL1_B2
L1
L1
VL2_B1
VL2_B1
VL3_B1
VL3_B1
VL2
VL2
VL3
VL3
VL1
VL1
Text is not SVG - cannot display
\ No newline at end of file