Skip to content

Commit

Permalink
added different plan builders, revert some changes in plan builder
Browse files Browse the repository at this point in the history
  • Loading branch information
rakow committed Mar 5, 2024
1 parent 629f62c commit 3ed313e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 56 deletions.
40 changes: 40 additions & 0 deletions src/main/java/org/matsim/prepare/choices/BestKPlanGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.matsim.prepare.choices;

import org.jetbrains.annotations.Nullable;
import org.matsim.modechoice.CandidateGenerator;
import org.matsim.modechoice.PlanCandidate;
import org.matsim.modechoice.PlanModel;
import org.matsim.modechoice.search.TopKChoicesGenerator;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* Keeps selected plan as the first candidate and adds the rest with best options.
*/
public class BestKPlanGenerator implements CandidateGenerator {
private final int topK;
private final TopKChoicesGenerator generator;

public <T> BestKPlanGenerator(int topK, TopKChoicesGenerator generator) {
this.topK = topK;
this.generator = generator;
}

@Override
public List<PlanCandidate> generate(PlanModel planModel, @Nullable Set<String> consideredModes, @Nullable boolean[] mask) {

List<String[]> chosen = new ArrayList<>();
chosen.add(planModel.getCurrentModes());

// Chosen candidate from data
PlanCandidate existing = generator.generatePredefined(planModel, chosen).get(0);

List<PlanCandidate> result = new ArrayList<>();
result.add(existing);
result.addAll(generator.generate(planModel, consideredModes, mask));

return result.stream().distinct().limit(topK).toList();
}
}
52 changes: 15 additions & 37 deletions src/main/java/org/matsim/prepare/choices/ComputePlanChoices.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import org.matsim.core.utils.timing.TimeInterpretation;
import org.matsim.modechoice.*;
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.modechoice.search.TopKChoicesGenerator;
import picocli.CommandLine;

Expand All @@ -50,7 +50,6 @@ public class ComputePlanChoices implements MATSimAppCommand, PersonAlgorithm {
* Rows for the result table.
*/
private final Queue<List<Object>> rows = new ConcurrentLinkedQueue<>();
private final MainModeIdentifier mmi = new DefaultAnalysisMainModeIdentifier();
@CommandLine.Mixin
private ScenarioOptions scenario;
@CommandLine.Mixin
Expand All @@ -67,6 +66,8 @@ public class ComputePlanChoices implements MATSimAppCommand, PersonAlgorithm {
private Path output;
private ThreadLocal<Ctx> thread;
private ProgressBar pb;
private MainModeIdentifier mmi = new DefaultAnalysisMainModeIdentifier();


public static void main(String[] args) {
new ComputePlanChoices().execute(args);
Expand All @@ -93,10 +94,10 @@ public Integer call() throws Exception {
Controler controler = this.scenario.createControler();

controler.addOverridingModule(InformedModeChoiceModule.newBuilder()
.withFixedCosts(FixedCostsEstimator.DailyConstant.class, "car")
.withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.ConsiderIfCarAvailable.class, "car")
.withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.AlwaysAvailable.class, "bike", "walk", "pt", "ride")
.withConstraint(RelaxedMassConservationConstraint.class)
.withActivityEstimator(DefaultActivityEstimator.class)
.build());

InformedModeChoiceConfigGroup imc = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class);
Expand All @@ -110,26 +111,23 @@ public Integer call() throws Exception {

PlanBuilder.addVehiclesToScenario(injector.getInstance(Scenario.class));

Population population = PopulationUtils.createPopulation(config);

PlanBuilder builder = new PlanBuilder(shp, new ShpOptions(facilities, null, null), population.getFactory());

builder.createPlans(input).forEach(population::addPerson);

thread = ThreadLocal.withInitial(() ->
new Ctx(
new PlanRouter(injector.getInstance(TripRouter.class),
TimeInterpretation.create(PlansConfigGroup.ActivityDurationInterpretation.tryEndTimeThenDuration,
PlansConfigGroup.TripDurationHandling.ignoreDelays)),
new DiversePlanCandidateGenerator(topK, injector.getInstance(TopKChoicesGenerator.class)),
new PseudoScorer(injector, population)
)
new BestKPlanGenerator(topK, injector.getInstance(TopKChoicesGenerator.class)))
);

Population population = PopulationUtils.createPopulation(config);

PlanBuilder builder = new PlanBuilder(shp, new ShpOptions(facilities, null, null), population.getFactory());

builder.createPlans(input).forEach(population::addPerson);

pb = new ProgressBar("Computing plan choices", population.getPersons().size());

ParallelPersonAlgorithmUtils.run(population, Runtime.getRuntime().availableProcessors() - 1, this);
ParallelPersonAlgorithmUtils.run(population, Runtime.getRuntime().availableProcessors(), this);

pb.close();

Expand All @@ -149,8 +147,6 @@ public Integer call() throws Exception {
header.add(String.format("plan_%d_%s_ride_hours", i, mode));
header.add(String.format("plan_%d_%s_n_switches", i, mode));
}

header.add(String.format("plan_%d_act_util", i));
header.add(String.format("plan_%d_valid", i));
}

Expand Down Expand Up @@ -191,28 +187,18 @@ public void run(Person person) {
// TODO: apply method might also shift times to better fit the schedule
candidate.applyTo(plan);
ctx.router.run(plan);

row.addAll(convert(plan, ctx.scorer));
row.addAll(convert(plan));
// available choice
row.add(1);
i++;
}

for (int j = i; j < topK; j++) {
row.addAll(convert(null, ctx.scorer));
row.addAll(convert(null));
// not available
row.add(0);
}

// Clean up routes, which use a lot of memory
plan.getPlanElements().forEach(el -> {
if (el instanceof Leg leg) {
leg.setRoute(null);
}
});

model.reset();

rows.add(row);

pb.step();
Expand All @@ -221,22 +207,19 @@ public void run(Person person) {
/**
* Create one csv entry row for a plan.
*/
private List<Object> convert(@Nullable Plan plan, PseudoScorer scorer) {
private List<Object> convert(@Nullable Plan plan) {

List<Object> row = new ArrayList<>();
if (plan == null) {
for (String ignored : modes) {
row.addAll(List.of(0, 0, 0, 0, 0));
}

row.add(0);

return row;
}

Map<String, ModeStats> stats = collect(plan);


for (String mode : modes) {
ModeStats modeStats = stats.get(mode);
row.add(modeStats.usage);
Expand All @@ -246,11 +229,6 @@ private List<Object> convert(@Nullable Plan plan, PseudoScorer scorer) {
row.add(modeStats.numSwitches);
}

// pseudo scorer commented out currently
// Object2DoubleMap<String> scores = scorer.score(plan);
// row.add(scores.getDouble("score"));
row.add(0);


return row;
}
Expand Down Expand Up @@ -294,6 +272,6 @@ private Map<String, ModeStats> collect(Plan plan) {
private record ModeStats(int usage, double travelTime, double travelDistance, double rideTime, long numSwitches) {
}

private record Ctx(PlanRouter router, DiversePlanCandidateGenerator generator, PseudoScorer scorer) {
private record Ctx(PlanRouter router, CandidateGenerator generator) {
}
}
24 changes: 5 additions & 19 deletions src/main/java/org/matsim/prepare/choices/PlanBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
import org.matsim.api.core.v01.population.Plan;
import org.matsim.api.core.v01.population.PopulationFactory;
import org.matsim.application.options.ShpOptions;
import org.matsim.application.prepare.population.SplitActivityTypesDuration;
import org.matsim.core.population.PersonUtils;
import org.matsim.core.population.PopulationUtils;
import org.matsim.core.utils.geometry.CoordUtils;
import org.matsim.prepare.population.InitLocationChoice;
import org.matsim.vehicles.Vehicle;
Expand Down Expand Up @@ -54,8 +52,6 @@ public class PlanBuilder {
*/
private final Object2LongMap<Location> features = new Object2LongOpenHashMap<>();

private final SplitActivityTypesDuration splitDuration = new SplitActivityTypesDuration();

private final SplittableRandom rnd = new SplittableRandom();

private final PopulationFactory f;
Expand Down Expand Up @@ -150,7 +146,6 @@ private Person createPerson(String id, int seq, List<Row> trips) {

Person person = f.createPerson(Id.createPersonId(id + "_" + seq));

PopulationUtils.putSubpopulation(person, "person");
PersonUtils.setCarAvail(person, trips.get(0).getInt("p_age") >= 18 ? "always" : "never");

VehicleUtils.insertVehicleIdsIntoPersonAttributes(person, Map.of("car", Id.createVehicleId("car"),
Expand All @@ -163,19 +158,14 @@ private Person createPerson(String id, int seq, List<Row> trips) {
if (trip == null)
return null;

// source-destination purpose
String sd = trips.get(0).getString("sd_group");

Activity act = f.createActivityFromCoord(sd.startsWith("home") ? "home": "other", trip.first());
int departure = trips.get(0).getInt("departure") * 60;
act.setEndTime(departure);
Activity act = f.createActivityFromCoord("act", trip.first());
act.setEndTime(trips.get(0).getInt("departure") * 60);
act.getAttributes().putAttribute("n", trips.get(0).getInt("n"));

plan.addActivity(act);
plan.addLeg(f.createLeg(trips.get(0).getString("main_mode")));

act = f.createActivityFromCoord(trips.get(0).getString("purpose"), trip.second());
act.setStartTime(departure + trips.get(0).getInt("duration") * 60);
act = f.createActivityFromCoord("act", trip.second());
plan.addActivity(act);

for (int i = 1; i < trips.size(); i++) {
Expand All @@ -186,14 +176,12 @@ private Person createPerson(String id, int seq, List<Row> trips) {
if (dest == null)
return null;

departure = row.getInt("departure") * 60;
act.setEndTime(departure);
act.setEndTime(row.getInt("departure") * 60);
act.getAttributes().putAttribute("n", row.getInt("n"));

plan.addLeg(f.createLeg(row.getString("main_mode")));

act = f.createActivityFromCoord(row.getString("purpose"), dest);
act.setStartTime(departure + row.getInt("duration") * 60);
act = f.createActivityFromCoord("act", dest);
plan.addActivity(act);
}

Expand All @@ -202,8 +190,6 @@ private Person createPerson(String id, int seq, List<Row> trips) {
person.addPlan(plan);
person.setSelectedPlan(plan);

splitDuration.run(person);

return person;
}

Expand Down
53 changes: 53 additions & 0 deletions src/main/java/org/matsim/prepare/choices/RandomPlanGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.matsim.prepare.choices;

import org.jetbrains.annotations.Nullable;
import org.matsim.modechoice.CandidateGenerator;
import org.matsim.modechoice.ModeEstimate;
import org.matsim.modechoice.PlanCandidate;
import org.matsim.modechoice.PlanModel;
import org.matsim.modechoice.search.TopKChoicesGenerator;

import java.util.*;

/**
* Generates random candidates.
*/
public class RandomPlanGenerator implements CandidateGenerator {

private final int topK;
private final TopKChoicesGenerator gen;
private final SplittableRandom rnd = new SplittableRandom(0);

public RandomPlanGenerator(int topK, TopKChoicesGenerator generator) {
this.topK = topK;
this.gen = generator;
}

@Override
public List<PlanCandidate> generate(PlanModel planModel, @Nullable Set<String> consideredModes, @Nullable boolean[] mask) {

List<String[]> chosen = new ArrayList<>();
chosen.add(planModel.getCurrentModes());

// Chosen candidate from data
PlanCandidate existing = gen.generatePredefined(planModel, chosen).get(0);

// This changes the internal state to randomize the estimates
for (Map.Entry<String, List<ModeEstimate>> entry : planModel.getEstimates().entrySet()) {
for (ModeEstimate est : entry.getValue()) {
double[] utils = est.getEstimates();
if (utils != null)
for (int i = 0; i < utils.length; i++) {
utils[i] = -rnd.nextDouble();
}
}
}

List<PlanCandidate> result = new ArrayList<>();
result.add(existing);
result.addAll(gen.generate(planModel, consideredModes, mask));

return result.stream().distinct().limit(topK).toList();
}

}

0 comments on commit 3ed313e

Please sign in to comment.