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 <fan_li...@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));

Reply via email to