diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java index abdcc4521b3..68d6e327ed5 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; @@ -158,6 +159,36 @@ public String getType() { public void setType(String type) { throw new UnsupportedOperationException(); } + + @Override + public int getIterationCreated() { + throw new UnsupportedOperationException(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public Id getId() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } @Override public void setScore(Double score) { diff --git a/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java b/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java index 75da3189c3a..f1ba43fd0f5 100644 --- a/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java +++ b/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java @@ -285,4 +285,35 @@ else if (planElement instanceof Leg) { destPlan.score = srcPlan.getScore(); } + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + + } + + @Override + public Id getId() { + throw new UnsupportedOperationException(); + } + + @Override + public int getIterationCreated() { + throw new UnsupportedOperationException(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } + } diff --git a/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java b/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java index 12cd0a7023f..33ee42fbc42 100644 --- a/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java +++ b/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java @@ -219,6 +219,24 @@ public String getType() { public void setType(final String type) { this.type = type; } + + @Override + public Id getId() { return null; } + + @Override + public void setPlanId(Id planId) { /* nothing to do here */ } + + @Override + public int getIterationCreated() { return -1; } + + @Override + public void setIterationCreated(int iteration) { /* nothing to do here */ } + + @Override + public String getPlanMutator() { return null; } + + @Override + public void setPlanMutator(String planMutator) { /* nothing to do here */ } @Override public final List getPlanElements() { diff --git a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java index c1def957531..5d5990a7237 100644 --- a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java +++ b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; @@ -172,6 +173,24 @@ public String getType() { public void setType(String type) { this.delegate.setType(type); } + + @Override + public Id getId() { return null; } + + @Override + public void setPlanId(Id planId) { /* nothing to do here */ } + + @Override + public int getIterationCreated() { return -1; } + + @Override + public void setIterationCreated(int iteration) { /* nothing to do here */ } + + @Override + public String getPlanMutator() { return null; } + + @Override + public void setPlanMutator(String planMutator) { /* nothing to do here */ } @Override public Person getPerson() { diff --git a/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java b/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java index e06412da59e..6329b62a626 100644 --- a/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java +++ b/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java @@ -23,6 +23,8 @@ import java.util.List; import org.matsim.api.core.v01.Customizable; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.core.api.internal.MatsimPopulationObject; import org.matsim.utils.objectattributes.attributable.Attributable; @@ -34,7 +36,7 @@ * The only thing which is not "expected" in the same sense is the score. * */ -public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, Attributable { +public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, Attributable, Identifiable { public abstract List getPlanElements(); @@ -49,6 +51,18 @@ public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, A public abstract String getType(); public abstract void setType(final String type); + + public abstract void setPlanId(Id planId); + + public abstract Id getId(); + + public abstract int getIterationCreated(); + + public abstract void setIterationCreated(int iteration); + + public abstract String getPlanMutator(); + + public abstract void setPlanMutator(String planMutator); public abstract Person getPerson(); @@ -59,5 +73,6 @@ public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, A * you are using this method!. */ public abstract void setPerson(Person person); + } diff --git a/matsim/src/main/java/org/matsim/core/config/Config.java b/matsim/src/main/java/org/matsim/core/config/Config.java index ecff0be5678..e6ee24c19be 100644 --- a/matsim/src/main/java/org/matsim/core/config/Config.java +++ b/matsim/src/main/java/org/matsim/core/config/Config.java @@ -45,10 +45,10 @@ import org.matsim.core.config.groups.HouseholdsConfigGroup; import org.matsim.core.config.groups.LinkStatsConfigGroup; import org.matsim.core.config.groups.NetworkConfigGroup; +import org.matsim.core.config.groups.PlanInheritanceConfigGroup; import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.config.groups.ScoringConfigGroup; -import org.matsim.core.config.groups.RoutingConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup;import org.matsim.core.config.groups.PlansConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.config.groups.ScenarioConfigGroup; import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; @@ -123,7 +123,6 @@ public void addCoreModules() { this.modules.put(QSimConfigGroup.GROUP_NAME, new QSimConfigGroup()); this.modules.put(CountsConfigGroup.GROUP_NAME, new CountsConfigGroup()); - this.modules.put(ScoringConfigGroup.GROUP_NAME, new ScoringConfigGroup()); this.modules.put(NetworkConfigGroup.GROUP_NAME, new NetworkConfigGroup()); @@ -147,6 +146,7 @@ public void addCoreModules() { this.modules.put(TimeAllocationMutatorConfigGroup.GROUP_NAME, new TimeAllocationMutatorConfigGroup()); this.modules.put(VspExperimentalConfigGroup.GROUP_NAME, new VspExperimentalConfigGroup()); + this.modules.put(TransitConfigGroup.GROUP_NAME, new TransitConfigGroup()); @@ -168,6 +168,8 @@ public void addCoreModules() { this.modules.put(HermesConfigGroup.NAME, new HermesConfigGroup()); this.modules.put(ReplanningAnnealerConfigGroup.GROUP_NAME, new ReplanningAnnealerConfigGroup()); + + this.modules.put(PlanInheritanceConfigGroup.GROUP_NAME, new PlanInheritanceConfigGroup()); this.addConfigConsistencyChecker(new VspConfigConsistencyCheckerImpl()); this.addConfigConsistencyChecker(new UnmaterializedConfigGroupChecker()); @@ -488,6 +490,10 @@ public HermesConfigGroup hermes() { public ReplanningAnnealerConfigGroup replanningAnnealer() { return (ReplanningAnnealerConfigGroup) this.getModule(ReplanningAnnealerConfigGroup.GROUP_NAME); } + + public PlanInheritanceConfigGroup planInheritance() { + return (PlanInheritanceConfigGroup) this.getModule(PlanInheritanceConfigGroup.GROUP_NAME); + } // other: diff --git a/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java new file mode 100644 index 00000000000..b889df2fa15 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java @@ -0,0 +1,59 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2011 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.core.config.groups; + +import java.util.Map; + +import org.matsim.core.config.ReflectiveConfigGroup; + +/** + * @author awagner + */ +public final class PlanInheritanceConfigGroup extends ReflectiveConfigGroup { + + public static final String GROUP_NAME = "planInheritance"; + + private static final String ENABLED = "enabled"; + + private boolean enabled = false; + + public PlanInheritanceConfigGroup() { + super(GROUP_NAME); + } + + @Override + public Map getComments() { + Map comments = super.getComments(); + comments.put(ENABLED, "Specifies whether or not PlanInheritance Information should be tracked."); + return comments; + } + + + @StringSetter( ENABLED ) + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + + @StringGetter( ENABLED ) + public boolean getEnabled() { + return this.enabled; + } +} diff --git a/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java b/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java index bd64b584d38..fc5c43da358 100755 --- a/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java +++ b/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java @@ -37,6 +37,7 @@ import org.matsim.core.population.VspPlansCleanerModule; import org.matsim.core.replanning.StrategyManagerModule; import org.matsim.core.replanning.annealing.ReplanningAnnealer; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; import org.matsim.core.router.TripRouterModule; import org.matsim.core.router.costcalculators.TravelDisutilityModule; import org.matsim.core.scoring.functions.CharyparNagelScoringFunctionModule; @@ -76,6 +77,7 @@ public void install() { install(new VspPlansCleanerModule()); install(new SnapshotWritersModule()); install(new DependencyGraphModule()); + install(new PlanInheritanceModule()); // Comment by Tarek Chouaki. // To make sure the cache files used under ChartUtils are located in tmp folder in the output directory diff --git a/matsim/src/main/java/org/matsim/core/population/PlanImpl.java b/matsim/src/main/java/org/matsim/core/population/PlanImpl.java index 9c8957cd480..8477a25b7dc 100644 --- a/matsim/src/main/java/org/matsim/core/population/PlanImpl.java +++ b/matsim/src/main/java/org/matsim/core/population/PlanImpl.java @@ -27,17 +27,22 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Customizable; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Activity; 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.api.core.v01.population.PlanElement; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; import org.matsim.core.scenario.CustomizableUtils; import org.matsim.utils.objectattributes.attributable.Attributes; import org.matsim.utils.objectattributes.attributable.AttributesImpl; /* deliberately package */ final class PlanImpl implements Plan { + private Id id= null; + private ArrayList actsLegs = new ArrayList<>(); private Double score = null; @@ -121,6 +126,44 @@ public String getType() { public void setType(final String type) { this.type = type; } + + @Override + public Id getId() { + if(this.id!=null) + return this.id; + else { + if(this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_ID)!=null) + return Id.create(this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_ID).toString(),Plan.class); + else return null; + } + + } + + @Override + public void setPlanId(Id planId) { + this.getAttributes().putAttribute(PlanInheritanceModule.PLAN_ID, planId.toString()); + this.id = planId; + } + + @Override + public int getIterationCreated() { + return (int) this.getAttributes().getAttribute(PlanInheritanceModule.ITERATION_CREATED); + } + + @Override + public void setIterationCreated(int iteration) { + this.getAttributes().putAttribute(PlanInheritanceModule.ITERATION_CREATED, iteration); + } + + @Override + public String getPlanMutator() { + return (String) this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_MUTATOR); + } + + @Override + public void setPlanMutator(String planMutator) { + this.getAttributes().putAttribute(PlanInheritanceModule.PLAN_MUTATOR, planMutator); + } @Override public final List getPlanElements() { @@ -164,6 +207,8 @@ public final Map getCustomAttributes() { return this.customizableDelegate.getCustomAttributes(); } + + // public final void setLocked() { // for ( PlanElement pe : this.actsLegs ) { // if ( pe instanceof ActivityImpl ) { diff --git a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java index 739b890ce91..9a572ead6ca 100644 --- a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java +++ b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java @@ -381,6 +381,36 @@ public String getType() { public void setType(String type) { throw new UnsupportedOperationException(); } + + @Override + public Id getId() { + return this.delegate.getId(); + } + + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + } + + @Override + public int getIterationCreated() { + return this.delegate.getIterationCreated(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + return this.delegate.getPlanMutator(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } @Override public void addLeg(Leg leg) { diff --git a/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java b/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java index db83e5d4749..b9063b0878d 100644 --- a/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java +++ b/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.population.BasicPlan; import org.matsim.api.core.v01.population.HasPlansAndId; +import org.matsim.api.core.v01.population.Plan; import org.matsim.core.replanning.modules.GenericPlanStrategyModule; import org.matsim.core.replanning.selectors.PlanSelector; import org.matsim.core.replanning.selectors.RandomUnscoredPlanSelector; @@ -92,6 +93,13 @@ public void run(final HasPlansAndId person) { // set the working plan to a copy of the selected plan: plan = person.createCopyOfSelectedPlanAndMakeSelected(); + //Id is only set inside planInheritance -> if null planInheritance is disabled + if (plan instanceof Plan && ((Plan) plan).getId() != null) { + // add plan inheritance flags + ((Plan) plan).setIterationCreated(this.replanningContext.getIteration()); + ((Plan) plan).setPlanMutator(this.toString()); + } + // add new plan to container that contains the plans that are handled by this PlanStrategy: this.plans.add(plan); diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java new file mode 100644 index 00000000000..0a0a48f961d --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java @@ -0,0 +1,269 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.groups.ControllerConfigGroup.CompressionType; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.events.BeforeMobsimEvent; +import org.matsim.core.controler.events.ShutdownEvent; +import org.matsim.core.controler.events.StartupEvent; +import org.matsim.core.controler.listener.BeforeMobsimListener; +import org.matsim.core.controler.listener.ShutdownListener; +import org.matsim.core.controler.listener.StartupListener; +import org.matsim.core.population.PersonUtils; +import org.matsim.core.replanning.GenericPlanStrategy; +import org.matsim.core.replanning.StrategyManager; +import org.matsim.core.utils.io.IOUtils; + +import com.google.inject.Singleton; + +/** + * The core plan inheritance module responsible for + *
  • Initialization of initially read plans and default writers + *
  • Book-keeping, i.e. setting additional plan attributes not stored in the plan itself + *
  • Calculation default stats like the distribution of mutators among (selected) plans + * + * @author neuma, alex94263 + */ +@Singleton +public class PlanInheritanceModule extends AbstractModule implements StartupListener, BeforeMobsimListener, ShutdownListener { + + public static final String PLAN_ID = "planId"; + public static final String ITERATION_CREATED = "iterationCreated"; + public static final String PLAN_MUTATOR = "planMutator"; + + public static final String INITIAL_PLAN = "initialPlan"; + public static final String NONE = "NONE"; + + public static final String FILENAME_PLAN_INHERITANCE_RECORDS = "planInheritanceRecords"; + + long numberOfPlanInheritanceRecordsCreated = 0; + Map, PlanInheritanceRecord> planId2planInheritanceRecords = new ConcurrentHashMap<>(); + + PlanInheritanceRecordWriter planInheritanceRecordWriter; + private ArrayList strategies; + + private final Character DELIMITER = '\t'; + private BufferedWriter selectedPlanStrategyShareWriter; + private BufferedWriter planStrategyShareWriter; + + @Override + public void notifyStartup(StartupEvent event) { + // initialize all default writers + CompressionType compressionType = event.getServices().getConfig().controller().getCompressionType(); + this.planInheritanceRecordWriter = new PlanInheritanceRecordWriter(event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + ".csv", compressionType)); + this.strategies = this.getActiveStrategies(event.getServices().getConfig().replanning().getStrategySettings(), event.getServices().getStrategyManager()); + this.selectedPlanStrategyShareWriter = this.initializeDistributionWriter(this.strategies, event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + "_shares_selected.csv")); + this.planStrategyShareWriter = this.initializeDistributionWriter(this.strategies, event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + "_shares.csv")); + + // reset all plan attributes that might be present from a previously performed matsim run + for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) { + for (Plan plan : person.getPlans()) { + plan.setPlanId(Id.create(NONE, Plan.class)); + plan.setPlanMutator(INITIAL_PLAN); + plan.setIterationCreated(0); + } + } + } + + /** + * Retrieve all active plan strategies with their correct name in alphabetical order + */ + private ArrayList getActiveStrategies(Collection strategySettings, StrategyManager strategyManager) { + Set activeSubpopulations = new HashSet<>(); + for (StrategySettings strategySetting : strategySettings) { + activeSubpopulations.add(strategySetting.getSubpopulation()); + } + + Set planStrategiesNames = new HashSet<>(); + for (String subpopulation : activeSubpopulations) { + for (GenericPlanStrategy planStrategy : strategyManager.getStrategies(subpopulation)) { + planStrategiesNames.add(planStrategy.toString()); + } + } + + ArrayList strategies = new ArrayList<>(planStrategiesNames.size() + 1); + strategies.addAll(planStrategiesNames); + Collections.sort(strategies); + strategies.add(0, INITIAL_PLAN); + + return strategies; + } + + /** + * Initialize the writer with the active strategies + */ + private BufferedWriter initializeDistributionWriter(ArrayList strategies, String filename) { + + BufferedWriter planStrategyShareWriter = IOUtils.getBufferedWriter(filename); + + StringBuffer header = new StringBuffer(); + header.append("iteration"); header.append(DELIMITER); + for (int i = 0; i < strategies.size(); i++) { + if (i > 0) { + header.append(DELIMITER); + } + header.append(strategies.get(i)); + } + + try { + planStrategyShareWriter.write(header.toString()); + planStrategyShareWriter.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan strategy share writer!", e); + } + + return planStrategyShareWriter; + } + + @Override + public void notifyBeforeMobsim(BeforeMobsimEvent event) { + // check the plans of the population and all currently stored plan records - do the actual book-keeping + + Set> activePlanIds = new HashSet<>(); + Set> selectedPlanIds = new HashSet<>(); + + for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) { + for (Plan plan : person.getPlans()) { + + if (plan.getPlanMutator() == null) { + // initial plan - set initial plan defaults + plan.setPlanMutator(INITIAL_PLAN); + plan.setIterationCreated(event.getIteration()); + } + + if (plan.getIterationCreated() == event.getIteration()) { + // it's a new plan created in this iteration - create a new record + + PlanInheritanceRecord planInheritanceRecord = new PlanInheritanceRecord(); + planInheritanceRecord.setAgentId(person.getId()); + planInheritanceRecord.setPlanId(Id.create(Long.toString(++this.numberOfPlanInheritanceRecordsCreated, 36), Plan.class)); + planInheritanceRecord.setAncestorId(plan.getId()); //works because new plan is copy of old selected and attributes are copied -> thus current attribute plan id is old selected plan id + plan.setPlanId(planInheritanceRecord.getPlanId()); + planInheritanceRecord.setIterationCreated(plan.getIterationCreated()); + planInheritanceRecord.setMutatedBy(plan.getPlanMutator()); + + this.planId2planInheritanceRecords.put(planInheritanceRecord.getPlanId(), planInheritanceRecord); + } + + if (PersonUtils.isSelected(plan)) { + this.planId2planInheritanceRecords.get(plan.getId()).getIterationsSelected().add(event.getIteration()); + selectedPlanIds.add(plan.getId()); + } + + activePlanIds.add(plan.getId()); + } + } + + List> deletedPlans = new ArrayList<>(); + for (Id planId : this.planId2planInheritanceRecords.keySet()) { + if (!activePlanIds.contains(planId)) { + deletedPlans.add(planId); + } + } + + for (Id deletedPlanId : deletedPlans) { + PlanInheritanceRecord deletedPlanInheritanceRecord = this.planId2planInheritanceRecords.remove(deletedPlanId); + deletedPlanInheritanceRecord.setIterationRemoved(event.getIteration()); + this.planInheritanceRecordWriter.write(deletedPlanInheritanceRecord); + } + + this.planInheritanceRecordWriter.flush(); + + this.calculateAndWriteDistribution(event.getIteration(), this.strategies, this.planId2planInheritanceRecords, selectedPlanIds, this.selectedPlanStrategyShareWriter); + this.calculateAndWriteDistribution(event.getIteration(), this.strategies, this.planId2planInheritanceRecords, this.planId2planInheritanceRecords.keySet(), this.planStrategyShareWriter); + } + + /** + * Updates the default plan stats - namely the distribution of plan mutators based on the given plan ids. + */ + private void calculateAndWriteDistribution(int currentIteration, ArrayList strategies, Map, PlanInheritanceRecord> planId2planInheritanceRecords, Set> planIds, BufferedWriter writer) { + Map strategy2count = new HashMap<>(); + for (String strategyName : strategies) { + strategy2count.put(strategyName, new AtomicLong(0)); + } + for (Id planId : planIds) { + String mutatedBy = planId2planInheritanceRecords.get(planId).getMutatedBy(); + strategy2count.get(mutatedBy).incrementAndGet(); + } + long sum = strategy2count.values().stream().mapToLong(count -> count.get()).sum(); + StringBuffer line = new StringBuffer(); + line.append(currentIteration); + line.append(DELIMITER); + for (int i = 0; i < strategies.size(); i++) { + if (i > 0) { + line.append(DELIMITER); + } + line.append(String.valueOf(strategy2count.get(strategies.get(i)).doubleValue() / sum)); + } + try { + writer.write(line.toString()); + writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan strategy share writer!", e); + } + } + + @Override + public void notifyShutdown(ShutdownEvent event) { + // flush all pending plan inheritance records and close the readers + + for (PlanInheritanceRecord planInheritanceRecord : this.planId2planInheritanceRecords.values()) { + this.planInheritanceRecordWriter.write(planInheritanceRecord); + } + + this.planInheritanceRecordWriter.flush(); + this.planInheritanceRecordWriter.close(); + + try { + this.selectedPlanStrategyShareWriter.flush(); + this.selectedPlanStrategyShareWriter.close(); + this.planStrategyShareWriter.flush(); + this.planStrategyShareWriter.close(); + } catch (IOException e) { + new RuntimeException(e); + } + + this.planId2planInheritanceRecords.clear(); + } + + @Override + public void install() { + if (getConfig().planInheritance().getEnabled()) addControlerListenerBinding().to(PlanInheritanceModule.class); + } +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java new file mode 100644 index 00000000000..7212f337896 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java @@ -0,0 +1,129 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +import java.util.ArrayList; +import java.util.List; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; + +/** + * Data container storing the data of a single plan. + * + * @author neuma, alex94263 + */ +public class PlanInheritanceRecord { + + /** + * Id of the person that plan record belongs to. + */ + private Id agentId; + + /** + * The globally unique plan id. + */ + private Id planId; + + /** + * Id of the plan that this plan had been copied from before mutating. + */ + private Id ancestorId; + + /** + * The name of the strategy that altered this plan. + */ + private String mutatedBy; + + /** + * Iteration in which this plan had been created. May be {@linkplain PlanInheritanceModule#INITIAL_PLAN} if the plan had been in the choice-set from the very beginning. + */ + private int iterationCreated; + + /** + * Iteration in which the plan had been removed from the choice-set. + */ + private int iterationRemoved = -1; + + /** + * Collection of iterations this plan had been the selected plan. + * Initialize this with one since each plan is selected at least once. + */ + private List iterationsSelected = new ArrayList<>(1); + + public Id getPlanId() { + return planId; + } + + public void setPlanId(Id planId) { + this.planId = planId; + } + + public Id getAncestorId() { + return ancestorId; + } + + public void setAncestorId(Id ancestorId) { + this.ancestorId = ancestorId; + } + + public String getMutatedBy() { + return mutatedBy; + } + + public void setMutatedBy(String mutatedBy) { + this.mutatedBy = mutatedBy; + } + + public int getIterationCreated() { + return iterationCreated; + } + + public void setIterationCreated(int iterationCreated) { + this.iterationCreated = iterationCreated; + } + + public int getIterationRemoved() { + return iterationRemoved; + } + + public void setIterationRemoved(int iterationRemoved) { + this.iterationRemoved = iterationRemoved; + } + + public List getIterationsSelected() { + return iterationsSelected; + } + + public void setIterationsSelected(List iterationsSelected) { + this.iterationsSelected = iterationsSelected; + } + + public Id getAgentId() { + return agentId; + } + + public void setAgentId(Id agentId) { + this.agentId = agentId; + } + +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java new file mode 100644 index 00000000000..8d83d6ef1de --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java @@ -0,0 +1,96 @@ +package org.matsim.core.replanning.inheritance; + +import java.io.BufferedReader; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.utils.io.IOUtils; + +/** + * Writes {@linkplain PlanInheritanceRecord} to file in a fixed column sequence. + * + * @author alex94263 + */ +public class PlanInheritanceRecordReader { + + + private final String DELIMITER = "\t"; + private final BufferedReader reader; + + public PlanInheritanceRecordReader(String filename) { + this.reader = IOUtils.getBufferedReader(filename); + + + + } + public Map buildIdx(String[] header) { + Map lookup = new HashMap(); + for (int i=0; i read() { + List records = new ArrayList(); + try { + Map lookUp = buildIdx(reader.readLine().split(DELIMITER)); + String lineString = reader.readLine(); + while(lineString !=null) { + String[] line = lineString.split(DELIMITER); + PlanInheritanceRecord planInheritanceRecord = new PlanInheritanceRecord(); + planInheritanceRecord.setAgentId(Id.createPersonId(line[lookUp.get(PlanInheritanceRecordWriter.AGENT_ID)])); + planInheritanceRecord.setPlanId(Id.create(line[lookUp.get(PlanInheritanceRecordWriter.PLAN_ID)], Plan.class)); + planInheritanceRecord.setAncestorId(Id.create(line[lookUp.get(PlanInheritanceRecordWriter.ANCESTOR_ID)], Plan.class)); + planInheritanceRecord.setMutatedBy(line[lookUp.get(PlanInheritanceRecordWriter.MUTATED_BY)]); + planInheritanceRecord.setIterationCreated(Integer.parseInt(line[lookUp.get(PlanInheritanceRecordWriter.ITERATION_CREATED)])); + planInheritanceRecord.setIterationRemoved(Integer.parseInt(line[lookUp.get(PlanInheritanceRecordWriter.ITERATION_REMOVED)])); + String iterationsSelected = line[lookUp.get(PlanInheritanceRecordWriter.ITERATIONS_SELECTED)]; + planInheritanceRecord.setIterationsSelected(Arrays.asList(iterationsSelected.substring(1, iterationsSelected.length()-1).split(", ")).stream() + .map(Integer::parseInt) + .collect(Collectors.toList())); + records.add(planInheritanceRecord); + lineString = reader.readLine(); + } + return records; + + } catch (IOException e) { + throw new RuntimeException("Could not read the plan inheritance records!", e); + } + + + + } + + +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java new file mode 100644 index 00000000000..1400a915406 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java @@ -0,0 +1,99 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; + +import org.matsim.core.utils.io.IOUtils; + +/** + * Writes {@linkplain PlanInheritanceRecord} to file in a fixed column sequence. + * + * @author neuma, alex94263 + */ +public class PlanInheritanceRecordWriter { + + public static final String AGENT_ID = "agentId"; + public static final String PLAN_ID = "planId"; + public static final String ANCESTOR_ID = "ancestorId"; + public static final String MUTATED_BY = "mutatedBy"; + public static final String ITERATION_CREATED = "iterationCreated"; + public static final String ITERATION_REMOVED = "iterationRemoved"; + public static final String ITERATIONS_SELECTED = "iterationsSelected"; + + private final Character DELIMITER = '\t'; + private final BufferedWriter writer; + + public PlanInheritanceRecordWriter(String filename) { + this.writer = IOUtils.getBufferedWriter(filename); + + StringBuffer header = new StringBuffer(); + header.append(AGENT_ID); header.append(DELIMITER); + header.append(PLAN_ID); header.append(DELIMITER); + header.append(ANCESTOR_ID); header.append(DELIMITER); + header.append(MUTATED_BY); header.append(DELIMITER); + header.append(ITERATION_CREATED); header.append(DELIMITER); + header.append(ITERATION_REMOVED); header.append(DELIMITER); + header.append(ITERATIONS_SELECTED); + + try { + this.writer.write(header.toString()); + this.writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan inheritance writer!", e); + } + } + + public void write(PlanInheritanceRecord planInheritanceRecord) { + StringBuffer line = new StringBuffer(); + line.append(planInheritanceRecord.getAgentId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getPlanId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getAncestorId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getMutatedBy()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationCreated()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationRemoved()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationsSelected()); + + try { + this.writer.write(line.toString()); + this.writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan inheritance writer!", e); + } + } + + public void flush() { + try { + this.writer.flush(); + } catch (IOException e) { + throw new RuntimeException("Failed to flush plan inheritance writer!", e); + } + } + + public void close() { + try { + this.writer.close(); + } catch (IOException e) { + throw new RuntimeException("Failed to close plan inheritance writer!", e); + } + } +} diff --git a/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java b/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java new file mode 100644 index 00000000000..c15436d3391 --- /dev/null +++ b/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java @@ -0,0 +1,129 @@ +package org.matsim.core.replanning.planInheritance; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.Config; +import org.matsim.core.controler.Controler; +import org.matsim.core.population.algorithms.PersonAlgorithm; +import org.matsim.core.population.io.StreamingPopulationReader; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; +import org.matsim.core.replanning.inheritance.PlanInheritanceRecord; +import org.matsim.core.replanning.inheritance.PlanInheritanceRecordReader; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.testcases.MatsimTestUtils; + + + +public class PlanInheritanceTest { + /** + * @author alex94263 + */ + + + @Rule + public MatsimTestUtils util = new MatsimTestUtils(); + + @Test + public void testPlanInheritanceEnabled() throws IOException { + String outputDirectory = util.getOutputDirectory(); + + Config config = this.util.loadConfig("test/scenarios/equil/config_plans1.xml"); + config.controller().setLastIteration(10); + config.controller().setOutputDirectory(outputDirectory); + config.planInheritance().setEnabled(true); + Controler c = new Controler(config); + + c.run(); + File csv = new File(outputDirectory, "planInheritanceRecords.csv.gz"); + + assertThat(csv).exists(); + + + List personList = new ArrayList(); + final Scenario scenario = ScenarioUtils.createScenario(config); + StreamingPopulationReader spr = new StreamingPopulationReader(scenario); + spr.addAlgorithm(new PersonAlgorithm() { + @Override + public void run(Person person) { + personList.add(person); + } + }); + spr.readFile(util.getOutputDirectory()+"output_plans.xml.gz"); + for(Person per : personList) { + for(Plan p : per.getPlans()) { + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_MUTATOR)); + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.ITERATION_CREATED)); + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_ID)); + } + + } + + PlanInheritanceRecordReader reader = new PlanInheritanceRecordReader(outputDirectory+"planInheritanceRecords.csv.gz"); + List records = reader.read(); + assert(records.size()==2); + assert( ((PlanInheritanceRecord) records.get(0)).getAgentId().equals(Id.createPersonId("1"))); + assert( ((PlanInheritanceRecord) records.get(0)).getAncestorId().equals(Id.create("NONE",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(0)).getMutatedBy().equals(PlanInheritanceModule.INITIAL_PLAN)); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationCreated() == 0); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationRemoved() == -1); + assert( ((PlanInheritanceRecord) records.get(0)).getPlanId().equals(Id.create("1",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationsSelected().equals(Arrays.asList(0, 1, 2, 3, 4, 6, 7, 8, 9, 10))); + + assert( ((PlanInheritanceRecord) records.get(1)).getAgentId().equals(Id.createPersonId("1"))); + assert( ((PlanInheritanceRecord) records.get(1)).getAncestorId().equals(Id.create("1",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(1)).getMutatedBy().equals("RandomPlanSelector_ReRoute")); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationCreated() == 5); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationRemoved() == -1); + assert( ((PlanInheritanceRecord) records.get(1)).getPlanId().equals(Id.create("2",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationsSelected().equals(Arrays.asList(5))); + + + + } + + @Test + public void testPlanInheritanceDisabled() throws IOException { + String outputDirectory = util.getOutputDirectory(); + + Config config = this.util.loadConfig("test/scenarios/equil/config_plans1.xml"); + config.controller().setLastIteration(1); + config.controller().setOutputDirectory(outputDirectory); + Controler c = new Controler(config); + + c.run(); + + File csv = new File(outputDirectory, "planInheritanceRecords.csv.gz"); + + assertThat(csv).doesNotExist(); + + List personList = new ArrayList(); + final Scenario scenario = ScenarioUtils.createScenario(config); + StreamingPopulationReader spr = new StreamingPopulationReader(scenario); + spr.addAlgorithm(new PersonAlgorithm() { + @Override + public void run(Person person) { + personList.add(person); + } + }); + spr.readFile(util.getOutputDirectory()+"output_plans.xml.gz"); + for(Person per : personList) { + for(Plan p : per.getPlans()) { + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_MUTATOR)); + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.ITERATION_CREATED)); + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_ID)); + } + + } + } +}