|
| 1 | +""" |
| 2 | +$TYPEDEF |
| 3 | +
|
| 4 | +Data structure for a city in the vehicle scheduling problem. |
| 5 | +Contains all the relevant information to build an instance of the problem. |
| 6 | +
|
| 7 | +# Fields |
| 8 | +$TYPEDFIELDS |
| 9 | +""" |
| 10 | +struct City |
| 11 | + "city width (in minutes)" |
| 12 | + width::Int |
| 13 | + # Objectives ponderation |
| 14 | + "cost of a vehicle in the objective function" |
| 15 | + vehicle_cost::Float64 |
| 16 | + "cost of one minute delay in the objective function" |
| 17 | + delay_cost::Float64 |
| 18 | + # Tasks |
| 19 | + "number of tasks to fulfill" |
| 20 | + nb_tasks::Int |
| 21 | + "tasks list (see [`Task`](@ref)), that should be ordered by start time" |
| 22 | + tasks::Vector{Task} |
| 23 | + # Stochastic specific stuff |
| 24 | + "idth (in minutes) of each district" |
| 25 | + district_width::Int |
| 26 | + "districts matrix (see [`District`](@ref)), indices corresponding to their relative positions" |
| 27 | + districts::Matrix{District} |
| 28 | + "a log-normal distribution modeling delay between districts" |
| 29 | + random_inter_area_factor::LogNormal{Float64} |
| 30 | + "size (nb_scenarios, 24), each row correspond to one scenario, each column to one hour of the day" |
| 31 | + scenario_inter_area_factor::Matrix{Float64} |
| 32 | +end |
| 33 | + |
| 34 | +""" |
| 35 | +$TYPEDSIGNATURES |
| 36 | +
|
| 37 | +Constructor for [`City`](@ref). |
| 38 | +""" |
| 39 | +function City(; |
| 40 | + nb_scenarios=default_nb_scenarios, |
| 41 | + width=default_width, |
| 42 | + vehicle_cost=default_vehicle_cost, |
| 43 | + nb_tasks=default_nb_tasks, |
| 44 | + tasks=Vector{Task}(undef, nb_tasks + 2), |
| 45 | + district_width=default_district_width, |
| 46 | + districts=Matrix{District}(undef, width ÷ district_width, width ÷ district_width), |
| 47 | + delay_cost=default_delay_cost, |
| 48 | + random_inter_area_factor=default_random_inter_area_factor, |
| 49 | + scenario_inter_area_factor=zeros(nb_scenarios, 24), |
| 50 | +) |
| 51 | + return City( |
| 52 | + width, |
| 53 | + vehicle_cost, |
| 54 | + delay_cost, |
| 55 | + nb_tasks, |
| 56 | + tasks, |
| 57 | + district_width, |
| 58 | + districts, |
| 59 | + random_inter_area_factor, |
| 60 | + scenario_inter_area_factor, |
| 61 | + ) |
| 62 | +end |
| 63 | + |
| 64 | +""" |
| 65 | +$TYPEDSIGNATURES |
| 66 | +
|
| 67 | +- Creates a city from `city_kwargs` |
| 68 | +- Depot location at city center |
| 69 | +- Randomize tasks, and add two dummy tasks : one `source` task at time=0 from the depot, |
| 70 | + and one `destination` task ending at time=end at depot |
| 71 | +- Roll every scenario. |
| 72 | +""" |
| 73 | +function create_random_city(; # TODO: use an rng here |
| 74 | + αᵥ_low=default_αᵥ_low, |
| 75 | + αᵥ_high=default_αᵥ_high, |
| 76 | + first_begin_time=default_first_begin_time, |
| 77 | + last_begin_time=default_last_begin_time, |
| 78 | + district_μ=default_district_μ, |
| 79 | + district_σ=default_district_σ, |
| 80 | + task_μ=default_task_μ, |
| 81 | + task_σ=default_task_σ, |
| 82 | + city_kwargs..., |
| 83 | +) |
| 84 | + city = City(; city_kwargs...) |
| 85 | + init_districts!(city, district_μ, district_σ) |
| 86 | + init_tasks!(city, αᵥ_low, αᵥ_high, first_begin_time, last_begin_time, task_μ, task_σ) |
| 87 | + generate_scenarios!(city) |
| 88 | + compute_perturbed_end_times!(city) |
| 89 | + return city |
| 90 | +end |
| 91 | + |
| 92 | +function init_districts!(city::City, district_μ::Distribution, district_σ::Distribution) |
| 93 | + nb_scenarios = size(city.scenario_inter_area_factor, 1) |
| 94 | + nb_district_per_edge = city.width ÷ city.district_width |
| 95 | + for x in 1:nb_district_per_edge |
| 96 | + for y in 1:nb_district_per_edge |
| 97 | + μ = rand(district_μ) |
| 98 | + σ = rand(district_σ) |
| 99 | + city.districts[x, y] = District(; |
| 100 | + random_delay=LogNormal(μ, σ), nb_scenarios=nb_scenarios |
| 101 | + ) |
| 102 | + end |
| 103 | + end |
| 104 | + return nothing |
| 105 | +end |
| 106 | + |
| 107 | +function init_tasks!( |
| 108 | + city::City, |
| 109 | + αᵥ_low::Real, |
| 110 | + αᵥ_high::Real, |
| 111 | + first_begin_time::Real, |
| 112 | + last_begin_time::Real, |
| 113 | + task_μ::Distribution, |
| 114 | + task_σ::Distribution, |
| 115 | +) |
| 116 | + nb_scenarios = size(city.scenario_inter_area_factor, 1) |
| 117 | + |
| 118 | + point_distribution = Uniform(0, city.width) |
| 119 | + start_time_distribution = Uniform(first_begin_time, last_begin_time) |
| 120 | + travel_time_multiplier_distribution = Uniform(αᵥ_low, αᵥ_high) |
| 121 | + |
| 122 | + for i_task in 1:(city.nb_tasks) |
| 123 | + start_point = draw_random_point(point_distribution) |
| 124 | + end_point = draw_random_point(point_distribution) |
| 125 | + |
| 126 | + start_time = rand(start_time_distribution) |
| 127 | + end_time = |
| 128 | + start_time + |
| 129 | + rand(travel_time_multiplier_distribution) * distance(start_point, end_point) |
| 130 | + |
| 131 | + μ = rand(task_μ) |
| 132 | + σ = rand(task_σ) |
| 133 | + random_delay = LogNormal(μ, σ) |
| 134 | + |
| 135 | + city.tasks[i_task + 1] = Task(; |
| 136 | + type=job::TaskType, |
| 137 | + start_point=start_point, |
| 138 | + end_point=end_point, |
| 139 | + start_time=start_time, |
| 140 | + end_time=end_time, |
| 141 | + random_delay=random_delay, |
| 142 | + nb_scenarios=nb_scenarios, |
| 143 | + ) |
| 144 | + end |
| 145 | + |
| 146 | + # add start and final "artificial" tasks |
| 147 | + city_center = Point(city.width / 2, city.width / 2) # ? hard coded ? |
| 148 | + city.tasks[1] = Task(; |
| 149 | + type=depot_start::TaskType, |
| 150 | + start_point=city_center, |
| 151 | + end_point=city_center, |
| 152 | + start_time=0.0, |
| 153 | + end_time=0.0, |
| 154 | + random_delay=ZERO_UNIFORM, |
| 155 | + nb_scenarios=nb_scenarios, |
| 156 | + ) |
| 157 | + final_task_time = 24 * 60.0 # ? hard coded ? |
| 158 | + city.tasks[end] = Task(; |
| 159 | + type=depot_end::TaskType, |
| 160 | + start_point=city_center, |
| 161 | + end_point=city_center, |
| 162 | + start_time=final_task_time, |
| 163 | + end_time=final_task_time, |
| 164 | + random_delay=ZERO_UNIFORM, |
| 165 | + nb_scenarios=nb_scenarios, |
| 166 | + ) |
| 167 | + |
| 168 | + # sort tasks by start time |
| 169 | + sort!(city.tasks; by=task -> task.start_time, rev=false) |
| 170 | + return nothing |
| 171 | +end |
| 172 | + |
| 173 | +""" |
| 174 | + get_district(point::Point, city::City) |
| 175 | +
|
| 176 | +Return indices of the `city` district containing `point`. |
| 177 | +""" |
| 178 | +function get_district(point::Point, city::City) |
| 179 | + return trunc(Int, point.x / city.district_width) + 1, |
| 180 | + trunc(Int, point.y / city.district_width) + 1 |
| 181 | +end |
| 182 | + |
| 183 | +function generate_scenarios!(city::City) |
| 184 | + # roll all tasks |
| 185 | + for task in city.tasks |
| 186 | + roll(task) |
| 187 | + end |
| 188 | + |
| 189 | + # roll all districts |
| 190 | + for district in city.districts |
| 191 | + roll(district) |
| 192 | + end |
| 193 | + |
| 194 | + # roll inter-district |
| 195 | + nb_scenarios, nb_hours = size(city.scenario_inter_area_factor) |
| 196 | + for s in 1:nb_scenarios |
| 197 | + previous_delay = 0.0 |
| 198 | + for h in 1:nb_hours |
| 199 | + previous_delay = (previous_delay + 0.1) * rand(city.random_inter_area_factor) # TODO : study formula |
| 200 | + city.scenario_inter_area_factor[s, h] = previous_delay |
| 201 | + end |
| 202 | + end |
| 203 | + return nothing |
| 204 | +end |
| 205 | + |
| 206 | +function compute_perturbed_end_times!(city::City) |
| 207 | + nb_scenarios = size(city.scenario_inter_area_factor, 1) |
| 208 | + |
| 209 | + for task in city.tasks[2:(end - 1)] |
| 210 | + start_time = task.start_time |
| 211 | + end_time = task.end_time |
| 212 | + start_point = task.start_point |
| 213 | + end_point = task.end_point |
| 214 | + |
| 215 | + origin_x, origin_y = get_district(start_point, city) |
| 216 | + destination_x, destination_y = get_district(end_point, city) |
| 217 | + origin_district = city.districts[origin_x, origin_y] |
| 218 | + destination_district = city.districts[destination_x, destination_y] |
| 219 | + |
| 220 | + scenario_start_time = task.scenario_start_time |
| 221 | + origin_delay = origin_district.scenario_delay |
| 222 | + destination_delay = destination_district.scenario_delay |
| 223 | + inter_area_delay = city.scenario_inter_area_factor |
| 224 | + |
| 225 | + for s in 1:nb_scenarios |
| 226 | + ξ₁ = scenario_start_time[s] |
| 227 | + ξ₂ = ξ₁ + origin_delay[s, hour_of(ξ₁)] |
| 228 | + ξ₃ = ξ₂ + end_time - start_time + inter_area_delay[s, hour_of(ξ₂)] |
| 229 | + task.scenario_end_time[s] = ξ₃ + destination_delay[s, hour_of(ξ₃)] |
| 230 | + end |
| 231 | + end |
| 232 | + return nothing |
| 233 | +end |
0 commit comments