From 9327efc4ac586ffa3df8da1d1040467fd9a58980 Mon Sep 17 00:00:00 2001 From: Leo Penneman Date: Wed, 15 Jul 2026 15:25:59 +0200 Subject: [PATCH 1/6] Removed changes from MOI_wrapper --- test/test_mathoptvrp.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_mathoptvrp.jl b/test/test_mathoptvrp.jl index 2a73b33..5ecd26f 100644 --- a/test/test_mathoptvrp.jl +++ b/test/test_mathoptvrp.jl @@ -1,6 +1,7 @@ using JuMP using Test using Vroom +import MathOptInterface as MOI using MathOptVRP # The wrapper has already stored routes per partition column on the @@ -9,6 +10,21 @@ function _vroom_read_routes(model, _nodes) return JuMP.unsafe_backend(model).routes end +# `Vroom.Optimizer` only supports `MathOptVRP.Partition`, not `List`, so +# `test_tsp` (which uses `List`) needs `MathOptVRP.ListToPartitionBridge` +# registered on the optimizer it gets from JuMP. This factory +# instantiates a bridged `Vroom.Optimizer` and registers the extra bridge +# directly on it before handing it back. +function _vroom_tsp_optimizer() + optimizer = MOI.instantiate(Vroom.Optimizer; with_bridge_type = Float64) + MOI.Bridges.add_bridge(optimizer, MathOptVRP.ListToPartitionBridge{Float64}) + return optimizer +end + +@testset "MathOptVRP.test_tsp" begin + MathOptVRP.Tests.test_tsp(_vroom_tsp_optimizer; read_routes = _vroom_read_routes) +end + @testset "MathOptVRP.test_vrp" begin # Vroom only supports the VRP variant here, so we invoke that test # directly rather than running `MathOptVRP.Tests.runtests`. From aded9753988371c586f288c5d47d568baaab3edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Aug 2026 08:44:11 +0200 Subject: [PATCH 2/6] Clean up with add_all_bridges --- test/test_mathoptvrp.jl | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/test/test_mathoptvrp.jl b/test/test_mathoptvrp.jl index 5ecd26f..bae31e1 100644 --- a/test/test_mathoptvrp.jl +++ b/test/test_mathoptvrp.jl @@ -4,29 +4,9 @@ using Vroom import MathOptInterface as MOI using MathOptVRP -# The wrapper has already stored routes per partition column on the -# inner `Vroom.Optimizer`, so `read_routes` just reads them back. -function _vroom_read_routes(model, _nodes) - return JuMP.unsafe_backend(model).routes -end - -# `Vroom.Optimizer` only supports `MathOptVRP.Partition`, not `List`, so -# `test_tsp` (which uses `List`) needs `MathOptVRP.ListToPartitionBridge` -# registered on the optimizer it gets from JuMP. This factory -# instantiates a bridged `Vroom.Optimizer` and registers the extra bridge -# directly on it before handing it back. -function _vroom_tsp_optimizer() +@testset "MathOptVRP" begin optimizer = MOI.instantiate(Vroom.Optimizer; with_bridge_type = Float64) - MOI.Bridges.add_bridge(optimizer, MathOptVRP.ListToPartitionBridge{Float64}) - return optimizer -end - -@testset "MathOptVRP.test_tsp" begin - MathOptVRP.Tests.test_tsp(_vroom_tsp_optimizer; read_routes = _vroom_read_routes) -end - -@testset "MathOptVRP.test_vrp" begin - # Vroom only supports the VRP variant here, so we invoke that test - # directly rather than running `MathOptVRP.Tests.runtests`. - MathOptVRP.Tests.test_vrp(Vroom.Optimizer; read_routes = _vroom_read_routes) + MathOptVRP.Bridges.add_all_bridges(optimizer) + MathOptVRP.Tests.test_tsp(optimizer) + MathOptVRP.Tests.test_vrp(Vroom.Optimizer) end From 9deaa3b05ff19ec9f50113900074d75ade0a6f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Aug 2026 10:10:38 +0200 Subject: [PATCH 3/6] Clean up --- test/test_mathoptvrp.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_mathoptvrp.jl b/test/test_mathoptvrp.jl index bae31e1..033caf4 100644 --- a/test/test_mathoptvrp.jl +++ b/test/test_mathoptvrp.jl @@ -4,9 +4,9 @@ using Vroom import MathOptInterface as MOI using MathOptVRP -@testset "MathOptVRP" begin - optimizer = MOI.instantiate(Vroom.Optimizer; with_bridge_type = Float64) - MathOptVRP.Bridges.add_all_bridges(optimizer) - MathOptVRP.Tests.test_tsp(optimizer) - MathOptVRP.Tests.test_vrp(Vroom.Optimizer) +@testset "$test" for test in [ + MathOptVRP.test_trp, + MathOptVRP.test_vrp, +] + test(Vroom.Optimizer) end From 52949414eb77473116377618bc0d174593a22fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Aug 2026 10:13:12 +0200 Subject: [PATCH 4/6] Fix --- test/test_mathoptvrp.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/test_mathoptvrp.jl b/test/test_mathoptvrp.jl index 033caf4..7da2f6f 100644 --- a/test/test_mathoptvrp.jl +++ b/test/test_mathoptvrp.jl @@ -4,9 +4,6 @@ using Vroom import MathOptInterface as MOI using MathOptVRP -@testset "$test" for test in [ - MathOptVRP.test_trp, - MathOptVRP.test_vrp, -] +@testset "$test" for test in [MathOptVRP.Tests.test_trp, MathOptVRP.Tests.test_vrp] test(Vroom.Optimizer) end From f1b9b7acd0468e189645ca415815b5755c03d4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 20 Aug 2026 10:23:25 +0200 Subject: [PATCH 5/6] fix --- test/test_mathoptvrp.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_mathoptvrp.jl b/test/test_mathoptvrp.jl index 7da2f6f..403005b 100644 --- a/test/test_mathoptvrp.jl +++ b/test/test_mathoptvrp.jl @@ -4,6 +4,6 @@ using Vroom import MathOptInterface as MOI using MathOptVRP -@testset "$test" for test in [MathOptVRP.Tests.test_trp, MathOptVRP.Tests.test_vrp] +@testset "$test" for test in [MathOptVRP.Tests.test_tsp, MathOptVRP.Tests.test_vrp] test(Vroom.Optimizer) end From 016e708ead2f3827c3dc61a45581b73f3fb7fad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 29 Aug 2026 16:46:40 +0200 Subject: [PATCH 6/6] up --- src/MOI_wrapper.jl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index a519671..a9c6a7f 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -195,7 +195,7 @@ function _parse_leaf(m::Optimizer, leaf::MOI.ScalarNonlinearFunction) error("Vroom: `:sum_distances` arg 1 must be a real matrix; got $(typeof(matrix))") items = _normalize_items(leaf.args[2]) length(items) >= 3 || - error("Vroom: `:sum_distances` vector must be `[depot; col; depot]` (≥ 3 entries)") + error("Vroom: `:sum_distances` vector must be `[depot; col; depot]`") items[1] isa Real || error("Vroom: depot_start must be a `Real`; got $(typeof(items[1]))") items[end] isa Real || @@ -203,6 +203,7 @@ function _parse_leaf(m::Optimizer, leaf::MOI.ScalarNonlinearFunction) depot_start = round(Int, items[1]) depot_end = round(Int, items[end]) depot_start == depot_end || error("Vroom: depot_start != depot_end is not supported") + depot = depot_start - 1 # MOI node values are one-based; Vroom is zero-based. # All interior items must be partition variables of one column. column = nothing for k = 2:(length(items)-1) @@ -222,7 +223,7 @@ function _parse_leaf(m::Optimizer, leaf::MOI.ScalarNonlinearFunction) end end column === nothing && error("Vroom: `:sum_distances` has no interior variables") - return matrix, depot_start, column::Int + return matrix, depot, column::Int end # JuMP can hand us the second `:sum_distances` arg as either a raw @@ -295,6 +296,7 @@ function MOI.optimize!(m::Optimizer) n_locations == size(durations, 2) || error("Vroom: distance matrix must be square; got $(size(durations))") n_clients = m.partition.num_clients + 0 <= depot < n_locations || error("Vroom: depot index $(depot + 1) is out of bounds") customer_locs = [loc for loc = 0:(n_locations-1) if loc != depot] length(customer_locs) == n_clients || error( "Vroom: matrix has $(length(customer_locs)) non-depot rows but Partition has ", @@ -329,7 +331,7 @@ function MOI.optimize!(m::Optimizer) truck_col = leaf_columns[r.vehicle+1] for step in r.steps step.type == "job" || continue - push!(routes[truck_col], step.location_index) + push!(routes[truck_col], step.location_index + 1) end end @@ -366,11 +368,10 @@ function MOI.get(m::Optimizer, attr::MOI.ObjectiveValue) return Float64(m.objective_value) end -# Vroom assigns customers itself, so individual `VariablePrimal` queries -# don't have a meaningful answer to return — the test reaches into -# `inner.routes` via `read_routes` instead. Return the depot value so -# `JuMP.value(::VariableRef)` at least doesn't throw. -function MOI.get(m::Optimizer, attr::MOI.VariablePrimal, ::MOI.VariableIndex) +# Map each zero-padded Partition proxy to its position in Vroom's route. +function MOI.get(m::Optimizer, attr::MOI.VariablePrimal, vi::MOI.VariableIndex) MOI.check_result_index_bounds(m, attr) - return 0.0 + row, column = m.variable_to_position[vi] + route = m.routes[column] + return Float64(row <= length(route) ? route[row] : 0) end