Skip to content

Commit

Permalink
Merge branch 'master' into spatialprefilterdrt
Browse files Browse the repository at this point in the history
  • Loading branch information
nkuehnel authored Dec 6, 2024
2 parents 13bb763 + 78bf6b7 commit 3f07dd5
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
*/
public class MinCostFlowRebalancingStrategy implements RebalancingStrategy {

public static final String REBALANCING_ZONAL_TARGET_ALPHA = "rebalalpha";
public static final String REBALANCING_ZONAL_TARGET_BETA = "rebalbeta";

private final RebalancingTargetCalculator rebalancingTargetCalculator;
private final ZoneSystem zonalSystem;
private final Fleet fleet;
Expand Down Expand Up @@ -68,18 +71,37 @@ public List<Relocation> calcRelocations(Stream<? extends DvrpVehicle> rebalancab
return calculateMinCostRelocations(time, rebalancableVehiclesPerZone, soonIdleVehiclesPerZone);
}

private List<Relocation> calculateMinCostRelocations(double time,
List<Relocation> calculateMinCostRelocations(double time,
Map<Zone, List<DvrpVehicle>> rebalancableVehiclesPerZone,
Map<Zone, List<DvrpVehicle>> soonIdleVehiclesPerZone) {
ToDoubleFunction<Zone> targetFunction = rebalancingTargetCalculator.calculate(time,
rebalancableVehiclesPerZone);
var minCostFlowRebalancingStrategyParams = (MinCostFlowRebalancingStrategyParams)params.getRebalancingStrategyParams();
double alpha = minCostFlowRebalancingStrategyParams.targetAlpha;
double beta = minCostFlowRebalancingStrategyParams.targetBeta;

List<DrtZoneVehicleSurplus> vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> {
List<DrtZoneVehicleSurplus> vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> {
double alpha;
double beta;
int rebalancable = rebalancableVehiclesPerZone.getOrDefault(z, List.of()).size();
int soonIdle = soonIdleVehiclesPerZone.getOrDefault(z, List.of()).size();

switch (minCostFlowRebalancingStrategyParams.targetCoefficientSource) {
case Static -> {
alpha = minCostFlowRebalancingStrategyParams.targetAlpha;
beta = minCostFlowRebalancingStrategyParams.targetBeta;
}
case FromZoneAttribute -> {
alpha = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA);
beta = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA);
}
case FromZoneAttributeOrStatic -> {
Object alphaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA);
alpha = alphaAttribute == null ? minCostFlowRebalancingStrategyParams.targetAlpha : (Double) alphaAttribute;
Object betaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA);
beta = betaAttribute == null ? minCostFlowRebalancingStrategyParams.targetBeta : (Double) betaAttribute;
}
default -> throw new IllegalStateException("Unknown target coefficient source " + minCostFlowRebalancingStrategyParams.targetCoefficientSource);
}

int target = (int)Math.floor(alpha * targetFunction.applyAsDouble(z) + beta);
int surplus = Math.min(rebalancable + soonIdle - target, rebalancable);
return new DrtZoneVehicleSurplus(z, surplus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ public enum RebalancingTargetCalculatorType {
@NotNull
public RebalancingTargetCalculatorType rebalancingTargetCalculatorType = RebalancingTargetCalculatorType.EstimatedDemand;


public enum TargetCoefficientSource {
Static, FromZoneAttribute, FromZoneAttributeOrStatic
}

@Parameter
@Comment("Defines whether the alpha and beta of the target function should be"
+ " [Static] or [FromZoneAttribute] in which case alpha and beta can be provided per zone as an attribute."
+ " [FromZoneAttributeOrStatic] will fall back to the static coefficients if no attribute is found for a given zone."
+ " Use " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA + " and " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA
+ " to set values accordingly.")
public TargetCoefficientSource targetCoefficientSource = TargetCoefficientSource.Static;

public enum ZonalDemandEstimatorType {PreviousIterationDemand, None}

@Parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ static Network createNetwork() {
network.addNode(b);

Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b);
Link bc = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
network.addLink(ab);
network.addLink(bc);
network.addLink(ba);
return network;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
package org.matsim.contrib.drt.optimizer.rebalancing.mincostflow;

import com.google.common.collect.ImmutableMap;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.prep.PreparedPolygon;
import org.matsim.api.core.v01.Coord;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.events.PersonDepartureEvent;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.network.Node;
import org.matsim.contrib.common.zones.Zone;
import org.matsim.contrib.common.zones.ZoneImpl;
import org.matsim.contrib.common.zones.ZoneSystem;
import org.matsim.contrib.common.zones.ZoneSystemImpl;
import org.matsim.contrib.drt.analysis.zonal.MostCentralDrtZoneTargetLinkSelector;
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams;
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy;
import org.matsim.contrib.drt.optimizer.rebalancing.demandestimator.PreviousIterationDrtDemandEstimator;
import org.matsim.contrib.drt.optimizer.rebalancing.targetcalculator.DemandEstimatorAsTargetCalculator;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.dvrp.fleet.*;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.utils.geometry.GeometryUtils;

import java.util.*;

public class MinCostFlowRebalancingStrategyTest {

private static final int ESTIMATION_PERIOD = 1800;

private final Network network = createNetwork();

private final Link link1 = network.getLinks().get(Id.createLinkId("link_1"));
private final Link link2 = network.getLinks().get(Id.createLinkId("link_2"));

private final Zone zone1 = new ZoneImpl(
Id.create("zone_1", Zone.class),
new PreparedPolygon(GeometryUtils.createGeotoolsPolygon(
List.of(
new Coord(0, 0),
new Coord(0, 500),
new Coord(500, 500),
new Coord(500, 0),
new Coord(0, 0)
))), "dummy");

private final Zone zone2 = new ZoneImpl(
Id.create("zone_2", Zone.class),
new PreparedPolygon(GeometryUtils.createGeotoolsPolygon(
List.of(
new Coord(500, 0),
new Coord(500, 500),
new Coord(1000, 500),
new Coord(1000, 0),
new Coord(500, 0)
))), "dummy");

private final ZoneSystem zonalSystem = new ZoneSystemImpl(List.of(zone1, zone2), coord -> {
if (coord == link1.getToNode().getCoord()) {
return Optional.of(zone1);
} else if (coord == link2.getToNode().getCoord()) {
return Optional.of(zone2);
} else {
throw new RuntimeException();
}
}, network);


@Test
void testEmptyDemandAndTarget() {
PreviousIterationDrtDemandEstimator estimator = createEstimator();
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);

RebalancingParams rebalancingParams = new RebalancingParams();
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);

AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);

Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations.isEmpty());
}

@Test
void testDemandWithoutSurplus() {
PreviousIterationDrtDemandEstimator estimator = createEstimator();
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);

RebalancingParams rebalancingParams = new RebalancingParams();
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);

AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);

//time bin 0-1800
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(500, link2, TransportMode.drt));
estimator.handleEvent(departureEvent(1500, link1, TransportMode.drt));
estimator.reset(1);

Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations.isEmpty());
}

@Test
void testDemandWithSurplus() {
PreviousIterationDrtDemandEstimator estimator = createEstimator();
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);

RebalancingParams rebalancingParams = new RebalancingParams();
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);

AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);

// 3 expected trips in zone 1
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(300, link1, TransportMode.drt));
// 1 expected trip in zone 2
estimator.handleEvent(departureEvent(100, link2, TransportMode.drt));
estimator.reset(1);

Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();

// 4 vehicles in zone 1 (surplus = 1)
List<DvrpVehicle> rebalanceableVehiclesZone1 = new ArrayList<>();
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);

List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations.size()).isEqualTo(1);
Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId());

rebalanceableVehicles.clear();

// 5 vehicles in zone 1 (surplus = 2)
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1));
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);

//set alpha to 2 -> send two vehicles to zone 2
minCostFlowRebalancingStrategyParams.targetAlpha = 2.;
List<RebalancingStrategy.Relocation> relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations2.size()).isEqualTo(2);
Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId());
Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId());
}

@Test
void testDemandWithSurplusZoneBasedTargetRates() {

// set attributes
zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 0.);
zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.);
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 1.);
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.);


PreviousIterationDrtDemandEstimator estimator = createEstimator();
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);

RebalancingParams rebalancingParams = new RebalancingParams();
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
minCostFlowRebalancingStrategyParams.targetCoefficientSource = MinCostFlowRebalancingStrategyParams.TargetCoefficientSource.FromZoneAttribute;
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);

AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);

// 3 expected trips in zone 1
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
estimator.handleEvent(departureEvent(300, link1, TransportMode.drt));
// 1 expected trip in zone 2
estimator.handleEvent(departureEvent(100, link2, TransportMode.drt));
estimator.reset(1);

Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();

// 4 vehicles in zone 1 (surplus = 1)
List<DvrpVehicle> rebalanceableVehiclesZone1 = new ArrayList<>();
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);

List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations.size()).isEqualTo(1);
Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId());

rebalanceableVehicles.clear();

// 5 vehicles in zone 1 (surplus = 2)
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1));
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);

//set alpha to 2 -> send two vehicles to zone 2
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 2.);
List<RebalancingStrategy.Relocation> relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
Assertions.assertThat(relocations2.size()).isEqualTo(2);
Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId());
Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId());
}

private DvrpVehicleImpl getDvrpVehicle(Id<DvrpVehicle> id, Link link) {
return new DvrpVehicleImpl(
ImmutableDvrpVehicleSpecification.newBuilder()
.id(id)
.capacity(0)
.serviceBeginTime(0)
.serviceEndTime(0)
.startLinkId(link.getId())
.build(), link);
}

private static Fleet createEmptyFleet() {
return () -> ImmutableMap.<Id<DvrpVehicle>, DvrpVehicle>builder().build();
}


private PreviousIterationDrtDemandEstimator createEstimator() {
RebalancingParams rebalancingParams = new RebalancingParams();
rebalancingParams.interval = ESTIMATION_PERIOD;

DrtConfigGroup drtConfigGroup = new DrtConfigGroup();
drtConfigGroup.addParameterSet(rebalancingParams);

return new PreviousIterationDrtDemandEstimator(zonalSystem, drtConfigGroup, ESTIMATION_PERIOD);
}

private PersonDepartureEvent departureEvent(double time, Link link, String mode) {
return new PersonDepartureEvent(time, null, link.getId(), mode, mode);
}

static Network createNetwork() {
Network network = NetworkUtils.createNetwork();
Node a = network.getFactory().createNode(Id.createNodeId("a"), new Coord(0,0));
Node b = network.getFactory().createNode(Id.createNodeId("b"), new Coord(500,0));
network.addNode(a);
network.addNode(b);

Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b);
Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
network.addLink(ab);
network.addLink(ba);
return network;
}
}

0 comments on commit 3f07dd5

Please sign in to comment.