From 585d5c09588031409b1267053a1517d26022c4dd Mon Sep 17 00:00:00 2001 From: markr Date: Fri, 2 Feb 2024 18:02:07 +1100 Subject: [PATCH] #100, #101, #99 working on path based implementation. Removed empiric approach, improved testing for stochastic results, added option to use only non-overlapping links for derivative calculation --- .../ltm/sltm/StaticLtmDirectedPath.java | 13 +- .../ltm/sltm/StaticLtmDirectedPathImpl.java | 42 +---- .../ltm/sltm/StaticLtmPathStrategy.java | 172 +++++------------- .../sltm/consumer/PathFlowUpdateConsumer.java | 2 +- .../consumer/PathLinkFlowUpdateConsumer.java | 12 +- .../sltm/loading/StaticLtmNetworkLoading.java | 2 +- .../sLtmAssignmentMultiDestinationTest.java | 77 +++++++- 7 files changed, 129 insertions(+), 191 deletions(-) diff --git a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPath.java b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPath.java index e04a284f..617e8da6 100644 --- a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPath.java +++ b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPath.java @@ -8,18 +8,9 @@ */ public interface StaticLtmDirectedPath extends ManagedDirectedPath { - public abstract void updatePathChoiceProbability(double probability); + public abstract void setPathChoiceProbability(double probability); - /** update previous value to current one, without changing it */ - public abstract void setPrevPathChoiceProbabilityToCurr(); - - public abstract double getCurrentPathChoiceProbability(); - - public abstract double getPreviousPathChoiceProbability(); - - public abstract void setPathCost(double pathCost); - - public abstract double getPathCost(); + public abstract double getPathChoiceProbability(); /** * Hashcode based solely on the directed link segments of this path diff --git a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPathImpl.java b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPathImpl.java index 1a748c79..f56f72e4 100644 --- a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPathImpl.java +++ b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmDirectedPathImpl.java @@ -25,16 +25,6 @@ public class StaticLtmDirectedPathImpl implements StaticLtmDirectedPath { */ private double currentPathChoiceProbability; - /** - * Previous path choice probability - */ - private double previousPathChoiceProbability; - - /** - * Current path cost - */ - private double pathCost; - /** the path we're decorating */ private ManagedDirectedPath wrappedPath; @@ -45,8 +35,6 @@ public class StaticLtmDirectedPathImpl implements StaticLtmDirectedPath { */ public StaticLtmDirectedPathImpl(ManagedDirectedPath pathToWrap) { this.currentPathChoiceProbability = 0; - this.previousPathChoiceProbability = 0; - this.pathCost = 0; this.linkSegmentsOnlyHashCode = java.util.Arrays.hashCode( IterableUtils.asStream(IterableUtils.toIterable(pathToWrap.iterator())).mapToLong( e -> e.getId()).toArray()); this.wrappedPath = pathToWrap; @@ -60,51 +48,25 @@ public StaticLtmDirectedPathImpl(ManagedDirectedPath pathToWrap) { */ public StaticLtmDirectedPathImpl(StaticLtmDirectedPathImpl other, boolean deepCopy) { this.currentPathChoiceProbability = other.currentPathChoiceProbability; - this.previousPathChoiceProbability = other.previousPathChoiceProbability; - this.pathCost = other.pathCost; this.linkSegmentsOnlyHashCode = other.linkSegmentsOnlyHashCode; this.wrappedPath = deepCopy ? other.wrappedPath.deepClone() : other.wrappedPath.shallowClone(); } @Override - public void updatePathChoiceProbability(double probability){ - setPrevPathChoiceProbabilityToCurr(); + public void setPathChoiceProbability(double probability){ this.currentPathChoiceProbability = probability; } - /** - * {@inheritDoc} - */ @Override - public void setPrevPathChoiceProbabilityToCurr() { - this.previousPathChoiceProbability = currentPathChoiceProbability; - } - - @Override - public double getCurrentPathChoiceProbability(){ + public double getPathChoiceProbability(){ return this.currentPathChoiceProbability; } - @Override - public double getPreviousPathChoiceProbability(){ - return this.previousPathChoiceProbability; - } - - @Override - public void setPathCost(double pathCost) { - this.pathCost = pathCost; - } - @Override - public double getPathCost() { - return pathCost; - } - @Override public int getLinkSegmentsOnlyHashCode(){ return this.linkSegmentsOnlyHashCode; } - @Override public long getId() { return wrappedPath.getId(); diff --git a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmPathStrategy.java b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmPathStrategy.java index f11ddc72..455cb627 100644 --- a/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmPathStrategy.java +++ b/src/main/java/org/goplanit/assignment/ltm/sltm/StaticLtmPathStrategy.java @@ -20,7 +20,6 @@ import org.goplanit.utils.arrays.ArrayUtils; import org.goplanit.utils.exceptions.PlanItRunTimeException; import org.goplanit.utils.id.IdGroupingToken; -import org.goplanit.utils.math.Precision; import org.goplanit.utils.misc.LoggingUtils; import org.goplanit.utils.mode.Mode; import org.goplanit.utils.path.ManagedDirectedPathFactory; @@ -73,9 +72,9 @@ protected void updateGap( double highestCostPerceivedCost, double odDemand) { gapFunction.increaseConvexityBound( - lowestCostPerceivedCost * (odDemand * (highestCostPath.getCurrentPathChoiceProbability() + lowestCostPath.getCurrentPathChoiceProbability()))); - gapFunction.increaseMeasuredCost(odDemand * lowestCostPath.getCurrentPathChoiceProbability() * lowestCostPerceivedCost); - gapFunction.increaseMeasuredCost(odDemand * highestCostPath.getCurrentPathChoiceProbability() * highestCostPerceivedCost); + lowestCostPerceivedCost * (odDemand * (highestCostPath.getPathChoiceProbability() + lowestCostPath.getPathChoiceProbability()))); + gapFunction.increaseMeasuredCost(odDemand * lowestCostPath.getPathChoiceProbability() * lowestCostPerceivedCost); + gapFunction.increaseMeasuredCost(odDemand * highestCostPath.getPathChoiceProbability() * highestCostPerceivedCost); } /** @@ -111,7 +110,6 @@ private OdPaths createOdPaths(final double[] currentSegme continue; } var sLtmPath = new StaticLtmDirectedPathImpl(path); - sLtmPath.setPathCost(oneToAllResult.getCostOf(destinationVertex)); newOdShortestPaths.setValue(origin, destination, sLtmPath); } } @@ -174,7 +172,7 @@ public void createInitialSolution(double[] initialLinkSegmentCosts, int iteratio (o, d, demand) -> { var odMultiPathList = new ArrayList(INITIAL_PER_OD_PATH_CAPACITY); var initialOdPath = newOdPaths.getValue(o, d); - initialOdPath.updatePathChoiceProbability(1); // set current probability to 100% + initialOdPath.setPathChoiceProbability(1); // set current probability to 100% odMultiPathList.add(initialOdPath); // add to path set odMultiPaths.setValue(o, d, odMultiPathList); }); @@ -221,12 +219,11 @@ public boolean performIteration(final Mode theMode, final double[] prevCosts, fi if (odPaths.stream().noneMatch(existingPath -> existingPath.getLinkSegmentsOnlyHashCode() == newOdPath.getLinkSegmentsOnlyHashCode())) { // new path, add to set odPaths.add(newOdPath); - newOdPath.setPathCost(PathUtils.computeEdgeSegmentAdditiveValues(newOdPath, prevCosts)); // so it is in line with other paths pre costs newPathAdded = true; } double[] currAbsolutePathCosts = PathUtils.computeEdgeSegmentAdditiveValues(odPaths, costsToUpdate); - double[] currCostRelatedPathProbabilities = odPaths.stream().map( p -> p.getCurrentPathChoiceProbability()).mapToDouble(v -> v).toArray(); + double[] currCostRelatedPathProbabilities = odPaths.stream().map( p -> p.getPathChoiceProbability()).mapToDouble(v -> v).toArray(); double[] currPerceivedPathCosts = stochasticPathChoice.computePerceivedPathCosts(currAbsolutePathCosts, currCostRelatedPathProbabilities, demand); //1. get i-1 iteration perceived cost for LOW and HIGH cost paths @@ -238,137 +235,52 @@ public boolean performIteration(final Mode theMode, final double[] prevCosts, fi double lowCostPathCurrPerceivedCost = currPerceivedPathCosts[lowCostPathIndex]; double highCostPathCurrPerceivedCost = currPerceivedPathCosts[highCostPathIndex]; - boolean doOld = false; final var choiceModel = stochasticPathChoice.getChoiceModel(); - Double highCostPathDenominator = null; - Double lowCostPathDenominator = null; - - // new based on analytical derivatives on combined link and path basis - if(!doOld){ - // low and high cost path based sum of dAbsoluteCostdFlow (so not perceived derivates yet, just the absolute cost component of it) - double lowCostPathDAbsoluteCostDFlow = PathUtils.computeEdgeSegmentAdditiveValues(lowCostPath, dCostDFlow); - double highCostPathDAbsoluteCostDFlow = PathUtils.computeEdgeSegmentAdditiveValues(highCostPath, dCostDFlow); // high cost path based sum of dCostdFlow - - // high cost path based dPerceivedCost/dFlow this required the derivative of the perceived cost related to the applied path choice model - highCostPathDenominator = choiceModel.computeDPerceivedCostDFlow( - highCostPathDAbsoluteCostDFlow, currAbsolutePathCosts[highCostPathIndex], highCostPath.getCurrentPathChoiceProbability() * demand, true); - // low cost path based dPerceivedCost/dFlow this required the derivative of the perceived cost related to the applied path choice model - lowCostPathDenominator = choiceModel.computeDPerceivedCostDFlow( - lowCostPathDAbsoluteCostDFlow, currAbsolutePathCosts[lowCostPathIndex], lowCostPath.getCurrentPathChoiceProbability() * demand, true); - - //7. determine newton step to determine flows/probabilities for i+1 - double newtonStepDenominator = highCostPathDenominator + lowCostPathDenominator; - // cost_high - step * dCost_high/d_Flow_high = cost_low - step * dCost_low/d_Flow_low - // rewrite towards step: step = (cost_high - cost_low)/((dCost_high/d_Flow_high)+(dCost_low/d_Flow_low)) - double newtonStep = - (highCostPathCurrPerceivedCost - lowCostPathCurrPerceivedCost) / newtonStepDenominator; - - // todo apply smoothing instead of multiplying by 0.5 (revert to always being iteration based for now I would think + implement fixed smoothing step option to use) - double newLowCostPathProbability = Math.min(1, ((lowCostPath.getCurrentPathChoiceProbability() * demand) + 0.66 * newtonStep)/demand); - double newHighCostPathProbability = Math.max(0, ((highCostPath.getCurrentPathChoiceProbability() * demand) - 0.66 * newtonStep)/demand); - - //7. prep for i + 1 iteration - { - // update stored path costs to new costs, so they are available for the next iteration as prev costs when needed - for(int index = 0; index < odPaths.size(); ++ index){ - odPaths.get(index).setPathCost(currAbsolutePathCosts[index]); - } - - // update probabilities applied, so they are available for the next iteration - lowCostPath.updatePathChoiceProbability(newLowCostPathProbability); - highCostPath.updatePathChoiceProbability(newHighCostPathProbability); - } - - } - // OLD BASED ON EMPIRIC DERIVATIVES - if(doOld) { - - double[] prevPerceivedPathCosts = odPaths.stream().map(p -> stochasticPathChoice.computePerceivedPathCost(p.getPathCost(), p.getCurrentPathChoiceProbability(), demand)).mapToDouble(v -> v).toArray(); - double[] prevCostRelatedPathProbabilities = odPaths.stream().map(p -> p.getPreviousPathChoiceProbability()).mapToDouble(v -> v).toArray(); - double lowCostPathPrevPerceivedCost = prevPerceivedPathCosts[lowCostPathIndex]; - double highCostPathPrevPerceivedCost = prevPerceivedPathCosts[highCostPathIndex]; - - //2. get i-1 iteration path flows used for i-1 LOW and HIGH cost paths - double lowCostPathPrevProbability = prevCostRelatedPathProbabilities[lowCostPathIndex]; - double highCostPathPrevProbability = prevCostRelatedPathProbabilities[highCostPathIndex]; - - //3. get i path flows used to get i iteration perceived costs - double lowCostPathCurrProbability = currCostRelatedPathProbabilities[lowCostPathIndex]; - double highCostPathCurrProbability = currCostRelatedPathProbabilities[highCostPathIndex]; - - //4. determine dCost and dFlow (dProbability) - double highCostPathDCost = highCostPathCurrPerceivedCost - highCostPathPrevPerceivedCost; - double highCostPathDProbability = highCostPathCurrProbability - highCostPathPrevProbability; - - highCostPathDenominator = Math.abs(0.5 * highCostPathDCost); // in case of no derivative available - if (Math.abs(highCostPathDProbability) > Precision.EPSILON_9) { - highCostPathDenominator = highCostPathDCost / highCostPathDProbability; - if (highCostPathDenominator < 0 && (highCostPathDCost > 0 || highCostPathDProbability > 0)) { - // inconsistency in derivative, likely because of external factors and pragmatic obtaining of this value instead of computing it link by link - // however, we can infer that when probability went down, cost cannot go up, so we set it to 0.0 instead, alternatively, when probability went - // up cost cannot go down, so we set it to zero as well - highCostPathDenominator = 0.0; - } + // low and high cost path based sum of dAbsoluteCostdFlow (so not perceived derivatives yet, just the absolute cost component of it) + double lowCostPathDAbsoluteCostDFlow = PathUtils.computeEdgeSegmentAdditiveValues(lowCostPath, dCostDFlow); + double highCostPathDAbsoluteCostDFlow = PathUtils.computeEdgeSegmentAdditiveValues(highCostPath, dCostDFlow); // high cost path based sum of dCostdFlow + + //todo: make configurable as this is a costly exercise (or if implemented more efficiently will cost more memory. Also unlikely to have an impact + // in many cases as bottlenecks are less likely to be overlapping. + boolean onlyConsiderNonOverlappingLinks = true; + if(onlyConsiderNonOverlappingLinks){ + int[] overlappingIndices = PathUtils.getOverlappingPathLinkIndices(lowCostPath, highCostPath); + for(var overlappingLinkIndex : overlappingIndices){ + lowCostPathDAbsoluteCostDFlow -= dCostDFlow[overlappingLinkIndex]; + highCostPathDAbsoluteCostDFlow -= dCostDFlow[overlappingLinkIndex]; } + } - double lowCostPathDCost = lowCostPathCurrPerceivedCost - lowCostPathPrevPerceivedCost; - double lowCostPathDProbability = lowCostPathCurrProbability - lowCostPathPrevProbability; - // in case of no prior change (for example no queue so derivative of zero, or new path), we can't determine the derivative, - // instead we take half of the low dcost as an approximation - //TODO: replace this with with a softmax with scale parameter so we have a way of determining the agressiveness - // boltzmann softmax --> exp(scale*X)/SUM_i(exp(scale*X_i)) --> result will be between min and max value of options chosen with high scaling factor resulting - // in a value closer to the maximum of all options...so high scale means small steps because gradient is steeper than in reality whereas low scale means closer - // to minimum gradient of the options, i.e., no impact of changing step so more aggressive step - lowCostPathDenominator = Math.abs(0.5 * lowCostPathDCost); // in case of no derivative available - if (Math.abs(lowCostPathDProbability) > Precision.EPSILON_9) { - lowCostPathDenominator = lowCostPathDCost / lowCostPathDProbability; - if (lowCostPathDenominator < 0 && (lowCostPathDCost > 0 || lowCostPathDProbability > 0)) { - // inconsistency in derivative, likely because of external factors and pragmatic obtaining of this value instead of computing it link by link - // however, we can infer that when probability went down, cost cannot go up, so we set it to 0.0 instead, alternatively, when probability went - // up cost cannot go down, so we set it to zero as well - lowCostPathDenominator = 0.0; - } - } - - - //7. determine newton step to determine flows/probabilities for i+1 - double newtonStepDenominator = highCostPathDenominator + lowCostPathDenominator; - double newtonStep = 0; - if(doOld && newtonStepDenominator < Precision.EPSILON_9){ - // in case no change is observed in probabilities for both, we assign half to the preferred path - newtonStep = Math.abs(0.5 * highCostPathDProbability); - }else { - // cost_high - step * dCost_high/d_Flow_high = cost_low - step * dCost_low/d_Flow_low - // rewrite towards step: step = (cost_high - cost_low)/((dCost_high/d_Flow_high)+(dCost_low/d_Flow_low)) - newtonStep = - (highCostPathCurrPerceivedCost - lowCostPathCurrPerceivedCost) - / - newtonStepDenominator; - } - - // todo apply smoothing instead of multiplying by 0.5 (revert to always being iteration based for now I would think + implement fixed smoothing step option to use) - double newLowCostPathProbability = Math.min(1, lowCostPathCurrProbability + 0.5 * newtonStep); - double newHighCostPathProbability = Math.max(0, highCostPathCurrProbability - 0.5 * newtonStep); - - //7. prep for i + 1 iteration - { - // update stored path costs to new costs, so they are available for the next iteration as prev costs when needed - for(int index = 0; index < odPaths.size(); ++ index){ - odPaths.get(index).setPathCost(currAbsolutePathCosts[index]); - } - - // update probabilities applied, so they are available for the next iteration - lowCostPath.updatePathChoiceProbability(newLowCostPathProbability); - highCostPath.updatePathChoiceProbability(newHighCostPathProbability); - } + // high cost path based dPerceivedCost/dFlow this required the derivative of the perceived cost related to the applied path choice model + double highCostPathDenominator = choiceModel.computeDPerceivedCostDFlow( + highCostPathDAbsoluteCostDFlow, currAbsolutePathCosts[highCostPathIndex], highCostPath.getPathChoiceProbability() * demand, true); + // low cost path based dPerceivedCost/dFlow this required the derivative of the perceived cost related to the applied path choice model + double lowCostPathDenominator = choiceModel.computeDPerceivedCostDFlow( + lowCostPathDAbsoluteCostDFlow, currAbsolutePathCosts[lowCostPathIndex], lowCostPath.getPathChoiceProbability() * demand, true); + + //7. determine newton step to determine flows/probabilities for i+1 + double newtonStepDenominator = highCostPathDenominator + lowCostPathDenominator; + // cost_high - step * dCost_high/d_Flow_high = cost_low - step * dCost_low/d_Flow_low + // rewrite towards step: step = (cost_high - cost_low)/((dCost_high/d_Flow_high)+(dCost_low/d_Flow_low)) + double newtonStep = + (highCostPathCurrPerceivedCost - lowCostPathCurrPerceivedCost) / newtonStepDenominator; + + // todo apply smoothing instead of multiplying by 0.25 (revert to always being iteration based for now I would think + implement fixed smoothing step option to use) + double newLowCostPathProbability = Math.min(1, ((lowCostPath.getPathChoiceProbability() * demand) + 0.25 * newtonStep)/demand); + double newHighCostPathProbability = Math.max(0, ((highCostPath.getPathChoiceProbability() * demand) - 0.25 * newtonStep)/demand); + + //7. prep for i + 1 iteration + { + // update probabilities applied, so they are available for the next iteration + lowCostPath.setPathChoiceProbability(newLowCostPathProbability); + highCostPath.setPathChoiceProbability(newHighCostPathProbability); } //6. update gap // todo should be based on all paths, not just the ones changed updateGap(gapFunction, lowCostPath, lowCostPathCurrPerceivedCost, highCostPath, highCostPathCurrPerceivedCost, demand); - - }); + }); LOGGER.info("Created new paths and updated path choice for path-based sLTM"); } diff --git a/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathFlowUpdateConsumer.java b/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathFlowUpdateConsumer.java index cf983277..68b6170c 100644 --- a/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathFlowUpdateConsumer.java +++ b/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathFlowUpdateConsumer.java @@ -68,7 +68,7 @@ public void accept(OdZone origin, OdZone destination, Double odDemand) { /* path */ var odPaths = odMultiPaths.getValue(origin, destination); for (StaticLtmDirectedPath odPath : odPaths) { - double acceptedPathFlowRate = odDemand * odPath.getCurrentPathChoiceProbability(); + double acceptedPathFlowRate = odDemand * odPath.getPathChoiceProbability(); if (odPath == null || odPath.isEmpty()) { LOGGER.warning(String.format("IGNORE: encountered empty path %s", odPath == null ? "" : odPath.getXmlId())); return; diff --git a/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathLinkFlowUpdateConsumer.java b/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathLinkFlowUpdateConsumer.java index 2e25aa2a..44569b14 100644 --- a/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathLinkFlowUpdateConsumer.java +++ b/src/main/java/org/goplanit/assignment/ltm/sltm/consumer/PathLinkFlowUpdateConsumer.java @@ -21,7 +21,7 @@ public class PathLinkFlowUpdateConsumer extends PathFlowUpdateConsumer diff --git a/src/test/java/org/goplanit/test/sltm/sLtmAssignmentMultiDestinationTest.java b/src/test/java/org/goplanit/test/sltm/sLtmAssignmentMultiDestinationTest.java index 008633e2..e3f3da33 100644 --- a/src/test/java/org/goplanit/test/sltm/sLtmAssignmentMultiDestinationTest.java +++ b/src/test/java/org/goplanit/test/sltm/sLtmAssignmentMultiDestinationTest.java @@ -75,7 +75,7 @@ private Demands createDemands() { return demands; } - private void testOutputs(StaticLtm sLTM) { + private void testDeterministicOutputs(StaticLtm sLTM) { double outflow0 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("0").getLinkSegmentAb()); double outflow1 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("1").getLinkSegmentAb()); double outflow2 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("2").getLinkSegmentAb()); @@ -96,7 +96,7 @@ private void testOutputs(StaticLtm sLTM) { assertTrue(Precision.smallerEqual(outflow8, 4000)); assertEquals(outflow0, 8000, Precision.EPSILON_3); - assertEquals(outflow1, 4523, 1); + assertEquals(outflow1, 4522.6, 1); assertEquals(outflow2, 1500.0, Precision.EPSILON_3); assertEquals(outflow3, outflow2, Precision.EPSILON_3); //assertEquals(outflow4, 3750, 1); do not test due to non-uniqueness being allowed under no congestion an triangular FD @@ -140,6 +140,71 @@ private void testOutputs(StaticLtm sLTM) { assertEquals(outflow12, inflow13 + inflow14, Precision.EPSILON_6); } + private void testStochasticOutputs(StaticLtm sLTM) { + double outflow0 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("0").getLinkSegmentAb()); + double outflow1 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("1").getLinkSegmentAb()); + double outflow2 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("2").getLinkSegmentAb()); + double outflow3 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("3").getLinkSegmentAb()); + double outflow4 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("4").getLinkSegmentAb()); + double outflow5 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("5").getLinkSegmentAb()); + double outflow6 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("6").getLinkSegmentAb()); + double outflow7 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("7").getLinkSegmentAb()); + double outflow8 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("8").getLinkSegmentAb()); + double outflow9 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("9").getLinkSegmentAb()); + double outflow10 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("10").getLinkSegmentAb()); + double outflow11 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("11").getLinkSegmentAb()); + double outflow12 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("12").getLinkSegmentAb()); + double outflow13 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("13").getLinkSegmentAb()); + double outflow14 = sLTM.getLinkSegmentOutflowPcuHour(networkLayer.getLinks().getByXmlId("14").getLinkSegmentAb()); + + assertTrue(Precision.smallerEqual(outflow4, 4000)); + assertTrue(Precision.smallerEqual(outflow8, 4000)); + + assertEquals(outflow0, 8000, Precision.EPSILON_3); + assertEquals(outflow1, 4520.3, 1); + assertEquals(outflow2, 1500.0, Precision.EPSILON_3); + assertEquals(outflow3, outflow2, Precision.EPSILON_3); + assertEquals(outflow4, 3749.7, 1); + assertEquals(outflow5, 3290.3, 1); + assertEquals(outflow6, 1500.0, Precision.EPSILON_3); + assertEquals(outflow7, outflow6, Precision.EPSILON_3); + assertEquals(outflow8, 3750.3, 1); + assertEquals(outflow9, 3000.0, Precision.EPSILON_3); + assertEquals(outflow10, 1500.0, Precision.EPSILON_3); + assertEquals(outflow11, outflow10, Precision.EPSILON_3); + assertEquals(outflow12, 4500.0, Precision.EPSILON_3); + assertEquals(outflow13, 2249.7, 1); + assertEquals(outflow14, 2249.7, 1); + + double inflow1 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("1").getLinkSegmentAb()); + double inflow2 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("2").getLinkSegmentAb()); + double inflow3 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("3").getLinkSegmentAb()); + double inflow4 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("4").getLinkSegmentAb()); + double inflow5 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("5").getLinkSegmentAb()); + double inflow6 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("6").getLinkSegmentAb()); + double inflow7 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("7").getLinkSegmentAb()); + double inflow8 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("8").getLinkSegmentAb()); + double inflow9 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("9").getLinkSegmentAb()); + double inflow10 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("10").getLinkSegmentAb()); + double inflow11 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("11").getLinkSegmentAb()); + double inflow12 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("12").getLinkSegmentAb()); + double inflow13 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("13").getLinkSegmentAb()); + double inflow14 = sLTM.getLinkSegmentInflowPcuHour(networkLayer.getLinks().getByXmlId("14").getLinkSegmentAb()); + + assertEquals(outflow0, inflow1 + inflow5, Precision.EPSILON_6); + assertEquals(outflow1, inflow2 + inflow9, Precision.EPSILON_6); + assertEquals(outflow2, inflow3, Precision.EPSILON_6); + assertEquals(outflow3 + outflow13, inflow4, Precision.EPSILON_6); + assertEquals(outflow4, inflow4, Precision.EPSILON_6); + assertEquals(outflow5, inflow10 + inflow6, Precision.EPSILON_6); + assertEquals(outflow6, inflow7, Precision.EPSILON_6); + assertEquals(outflow7 + outflow14, inflow8, Precision.EPSILON_6); + assertEquals(outflow8, inflow8, Precision.EPSILON_6); + assertEquals(outflow9 + outflow11, inflow12, Precision.EPSILON_6); + assertEquals(outflow10, inflow11, Precision.EPSILON_6); + assertEquals(outflow12, inflow13 + inflow14, Precision.EPSILON_6); + } + /** * {@inheritDoc} */ @@ -342,7 +407,7 @@ public void sLtmPointQueuePathBasedAssignmentTest() { { /* Weibit for path choice */ var choiceModel = suePathChoice.createAndRegisterChoiceModel(ChoiceModel.WEIBIT); - choiceModel.setScalingFactor(7); + choiceModel.setScalingFactor(1); // we go for rather muddled perceived cost to differentiate from deterministic result // by not setting a fixed od path set (suePathChoice.setFixedOdPathMatrix(...)), it is assumed we want a dynamic path set } @@ -360,7 +425,7 @@ public void sLtmPointQueuePathBasedAssignmentTest() { sLTM.setActivateDetailedLogging(true); sLTM.execute(); - testOutputs(sLTM); + testStochasticOutputs(sLTM); } catch (Exception e) { e.printStackTrace(); @@ -394,7 +459,7 @@ public void sLtmPointQueueBushOriginBasedAssignmentTest() { sLTM.setActivateDetailedLogging(true); sLTM.execute(); - testOutputs(sLTM); + testDeterministicOutputs(sLTM); } catch (Exception e) { e.printStackTrace(); @@ -428,7 +493,7 @@ public void sLtmPointQueueBushDestinationBasedAssignmentTest() { sLTM.setActivateDetailedLogging(true); sLTM.execute(); - testOutputs(sLTM); + testDeterministicOutputs(sLTM); } catch (Exception e) { e.printStackTrace();