Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Informed-mode-choice maintenance #3107

Merged
merged 13 commits into from
Feb 13, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* Aggregate class for informed-mode-choice that makes sure to invoke the correct estimator for each drt mode.
*/
public class MultiModalDrtLegEstimator implements LegEstimator<ModeAvailability> {
public class MultiModalDrtLegEstimator implements LegEstimator {

private static final Logger log = LogManager.getLogger(MultiModalDrtLegEstimator.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.matsim.api.core.v01.population.Plan;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
Expand All @@ -15,14 +15,15 @@ public interface CandidateGenerator {
/**
* Generate plan candidates, ordered by their natural comparator.
*/
default Collection<PlanCandidate> generate(PlanModel planModel) {
default List<PlanCandidate> generate(PlanModel planModel) {
return generate(planModel, null, null);
}

/**
* Generate plan candidates, ordered by their natural comparator.
* @param consideredModes if not null, will restrict usable modes to these present in the set
* @param mask if not null, only include these trips with a true entry at their respective index.
* @return list of candidates, this list must be mutable
*/
Collection<PlanCandidate> generate(PlanModel planModel, @Nullable Set<String> consideredModes, @Nullable boolean[] mask);
List<PlanCandidate> generate(PlanModel planModel, @Nullable Set<String> consideredModes, @Nullable boolean[] mask);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import org.matsim.core.controler.AbstractModule;
import org.matsim.core.controler.listener.ControlerListener;
import org.matsim.core.router.PlanRouter;
import org.matsim.core.router.TripRouter;
import org.matsim.core.utils.timing.TimeInterpretation;
Expand Down Expand Up @@ -48,6 +49,10 @@ private InformedModeChoiceModule(Builder builder) {
this.builder = builder;
}

public static Builder newBuilder() {
return new Builder();
}

@Override
public void install() {

Expand Down Expand Up @@ -80,7 +85,12 @@ public void install() {

MapBinder<String, CandidatePruner> pBinder = MapBinder.newMapBinder(binder(), String.class, CandidatePruner.class);
for (Map.Entry<String, CandidatePruner> e : builder.pruner.entrySet()) {
pBinder.addBinding(e.getKey()).toInstance(e.getValue());
CandidatePruner instance = e.getValue();

pBinder.addBinding(e.getKey()).toInstance(instance);

if (instance instanceof ControlerListener cl)
addControlerListenerBinding().toInstance(cl);
}

addPlanStrategyBinding(SELECT_BEST_K_PLAN_MODES_STRATEGY).toProvider(SelectBestKPlanModesStrategyProvider.class);
Expand Down Expand Up @@ -131,20 +141,16 @@ private <T> void bindAllModes(Map<String, Class<? extends T>> map, TypeLiteral<T
}
}

public static Builder newBuilder() {
return new Builder();
}

/**
* Builder to configure the module.
*/
public static final class Builder {

private final Map<String, Class<? extends FixedCostsEstimator<?>>> fixedCosts = new HashMap<>();
private final Map<String, Class<? extends LegEstimator<?>>> legEstimators = new HashMap<>();
private final Map<String, Class<? extends TripEstimator<?>>> tripEstimators = new HashMap<>();
private final Map<String, Class<? extends FixedCostsEstimator>> fixedCosts = new HashMap<>();
private final Map<String, Class<? extends LegEstimator>> legEstimators = new HashMap<>();
private final Map<String, Class<? extends TripEstimator>> tripEstimators = new HashMap<>();

private final Map<String, Class<? extends ModeOptions<?>>> options = new HashMap<>();
private final Map<String, Class<? extends ModeOptions>> options = new HashMap<>();

private final Set<Class<? extends TripConstraint<?>>> constraints = new LinkedHashSet<>();

Expand All @@ -155,7 +161,7 @@ public static final class Builder {
/**
* Adds a fixed cost to one or more modes.
*/
public <T extends Enum<?>> Builder withFixedCosts(Class<? extends FixedCostsEstimator<T>> estimator, String... modes) {
public Builder withFixedCosts(Class<? extends FixedCostsEstimator> estimator, String... modes) {

for (String mode : modes) {
fixedCosts.put(mode, estimator);
Expand All @@ -167,8 +173,8 @@ public <T extends Enum<?>> Builder withFixedCosts(Class<? extends FixedCostsEsti
/**
* Adds a {@link LegEstimator} to one or more modes.
*/
public <T extends Enum<T>> Builder withLegEstimator(Class<? extends LegEstimator<T>> estimator, Class<? extends ModeOptions<T>> option,
String... modes) {
public Builder withLegEstimator(Class<? extends LegEstimator> estimator, Class<? extends ModeOptions> option,
String... modes) {

for (String mode : modes) {

Expand All @@ -187,8 +193,8 @@ public <T extends Enum<T>> Builder withLegEstimator(Class<? extends LegEstimator
/**
* Adds a {@link TripEstimator} to one or more modes.
*/
public <T extends Enum<?>> Builder withTripEstimator(Class<? extends TripEstimator<T>> estimator, Class<? extends ModeOptions<T>> option,
String... modes) {
public Builder withTripEstimator(Class<? extends TripEstimator> estimator, Class<? extends ModeOptions> option,
String... modes) {

for (String mode : modes) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
package org.matsim.modechoice;

/**
* Default mode availability enumeration with two different options.
* Mode availability enumeration with different options.
* Different options are required in case there a different pricing schemes, depending on the usage of the mode.
*/
public enum ModeAvailability {

YES,
NO;
NO,

MONTHLY_SUBSCRIPTION,
YEARLY_SUBSCRIPTION,
DAILY_TICKET,

/**
* May be used if the other options are not applicable.
*/
OTHER;

/**
* True for all options except {@link #NO}.
*/
public boolean isModeAvailable() {
return this != NO;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public final class ModeEstimate {

private final String mode;
private final Enum<?> option;
private final ModeAvailability option;

private final double[] est;
private final double[] tripEst;
Expand All @@ -35,7 +35,7 @@ public final class ModeEstimate {
* @param isMin whether these are minimum estimates
* @param storeTripEst whether trip est needs to be stored
*/
ModeEstimate(String mode, Enum<?> option, int n, boolean isUsable, boolean storeTripEst, boolean isMin) {
ModeEstimate(String mode, ModeAvailability option, int n, boolean isUsable, boolean storeTripEst, boolean isMin) {
this.mode = mode;
this.option = option;
this.min = isMin;
Expand All @@ -48,7 +48,7 @@ public String getMode() {
return mode;
}

public Enum<?> getOption() {
public ModeAvailability getOption() {
return option;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@
import java.util.List;

/**
* Interface to determine which mode options are available to an agent and considered when computing best options.
* Interface to determine which {@link ModeAvailability} are available to an agent and considered when computing best options.
* <p>
* This interface also contains the default implementations.
*
* @param <T> enum listing the possible options
*/
public interface ModeOptions<T extends Enum<?>> {
public interface ModeOptions {

/**
* Determine options for one agent.
*/
List<T> get(Person person);
List<ModeAvailability> get(Person person);

/**
* Return whether an option allows to use the mode. Normally only one of the option should forbid using the mode at all.
* Return whether an option allows to use the mode.
*/
boolean allowUsage(T option);
default boolean allowUsage(ModeAvailability option) {
return option.isModeAvailable();
}

/**
* The mode is always available and considered.
* This also means that there should be no daily costs associated with the mode.
*/
final class AlwaysAvailable implements ModeOptions<ModeAvailability> {
final class AlwaysAvailable implements ModeOptions {

private static final List<ModeAvailability> YES = List.of(ModeAvailability.YES);
private static final List<ModeAvailability> NO = List.of(ModeAvailability.NO);
Expand All @@ -37,16 +39,12 @@ public List<ModeAvailability> get(Person person) {
return YES;
}

@Override
public boolean allowUsage(ModeAvailability option) {
return option == ModeAvailability.YES;
}
}

/**
* Plans are considered with and without this mode.
*/
final class ConsiderYesAndNo implements ModeOptions<ModeAvailability> {
final class ConsiderYesAndNo implements ModeOptions {

private static final List<ModeAvailability> BOTH = List.of(ModeAvailability.YES, ModeAvailability.NO);

Expand All @@ -55,17 +53,13 @@ public List<ModeAvailability> get(Person person) {
return BOTH;
}

@Override
public boolean allowUsage(ModeAvailability option) {
return option == ModeAvailability.YES;
}
}


/**
* Consider both options if car is available, otherwise none.
*/
final class ConsiderIfCarAvailable implements ModeOptions<ModeAvailability> {
final class ConsiderIfCarAvailable implements ModeOptions {

@Override
public List<ModeAvailability> get(Person person) {
Expand All @@ -75,10 +69,5 @@ public List<ModeAvailability> get(Person person) {

return AlwaysAvailable.NO;
}

@Override
public boolean allowUsage(ModeAvailability option) {
return option == ModeAvailability.YES;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public double getUtility() {
}

/**
* Get mode for trip i.
* Get mode for trip i. Indexing starts at 0.
* @see #size()
*/
public String getMode(int i) {
return modes[i];
Expand All @@ -51,6 +52,25 @@ public String[] getModes() {
return Arrays.copyOf(modes, modes.length);
}

/**
* Check whether a certain mode is present at least once.
*/
public boolean containsMode(String mode) {
for (String m : modes) {
if (mode.equals(m)) {
return true;
}
}
return false;
}

/**
* Number of trips.
*/
public int size() {
return modes.length;
}

/**
* Return features vector with number of occurrences per mode.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public Person getPerson() {
}

public Plan getPlan() {
// TODO: This should better be removed, memory usage by keeping these plans is increased
return plan;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
public final class PlanModelService implements StartupListener {

@Inject
private Map<String, LegEstimator<?>> legEstimators;
private Map<String, LegEstimator> legEstimators;

@Inject
private Map<String, TripEstimator<?>> tripEstimator;
private Map<String, TripEstimator> tripEstimator;

@Inject
private Set<TripConstraint<?>> constraints;
Expand All @@ -49,10 +49,10 @@ public final class PlanModelService implements StartupListener {
private ControlerListenerManager controlerListenerManager;

private final InformedModeChoiceConfigGroup config;
private final Map<String, ModeOptions<?>> options;
private final Map<String, ModeOptions> options;

@Inject
private PlanModelService(InformedModeChoiceConfigGroup config, Map<String, ModeOptions<?>> options) {
private PlanModelService(InformedModeChoiceConfigGroup config, Map<String, ModeOptions> options) {
this.config = config;
this.options = options;

Expand Down Expand Up @@ -92,14 +92,14 @@ public void initEstimates(EstimatorContext context, PlanModel planModel) {

for (String mode : config.getModes()) {

ModeOptions<Enum<?>> t = (ModeOptions<Enum<?>>) this.options.get(mode);
ModeOptions t = this.options.get(mode);

List<Enum<?>> modeOptions = t.get(planModel.getPerson());
List<ModeAvailability> modeOptions = t.get(planModel.getPerson());

List<ModeEstimate> c = new ArrayList<>();

for (Enum<?> modeOption : modeOptions) {
TripEstimator<Enum<?>> te = (TripEstimator<Enum<?>>) tripEstimator.get(mode);
for (ModeAvailability modeOption : modeOptions) {
TripEstimator te = tripEstimator.get(mode);

boolean usable = t.allowUsage(modeOption);

Expand All @@ -123,7 +123,7 @@ public void initEstimates(EstimatorContext context, PlanModel planModel) {
/**
* Return the modes an estimator was registered for.
*/
public List<String> modesForEstimator(LegEstimator<?> est) {
public List<String> modesForEstimator(LegEstimator est) {
return legEstimators.entrySet().stream().filter(e -> e.getValue().equals(est))
.map(Map.Entry::getKey)
.distinct()
Expand All @@ -139,10 +139,10 @@ public List<String> allowedModes(PlanModel planModel) {

for (String mode : config.getModes()) {

ModeOptions<Enum<?>> t = (ModeOptions<Enum<?>>) this.options.get(mode);
List<Enum<?>> modeOptions = t.get(planModel.getPerson());
ModeOptions t = this.options.get(mode);
List<ModeAvailability> modeOptions = t.get(planModel.getPerson());

for (Enum<?> modeOption : modeOptions) {
for (ModeAvailability modeOption : modeOptions) {
boolean usable = t.allowUsage(modeOption);

if (usable) {
Expand Down Expand Up @@ -181,7 +181,7 @@ public void calculateEstimates(EstimatorContext context, PlanModel planModel) {
continue;
}

TripEstimator<Enum<?>> tripEst = (TripEstimator<Enum<?>>) tripEstimator.get(c.getMode());
TripEstimator tripEst = tripEstimator.get(c.getMode());

// some options may produce equivalent results, but are re-estimated
// however, the more expensive computation is routing and only done once
Expand Down Expand Up @@ -265,7 +265,7 @@ public boolean isValidOption(PlanModel model, String[] modes) {
/**
* Return the trip estimator for one specific mode.
*/
public TripEstimator<?> getTripEstimator(String mode) {
public TripEstimator getTripEstimator(String mode) {
return tripEstimator.get(mode);
}

Expand Down
Loading
Loading