Booking management
This tutorial was generated using Literate.jl. Download the source as a .jl
file. Download the source as a .ipynb
file.
This example concerns the acceptance of booking requests for rooms in a hotel in the lead up to a large event.
Each stage, we receive a booking request and can choose to accept or decline it. Once accepted, bookings cannot be terminated.
using SDDP, HiGHS, Test
function booking_management_model(num_days, num_rooms, num_requests)
# maximum revenue that could be accrued.
max_revenue = (num_rooms + num_requests) * num_days * num_rooms
# booking_requests is a vector of {0,1} arrays of size
# (num_days x num_rooms) if the room is requested.
booking_requests = Array{Int,2}[]
for room in 1:num_rooms
for day in 1:num_days
# note: length_of_stay is 0 indexed to avoid unnecessary +/- 1
# on the indexing
for length_of_stay in 0:(num_days-day)
req = zeros(Int, (num_rooms, num_days))
req[room:room, day.+(0:length_of_stay)] .= 1
push!(booking_requests, req)
end
end
end
return model = SDDP.LinearPolicyGraph(;
stages = num_requests,
upper_bound = max_revenue,
sense = :Max,
optimizer = HiGHS.Optimizer,
) do sp, stage
@variable(
sp,
0 <= vacancy[room = 1:num_rooms, day = 1:num_days] <= 1,
SDDP.State,
Bin,
initial_value = 1
)
@variables(
sp,
begin
# Accept request for booking of room for length of time.
0 <= accept_request <= 1, Bin
# Accept a booking for an individual room on an individual day.
0 <= room_request_accepted[1:num_rooms, 1:num_days] <= 1, Bin
# Helper for JuMP.fix
req[1:num_rooms, 1:num_days]
end
)
for room in 1:num_rooms, day in 1:num_days
@constraints(
sp,
begin
# Update vacancy if we accept a room request
vacancy[room, day].out ==
vacancy[room, day].in - room_request_accepted[room, day]
# Can't accept a request of a filled room
room_request_accepted[room, day] <= vacancy[room, day].in
# Can't accept invididual room request if entire request is declined
room_request_accepted[room, day] <= accept_request
# Can't accept request if room not requested
room_request_accepted[room, day] <= req[room, day]
# Accept all individual rooms is entire request is accepted
room_request_accepted[room, day] + (1 - accept_request) >= req[room, day]
end
)
end
SDDP.parameterize(sp, booking_requests) do request
return JuMP.fix.(req, request)
end
@stageobjective(
sp,
sum(
(room + stage - 1) * room_request_accepted[room, day] for
room in 1:num_rooms for day in 1:num_days
)
)
end
end
function booking_management(duality_handler)
m_1_2_5 = booking_management_model(1, 2, 5)
SDDP.train(m_1_2_5; log_frequency = 5, duality_handler = duality_handler)
if duality_handler == SDDP.ContinuousConicDuality()
@test SDDP.calculate_bound(m_1_2_5) >= 7.25 - 1e-4
else
@test isapprox(SDDP.calculate_bound(m_1_2_5), 7.25, atol = 0.02)
end
m_2_2_3 = booking_management_model(2, 2, 3)
SDDP.train(m_2_2_3; log_frequency = 10, duality_handler = duality_handler)
if duality_handler == SDDP.ContinuousConicDuality()
@test SDDP.calculate_bound(m_1_2_5) > 6.13
else
@test isapprox(SDDP.calculate_bound(m_2_2_3), 6.13, atol = 0.02)
end
end
booking_management(SDDP.ContinuousConicDuality())
Test Passed
New version of HiGHS stalls booking_management(SDDP.LagrangianDuality())