diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/Prim.java b/algo/src/main/java/org/neo4j/graphalgo/impl/Prim.java index a9cb3cb0f..7c218f898 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/Prim.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/Prim.java @@ -45,6 +45,7 @@ */ public class Prim extends Algorithm { + public static final int UNUSED = 42; private final RelationshipIterator relationshipIterator; private final RelationshipWeights weights; private final int nodeCount; @@ -90,11 +91,16 @@ private SpanningTree prim(int startNode, boolean max) { if (visited.contains(t)) { return true; } - // invert weight to calculate maximum + // inverting the weight calculates maximum- instead of minimum spanning tree final double w = max ? -weights.weightOf(s, t) : weights.weightOf(s, t); if (w < cost.getOrDefault(t, Double.MAX_VALUE)) { - cost.put(t, w); - queue.add(t, -1.0); + if (cost.containsKey(t)) { + cost.put(t, w); + queue.update(t); + } else { + cost.put(t, w); + queue.add(t, w); + } spanningTree.parent[t] = s; } return true; diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPathDijkstra.java b/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPathDijkstra.java index 90b741de0..53b2a461e 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPathDijkstra.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPathDijkstra.java @@ -39,6 +39,7 @@ public class ShortestPathDijkstra extends Algorithm { private static final int PATH_END = -1; public static final double NO_PATH_FOUND = -1.0; + public static final int UNUSED = 42; private Graph graph; @@ -152,28 +153,28 @@ private void run(int goal, Direction direction) { graph.forEachRelationship( node, direction, (source, target, relId, weight) -> { - boolean oldCostChanged = updateCosts(source, target, weight + costs); - if (!visited.get(target)) { - if (oldCostChanged) { - queue.update(target); - } else { - queue.add(target, 0); - } - } + updateCosts(source, target, weight + costs); return true; }); progressLogger.logProgress((double) node / (nodeCount - 1)); } } - private boolean updateCosts(int source, int target, double newCosts) { - double oldCosts = costs.getOrDefault(target, Double.MAX_VALUE); - if (newCosts < oldCosts) { - costs.put(target, newCosts); - path.put(target, source); - return oldCosts < Double.MAX_VALUE; + private void updateCosts(int source, int target, double newCosts) { + + if (costs.containsKey(target)) { + if (newCosts < costs.getOrDefault(target, Double.MAX_VALUE)) { + costs.put(target, newCosts); + path.put(target, source); + queue.update(target); + } + } else { + if (newCosts < costs.getOrDefault(target, Double.MAX_VALUE)) { + costs.put(target, newCosts); + path.put(target, source); + queue.add(target, newCosts); + } } - return false; } @Override diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPaths.java b/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPaths.java index 65521b815..bd71c33ec 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPaths.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/ShortestPaths.java @@ -99,7 +99,7 @@ private void run() { final double targetCosts = this.costs.getOrDefault(target, Double.POSITIVE_INFINITY); if (weight + sourceCosts < targetCosts) { costs.put(target, weight + sourceCosts); - queue.set(target, targetCosts); + queue.set(target, weight + sourceCosts); } return true; }); diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/KSpanningTree.java b/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/KSpanningTree.java index 6d39bb2b5..d1ae9c4eb 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/KSpanningTree.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/KSpanningTree.java @@ -29,8 +29,6 @@ import org.neo4j.graphalgo.results.AbstractResultBuilder; /** - * Sequential Single-Source minimum weight spanning tree algorithm (PRIM). - *

* The algorithm computes the MST by traversing all nodes from a given * startNodeId. It aggregates all transitions into a MinPriorityQueue * and visits each (unvisited) connected node by following only the diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/Prim.java b/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/Prim.java index 0967ce4d3..9e57cbde9 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/Prim.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/spanningTrees/Prim.java @@ -86,8 +86,13 @@ private SpanningTree prim(int startNode, boolean max) { // invert weight to calculate maximum final double w = max ? -weights.weightOf(s, t) : weights.weightOf(s, t); if (w < cost.getOrDefault(t, Double.MAX_VALUE)) { - cost.put(t, w); - queue.add(t, -1.0); + if (cost.containsKey(t)) { + cost.put(t, w); + queue.update(t); + } else { + cost.put(t, w); + queue.add(t, -1.0); + } parent[t] = s; } return true; diff --git a/algo/src/main/java/org/neo4j/graphalgo/impl/yens/Dijkstra.java b/algo/src/main/java/org/neo4j/graphalgo/impl/yens/Dijkstra.java index a044541b5..99668cdf7 100644 --- a/algo/src/main/java/org/neo4j/graphalgo/impl/yens/Dijkstra.java +++ b/algo/src/main/java/org/neo4j/graphalgo/impl/yens/Dijkstra.java @@ -24,7 +24,6 @@ import org.neo4j.graphalgo.core.utils.TerminationFlag; import org.neo4j.graphalgo.core.utils.queue.IntPriorityQueue; import org.neo4j.graphalgo.core.utils.queue.SharedIntPriorityQueue; -import org.neo4j.graphalgo.core.utils.traverse.SimpleBitSet; import org.neo4j.graphdb.Direction; import java.util.Arrays; @@ -38,18 +37,19 @@ public class Dijkstra { // initial weighted path capacity + public static final int INITIAL_CAPACITY = 64; private static final int PATH_END = -1; - private final Graph graph; + private final int nodeCount; private TerminationFlag terminationFlag = TerminationFlag.RUNNING_TRUE; - // node to cost map private final IntDoubleMap costs; // next node priority queue + private final IntPriorityQueue queue; // auxiliary path map private final IntIntMap path; @@ -157,7 +157,8 @@ private boolean dijkstra(int source, int target, Direction direction, int maxDep visited.clear(); costs.put(source, 0.0); queue.add(source, 0.0); - Arrays.fill(depth, 1); + Arrays.fill(depth, 0); + depth[source] = 1; while (!queue.isEmpty() && terminationFlag.running()) { int node = queue.pop(); final int d = depth[node]; @@ -176,14 +177,19 @@ private boolean dijkstra(int source, int target, Direction direction, int maxDep return true; } final double w = graph.weightOf(s, t); - final boolean updateCosts = updateCosts(s, t, w + costs); + final UpdateResult updateCosts = updateCosts(s, t, w + costs); if (!visited.get(t)) { - depth[t] = d + 1; - if (updateCosts) { - queue.update(t); - } else { - queue.add(t, w); + switch (updateCosts) { + case NO_PREVIOUS_COSTS: + queue.add(t, w); + break; + case UPDATED_COST: + queue.update(t); + break; + default: + break; } + depth[t] = depth[s] + 1; } return terminationFlag.running(); }); @@ -191,17 +197,29 @@ private boolean dijkstra(int source, int target, Direction direction, int maxDep return false; } + /** * update cost map */ - private boolean updateCosts(int source, int target, double newCosts) { + private UpdateResult updateCosts(int source, int target, double newCosts) { double oldCosts = costs.getOrDefault(target, Double.MAX_VALUE); + if (oldCosts == Double.MAX_VALUE) { + if (!costs.containsKey(target)) { + costs.put(target, newCosts); + path.put(target, source); + return UpdateResult.NO_PREVIOUS_COSTS; + } + } if (newCosts < oldCosts) { costs.put(target, newCosts); path.put(target, source); - return oldCosts < Double.MAX_VALUE; + return UpdateResult.UPDATED_COST; } - return false; + return UpdateResult.COST_NOT_COMPETITIVE; + } + + private enum UpdateResult { + NO_PREVIOUS_COSTS, UPDATED_COST, COST_NOT_COMPETITIVE; } } diff --git a/benchmark/src/main/java/org/neo4j/graphalgo/bench/AllShortestPathsComparisionBenchmark.java b/benchmark/src/main/java/org/neo4j/graphalgo/bench/AllShortestPathsComparisionBenchmark.java index 6520b6bed..6f0e98dc2 100644 --- a/benchmark/src/main/java/org/neo4j/graphalgo/bench/AllShortestPathsComparisionBenchmark.java +++ b/benchmark/src/main/java/org/neo4j/graphalgo/bench/AllShortestPathsComparisionBenchmark.java @@ -1,56 +1,37 @@ /** * Copyright (c) 2017 "Neo4j, Inc." - * + *

* This file is part of Neo4j Graph Algorithms . - * + *

* Neo4j Graph Algorithms is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + *

* You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.neo4j.graphalgo.bench; -import org.neo4j.graphalgo.ShortestPathDeltaSteppingProc; -import org.neo4j.graphalgo.ShortestPathsProc; import org.neo4j.graphalgo.api.Graph; import org.neo4j.graphalgo.api.HugeGraph; import org.neo4j.graphalgo.core.GraphLoader; -import org.neo4j.graphalgo.core.heavyweight.HeavyGraphFactory; import org.neo4j.graphalgo.core.huge.HugeGraphFactory; import org.neo4j.graphalgo.core.utils.Pools; import org.neo4j.graphalgo.core.utils.paged.AllocationTracker; import org.neo4j.graphalgo.impl.AllShortestPaths; import org.neo4j.graphalgo.impl.HugeMSBFSAllShortestPaths; import org.neo4j.graphalgo.impl.MSBFSAllShortestPaths; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.RelationshipType; -import org.neo4j.graphdb.Transaction; -import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.*; import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.kernel.impl.proc.Procedures; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.test.TestGraphDatabaseFactory; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.*; import java.util.ArrayList; import java.util.HashMap; @@ -143,7 +124,7 @@ private static Relationship createRelation(Node from, Node to) { @Benchmark public long _01_benchmark_ASP() { - return new AllShortestPaths(graph, Pools.DEFAULT, 8, Direction.OUTGOING) + return new AllShortestPaths(graph, Pools.DEFAULT, 8, Direction.OUTGOING) .resultStream() .count(); } diff --git a/core/src/main/java/org/neo4j/graphalgo/core/utils/queue/IntPriorityQueue.java b/core/src/main/java/org/neo4j/graphalgo/core/utils/queue/IntPriorityQueue.java index ce95957c4..d9aaca23e 100644 --- a/core/src/main/java/org/neo4j/graphalgo/core/utils/queue/IntPriorityQueue.java +++ b/core/src/main/java/org/neo4j/graphalgo/core/utils/queue/IntPriorityQueue.java @@ -239,9 +239,10 @@ private void downHeap(int i) { private void ensureCapacityForInsert() { if (size >= heap.length) { + final int oversize = ArrayUtil.oversize(size + 1, Integer.BYTES); heap = Arrays.copyOf( heap, - ArrayUtil.oversize(size + 1, Integer.BYTES)); + oversize); } }