Skip to content

Commit 3bcd816

Browse files
Merge pull request #121 from moia-oss/zoneBasedRebalancing
Deploy spatial rebalancing
2 parents fd3d3a2 + 38cdc52 commit 3bcd816

File tree

4 files changed

+320
-6
lines changed

4 files changed

+320
-6
lines changed

contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategy.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
*/
4141
public class MinCostFlowRebalancingStrategy implements RebalancingStrategy {
4242

43+
public static final String REBALANCING_ZONAL_TARGET_ALPHA = "rebalalpha";
44+
public static final String REBALANCING_ZONAL_TARGET_BETA = "rebalbeta";
45+
4346
private final RebalancingTargetCalculator rebalancingTargetCalculator;
4447
private final ZoneSystem zonalSystem;
4548
private final Fleet fleet;
@@ -68,18 +71,37 @@ public List<Relocation> calcRelocations(Stream<? extends DvrpVehicle> rebalancab
6871
return calculateMinCostRelocations(time, rebalancableVehiclesPerZone, soonIdleVehiclesPerZone);
6972
}
7073

71-
private List<Relocation> calculateMinCostRelocations(double time,
74+
List<Relocation> calculateMinCostRelocations(double time,
7275
Map<Zone, List<DvrpVehicle>> rebalancableVehiclesPerZone,
7376
Map<Zone, List<DvrpVehicle>> soonIdleVehiclesPerZone) {
7477
ToDoubleFunction<Zone> targetFunction = rebalancingTargetCalculator.calculate(time,
7578
rebalancableVehiclesPerZone);
7679
var minCostFlowRebalancingStrategyParams = (MinCostFlowRebalancingStrategyParams)params.getRebalancingStrategyParams();
77-
double alpha = minCostFlowRebalancingStrategyParams.targetAlpha;
78-
double beta = minCostFlowRebalancingStrategyParams.targetBeta;
7980

80-
List<DrtZoneVehicleSurplus> vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> {
81+
List<DrtZoneVehicleSurplus> vehicleSurpluses = zonalSystem.getZones().values().stream().map(z -> {
82+
double alpha;
83+
double beta;
8184
int rebalancable = rebalancableVehiclesPerZone.getOrDefault(z, List.of()).size();
8285
int soonIdle = soonIdleVehiclesPerZone.getOrDefault(z, List.of()).size();
86+
87+
switch (minCostFlowRebalancingStrategyParams.targetCoefficientSource) {
88+
case Static -> {
89+
alpha = minCostFlowRebalancingStrategyParams.targetAlpha;
90+
beta = minCostFlowRebalancingStrategyParams.targetBeta;
91+
}
92+
case FromZoneAttribute -> {
93+
alpha = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA);
94+
beta = (Double) z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA);
95+
}
96+
case FromZoneAttributeOrStatic -> {
97+
Object alphaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_ALPHA);
98+
alpha = alphaAttribute == null ? minCostFlowRebalancingStrategyParams.targetAlpha : (Double) alphaAttribute;
99+
Object betaAttribute = z.getAttributes().getAttribute(REBALANCING_ZONAL_TARGET_BETA);
100+
beta = betaAttribute == null ? minCostFlowRebalancingStrategyParams.targetBeta : (Double) betaAttribute;
101+
}
102+
default -> throw new IllegalStateException("Unknown target coefficient source " + minCostFlowRebalancingStrategyParams.targetCoefficientSource);
103+
}
104+
83105
int target = (int)Math.floor(alpha * targetFunction.applyAsDouble(z) + beta);
84106
int surplus = Math.min(rebalancable + soonIdle - target, rebalancable);
85107
return new DrtZoneVehicleSurplus(z, surplus);

contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/mincostflow/MinCostFlowRebalancingStrategyParams.java

+13
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ public enum RebalancingTargetCalculatorType {
5656
@NotNull
5757
public RebalancingTargetCalculatorType rebalancingTargetCalculatorType = RebalancingTargetCalculatorType.EstimatedDemand;
5858

59+
60+
public enum TargetCoefficientSource {
61+
Static, FromZoneAttribute, FromZoneAttributeOrStatic
62+
}
63+
64+
@Parameter
65+
@Comment("Defines whether the alpha and beta of the target function should be"
66+
+ " [Static] or [FromZoneAttribute] in which case alpha and beta can be provided per zone as an attribute."
67+
+ " [FromZoneAttributeOrStatic] will fall back to the static coefficients if no attribute is found for a given zone."
68+
+ " Use " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA + " and " + MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA
69+
+ " to set values accordingly.")
70+
public TargetCoefficientSource targetCoefficientSource = TargetCoefficientSource.Static;
71+
5972
public enum ZonalDemandEstimatorType {PreviousIterationDemand, None}
6073

6174
@Parameter

contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/PreviousIterationDrtDemandEstimatorTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ static Network createNetwork() {
199199
network.addNode(b);
200200

201201
Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b);
202-
Link bc = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
202+
Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
203203
network.addLink(ab);
204-
network.addLink(bc);
204+
network.addLink(ba);
205205
return network;
206206
}
207207
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package org.matsim.contrib.drt.optimizer.rebalancing.mincostflow;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import org.assertj.core.api.Assertions;
5+
import org.junit.jupiter.api.Test;
6+
import org.locationtech.jts.geom.prep.PreparedPolygon;
7+
import org.matsim.api.core.v01.Coord;
8+
import org.matsim.api.core.v01.Id;
9+
import org.matsim.api.core.v01.TransportMode;
10+
import org.matsim.api.core.v01.events.PersonDepartureEvent;
11+
import org.matsim.api.core.v01.network.Link;
12+
import org.matsim.api.core.v01.network.Network;
13+
import org.matsim.api.core.v01.network.Node;
14+
import org.matsim.contrib.common.zones.Zone;
15+
import org.matsim.contrib.common.zones.ZoneImpl;
16+
import org.matsim.contrib.common.zones.ZoneSystem;
17+
import org.matsim.contrib.common.zones.ZoneSystemImpl;
18+
import org.matsim.contrib.drt.analysis.zonal.MostCentralDrtZoneTargetLinkSelector;
19+
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams;
20+
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy;
21+
import org.matsim.contrib.drt.optimizer.rebalancing.demandestimator.PreviousIterationDrtDemandEstimator;
22+
import org.matsim.contrib.drt.optimizer.rebalancing.targetcalculator.DemandEstimatorAsTargetCalculator;
23+
import org.matsim.contrib.drt.run.DrtConfigGroup;
24+
import org.matsim.contrib.dvrp.fleet.*;
25+
import org.matsim.core.network.NetworkUtils;
26+
import org.matsim.core.utils.geometry.GeometryUtils;
27+
28+
import java.util.*;
29+
30+
public class MinCostFlowRebalancingStrategyTest {
31+
32+
private static final int ESTIMATION_PERIOD = 1800;
33+
34+
private final Network network = createNetwork();
35+
36+
private final Link link1 = network.getLinks().get(Id.createLinkId("link_1"));
37+
private final Link link2 = network.getLinks().get(Id.createLinkId("link_2"));
38+
39+
private final Zone zone1 = new ZoneImpl(
40+
Id.create("zone_1", Zone.class),
41+
new PreparedPolygon(GeometryUtils.createGeotoolsPolygon(
42+
List.of(
43+
new Coord(0, 0),
44+
new Coord(0, 500),
45+
new Coord(500, 500),
46+
new Coord(500, 0),
47+
new Coord(0, 0)
48+
))), "dummy");
49+
50+
private final Zone zone2 = new ZoneImpl(
51+
Id.create("zone_2", Zone.class),
52+
new PreparedPolygon(GeometryUtils.createGeotoolsPolygon(
53+
List.of(
54+
new Coord(500, 0),
55+
new Coord(500, 500),
56+
new Coord(1000, 500),
57+
new Coord(1000, 0),
58+
new Coord(500, 0)
59+
))), "dummy");
60+
61+
private final ZoneSystem zonalSystem = new ZoneSystemImpl(List.of(zone1, zone2), coord -> {
62+
if (coord == link1.getToNode().getCoord()) {
63+
return Optional.of(zone1);
64+
} else if (coord == link2.getToNode().getCoord()) {
65+
return Optional.of(zone2);
66+
} else {
67+
throw new RuntimeException();
68+
}
69+
}, network);
70+
71+
72+
@Test
73+
void testEmptyDemandAndTarget() {
74+
PreviousIterationDrtDemandEstimator estimator = createEstimator();
75+
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);
76+
77+
RebalancingParams rebalancingParams = new RebalancingParams();
78+
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
79+
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
80+
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
81+
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);
82+
83+
AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
84+
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
85+
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);
86+
87+
Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
88+
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
89+
Assertions.assertThat(relocations.isEmpty());
90+
}
91+
92+
@Test
93+
void testDemandWithoutSurplus() {
94+
PreviousIterationDrtDemandEstimator estimator = createEstimator();
95+
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);
96+
97+
RebalancingParams rebalancingParams = new RebalancingParams();
98+
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
99+
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
100+
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
101+
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);
102+
103+
AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
104+
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
105+
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);
106+
107+
//time bin 0-1800
108+
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
109+
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
110+
estimator.handleEvent(departureEvent(500, link2, TransportMode.drt));
111+
estimator.handleEvent(departureEvent(1500, link1, TransportMode.drt));
112+
estimator.reset(1);
113+
114+
Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
115+
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
116+
Assertions.assertThat(relocations.isEmpty());
117+
}
118+
119+
@Test
120+
void testDemandWithSurplus() {
121+
PreviousIterationDrtDemandEstimator estimator = createEstimator();
122+
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);
123+
124+
RebalancingParams rebalancingParams = new RebalancingParams();
125+
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
126+
minCostFlowRebalancingStrategyParams.targetAlpha = 1.;
127+
minCostFlowRebalancingStrategyParams.targetBeta = 0.;
128+
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);
129+
130+
AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
131+
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
132+
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);
133+
134+
// 3 expected trips in zone 1
135+
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
136+
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
137+
estimator.handleEvent(departureEvent(300, link1, TransportMode.drt));
138+
// 1 expected trip in zone 2
139+
estimator.handleEvent(departureEvent(100, link2, TransportMode.drt));
140+
estimator.reset(1);
141+
142+
Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
143+
144+
// 4 vehicles in zone 1 (surplus = 1)
145+
List<DvrpVehicle> rebalanceableVehiclesZone1 = new ArrayList<>();
146+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
147+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
148+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
149+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
150+
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);
151+
152+
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
153+
Assertions.assertThat(relocations.size()).isEqualTo(1);
154+
Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId());
155+
156+
rebalanceableVehicles.clear();
157+
158+
// 5 vehicles in zone 1 (surplus = 2)
159+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
160+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
161+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
162+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
163+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1));
164+
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);
165+
166+
//set alpha to 2 -> send two vehicles to zone 2
167+
minCostFlowRebalancingStrategyParams.targetAlpha = 2.;
168+
List<RebalancingStrategy.Relocation> relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
169+
Assertions.assertThat(relocations2.size()).isEqualTo(2);
170+
Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId());
171+
Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId());
172+
}
173+
174+
@Test
175+
void testDemandWithSurplusZoneBasedTargetRates() {
176+
177+
// set attributes
178+
zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 0.);
179+
zone1.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.);
180+
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 1.);
181+
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_BETA, 0.);
182+
183+
184+
PreviousIterationDrtDemandEstimator estimator = createEstimator();
185+
DemandEstimatorAsTargetCalculator targetCalculator = new DemandEstimatorAsTargetCalculator(estimator, ESTIMATION_PERIOD);
186+
187+
RebalancingParams rebalancingParams = new RebalancingParams();
188+
MinCostFlowRebalancingStrategyParams minCostFlowRebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams();
189+
minCostFlowRebalancingStrategyParams.targetCoefficientSource = MinCostFlowRebalancingStrategyParams.TargetCoefficientSource.FromZoneAttribute;
190+
rebalancingParams.addParameterSet(minCostFlowRebalancingStrategyParams);
191+
192+
AggregatedMinCostRelocationCalculator relocationCalculator = new AggregatedMinCostRelocationCalculator(new MostCentralDrtZoneTargetLinkSelector(zonalSystem));
193+
MinCostFlowRebalancingStrategy strategy = new MinCostFlowRebalancingStrategy(targetCalculator,
194+
zonalSystem, createEmptyFleet(), relocationCalculator, rebalancingParams);
195+
196+
// 3 expected trips in zone 1
197+
estimator.handleEvent(departureEvent(100, link1, TransportMode.drt));
198+
estimator.handleEvent(departureEvent(200, link1, TransportMode.drt));
199+
estimator.handleEvent(departureEvent(300, link1, TransportMode.drt));
200+
// 1 expected trip in zone 2
201+
estimator.handleEvent(departureEvent(100, link2, TransportMode.drt));
202+
estimator.reset(1);
203+
204+
Map<Zone, List<DvrpVehicle>> rebalanceableVehicles = new HashMap<>();
205+
206+
// 4 vehicles in zone 1 (surplus = 1)
207+
List<DvrpVehicle> rebalanceableVehiclesZone1 = new ArrayList<>();
208+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
209+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
210+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
211+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
212+
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);
213+
214+
List<RebalancingStrategy.Relocation> relocations = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
215+
Assertions.assertThat(relocations.size()).isEqualTo(1);
216+
Assertions.assertThat(relocations.getFirst().link.getId()).isEqualTo(link2.getId());
217+
218+
rebalanceableVehicles.clear();
219+
220+
// 5 vehicles in zone 1 (surplus = 2)
221+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a1", DvrpVehicle.class), link1));
222+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a2", DvrpVehicle.class), link1));
223+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a3", DvrpVehicle.class), link1));
224+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a4", DvrpVehicle.class), link1));
225+
rebalanceableVehiclesZone1.add(getDvrpVehicle(Id.create("a5", DvrpVehicle.class), link1));
226+
rebalanceableVehicles.put(zone1, rebalanceableVehiclesZone1);
227+
228+
//set alpha to 2 -> send two vehicles to zone 2
229+
zone2.getAttributes().putAttribute(MinCostFlowRebalancingStrategy.REBALANCING_ZONAL_TARGET_ALPHA, 2.);
230+
List<RebalancingStrategy.Relocation> relocations2 = strategy.calculateMinCostRelocations(0, rebalanceableVehicles, Collections.emptyMap());
231+
Assertions.assertThat(relocations2.size()).isEqualTo(2);
232+
Assertions.assertThat(relocations2.getFirst().link.getId()).isEqualTo(link2.getId());
233+
Assertions.assertThat(relocations2.getLast().link.getId()).isEqualTo(link2.getId());
234+
}
235+
236+
private DvrpVehicleImpl getDvrpVehicle(Id<DvrpVehicle> id, Link link) {
237+
return new DvrpVehicleImpl(
238+
ImmutableDvrpVehicleSpecification.newBuilder()
239+
.id(id)
240+
.capacity(0)
241+
.serviceBeginTime(0)
242+
.serviceEndTime(0)
243+
.startLinkId(link.getId())
244+
.build(), link);
245+
}
246+
247+
private static Fleet createEmptyFleet() {
248+
return () -> ImmutableMap.<Id<DvrpVehicle>, DvrpVehicle>builder().build();
249+
}
250+
251+
252+
private PreviousIterationDrtDemandEstimator createEstimator() {
253+
RebalancingParams rebalancingParams = new RebalancingParams();
254+
rebalancingParams.interval = ESTIMATION_PERIOD;
255+
256+
DrtConfigGroup drtConfigGroup = new DrtConfigGroup();
257+
drtConfigGroup.addParameterSet(rebalancingParams);
258+
259+
return new PreviousIterationDrtDemandEstimator(zonalSystem, drtConfigGroup, ESTIMATION_PERIOD);
260+
}
261+
262+
private PersonDepartureEvent departureEvent(double time, Link link, String mode) {
263+
return new PersonDepartureEvent(time, null, link.getId(), mode, mode);
264+
}
265+
266+
static Network createNetwork() {
267+
Network network = NetworkUtils.createNetwork();
268+
Node a = network.getFactory().createNode(Id.createNodeId("a"), new Coord(0,0));
269+
Node b = network.getFactory().createNode(Id.createNodeId("b"), new Coord(500,0));
270+
network.addNode(a);
271+
network.addNode(b);
272+
273+
Link ab = network.getFactory().createLink(Id.createLinkId("link_1"), a, b);
274+
Link ba = network.getFactory().createLink(Id.createLinkId("link_2"), b, a);
275+
network.addLink(ab);
276+
network.addLink(ba);
277+
return network;
278+
}
279+
}

0 commit comments

Comments
 (0)