Skip to content

Commit d4af27e

Browse files
authored
feat: generalize pseudo random errors (#290)
* feat: generalize pseudo random errors * make prefix private
1 parent 55e4dc8 commit d4af27e

8 files changed

Lines changed: 72 additions & 99 deletions

File tree

core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public class EqasimConfigGroup extends ReflectiveConfigGroup {
2727

2828
private final static String USE_SCHEDULE_BASED_TRANSPORT = "useScheduleBasedTransport";
2929

30+
private final static String USE_PSEUDO_RANDOM_ERRORS = "usePseudoRandomErrors";
31+
3032
private double sampleSize = 1.0;
3133
private DistanceUnit distanceUnit = DistanceUnit.meter;
3234

@@ -42,6 +44,8 @@ public class EqasimConfigGroup extends ReflectiveConfigGroup {
4244

4345
private boolean useScheduleBasedTransport = true;
4446

47+
private boolean usePseudoRandomErrors = false;
48+
4549
public EqasimConfigGroup() {
4650
super(GROUP_NAME);
4751
}
@@ -66,6 +70,16 @@ public void setSampleSize(double sampleSize) {
6670
this.sampleSize = sampleSize;
6771
}
6872

73+
@StringGetter(USE_PSEUDO_RANDOM_ERRORS)
74+
public boolean getUsePseudoRandomErrors() {
75+
return usePseudoRandomErrors;
76+
}
77+
78+
@StringSetter(USE_PSEUDO_RANDOM_ERRORS)
79+
public void setUsePseudoRandomErrors(boolean usePseudoRandomErrors) {
80+
this.usePseudoRandomErrors = usePseudoRandomErrors;
81+
}
82+
6983
@Override
7084
public ConfigGroup createParameterSet(String type) {
7185
switch (type) {

core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import org.eqasim.core.simulation.mode_choice.constraints.PassengerConstraint;
1212
import org.eqasim.core.simulation.mode_choice.cost.CostModel;
1313
import org.eqasim.core.simulation.mode_choice.cost.ZeroCostModel;
14+
import org.eqasim.core.simulation.mode_choice.epsilon.EpsilonModule;
15+
import org.eqasim.core.simulation.mode_choice.epsilon.EpsilonProvider;
1416
import org.eqasim.core.simulation.mode_choice.filters.OutsideFilter;
1517
import org.eqasim.core.simulation.mode_choice.filters.TourLengthFilter;
16-
import org.eqasim.core.simulation.mode_choice.utilities.ModalUtilityEstimator;
18+
import org.eqasim.core.simulation.mode_choice.utilities.EqasimUtilityEstimator;
1719
import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
1820
import org.eqasim.core.simulation.mode_choice.utilities.estimators.BikeUtilityEstimator;
1921
import org.eqasim.core.simulation.mode_choice.utilities.estimators.CarUtilityEstimator;
@@ -76,7 +78,7 @@ protected void installEqasimExtension() {
7678
bindTourFilter(TOUR_LENGTH_FILTER_NAME).to(TourLengthFilter.class);
7779
bindTourFilter(OUTSIDE_FILTER_NAME).to(OutsideFilter.class);
7880

79-
bindTripEstimator(UTILITY_ESTIMATOR_NAME).to(ModalUtilityEstimator.class);
81+
bindTripEstimator(UTILITY_ESTIMATOR_NAME).to(EqasimUtilityEstimator.class);
8082

8183
bind(CarPredictor.class);
8284
bind(PtPredictor.class);
@@ -96,12 +98,14 @@ protected void installEqasimExtension() {
9698

9799
bindTourConstraintFactory(VEHICLE_TOUR_CONSTRAINT).to(EqasimVehicleTourConstraint.Factory.class);
98100
bindHomeFinder(HOME_FINDER).to(EqasimHomeFinder.class);
101+
102+
install(new EpsilonModule());
99103
}
100104

101105
@Provides
102-
public ModalUtilityEstimator provideModularUtilityEstimator(TripRouter tripRouter, ActivityFacilities facilities,
106+
public EqasimUtilityEstimator provideModularUtilityEstimator(TripRouter tripRouter, ActivityFacilities facilities,
103107
Map<String, Provider<UtilityEstimator>> factory, EqasimConfigGroup config,
104-
TimeInterpretation timeInterpretation, DiscreteModeChoiceConfigGroup dmcConfig) {
108+
TimeInterpretation timeInterpretation, DiscreteModeChoiceConfigGroup dmcConfig, EpsilonProvider epsilonProvider) {
105109
Map<String, UtilityEstimator> estimators = new HashMap<>();
106110

107111
for (Map.Entry<String, String> entry : config.getEstimators().entrySet()) {
@@ -115,8 +119,8 @@ public ModalUtilityEstimator provideModularUtilityEstimator(TripRouter tripRoute
115119
}
116120
}
117121

118-
return new ModalUtilityEstimator(tripRouter, facilities, estimators, timeInterpretation,
119-
Collections.emptySet()); // Here we may add "pt" etc. as pre-routed modes.
122+
return new EqasimUtilityEstimator(tripRouter, facilities, estimators, timeInterpretation,
123+
Collections.emptySet(), epsilonProvider); // Here we may add "pt" etc. as pre-routed modes.
120124
}
121125

122126
@Provides

core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/AdaptConfigForEpsilon.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import org.matsim.core.config.Config;
88
import org.matsim.core.config.ConfigUtils;
99

10-
import java.util.Map;
11-
1210
public class AdaptConfigForEpsilon {
1311

1412
public static void main(String[] args) throws CommandLine.ConfigurationException {
@@ -21,15 +19,8 @@ public static void main(String[] args) throws CommandLine.ConfigurationException
2119
discreteModeChoiceConfigGroup.setSelector(SelectorModule.MAXIMUM);
2220

2321
EqasimConfigGroup eqasimConfigGroup = (EqasimConfigGroup) config.getModules().get(EqasimConfigGroup.GROUP_NAME);
24-
25-
26-
for(Map.Entry<String, String> entry: eqasimConfigGroup.getEstimators().entrySet()) {
27-
if(entry.getValue().startsWith(EpsilonModule.EPSILON_UTILITY_PREFIX)) {
28-
continue;
29-
}
30-
eqasimConfigGroup.setEstimator(entry.getKey(), EpsilonModule.EPSILON_UTILITY_PREFIX + entry.getValue());
31-
}
32-
22+
eqasimConfigGroup.setUsePseudoRandomErrors(true);
23+
3324
ConfigUtils.writeConfig(config, commandLine.getOptionStrict("output-config-path"));
3425
}
3526
}

core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/EpsilonAdapter.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/EpsilonModule.java

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,45 @@
11
package org.eqasim.core.simulation.mode_choice.epsilon;
22

3-
import com.google.inject.Inject;
4-
import com.google.inject.Provider;
5-
import com.google.inject.Provides;
3+
import java.util.Map;
4+
65
import org.apache.logging.log4j.LogManager;
76
import org.apache.logging.log4j.Logger;
87
import org.eqasim.core.components.config.EqasimConfigGroup;
98
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
10-
import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
119
import org.matsim.core.config.groups.GlobalConfigGroup;
1210

13-
import java.util.HashSet;
14-
import java.util.Map;
15-
import java.util.Set;
11+
import com.google.inject.Provides;
1612

1713
public class EpsilonModule extends AbstractEqasimExtension {
1814

1915
public static final Logger logger = LogManager.getLogger(EpsilonModule.class);
2016

21-
public static final String EPSILON_UTILITY_PREFIX = "epsilon_";
17+
// made private to avoid use
18+
private static final String EPSILON_UTILITY_PREFIX = "epsilon_";
19+
2220
@Provides
2321
public GumbelEpsilonProvider provideGumbelEpsilonProvider(GlobalConfigGroup config) {
2422
return new GumbelEpsilonProvider(config.getRandomSeed(), 1.0);
2523
}
2624

2725
@Override
2826
protected void installEqasimExtension() {
29-
bind(EpsilonProvider.class).to(GumbelEpsilonProvider.class);
27+
EqasimConfigGroup eqasimConfigGroup = EqasimConfigGroup.get(getConfig());
3028

29+
if (eqasimConfigGroup.getUsePseudoRandomErrors()) {
30+
bind(EpsilonProvider.class).to(GumbelEpsilonProvider.class);
31+
} else {
32+
bind(EpsilonProvider.class).toInstance(NoopEpsilonProvider.INSTANCE);
33+
}
3134

32-
EqasimConfigGroup eqasimConfigGroup = (EqasimConfigGroup) getConfig().getModules().get(EqasimConfigGroup.GROUP_NAME);
33-
Set<String> processed = new HashSet<>();
34-
for(Map.Entry<String, String > entry: eqasimConfigGroup.getEstimators().entrySet()) {
35+
for (Map.Entry<String, String> entry : eqasimConfigGroup.getEstimators().entrySet()) {
3536
String mode = entry.getKey();
3637
String utilityEstimator = entry.getValue();
37-
if(utilityEstimator.startsWith(EPSILON_UTILITY_PREFIX)) {
38-
if(processed.contains(utilityEstimator)) {
39-
logger.warn(String.format("The epsilon utility estimator '%s' is used for more than one mode. The seed of the epsilon generator will rely on the first mode", utilityEstimator));
40-
continue;
41-
}
42-
processed.add(utilityEstimator);
43-
String baseEstimator = utilityEstimator.substring(EPSILON_UTILITY_PREFIX.length());
44-
bindUtilityEstimator(utilityEstimator).toProvider(new Provider<>() {
45-
@Inject
46-
private Map<String, Provider<UtilityEstimator>> factory;
47-
48-
@Inject
49-
private EpsilonProvider epsilonProvider;
50-
51-
@Override
52-
public UtilityEstimator get() {
53-
UtilityEstimator delegate = factory.get(baseEstimator).get();
54-
return new EpsilonAdapter(mode, delegate, epsilonProvider);
55-
}
56-
});
38+
39+
if (utilityEstimator.startsWith(EPSILON_UTILITY_PREFIX)) {
40+
throw new IllegalStateException(String.format(
41+
"Estimator for %s is prefixed with %s. Use eqasim.usePseudoRandomErrors = true instead now.",
42+
mode, EPSILON_UTILITY_PREFIX));
5743
}
5844
}
5945
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.eqasim.core.simulation.mode_choice.epsilon;
2+
3+
import org.matsim.api.core.v01.Id;
4+
import org.matsim.api.core.v01.population.Person;
5+
6+
public class NoopEpsilonProvider implements EpsilonProvider {
7+
final static public NoopEpsilonProvider INSTANCE = new NoopEpsilonProvider();
8+
9+
@Override
10+
public double getEpsilon(Id<Person> personId, int tripIndex, String mode) {
11+
return 0.0;
12+
}
13+
}

core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/ModalUtilityEstimator.java renamed to core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/EqasimUtilityEstimator.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55
import java.util.Map;
66

7+
import org.eqasim.core.simulation.mode_choice.epsilon.EpsilonProvider;
78
import org.matsim.api.core.v01.population.Person;
89
import org.matsim.api.core.v01.population.PlanElement;
910
import org.matsim.contribs.discrete_mode_choice.components.estimators.AbstractTripRouterEstimator;
@@ -13,14 +14,16 @@
1314
import org.matsim.core.utils.timing.TimeInterpretation;
1415
import org.matsim.facilities.ActivityFacilities;
1516

16-
public class ModalUtilityEstimator extends AbstractTripRouterEstimator {
17+
public class EqasimUtilityEstimator extends AbstractTripRouterEstimator {
1718
private final Map<String, UtilityEstimator> estimators;
19+
private final EpsilonProvider epsilonProvider;
1820

19-
public ModalUtilityEstimator(TripRouter tripRouter, ActivityFacilities facilities,
21+
public EqasimUtilityEstimator(TripRouter tripRouter, ActivityFacilities facilities,
2022
Map<String, UtilityEstimator> estimators, TimeInterpretation timeInterpretation,
21-
Collection<String> preroutedModes) {
23+
Collection<String> preroutedModes, EpsilonProvider epsilonProvider) {
2224
super(tripRouter, facilities, timeInterpretation, preroutedModes);
2325
this.estimators = estimators;
26+
this.epsilonProvider = epsilonProvider;
2427
}
2528

2629
@Override
@@ -31,7 +34,9 @@ protected double estimateTrip(Person person, String mode, DiscreteModeChoiceTrip
3134
if (estimator == null) {
3235
throw new IllegalStateException(String.format("No estimator registered for mode '%s'", mode));
3336
} else {
34-
return estimator.estimateUtility(person, trip, elements);
37+
double utility = estimator.estimateUtility(person, trip, elements);
38+
utility += epsilonProvider.getEpsilon(person.getId(), trip.getIndex(), mode);
39+
return utility;
3540
}
3641
}
3742
}

core/src/main/java/org/eqasim/core/simulation/modes/feeder_drt/mode_choice/EqasimFeederDrtModeChoiceModule.java

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package org.eqasim.core.simulation.modes.feeder_drt.mode_choice;
22

3-
import com.google.inject.Provider;
4-
import com.google.inject.Provides;
3+
import java.util.Map;
4+
import java.util.stream.Collectors;
5+
56
import org.eqasim.core.components.config.EqasimConfigGroup;
67
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
7-
import org.eqasim.core.simulation.mode_choice.epsilon.EpsilonAdapter;
88
import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
9-
import org.eqasim.core.simulation.modes.feeder_drt.mode_choice.constraints.FeederDrtConstraint;
109
import org.eqasim.core.simulation.modes.feeder_drt.config.MultiModeFeederDrtConfigGroup;
10+
import org.eqasim.core.simulation.modes.feeder_drt.mode_choice.constraints.FeederDrtConstraint;
1111
import org.eqasim.core.simulation.modes.feeder_drt.mode_choice.utilities.estimator.DefaultFeederDrtUtilityEstimator;
1212

13-
import java.util.List;
14-
import java.util.Map;
15-
import java.util.stream.Collectors;
13+
import com.google.inject.Provider;
14+
import com.google.inject.Provides;
1615

1716
public class EqasimFeederDrtModeChoiceModule extends AbstractEqasimExtension {
1817

@@ -28,14 +27,6 @@ protected void installEqasimExtension() {
2827
public DefaultFeederDrtUtilityEstimator provideDefaultFeederDrtUtilityEstimator(EqasimConfigGroup eqasimConfigGroup, MultiModeFeederDrtConfigGroup multiModeFeederDrtConfigGroup, Map<String, Provider<UtilityEstimator>> utilityEstimatorProviders) {
2928
Map<String, UtilityEstimator> ptEstimators = multiModeFeederDrtConfigGroup.getModalElements().stream().collect(Collectors.toMap(cfg -> cfg.mode, cfg -> utilityEstimatorProviders.get(eqasimConfigGroup.getEstimators().get(cfg.ptModeName)).get()));
3029
Map<String, UtilityEstimator> drtEstimators = multiModeFeederDrtConfigGroup.getModalElements().stream().collect(Collectors.toMap(cfg -> cfg.mode, cfg -> utilityEstimatorProviders.get(eqasimConfigGroup.getEstimators().get(cfg.accessEgressModeName)).get()));
31-
// When we use the Epsilon adapter, we do not want to sum the pseudo-random errors of each sub-mode but rather only use one pseudo-error specific to the current mode
32-
for(Map<String, UtilityEstimator> map: List.of(ptEstimators, drtEstimators)) {
33-
for(String mode: map.keySet()) {
34-
if(map.get(mode) instanceof EpsilonAdapter epsilonAdapter) {
35-
map.put(mode, epsilonAdapter.getDelegate());
36-
}
37-
}
38-
}
3930
return new DefaultFeederDrtUtilityEstimator(ptEstimators, drtEstimators);
4031
}
4132

0 commit comments

Comments
 (0)