Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ module Bridges
import MathOptInterface as MOI
import JuMP

using ..MathOptVRP: Partition, Permutation
using ..MathOptVRP: Partition, PartitionPD, Permutation

include("PermutationToPartitionBridge.jl")

const _ALL_BRIDGE_TYPES = Any[PermutationToPartitionBridge]
const _ALL_BRIDGE_TYPES = Any[
PermutationToPartitionBridge,
PartitionToPartitionPDBridge,
]

"""
add_all_bridges(model::MOI.ModelLike, ::Type{T} = Float64)

Add all `MathOptVRP` bridges to `model`. The model is typically a
[`MOI.Bridges.LazyBridgeOptimizer`](@ref), allowing a backend that supports
[`Partition`](@ref), but not [`Permutation`](@ref), to accept permutation
variables.
[`PartitionPD`](@ref) as its most general route constructor to also accept
[`Partition`](@ref) and [`Permutation`](@ref) variables.
"""
function add_all_bridges(model::MOI.ModelLike, ::Type{T} = Float64) where {T}
for bridge_type in _ALL_BRIDGE_TYPES
Expand Down
9 changes: 9 additions & 0 deletions src/Bridges/PermutationToPartitionBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,12 @@ Bridges `Permutation(n)` to `Partition(n, 1)`, for backends that support
`Partition` but not `Permutation`.
"""
const PermutationToPartitionBridge{T} = SetConversionBridge{T,Partition,Permutation}

"""
PartitionToPartitionPDBridge{T} = SetConversionBridge{T,PartitionPD,Partition}

Bridges `Partition(n, k)` to `PartitionPD(n, 0, k)`, for backends that expose
only the more general pickup/delivery partition constructor.
"""
const PartitionToPartitionPDBridge{T} =
SetConversionBridge{T,PartitionPD,Partition}
22 changes: 22 additions & 0 deletions src/sets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,28 @@ struct PartitionPD <: MOI.AbstractVectorSet
num_trucks::Int
end

"""
Base.convert(::Type{PartitionPD}, s::Partition)

A plain partition is exactly a pickup/delivery partition with no pairs: all
`num_clients` nodes are ordinary services.
"""
Base.convert(::Type{PartitionPD}, s::Partition) =
PartitionPD(s.num_clients, 0, s.num_trucks)

"""
Base.convert(::Type{Partition}, s::PartitionPD)

The inverse conversion is defined only when `s` contains no pickup/delivery
pairs.
"""
function Base.convert(::Type{Partition}, s::PartitionPD)
if !iszero(s.num_pickup_deliveries)
throw(InexactError(:convert, Partition, s))
end
return Partition(s.num_services, s.num_trucks)
end

_pd_n_total(s::PartitionPD) = s.num_services + 2 * s.num_pickup_deliveries

MOI.dimension(s::PartitionPD) = _pd_n_total(s) * s.num_trucks
Expand Down
40 changes: 40 additions & 0 deletions test/Bridges/PermutationToPartitionBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,42 @@ function test_runtests_PermutationToPartition()
return
end

function test_runtests_PartitionToPartitionPD()
MOI.Bridges.runtests(
MathOptVRP.Bridges.PartitionToPartitionPDBridge,
model -> MOI.add_constrained_variables(model, MathOptVRP.Partition(3, 2)),
model -> MOI.add_constrained_variables(
model,
MathOptVRP.PartitionPD(3, 0, 2),
),
)
return
end

function test_add_all_bridges()
model = MOI.Bridges.LazyBridgeOptimizer(MOI.Utilities.Model{Float64}())
MathOptVRP.Bridges.add_all_bridges(model)
@test MathOptVRP.Bridges.PermutationToPartitionBridge{Float64} in
model.variable_bridge_types
@test MathOptVRP.Bridges.PartitionToPartitionPDBridge{Float64} in
model.variable_bridge_types
return
end


function test_supports_constrained_variable_PartitionToPartitionPD()
BT = MathOptVRP.Bridges.PartitionToPartitionPDBridge{Float64}
@test MOI.Bridges.Variable.supports_constrained_variable(
BT,
MathOptVRP.Partition,
)
@test !MOI.Bridges.Variable.supports_constrained_variable(
BT,
MathOptVRP.PartitionPD,
)
@test MOI.Bridges.added_constrained_variable_types(BT) ==
[(MathOptVRP.PartitionPD,)]
@test isempty(MOI.Bridges.added_constraint_types(BT))
return
end

Expand Down Expand Up @@ -90,6 +121,15 @@ function test_map_set()
return
end

function test_map_set_PartitionToPartitionPD()
BT = MathOptVRP.Bridges.PartitionToPartitionPDBridge{Float64}
@test MOI.Bridges.inverse_map_set(BT, MathOptVRP.Partition(3, 2)) ==
MathOptVRP.PartitionPD(3, 0, 2)
@test MOI.Bridges.map_set(BT, MathOptVRP.PartitionPD(3, 0, 2)) ==
MathOptVRP.Partition(3, 2)
return
end

end # module TestPermutationToPartitionBridge

TestPermutationToPartitionBridge.runtests()
11 changes: 11 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ import MathOptInterface as MOI
)
end

@testset "Partition <=> PartitionPD conversion" begin
@test convert(MathOptVRP.PartitionPD, MathOptVRP.Partition(5, 2)) ==
MathOptVRP.PartitionPD(5, 0, 2)
@test convert(MathOptVRP.Partition, MathOptVRP.PartitionPD(5, 0, 2)) ==
MathOptVRP.Partition(5, 2)
@test_throws InexactError convert(
MathOptVRP.Partition,
MathOptVRP.PartitionPD(3, 1, 2),
)
end

@testset "PartitionPD" begin
s = MathOptVRP.PartitionPD(2, 3, 4) # 2 services + 2*3 pd = 8 rows, 4 trucks
@test s.num_services == 2
Expand Down