From d7d0b8b17b254f963b08ce282a3fbd2c3fd1cf6f Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 20 Feb 2023 07:39:18 +1300 Subject: [PATCH 1/5] Rename NISE as Dichotomy --- README.md | 4 +- src/algorithms/{NISE.jl => Dichotomy.jl} | 40 +++++++++---- test/algorithms/{NISE.jl => Dichotomy.jl} | 68 +++++++++++++++++++---- 3 files changed, 88 insertions(+), 24 deletions(-) rename src/algorithms/{NISE.jl => Dichotomy.jl} (77%) rename test/algorithms/{NISE.jl => Dichotomy.jl} (81%) diff --git a/README.md b/README.md index b9e837f..6d4a94c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ using JuMP import HiGHS import MultiObjectiveAlgorithms as MOA model = JuMP.Model(() -> MOA.Optimizer(HiGHS.Optimizer)) -set_optimizer_attribute(model, MOA.Algorithm(), MOA.NISE()) +set_optimizer_attribute(model, MOA.Algorithm(), MOA.Dichotomy()) set_optimizer_attribute(model, MOA.SolutionLimit(), 4) ``` @@ -44,11 +44,11 @@ Set the algorithm using the `MOA.Algorithm()` attribute. The value must be one of the algorithms supported by MOA: * `MOA.EpsilonConstraint()` + * `MOA.Dichotomy()` * `MOA.DominguezRios()` * `MOA.Hierarchical()` * `MOA.KirlikSayin()` * `MOA.Lexicographic()` [default] - * `MOA.NISE()` Consult their docstrings for details. diff --git a/src/algorithms/NISE.jl b/src/algorithms/Dichotomy.jl similarity index 77% rename from src/algorithms/NISE.jl rename to src/algorithms/Dichotomy.jl index 01cb258..53c8f37 100644 --- a/src/algorithms/NISE.jl +++ b/src/algorithms/Dichotomy.jl @@ -3,6 +3,24 @@ # v.2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at http://mozilla.org/MPL/2.0/. +""" + Dichotomy() + +A solver that implements the Non-Inferior Set Estimation algorithm of: + +Y. P. Aneja, K. P. K. Nair, (1979) Bicriteria Transportation Problem. Management +Science 25(1), 73-78. + +## Supported optimizer attributes + + * `MOA.SolutionLimit()` +""" +mutable struct Dichotomy <: AbstractAlgorithm + solution_limit::Union{Nothing,Int} + + Dichotomy() = new(nothing) +end + """ NISE() @@ -12,32 +30,32 @@ Cohon, J. L., Church, R. L., & Sheer, D. P. (1979). Generating multiobjective trade‐offs: An algorithm for bicriterion problems. Water Resources Research, 15(5), 1001-1010. +!!! note + This algorithm is identical to `Dichotomy()`, and it may be removed in a + future release. + ## Supported optimizer attributes * `MOA.SolutionLimit()` """ -mutable struct NISE <: AbstractAlgorithm - solution_limit::Union{Nothing,Int} - - NISE() = new(nothing) -end +NISE() = Dichotomy() -MOI.supports(::NISE, ::SolutionLimit) = true +MOI.supports(::Dichotomy, ::SolutionLimit) = true -function MOI.set(alg::NISE, ::SolutionLimit, value) +function MOI.set(alg::Dichotomy, ::SolutionLimit, value) alg.solution_limit = value return end -function MOI.get(alg::NISE, attr::SolutionLimit) +function MOI.get(alg::Dichotomy, attr::SolutionLimit) return something(alg.solution_limit, default(alg, attr)) end -function _solve_weighted_sum(model::Optimizer, alg::NISE, weight::Float64) +function _solve_weighted_sum(model::Optimizer, alg::Dichotomy, weight::Float64) return _solve_weighted_sum(model, alg, [weight, 1 - weight]) end -function _solve_weighted_sum(model::Optimizer, ::NISE, weights::Vector{Float64}) +function _solve_weighted_sum(model::Optimizer, ::Dichotomy, weights::Vector{Float64}) f = _scalarise(model.f, weights) MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.optimize!(model.inner) @@ -51,7 +69,7 @@ function _solve_weighted_sum(model::Optimizer, ::NISE, weights::Vector{Float64}) return status, SolutionPoint(X, Y) end -function optimize_multiobjective!(algorithm::NISE, model::Optimizer) +function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) if MOI.output_dimension(model.f) > 2 error("Only scalar or bi-objective problems supported.") end diff --git a/test/algorithms/NISE.jl b/test/algorithms/Dichotomy.jl similarity index 81% rename from test/algorithms/NISE.jl rename to test/algorithms/Dichotomy.jl index ffbd063..2b5d690 100644 --- a/test/algorithms/NISE.jl +++ b/test/algorithms/Dichotomy.jl @@ -3,7 +3,7 @@ # v.2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at http://mozilla.org/MPL/2.0/. -module TestNISE +module TestDichotomy using Test @@ -23,9 +23,9 @@ function run_tests() return end -function test_NISE_SolutionLimit() +function test_Dichotomy_SolutionLimit() model = MOA.Optimizer(HiGHS.Optimizer) - MOI.set(model, MOA.Algorithm(), MOA.NISE()) + MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) @test MOI.get(model, MOA.SolutionLimit()) == MOA.default(MOA.SolutionLimit()) MOI.set(model, MOA.SolutionLimit(), 1) @@ -36,7 +36,7 @@ end function test_moi_bolp_1() f = MOI.OptimizerWithAttributes( () -> MOA.Optimizer(HiGHS.Optimizer), - MOA.Algorithm() => MOA.NISE(), + MOA.Algorithm() => MOA.Dichotomy(), ) model = MOI.instantiate(f) MOI.set(model, MOI.Silent(), true) @@ -72,7 +72,7 @@ end function test_moi_bolp_1_maximize() f = MOI.OptimizerWithAttributes( () -> MOA.Optimizer(HiGHS.Optimizer), - MOA.Algorithm() => MOA.NISE(), + MOA.Algorithm() => MOA.Dichotomy(), ) model = MOI.instantiate(f) MOI.set(model, MOI.Silent(), true) @@ -108,7 +108,7 @@ end function test_moi_bolp_1_reversed() f = MOI.OptimizerWithAttributes( () -> MOA.Optimizer(HiGHS.Optimizer), - MOA.Algorithm() => MOA.NISE(), + MOA.Algorithm() => MOA.Dichotomy(), ) model = MOI.instantiate(f) MOI.set(model, MOI.Silent(), true) @@ -144,7 +144,7 @@ end function test_moi_bolp_1_scalar() f = MOI.OptimizerWithAttributes( () -> MOA.Optimizer(HiGHS.Optimizer), - MOA.Algorithm() => MOA.NISE(), + MOA.Algorithm() => MOA.Dichotomy(), ) model = MOI.instantiate(f) MOI.set(model, MOI.Silent(), true) @@ -198,7 +198,7 @@ function test_biobjective_knapsack() w = [80, 87, 68, 72, 66, 77, 99, 85, 70, 93, 98, 72, 100, 89, 67, 86, 91] f = MOI.OptimizerWithAttributes( () -> MOA.Optimizer(HiGHS.Optimizer), - MOA.Algorithm() => MOA.NISE(), + MOA.Algorithm() => MOA.Dichotomy(), ) model = MOI.instantiate(f) MOI.set(model, MOI.Silent(), true) @@ -234,7 +234,7 @@ end function test_infeasible() model = MOA.Optimizer(HiGHS.Optimizer) - MOI.set(model, MOA.Algorithm(), MOA.NISE()) + MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) MOI.set(model, MOI.Silent(), true) x = MOI.add_variables(model, 2) MOI.add_constraint.(model, x, MOI.GreaterThan(0.0)) @@ -250,7 +250,7 @@ end function test_unbounded() model = MOA.Optimizer(HiGHS.Optimizer) - MOI.set(model, MOA.Algorithm(), MOA.NISE()) + MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) MOI.set(model, MOI.Silent(), true) x = MOI.add_variables(model, 2) MOI.add_constraint.(model, x, MOI.GreaterThan(0.0)) @@ -264,6 +264,52 @@ function test_unbounded() return end +function test_bicriteria_transportation_nise() + m, n = 3, 4 + c = Float64[1 2 7 7; 1 9 3 4; 8 9 4 6] + d = Float64[4 4 3 4; 5 8 9 10; 6 2 5 1] + a = Float64[11, 3, 14, 16] + b = Float64[8, 19, 17] + model = MOA.Optimizer(HiGHS.Optimizer) + MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) + MOI.set(model, MOI.Silent(), true) + x = [MOI.add_variable(model) for i in 1:m, j in 1:n] + MOI.add_constraint.(model, x, MOI.GreaterThan(0.0)) + for j in 1:n + terms = [MOI.ScalarAffineTerm(1.0, x[i, j]) for i in 1:m] + MOI.add_constraint( + model, + MOI.ScalarAffineFunction(terms, 0.0), + MOI.EqualTo(a[j]), + ) + end + for i in 1:m + terms = [MOI.ScalarAffineTerm(1.0, x[i, j]) for j in 1:n] + MOI.add_constraint( + model, + MOI.ScalarAffineFunction(terms, 0.0), + MOI.EqualTo(b[i]), + ) + end + f = MOI.Utilities.vectorize([ + sum(c[i, j] * x[i, j] for i in 1:m, j in 1:n), + sum(d[i, j] * x[i, j] for i in 1:m, j in 1:n), + ]) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.optimize!(model) + N = MOI.get(model, MOI.ResultCount()) + y_sol = hcat(MOI.get.(model, MOI.ObjectiveValue.(1:N))...) + Y_N = Float64[143 156 176 186 208; 265 200 175 171 167] + @test isapprox(y_sol, Y_N; atol = 1e-6) + return +end + +function test_deprecated() + @test MOA.Dichotomy() == MOA.NISE() + return +end + end -TestNISE.run_tests() +TestDichotomy.run_tests() From 298e0de3356bc50da6dae869e7a2536e611c0a2c Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 20 Feb 2023 07:53:26 +1300 Subject: [PATCH 2/5] Fix --- src/algorithms/Dichotomy.jl | 6 +++++- test/algorithms/Dichotomy.jl | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/algorithms/Dichotomy.jl b/src/algorithms/Dichotomy.jl index 53c8f37..307f215 100644 --- a/src/algorithms/Dichotomy.jl +++ b/src/algorithms/Dichotomy.jl @@ -55,7 +55,11 @@ function _solve_weighted_sum(model::Optimizer, alg::Dichotomy, weight::Float64) return _solve_weighted_sum(model, alg, [weight, 1 - weight]) end -function _solve_weighted_sum(model::Optimizer, ::Dichotomy, weights::Vector{Float64}) +function _solve_weighted_sum( + model::Optimizer, + ::Dichotomy, + weights::Vector{Float64}, +) f = _scalarise(model.f, weights) MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f) MOI.optimize!(model.inner) diff --git a/test/algorithms/Dichotomy.jl b/test/algorithms/Dichotomy.jl index 2b5d690..02a50cf 100644 --- a/test/algorithms/Dichotomy.jl +++ b/test/algorithms/Dichotomy.jl @@ -306,7 +306,10 @@ function test_bicriteria_transportation_nise() end function test_deprecated() - @test MOA.Dichotomy() == MOA.NISE() + nise = MOA.NISE() + dichotomy = MOA.Dichotomy() + @test nise isa typeof(dichotomy) + @test nise.solution_limit === dichotomy.solution_limit return end From b34074e5fdc297c8fdc1064b036886a089a3263d Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 20 Feb 2023 10:22:19 +1300 Subject: [PATCH 3/5] More tests --- test/algorithms/Dichotomy.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/algorithms/Dichotomy.jl b/test/algorithms/Dichotomy.jl index 02a50cf..2608c98 100644 --- a/test/algorithms/Dichotomy.jl +++ b/test/algorithms/Dichotomy.jl @@ -26,6 +26,8 @@ end function test_Dichotomy_SolutionLimit() model = MOA.Optimizer(HiGHS.Optimizer) MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) + @test MOI.supports(MOA.Dichotomy(), MOI.SolutionLimit()) + @test MOI.supports(model, MOI.SolutionLimit()) @test MOI.get(model, MOA.SolutionLimit()) == MOA.default(MOA.SolutionLimit()) MOI.set(model, MOA.SolutionLimit(), 1) @@ -313,6 +315,24 @@ function test_deprecated() return end +function test_three_objective() + model = MOA.Optimizer(HiGHS.Optimizer) + MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) + MOI.set(model, MOI.Silent(), true) + MOI.Utilities.loadfromstring!( + model, + """ +variables: x +maxobjective: [1.0 * x, -1.0 * x, 2.0 * x + 2.0] +""", + ) + @test_throws( + ErrorException("Only scalar of bi-objective problems supported."), + MOI.optimize!(model), + ) + return +end + end TestDichotomy.run_tests() From 91505597bb837c65953aeb5d99b5b86f4385dd19 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 20 Feb 2023 10:30:14 +1300 Subject: [PATCH 4/5] Apply suggestions from code review --- test/algorithms/Dichotomy.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/algorithms/Dichotomy.jl b/test/algorithms/Dichotomy.jl index 2608c98..ca8ae86 100644 --- a/test/algorithms/Dichotomy.jl +++ b/test/algorithms/Dichotomy.jl @@ -26,8 +26,8 @@ end function test_Dichotomy_SolutionLimit() model = MOA.Optimizer(HiGHS.Optimizer) MOI.set(model, MOA.Algorithm(), MOA.Dichotomy()) - @test MOI.supports(MOA.Dichotomy(), MOI.SolutionLimit()) - @test MOI.supports(model, MOI.SolutionLimit()) + @test MOI.supports(MOA.Dichotomy(), MOA.SolutionLimit()) + @test MOI.supports(model, MOA.SolutionLimit()) @test MOI.get(model, MOA.SolutionLimit()) == MOA.default(MOA.SolutionLimit()) MOI.set(model, MOA.SolutionLimit(), 1) @@ -327,7 +327,7 @@ maxobjective: [1.0 * x, -1.0 * x, 2.0 * x + 2.0] """, ) @test_throws( - ErrorException("Only scalar of bi-objective problems supported."), + ErrorException("Only scalar or bi-objective problems supported."), MOI.optimize!(model), ) return From cd3fc9c848b97e567755e16a75f0d69b772e0f28 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 20 Feb 2023 10:44:21 +1300 Subject: [PATCH 5/5] Update src/algorithms/Dichotomy.jl --- src/algorithms/Dichotomy.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/Dichotomy.jl b/src/algorithms/Dichotomy.jl index 307f215..0118a07 100644 --- a/src/algorithms/Dichotomy.jl +++ b/src/algorithms/Dichotomy.jl @@ -6,7 +6,7 @@ """ Dichotomy() -A solver that implements the Non-Inferior Set Estimation algorithm of: +A solver that implements the algorithm of: Y. P. Aneja, K. P. K. Nair, (1979) Bicriteria Transportation Problem. Management Science 25(1), 73-78.