diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/DisappointmentAnalyzer.java b/experimental/src/main/java/org/matsim/contrib/greedo/DisappointmentAnalyzer.java
new file mode 100644
index 0000000..dc641d1
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/DisappointmentAnalyzer.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeCounts;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeIndicators;
+
+import floetteroed.utilities.DynamicData;
+import floetteroed.utilities.Tuple;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ */
+public class DisappointmentAnalyzer {
+
+ // -------------------- MEMBERS --------------------
+
+ private final GreedoConfigGroup conf;
+
+ private final Map, Double> _B;
+
+ // TODO Find more representative statistics.
+ // private Double lastNaiveIndividualAbsE = null;
+ // private Double lastEstimIndividualAbsE = null;
+
+ // -------------------- CONSTRUCTION --------------------
+
+ public DisappointmentAnalyzer(final GreedoConfigGroup conf) {
+ this.conf = conf;
+ this._B = new LinkedHashMap<>();
+ }
+
+ // -------------------- INTERNALS --------------------
+
+ private double predictVariability(final Id personId, final SpaceTimeCounts> interactions) {
+ double result = 0;
+ for (Map.Entry, Integer>, Double> entry : interactions.entriesView()) {
+ final Id> loc = entry.getKey().getA();
+ final double interaction = entry.getValue();
+ result += interaction * this._B.getOrDefault(loc, 0.0);
+ }
+ return result;
+ }
+
+ // -------------------- IMPLEMENTATION --------------------
+
+ public void update(final Map, SpaceTimeIndicators>> currentIndicators,
+ final Map, SpaceTimeIndicators>> anticipatedIndicators,
+ final Map, Double> personId2realizedUtilityChange,
+ final Map, Double> personId2anticipatedUtilityChange, final Set> replannerIds,
+ final Map, SpaceTimeCounts>> personId2interactions, final double stepSize) {
+
+// this.lastEstimIndividualAbsE = 0.0;
+// this.lastNaiveIndividualAbsE = 0.0;
+
+ final Map, Double> deltaB = new LinkedHashMap<>();
+
+ double etaNumerator = 0.0; // TODO revisit step size logic
+ // vector in eta denominator equals deltaDiagonal2 + ... other ^2 terms
+
+ for (Id personId : personId2realizedUtilityChange.keySet()) {
+
+ final SpaceTimeCounts> interactions = personId2interactions.get(personId);
+ final double variability = this.predictVariability(personId, interactions);
+
+ final double anticipatedUtilityChange;
+ if (replannerIds.contains(personId)) {
+ anticipatedUtilityChange = personId2anticipatedUtilityChange.get(personId);
+ } else {
+ anticipatedUtilityChange = 0.0;
+ }
+
+ final double naiveEn = anticipatedUtilityChange - personId2realizedUtilityChange.get(personId);
+
+ // TODO >>> Used this code before >>>
+// double en;
+// if (this.variabilityConfig.getUseAbsoluteVariability()) {
+// throw new RuntimeException("unsupported");
+// } else {
+// if (this.variabilityConfig.getTruncateImprovement()) {
+// throw new RuntimeException("unsupported");
+// } else {
+// en = this.beta * anticipatedUtilityChange - (this.variabilityConfig.getEstimateStationaryB() ? 0.0
+// : personId2realizedUtilityChange.get(personId)) - variability;
+// }
+// }
+ // TODO <<< Used this code before <<<
+ final double en = (anticipatedUtilityChange - variability) - personId2realizedUtilityChange.get(personId);
+
+// this.lastEstimIndividualAbsE += Math.abs(en);
+ etaNumerator += en * en;
+
+ for (Map.Entry, Integer>, Double> entry : interactions.entriesView()) {
+ final Id> loc = entry.getKey().getA();
+ double interaction = entry.getValue();
+ deltaB.put(loc, deltaB.getOrDefault(loc, 0.0) + en * interaction);
+ }
+
+ }
+
+ double etaDenominator = 0.0; // TODO stream
+ for (double val : deltaB.values()) {
+ etaDenominator += val * val;
+ }
+
+ if (etaDenominator >= 1e-8) {
+ final double eta = stepSize * etaNumerator / etaDenominator;
+ for (Map.Entry, Double> entry : deltaB.entrySet()) {
+ final Id> loc = entry.getKey();
+ double newBValue = this._B.getOrDefault(loc, 0.0) + eta * entry.getValue();
+ if (this.conf.getNonnegativeB()) {
+ newBValue = Math.max(0.0, newBValue);
+ }
+ this._B.put(loc, newBValue);
+ }
+ }
+ }
+
+// public Double getLastNaiveAbsE() {
+// return this.lastNaiveIndividualAbsE;
+// }
+
+// public Double getLastEstimAbsE() {
+// return this.lastEstimIndividualAbsE;
+// }
+
+ // TODO Stick to a map; take out dynamics.
+ public DynamicData> getB() {
+ final DynamicData> result = new DynamicData<>(this.conf.newTimeDiscretization());
+ for (Map.Entry, Double> entry : this._B.entrySet()) {
+ for (int timeBin = 0; timeBin < result.getBinCnt(); timeBin++) {
+ result.put(entry.getKey(), timeBin, entry.getValue());
+ }
+ }
+ return result;
+ }
+}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/GreedoConfigGroup.java b/experimental/src/main/java/org/matsim/contrib/greedo/GreedoConfigGroup.java
index f42b454..227ae27 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/GreedoConfigGroup.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/GreedoConfigGroup.java
@@ -133,6 +133,50 @@ public double getAgeWeight(final double age) {
return Math.pow(1.0 / (1.0 + age), this.getAgeWeightExponent());
}
+ // ---------- VARIABILITY ANALYSIS minPhysLinkSize_veh ----------
+
+ // TODO This could be used even in the slot definition.
+
+ private double minPhysLinkSize_veh = 0.0;
+
+ @StringGetter("minPhysLinkSize_veh")
+ public double getMinPhysLinkSize_veh() {
+ return this.minPhysLinkSize_veh;
+ }
+
+ @StringSetter("minPhysLinkSize_veh")
+ public void setMinPhysLinkSize_veh(final double minPhysLinkSize_veh) {
+ this.minPhysLinkSize_veh = minPhysLinkSize_veh;
+ }
+
+ // -------------------- VARIABILITY ANALYSIS: nonnegativeB --------------------
+
+ private boolean nonnegativeB = false;
+
+ @StringGetter("nonnegativeB")
+ public boolean getNonnegativeB() {
+ return this.nonnegativeB;
+ }
+
+ @StringSetter("nonnegativeB")
+ public void setNonnegativeB(final boolean nonnegativeB) {
+ this.nonnegativeB = nonnegativeB;
+ }
+
+ // --------------- VARIABILITY ANALYSIS: selfInteraction ---------------
+
+ private boolean selfInteraction = false;
+
+ @StringGetter("selfInteraction")
+ public boolean getSelfInteraction() {
+ return this.selfInteraction;
+ }
+
+ @StringSetter("selfInteraction")
+ public void setSelfInteraction(final boolean selfInteraction) {
+ this.selfInteraction = selfInteraction;
+ }
+
// --------------- adaptiveMSADenominatorIncreaseIfSuccess ---------------
private double adaptiveMSADenominatorIncreaseIfSuccess = 0.1;
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/LogDataWrapper.java b/experimental/src/main/java/org/matsim/contrib/greedo/LogDataWrapper.java
index 5880d0d..cea0bab 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/LogDataWrapper.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/LogDataWrapper.java
@@ -35,13 +35,13 @@ public class LogDataWrapper {
private final Utilities.SummaryStatistics utilitySummaryStatistics;
- private final ReplannerIdentifier.SummaryStatistics replanningSummaryStatistics;
+ private final ReplannerIdentifierTII.SummaryStatistics replanningSummaryStatistics;
private final int iteration;
public LogDataWrapper(final GreedoConfigGroup greedoConfig,
final Utilities.SummaryStatistics utilitySummaryStatistics,
- final ReplannerIdentifier.SummaryStatistics replanningSummaryStatistics, final int iteration) {
+ final ReplannerIdentifierTII.SummaryStatistics replanningSummaryStatistics, final int iteration) {
this.greedoConfig = greedoConfig;
this.utilitySummaryStatistics = utilitySummaryStatistics;
this.replanningSummaryStatistics = replanningSummaryStatistics;
@@ -73,7 +73,7 @@ public int getIteration() {
return this.iteration;
}
- public ReplannerIdentifier.SummaryStatistics getReplanningSummaryStatistics() {
+ public ReplannerIdentifierTII.SummaryStatistics getReplanningSummaryStatistics() {
return this.replanningSummaryStatistics;
}
}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifier.java b/experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifierTII.java
similarity index 64%
rename from experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifier.java
rename to experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifierTII.java
index bce7f4b..3386f14 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifier.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/ReplannerIdentifierTII.java
@@ -19,8 +19,10 @@
*/
package org.matsim.contrib.greedo;
+import static org.matsim.contrib.greedo.datastructures.SlotUsageUtilities.addIndicatorsToTotalsTreatingNullAsZero;
+import static org.matsim.contrib.greedo.datastructures.SlotUsageUtilities.newTotals;
+
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -31,19 +33,20 @@
import org.apache.log4j.Logger;
import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Person;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeCounts;
import org.matsim.contrib.greedo.datastructures.SpaceTimeIndicators;
import floetteroed.utilities.DynamicData;
import floetteroed.utilities.DynamicDataUtils;
-import floetteroed.utilities.TimeDiscretization;
/**
*
* @author Gunnar Flötteröd
*
*/
-class ReplannerIdentifier {
+class ReplannerIdentifierTII {
// -------------------- CONSTANTS --------------------
@@ -55,114 +58,70 @@ class ReplannerIdentifier {
private final Map, SpaceTimeIndicators>> personId2physicalSlotUsage;
private final Map, SpaceTimeIndicators>> personId2hypothetialSlotUsage;
- private final DynamicData> currentWeightedCounts;
- private final DynamicData> upcomingWeightedCounts;
private final Map, Double> personId2hypotheticalUtilityChange;
private final Map, Double> personId2currentUtility;
- private final double sumOfWeightedCountDifferences2;
+ private final DynamicData> _B;
+
private final double lambdaBar;
- private final double beta;
+
+ private DynamicData> presenceResiduals;
+ private DynamicData> changeResiduals;
+
+ private Map, SpaceTimeCounts>> personId2interactions = new LinkedHashMap<>();
+ private Map, Double> personId2Dn0 = new LinkedHashMap<>();
+ private Map, Double> personId2Tn = new LinkedHashMap<>();
private SummaryStatistics lastExpectations = null;
// -------------------- CONSTRUCTION --------------------
- ReplannerIdentifier(// final Double unconstrainedBeta,
- final GreedoConfigGroup greedoConfig, final int iteration,
+ ReplannerIdentifierTII(final GreedoConfigGroup greedoConfig,
final Map, SpaceTimeIndicators>> personId2physicalSlotUsage,
final Map, SpaceTimeIndicators>> personId2hypotheticalSlotUsage,
final Map, Double> personId2hypotheticalUtilityChange,
- final Map, Double> personId2currentUtility,
- // final MovingWindowAverage realizedToTargetLambdaRatio,
- final double betaScale) {
+ final Map, Double> personId2currentUtility, final DynamicData> _B,
+ final double lambdaBar) {
+ this._B = _B;
+ this.lambdaBar = lambdaBar;
this.greedoConfig = greedoConfig;
+
this.personId2physicalSlotUsage = personId2physicalSlotUsage;
this.personId2hypothetialSlotUsage = personId2hypotheticalSlotUsage;
this.personId2hypotheticalUtilityChange = personId2hypotheticalUtilityChange;
- final double totalUtilityChange = personId2hypotheticalUtilityChange.values().stream()
- .mapToDouble(utlChange -> utlChange).sum();
this.personId2currentUtility = personId2currentUtility;
- log.info("number of entries (persons) in different maps:");
- log.info(" personId2physicalSlotUsage.size() = " + personId2physicalSlotUsage.size());
- log.info(" personId2hypothetialSlotUsage.size() = " + personId2hypotheticalSlotUsage.size());
- log.info(" personId2currentUtility.size() = " + personId2currentUtility.size());
- log.info(" personId2hypotheticalUtilityChange.size() = " + personId2hypotheticalUtilityChange.size());
-
- this.currentWeightedCounts = newWeightedCounts(this.greedoConfig.newTimeDiscretization(),
- this.personId2physicalSlotUsage.values(), true, true);
- this.upcomingWeightedCounts = newWeightedCounts(this.greedoConfig.newTimeDiscretization(),
- this.personId2hypothetialSlotUsage.values(), true, true);
- this.sumOfWeightedCountDifferences2 = DynamicDataUtils.sumOfDifferences2(this.currentWeightedCounts,
- this.upcomingWeightedCounts);
-
- log.info("sumOfWeightedCountDifference2 = " + this.sumOfWeightedCountDifferences2
- + " based on the following data:");
- log.info(" currentWeightedCounts2 = " + DynamicDataUtils.sumOfEntries2(this.currentWeightedCounts));
- log.info(" upcomingWeightedCounts2 = " + DynamicDataUtils.sumOfEntries2(this.upcomingWeightedCounts));
-
- // Logger.getLogger(this.getClass()).warn("Overriding beta!!!");
- // if ((unconstrainedBeta != null) && (unconstrainedBeta > 0.0)) {
- // this.beta = unconstrainedBeta;
- // this.lambdaBar = 0.5 * unconstrainedBeta * totalUtilityChange
- // / Math.max(sumOfWeightedCountDifferences2, 1e-8);
- // } else
- // {
- // final double ratio;
- // if (realizedToTargetLambdaRatio.getTotalCount() > 0) {
- // ratio = realizedToTargetLambdaRatio.average();
- // } else {
- // ratio = 1.0;
- // }
- this.lambdaBar = greedoConfig.getMSAReplanningRate(iteration);
- // this.beta = 2.0 * (this.lambdaBar / ratio) * sumOfWeightedCountDifferences2
- // / Math.max(totalUtilityChange, 1e-8);
- this.beta = betaScale * 2.0 * this.lambdaBar * this.sumOfWeightedCountDifferences2
- / Math.max(totalUtilityChange, 1e-8);
-
- log.info("beta = " + this.beta + " based on the following data:");
- log.info(" betaScale = " + betaScale);
- log.info(" lambdaBar = " + this.lambdaBar);
- log.info(" sumOfWeightedCountDifferences2 = " + this.sumOfWeightedCountDifferences2);
- log.info(" totalUtilityChange = " + totalUtilityChange);
-
- // }
- }
-
- // -------------------- INTERNALS --------------------
-
- private static void addIndicatorsToTotalsTreatingNullAsZero(final DynamicData weightedTotals,
- final SpaceTimeIndicators indicators, final double factor, final boolean useParticleWeight,
- final boolean useSlotWeight) {
- if (indicators != null) {
- for (int bin = 0; bin < indicators.getTimeBinCnt(); bin++) {
- for (SpaceTimeIndicators.Visit visit : indicators.getVisits(bin)) {
- weightedTotals.add(visit.spaceObject, bin, factor * visit.weight(useParticleWeight, useSlotWeight));
- }
- }
+ this.log.info("number of entries (persons) in different maps:");
+ this.log.info(" personId2physicalSlotUsage.size() = " + personId2physicalSlotUsage.size());
+ this.log.info(" personId2hypothetialSlotUsage.size() = " + personId2hypotheticalSlotUsage.size());
+ this.log.info(" personId2currentUtility.size() = " + personId2currentUtility.size());
+ this.log.info(" personId2hypotheticalUtilityChange.size() = " + personId2hypotheticalUtilityChange.size());
+
+ {
+ final DynamicData> currentWeightedCounts = newTotals(this.greedoConfig.newTimeDiscretization(),
+ this.personId2physicalSlotUsage.values(), true, true);
+ final DynamicData> upcomingWeightedCounts = newTotals(this.greedoConfig.newTimeDiscretization(),
+ this.personId2hypothetialSlotUsage.values(), true, true);
+ this.changeResiduals = DynamicDataUtils.newWeightedSum(upcomingWeightedCounts, +lambdaBar,
+ currentWeightedCounts, -lambdaBar);
}
- }
- private static DynamicData newWeightedCounts(final TimeDiscretization timeDiscr,
- final Collection> allIndicators, final boolean useParticleWeight,
- final boolean useSlotWeight) {
- final DynamicData weightedCounts = new DynamicData(timeDiscr);
- for (SpaceTimeIndicators indicators : allIndicators) {
- addIndicatorsToTotalsTreatingNullAsZero(weightedCounts, indicators, 1.0, useParticleWeight, useSlotWeight);
+ {
+ final DynamicData> currentUnweightedCounts = newTotals(this.greedoConfig.newTimeDiscretization(),
+ this.personId2physicalSlotUsage.values(), true, false);
+ final DynamicData> upcomingUnweightedCounts = newTotals(this.greedoConfig.newTimeDiscretization(),
+ this.personId2hypothetialSlotUsage.values(), true, false);
+ this.presenceResiduals = DynamicDataUtils.newWeightedSum(currentUnweightedCounts, (1.0 - lambdaBar),
+ upcomingUnweightedCounts, lambdaBar);
}
- return weightedCounts;
}
// -------------------- IMPLEMENTATION --------------------
- Set> drawReplanners() {
-
- final DynamicData> interactionResiduals = DynamicDataUtils.newDifference(this.upcomingWeightedCounts,
- this.currentWeightedCounts, this.lambdaBar);
+ Set> drawReplanners(Network network, final Map, Double> personId2cn) {
final DynamicData> weightedReplannerCountDifferences = new DynamicData<>(
this.greedoConfig.newTimeDiscretization());
@@ -177,20 +136,31 @@ Set> drawReplanners() {
double replannerSizeSum = 0.0;
double nonReplannerSizeSum = 0.0;
+ this.personId2Dn0.clear();
+ this.personId2Tn.clear();
+
final Set> replanners = new LinkedHashSet<>();
final List> allPersonIdsShuffled = new ArrayList<>(this.personId2hypotheticalUtilityChange.keySet());
Collections.shuffle(allPersonIdsShuffled);
+ int replanned = 0;
for (Id personId : allPersonIdsShuffled) {
+ this.log.info("replanning " + (++replanned) + " of " + allPersonIdsShuffled.size());
- final ScoreUpdater> scoreUpdater = new ScoreUpdater<>(this.personId2physicalSlotUsage.get(personId),
- this.personId2hypothetialSlotUsage.get(personId), this.lambdaBar, this.beta, interactionResiduals,
- this.personId2hypotheticalUtilityChange.get(personId), this.greedoConfig.getCorrectAgentSize());
+ final ScoreUpdaterTII> scoreUpdater = new ScoreUpdaterTII>(
+ this.personId2physicalSlotUsage.get(personId), this.personId2hypothetialSlotUsage.get(personId),
+ this.lambdaBar, this.personId2hypotheticalUtilityChange.get(personId), this._B,
+ this.presenceResiduals, this.changeResiduals, this.greedoConfig, network);
- final boolean isReplanner = this.greedoConfig.getReplannerIdentifierRecipe().isReplanner(personId,
- scoreUpdater.getScoreChangeIfOne(), scoreUpdater.getScoreChangeIfZero(),
+ boolean isReplanner = this.greedoConfig.getReplannerIdentifierRecipe().isReplanner(personId,
+ scoreUpdater.getScoreChangeIfOne(),
+ scoreUpdater.getScoreChangeIfZero() - personId2cn.getOrDefault(personId, 0.0),
this.personId2currentUtility.get(personId), this.personId2hypotheticalUtilityChange.get(personId));
+ this.personId2interactions.put(personId, scoreUpdater.getRealizedInteractions(isReplanner));
+ this.personId2Dn0.put(personId, scoreUpdater.Dn0);
+ this.personId2Tn.put(personId, scoreUpdater.Tn);
+
if (isReplanner) {
replanners.add(personId);
addIndicatorsToTotalsTreatingNullAsZero(weightedReplannerCountDifferences,
@@ -212,6 +182,7 @@ Set> drawReplanners() {
this.personId2hypothetialSlotUsage.get(personId), +1.0, true, true);
addIndicatorsToTotalsTreatingNullAsZero(weightedNonReplannerCountDifferences,
this.personId2physicalSlotUsage.get(personId), -1.0, true, true);
+
nonReplannerUtilityChangeSum += this.personId2hypotheticalUtilityChange.get(personId);
if (this.personId2physicalSlotUsage.containsKey(personId)) {
nonReplannerSizeSum += this.personId2physicalSlotUsage.get(personId).size();
@@ -244,9 +215,8 @@ Set> drawReplanners() {
personId2similarity.put(personId, similarityNumerator / this.personId2hypotheticalUtilityChange.size());
}
- this.lastExpectations = new SummaryStatistics(this.lambdaBar, this.beta, replannerUtilityChangeSum,
- nonReplannerUtilityChangeSum, this.sumOfWeightedCountDifferences2,
- DynamicDataUtils.sumOfEntries2(weightedReplannerCountDifferences),
+ this.lastExpectations = new SummaryStatistics(this.lambdaBar, null, replannerUtilityChangeSum,
+ nonReplannerUtilityChangeSum, null, DynamicDataUtils.sumOfEntries2(weightedReplannerCountDifferences),
DynamicDataUtils.sumOfEntries2(weightedNonReplannerCountDifferences),
DynamicDataUtils.sumOfEntries2(locationWeightedReplannerCountDifferences), replannerSizeSum,
nonReplannerSizeSum, replanners.size(),
@@ -256,6 +226,18 @@ Set> drawReplanners() {
return replanners;
}
+ Map, SpaceTimeCounts>> getPersonId2InteractionsView() {
+ return Collections.unmodifiableMap(this.personId2interactions);
+ }
+
+ public Map, Double> getPersonId2Dn0() {
+ return Collections.unmodifiableMap(personId2Dn0);
+ }
+
+ public Map, Double> getPersonId2Tn() {
+ return Collections.unmodifiableMap(personId2Tn);
+ }
+
// -------------------- INNER CLASS --------------------
SummaryStatistics getSummaryStatistics(final Set> replanners,
@@ -328,25 +310,6 @@ public Double getSumOfAnticipatedUtilityChanges() {
}
}
- // public Double getSumOfUtilityChangesGivenUniformReplanning() {
- // final Double sumOfUtilityChanges = this.getSumOfUtilityChanges();
- // if ((sumOfUtilityChanges != null) && (this.lambdaBar != null)) {
- // return this.lambdaBar * sumOfUtilityChanges;
- // } else {
- // return null;
- // }
- // }
-
- // public Double getSumOfWeightedCountDifferences2() {
- // if ((this.sumOfWeightedReplannerCountDifferences2 != null)
- // && (this.sumOfWeightedNonReplannerCountDifferences2 != null)) {
- // return (this.sumOfWeightedReplannerCountDifferences2 +
- // this.sumOfWeightedNonReplannerCountDifferences2);
- // } else {
- // return null;
- // }
- // }
-
public Integer getNumberOfReplanningCandidates() {
if ((this.numberOfReplanners != null) && (this.numberOfNonReplanners != null)) {
return (this.numberOfReplanners + this.numberOfNonReplanners);
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdater.java b/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdater.java
deleted file mode 100644
index 05455a8..0000000
--- a/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdater.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2018 Gunnar Flötteröd
- *
- * This program 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 .
- *
- * contact: gunnar.flotterod@gmail.com
- *
- */
-package org.matsim.contrib.greedo;
-
-import java.util.Map;
-
-import org.matsim.contrib.greedo.datastructures.SpaceTimeCounts;
-import org.matsim.contrib.greedo.datastructures.SpaceTimeIndicators;
-
-import floetteroed.utilities.DynamicData;
-import floetteroed.utilities.Tuple;
-
-/**
- * The "score" this class refers to is the anticipated change of the search
- * acceleration objective function resulting from setting a single agent's
- * (possibly space-weighted) 0/1 re-planning indicator.
- *
- * Implements the score used in the greedy heuristic of Merz, P. and Freisleben,
- * B. (2002). "Greedy and local search heuristics for unconstrained binary
- * quadratic programming." Journal of Heuristics 8:197–213.
- *
- * @author Gunnar Flötteröd
- *
- * @param L
- * the space coordinate type
- *
- */
-class ScoreUpdater {
-
- // -------------------- MEMBERS --------------------
-
- private final DynamicData interactionResiduals;
-
- private final SpaceTimeCounts individualWeightedChanges;
-
- private /* final */ double scoreChangeIfZero;
-
- private /* final */ double scoreChangeIfOne;
-
- private boolean residualsUpdated = false;
-
- // -------------------- CONSTRUCTION --------------------
-
- ScoreUpdater(final SpaceTimeIndicators currentIndicators, final SpaceTimeIndicators upcomingIndicators,
- final double meanLambda, final double beta, final DynamicData interactionResiduals,
- final double individualUtilityChange, final boolean correctAgentSize) {
-
- /*
- * One has to go beyond 0/1 indicator arithmetics in the following because the
- * same vehicle may enter the same link multiple times during one time bin.
- */
- this.individualWeightedChanges = new SpaceTimeCounts(upcomingIndicators, true, true);
- this.individualWeightedChanges.subtract(new SpaceTimeCounts<>(currentIndicators, true, true));
-
- /*
- * Update the interaction residuals.
- */
- this.interactionResiduals = interactionResiduals;
- for (Map.Entry, Double> entry : this.individualWeightedChanges.entriesView()) {
- final L spaceObj = entry.getKey().getA();
- final int timeBin = entry.getKey().getB();
- final double weightedIndividualChange = entry.getValue();
- double oldResidual = this.interactionResiduals.getBinValue(spaceObj, timeBin);
- double newResidual = oldResidual - meanLambda * weightedIndividualChange;
- this.interactionResiduals.put(spaceObj, timeBin, newResidual);
- }
-
- /*
- * Compute score (approximated objective function in the solution heuristic):
- *
- * interaction(lambda) - beta * lambda * individualUtilityChange.
- */
- double sumOfWeightedIndividualChanges2 = 0.0;
- double sumOfWeightedIndividualChangesTimesInteractionResiduals = 0.0;
- for (Map.Entry, Double> entry : this.individualWeightedChanges.entriesView()) {
- final L spaceObj = entry.getKey().getA();
- final int timeBin = entry.getKey().getB();
- final double weightedIndividualChange = entry.getValue();
- sumOfWeightedIndividualChanges2 += weightedIndividualChange * weightedIndividualChange;
- sumOfWeightedIndividualChangesTimesInteractionResiduals += weightedIndividualChange
- * this.interactionResiduals.getBinValue(spaceObj, timeBin);
- }
-
- final double scoreIfOne = this.expectedInteraction(1.0, sumOfWeightedIndividualChanges2,
- sumOfWeightedIndividualChangesTimesInteractionResiduals) - beta * 1.0 * individualUtilityChange;
- final double scoreIfMean = this.expectedInteraction(meanLambda, sumOfWeightedIndividualChanges2,
- sumOfWeightedIndividualChangesTimesInteractionResiduals) - beta * meanLambda * individualUtilityChange;
- final double scoreIfZero = this.expectedInteraction(0.0, sumOfWeightedIndividualChanges2,
- sumOfWeightedIndividualChangesTimesInteractionResiduals) - beta * 0.0 * individualUtilityChange;
-
- this.scoreChangeIfOne = scoreIfOne - scoreIfMean;
- this.scoreChangeIfZero = scoreIfZero - scoreIfMean;
-
- if (correctAgentSize) {
- final double agentSizeCorrection = (-1.0) * this.individualWeightedChanges.sumOfSquareEntries();
- this.scoreChangeIfOne += (1.0 - meanLambda) * agentSizeCorrection;
- this.scoreChangeIfZero += (0.0 - meanLambda) * agentSizeCorrection;
- }
- }
-
- // -------------------- INTERNALS --------------------
-
- private double expectedInteraction(final double lambda, final double sumOfWeightedIndividualChanges2,
- final double sumOfWeightedIndividualChangesTimesInteractionResiduals) {
- return lambda * lambda * sumOfWeightedIndividualChanges2
- + 2.0 * lambda * sumOfWeightedIndividualChangesTimesInteractionResiduals;
- }
-
- // -------------------- IMPLEMENTATION --------------------
-
- void updateResiduals(final double newLambda) {
- if (this.residualsUpdated) {
- throw new RuntimeException("Residuals have already been updated.");
- }
- this.residualsUpdated = true;
-
- for (Map.Entry, Double> entry : this.individualWeightedChanges.entriesView()) {
- final L spaceObj = entry.getKey().getA();
- final int timeBin = entry.getKey().getB();
- final double oldResidual = this.interactionResiduals.getBinValue(spaceObj, timeBin);
- final double newResidual = oldResidual + newLambda * entry.getValue();
- this.interactionResiduals.put(spaceObj, timeBin, newResidual);
- }
- }
-
- // -------------------- GETTERS --------------------
-
- double getScoreChangeIfOne() {
- return this.scoreChangeIfOne;
- }
-
- double getScoreChangeIfZero() {
- return this.scoreChangeIfZero;
- }
-}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdaterTII.java b/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdaterTII.java
new file mode 100644
index 0000000..52d4a3b
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/ScoreUpdaterTII.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2018 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo;
+
+import java.util.Map;
+
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeCounts;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeIndicators;
+
+import floetteroed.utilities.DynamicData;
+import floetteroed.utilities.Tuple;
+
+/**
+ * The "score" this class refers to is the anticipated change of the search
+ * acceleration objective function resulting from setting a single agent's
+ * (possibly space-weighted) 0/1 re-planning indicator.
+ *
+ * Implements the score used in the greedy heuristic of Merz, P. and Freisleben,
+ * B. (2002). "Greedy and local search heuristics for unconstrained binary
+ * quadratic programming." Journal of Heuristics 8:197–213.
+ *
+ * @author Gunnar Flötteröd
+ *
+ * @param L the space coordinate type
+ *
+ */
+class ScoreUpdaterTII {
+
+ // -------------------- MEMBERS --------------------
+
+ private final GreedoConfigGroup greedoConfig;
+
+ private final DynamicData presenceResiduals;
+ private final DynamicData changeResiduals;
+
+ private final SpaceTimeCounts unweightedNewXn;
+ private final SpaceTimeCounts unweightedOldXn;
+
+ private final SpaceTimeCounts unweightedDeltaXn;
+ private final SpaceTimeCounts weightedDeltaXn;
+
+ // Interaction i = x_ni * deltaY_i, without abs(.) operation.
+ private final SpaceTimeCounts interactionsWhenStaying;
+ private final SpaceTimeCounts interactionsWhenMoving;
+
+ private DynamicData _B;
+
+ private final double scoreChangeIfZero;
+ private final double scoreChangeIfOne;
+
+ private final double disappointmentIfZero;
+ private final double disappointmentIfOne;
+
+ private boolean residualsUpdated = false;
+
+ public Double Tn = null;
+ public Double Dn0 = null;
+
+ // -------------------- CONSTRUCTION --------------------
+
+ ScoreUpdaterTII(final SpaceTimeIndicators currentIndicators, final SpaceTimeIndicators upcomingIndicators,
+ final double meanLambda, final double individualUtilityChange, final DynamicData _B,
+ final DynamicData presenceResiduals, final DynamicData changeResiduals,
+ final GreedoConfigGroup greedoConfig, final Network network) {
+
+ this.greedoConfig = greedoConfig;
+ this._B = _B;
+
+ /*
+ * One has to go beyond 0/1 indicator arithmetics in the following because the
+ * same particle may enter the same slot multiple times during one time bin.
+ */
+ this.unweightedNewXn = new SpaceTimeCounts(upcomingIndicators, true, false);
+ this.unweightedOldXn = new SpaceTimeCounts(currentIndicators, true, false);
+
+ this.unweightedDeltaXn = new SpaceTimeCounts(upcomingIndicators, true, false);
+ this.unweightedDeltaXn.subtract(new SpaceTimeCounts(currentIndicators, true, false));
+
+ this.weightedDeltaXn = new SpaceTimeCounts(upcomingIndicators, true, true);
+ this.weightedDeltaXn.subtract(new SpaceTimeCounts<>(currentIndicators, true, true));
+
+ /*
+ * Update the residuals.
+ */
+ this.presenceResiduals = presenceResiduals;
+ for (Map.Entry, Double> entry : this.unweightedDeltaXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ final double oldResidual = this.presenceResiduals.getBinValue(spaceObj, timeBin);
+ final double newResidual = oldResidual - meanLambda * entry.getValue();
+ this.presenceResiduals.put(spaceObj, timeBin, newResidual);
+ }
+
+ this.changeResiduals = changeResiduals;
+ for (Map.Entry, Double> entry : this.weightedDeltaXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ final double oldResidual = this.changeResiduals.getBinValue(spaceObj, timeBin);
+ final double newResidual = oldResidual - meanLambda * entry.getValue();
+ this.changeResiduals.put(spaceObj, timeBin, newResidual);
+ }
+
+ /*
+ * Interaction terms (new).
+ */
+ this.interactionsWhenMoving = new SpaceTimeCounts();
+ this.interactionsWhenStaying = new SpaceTimeCounts();
+ for (int timeBin = 0; timeBin < _B.getBinCnt(); timeBin++) {
+ if (upcomingIndicators != null) {
+ for (SpaceTimeIndicators.Visit visit : upcomingIndicators.getVisits(timeBin)) {
+ if (this.largeEnough(visit.spaceObject, network, greedoConfig.getMinPhysLinkSize_veh())) {
+ this.interactionsWhenMoving.add(visit.spaceObject, timeBin,
+ changeResiduals.getBinValue(visit.spaceObject, timeBin));
+ }
+ }
+ }
+ if (currentIndicators != null) {
+ for (SpaceTimeIndicators.Visit visit : currentIndicators.getVisits(timeBin)) {
+ if (this.largeEnough(visit.spaceObject, network, greedoConfig.getMinPhysLinkSize_veh())) {
+ this.interactionsWhenStaying.add(visit.spaceObject, timeBin,
+ changeResiduals.getBinValue(visit.spaceObject, timeBin));
+ }
+ }
+ }
+ }
+
+ /*
+ * Finally, the scores.
+ */
+
+ this.disappointmentIfOne = this.disappointment(1.0);
+ this.disappointmentIfZero = this.disappointment(0.0);
+
+ this.Dn0 = this.disappointmentIfZero;
+ this.Tn = this.disappointmentIfOne - this.disappointmentIfZero;
+
+ final double scoreIfOne = -(1.0 * individualUtilityChange - this.disappointmentIfOne);
+ final double scoreIfMean = -(meanLambda * individualUtilityChange - this.disappointment(meanLambda));
+ final double scoreIfZero = -(0.0 * individualUtilityChange - this.disappointmentIfZero);
+
+ this.scoreChangeIfOne = scoreIfOne - scoreIfMean;
+ this.scoreChangeIfZero = scoreIfZero - scoreIfMean;
+ }
+
+ private boolean largeEnough(final L linkId, final Network net, final double minPhysVehSize_veh) {
+ final Link link = net.getLinks().get(linkId); // TODO works only for road traffic!
+ final double size_veh = link.getLength() * link.getNumberOfLanes() / 7.5;
+ return (size_veh >= minPhysVehSize_veh);
+ }
+
+ // -------------------- INTERNALS --------------------
+
+ private double disappointment(final double lambda) {
+ double result = 0;
+
+ for (Map.Entry, Double> entry : this.unweightedNewXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ double variability = this.changeResiduals.getBinValue(spaceObj, timeBin);
+ if (this.greedoConfig.getSelfInteraction()) {
+ variability += lambda * this.weightedDeltaXn.get(spaceObj, timeBin);
+ }
+ result += lambda * entry.getValue() * this._B.getBinValue(spaceObj, timeBin) * variability;
+ }
+
+ for (Map.Entry, Double> entry : this.unweightedOldXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ double variability = this.changeResiduals.getBinValue(spaceObj, timeBin);
+ if (this.greedoConfig.getSelfInteraction()) {
+ variability += lambda * this.weightedDeltaXn.get(spaceObj, timeBin);
+ }
+ result += (1.0 - lambda) * entry.getValue() * this._B.getBinValue(spaceObj, timeBin) * variability;
+ }
+
+ return result;
+ }
+
+ // -------------------- IMPLEMENTATION --------------------
+
+ void updateResiduals(final double newLambda) {
+ if (this.residualsUpdated) {
+ throw new RuntimeException("Residuals have already been updated.");
+ }
+ this.residualsUpdated = true;
+
+ for (Map.Entry, Double> entry : this.unweightedDeltaXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ final double oldResidual = this.presenceResiduals.getBinValue(spaceObj, timeBin);
+ final double newResidual = oldResidual + newLambda * entry.getValue();
+ this.presenceResiduals.put(spaceObj, timeBin, newResidual);
+ }
+
+ for (Map.Entry, Double> entry : this.weightedDeltaXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ final double oldResidual = this.changeResiduals.getBinValue(spaceObj, timeBin);
+ final double newResidual = oldResidual + newLambda * entry.getValue();
+ this.changeResiduals.put(spaceObj, timeBin, newResidual);
+ }
+ }
+
+ // -------------------- GETTERS --------------------
+
+ double getScoreChangeIfOne() {
+ return this.scoreChangeIfOne;
+ }
+
+ double getScoreChangeIfZero() {
+ return this.scoreChangeIfZero;
+ }
+
+ double getDisappointmentIfOne() {
+ return this.disappointmentIfOne;
+ }
+
+ double getDisappointmentIfZero() {
+ return this.disappointmentIfZero;
+ }
+
+ SpaceTimeCounts getRealizedInteractions(final boolean isReplanner) {
+ final SpaceTimeCounts result;
+ if (isReplanner) {
+ result = this.interactionsWhenMoving;
+ if (this.greedoConfig.getSelfInteraction()) {
+ for (Map.Entry, Double> entry : this.unweightedNewXn.entriesView()) {
+ final L spaceObj = entry.getKey().getA();
+ final int timeBin = entry.getKey().getB();
+ result.add(spaceObj, timeBin, this.weightedDeltaXn.get(spaceObj, timeBin));
+ }
+ throw new RuntimeException("unsupported feature: self interaction");
+ }
+ } else {
+ result = this.interactionsWhenStaying;
+ }
+ return result;
+ }
+
+}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/Utilities.java b/experimental/src/main/java/org/matsim/contrib/greedo/Utilities.java
index c62af12..268a19c 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/Utilities.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/Utilities.java
@@ -76,12 +76,14 @@ Double getLastRealizedUtilityChange() {
class SummaryStatistics {
final Map, Double> personId2expectedUtilityChange;
+ final Map, Double> personId2realizedUtilityChange; // TODO NEW
final Map, Double> personId2experiencedUtility;
final public Double realizedUtilitySum;
final public Double realizedUtilityChangeSum;
private SummaryStatistics() {
final Map, Double> personId2expectedUtilityChange = new LinkedHashMap<>();
+ final Map, Double> personId2realizedUtilityChange = new LinkedHashMap<>(); // TODO NEW
final Map, Double> personId2experiencedUtility = new LinkedHashMap<>();
Double realizedUtilitySum = null;
Double realizedUtilityChangeSum = null;
@@ -92,6 +94,10 @@ private SummaryStatistics() {
if (entry.getLastExpectedUtilityChange() != null) {
personId2expectedUtilityChange.put(personId, entry.getLastExpectedUtilityChange());
}
+ if (entry.getLastRealizedUtilityChange() != null) {
+ // TODO NEW
+ personId2realizedUtilityChange.put(personId, entry.getLastRealizedUtilityChange());
+ }
if (entry.getLastRealizedUtility() != null) {
if (realizedUtilitySum == null) {
realizedUtilitySum = entry.getLastRealizedUtility();
@@ -108,6 +114,7 @@ private SummaryStatistics() {
}
}
this.personId2expectedUtilityChange = Collections.unmodifiableMap(personId2expectedUtilityChange);
+ this.personId2realizedUtilityChange = Collections.unmodifiableMap(personId2realizedUtilityChange); // TODO NEW
this.personId2experiencedUtility = Collections.unmodifiableMap(personId2experiencedUtility);
this.realizedUtilitySum = realizedUtilitySum;
this.realizedUtilityChangeSum = realizedUtilityChangeSum;
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/WireGreedoIntoMATSimControlerListener.java b/experimental/src/main/java/org/matsim/contrib/greedo/WireGreedoIntoMATSimControlerListener.java
index d70a804..36638f4 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/WireGreedoIntoMATSimControlerListener.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/WireGreedoIntoMATSimControlerListener.java
@@ -21,6 +21,7 @@
import java.io.File;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -29,9 +30,11 @@
import javax.inject.Singleton;
+import org.apache.log4j.Logger;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.Plan;
+import org.matsim.contrib.greedo.datastructures.SpaceTimeCounts;
import org.matsim.contrib.greedo.datastructures.SpaceTimeIndicators;
import org.matsim.contrib.greedo.listeners.SlotUsageListener;
import org.matsim.contrib.greedo.logging.AsymptoticAgeLogger;
@@ -50,6 +53,7 @@
import org.matsim.contrib.greedo.logging.NormalizedWeightedNonReplannerCountDifferences2;
import org.matsim.contrib.greedo.logging.NormalizedWeightedReplannerCountDifferences2;
import org.matsim.contrib.greedo.logging.ReplanningRecipe;
+import org.matsim.contrib.greedo.stationaryreplanningregulation.StationaryReplanningRegulator;
import org.matsim.contrib.ier.replannerselection.ReplannerSelector;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.MatsimServices;
@@ -60,6 +64,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
+import floetteroed.utilities.math.BasicStatistics;
import floetteroed.utilities.statisticslogging.StatisticsWriter;
import floetteroed.utilities.statisticslogging.TimeStampStatistic;
@@ -83,7 +88,8 @@ public class WireGreedoIntoMATSimControlerListener implements Provider hypotheticalSlotUsageListeners = new LinkedList<>();
- private double betaScale = 1.0;
+ // private double betaScale = 1.0;
+ private double beta = 1.0;
private Plans lastPhysicalPopulationState = null;
- private ReplannerIdentifier.SummaryStatistics lastReplanningSummaryStatistics = new ReplannerIdentifier.SummaryStatistics();
+ private ReplannerIdentifierTII.SummaryStatistics lastReplanningSummaryStatistics = new ReplannerIdentifierTII.SummaryStatistics();
+
+ private Map, SpaceTimeIndicators>> previousCurrentSlotUsages = null;
+ private Map, SpaceTimeIndicators>> previousAnticipatedSlotUsages = null;
+ private Set> previousReplannerIds = null;
+
+ // private DynamicData> weightedReplannerDeltaX = null;
+
+ private Map, SpaceTimeCounts>> personId2interactions = null;
+
+ public Map, Double> personId2Dn0 = null;
+ public Map, Double> personId2Tn = null;
// -------------------- CONSTRUCTION --------------------
@@ -109,8 +127,10 @@ public WireGreedoIntoMATSimControlerListener(final MatsimServices services) {
this.physicalSlotUsageListener = new SlotUsageListener(this.greedoConfig.newTimeDiscretization(),
this.ages.getWeightsView(), this.greedoConfig.getConcurrentLinkWeights(),
this.greedoConfig.getConcurrentTransitVehicleWeights());
- // this.replanningEfficiencyEstimator = new
- // ReplanningEfficiencyEstimator2(this.greedoConfig);
+
+ this.disappointmentAnalyzer = new DisappointmentAnalyzer(this.greedoConfig);
+ this.stationaryReplanningRegulator = new StationaryReplanningRegulator(
+ this.greedoConfig.getMSAReplanningRate(0), this.greedoConfig.getMSAReplanningRate(0));
this.asymptoticAgeLogger = new AsymptoticAgeLogger(this.greedoConfig.getMaxRelativeMemoryLength(),
new File("./output/"), "asymptoticAges.", ".txt");
@@ -219,6 +239,38 @@ public IEREventHandlerProvider beforeReplanningAndGetEventHandlerProvider() {
this.lastPhysicalPopulationState.getSelectedPlan(person.getId()).getScore());
}
+ // >>> NEW 2020-08-27 >>>
+ if (this.previousReplannerIds != null /* has replanned before */) {
+ Utilities.SummaryStatistics utilityStats = this.utilities.newSummaryStatistics();
+// this.disappointment.update(utilityStats.personId2realizedUtilityChange,
+// utilityStats.personId2expectedUtilityChange, this.previousReplannerIds, this.weightedReplannerDeltaX);
+ this.disappointmentAnalyzer.update(this.previousCurrentSlotUsages, this.previousAnticipatedSlotUsages,
+ utilityStats.personId2realizedUtilityChange, utilityStats.personId2expectedUtilityChange,
+ this.previousReplannerIds, this.personId2interactions,
+ this.greedoConfig.getMSAReplanningRate(this.iteration()));
+// final double naiveE2 = this.disappointmentAnalyzer.getLastNaiveAbsE();
+// final double estimE2 = this.disappointmentAnalyzer.getLastEstimAbsE();
+// Logger.getLogger(this.getClass())
+// .info("DISAPPOINTMENT: naive E2 = " + naiveE2 + ", estimated E2 = " + estimE2
+// + ", reductionRatio = " + (estimE2 / naiveE2) + "; replanningThreshold = "
+// + this.disappointmentAnalyzer.getTheta());
+
+ this.stationaryReplanningRegulator
+ .setMeanReplanningRate(this.greedoConfig.getMSAReplanningRate(this.iteration()));
+ this.stationaryReplanningRegulator.setStepSize(this.greedoConfig.getMSAReplanningRate(this.iteration()));
+ this.stationaryReplanningRegulator.update(utilityStats.personId2expectedUtilityChange, this.personId2Dn0,
+ this.personId2Tn);
+
+ BasicStatistics stats = new BasicStatistics();
+ for (Double cn : this.stationaryReplanningRegulator.getCnView().values()) {
+ stats.add(cn);
+ }
+ Logger.getLogger(this.getClass())
+ .info("CN STATS: mean = " + stats.getAvg() + " stddev = " + stats.getStddev());
+
+ }
+ // <<< NEW 2020-08-27 <<<
+
final LogDataWrapper logDataWrapper = new LogDataWrapper(this.greedoConfig,
this.utilities.newSummaryStatistics(), this.lastReplanningSummaryStatistics,
this.services.getIterationNumber() - 1);
@@ -261,14 +313,52 @@ public void afterReplanning() {
this.hypotheticalSlotUsageListeners.clear();
final Utilities.SummaryStatistics utilityStats = this.utilities.newSummaryStatistics();
- final ReplannerIdentifier replannerIdentifier = new ReplannerIdentifier(
- // this.replanningEfficiencyEstimator.getBeta(),
- this.greedoConfig, this.iteration(), this.physicalSlotUsageListener.getIndicatorView(),
- hypotheticalSlotUsageIndicators, utilityStats.personId2expectedUtilityChange,
- utilityStats.personId2experiencedUtility,
- // this.realizedToTargetLambdaRatio,
- this.betaScale);
- final Set> replannerIds = replannerIdentifier.drawReplanners();
+
+ // >>>>>>>>>> TODO 2020-09-05 Replaced the replanner identifier! >>>>>>>>>>
+
+// final ReplannerIdentifier replannerIdentifier = new ReplannerIdentifier(
+// // this.replanningEfficiencyEstimator.getBeta(),
+// this.greedoConfig, this.iteration(), this.physicalSlotUsageListener.getIndicatorView(),
+// hypotheticalSlotUsageIndicators, utilityStats.personId2expectedUtilityChange,
+// utilityStats.personId2experiencedUtility,
+// // this.realizedToTargetLambdaRatio,
+// this.betaScale);
+// final Set> replannerIds = replannerIdentifier.drawReplanners();
+
+ final double lambdaBar;
+ if ((this.lastReplanningSummaryStatistics.numberOfReplanners != null)
+ && (this.lastReplanningSummaryStatistics.numberOfNonReplanners != null)) {
+ lambdaBar = ((double) this.lastReplanningSummaryStatistics.numberOfReplanners)
+ / (this.lastReplanningSummaryStatistics.numberOfReplanners
+ + this.lastReplanningSummaryStatistics.numberOfNonReplanners);
+ } else {
+ lambdaBar = 1.0;
+ }
+
+ final ReplannerIdentifierTII replannerIdentifier = new ReplannerIdentifierTII(this.greedoConfig,
+ this.physicalSlotUsageListener.getIndicatorView(), hypotheticalSlotUsageIndicators,
+ utilityStats.personId2expectedUtilityChange, utilityStats.personId2experiencedUtility,
+ this.disappointmentAnalyzer.getB(), lambdaBar);
+ final Set> replannerIds = replannerIdentifier.drawReplanners(
+ this.services.getScenario().getNetwork(), this.stationaryReplanningRegulator.getCnView());
+
+ this.personId2interactions = replannerIdentifier.getPersonId2InteractionsView();
+ this.personId2Dn0 = replannerIdentifier.getPersonId2Dn0();
+ this.personId2Tn = replannerIdentifier.getPersonId2Tn();
+
+ this.previousAnticipatedSlotUsages = new LinkedHashMap<>(hypotheticalSlotUsageIndicators);
+ this.previousCurrentSlotUsages = new LinkedHashMap<>(this.physicalSlotUsageListener.getIndicatorView());
+ this.previousReplannerIds = new LinkedHashSet<>(replannerIds);
+// this.weightedReplannerDeltaX = replannerIdentifier.weightedReplannerCountDifferences;
+
+ // <<<<<<<<<< TODO 2020-09-05 Replaced the replanner identifier! <<<<<<<<<<
+
+// for (Id personId : this.services.getScenario().getPopulation().getPersons().keySet()) {
+// if (!replannerIds.contains(personId)) {
+// this.previousAnticipatedSlotUsages.remove(personId);
+// this.previousCurrentSlotUsages.remove(personId);
+// }
+// }
// IERChecker.initHypothetical();
// IERChecker.clearRealized();
@@ -304,14 +394,11 @@ public void afterReplanning() {
this.ages.update(replannerIds);
this.physicalSlotUsageListener.updatePersonWeights(this.ages.getWeightsView());
- // TODO 2019-07-08 NEW
- if (this.greedoConfig.getEnforceMeanReplanningRate()) {
- final double lambdaRealized = ((double) this.lastReplanningSummaryStatistics.numberOfReplanners)
- / this.lastReplanningSummaryStatistics.getNumberOfReplanningCandidates();
- final double lambdaBar = this.lastReplanningSummaryStatistics.lambdaBar;
- // this.realizedToTargetLambdaRatio.add(lambdaRealized / lambdaBar);
- this.betaScale *= (lambdaBar / lambdaRealized);
- }
+// if (this.greedoConfig.getEnforceMeanReplanningRate()) {
+// final double lambdaRealized = ((double) this.lastReplanningSummaryStatistics.numberOfReplanners)
+// / this.lastReplanningSummaryStatistics.getNumberOfReplanningCandidates();
+// this.betaScale *= (this.lastReplanningSummaryStatistics.lambdaBar / lambdaRealized);
+// }
}
// --------------- IMPLEMENTATION OF Provider ---------------
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/DynamicDataUtils2.java b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/DynamicDataUtils2.java
new file mode 100644
index 0000000..a74aa9a
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/DynamicDataUtils2.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo.datastructures;
+
+import floetteroed.utilities.DynamicData;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ */
+public class DynamicDataUtils2 {
+
+ private DynamicDataUtils2() {
+ }
+
+ public static void makeEntriesAbsolute(final DynamicData data) {
+ for (K key : data.keySet()) {
+ for (int bin = 0; bin < data.getBinCnt(); bin++) {
+ final double value = data.getBinValue(key, bin);
+ if (value < 0.0) {
+ data.put(key, bin, Math.abs(value));
+ }
+ }
+ }
+ }
+
+ public static void makeEntriesSquare(final DynamicData data) {
+ for (K key : data.keySet()) {
+ for (int bin = 0; bin < data.getBinCnt(); bin++) {
+ final double value = data.getBinValue(key, bin);
+ if (value != 0.0) {
+ data.put(key, bin, value * value);
+ }
+ }
+ }
+ }
+
+}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/MatsimLinkDataIO.java b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/MatsimLinkDataIO.java
new file mode 100644
index 0000000..49f0353
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/MatsimLinkDataIO.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo.datastructures;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.network.Link;
+
+import floetteroed.utilities.DynamicDataXMLFileIO;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ */
+@SuppressWarnings("serial")
+public class MatsimLinkDataIO extends DynamicDataXMLFileIO> {
+
+ @Override
+ protected String key2attrValue(Id key) {
+ return key.toString();
+ }
+
+ @Override
+ protected Id attrValue2key(String string) {
+ return Id.createLinkId(string);
+ }
+
+}
+
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SlotUsageUtilities.java b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SlotUsageUtilities.java
new file mode 100644
index 0000000..4ab5dbb
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SlotUsageUtilities.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo.datastructures;
+
+import java.util.Collection;
+
+import floetteroed.utilities.DynamicData;
+import floetteroed.utilities.TimeDiscretization;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ */
+public class SlotUsageUtilities {
+
+ private SlotUsageUtilities() {
+ }
+
+ public static void addIndicatorsToTotalsTreatingNullAsZero(final DynamicData totals,
+ final SpaceTimeIndicators indicators, final double factor, final boolean useParticleWeight,
+ final boolean useSlotWeight) {
+ if (indicators != null) {
+ for (int bin = 0; bin < indicators.getTimeBinCnt(); bin++) {
+ for (SpaceTimeIndicators.Visit visit : indicators.getVisits(bin)) {
+ totals.add(visit.spaceObject, bin, factor * visit.weight(useParticleWeight, useSlotWeight));
+ }
+ }
+ }
+ }
+
+ public static DynamicData newTotals(final TimeDiscretization timeDiscr,
+ final Collection> allIndicators, final boolean useParticleWeight,
+ final boolean useSlotWeight) {
+ final DynamicData totals = new DynamicData(timeDiscr);
+ for (SpaceTimeIndicators indicators : allIndicators) {
+ addIndicatorsToTotalsTreatingNullAsZero(totals, indicators, 1.0, useParticleWeight, useSlotWeight);
+ }
+ return totals;
+ }
+
+}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SpaceTimeCounts.java b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SpaceTimeCounts.java
index a7050ce..b16e058 100644
--- a/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SpaceTimeCounts.java
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/datastructures/SpaceTimeCounts.java
@@ -49,6 +49,10 @@ public class SpaceTimeCounts {
// -------------------- CONSTRUCTION --------------------
+ // TODO 2020-09-10 NEW
+ public SpaceTimeCounts() {
+ }
+
public SpaceTimeCounts(final SpaceTimeIndicators parent, final boolean useParticleWeight,
final boolean useSlotWeight) {
if (parent != null) {
@@ -98,7 +102,15 @@ public void subtract(final SpaceTimeCounts other) {
}
}
- // NEW
+ // TODO NEW 2020-09-05
+ public double get(final L key, final int timeBin) {
+ return this.get(new Tuple<>(key, timeBin));
+ }
+
+ // TODO NEW 2020-09-10
+ public void add(final L key, final int timeBin, final double addend) {
+ this.add(new Tuple<>(key, timeBin), addend);
+ }
public double sumOfSquareEntries() {
double result = 0.0;
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/AdaptiveQuantileEstimator.java b/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/AdaptiveQuantileEstimator.java
new file mode 100644
index 0000000..63da802
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/AdaptiveQuantileEstimator.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo.stationaryreplanningregulation;
+
+import java.util.Random;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ * @see https://www.expertsinuncertainty.net/Portals/60/Multimedia/Presentations/MMRM%20-%20EJ%20Workshop%20August%2014/Keming%20Yu%20-%20Bayesian%20Quantile%20Regression.pdf?ver=2017-08-08-192722-947
+ */
+public class AdaptiveQuantileEstimator {
+
+ private double stepSize;
+
+ private double proba;
+
+ private double quantile;
+
+ public AdaptiveQuantileEstimator(final double stepSize, final double proba, final double initialQuantileGuess) {
+ this.stepSize = stepSize;
+ this.proba = proba;
+ this.quantile = initialQuantileGuess;
+ }
+
+ public void setStepSize(final double stepSize) {
+ this.stepSize = stepSize;
+ }
+
+ public void update(final double realization) {
+
+ final double dLoss_dQuantile;
+ if (realization > this.quantile) {
+ dLoss_dQuantile = -this.proba;
+ } else {
+ dLoss_dQuantile = 1.0 - this.proba;
+ }
+
+ // Step size one as we are only estimating a constant.
+ this.quantile -= this.stepSize * dLoss_dQuantile;
+ }
+
+ public void setProbability(final double probability) {
+ this.proba = probability;
+ }
+
+ public double getQuantile() {
+ return this.quantile;
+ }
+
+ public static void main(String[] args) {
+ Random rnd = new Random();
+ AdaptiveQuantileEstimator aqe = new AdaptiveQuantileEstimator(0.1, 0.1359, 0.0);
+ for (int i = 0; i < 100; i++) {
+ double realization = rnd.nextGaussian();
+ aqe.update(realization);
+ System.out.println(realization + "\t" + aqe.quantile);
+ }
+ }
+}
diff --git a/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/StationaryReplanningRegulator.java b/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/StationaryReplanningRegulator.java
new file mode 100644
index 0000000..23f170c
--- /dev/null
+++ b/experimental/src/main/java/org/matsim/contrib/greedo/stationaryreplanningregulation/StationaryReplanningRegulator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 Gunnar Flötteröd
+ *
+ * This program 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 .
+ *
+ * contact: gunnar.flotterod@gmail.com
+ *
+ */
+package org.matsim.contrib.greedo.stationaryreplanningregulation;
+
+import static java.util.Collections.unmodifiableMap;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.population.Person;
+
+/**
+ *
+ * @author Gunnar Flötteröd
+ *
+ */
+public class StationaryReplanningRegulator {
+
+ // -------------------- MEMBERS --------------------
+
+ private double stepSize;
+
+ private double meanReplanningRate;
+
+ private final Map, AdaptiveQuantileEstimator> personId2cn = new LinkedHashMap<>();
+
+ private final Map, Double> personId2expDn0 = new LinkedHashMap<>();
+ private Double avgSqrtOfExpDn0;
+
+ // -------------------- CONSTRUCTION --------------------
+
+ public StationaryReplanningRegulator(final double stepSize, final double meanReplanningRate) {
+ this.stepSize = stepSize;
+ this.meanReplanningRate = meanReplanningRate;
+ }
+
+ // -------------------- IMPLEMENTATION --------------------
+
+ public void setStepSize(final double stepSize) {
+ this.stepSize = stepSize;
+ for (AdaptiveQuantileEstimator cn : this.personId2cn.values()) {
+ cn.setStepSize(stepSize);
+ }
+ }
+
+ public void setMeanReplanningRate(final double meanReplanningRate) {
+ this.meanReplanningRate = meanReplanningRate;
+ }
+
+ public Map, Double> getCnView() {
+ final Map, Double> result = new LinkedHashMap<>();
+ for (Map.Entry, AdaptiveQuantileEstimator> entry : this.personId2cn.entrySet()) {
+ result.put(entry.getKey(), entry.getValue().getQuantile());
+ }
+ return unmodifiableMap(result);
+ }
+
+ // TODO CONTINUE HERE
+
+ public void update(final Map, Double> personId2deltaUn0, final Map, Double> personId2Dn0,
+ final Map, Double> personId2Tn) {
+
+ // Update disappointment when not replanning.
+
+ this.avgSqrtOfExpDn0 = 0.0;
+ for (Map.Entry, Double> entry : personId2Dn0.entrySet()) {
+ final Id personId = entry.getKey();
+ final double dn0 = entry.getValue();
+
+ final double newExpDn0 = Math.max(0.0,
+ (1.0 - this.stepSize) * this.personId2expDn0.getOrDefault(personId, 0.0) + this.stepSize * dn0);
+ this.personId2expDn0.put(personId, newExpDn0);
+ this.avgSqrtOfExpDn0 += Math.sqrt(newExpDn0);
+
+ }
+ this.avgSqrtOfExpDn0 /= this.personId2expDn0.size();
+ this.avgSqrtOfExpDn0 = Math.max(1e-8, this.avgSqrtOfExpDn0);
+
+ // Adjust cn replanning thresholds.
+
+ for (Id personId : personId2deltaUn0.keySet()) {
+ final double targetReplanningRate = this.meanReplanningRate * Math.sqrt(this.personId2expDn0.get(personId))
+ / this.avgSqrtOfExpDn0;
+ AdaptiveQuantileEstimator cn = this.personId2cn.get(personId);
+ if (cn == null) {
+ cn = new AdaptiveQuantileEstimator(this.stepSize, targetReplanningRate, 0.0);
+ this.personId2cn.put(personId, cn);
+ } else {
+ cn.setProbability(targetReplanningRate);
+ }
+ cn.update(personId2deltaUn0.get(personId) - personId2Tn.get(personId));
+ }
+ }
+}
diff --git a/experimental/src/main/java/stockholm/wum/WUMProductionRunner.java b/experimental/src/main/java/stockholm/wum/WUMProductionRunner.java
index bf29a17..561c23c 100644
--- a/experimental/src/main/java/stockholm/wum/WUMProductionRunner.java
+++ b/experimental/src/main/java/stockholm/wum/WUMProductionRunner.java
@@ -160,7 +160,7 @@ static void runProductionScenarioWithSampersDynamics(final String configFileName
controler.addOverridingModule(new SampersDifferentiatedPTScoringFunctionModule());
controler.addOverridingModule(new AbstractModule() {
@Override
- public void install() {
+ public void install() {
this.install(new SBBTransitModule());
this.install(new SwissRailRaptorModule());
}
diff --git a/utilities/src/main/java/floetteroed/utilities/DynamicDataUtils.java b/utilities/src/main/java/floetteroed/utilities/DynamicDataUtils.java
index f2d8cf7..82ea0c6 100644
--- a/utilities/src/main/java/floetteroed/utilities/DynamicDataUtils.java
+++ b/utilities/src/main/java/floetteroed/utilities/DynamicDataUtils.java
@@ -28,7 +28,7 @@ public class DynamicDataUtils {
private DynamicDataUtils() {
}
-
+
public static double sumOfEntries2(final DynamicData data) {
double result = 0.0;
for (L locObj : data.keySet()) {
@@ -70,5 +70,22 @@ public static DynamicData newDifference(final DynamicData data1, final
}
return result;
}
-}
+ public static DynamicData newWeightedSum(final DynamicData data1, final double weight1,
+ final DynamicData data2, final double weight2) {
+ if (data1.getBinCnt() != data2.getBinCnt()) {
+ throw new RuntimeException(
+ "arg1 has " + data1.getBinCnt() + " bins, but arg2 has " + data2.getBinCnt() + " bins.");
+ }
+ final DynamicData result = new DynamicData(data1.getStartTime_s(), data1.getBinSize_s(),
+ data1.getBinCnt());
+ for (L locObj : SetUtils.union(data1.keySet(), data2.keySet())) {
+ for (int bin = 0; bin < data1.getBinCnt(); bin++) {
+ result.put(locObj, bin,
+ weight1 * data1.getBinValue(locObj, bin) + weight2 * data2.getBinValue(locObj, bin));
+ }
+ }
+ return result;
+ }
+
+}