From 321c4b48782e0d86d7ad77d725b730bec352d2a5 Mon Sep 17 00:00:00 2001 From: Azzaare Date: Mon, 22 Jul 2024 13:15:59 +0000 Subject: [PATCH] Fix breaking changes in CBLS, JuMP and MOI --- src/ConstraintModels.jl | 4 +-- src/cut.jl | 12 +++---- src/golomb.jl | 20 ++++-------- src/magic_square.jl | 18 +++-------- src/scheduling.jl | 2 +- src/sudoku.jl | 52 +++++++++++++++--------------- test/JuMP.jl | 35 ++++++++++---------- test/MOI_wrapper.jl | 71 ++--------------------------------------- 8 files changed, 62 insertions(+), 152 deletions(-) diff --git a/src/ConstraintModels.jl b/src/ConstraintModels.jl index 5303694..aea9dab 100644 --- a/src/ConstraintModels.jl +++ b/src/ConstraintModels.jl @@ -16,7 +16,7 @@ export qap export magic_square export mincut export n_queens -export scheduling +# export scheduling export sudoku include("assignment.jl") @@ -25,7 +25,7 @@ include("cut.jl") include("golomb.jl") include("magic_square.jl") include("n_queens.jl") -include("scheduling.jl") +# include("scheduling.jl") include("sudoku.jl") end diff --git a/src/cut.jl b/src/cut.jl index 581956c..7d86b9a 100644 --- a/src/cut.jl +++ b/src/cut.jl @@ -10,16 +10,12 @@ function mincut(graph, source, sink, interdiction, ::Val{:raw}) foreach(_ -> variable!(m, d), 0:n) # Extract error function from usual_constraint - e1 = (x; param=nothing, dom_size=n + 1) -> error_f( - usual_constraints[:ordered])(x; param, dom_size - ) - e2 = (x; param=nothing, dom_size=n + 1) -> error_f( - usual_constraints[:all_different])(x; param, dom_size - ) + e1 = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:ordered])(x; kargs...) + e2 = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:all_different])(x; kargs...) # Add constraint constraint!(m, e1, [source, separator, sink]) - constraint!(m, e2, 1:(n + 1)) + constraint!(m, e2, 1:(n+1)) # Add objective objective!(m, (x...) -> o_mincut(graph, x...; interdiction)) @@ -56,6 +52,6 @@ Compute the minimum cut of a graph. - `interdiction`: indicates the number of forbidden links - `modeler`: Default to `:JuMP`. """ -function mincut(graph; source, sink, interdiction =0, modeler = :JuMP) +function mincut(graph; source, sink, interdiction=0, modeler=:JuMP) return mincut(graph, source, sink, interdiction, Val(modeler)) end diff --git a/src/golomb.jl b/src/golomb.jl index ec2c447..0c2d97e 100644 --- a/src/golomb.jl +++ b/src/golomb.jl @@ -6,20 +6,14 @@ function golomb(n, L, ::Val{:raw}) foreach(_ -> variable!(m, d), 1:n) # Extract error function from usual_constraint - e1 = (x; param=nothing, dom_size=n) -> error_f( - usual_constraints[:all_different])(x; param, dom_size - ) - e2 = (x; param=nothing, dom_size=n) -> error_f( - usual_constraints[:all_equal_param])(x; param, dom_size - ) - e3 = (x; param=nothing, dom_size=n) -> error_f( - usual_constraints[:dist_different])(x; param, dom_size - ) + e1 = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:all_different])(x; kargs...) + e2 = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:all_equal])(x; kargs...) + e3 = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:dist_different])(x; kargs...) # # Add constraints constraint!(m, e1, 1:n) - constraint!(m, x -> e2(x; param=0), 1:1) - for i in 1:(n - 1), j in (i + 1):n, k in i:(n - 1), l in (k + 1):n + constraint!(m, e2, 1:1) + for i in 1:(n-1), j in (i+1):n, k in i:(n-1), l in (k+1):n (i, j) < (k, l) || continue constraint!(m, e3, [i, j, k, l]) end @@ -39,7 +33,7 @@ function golomb(n, L, ::Val{:JuMP}) @constraint(m, X in Ordered()) # for output convenience, keep them ordered # No two pairs have the same length - for i in 1:(n - 1), j in (i + 1):n, k in i:(n - 1), l in (k + 1):n + for i in 1:(n-1), j in (i+1):n, k in i:(n-1), l in (k+1):n (i, j) < (k, l) || continue @constraint(m, [X[i], X[j], X[k], X[l]] in DistDifferent()) end @@ -55,4 +49,4 @@ end Model the Golomb problem of `n` marks on the ruler `0:L`. The `modeler` argument accepts :raw, and :JuMP (default), which refer respectively to the solver internal model, the MathOptInterface model, and the JuMP model. """ -golomb(n, L=n^2; modeler = :JuMP) = golomb(n, L, Val(modeler)) +golomb(n, L=n^2; modeler=:JuMP) = golomb(n, L, Val(modeler)) diff --git a/src/magic_square.jl b/src/magic_square.jl index 9125ddc..89d2ead 100644 --- a/src/magic_square.jl +++ b/src/magic_square.jl @@ -7,11 +7,11 @@ function magic_square(n, ::Val{:JuMP}) @constraint(model, vec(X) in AllDifferent()) for i in 1:n - @constraint(model, X[i,:] in SumEqualParam(magic_constant)) - @constraint(model, X[:,i] in SumEqualParam(magic_constant)) + @constraint(model, X[i, :] in AllEqual(; val=magic_constant)) + @constraint(model, X[:, i] in AllEqual(; val=magic_constant)) end - @constraint(model, [X[i,i] for i in 1:n] in SumEqualParam(magic_constant)) - @constraint(model, [X[i,n + 1 - i] for i in 1:n] in SumEqualParam(magic_constant)) + @constraint(model, [X[i, i] for i in 1:n] in AllEqual(; val=magic_constant)) + @constraint(model, [X[i, n+1-i] for i in 1:n] in AllEqual(; val=magic_constant)) return model, X end @@ -21,12 +21,4 @@ end Create a model for the magic square problem of order `n`. The `modeler` argument accepts :JuMP (default), which refer to the solver the JuMP model. """ -magic_square(n; modeler = :JuMP) = magic_square(n, Val(modeler)) - - - - - - - - +magic_square(n; modeler=:JuMP) = magic_square(n, Val(modeler)) diff --git a/src/scheduling.jl b/src/scheduling.jl index 294ffb9..3595249 100644 --- a/src/scheduling.jl +++ b/src/scheduling.jl @@ -1,5 +1,5 @@ function scheduling(processing_times, due_dates, ::Val{:raw}) - m = model(; kind = :scheduling) + m = model(; kind=:scheduling) n = length(processing_times) # number of jobs max_time = sum(processing_times) diff --git a/src/sudoku.jl b/src/sudoku.jl index 39e6b00..28df489 100644 --- a/src/sudoku.jl +++ b/src/sudoku.jl @@ -2,7 +2,7 @@ function sudoku(n, start, ::Val{:raw}) N = n^2 d = domain(1:N) - m = model(;kind=:sudoku) + m = model(; kind=:sudoku) # Add variables if isnothing(start) @@ -12,19 +12,17 @@ function sudoku(n, start, ::Val{:raw}) end - e = (x; param=nothing, dom_size=N) -> error_f( - usual_constraints[:all_different])(x; param=param, dom_size=dom_size - ) + e = (x; kargs...) -> error_f(USUAL_CONSTRAINTS[:all_different])(x; kargs...) # Add constraints: line, columns; blocks - foreach(i -> constraint!(m, e, (i * N + 1):((i + 1) * N)), 0:(N - 1)) - foreach(i -> constraint!(m, e, [j * N + i for j in 0:(N - 1)]), 1:N) + foreach(i -> constraint!(m, e, (i*N+1):((i+1)*N)), 0:(N-1)) + foreach(i -> constraint!(m, e, [j * N + i for j in 0:(N-1)]), 1:N) - for i in 0:(n - 1) - for j in 0:(n - 1) + for i in 0:(n-1) + for j in 0:(n-1) vars = Vector{Int}() for k in 1:n - for l in 0:(n - 1) + for l in 0:(n-1) push!(vars, (j * n + l) * N + i * n + k) end end @@ -45,16 +43,16 @@ function sudoku(n, start, ::Val{:MOI}) foreach(i -> MOI.add_constraint(m, VI(i), DiscreteSet(1:N)), 1:N^2) # Add constraints: line, columns; blocks - foreach(i -> MOI.add_constraint(m, VOV(map(VI, (i * N + 1):((i + 1) * N))), - MOIAllDifferent(N)), 0:(N - 1)) - foreach(i -> MOI.add_constraint(m, VOV(map(VI, [j * N + i for j in 0:(N - 1)])), + foreach(i -> MOI.add_constraint(m, VOV(map(VI, (i*N+1):((i+1)*N))), + MOIAllDifferent(N)), 0:(N-1)) + foreach(i -> MOI.add_constraint(m, VOV(map(VI, [j * N + i for j in 0:(N-1)])), MOIAllDifferent(N)), 1:N) - for i in 0:(n - 1) - for j in 0:(n - 1) + for i in 0:(n-1) + for j in 0:(n-1) vars = Vector{Int}() for k in 1:n - for l in 0:(n - 1) + for l in 0:(n-1) push!(vars, (j * n + l) * N + i * n + k) end end @@ -72,16 +70,16 @@ function sudoku(n, start, ::Val{:JuMP}) @variable(m, 1 ≤ X[1:N, 1:N] ≤ N, Int) if !isnothing(start) for i in 1:N, j in 1:N - v_ij = start[i,j] + v_ij = start[i, j] if 1 ≤ v_ij ≤ N - @constraint(m, X[i,j] == v_ij) + @constraint(m, X[i, j] == v_ij) end end end for i in 1:N - @constraint(m, X[i,:] in AllDifferent()) # rows - @constraint(m, X[:,i] in AllDifferent()) # columns + @constraint(m, X[i, :] in AllDifferent()) # rows + @constraint(m, X[:, i] in AllDifferent()) # columns end for i in 0:(n-1), j in 0:(n-1) @constraint(m, vec(X[(i*n+1):(n*(i+1)), (j*n+1):(n*(j+1))]) in AllDifferent()) # blocks @@ -119,7 +117,7 @@ solution = value.(grid) display(solution, Val(:sudoku)) ``` """ -sudoku(n; start=nothing, modeler = :JuMP) = sudoku(n, start, Val(modeler)) +sudoku(n; start=nothing, modeler=:JuMP) = sudoku(n, start, Val(modeler)) @doc raw""" ```julia @@ -138,10 +136,10 @@ SudokuInstance(P::Pair{Tuple{Int, Int}, T}...) # again, default to 9×9 sudoku, ``` Constructor functions for the `SudokuInstance` `struct`. """ -mutable struct SudokuInstance{T <: Integer} <: AbstractMatrix{T} - A::AbstractMatrix{T} where {T <: Integer} +mutable struct SudokuInstance{T<:Integer} <: AbstractMatrix{T} + A::AbstractMatrix{T} where {T<:Integer} - function SudokuInstance(A::AbstractMatrix{T}) where {T <: Integer} + function SudokuInstance(A::AbstractMatrix{T}) where {T<:Integer} size(A, 1) == size(A, 2) || throw(error("Sodokus must be square; received matrix of size $(size(A, 1))×$(size(A, 2)).")) isequal(sqrt(size(A, 1)), isqrt(size(A, 1))) || throw(error("SudokuInstances must be able to split into equal boxes (e.g., a 9×9 SudokuInstance has three 3×3 squares). Size given is $(size(A, 1))×$(size(A, 2)).")) new{T}(A) @@ -172,8 +170,8 @@ Construct a `SudokuInstance` with the values `X` of a solver as input. function SudokuInstance(X::Dictionary) n = isqrt(length(X)) A = zeros(Int, n, n) - for (k,v) in enumerate(Base.Iterators.partition(X, n)) - A[k,:] = v + for (k, v) in enumerate(Base.Iterators.partition(X, n)) + A[k, :] = v end return SudokuInstance(A) end @@ -238,7 +236,7 @@ function _format_line_segment(r, col_pos, M) line = string() for k in axes(r, 1) n_spaces = 1 - Δ = maximum((ndigits(i) for i in M[:, (col_pos * sep_length) + k])) - ndigits(r[k]) + Δ = maximum((ndigits(i) for i in M[:, (col_pos*sep_length)+k])) - ndigits(r[k]) if Δ ≥ 0 n_spaces = Δ + 1 end @@ -259,7 +257,7 @@ function _format_line(r, M) line = _rules[:column] for i in 1:sep_length abs_sep_pos = sep_length * i - line *= _format_line_segment(r[(abs_sep_pos - sep_length + 1):abs_sep_pos], i - 1, M) + line *= _format_line_segment(r[(abs_sep_pos-sep_length+1):abs_sep_pos], i - 1, M) end return line diff --git a/test/JuMP.jl b/test/JuMP.jl index e7e9805..8611a18 100644 --- a/test/JuMP.jl +++ b/test/JuMP.jl @@ -11,10 +11,7 @@ @constraint(m, X in AllDifferent()) @constraint(m, X in AllEqual()) - @constraint(m, X in AllEqualParam(2)) - @constraint(m, X in AlwaysTrue()) @constraint(m, X[1:4] in DistDifferent()) - @constraint(m, X[1:2] in Eq()) @constraint(m, X in Ordered()) end @@ -64,15 +61,15 @@ end @variable(model, x in DiscreteSet(0:20)) @variable(model, y in DiscreteSet(0:20)) - @constraint(model, [x,y] in Predicate(v -> 6v[1] + 8v[2] >= 100 )) - @constraint(model, [x,y] in Predicate(v -> 7v[1] + 12v[2] >= 120 )) + @constraint(model, [x, y] in Predicate(v -> 6v[1] + 8v[2] >= 100)) + @constraint(model, [x, y] in Predicate(v -> 7v[1] + 12v[2] >= 120)) objFunc = v -> 12v[1] + 20v[2] @objective(model, Min, ScalarFunction(objFunc)) optimize!(model) @info solution_summary(model) - @info "JuMP: basic opt" value(x) value(y) (12*value(x)+20*value(y)) + @info "JuMP: basic opt" value(x) value(y) (12 * value(x) + 20 * value(y)) end @testset "JuMP: Chemical equilibrium" begin @@ -84,22 +81,22 @@ end @info "JuMP: $compounds_names ⟺ $mixture_name" value.(X) end -@testset "JuMP: Scheduling" begin - model, completion_times, start_times = scheduling(processing_times, due_times) - optimize!(model) - @info solution_summary(model) - @info "JuMP: scheduling" value.(start_times) value.(completion_times) processing_times due_times - @info (value.(start_times)+processing_times == value.(completion_times)) -end +# @testset "JuMP: Scheduling" begin +# model, completion_times, start_times = scheduling(processing_times, due_times) +# optimize!(model) +# @info solution_summary(model) +# @info "JuMP: scheduling" value.(start_times) value.(completion_times) processing_times due_times +# @info (value.(start_times)+processing_times == value.(completion_times)) +# end @testset "JuMP: Minimum Cut" begin graph = zeros(5, 5) - graph[1,2] = 1.0 - graph[1,3] = 2.0 - graph[1,4] = 3.0 - graph[2,5] = 1.0 - graph[3,5] = 2.0 - graph[4,5] = 3.0 + graph[1, 2] = 1.0 + graph[1, 3] = 2.0 + graph[1, 4] = 3.0 + graph[2, 5] = 1.0 + graph[3, 5] = 2.0 + graph[4, 5] = 3.0 model, links = mincut(graph; source=1, sink=5, interdiction=1) optimize!(model) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 623596d..39ffe3d 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -1,78 +1,11 @@ const MOI = MathOptInterface -const MOIT = MOI.Test -const MOIU = MOI.Utilities -const MOIB = MOI.Bridges - -const VOV = MOI.VectorOfVariables const VI = MOI.VariableIndex -const OPTIMIZER_CONSTRUCTOR = MOI.OptimizerWithAttributes( - CBLS.Optimizer, MOI.Silent() => true -) -const OPTIMIZER = MOI.instantiate(OPTIMIZER_CONSTRUCTOR) - -@testset "LocalSearchSolvers" begin - @test MOI.get(OPTIMIZER, MOI.SolverName()) == "LocalSearchSolvers" -end - -# @testset "supports_default_copy_to" begin -# @test MOIU.supports_default_copy_to(OPTIMIZER, false) -# # Use `@test !...` if names are not supported -# @test !MOIU.supports_default_copy_to(OPTIMIZER, true) -# end - -const BRIDGED = MOI.instantiate( - OPTIMIZER_CONSTRUCTOR, with_bridge_type = Float64 -) -const CONFIG = MOIT.TestConfig(atol=1e-6, rtol=1e-6) - -# @testset "Unit" begin -# # Test all the functions included in dictionary `MOI.Test.unittests`, -# # except functions "number_threads" and "solve_qcp_edge_cases." -# MOIT.unittest( -# BRIDGED, -# CONFIG, -# ["number_threads", "solve_qcp_edge_cases"] -# ) -# end - -# @testset "Modification" begin -# MOIT.modificationtest(BRIDGED, CONFIG) -# end - -# @testset "Continuous Linear" begin -# MOIT.contlineartest(BRIDGED, CONFIG) -# end - -# @testset "Continuous Conic" begin -# MOIT.contlineartest(BRIDGED, CONFIG) -# end - -# @testset "Integer Conic" begin -# MOIT.intconictest(BRIDGED, CONFIG) -# end @testset "MOI: examples" begin - # m = LocalSearchSolvers.Optimizer() - # MOI.add_variables(m, 3) - # MOI.add_constraint(m, VI(1), LS.DiscreteSet([1,2,3])) - # MOI.add_constraint(m, VI(2), LS.DiscreteSet([1,2,3])) - # MOI.add_constraint(m, VI(3), LS.DiscreteSet([1,2,3])) - - # MOI.add_constraint(m, VOV([VI(1),VI(2)]), LS.MOIPredicate(allunique)) - # MOI.add_constraint(m, VOV([VI(2),VI(3)]), LS.MOIAllDifferent(2)) - - # MOI.set(m, MOI.ObjectiveFunction{LS.ScalarFunction}(), LS.ScalarFunction(sum, VI(1))) - - # MOI.optimize!(m) - m1 = CBLS.Optimizer() MOI.add_variable(m1) - MOI.add_constraint(m1, VI(1), CBLS.DiscreteSet([1,2,3])) + MOI.add_constraint(m1, VI(1), CBLS.DiscreteSet([1, 2, 3])) m2 = CBLS.Optimizer() - MOI.add_constrained_variable(m2, CBLS.DiscreteSet([1,2,3])) - - # opt = CBLS.sudoku(3, modeler = :MOI) - # MOI.optimize!(opt) - # @info solution(opt) + MOI.add_constrained_variable(m2, CBLS.DiscreteSet([1, 2, 3])) end