From 03ca6fed2051e9dd68a574fb6c09d9981f1e4f2b Mon Sep 17 00:00:00 2001 From: rakow Date: Sat, 7 Dec 2024 12:09:43 +0100 Subject: [PATCH 1/7] implemented pseudo random error as trip scoring, instead of leg --- .../AdvancedScoringFunctionFactory.java | 24 +++++-- .../scoring/DefaultPseudoRandomTripError.java | 5 +- .../scoring/PiecewiseLinearlLegScoring.java | 70 +++---------------- .../run/scoring/PseudoRandomScorer.java | 7 +- .../run/scoring/PseudoRandomTripError.java | 3 +- .../run/scoring/PseudoRandomTripScoring.java | 62 ++++++++++++++++ 6 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java diff --git a/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java b/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java index 73c1e4a9..1fbfcd49 100644 --- a/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java +++ b/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java @@ -3,6 +3,7 @@ import com.google.inject.Inject; import org.matsim.api.core.v01.population.Person; import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; import org.matsim.core.scoring.ScoringFunction; import org.matsim.core.scoring.ScoringFunctionFactory; import org.matsim.core.scoring.SumScoringFunction; @@ -13,14 +14,18 @@ */ public class AdvancedScoringFunctionFactory implements ScoringFunctionFactory { - @Inject - private Config config; - - @Inject - private ScoringParametersForPerson params; + private final Config config; + private final AdvancedScoringConfigGroup scoring; + private final ScoringParametersForPerson params; + private final PseudoRandomScorer pseudoRNG; @Inject - private PseudoRandomScorer pseudoRNG; + public AdvancedScoringFunctionFactory(Config config, ScoringParametersForPerson params, PseudoRandomScorer pseudoRNG) { + this.config = config; + this.scoring = ConfigUtils.addOrGetModule(config, AdvancedScoringConfigGroup.class); + this.params = params; + this.pseudoRNG = pseudoRNG; + } @Override public ScoringFunction createNewScoringFunction(Person person) { @@ -28,8 +33,13 @@ public ScoringFunction createNewScoringFunction(Person person) { SumScoringFunction sumScoringFunction = new SumScoringFunction(); sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(parameters)); + + if (scoring.pseudoRamdomScale > 0) { + sumScoringFunction.addScoringFunction(new PseudoRandomTripScoring(person.getId(), pseudoRNG)); + } + // replaced original leg scoring - sumScoringFunction.addScoringFunction(new PiecewiseLinearlLegScoring(parameters, person.getId(), config.transit().getTransitModes(), pseudoRNG)); + sumScoringFunction.addScoringFunction(new PiecewiseLinearlLegScoring(parameters, config.transit().getTransitModes())); sumScoringFunction.addScoringFunction(new CharyparNagelMoneyScoring(parameters)); sumScoringFunction.addScoringFunction(new CharyparNagelAgentStuckScoring(parameters)); sumScoringFunction.addScoringFunction(new ScoreEventScoring()); diff --git a/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java b/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java index b82ca7f0..8b386de5 100644 --- a/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java +++ b/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java @@ -2,6 +2,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; +import org.matsim.core.router.TripStructureUtils; /** * Computes a random seed based on person id, previous activity and routing mode. @@ -9,12 +10,12 @@ public final class DefaultPseudoRandomTripError implements PseudoRandomTripError { @Override - public long getSeed(Id personId, String routingMode, String prevActivityType) { + public long getSeed(Id personId, String routingMode, TripStructureUtils.Trip trip) { int personHash = personId.toString().hashCode(); int modeHash = routingMode.hashCode(); - int modeAndActHash = 31 * modeHash + prevActivityType.hashCode(); + int modeAndActHash = 31 * modeHash + trip.getOriginActivity().getType().hashCode(); // Combine two integers to long return (long) personHash << 32 | modeAndActHash & 0xFFFFFFFFL; diff --git a/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java b/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java index a8889318..233fea15 100644 --- a/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java +++ b/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java @@ -22,25 +22,19 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.events.ActivityEndEvent; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.events.PersonDepartureEvent; import org.matsim.api.core.v01.events.PersonEntersVehicleEvent; import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Route; import org.matsim.core.gbl.Gbl; -import org.matsim.core.router.TripStructureUtils; -import org.matsim.core.scoring.ScoringFunction; import org.matsim.core.scoring.functions.ModeUtilityParameters; import org.matsim.core.scoring.functions.ScoringParameters; import org.matsim.pt.PtConstants; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Set; /** @@ -57,9 +51,7 @@ public final class PiecewiseLinearlLegScoring implements org.matsim.core.scoring * The parameters used for scoring. */ private final ScoringParameters params; - private final Id personId; private final Set ptModes; - private final PseudoRandomScorer pseudoRNG; private final double marginalUtilityOfMoney; private final Set modesAlreadyConsideredForDailyConstants; private double score; @@ -67,21 +59,12 @@ public final class PiecewiseLinearlLegScoring implements org.matsim.core.scoring private boolean nextStartPtLegIsFirstOfTrip = true; private boolean currentLegIsPtLeg = false; private double lastActivityEndTime = Double.NaN; - private String lastActivityType = null; - /** - * The number of legs since the last activity. - */ - private int legIndex = 0; - private final List legScores; - public PiecewiseLinearlLegScoring(final ScoringParameters params, Id personId, Set ptModes, PseudoRandomScorer pseudoRNG) { + public PiecewiseLinearlLegScoring(final ScoringParameters params, Set ptModes) { this.params = params; - this.personId = personId; this.ptModes = ptModes; - this.pseudoRNG = pseudoRNG; this.modesAlreadyConsideredForDailyConstants = new HashSet<>(); this.marginalUtilityOfMoney = this.params.marginalUtilityOfMoney; - this.legScores = new LinkedList<>(); } @Override @@ -94,36 +77,15 @@ public double getScore() { return this.score; } - @Override - public void explainScore(StringBuilder out) { - out.append("legs_util=").append(score); - - // Store for each leg - if (!legScores.isEmpty()) { - for (int i = 0; i < legScores.size(); i++) { - out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_total=").append(legScores.get(i).total); - out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_randomComponent=").append(legScores.get(i).randomComponent); - out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_constant=").append(legScores.get(i).constant); - } - } - } - /** * Calculate the score for a leg. */ - private Score calcLegScore(final double departureTime, final double arrivalTime, final Leg leg) { + private double calcLegScore(final double departureTime, final double arrivalTime, final Leg leg) { double tmpScore = 0.0; // travel time in seconds double travelTime = arrivalTime - departureTime; ModeUtilityParameters modeParams = this.params.modeParams.get(leg.getMode()); - // The first leg of a trip incurs trip specific random utility - double randomComponent = 0.0; - if (legIndex == 0) { - randomComponent = pseudoRNG.scoreTrip(personId, leg.getRoutingMode(), lastActivityType); - tmpScore += randomComponent; - } - if (modeParams == null) { if (leg.getMode().equals(TransportMode.transit_walk) || leg.getMode().equals(TransportMode.non_network_walk)) { modeParams = this.params.modeParams.get(TransportMode.walk); @@ -201,24 +163,18 @@ private Score calcLegScore(final double departureTime, final double arrivalTime, // yyyy the above will cause problems if we ever decide to differentiate pt mode into bus, tram, train, ... // Might have to move the MainModeIdentifier then. kai, sep'18 - return new Score(tmpScore, randomComponent, modeParams.constant); + return tmpScore; } @Override public void handleEvent(Event event) { - if (event instanceof ActivityEndEvent a) { + if (event instanceof ActivityEndEvent) { // When there is a "real" activity, flags are reset: - if (!PtConstants.TRANSIT_ACTIVITY_TYPE.equals(a.getActType())) { + if (!PtConstants.TRANSIT_ACTIVITY_TYPE.equals(((ActivityEndEvent) event).getActType())) { this.nextEnterVehicleIsFirstOfTrip = true; this.nextStartPtLegIsFirstOfTrip = true; } this.lastActivityEndTime = event.getTime(); - - // Trip occurs when non stating activity ends - if (!TripStructureUtils.isStageActivityType(a.getActType())) { - this.legIndex = 0; - this.lastActivityType = ((ActivityEndEvent) event).getActType(); - } } if (event instanceof PersonEntersVehicleEvent && currentLegIsPtLeg) { @@ -251,24 +207,14 @@ public void handleLeg(Leg leg) { Gbl.assertIf(leg.getDepartureTime().isDefined()); Gbl.assertIf(leg.getTravelTime().isDefined()); - Score legScore = calcLegScore( + double legScore = calcLegScore( leg.getDepartureTime().seconds(), leg.getDepartureTime().seconds() + leg.getTravelTime() .seconds(), leg); - - // Increase leg index after scoring - legIndex++; - - if (Double.isNaN(legScore.total)) { + if (Double.isNaN(legScore)) { log.error("dpTime=" + leg.getDepartureTime().seconds() + "; ttime=" + leg.getTravelTime().seconds() + "; leg=" + leg); throw new RuntimeException("score is NaN"); } - - this.score += legScore.total; - this.legScores.add(legScore); - } - - private record Score(double total, double randomComponent, double constant) { + this.score += legScore; } - } diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java b/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java index 314738b9..c8e44d98 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java @@ -10,6 +10,7 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.router.TripStructureUtils; import java.util.SplittableRandom; @@ -38,7 +39,7 @@ public PseudoRandomScorer(PseudoRandomTripError tripScore, Config config) { for (int i = 0; i < WARMUP_ITERATIONS; i++) { rnd.nextDouble(); } - + // Create a random seed from the global one this.seed = rnd.nextLong(); } @@ -46,12 +47,12 @@ public PseudoRandomScorer(PseudoRandomTripError tripScore, Config config) { /** * Calculates the pseudo random score of a trip. */ - public double scoreTrip(Id personId, String routingMode, String prevActivityType) { + public double scoreTrip(Id personId, String routingMode, TripStructureUtils.Trip trip) { if (tripScore == null || scale == 0) return 0; - long tripSeed = tripScore.getSeed(personId, routingMode, prevActivityType); + long tripSeed = tripScore.getSeed(personId, routingMode, trip); // Need to create a new instance because reusing them will also create a lot of intermediate arrays XoRoShiRo128PlusPlus rng = new XoRoShiRo128PlusPlus(seed, tripSeed); diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java b/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java index 79628aa8..bcf589cd 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java @@ -2,6 +2,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; +import org.matsim.core.router.TripStructureUtils; /** * Interface to provide pseudo-random errors for a trip. @@ -11,7 +12,7 @@ public interface PseudoRandomTripError { /** * Return a seed for a trip. The seed must be designed such that it is constant for the same choice situations. */ - long getSeed(Id personId, String routingMode, String prevActivityType); + long getSeed(Id personId, String routingMode, TripStructureUtils.Trip trip); } diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java new file mode 100644 index 00000000..9684f541 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java @@ -0,0 +1,62 @@ +package org.matsim.run.scoring; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.scoring.ScoringFunction; +import org.matsim.core.scoring.SumScoringFunction; + +import java.util.List; + +public class PseudoRandomTripScoring implements SumScoringFunction.TripScoring { + + private static final Logger log = LogManager.getLogger(PseudoRandomTripScoring.class); + + private final Id id; + private final PseudoRandomScorer rng; + + private final DoubleList scores = new DoubleArrayList(); + private double score; + + public PseudoRandomTripScoring(Id id, PseudoRandomScorer rng) { + this.id = id; + this.rng = rng; + } + + @Override + public void finish() { + } + + @Override + public double getScore() { + return score; + } + + @Override + public void handleTrip(TripStructureUtils.Trip trip) { + + List legs = trip.getLegsOnly(); + if (legs.isEmpty()) { + log.warn("Trip {} for person {} has not legs and can not be scored", trip, id); + return; + } + + double tripScore = rng.scoreTrip(id, legs.getFirst().getRoutingMode(), trip); + scores.add(tripScore); + score += tripScore; + } + + @Override + public void explainScore(StringBuilder out) { + out.append("trips_util=").append(score); + for (int i = 0; i < scores.size(); i++) { + double s = scores.getDouble(i); + out.append(ScoringFunction.SCORE_DELIMITER).append("trip_").append(i).append("=").append(s); + } + } +} From c1c7a20967ddd0eb1fe44a48f3311b099ed9fa62 Mon Sep 17 00:00:00 2001 From: rakow Date: Sat, 7 Dec 2024 14:48:07 +0100 Subject: [PATCH 2/7] update matsim --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1ef396ea..0bf75f98 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ matsim-all - 2025.0-PR3628 + 2025.0-PR3636 From af74b60cad406d3f74d9d35ec5ec2a0296ec70b8 Mon Sep 17 00:00:00 2001 From: rakow Date: Sat, 7 Dec 2024 14:49:02 +0100 Subject: [PATCH 3/7] add missing java doc --- .../java/org/matsim/run/scoring/PseudoRandomTripScoring.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java index 9684f541..ea2a6883 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java @@ -13,6 +13,9 @@ import java.util.List; +/** + * Scoring function that assigns a pseudo-random score to each trip. + */ public class PseudoRandomTripScoring implements SumScoringFunction.TripScoring { private static final Logger log = LogManager.getLogger(PseudoRandomTripScoring.class); From 68e4ae3de4bc82cef2672e0e8e934a5a3915ec52 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 10 Dec 2024 20:28:39 +0100 Subject: [PATCH 4/7] and choice experiment policy --- .../policies/OpenBerlinChoiceExperiment.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java diff --git a/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java new file mode 100644 index 00000000..0907006f --- /dev/null +++ b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java @@ -0,0 +1,45 @@ +package org.matsim.run.policies; + +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.application.MATSimApplication; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.run.OpenBerlinScenario; +import picocli.CommandLine; + +/** + * This class can be used to run some synthetic choice experiments on the OpenBerlin scenario. + */ +public class OpenBerlinChoiceExperiment extends OpenBerlinScenario { + + @CommandLine.Option(names = "--bike-speed-factor", description = "Speed factor for bikes", defaultValue = "1.0") + private double bikeSpeedFactor = 1.0; + + public static void main(String[] args) { + MATSimApplication.execute(OpenBerlinChoiceExperiment.class, args); + } + + @Override + protected void prepareScenario(Scenario scenario) { + + super.prepareScenario(scenario); + + // If bike speed is adjusted, we need to remove all bike routes and travel times + // These times will be recalculated by the router + if (bikeSpeedFactor != 1.0) { + for (Person person : scenario.getPopulation().getPersons().values()) { + for (Plan plan : person.getPlans()) { + for (Leg leg : TripStructureUtils.getLegs(plan)) { + if (leg.getMode().equals(TransportMode.bike)) { + leg.setRoute(null); + leg.setTravelTimeUndefined(); + } + } + } + } + } + } +} From 05ae1b009ddf7f3ca169e4225ab00a6a97db5ee1 Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 13 Dec 2024 08:54:35 +0100 Subject: [PATCH 5/7] use main mode in trip scorer, implement class for choice experiments --- .../policies/OpenBerlinChoiceExperiment.java | 81 ++++++++++++++++++- .../scoring/AdvancedScoringConfigGroup.java | 2 +- .../AdvancedScoringFunctionFactory.java | 8 +- .../scoring/DefaultPseudoRandomTripError.java | 6 +- .../run/scoring/PseudoRandomScorer.java | 4 +- .../run/scoring/PseudoRandomTripError.java | 2 +- .../run/scoring/PseudoRandomTripScoring.java | 8 +- 7 files changed, 96 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java index 0907006f..681c46ff 100644 --- a/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java +++ b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java @@ -1,27 +1,68 @@ package org.matsim.run.policies; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.application.MATSimApplication; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.Controler; import org.matsim.core.router.TripStructureUtils; +import org.matsim.modechoice.InformedModeChoiceConfigGroup; +import org.matsim.modechoice.InformedModeChoiceModule; +import org.matsim.modechoice.ModeOptions; +import org.matsim.modechoice.constraints.RelaxedMassConservationConstraint; +import org.matsim.modechoice.estimators.DefaultActivityEstimator; +import org.matsim.modechoice.estimators.DefaultLegScoreEstimator; +import org.matsim.modechoice.estimators.FixedCostsEstimator; import org.matsim.run.OpenBerlinScenario; +import org.matsim.run.scoring.AdvancedScoringConfigGroup; +import org.matsim.vehicles.VehicleType; import picocli.CommandLine; /** - * This class can be used to run some synthetic choice experiments on the OpenBerlin scenario. + * This class can be used to run some synthetic mode choice experiments on the OpenBerlin scenario. */ public class OpenBerlinChoiceExperiment extends OpenBerlinScenario { - @CommandLine.Option(names = "--bike-speed-factor", description = "Speed factor for bikes", defaultValue = "1.0") - private double bikeSpeedFactor = 1.0; + @CommandLine.Option(names = "--bike-speed-offset", description = "Offset the default bike speed in km/h", defaultValue = "0") + private double bikeSpeedOffset; + + @CommandLine.Option(names = "--imc", description = "Enable informed-mode-choice functionality") + private boolean imc; public static void main(String[] args) { MATSimApplication.execute(OpenBerlinChoiceExperiment.class, args); } + @Override + protected Config prepareConfig(Config config) { + + if (imc) { + + InformedModeChoiceConfigGroup imcConfig = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class); + + imcConfig.setConstraintCheck(InformedModeChoiceConfigGroup.ConstraintCheck.repair); + + // TODO: enable pruning + + // TODO: replace strategy + + // TODO: start imc runs + // from uncalibrated population with baseline calibration + + // next, with new mode scoring + + // different number iterations x pruning thresholds/top k + + } + + return config; + } + @Override protected void prepareScenario(Scenario scenario) { @@ -29,7 +70,11 @@ protected void prepareScenario(Scenario scenario) { // If bike speed is adjusted, we need to remove all bike routes and travel times // These times will be recalculated by the router - if (bikeSpeedFactor != 1.0) { + if (bikeSpeedOffset != 0) { + + VehicleType bike = scenario.getVehicles().getVehicleTypes().get(Id.create(TransportMode.bike, VehicleType.class)); + bike.setMaximumVelocity(bike.getMaximumVelocity() + bikeSpeedOffset / 3.6); + for (Person person : scenario.getPopulation().getPersons().values()) { for (Plan plan : person.getPlans()) { for (Leg leg : TripStructureUtils.getLegs(plan)) { @@ -42,4 +87,32 @@ protected void prepareScenario(Scenario scenario) { } } } + + + @Override + protected void prepareControler(Controler controler) { + super.prepareControler(controler); + + if (imc) { + + InformedModeChoiceModule.Builder builder = InformedModeChoiceModule.newBuilder() + .withActivityEstimator(DefaultActivityEstimator.class) + .withFixedCosts(FixedCostsEstimator.DailyConstant.class, "car", "pt") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.ConsiderIfCarAvailable.class, "car") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.AlwaysAvailable.class, "pt", "walk", "bike", "ride") + .withConstraint(RelaxedMassConservationConstraint.class); + + if (ConfigUtils.hasModule(controler.getConfig(), AdvancedScoringConfigGroup.class)) { + + // TODO: add pseudo random errors to estimator + // Implement pseudo trip scoring into informed mode choice + + // TODO: option for pruning + + } + + controler.addOverridingModule(builder.build()); + } + + } } diff --git a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java index 188cc174..0ed3787b 100644 --- a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java +++ b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java @@ -178,7 +178,7 @@ public static final class ModeParams extends ReflectiveConfigGroup { public VariationType varDailyConstant = VariationType.fixed; @Parameter - @Comment("total delta utility per dist group.") + @Comment("Total delta utility per dist group.") public List deltaPerDistGroup; public ModeParams() { diff --git a/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java b/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java index 1fbfcd49..d9b75460 100644 --- a/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java +++ b/src/main/java/org/matsim/run/scoring/AdvancedScoringFunctionFactory.java @@ -4,6 +4,7 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.router.AnalysisMainModeIdentifier; import org.matsim.core.scoring.ScoringFunction; import org.matsim.core.scoring.ScoringFunctionFactory; import org.matsim.core.scoring.SumScoringFunction; @@ -16,13 +17,16 @@ public class AdvancedScoringFunctionFactory implements ScoringFunctionFactory { private final Config config; private final AdvancedScoringConfigGroup scoring; + private final AnalysisMainModeIdentifier mmi; private final ScoringParametersForPerson params; private final PseudoRandomScorer pseudoRNG; @Inject - public AdvancedScoringFunctionFactory(Config config, ScoringParametersForPerson params, PseudoRandomScorer pseudoRNG) { + public AdvancedScoringFunctionFactory(Config config, AnalysisMainModeIdentifier mmi, + ScoringParametersForPerson params, PseudoRandomScorer pseudoRNG) { this.config = config; this.scoring = ConfigUtils.addOrGetModule(config, AdvancedScoringConfigGroup.class); + this.mmi = mmi; this.params = params; this.pseudoRNG = pseudoRNG; } @@ -35,7 +39,7 @@ public ScoringFunction createNewScoringFunction(Person person) { sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(parameters)); if (scoring.pseudoRamdomScale > 0) { - sumScoringFunction.addScoringFunction(new PseudoRandomTripScoring(person.getId(), pseudoRNG)); + sumScoringFunction.addScoringFunction(new PseudoRandomTripScoring(person.getId(), mmi, pseudoRNG)); } // replaced original leg scoring diff --git a/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java b/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java index 8b386de5..f02b24e3 100644 --- a/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java +++ b/src/main/java/org/matsim/run/scoring/DefaultPseudoRandomTripError.java @@ -10,14 +10,14 @@ public final class DefaultPseudoRandomTripError implements PseudoRandomTripError { @Override - public long getSeed(Id personId, String routingMode, TripStructureUtils.Trip trip) { + public long getSeed(Id personId, String mainMode, TripStructureUtils.Trip trip) { int personHash = personId.toString().hashCode(); - int modeHash = routingMode.hashCode(); + int modeHash = mainMode.hashCode(); int modeAndActHash = 31 * modeHash + trip.getOriginActivity().getType().hashCode(); // Combine two integers to long - return (long) personHash << 32 | modeAndActHash & 0xFFFFFFFFL; + return ((long) personHash << 32) | (modeAndActHash & 0xFFFFFFFFL); } } diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java b/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java index c8e44d98..2257e746 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomScorer.java @@ -47,12 +47,12 @@ public PseudoRandomScorer(PseudoRandomTripError tripScore, Config config) { /** * Calculates the pseudo random score of a trip. */ - public double scoreTrip(Id personId, String routingMode, TripStructureUtils.Trip trip) { + public double scoreTrip(Id personId, String mainMode, TripStructureUtils.Trip trip) { if (tripScore == null || scale == 0) return 0; - long tripSeed = tripScore.getSeed(personId, routingMode, trip); + long tripSeed = tripScore.getSeed(personId, mainMode, trip); // Need to create a new instance because reusing them will also create a lot of intermediate arrays XoRoShiRo128PlusPlus rng = new XoRoShiRo128PlusPlus(seed, tripSeed); diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java b/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java index bcf589cd..eaae9bcd 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomTripError.java @@ -12,7 +12,7 @@ public interface PseudoRandomTripError { /** * Return a seed for a trip. The seed must be designed such that it is constant for the same choice situations. */ - long getSeed(Id personId, String routingMode, TripStructureUtils.Trip trip); + long getSeed(Id personId, String mainMode, TripStructureUtils.Trip trip); } diff --git a/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java index ea2a6883..b16491da 100644 --- a/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java +++ b/src/main/java/org/matsim/run/scoring/PseudoRandomTripScoring.java @@ -7,6 +7,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; +import org.matsim.core.router.MainModeIdentifier; import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scoring.ScoringFunction; import org.matsim.core.scoring.SumScoringFunction; @@ -21,13 +22,15 @@ public class PseudoRandomTripScoring implements SumScoringFunction.TripScoring { private static final Logger log = LogManager.getLogger(PseudoRandomTripScoring.class); private final Id id; + private final MainModeIdentifier mmi; private final PseudoRandomScorer rng; private final DoubleList scores = new DoubleArrayList(); private double score; - public PseudoRandomTripScoring(Id id, PseudoRandomScorer rng) { + public PseudoRandomTripScoring(Id id, MainModeIdentifier mmi, PseudoRandomScorer rng) { this.id = id; + this.mmi = mmi; this.rng = rng; } @@ -49,7 +52,8 @@ public void handleTrip(TripStructureUtils.Trip trip) { return; } - double tripScore = rng.scoreTrip(id, legs.getFirst().getRoutingMode(), trip); + String mainMode = mmi.identifyMainMode(legs); + double tripScore = rng.scoreTrip(id, mainMode, trip); scores.add(tripScore); score += tripScore; } From a0324cb720aae0b7b0d991f6dd7f1004d9e56c0b Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 13 Dec 2024 08:57:41 +0100 Subject: [PATCH 6/7] add comment --- .../run/scoring/AdvancedScoringConfigGroup.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java index 0ed3787b..9e7fc6eb 100644 --- a/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java +++ b/src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java @@ -181,6 +181,22 @@ public static final class ModeParams extends ReflectiveConfigGroup { @Comment("Total delta utility per dist group.") public List deltaPerDistGroup; + /* + Unused options: + + @Parameter + @Comment("Marginal utility of distance calculated as beta_dist * (dist/ref_dist)^exp_dist.") + public double betaDist = 0; + + @Parameter + @Comment("Reference mean distance.") + public double refDist; + + @Parameter + @Comment("Exponent controlling non-linearity of distance utility.") + public double expDist = 0; + */ + public ModeParams() { super(GROUP_NAME); } From 5a55ca4eb4a5ae29a86b9cdfadc1ebc56baaa1f9 Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 13 Dec 2024 12:39:36 +0100 Subject: [PATCH 7/7] add config prepare line in experiment --- .../org/matsim/run/policies/OpenBerlinChoiceExperiment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java index 681c46ff..a123ffb3 100644 --- a/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java +++ b/src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java @@ -41,6 +41,8 @@ public static void main(String[] args) { @Override protected Config prepareConfig(Config config) { + config = super.prepareConfig(config); + if (imc) { InformedModeChoiceConfigGroup imcConfig = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class);