1+ function must_dispatch_in_zone (state:: DVSPState )
2+ (; state_instance, is_must_dispatch) = state
3+
4+ startTimes = state_instance. start_time
5+ serviceTimes = state_instance. service_time
6+ durations = state_instance. duration
7+
8+ n = length (startTimes)
9+ must_dispatch_counts = zeros (n)
10+
11+ # For each customer j
12+ for j in 1 : n
13+ # Count how many must-dispatch customers i can reach j
14+ for i in 2 : n
15+ if is_must_dispatch[i] && i != j
16+ # Check if customer i can reach customer j in time
17+ if startTimes[i] + serviceTimes[i] + durations[i, j] < startTimes[j]
18+ must_dispatch_counts[j] += 1
19+ end
20+ end
21+ end
22+ end
23+
24+ return must_dispatch_counts
25+ end
26+
27+ function count_reachable_from (state:: DVSPState )
28+ (; state_instance) = state
29+
30+ startTimes = state_instance. start_time
31+ serviceTimes = state_instance. service_time
32+ durations = state_instance. duration
33+
34+ n = length (startTimes)
35+ reachable_counts = zeros (n)
36+
37+ # For each customer j
38+ for j in 1 : n
39+ # Count how many customers i are reachable from j
40+ for i in 2 : n
41+ if i != j
42+ # Check if customer i can reach customer j in time
43+ if startTimes[j] + serviceTimes[j] + durations[j, i] < startTimes[i]
44+ reachable_counts[j] += 1
45+ end
46+ end
47+ end
48+ end
49+
50+ return reachable_counts
51+ end
52+
53+ function count_reachable_to (state:: DVSPState )
54+ (; state_instance) = state
55+
56+ startTimes = state_instance. start_time
57+ serviceTimes = state_instance. service_time
58+ durations = state_instance. duration
59+
60+ n = length (startTimes)
61+ reachable_counts = zeros (n)
62+
63+ # For each customer j
64+ for j in 1 : n
65+ # Count how many customers i can reach j
66+ for i in 2 : n
67+ if i != j
68+ # Check if customer i can reach customer j in time
69+ if startTimes[i] + serviceTimes[i] + durations[i, j] < startTimes[j]
70+ reachable_counts[j] += 1
71+ end
72+ end
73+ end
74+ end
75+
76+ return reachable_counts
77+ end
78+
79+ function quantile_reachable_new_requests (
80+ state:: DVSPState ,
81+ instance:: Instance ;
82+ n_samples:: Int = 100 ,
83+ quantiles= [i * 0.1 for i in 1 : 9 ],
84+ )
85+ (; state_instance, current_epoch) = state
86+ (; static_instance, epoch_duration, Δ_dispatch, max_requests_per_epoch) = instance
87+
88+ startTimes = state_instance. start_time
89+ serviceTimes = state_instance. service_time
90+ durations = state_instance. duration
91+ n_current = length (startTimes)
92+
93+ # Time window for next epoch
94+ next_time = epoch_duration * current_epoch + Δ_dispatch
95+ min_time = minimum (static_instance. start_time)
96+ max_time = maximum (static_instance. start_time)
97+ N = customer_count (static_instance)
98+
99+ # Store reachability percentages for each customer across samples
100+ reachability_matrix = zeros (Float64, n_current, n_samples)
101+
102+ rng = MersenneTwister (42 )
103+ for s in 1 : n_samples
104+ # Sample new requests similar to scenario generation
105+ coordinate_indices = sample_indices (rng, max_requests_per_epoch, N)
106+ sampled_start_times = sample_times (
107+ rng, max_requests_per_epoch, max (min_time, next_time), max_time
108+ )
109+ service_time_indices = sample_indices (rng, max_requests_per_epoch, N)
110+
111+ # Check feasibility (can reach from depot)
112+ depot_durations = static_instance. duration[1 , coordinate_indices]
113+ is_feasible = next_time .+ depot_durations .<= sampled_start_times
114+
115+ feasible_coords = coordinate_indices[is_feasible]
116+ feasible_start_times = sampled_start_times[is_feasible]
117+ feasible_service_times = static_instance. service_time[service_time_indices[is_feasible]]
118+
119+ n_new = length (feasible_coords)
120+ if n_new == 0
121+ continue # No reachable requests in this sample
122+ end
123+
124+ # For each current customer, count how many new requests it can reach
125+ for j in 1 : n_current
126+ reachable_count = 0
127+ for k in 1 : n_new
128+ # Get duration from current customer location to new request location
129+ customer_loc = state. location_indices[j]
130+ new_loc = feasible_coords[k]
131+ travel_time = static_instance. duration[customer_loc, new_loc]
132+ travel_time_back = static_instance. duration[new_loc, customer_loc]
133+
134+ # Check if customer j can reach new request k or if k can reach j
135+ if startTimes[j] + serviceTimes[j] + travel_time <
136+ feasible_start_times[k] ||
137+ startTimes[j] >
138+ feasible_start_times[k] + feasible_service_times[k] + travel_time_back
139+ reachable_count += 1
140+ end
141+ end
142+ reachability_matrix[j, s] = reachable_count / n_new
143+ end
144+ end
145+
146+ # Compute quantiles for each customer
147+ quantile_features = zeros (Float64, n_current, length (quantiles))
148+ for j in 1 : n_current
149+ quantile_features[j, :] = quantile (reachability_matrix[j, :], quantiles)
150+ end
151+
152+ return quantile_features
153+ end
154+
1155function get_features_quantileTimeToRequests (state:: DVSPState , instance:: Instance )
2156 quantiles = [i * 0.1 for i in 1 : 9 ]
3157 a = instance. static_instance. duration[state. location_indices, 2 : end ]
@@ -6,7 +160,7 @@ function get_features_quantileTimeToRequests(state::DVSPState, instance::Instanc
6160end
7161
8162function compute_model_free_features (state:: DVSPState , instance:: Instance )
9- (; state_instance, is_postponable) = state
163+ (; state_instance, is_postponable, is_must_dispatch ) = state
10164
11165 startTimes = state_instance. start_time
12166 endTimes = startTimes .+ state_instance. service_time
@@ -15,19 +169,34 @@ function compute_model_free_features(state::DVSPState, instance::Instance)
15169
16170 slack_next_epoch = startTimes .- instance. epoch_duration
17171
172+ must_dispatch_counts = must_dispatch_in_zone (state)
173+ nb_must_dispatch = sum (is_must_dispatch)
174+ if nb_must_dispatch > 0
175+ must_dispatch_counts ./= nb_must_dispatch
176+ end
177+
178+ reachable_to_ratios = count_reachable_to (state) ./ (length (startTimes) - 1 )
179+ reachable_from_ratios = count_reachable_from (state) ./ (length (startTimes) - 1 )
180+ reachable_ratios = reachable_to_ratios .+ reachable_from_ratios
181+
18182 model_free_features = hcat (
19183 startTimes[is_postponable], # 1
20184 endTimes[is_postponable], # 2
21185 timeDepotRequest[is_postponable], # 3
22186 timeRequestDepot[is_postponable], # 4
23- slack_next_epoch[is_postponable], # 5-14
187+ slack_next_epoch[is_postponable], # 5
188+ must_dispatch_counts[is_postponable], # 6
189+ reachable_to_ratios[is_postponable], # 7
190+ reachable_from_ratios[is_postponable], # 8
191+ reachable_ratios[is_postponable], # 9
24192 )
25193 return model_free_features
26194end
27195
28196function compute_model_aware_features (state:: DVSPState , instance:: Instance )
29197 quantileTimeToRequests = get_features_quantileTimeToRequests (state, instance)
30- model_aware_features = quantileTimeToRequests
198+ quantileReachableNewRequests = quantile_reachable_new_requests (state, instance)
199+ model_aware_features = hcat (quantileTimeToRequests, quantileReachableNewRequests)
31200 return model_aware_features[state. is_postponable, :]
32201end
33202
0 commit comments