Skip to content

Commit c801582

Browse files
committed
Merge branch 'main' into compathelper/new_version/2025-10-04-00-15-43-020-02061308093
2 parents f020a02 + 066e3a1 commit c801582

37 files changed

+556
-223
lines changed

.github/workflows/Documentation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
statuses: write
2323
runs-on: ubuntu-latest
2424
steps:
25-
- uses: actions/checkout@v5
25+
- uses: actions/checkout@v6
2626
- uses: julia-actions/setup-julia@v2
2727
with:
2828
version: '1'

.github/workflows/Test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ jobs:
2323
runs-on: ubuntu-latest
2424
strategy:
2525
matrix:
26-
julia-version: ['1']
26+
julia-version: ['1.10', '1']
2727

2828
steps:
29-
- uses: actions/checkout@v5
29+
- uses: actions/checkout@v6
3030
- uses: julia-actions/setup-julia@v2
3131
with:
3232
version: ${{ matrix.julia-version }}

Project.toml

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
name = "DecisionFocusedLearningBenchmarks"
22
uuid = "2fbe496a-299b-4c81-bab5-c44dfc55cf20"
33
authors = ["Members of JuliaDecisionFocusedLearning"]
4-
version = "0.3.0"
4+
version = "0.4.0"
5+
6+
[workspace]
7+
projects = ["docs", "test"]
58

69
[deps]
710
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
@@ -56,31 +59,12 @@ LinearAlgebra = "1"
5659
Metalhead = "0.9.4"
5760
NPZ = "0.4"
5861
Plots = "1"
59-
Printf = "1.11.0"
62+
Printf = "1"
6063
Random = "1"
6164
Requires = "1.3.0"
6265
SCIP = "0.12"
6366
SimpleWeightedGraphs = "1.4"
6467
SparseArrays = "1"
65-
Statistics = "1.11.1"
68+
Statistics = "1"
6669
StatsBase = "0.34.4"
67-
julia = "1.6"
68-
69-
[extras]
70-
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
71-
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
72-
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
73-
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
74-
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
75-
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
76-
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
77-
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
78-
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
79-
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
80-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
81-
TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
82-
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
83-
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
84-
85-
[targets]
86-
test = ["Aqua", "Documenter", "Flux", "Graphs", "JET", "JuliaFormatter", "Random", "ProgressMeter", "StableRNGs", "Statistics", "Test", "TestItemRunner", "UnicodePlots", "Zygote"]
70+
julia = "1.10"

docs/src/benchmarks/dvsp.md

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The Dynamic Vehicle Scheduling Problem (DVSP) is a sequential decision-making pr
66

77
### Overview
88

9-
In the dynamic vehicle scheduling problem, a fleet operator must decide at each time step which customer requests to serve immediately and which to postpone to future time steps.
9+
In the dynamic vehicle scheduling problem, a fleet operator must decide at each time step which customer to serve immediately and which to postpone to future time steps.
1010
The goal is to serve all customers by the end of the planning horizon while minimizing total travel time.
1111

1212
This is a simplified version of the more complex Dynamic Vehicle Routing Problem with Time Windows (DVRPTW), focusing on the core sequential decision-making aspects without capacity or time window constraints.
@@ -24,18 +24,18 @@ The dynamic vehicle scheduling problem can be formulated as a finite-horizon Mar
2424
s_t = (R_t, D_t, t)
2525
```
2626
where:
27-
- ``R_t`` are the pending customer requests (not yet served), where each request ``r_i \in R_t`` contains:
27+
- ``R_t`` are the pending customer (not yet served), where each customer ``r_i \in R_t`` contains:
2828
- ``x_i, y_i``: 2d spatial coordinates of the customer location
2929
- ``\tau_i``: start time when the customer needs to be served
3030
- ``s_i``: service time required to serve the customer
31-
- ``D_t`` indicates which requests must be dispatched this time step (i.e. that cannot be postponed further, otherwise they will be infeasible at the next time step because of their start time)
31+
- ``D_t`` indicates which customers must be dispatched this time step (i.e. that cannot be postponed further, otherwise they will be infeasible at the next time step because of their start time)
3232
- ``t \in \{1, 2, \ldots, T\}`` is the current time step
3333

3434
The state also implicitly includes (constant over time):
3535
- Travel duration matrix ``d_{ij}``: time to travel from location ``i`` to location ``j``
3636
- Depot location
3737

38-
**Action Space** ``\mathcal{A}``: The action at time step ``t`` is a set of vehicle routes:
38+
**Action Space** ``\mathcal{A}(s_t)``: The action at time step ``t`` is a set of vehicle routes:
3939
```math
4040
a_t = \{r_1, r_2, \ldots, r_k\}
4141
```
@@ -47,7 +47,7 @@ A route is feasible if:
4747

4848
**Transition Dynamics** ``\mathcal{P}(s_{t+1} | s_t, a_t)``: After executing routes ``a_t``:
4949

50-
1. **Remove served customers** from the pending request set
50+
1. **Remove served customers** from the pending customer set
5151
2. **Generate new customer arrivals** according to the underlying exogenous distribution
5252
3. **Update must-dispatch set** based on postponement rules
5353

@@ -70,7 +70,7 @@ where ``d_{ij}`` is the travel duration from location ``i`` to location ``j``, a
7070

7171
The main benchmark configuration with the following parameters:
7272

73-
- `max_requests_per_epoch`: Maximum number of new customer requests per time step (default: 10)
73+
- `max_requests_per_epoch`: Maximum number of new customers per time step (default: 10)
7474
- `Δ_dispatch`: Time delay between decision and vehicle dispatch (default: 1.0)
7575
- `epoch_duration`: Duration of each decision time step (default: 1.0)
7676
- `two_dimensional_features`: Whether to use simplified 2D features instead of full feature set (default: false)
@@ -82,51 +82,64 @@ Problem instances are generated from static vehicle routing datasets and include
8282
- **Customer locations**: Spatial coordinates for pickup/delivery points
8383
- **Depot location**: Central starting and ending point for all routes
8484
- **Travel times**: Distance/duration matrix between all location pairs
85-
- **Service requirements**: Time needed to serve each customer
85+
- **Service times**: Service time each customer
8686

87-
The dynamic version samples new customer arrivals from the static instance, drawing new customers by independently sampling their locations and service times.
87+
The dynamic version samples new customer arrivals from the static instance, drawing new customers by independently sampling:
88+
- their locations from the set of static customer locations
89+
- service times, uniformly from the range of service times in the static instance
8890

8991
### Features
9092

91-
The benchmark provides two feature representations:
92-
93-
**Full Features** (14-dimensional):
94-
- Start times for postponable requests
95-
- End times (start + service time)
96-
- Travel time from depot to request
97-
- Travel time from request to depot
98-
- Slack time until next time step
99-
- Quantile-based travel times to other requests (9 quantiles)
93+
The benchmark provides two feature matrix representations, containing one column per postponable customer in the state:
94+
95+
**Full Features** (27-dimensional):
96+
- Start times for postponable customers (1)
97+
- End times (start + service time) (2)
98+
- Travel time from depot to customer (3)
99+
- Travel time from customer to depot (4)
100+
- Slack time until next time step (5)
101+
- % of must-dispatch customers that can reach this customer on time (6)
102+
- % of customers reachable from this customer on time (7)
103+
- % of customers that can reach this customer on time (8)
104+
- % of customers reachable or that can reach this customer on time (9)
105+
- Quantile-based travel times to other customers (9 quantiles) (10-18)
106+
- Quantiles of % of reachable new customers (9 quantiles) (19-27)
100107

101108
**2D Features** (simplified):
102-
- Travel time from depot to request
103-
- Mean travel time to other requests
109+
- Travel time from depot to customer (1)
110+
- Mean travel time to other customers (2)
104111

105112
## Benchmark Policies
106113

107114
### Lazy Policy
108115

109-
The lazy policy postpones all possible requests, serving only those that must be dispatched.
116+
The lazy policy postpones all possible customers, serving only those that must be dispatched.
110117

111118
### Greedy Policy
112119

113-
The greedy policy serves all pending requests as soon as they arrive, without considering future consequences.
120+
The greedy policy serves all pending customers as soon as they arrive, without considering future consequences.
114121

115122
## Decision-Focused Learning Policy
116123

117124
```math
118125
\xrightarrow[\text{State}]{s_t}
119126
\fbox{Neural network $\varphi_w$}
120-
\xrightarrow[\text{Priorities}]{\theta}
127+
\xrightarrow[\text{Prizes}]{\theta}
121128
\fbox{Prize-collecting VSP}
122129
\xrightarrow[\text{Routes}]{a_t}
123130
```
124131

125132
**Components**:
126133

127-
1. **Neural Network** ``\varphi_w``: Takes current state features as input and predicts customer priorities ``\theta = (\theta_1, \ldots, \theta_n)``
128-
2. **Optimization Layer**: Solves the prize-collecting vehicle scheduling problem to determine optimal routes given the predicted priorities
134+
1. **Neural Network** ``\varphi_w``: Takes current state features as input and predicts customer prizes ``\theta = (\theta_1, \ldots, \theta_n)``, one value per postponable customer.
135+
2. **Optimization Layer**: Solves the prize-collecting vehicle scheduling problem to determine optimal routes given the predicted prizes, by maximizing total collected prizes minus travel costs:
136+
```math
137+
\max_{a_t\in \mathcal{A}(s_t)} \sum_{r \in a_t} \left( \sum_{i \in r} \theta_i - \sum_{(i,j) \in r} d_{ij} \right)
138+
```
139+
This can be modeled as a flow linear program on a directed acyclic graph (DAG) and is solved using standard LP solvers.
129140
130141
The neural network architecture adapts to the feature dimensionality:
131-
- **2D features**: `Dense(2 => 1)` followed by vectorization
132-
- **Full features**: `Dense(14 => 1)` followed by vectorization
142+
- **2D features**: `Dense(2 => 1)`, applied in parallel to each postponable customer
143+
- **Full features**: `Dense(27 => 1)` applied in parallel to each postponable customer
144+
145+
**Note:** one can also use more complex architectures such as a deeper MLP or a graph neural network for better performance.

src/Argmax2D/Argmax2D.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function Utils.generate_sample(bench::Argmax2DBenchmark, rng::AbstractRNG)
6262
θ_true ./= 2 * norm(θ_true)
6363
instance = build_polytope(rand(rng, polytope_vertex_range); shift=rand(rng))
6464
y_true = maximizer(θ_true; instance)
65-
return DataSample(; x=x, θ=θ_true, y=y_true, info=instance)
65+
return DataSample(; x=x, θ=θ_true, y=y_true, instance=instance)
6666
end
6767

6868
"""
@@ -88,11 +88,11 @@ function Utils.generate_statistical_model(
8888
return model
8989
end
9090

91-
function Utils.plot_data(::Argmax2DBenchmark; info, θ, kwargs...)
91+
function Utils.plot_data(::Argmax2DBenchmark; instance, θ, kwargs...)
9292
pl = init_plot()
93-
plot_polytope!(pl, info)
93+
plot_polytope!(pl, instance)
9494
plot_objective!(pl, θ)
95-
return plot_maximizer!(pl, θ, info, maximizer)
95+
return plot_maximizer!(pl, θ, instance, maximizer)
9696
end
9797

9898
"""
@@ -101,9 +101,13 @@ $TYPEDSIGNATURES
101101
Plot the data sample for the [`Argmax2DBenchmark`](@ref).
102102
"""
103103
function Utils.plot_data(
104-
bench::Argmax2DBenchmark, sample::DataSample; info=sample.info, θ=sample.θ, kwargs...
104+
bench::Argmax2DBenchmark,
105+
sample::DataSample;
106+
instance=sample.instance,
107+
θ=sample.θ,
108+
kwargs...,
105109
)
106-
return Utils.plot_data(bench; info, θ, kwargs...)
110+
return Utils.plot_data(bench; instance, θ, kwargs...)
107111
end
108112

109113
export Argmax2DBenchmark

src/DecisionFocusedLearningBenchmarks.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export generate_sample, generate_dataset, generate_environments, generate_enviro
7070
export generate_scenario
7171
export generate_policies
7272
export generate_statistical_model
73-
export generate_maximizer, maximizer_kwargs
73+
export generate_maximizer
7474
export generate_anticipative_solution
7575
export is_exogenous, is_endogenous
7676

src/DynamicAssortment/DynamicAssortment.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Outputs a data sample containing an [`Instance`](@ref).
8383
function Utils.generate_sample(
8484
b::DynamicAssortmentBenchmark, rng::AbstractRNG=MersenneTwister(0)
8585
)
86-
return DataSample(; info=Instance(b, rng))
86+
return DataSample(; instance=Instance(b, rng))
8787
end
8888

8989
"""

src/DynamicVehicleScheduling/DynamicVehicleScheduling.jl

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,20 @@ $TYPEDFIELDS
5757
two_dimensional_features::Bool = false
5858
end
5959

60+
"""
61+
$TYPEDSIGNATURES
62+
63+
Generate a dataset for the dynamic vehicle scheduling benchmark.
64+
Returns a vector of [`DataSample`](@ref) objects, each containing an [`Instance`](@ref).
65+
The dataset is generated from pre-existing DVRPTW files.
66+
"""
6067
function Utils.generate_dataset(b::DynamicVehicleSchedulingBenchmark, dataset_size::Int=1)
6168
(; max_requests_per_epoch, Δ_dispatch, epoch_duration, two_dimensional_features) = b
6269
files = readdir(datadep"dvrptw"; join=true)
6370
dataset_size = min(dataset_size, length(files))
6471
return [
6572
DataSample(;
66-
info=Instance(
73+
instance=Instance(
6774
read_vsp_instance(files[i]);
6875
max_requests_per_epoch,
6976
Δ_dispatch,
@@ -74,21 +81,45 @@ function Utils.generate_dataset(b::DynamicVehicleSchedulingBenchmark, dataset_si
7481
]
7582
end
7683

84+
"""
85+
$TYPEDSIGNATURES
86+
87+
Creates an environment from an [`Instance`](@ref) of the dynamic vehicle scheduling benchmark.
88+
The seed of the environment is randomly generated using the provided random number generator.
89+
"""
7790
function Utils.generate_environment(
7891
::DynamicVehicleSchedulingBenchmark, instance::Instance, rng::AbstractRNG; kwargs...
7992
)
8093
seed = rand(rng, 1:typemax(Int))
8194
return DVSPEnv(instance; seed)
8295
end
8396

97+
"""
98+
$TYPEDSIGNATURES
99+
100+
Returns a linear maximizer for the dynamic vehicle scheduling benchmark, of the form:
101+
θ ↦ argmax_{y} θᵀg(y) + h(y)
102+
"""
84103
function Utils.generate_maximizer(::DynamicVehicleSchedulingBenchmark)
85104
return LinearMaximizer(oracle; g, h)
86105
end
87106

107+
"""
108+
$TYPEDSIGNATURES
109+
110+
Generate a scenario for the dynamic vehicle scheduling benchmark.
111+
This is a wrapper around the generic scenario generation function.
112+
"""
88113
function Utils.generate_scenario(b::DynamicVehicleSchedulingBenchmark, args...; kwargs...)
89114
return Utils.generate_scenario(args...; kwargs...)
90115
end
91116

117+
"""
118+
$TYPEDSIGNATURES
119+
120+
Generate an anticipative solution for the dynamic vehicle scheduling benchmark.
121+
The solution is computed using the anticipative solver with the benchmark's feature configuration.
122+
"""
92123
function Utils.generate_anticipative_solution(
93124
b::DynamicVehicleSchedulingBenchmark, args...; kwargs...
94125
)
@@ -97,6 +128,14 @@ function Utils.generate_anticipative_solution(
97128
)
98129
end
99130

131+
"""
132+
$TYPEDSIGNATURES
133+
134+
Generate baseline policies for the dynamic vehicle scheduling benchmark.
135+
Returns a tuple containing:
136+
- `lazy`: A policy that dispatches vehicles only when they are ready
137+
- `greedy`: A policy that dispatches vehicles to the nearest customer
138+
"""
100139
function Utils.generate_policies(b::DynamicVehicleSchedulingBenchmark)
101140
lazy = Policy(
102141
"Lazy",
@@ -111,11 +150,18 @@ function Utils.generate_policies(b::DynamicVehicleSchedulingBenchmark)
111150
return (lazy, greedy)
112151
end
113152

153+
"""
154+
$TYPEDSIGNATURES
155+
156+
Generate a statistical model for the dynamic vehicle scheduling benchmark.
157+
The model is a simple linear chain with a single dense layer that maps features to a scalar output.
158+
The input dimension depends on whether two-dimensional features are used (2 features) or not (27 features).
159+
"""
114160
function Utils.generate_statistical_model(
115161
b::DynamicVehicleSchedulingBenchmark; seed=nothing
116162
)
117163
Random.seed!(seed)
118-
return Chain(Dense((b.two_dimensional_features ? 2 : 14) => 1), vec)
164+
return Chain(Dense((b.two_dimensional_features ? 2 : 27) => 1), vec)
119165
end
120166

121167
export DynamicVehicleSchedulingBenchmark

0 commit comments

Comments
 (0)