Skip to content

Commit

Permalink
add more functionality for advanced scoring options
Browse files Browse the repository at this point in the history
  • Loading branch information
rakow committed Feb 28, 2024
1 parent e0cd228 commit 6e32c51
Show file tree
Hide file tree
Showing 14 changed files with 641 additions and 297 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
import org.matsim.prepare.population.*;
import org.matsim.run.Activities;
import org.matsim.run.OpenBerlinScenario;
import org.matsim.run.scoring.VspScoringModule;
import org.matsim.run.scoring.AdvancedScoringModule;
import org.matsim.simwrapper.SimWrapperConfigGroup;
import org.matsim.simwrapper.SimWrapperModule;
import org.matsim.smallScaleCommercialTrafficGeneration.GenerateSmallScaleCommercialTrafficDemand;
Expand Down Expand Up @@ -399,7 +399,7 @@ public void install() {
});

controler.addOverridingModule(new OpenBerlinScenario.TravelTimeBinding());
controler.addOverridingModule(new VspScoringModule());
controler.addOverridingModule(new AdvancedScoringModule());
controler.addOverridingModule(new SimWrapperModule());
}

Expand Down
177 changes: 177 additions & 0 deletions src/main/java/org/matsim/run/scoring/AdvancedScoringConfigGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.matsim.run.scoring;

import org.matsim.core.config.ConfigGroup;
import org.matsim.core.config.ReflectiveConfigGroup;
import org.matsim.utils.objectattributes.attributable.Attributes;

import java.util.*;

/**
* Stores scoring parameters for {@link AdvancedScoringModule}.
*/
public final class AdvancedScoringConfigGroup extends ReflectiveConfigGroup {

private static final String GROUP_NAME = "advancedScoring";

private final List<ScoringParameters> scoringParameters = new ArrayList<>();

@Parameter
@Comment("The distance groups if marginal utility of distance is adjusted. In meters.")
public List<Integer> distGroups;

@Parameter
@Comment("Enable income dependent marginal utility of money.")
public boolean incomeDependent = true;


// TODO: maybe option to re-assign variations or use them from attributes

public AdvancedScoringConfigGroup() {
super(GROUP_NAME);
}

/**
* Return the defined scoring parameters.
*/
public List<ScoringParameters> getScoringParameters() {
return Collections.unmodifiableList(scoringParameters);
}

@Override
public ConfigGroup createParameterSet(String type) {
if (type.equals(ScoringParameters.GROUP_NAME)) {
return new ScoringParameters();
} else {
throw new IllegalArgumentException("Unsupported parameter set type: " + type);
}
}

@Override
public void addParameterSet(ConfigGroup set) {
if (set instanceof ScoringParameters p) {
super.addParameterSet(set);
scoringParameters.add(p);
} else {
throw new IllegalArgumentException("Unsupported parameter set class: " + set);
}
}

public static final class ScoringParameters extends ReflectiveConfigGroup {

private static final String GROUP_NAME = "scoringParameters";

/**
* Params per mode.
*/
private final Map<String, ModeParams> modeParams = new HashMap<>();

// TODO: option to match as list

public ScoringParameters() {
super(GROUP_NAME, true);
}

/**
* Checks if the given attributes match the config. If true these parameters are applicable to tbe object.
*/
public boolean matchObject(Attributes attr) {

// TODO: special case int <-> double and numbers
// boolean values
// allow lists

// TODO: matching is not yet correct
// TODO: add test

for (Map.Entry<String, String> e : this.getParams().entrySet()) {
// might be null if not defined
Object objValue = attr.getAttribute(e.getKey());

// compare as string
if (!Objects.toString(objValue).equals(e.getValue()))
return false;

}

return true;
}

public Map<String, ModeParams> getModeParams() {
return modeParams;
}

@Override
public ConfigGroup createParameterSet(final String type) {
return switch (type) {
case ModeParams.GROUP_NAME -> new ModeParams();
default -> throw new IllegalArgumentException(type);
};
}

@Override
public void addParameterSet(ConfigGroup set) {
if (set instanceof ModeParams p) {
super.addParameterSet(set);
modeParams.put(p.mode, p);
} else {
throw new IllegalArgumentException("Unsupported parameter set class: " + set);
}
}

/**
* Retrieve mode parameters.
*/
public ModeParams getOrCreateModeParams(String mode) {
if (!modeParams.containsKey(mode)) {
ModeParams p = new ModeParams();
p.mode = mode;

addParameterSet(p);
return p;
}

return modeParams.get(mode);
}

}

/**
* Stores mode specific parameters and also attributes to whom to apply this specification.
*/
public static final class ModeParams extends ReflectiveConfigGroup {

private static final String GROUP_NAME = "modeParams";

@Parameter
@Comment("The mode for which the parameters are defined.")
public String mode;

@Parameter
@Comment("[utils/leg] alternative-specific constant.")
public double deltaConstant;

@Parameter
@Comment("Variation of the constant across individuals.")
public VariationType varConstant = VariationType.fixed;

@Parameter
@Comment("[utils/day] if the mode is used at least once.")
public double deltaDailyConstant;

@Parameter
@Comment("Variation of the daily constant across individuals.")
public VariationType varDailyConstant = VariationType.fixed;

@Parameter
@Comment("[utils/m] for each distance group.")
public List<Double> deltaUtilsDistance;

public ModeParams() {
super(GROUP_NAME);
}
}

public enum VariationType {
fixed, normal
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* Same as {@link org.matsim.core.scoring.functions.CharyparNagelScoringFunctionFactory} but with {@link PiecewiseLinearlLegScoring}.
*/
public class VspScoringFunctionFactory implements ScoringFunctionFactory {
public class AdvancedScoringFunctionFactory implements ScoringFunctionFactory {

@Inject
private Config config;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/org/matsim/run/scoring/AdvancedScoringModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.matsim.run.scoring;

import com.google.inject.Key;
import jakarta.inject.Singleton;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.AbstractModule;
import org.matsim.core.controler.listener.ControlerListener;
import org.matsim.core.scoring.functions.ScoringParametersForPerson;

/**
* Module to bind components needed for advanced scoring functionality configured by {@link AdvancedScoringConfigGroup}.
*/
public class AdvancedScoringModule extends AbstractModule {

@Override
public void install() {

ConfigUtils.addOrGetModule(getConfig(), AdvancedScoringConfigGroup.class);

bind(ScoringParametersForPerson.class).to(IndividualPersonScoringParameters.class).in(Singleton.class);

addControlerListenerBinding().to(AdvancedScoringOutputWriter.class).in(Singleton.class);

bindScoringFunctionFactory().to(AdvancedScoringFunctionFactory.class).in(Singleton.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.matsim.run.scoring;

import com.google.inject.Inject;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.controler.OutputDirectoryHierarchy;
import org.matsim.core.controler.events.IterationEndsEvent;
import org.matsim.core.controler.listener.IterationEndsListener;
import org.matsim.core.scoring.functions.ScoringParametersForPerson;
import org.matsim.core.utils.io.IOUtils;

import java.io.IOException;
import java.io.UncheckedIOException;

/**
* This class writes person specific information from {@link IndividualPersonScoringParameters} to the output.
*/
public class AdvancedScoringOutputWriter implements IterationEndsListener {


@Inject
private ScoringParametersForPerson scoring;

private boolean outputWritten = false;

@Override
public void notifyIterationEnds(IterationEndsEvent event) {

if (outputWritten)
return;

if (!(scoring instanceof IndividualPersonScoringParameters params))
return;

OutputDirectoryHierarchy io = event.getServices().getControlerIO();

String output = io.getOutputFilename("person_util_variations.csv");

// Write scoring information for each person
try (CSVPrinter csv = new CSVPrinter(IOUtils.getBufferedWriter(output), CSVFormat.DEFAULT)) {

csv.print("person");
csv.printRecord(params.header);

for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) {

Object2DoubleMap<String> values = params.info.get(person.getId());
if (values == null) {
continue;
}

csv.print(person.getId());
for (String s : params.header) {
csv.print(values.getDouble(s));
}
csv.println();
}

} catch (IOException e) {
throw new UncheckedIOException(e);
}

params.header.clear();
params.info.clear();

outputWritten = true;
}
}

This file was deleted.

15 changes: 15 additions & 0 deletions src/main/java/org/matsim/run/scoring/DistanceGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.matsim.run.scoring;

/**
* Store distance group
*
* @param dist lower bound for distance group
* @param constant added constant
* @param util_m utility per meter, i.e. slope of linear function
*/
record DistanceGroup(double dist, double constant, double util_m) implements Comparable<DistanceGroup> {
@Override
public int compareTo(DistanceGroup o) {
return Double.compare(dist, o.dist);
}
}
Loading

0 comments on commit 6e32c51

Please sign in to comment.