Skip to content

Commit

Permalink
Add Graphs.transpose() static utility methods for viewing the transpo…
Browse files Browse the repository at this point in the history
…se of a graph. See: https://en.wikipedia.org/wiki/Transpose_graph

Add Graphs.asBasicGraph(). One downside to having BasicGraph *under* Graph in the type hierarchy (still overall a good idea though :P) is that people are likely to write methods that accept "BasicGraph<N>", even though they COULD accept "Graph<N, ?>". A little bit of javadoc is not going to stop that from happening, so might as well make it less painful to handle. Also, if someone e.g. wants to make a MutableBasicGraph copy of a Graph, there was really no way to do that before. They can now do:

Graphs.copyOf(Graphs.asBasicGraph(myGraph));

Note that we cannot simply change the MutableBasicGraph version of copyOf() to accept a Graph<N, ?>, because then it would have the same type erasure as the MutableGraph version of copyOf() but a different return type. ImmutableBasicGraph.copyOf() has the same issue, because ImmutableBasicGraph extends ImmutableGraph which has it's own copyOf().

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130939830
  • Loading branch information
Bezier89 authored and cpovirk committed Aug 22, 2016
1 parent 4d5f8e0 commit ccccbf2
Show file tree
Hide file tree
Showing 2 changed files with 399 additions and 5 deletions.
153 changes: 150 additions & 3 deletions guava-tests/test/com/google/common/graph/GraphsTest.java
Expand Up @@ -16,14 +16,17 @@


package com.google.common.graph; package com.google.common.graph;


import static com.google.common.graph.Graphs.asBasicGraph;
import static com.google.common.graph.Graphs.copyOf; import static com.google.common.graph.Graphs.copyOf;
import static com.google.common.graph.Graphs.inducedSubgraph; import static com.google.common.graph.Graphs.inducedSubgraph;
import static com.google.common.graph.Graphs.reachableNodes; import static com.google.common.graph.Graphs.reachableNodes;
import static com.google.common.graph.Graphs.transitiveClosure; import static com.google.common.graph.Graphs.transitiveClosure;
import static com.google.common.graph.Graphs.transpose;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;


import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.graph.BasicGraph.Presence;
import java.util.Set; import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
Expand All @@ -45,7 +48,10 @@ public class GraphsTest {
private static final String E12_A = "1-2a"; private static final String E12_A = "1-2a";
private static final String E12_B = "1-2b"; private static final String E12_B = "1-2b";
private static final String E21 = "2-1"; private static final String E21 = "2-1";
private static final String E23 = "2-3";
private static final String E13 = "1-3"; private static final String E13 = "1-3";
private static final String E31 = "3-1";
private static final String E34 = "3-4";
private static final String E44 = "4-4"; private static final String E44 = "4-4";
private static final int NODE_COUNT = 20; private static final int NODE_COUNT = 20;
private static final int EDGE_COUNT = 20; private static final int EDGE_COUNT = 20;
Expand Down Expand Up @@ -210,8 +216,149 @@ public void transitiveClosure_undirectedCycleGraph() {
checkTransitiveClosure(undirectedGraph, expectedClosure); checkTransitiveClosure(undirectedGraph, expectedClosure);
} }


@SuppressWarnings("deprecation")
@Test @Test
public void inducedSubgraph_BasicGraph() { public void asBasicGraph_basicGraphOptimized() {
BasicGraph<Integer> basicGraph = BasicGraphBuilder.undirected().build();
assertThat(asBasicGraph(basicGraph)).isSameAs(basicGraph);
assertThat(asBasicGraph((Graph<Integer, Presence>) basicGraph)).isSameAs(basicGraph);
}

@Test
public void asBasicGraph_graph() {
MutableGraph<Integer, String> graph = GraphBuilder.directed().build();
graph.putEdgeValue(N1, N2, E12);
graph.putEdgeValue(N1, N3, E13);

BasicGraph<Integer> basicGraph = asBasicGraph(graph);
assertThat(basicGraph.edgeValue(N1, N2)).isEqualTo(Presence.EDGE_EXISTS);
assertThat(basicGraph.edgeValue(N1, N3)).isEqualTo(Presence.EDGE_EXISTS);
assertThat(basicGraph.edgeValueOrDefault(N2, N3, null)).isEqualTo(null);
assertThat(basicGraph.edgeValueOrDefault(N2, N3, Presence.EDGE_EXISTS))
.isEqualTo(Presence.EDGE_EXISTS);
try {
basicGraph.edgeValue(N2, N3);
fail("Should have rejected edgeValue() on non-existent edge");
} catch (IllegalArgumentException expected) {
}
AbstractGraphTest.validateGraph(basicGraph);

graph.putEdgeValue(N2, N3, E23);
// View should be updated.
assertThat(basicGraph.edgeValue(N2, N3)).isEqualTo(Presence.EDGE_EXISTS);
AbstractGraphTest.validateGraph(basicGraph);
}

@Test
public void transpose_undirectedBasicGraph() {
MutableBasicGraph<Integer> undirectedGraph = BasicGraphBuilder.undirected().build();
undirectedGraph.putEdge(N1, N2);

assertThat(transpose(undirectedGraph)).isEqualTo(undirectedGraph);
}

@Test
public void transpose_directedBasicGraph() {
MutableBasicGraph<Integer> directedGraph =
BasicGraphBuilder.directed().allowsSelfLoops(true).build();
directedGraph.putEdge(N3, N1);
directedGraph.putEdge(N1, N2);
directedGraph.putEdge(N1, N1);
directedGraph.putEdge(N3, N4);

MutableBasicGraph<Integer> expectedTranspose =
BasicGraphBuilder.directed().allowsSelfLoops(true).build();
expectedTranspose.putEdge(N1, N3);
expectedTranspose.putEdge(N2, N1);
expectedTranspose.putEdge(N1, N1);
expectedTranspose.putEdge(N4, N3);

BasicGraph<Integer> transpose = Graphs.transpose(directedGraph);
assertThat(transpose).isEqualTo(expectedTranspose);
assertThat(transpose(transpose)).isEqualTo(directedGraph);
AbstractGraphTest.validateGraph(transpose);

assertThat(transpose.successors(N1)).doesNotContain(N2);
directedGraph.putEdge(N2, N1);
// View should be updated.
assertThat(transpose.successors(N1)).contains(N2);
AbstractGraphTest.validateGraph(transpose);
}

@Test
public void transpose_undirectedGraph() {
MutableGraph<Integer, String> undirectedGraph = GraphBuilder.undirected().build();
undirectedGraph.putEdgeValue(N1, N2, E12);

assertThat(transpose(undirectedGraph)).isEqualTo(undirectedGraph);
}

@Test
public void transpose_directedGraph() {
MutableGraph<Integer, String> directedGraph =
GraphBuilder.directed().allowsSelfLoops(true).build();
directedGraph.putEdgeValue(N3, N1, E31);
directedGraph.putEdgeValue(N1, N2, E12);
directedGraph.putEdgeValue(N1, N1, E11);
directedGraph.putEdgeValue(N3, N4, E34);

MutableGraph<Integer, String> expectedTranspose =
GraphBuilder.directed().allowsSelfLoops(true).build();
expectedTranspose.putEdgeValue(N1, N3, E31);
expectedTranspose.putEdgeValue(N2, N1, E12);
expectedTranspose.putEdgeValue(N1, N1, E11);
expectedTranspose.putEdgeValue(N4, N3, E34);

Graph<Integer, String> transpose = Graphs.transpose(directedGraph);
assertThat(transpose).isEqualTo(expectedTranspose);
assertThat(transpose(transpose)).isEqualTo(directedGraph);
AbstractGraphTest.validateGraph(transpose);

assertThat(transpose.edgeValueOrDefault(N1, N2, null)).isNull();
directedGraph.putEdgeValue(N2, N1, E21);
// View should be updated.
assertThat(transpose.edgeValueOrDefault(N1, N2, null)).isEqualTo(E21);
AbstractGraphTest.validateGraph(transpose);
}

@Test
public void transpose_undirectedNetwork() {
MutableNetwork<Integer, String> undirectedGraph = NetworkBuilder.undirected().build();
undirectedGraph.addEdge(N1, N2, E12);

assertThat(transpose(undirectedGraph)).isEqualTo(undirectedGraph);
}

@Test
public void transpose_directedNetwork() {
MutableNetwork<Integer, String> directedGraph =
NetworkBuilder.directed().allowsSelfLoops(true).build();
directedGraph.addEdge(N3, N1, E31);
directedGraph.addEdge(N1, N2, E12);
directedGraph.addEdge(N1, N1, E11);
directedGraph.addEdge(N3, N4, E34);

MutableNetwork<Integer, String> expectedTranspose =
NetworkBuilder.directed().allowsSelfLoops(true).build();
expectedTranspose.addEdge(N1, N3, E31);
expectedTranspose.addEdge(N2, N1, E12);
expectedTranspose.addEdge(N1, N1, E11);
expectedTranspose.addEdge(N4, N3, E34);

Network<Integer, String> transpose = Graphs.transpose(directedGraph);
assertThat(transpose).isEqualTo(expectedTranspose);
assertThat(transpose(transpose)).isEqualTo(directedGraph);
AbstractNetworkTest.validateNetwork(transpose);

assertThat(transpose.edgesConnecting(N1, N2)).isEmpty();
directedGraph.addEdge(N2, N1, E21);
// View should be updated.
assertThat(transpose.edgesConnecting(N1, N2)).containsExactly(E21);
AbstractNetworkTest.validateNetwork(transpose);
}

@Test
public void inducedSubgraph_basicGraph() {
Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4); Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4);


MutableBasicGraph<Integer> directedGraph = MutableBasicGraph<Integer> directedGraph =
Expand All @@ -232,7 +379,7 @@ public void inducedSubgraph_BasicGraph() {
} }


@Test @Test
public void inducedSubgraph_Graph() { public void inducedSubgraph_graph() {
Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4); Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4);


MutableGraph<Integer, String> directedGraph = MutableGraph<Integer, String> directedGraph =
Expand All @@ -253,7 +400,7 @@ public void inducedSubgraph_Graph() {
} }


@Test @Test
public void inducedSubgraph_Network() { public void inducedSubgraph_network() {
Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4); Set<Integer> nodeSubset = ImmutableSet.of(N1, N2, N4);


MutableNetwork<Integer, String> directedGraph = MutableNetwork<Integer, String> directedGraph =
Expand Down

0 comments on commit ccccbf2

Please sign in to comment.