Added quicksort example, and made larger change as it wasn't as expected

first. This change should improve the results of the analysis.
This commit is contained in:
Stephan Gocht 2015-11-06 18:52:11 +01:00
parent 381cf2d2f1
commit dfaa44d111
10 changed files with 420 additions and 210 deletions

View File

@ -174,4 +174,53 @@ public class Detectable {
throw new IllegalArgumentException();
}
}
public String afterLoop(int[] arr) {
int len = arr.length - 1;
StringBuffer buffer = new StringBuffer();
int zero = 0;
if (zero < arr.length) {
int i = zero;
while (i < len) {
buffer.append(arr[i]);
buffer.append(", ");
i++;
}
buffer.append(arr[i]);
}
return buffer.toString();
}
public static void quickSort(int[] arr, int left, int right) {
if (0 <= left && right <= arr.length && left < right - 1) {
int pivot = arr[left];
int lhs = left + 1;
int rhs = right - 1;
while (lhs < rhs) {
while (arr[lhs] <= pivot && lhs < right - 1) {
lhs++;
}
while (arr[rhs] >= pivot && rhs > left) {
rhs--;
}
if (lhs < rhs) {
int tmp = arr[lhs];
arr[lhs] = arr[rhs];
arr[rhs] = tmp;
}
}
if (arr[lhs] < pivot) {
arr[left] = arr[lhs];
arr[lhs] = pivot;
}
quickSort(arr, left, lhs);
quickSort(arr, lhs, right);
}
}
}

View File

@ -138,4 +138,30 @@ public class NotDetectable {
throw new IllegalArgumentException();
}
}
/**
* The analysis can not detect, that forward is false every other iteration.
* So there is a positive and a negative loop. The positive loop is bound by
* the loop condition, while the negative loop is unbound, so index might be
* smaller than zero. This should result in the lower bound check beeing
* necessary.
*
* @param arr
* @return sum of all elements in arr
*/
public int nonMonotounous(int arr[]) {
int index = 0;
int sum = 0;
boolean forward = true;
while (index < arr.length) {
sum += arr[index];
if (forward) {
index += 2;
} else {
index -= 1;
}
forward = !forward;
}
return sum;
}
}

View File

@ -41,4 +41,40 @@ public class NotInBound {
throw new IllegalArgumentException();
}
}
public int innerLoop(int[] arr) {
int sum = 0;
int i = 0;
while (i < arr.length) {
while (i < 6) {
i++;
sum += arr[i];
}
}
return sum;
}
public int comparedWrong(int[] arr, int index) {
if (index > 0 && index > arr.length) {
return arr[index];
} else {
throw new IllegalArgumentException();
}
}
public int nonTrackedOperationIsSafe(int[] arr, int index) {
if (index > 0 && index > arr.length) {
return arr[2*index];
} else {
throw new IllegalArgumentException();
}
}
public int unboundNegativeLoop(int[] arr, int index) {
int sum = 0;
for (int i = 5; i < arr.length; i--) {
sum += arr[i];
}
return sum;
}
}

View File

@ -25,6 +25,9 @@ import com.ibm.wala.ssa.AllDueToBranchePiPolicy;
import com.ibm.wala.ssa.DefaultIRFactory;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.IRFactory;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.config.AnalysisScopeReader;
@ -45,13 +48,13 @@ public class ArrayboundsAnalysisTest {
private static ClassLoader CLASS_LOADER = ArrayboundsAnalysisTest.class.getClassLoader();
private static final String DETECTABLE_TESTDATA = "Larraybounds/Detectable";
private static final int DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 21;
private static final int DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 34;
private static final String NOT_DETECTABLE_TESTDATA = "Larraybounds/NotDetectable";
private static final int NOT_DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 7;
private static final int NOT_DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 8;
private static final String NOT_IN_BOUND_TESTDATA = "Larraybounds/NotInBound";
private static final int NOT_IN_BOUND_TESTDATA_NUMBER_OF_ARRAY_ACCESS = 4;
private static final int NOT_IN_BOUND_TESTDATA_NUMBER_OF_ARRAY_ACCESS = 8;
private static IRFactory<IMethod> irFactory;
private static AnalysisOptions options;
@ -102,12 +105,23 @@ public class ArrayboundsAnalysisTest {
int numberOfArrayAccesses = 0;
for (IMethod method : iClass.getAllMethods()) {
if (method.getDeclaringClass().equals(iClass)) {
IR ir = getIr(method);
StringBuilder builder = new StringBuilder();
for (ISSABasicBlock block : ir.getControlFlowGraph()) {
for (SSAInstruction instruction : block) {
builder.append(instruction);
builder.append("\n");
}
}
String identifyer = method.getDeclaringClass().getName().toString() + "#" + method.getName().toString();
ArrayOutOfBoundsAnalysis analysis = new ArrayOutOfBoundsAnalysis(getIr(method));
for (UnnecessaryCheck unnecessary : analysis.getBoundsCheckNecessary().values()) {
ArrayOutOfBoundsAnalysis analysis = new ArrayOutOfBoundsAnalysis(ir);
for (SSAArrayReferenceInstruction key : analysis.getBoundsCheckNecessary().keySet()) {
numberOfArrayAccesses++;
collector.checkThat("Unexpected necessity for bounds check in " + identifyer, unnecessary, matcher);
UnnecessaryCheck unnecessary = analysis.getBoundsCheckNecessary().get(key);
collector.checkThat("Unexpected necessity for bounds check in " + identifyer + ":" + method.getLineNumber(key.iindex),
unnecessary, matcher);
}
}
}

View File

@ -65,7 +65,7 @@ public class PruneArrayOutOfBoundExceptionEdge {
* The number of Basic Blocks, which have an exception edge, that should be
* removed. (#[array access] + #[other])
*/
private static final int DETECTABLE_EXPECTED_COUNT = 21 + 1;
private static final int DETECTABLE_EXPECTED_COUNT = 34 + 2;
private static final String NOT_DETECTABLE_TESTDATA = "Larraybounds/NotDetectable";
/**

View File

@ -7,10 +7,12 @@ import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.SoftFinalHyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.AdditiveEdgeWeight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
import com.ibm.wala.util.collections.Pair;
/**
* Some thoughts about implementation details, not mentioned in [1]:
@ -59,6 +61,9 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
* which is never produced by ssa generation
*/
public final static Integer ZERO = -1;
public final static Integer ZERO_HELPER = -3;
/**
* We need a ssa variable representing unlimited (values we don't know
* anything about). So we just use an integer, which is never produced by
@ -79,6 +84,8 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
private final HashMap<Integer, Integer> arrayLength;
private final HashSet<Integer> phis;
private final HashMap<Integer, Pair<Integer,Integer>> constants;
/**
* For simplicity we introduce extra variables, for arrayLength, to have a
@ -87,17 +94,37 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
*
* Start with -3 so it is unequal to other constants
*/
private Integer arrayCounter = -3;
private Integer arrayCounter = -4;
public ArrayBoundsGraph() {
this.arrayAccess = new HashMap<>();
this.arrayLength = new HashMap<>();
this.constants = new HashMap<>();
this.phis = new HashSet<>();
this.addNode(UNLIMITED);
this.phis.add(UNLIMITED);
this.createSourceVar(ZERO);
this.addNode(ZERO_HELPER);
this.addEdge(ZERO, ZERO_HELPER);
//this.phis.add(ZERO_HELPER);
}
public void postProcessConstants() {
for (Integer constant:constants.keySet()) {
HyperNode<Integer> constantNode = this.getNodes().get(constant);
HyperNode<Integer> helper1 = this.getNodes().get(constants.get(constant).fst);
HyperNode<Integer> helper2 = this.getNodes().get(constants.get(constant).snd);
for (DirectedHyperEdge<Integer> edge:constantNode.getOutEdges()) {
if (!edge.getDestination().contains(helper2)) {
edge.getSource().remove(constant);
edge.getSource().add(helper1);
}
}
}
}
public void addAdditionEdge(Integer src, Integer dst, Integer value) {
this.addNode(src);
final HyperNode<Integer> srcNode = this.getNodes().get(src);
@ -151,10 +178,12 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
final Integer helper1 = this.generateNewVar();
final Integer helper2 = this.generateNewVar();
this.addAdditionEdge(ZERO, helper1, value);
this.addEdge(helper1, variable);
this.addAdditionEdge(ZERO_HELPER, helper1, value);
// this.addEdge(helper1, variable);
this.addAdditionEdge(variable, helper2, -value);
this.addEdge(helper2, ZERO);
this.addEdge(helper2, ZERO_HELPER);
this.constants.put(variable, Pair.make(helper1, helper2));
}
public void addEdge(Integer src, Integer dst) {
@ -194,19 +223,24 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
* @param var
*/
public void createSourceVar(Integer var) {
this.addNode(var);
if (this.getNodes().keySet().contains(var)){
throw new AssertionError("Source variables should only be created once.");
}
SoftFinalHyperNode<Integer> node = new SoftFinalHyperNode<Integer>(var);
this.getNodes().put(var, node);
final HyperNode<Integer> arrayNode = this.getNodes().get(var);
final HyperNode<Integer> unlimitedNode = this.getNodes().get(UNLIMITED);
// final HyperNode<Integer> varNode = this.getNodes().get(var);
// final HyperNode<Integer> unlimitedNode = this.getNodes().get(UNLIMITED);
final DirectedHyperEdge<Integer> edge = new DirectedHyperEdge<>();
edge.setWeight(new AdditiveEdgeWeight(Weight.UNLIMITED));
edge.getSource().add(arrayNode);
edge.getDestination().add(unlimitedNode);
this.getEdges().add(edge);
// final DirectedHyperEdge<Integer> edge = new DirectedHyperEdge<>();
// edge.setWeight(new AdditiveEdgeWeight(Weight.UNLIMITED));
// edge.getSource().add(varNode);
// edge.getDestination().add(unlimitedNode);
// this.getEdges().add(edge);
this.addEdge(UNLIMITED, var);
this.addEdge(var, var);
//this.addEdge(var, var);
}
public Integer generateNewVar() {
@ -264,4 +298,19 @@ public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
public void markAsDeadEnd(Integer variable) {
this.addEdge(UNLIMITED, variable);
}
public Weight getVariableWeight(Integer variable){
if (constants.containsKey(variable)) {
variable = constants.get(variable).fst;
}
return this.getNodes().get(variable).getWeight();
}
@Override
public void reset() {
super.reset();
this.getNodes().get(UNLIMITED).setWeight(Weight.UNLIMITED);
this.getNodes().get(UNLIMITED).setNewWeight(Weight.UNLIMITED);
}
}

View File

@ -57,6 +57,15 @@ public class ArrayBoundsGraphBuilder {
this.exploreIr();
this.addConstructionLength();
this.lowerBoundGraph.updateNodeEdges();
this.upperBoundGraph.updateNodeEdges();
this.lowerBoundGraph.postProcessConstants();
this.upperBoundGraph.postProcessConstants();
this.lowerBoundGraph.updateNodeEdges();
this.upperBoundGraph.updateNodeEdges();
this.bundleDeadEnds(this.lowerBoundGraph);
this.bundleDeadEnds(this.upperBoundGraph);

View File

@ -99,8 +99,10 @@ public class ArrayOutOfBoundsAnalysis {
ShortestPath.compute(this.lowerBoundGraph, zero, new NormalOrder());
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
final HyperNode<Integer> node = this.lowerBoundGraph.getNodes().get(instruction.getIndex());
if (node.getWeight().getType() == Weight.Type.NUMBER && node.getWeight().getNumber() >= 0) {
Weight weight = this.lowerBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() >= 0) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.LOWER);
}
}
@ -118,9 +120,9 @@ public class ArrayOutOfBoundsAnalysis {
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
if (instruction.getArrayRef() == array) {
final HyperNode<Integer> node = this.upperBoundGraph.getNodes().get(instruction.getIndex());
if (node.getWeight().getType() == Weight.Type.NUMBER && node.getWeight().getNumber() <= -1) {
Weight weight = this.upperBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() <= -1) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.UPPER);
}
}

View File

@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.analysis.arraybounds.hypergraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
public class SoftFinalHyperNode<T> extends HyperNode<T> {
public SoftFinalHyperNode(T value) {
super(value);
}
@Override
public void setWeight(Weight weight) {
if (weight.equals(Weight.NOT_SET) || this.getWeight().equals(Weight.NOT_SET)) {
super.setWeight(weight);
}
};
}

View File

@ -16,214 +16,212 @@ import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeigh
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <T>
* NodeValueType
* NodeValueType
* @see ShortestPath#compute(DirectedHyperGraph, HyperNode, Comparator)
*/
public class ShortestPath<T> {
/**
* Computes all shortest paths from source. The result is stored in
* {@link HyperNode#getWeight()}.
*
* This is using a variation of Bellman-Ford for hyper graphs.
*
* @param graph
* @param source
* @param comparator
* defines order on weights.
*/
public static <NodeValueType> void compute(
DirectedHyperGraph<NodeValueType> graph,
HyperNode<NodeValueType> source, Comparator<Weight> comparator) {
graph.reset();
source.setWeight(Weight.ZERO);
new ShortestPath<>(graph, comparator);
}
/**
* Computes all shortest paths from source. The result is stored in
* {@link HyperNode#getWeight()}.
*
* This is using a variation of Bellman-Ford for hyper graphs.
*
* @param graph
* @param source
* @param comparator
* defines order on weights.
*/
public static <NodeValueType> void compute(DirectedHyperGraph<NodeValueType> graph, HyperNode<NodeValueType> source,
Comparator<Weight> comparator) {
graph.reset();
source.setWeight(Weight.ZERO);
new ShortestPath<>(graph, comparator);
}
private Set<DirectedHyperEdge<T>> updatedEdges;
private final Comparator<Weight> comparator;
private final DirectedHyperGraph<T> graph;
private boolean setUnlimitedOnChange = false;
private Set<DirectedHyperEdge<T>> updatedEdges;
private final Comparator<Weight> comparator;
private final DirectedHyperGraph<T> graph;
private boolean setUnlimitedOnChange = false;
private boolean hasNegativeCycle = false;
private boolean hasNegativeCycle = false;
/**
* @param graph
* Source nodes for shortest path computation should be set to 0,
* other nodes should be set to {@link Weight#NOT_SET}.
* @param comparator
* defines order on weights.
*/
private ShortestPath(DirectedHyperGraph<T> graph,
Comparator<Weight> comparator) {
this.comparator = comparator;
this.graph = graph;
/**
* @param graph
* Source nodes for shortest path computation should be set to 0,
* other nodes should be set to {@link Weight#NOT_SET}.
* @param comparator
* defines order on weights.
*/
private ShortestPath(DirectedHyperGraph<T> graph, Comparator<Weight> comparator) {
this.comparator = comparator;
this.graph = graph;
this.computeShortestPaths();
this.hasNegativeCycle = (this.updatedEdges.size() > 0);
if (this.hasNegativeCycle) {
// trigger, that changing values are set to infty in writeChanges:
this.setUnlimitedOnChange = true;
this.computeShortestPaths();
this.hasNegativeCycle = (this.updatedEdges.size() > 0);
if (this.hasNegativeCycle) {
// trigger, that changing values are set to infty in writeChanges:
this.setUnlimitedOnChange = true;
// we need to propagate negative cycle to all connected nodes
this.computeShortestPaths();
}
}
// we need to propagate negative cycle to all connected nodes
this.computeShortestPaths();
}
}
private void computeShortestPaths() {
this.updatedEdges = this.graph.getEdges();
final int nodeCount = this.graph.getNodes().size();
for (int i = 0; i < nodeCount - 1; i++) {
this.updateAllEdges();
if (this.updatedEdges.size() == 0) {
break;
}
}
}
private void computeShortestPaths() {
this.updatedEdges = this.graph.getEdges();
final int nodeCount = this.graph.getNodes().size();
for (int i = 0; i < nodeCount - 1; i++) {
this.updateAllEdges();
if (this.updatedEdges.size() == 0) {
break;
}
}
}
/**
* @param weight
* @param otherWeight
* @return weight > otherWeight
*/
private boolean greaterThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET
|| this.comparator.compare(weight, otherWeight) > 0;
}
/**
* @param weight
* @param otherWeight
* @return weight > otherWeight
*/
private boolean greaterThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET || this.comparator.compare(weight, otherWeight) > 0;
}
/**
* @param weight
* @param otherWeight
* @return weight < otherWeight
*/
private boolean lessThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET
|| this.comparator.compare(weight, otherWeight) < 0;
}
/**
* @param weight
* @param otherWeight
* @return weight < otherWeight
*/
private boolean lessThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET || this.comparator.compare(weight, otherWeight) < 0;
}
/**
* Maximum of source weights, modified by the value of the edge. Note that
* every weight is larger than {@link Weight#NOT_SET} for max computation.
* This allows distances to propagate, even if not all nodes are connected
* to the source of the shortest path computation. Otherwise (source,
* other)->(sink) would not have a path from source to sink.
*
* @param edge
* @return max{edgeValue.newValue(sourceWeight) | sourceWeight \in
* edge.getSources()}
*/
private Weight maxOfSources(final DirectedHyperEdge<T> edge) {
final EdgeWeight edgeValue = edge.getWeight();
Weight newWeight = Weight.NOT_SET;
for (final HyperNode<T> node : edge.getSource()) {
/**
* Maximum of source weights, modified by the value of the edge. Note that
* every weight is larger than {@link Weight#NOT_SET} for max computation.
* This allows distances to propagate, even if not all nodes are connected to
* the source of the shortest path computation. Otherwise (source,
* other)->(sink) would not have a path from source to sink.
*
* @param edge
* @return max{edgeValue.newValue(sourceWeight) | sourceWeight \in
* edge.getSources()}
*/
private Weight maxOfSources(final DirectedHyperEdge<T> edge) {
final EdgeWeight edgeValue = edge.getWeight();
Weight newWeight = Weight.NOT_SET;
for (final HyperNode<T> node : edge.getSource()) {
final Weight nodeWeight = node.getWeight();
if (nodeWeight.getType() != Type.NOT_SET) {
final Weight nodeWeight = node.getWeight();
if (nodeWeight.getType() != Type.NOT_SET) {
final Weight temp = edgeValue.newValue(nodeWeight);
if (this.greaterThen(temp, newWeight)) {
newWeight = temp;
}
}
}
return newWeight;
}
final Weight temp = edgeValue.newValue(nodeWeight);
if (this.greaterThen(temp, newWeight)) {
newWeight = temp;
}
} else {
newWeight = Weight.NOT_SET;
break;
}
}
return newWeight;
}
/**
* We do not need to iterate all edges, but edges of which the source weight
* was changed, other edges will not lead to a change of the destination
* weight. For correct updating of the destination weight, we need to
* consider all incoming edges. (The minimum of in edges is computed per
* round, not global - see
* {@link ShortestPath#updateDestinationsWithMin(HashSet, DirectedHyperEdge, Weight)}
* )
*
* @return A set of edges, that may lead to changes of weights.
*/
private HashSet<DirectedHyperEdge<T>> selectEdgesToIterate() {
final HashSet<DirectedHyperEdge<T>> edgesToIterate = new HashSet<>();
for (final DirectedHyperEdge<T> edge : this.updatedEdges) {
for (final HyperNode<T> node : edge.getDestination()) {
edgesToIterate.addAll(node.getInEdges());
}
}
return edgesToIterate;
}
/**
* We do not need to iterate all edges, but edges of which the source weight
* was changed, other edges will not lead to a change of the destination
* weight. For correct updating of the destination weight, we need to consider
* all incoming edges. (The minimum of in edges is computed per round, not
* global - see
* {@link ShortestPath#updateDestinationsWithMin(HashSet, DirectedHyperEdge, Weight)}
* )
*
* @return A set of edges, that may lead to changes of weights.
*/
private HashSet<DirectedHyperEdge<T>> selectEdgesToIterate() {
final HashSet<DirectedHyperEdge<T>> edgesToIterate = new HashSet<>();
for (final DirectedHyperEdge<T> edge : this.updatedEdges) {
for (final HyperNode<T> node : edge.getDestination()) {
edgesToIterate.addAll(node.getInEdges());
}
}
return edgesToIterate;
}
private void updateAllEdges() {
private void updateAllEdges() {
for (final DirectedHyperEdge<T> edge : this.selectEdgesToIterate()) {
final Weight maxOfSources = this.maxOfSources(edge);
for (final DirectedHyperEdge<T> edge : this.selectEdgesToIterate()) {
final Weight maxOfSources = this.maxOfSources(edge);
if (maxOfSources.getType() != Type.NOT_SET) {
this.updateDestinationsWithMin(edge, maxOfSources);
}
}
if (maxOfSources.getType() != Type.NOT_SET) {
this.updateDestinationsWithMin(edge, maxOfSources);
}
}
this.writeChanges();
}
this.writeChanges();
}
/**
* Updates Nodes with the minimum of all incoming edges. The minimum is
* computed over the minimum of all edges that were processed in this round
* ({@link ShortestPath#selectEdgesToIterate()}).
*
* This is necessary for the feature described in
* {@link ShortestPath#maxOfSources(DirectedHyperEdge)} to work properly:
* The result of different rounds is not always monotonous, p.a.:
*
* <pre>
* (n1, n2)->(n3)
* Round 1: n1 = unset, n2 = -3 -> n3 = max(unset,-3) = -3
* Round 2: n1 = 1, n2 = -3 -> n3 = max(1,-3) = 1
* </pre>
*
* Would we compute the minimum of n3 over all rounds, it would be -3, but 1
* is correct.
*
* Note: that every weight is smaller than {@link Weight#NOT_SET} for min
* computation. This allows distances to propagate, even if not all nodes
* are connected to the source of the shortest path computation. Otherwise
* (source)->(sink), (other)->(sink), would not have a path from source to
* sink.
*
* @param edge
* @param newWeight
*/
private void updateDestinationsWithMin(final DirectedHyperEdge<T> edge,
Weight newWeight) {
for (final HyperNode<T> node : edge.getDestination()) {
if (this.lessThen(newWeight, node.getNewWeight())) {
node.setNewWeight(newWeight);
}
}
}
/**
* Updates Nodes with the minimum of all incoming edges. The minimum is
* computed over the minimum of all edges that were processed in this round (
* {@link ShortestPath#selectEdgesToIterate()}).
*
* This is necessary for the feature described in
* {@link ShortestPath#maxOfSources(DirectedHyperEdge)} to work properly: The
* result of different rounds is not always monotonous, p.a.:
*
* <pre>
* (n1, n2)->(n3)
* Round 1: n1 = unset, n2 = -3 -> n3 = max(unset,-3) = -3
* Round 2: n1 = 1, n2 = -3 -> n3 = max(1,-3) = 1
* </pre>
*
* Would we compute the minimum of n3 over all rounds, it would be -3, but 1
* is correct.
*
* Note: that every weight is smaller than {@link Weight#NOT_SET} for min
* computation. This allows distances to propagate, even if not all nodes are
* connected to the source of the shortest path computation. Otherwise
* (source)->(sink), (other)->(sink), would not have a path from source to
* sink.
*
* @param edge
* @param newWeight
*/
private void updateDestinationsWithMin(final DirectedHyperEdge<T> edge, Weight newWeight) {
if (!newWeight.equals(Weight.NOT_SET)) {
for (final HyperNode<T> node : edge.getDestination()) {
if (this.lessThen(newWeight, node.getNewWeight())) {
node.setNewWeight(newWeight);
}
}
}
}
/**
* This method is necessary, as the min is updated per round. (See
* {@link ShortestPath#updateDestinationsWithMin(DirectedHyperEdge, Weight)}
* )
*/
private void writeChanges() {
final HashSet<DirectedHyperEdge<T>> newUpdatedEdges = new HashSet<>();
/**
* This method is necessary, as the min is updated per round. (See
* {@link ShortestPath#updateDestinationsWithMin(DirectedHyperEdge, Weight)} )
*/
private void writeChanges() {
final HashSet<DirectedHyperEdge<T>> newUpdatedEdges = new HashSet<>();
for (final HyperNode<T> node : this.graph.getNodes().values()) {
final Weight oldWeight = node.getWeight();
final Weight newWeight = node.getNewWeight();
if (!newWeight.equals(Weight.NOT_SET)
&& !oldWeight.equals(newWeight)) {
// node weight has changed, so out edges have to be updated next
// round:
newUpdatedEdges.addAll(node.getOutEdges());
for (final HyperNode<T> node : this.graph.getNodes().values()) {
final Weight oldWeight = node.getWeight();
final Weight newWeight = node.getNewWeight();
if (!newWeight.equals(Weight.NOT_SET) && !oldWeight.equals(newWeight)) {
// node weight has changed, so out edges have to be updated next
// round:
newUpdatedEdges.addAll(node.getOutEdges());
if (this.setUnlimitedOnChange) {
node.setWeight(Weight.UNLIMITED);
} else {
node.setWeight(node.getNewWeight());
}
}
node.setNewWeight(Weight.NOT_SET);
}
if (this.setUnlimitedOnChange) {
node.setWeight(Weight.UNLIMITED);
} else {
node.setWeight(node.getNewWeight());
}
}
node.setNewWeight(Weight.NOT_SET);
}
this.updatedEdges = newUpdatedEdges;
}
this.updatedEdges = newUpdatedEdges;
}
}