You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2020/06/30 06:50:51 UTC
[calcite] branch master updated: [CALCITE-4049] Improve the
implementation of the shortest-path algorithm
This is an automated email from the ASF dual-hosted git repository.
rubenql pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new bd121aa [CALCITE-4049] Improve the implementation of the shortest-path algorithm
bd121aa is described below
commit bd121aa3a6d3b0314785a4c9141028b2ef252ebf
Author: liyafan82 <fa...@foxmail.com>
AuthorDate: Tue Jun 30 11:01:15 2020 +0800
[CALCITE-4049] Improve the implementation of the shortest-path algorithm
---
.../apache/calcite/plan/ConventionTraitDef.java | 6 +-
.../calcite/plan/RelOptMaterializations.java | 4 +-
.../java/org/apache/calcite/runtime/ConsList.java | 9 ++-
.../java/org/apache/calcite/util/graph/Graphs.java | 64 +++++++++-------------
.../calcite/util/graph/DirectedGraphTest.java | 30 ++++++----
5 files changed, 55 insertions(+), 58 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
index 13a4ad9..d38d296 100644
--- a/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
+++ b/core/src/main/java/org/apache/calcite/plan/ConventionTraitDef.java
@@ -197,7 +197,7 @@ public class ConventionTraitDef extends RelTraitDef<Convention> {
Convention toConvention) {
ConversionData conversionData = getConversionData(planner);
return fromConvention.canConvertConvention(toConvention)
- || conversionData.getShortestPath(fromConvention, toConvention) != null;
+ || conversionData.getShortestDistance(fromConvention, toConvention) != -1;
}
private ConversionData getConversionData(RelOptPlanner planner) {
@@ -234,10 +234,10 @@ public class ConventionTraitDef extends RelTraitDef<Convention> {
return pathMap;
}
- public List<Convention> getShortestPath(
+ public int getShortestDistance(
Convention fromConvention,
Convention toConvention) {
- return getPathMap().getShortestPath(fromConvention, toConvention);
+ return getPathMap().getShortestDistance(fromConvention, toConvention);
}
}
}
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
index d06ec2b..eb15bf0 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
@@ -254,8 +254,8 @@ public abstract class RelOptMaterializations {
Set<RelOptTable> usedTables,
Graphs.FrozenGraph<List<String>, DefaultEdge> usesGraph) {
for (RelOptTable queryTable : usedTables) {
- if (usesGraph.getShortestPath(queryTable.getQualifiedName(), qualifiedName)
- != null) {
+ if (usesGraph.getShortestDistance(queryTable.getQualifiedName(), qualifiedName)
+ != -1) {
return true;
}
}
diff --git a/core/src/main/java/org/apache/calcite/runtime/ConsList.java b/core/src/main/java/org/apache/calcite/runtime/ConsList.java
index b212626..445cf7e 100644
--- a/core/src/main/java/org/apache/calcite/runtime/ConsList.java
+++ b/core/src/main/java/org/apache/calcite/runtime/ConsList.java
@@ -33,7 +33,6 @@ import javax.annotation.Nonnull;
public class ConsList<E> extends AbstractImmutableList<E> {
private final E first;
private final List<E> rest;
- private final int size;
/** Creates a ConsList.
* It consists of an element pre-pended to another list.
@@ -52,7 +51,6 @@ public class ConsList<E> extends AbstractImmutableList<E> {
private ConsList(E first, List<E> rest) {
this.first = first;
this.rest = rest;
- this.size = 1 + rest.size();
}
public E get(int index) {
@@ -68,7 +66,12 @@ public class ConsList<E> extends AbstractImmutableList<E> {
}
public int size() {
- return size;
+ int s = 1;
+ for (ConsList c = this;; c = (ConsList) c.rest, ++s) {
+ if (!(c.rest instanceof ConsList)) {
+ return s + c.rest.size();
+ }
+ }
}
@Override public int hashCode() {
diff --git a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
index 2aaebd1..e731e17 100644
--- a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
+++ b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
@@ -22,14 +22,13 @@ import com.google.common.collect.ImmutableList;
import java.util.AbstractList;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static org.apache.calcite.util.Static.cons;
-
/**
* Miscellaneous graph utilities.
*/
@@ -56,42 +55,40 @@ public class Graphs {
public static <V, E extends DefaultEdge> FrozenGraph<V, E> makeImmutable(
DirectedGraph<V, E> graph) {
DefaultDirectedGraph<V, E> graph1 = (DefaultDirectedGraph<V, E>) graph;
- Map<Pair<V, V>, List<V>> shortestPaths = new HashMap<>();
+ Map<Pair<V, V>, int[]> shortestDistances = new HashMap<>();
for (DefaultDirectedGraph.VertexInfo<V, E> arc
: graph1.vertexMap.values()) {
for (E edge : arc.outEdges) {
final V source = graph1.source(edge);
final V target = graph1.target(edge);
- shortestPaths.put(Pair.of(source, target),
- ImmutableList.of(source, target));
+ shortestDistances.put(Pair.of(source, target), new int[] {1});
}
}
while (true) {
// Take a copy of the map's keys to avoid
// ConcurrentModificationExceptions.
final List<Pair<V, V>> previous =
- ImmutableList.copyOf(shortestPaths.keySet());
- int changeCount = 0;
+ ImmutableList.copyOf(shortestDistances.keySet());
+ boolean changed = false;
for (E edge : graph.edgeSet()) {
for (Pair<V, V> edge2 : previous) {
if (edge.target.equals(edge2.left)) {
final Pair<V, V> key = Pair.of(graph1.source(edge), edge2.right);
- List<V> bestPath = shortestPaths.get(key);
- List<V> arc2Path = shortestPaths.get(edge2);
- if ((bestPath == null)
- || (bestPath.size() > (arc2Path.size() + 1))) {
- shortestPaths.put(key,
- cons(graph1.source(edge), arc2Path));
- changeCount++;
+ int[] bestDistance = shortestDistances.get(key);
+ int[] arc2Distance = shortestDistances.get(edge2);
+ if ((bestDistance == null)
+ || (bestDistance[0] > (arc2Distance[0] + 1))) {
+ shortestDistances.put(key, new int[] {arc2Distance[0] + 1});
+ changed = true;
}
}
}
}
- if (changeCount == 0) {
+ if (!changed) {
break;
}
}
- return new FrozenGraph<>(graph1, shortestPaths);
+ return new FrozenGraph<>(graph1, shortestDistances);
}
/**
@@ -102,39 +99,29 @@ public class Graphs {
*/
public static class FrozenGraph<V, E extends DefaultEdge> {
private final DefaultDirectedGraph<V, E> graph;
- private final Map<Pair<V, V>, List<V>> shortestPaths;
+ private final Map<Pair<V, V>, int[]> shortestDistances;
/** Creates a frozen graph as a copy of another graph. */
FrozenGraph(DefaultDirectedGraph<V, E> graph,
- Map<Pair<V, V>, List<V>> shortestPaths) {
+ Map<Pair<V, V>, int[]> shortestDistances) {
this.graph = graph;
- this.shortestPaths = shortestPaths;
+ this.shortestDistances = shortestDistances;
}
/**
- * Returns an iterator of all paths between two nodes, shortest first.
+ * Returns an iterator of all paths between two nodes,
+ * in non-decreasing order of path lengths.
*
* <p>The current implementation is not optimal.</p>
*/
public List<List<V>> getPaths(V from, V to) {
List<List<V>> list = new ArrayList<>();
- findPaths(from, to, list);
- return list;
- }
-
- /**
- * Returns the shortest path between two points, null if there is no path.
- *
- * @param from From
- * @param to To
- *
- * @return A list of arcs, null if there is no path.
- */
- public List<V> getShortestPath(V from, V to) {
if (from.equals(to)) {
- return ImmutableList.of();
+ list.add(ImmutableList.of(from));
}
- return shortestPaths.get(Pair.of(from, to));
+ findPaths(from, to, list);
+ list.sort(Comparator.comparingInt(List::size));
+ return list;
}
/**
@@ -147,13 +134,12 @@ public class Graphs {
if (from.equals(to)) {
return 0;
}
- List<V> path = shortestPaths.get(Pair.of(from, to));
- return path == null ? -1 : path.size() - 1;
+ int[] distance = shortestDistances.get(Pair.of(from, to));
+ return distance == null ? -1 : distance[0];
}
private void findPaths(V from, V to, List<List<V>> list) {
- final List<V> shortestPath = shortestPaths.get(Pair.of(from, to));
- if (shortestPath == null) {
+ if (getShortestDistance(from, to) == -1) {
return;
}
// final E edge = graph.getEdge(from, to);
diff --git a/core/src/test/java/org/apache/calcite/util/graph/DirectedGraphTest.java b/core/src/test/java/org/apache/calcite/util/graph/DirectedGraphTest.java
index 87a4f4e..c98330e 100644
--- a/core/src/test/java/org/apache/calcite/util/graph/DirectedGraphTest.java
+++ b/core/src/test/java/org/apache/calcite/util/graph/DirectedGraphTest.java
@@ -64,14 +64,21 @@ class DirectedGraphTest {
g.addEdge("B", "D");
assertEquals("[A, B, D]", shortestPath(g, "A", "D").toString());
assertNull(shortestPath(g, "A", "E"), "There is no path from A to E");
- assertEquals("[]", shortestPath(g, "D", "D").toString());
+ assertEquals("[D]", shortestPath(g, "D", "D").toString());
assertNull(shortestPath(g, "X", "A"), "Node X is not in the graph");
- assertEquals("[[A, B, C, D], [A, B, D]]", paths(g, "A", "D").toString());
+ assertEquals("[[A, B, D], [A, B, C, D]]", paths(g, "A", "D").toString());
}
private <V> List<V> shortestPath(DirectedGraph<V, DefaultEdge> g,
V source, V target) {
- return Graphs.makeImmutable(g).getShortestPath(source, target);
+ List<List<V>> paths = Graphs.makeImmutable(g).getPaths(source, target);
+ return paths.isEmpty() ? null : paths.get(0);
+ }
+
+ private <V> List<V> shortestPath(Graphs.FrozenGraph<V, DefaultEdge> g,
+ V source, V target) {
+ List<List<V>> paths = g.getPaths(source, target);
+ return paths.isEmpty() ? null : paths.get(0);
}
private <V> List<List<V>> paths(DirectedGraph<V, DefaultEdge> g,
@@ -217,15 +224,16 @@ class DirectedGraphTest {
final DefaultDirectedGraph<String, DefaultEdge> graph = createDag1();
final Graphs.FrozenGraph<String, DefaultEdge> frozenGraph =
Graphs.makeImmutable(graph);
- assertEquals("[A, B]", frozenGraph.getShortestPath("A", "B").toString());
+ assertEquals("[A, B]", shortestPath(frozenGraph, "A", "B").toString());
assertEquals("[[A, B]]", frozenGraph.getPaths("A", "B").toString());
- assertEquals("[A, D, E]", frozenGraph.getShortestPath("A", "E").toString());
- assertEquals("[[A, B, C, E], [A, D, E]]",
+ assertEquals("[A, D, E]", shortestPath(frozenGraph, "A", "E").toString());
+ assertEquals("[[A, D, E], [A, B, C, E]]",
frozenGraph.getPaths("A", "E").toString());
- assertNull(frozenGraph.getShortestPath("B", "A"));
- assertNull(frozenGraph.getShortestPath("D", "C"));
+ assertNull(shortestPath(frozenGraph, "B", "A"));
+
+ assertNull(shortestPath(frozenGraph, "D", "C"));
assertEquals("[[D, E]]", frozenGraph.getPaths("D", "E").toString());
- assertEquals("[D, E]", frozenGraph.getShortestPath("D", "E").toString());
+ assertEquals("[D, E]", shortestPath(frozenGraph, "D", "E").toString());
}
@Test void testDistances() {
@@ -355,9 +363,9 @@ class DirectedGraphTest {
g.addEdge("B", "D", 1);
assertEquals("[A, B, D]", shortestPath(g, "A", "D").toString());
assertNull(shortestPath(g, "A", "E"), "There is no path from A to E");
- assertEquals("[]", shortestPath(g, "D", "D").toString());
+ assertEquals("[D]", shortestPath(g, "D", "D").toString());
assertNull(shortestPath(g, "X", "A"), "Node X is not in the graph");
- assertEquals("[[A, B, C, D], [A, B, D]]", paths(g, "A", "D").toString());
+ assertEquals("[[A, B, D], [A, B, C, D]]", paths(g, "A", "D").toString());
assertThat(g.addVertex("B"), is(false));
assertThat(Iterables.size(g.getEdges("A", "B")), is(1));