Skip to content

Commit

Permalink
Documentation mode choice + make mode choice analysis standard (matsi…
Browse files Browse the repository at this point in the history
…m-org#3285)

* add comments to clarify configs

* add ModeChoiceCoverageControlerListener as default analysis

* fix tests

* fix tests
  • Loading branch information
simei94 authored May 27, 2024
1 parent 4796104 commit 7bb99a7
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,95 +98,102 @@ public void notifyIterationEnds(final IterationEndsEvent event) {

updateModesUsedPerPerson();


/*
* Looks through modesUsedPerPersonTrip at each person-trip. How many of those person trips have used each mode more than the
* predefined limits.
*/
int totalPersonTripCount = 0;
Map<Integer, Map<String, Double>> modeCountCurrentIteration = new TreeMap<>();
//Map<Limit, Map<Mode , TotalTripCount >>

for (Map<Integer, Map<String, Integer>> mapForPerson : modesUsedPerPersonTrip.values()) {
//Map<Trip # , Map<Mode , Count >>
for (Map<String, Integer> mapForPersonTrip : mapForPerson.values()) {
//Map<Mode , Count >
totalPersonTripCount++;
for (String mode : mapForPersonTrip.keySet()) {
Integer realCount = mapForPersonTrip.get(mode);
for (Integer limit : limits) {
Map<String, Double> modeCountMap = modeCountCurrentIteration.computeIfAbsent(limit, k -> new TreeMap<>());
Double modeCount = modeCountMap.computeIfAbsent(mode, k -> 0.);
if (realCount >= limit) {
modeCount++;
}
modeCountMap.put(mode, modeCount);
modeCountCurrentIteration.put(limit, modeCountMap);
}
}
}
}
// Calculates mcc share for each mode in current iteration, and updates modeCCHistory accordingly
for (Integer limit : limits) {
Map<String, Double> modeCnt = modeCountCurrentIteration.get(limit);
this.modes.addAll(modeCnt.keySet()); // potentially adds new modes to setthat just showed up in current iter
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.computeIfAbsent(limit, k -> new HashMap<>());
for (String mode : modes) {
Double cnt = modeCnt.get(mode);
double share = 0.;
if (cnt != null) {
share = cnt / totalPersonTripCount;
}

log.info("-- mode choice coverage (" + limit + "x) of mode " + mode + " = " + share);

Map<Integer, Double> iterationShareMap = modeIterationShareMap.get(mode);

// If this is the first iteration where the mode shows up, add zeros to all previous iterations in history
if (iterationShareMap == null) {
iterationShareMap = new TreeMap<>();
for (int iter = firstIteration; iter < event.getIteration(); iter++) {
iterationShareMap.put(iter, 0.0);
}
modeIterationShareMap.put(mode, iterationShareMap);
}

iterationShareMap.put(event.getIteration(), share);
}
}


// Print MCC Stats to output file
for (Integer limit : limits) {
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.get(limit);

BufferedWriter modeOut = IOUtils.getBufferedWriter(this.modeFileName + limit + "x.txt");
try {
modeOut.write("Iteration");
for (String mode : modes) {
modeOut.write("\t" + mode);
}
modeOut.write("\n");
for (int iter = firstIteration; iter <= event.getIteration(); iter++) {
modeOut.write(String.valueOf(iter));
for (String mode : modes) {
modeOut.write("\t" + modeIterationShareMap.get(mode).get(iter));
}
modeOut.write("\n");
}

modeOut.flush();
modeOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}

// Produce Graphs
if (this.createPNG && event.getIteration() > this.minIteration) {
produceGraphs();
}

/*
* Looks through modesUsedPerPersonTrip at each person-trip. How many of those person trips have used each mode more than the
* predefined limits.
*/
int totalPersonTripCount = 0;
Map<Integer, Map<String, Double>> modeCountCurrentIteration = new TreeMap<>();
//Map<Limit, Map<Mode , TotalTripCount >>

for (Map<Integer, Map<String, Integer>> mapForPerson : modesUsedPerPersonTrip.values()) {
//Map<Trip # , Map<Mode , Count >>
for (Map<String, Integer> mapForPersonTrip : mapForPerson.values()) {
//Map<Mode , Count >
totalPersonTripCount++;
for (String mode : mapForPersonTrip.keySet()) {
Integer realCount = mapForPersonTrip.get(mode);
for (Integer limit : limits) {
Map<String, Double> modeCountMap = modeCountCurrentIteration.computeIfAbsent(limit, k -> new TreeMap<>());
Double modeCount = modeCountMap.computeIfAbsent(mode, k -> 0.);
if (realCount >= limit) {
modeCount++;
}
modeCountMap.put(mode, modeCount);
modeCountCurrentIteration.put(limit, modeCountMap);
}
}
}
}


// for testing purposes: if there are any trips, do analysis. If not, it is probably a test or a faulty / empty population. -sme0524
if (!modeCountCurrentIteration.isEmpty()) {
// Calculates mcc share for each mode in current iteration, and updates modeCCHistory accordingly
for (Integer limit : limits) {
Map<String, Double> modeCnt = modeCountCurrentIteration.get(limit);
this.modes.addAll(modeCnt.keySet()); // potentially adds new modes to setthat just showed up in current iter
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.computeIfAbsent(limit, k -> new HashMap<>());
for (String mode : modes) {
Double cnt = modeCnt.get(mode);
double share = 0.;
if (cnt != null) {
share = cnt / totalPersonTripCount;
}

log.info("-- mode choice coverage (" + limit + "x) of mode " + mode + " = " + share);

Map<Integer, Double> iterationShareMap = modeIterationShareMap.get(mode);

// If this is the first iteration where the mode shows up, add zeros to all previous iterations in history
if (iterationShareMap == null) {
iterationShareMap = new TreeMap<>();
for (int iter = firstIteration; iter < event.getIteration(); iter++) {
iterationShareMap.put(iter, 0.0);
}
modeIterationShareMap.put(mode, iterationShareMap);
}

iterationShareMap.put(event.getIteration(), share);
}
}


// Print MCC Stats to output file
for (Integer limit : limits) {
Map<String, Map<Integer, Double>> modeIterationShareMap = modeCCHistory.get(limit);

BufferedWriter modeOut = IOUtils.getBufferedWriter(this.modeFileName + limit + "x.txt");
try {
modeOut.write("Iteration");
for (String mode : modes) {
modeOut.write("\t" + mode);
}
modeOut.write("\n");
for (int iter = firstIteration; iter <= event.getIteration(); iter++) {
modeOut.write(String.valueOf(iter));
for (String mode : modes) {
modeOut.write("\t" + modeIterationShareMap.get(mode).get(iter));
}
modeOut.write("\n");
}

modeOut.flush();
modeOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}

// Produce Graphs
if (this.createPNG && event.getIteration() > this.minIteration) {
produceGraphs();
}

} else {
log.warn("There are no trips conducted by the analyzed population. This should only be the case for tests. If you are running a simulation run, " +
" this should not happen. Check your population.");
}
}

private void updateModesUsedPerPerson() {
Expand Down
2 changes: 2 additions & 0 deletions matsim/src/main/java/org/matsim/analysis/ModeStatsModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ public class ModeStatsModule extends AbstractModule {
public void install() {
bind(ModeStatsControlerListener.class).in(Singleton.class);
addControlerListenerBinding().to(ModeStatsControlerListener.class);
// KN: if this is a somewhat standard analysis it should be added by default rather than adding it in every scenario run class
addControlerListenerBinding().to(ModeChoiceCoverageControlerListener.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private StrategySettings getStrategySettings(final Id<StrategySettings> index, f
@Override
public final Map<String, String> getComments() {
Map<String,String> map = super.getComments();
map.put(ReflectiveDelegate.ITERATION_FRACTION_TO_DISABLE_INNOVATION, "fraction of iterations where innovative strategies are switched off. Something like 0.8 should be good. E.g. if you run from iteration 400 to iteration 500, innovation is switched off at iteration 480" ) ;
map.put(ReflectiveDelegate.ITERATION_FRACTION_TO_DISABLE_INNOVATION, "fraction of iterations where innovative strategies are switched off. Something like 0.8 should be good. E.g. if you run from iteration 400 to iteration 500, innovation is switched off at iteration 480. If the ReplanningAnnealer is used, it will also be switched off." ) ;
map.put(ReflectiveDelegate.MAX_AGENT_PLAN_MEMORY_SIZE, "maximum number of plans per agent. ``0'' means ``infinity''. Currently (2010), ``5'' is a good number");

StringBuilder strb = new StringBuilder() ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@
public final class SubtourModeChoiceConfigGroup extends ReflectiveConfigGroup {

public static final String GROUP_NAME = "subtourModeChoice";

public final static String MODES = "modes";
public final static String CHAINBASEDMODES = "chainBasedModes";
public final static String CARAVAIL = "considerCarAvailability";
public final static String SINGLE_PROBA = "probaForRandomSingleTripMode";
public final static String COORD_DISTANCE = "coordDistance";

private static final String BEHAVIOR = "behavior";

private String[] chainBasedModes = new String[] { TransportMode.car, TransportMode.bike };
private String[] allModes = new String[] { TransportMode.car, TransportMode.pt, TransportMode.bike, TransportMode.walk };
// default is false for backward compatibility
private boolean considerCarAvailability = false;
private SubtourModeChoice.Behavior behavior = SubtourModeChoice.Behavior.fromSpecifiedModesToSpecifiedModes ;

private double probaForRandomSingleTripMode = 0. ; // yyyyyy backwards compatibility setting; should be change. kai, may'18

private double coordDistance = 0;

public SubtourModeChoiceConfigGroup() {
super(GROUP_NAME);
}
Expand All @@ -67,7 +67,7 @@ private String getChainBaseModesString() {

private static String toString( final String[] modes ) {
// (not same as toString() because of argument!)

StringBuilder b = new StringBuilder();

if (modes.length > 0) b.append( modes[ 0 ] );
Expand Down Expand Up @@ -106,7 +106,8 @@ public Map<String, String> getComments() {
comments.put(CHAINBASEDMODES, "Defines the chain-based modes, seperated by commas" );
comments.put(CARAVAIL, "Defines whether car availability must be considered or not. A agent has no car only if it has no license, or never access to a car" );
comments.put(SINGLE_PROBA, "Defines the probability of changing a single trip for a unchained mode instead of subtour.");
comments.put(COORD_DISTANCE, "If greater than 0, subtours will also consider coordinates to be at the same location when smaller than set distance.");
comments.put(COORD_DISTANCE, "If greater than 0, activities that are closer than coordDistance, to each other, will be considered part of the same subtour." +
"i.e. if two activities are close to each other, the agent is allowed to use the same 'chain-based' vehicle for both subtours.");

{
StringBuilder msg = new StringBuilder("Only for backwards compatibility. Defines if only trips from modes list should change mode, or all trips. Options: ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public Map<String, String> getComments() {
map.put(HALFLIFE,
"this parameter enters the exponential and sigmoid formulas. May be an iteration or a share, i.e. 0.5 for halfLife at 50% of iterations.");
map.put(SHAPE_FACTOR, "see comment of parameter annealType.");
map.put(ANNEAL_TYPE, "options: linear, exponential, geometric, msa, sigmoid and disabled (no annealing)." + " sigmoid: 1/(1+e^(shapeFactor*(it - halfLife))); geometric: startValue * shapeFactor^it; msa: startValue / it^shapeFactor. Exponential: startValue / exp(it/halfLife)");
map.put(ANNEAL_TYPE, "options: linear, exponential, geometric, msa, sigmoid and disabled (no annealing). sigmoid: 1/(1+e^(shapeFactor*(it - halfLife))); geometric: startValue * shapeFactor^it; msa: startValue / it^shapeFactor. Exponential: startValue / exp(it/halfLife)");
map.put(ANNEAL_PARAM,
"list of config parameters that shall be annealed. Currently supported: globalInnovationRate, BrainExpBeta, PathSizeLogitBeta, learningRate. Default is globalInnovationRate");
map.put(SUBPOPULATION, "subpopulation to have the global innovation rate adjusted. Not applicable when annealing with other parameters.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@
* different mode given a list of possible modes.
*
* A subtour is a consecutive subset of a plan which starts and ends at the same link.
*
*
* Certain modes are considered only if the choice would not require some resource to appear
* out of thin air. For example, you can only drive your car back from work if you have previously parked it
* there. These are called chain-based modes.
*
*
* The assumption is that each chain-based mode requires one resource (car, bike, ...) and that this
* resource is initially positioned at home. Home is the location of the first activity in the plan.
*
* If the plan initially violates this constraint, this module may (!) repair it.
*
*
* If the plan initially violates this constraint, this module may (!) repair it.
*
* @author michaz
*
*/
Expand All @@ -51,7 +51,27 @@ public class SubtourModeChoice extends AbstractMultithreadedModule {
private final double probaForChangeSingleTripMode;
private final double coordDist;

public enum Behavior {fromAllModesToSpecifiedModes, fromSpecifiedModesToSpecifiedModes, betweenAllAndFewerConstraints}
public enum Behavior {
/**
* Allow agents to switch to specified modes from all other modes.
* This implies that agents might switch to a specified mode, but won't be able to switch back
* to their original mode. This option should not be used.
*/
@Deprecated
fromAllModesToSpecifiedModes,

/**
* Allow agents switching from one of a specified mode to another specified mode.
* Note, that agents that have an unclosed subtour, are not able to switch mode.
* If you have unclosed/open subtours in your data, consider using {@link #betweenAllAndFewerConstraints}.
*/
fromSpecifiedModesToSpecifiedModes,

/**
* Same as "fromSpecifiedModesToSpecifiedModes", but also allow agents with open subtours to switch modes.
*/
betweenAllAndFewerConstraints
}

private Behavior behavior = Behavior.fromSpecifiedModesToSpecifiedModes;

Expand Down Expand Up @@ -85,19 +105,19 @@ public SubtourModeChoice(GlobalConfigGroup globalConfigGroup,
this.probaForChangeSingleTripMode = probaForChangeSingleTripMode;
this.coordDist = coordDist;
}

@Deprecated // only use when backwards compatibility is needed. kai, may'18
public final void setBehavior ( Behavior behavior ) {
this.behavior = behavior ;
}

protected String[] getModes() {
return modes.clone();
}

@Override
public PlanAlgorithm getPlanAlgoInstance() {

final ChooseRandomLegModeForSubtour chooseRandomLegMode =
new ChooseRandomLegModeForSubtour(
TripStructureUtils.getRoutingModeIdentifier(),
Expand Down

0 comments on commit 7bb99a7

Please sign in to comment.