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:
parent
381cf2d2f1
commit
dfaa44d111
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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";
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user