From 62f6b659cbe46834c73e1800b9698e1f3bb11051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Kr=C3=B6ger?= Date: Wed, 17 Jun 2020 13:38:27 +0200 Subject: [PATCH] table constraint and bugfix when vars not created (#169) * table constraint and bugfix when vars not created * v0.2.0 --- CHANGELOG.md | 4 +++ Project.toml | 4 ++- src/ConstraintSolver.jl | 4 +++ src/MOI_wrapper/MOI_wrapper.jl | 2 +- src/MOI_wrapper/constraints.jl | 57 ++++++-------------------------- src/constraints/all_different.jl | 13 ++++++++ src/constraints/indicator.jl | 27 +++++++++++++++ src/constraints/table.jl | 15 +++++++++ test/constraints/indicator.jl | 21 ++++++++++++ 9 files changed, 99 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4f8b04..f84fa910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # ConstrainSolver.jl - Changelog +## v0.2.0 (17th of June 2020) +- Bugfix for indicator constraints + - support for TableConstraint in Indicator + ## v0.1.8 (15th of June 2020) - Support for indicator constraints - i.e. `@constraint(m, b => { x + y <= 10 })` diff --git a/Project.toml b/Project.toml index e9944650..843c5abe 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,11 @@ name = "ConstraintSolver" uuid = "e0e52ebd-5523-408d-9ca3-7641f1cd1405" authors = ["Ole Kröger "] -version = "0.1.8" +version = "0.2.0" [deps] Formatting = "59287772-0a20-5a39-b81b-1366585eb4c0" +GMP_jll = "781609d7-10c4-51f6-84f2-b8444358ff6d" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" @@ -13,6 +14,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] Formatting = "^0.4.1" +GMP_jll = "=6.1.2" JSON = "~0.18, ~0.19, ~0.20, ~0.21" JuMP = "^0.21.0" MathOptInterface = "^0.9.1" diff --git a/src/ConstraintSolver.jl b/src/ConstraintSolver.jl index 31394280..bc98eade 100644 --- a/src/ConstraintSolver.jl +++ b/src/ConstraintSolver.jl @@ -152,6 +152,10 @@ function set_pvals!(com::CS.CoM, constraint::Constraint) pvals = vcat(pvals, collect(interval.from:interval.to)) end constraint.std.pvals = pvals + if constraint isa IndicatorConstraint + # TODO: This will always include 0/1 even if not in the inner constraint + constraint.inner_constraint.std.pvals = pvals + end end """ diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 49d1c849..4096b7aa 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -67,7 +67,7 @@ Copy constructor for the optimizer """ MOIU.supports_default_copy_to(model::Optimizer, copy_names::Bool) = !copy_names function MOI.copy_to(model::Optimizer, src::MOI.ModelLike; kws...) - return MOI.Utilities.automatic_copy_to(model, src; kws...) + return MOIU.automatic_copy_to(model, src; kws...) end MOI.supports(::Optimizer, ::MOI.RawParameter) = true diff --git a/src/MOI_wrapper/constraints.jl b/src/MOI_wrapper/constraints.jl index 719527ed..7fbd0591 100644 --- a/src/MOI_wrapper/constraints.jl +++ b/src/MOI_wrapper/constraints.jl @@ -10,8 +10,7 @@ function JuMP._build_indicator_constraint( _error::Function, variable::JuMP.AbstractVariableRef, constraint::JuMP.VectorConstraint, ::Type{MOI.IndicatorSet{A}}) where A - variable_indices = [v.index for v in constraint.func] - set = CS.IndicatorSet{A}(variable, MOI.VectorOfVariables(variable_indices), constraint.set, 0) + set = CS.IndicatorSet{A}(variable, MOI.VectorOfVariables(constraint.func), constraint.set, 1+length(constraint.func)) vov = VariableRef[variable] append!(vov, constraint.func) return JuMP.VectorConstraint(vov, set) @@ -248,16 +247,7 @@ function MOI.add_constraint( Int[v.value for v in vars.variables] ) - constraint = AllDifferentConstraint( - internals, - Int[], # pval_mapping will be filled later - Int[], # vertex_mapping => later - Int[], # vertex_mapping_bw => later - Int[], # di_ei => later - Int[], # di_ej => later - MatchingInit(), - Int[] - ) + constraint = init_constraint_struct(AllDifferentSetInternal, internals) push!(com.constraints, constraint) for (i, ind) in enumerate(constraint.std.indices) @@ -282,18 +272,7 @@ function MOI.add_constraint( Int[v.value for v in vars.variables] ) - constraint = TableConstraint( - internals, - RSparseBitSet(), - TableSupport(), # will be filled in init_constraint! - Int[], # will be changes later as it needs the number of words - TableResidues(), - Vector{TableBacktrackInfo}(), - Int[], # changed_vars - Int[], # unfixed_vars - Int[], # sum_min - Int[] # sum_max - ) + constraint = init_constraint_struct(TableSetInternal, internals) push!(com.constraints, constraint) for (i, ind) in enumerate(constraint.std.indices) @@ -399,28 +378,14 @@ function MOI.add_constraint( Int[v.value for v in vars.variables] ) - inner_constraint = nothing - if set.set isa CS.AllDifferentSetInternal - inner_internals = ConstraintInternals( - 0, - MOI.VectorOfVariables(vars.variables[2:end]), - set.set, - Int[v.value for v in vars.variables[2:end]] - ) - - inner_constraint = AllDifferentConstraint( - inner_internals, - Int[], # pval_mapping will be filled later - Int[], # vertex_mapping => later - Int[], # vertex_mapping_bw => later - Int[], # di_ei => later - Int[], # di_ej => later - MatchingInit(), - Int[] - ) - end - - + inner_internals = ConstraintInternals( + 0, + MOI.VectorOfVariables(vars.variables[2:end]), + set.set, + Int[v.value for v in vars.variables[2:end]] + ) + inner_constraint = init_constraint_struct(typeof(set.set), inner_internals) + con = IndicatorConstraint(internals, A, inner_constraint) push!(com.constraints, con) diff --git a/src/constraints/all_different.jl b/src/constraints/all_different.jl index 160298d5..f08c5ce5 100644 --- a/src/constraints/all_different.jl +++ b/src/constraints/all_different.jl @@ -28,6 +28,19 @@ function all_different(variables::Vector{Variable}) return constraint end +function init_constraint_struct(::Type{AllDifferentSetInternal}, internals) + AllDifferentConstraint( + internals, + Int[], # pval_mapping will be filled later + Int[], # vertex_mapping => later + Int[], # vertex_mapping_bw => later + Int[], # di_ei => later + Int[], # di_ej => later + MatchingInit(), + Int[] + ) +end + """ init_constraint!(com::CS.CoM, constraint::AllDifferentConstraint, fct::MOI.VectorOfVariables, set::AllDifferentSetInternal) diff --git a/src/constraints/indicator.jl b/src/constraints/indicator.jl index 85a6dd6c..6df1f3cc 100644 --- a/src/constraints/indicator.jl +++ b/src/constraints/indicator.jl @@ -1,3 +1,30 @@ +""" + init_constraint!( + com::CS.CoM, + constraint::IndicatorConstraint, + fct::Union{MOI.VectorOfVariables, VAF{T}}, + set::IS + ) where {A, T<:Real, ASS<:MOI.AbstractScalarSet, IS<:Union{IndicatorSet{A}, MOI.IndicatorSet{A, ASS}}} + +Initialize the inner constraint if it needs to be initialized +""" +function init_constraint!( + com::CS.CoM, + constraint::IndicatorConstraint, + fct::Union{MOI.VectorOfVariables, VAF{T}}, + set::IS +) where {A, T<:Real, ASS<:MOI.AbstractScalarSet, IS<:Union{IndicatorSet{A}, MOI.IndicatorSet{A, ASS}}} + inner_constraint = constraint.inner_constraint + if hasmethod( + init_constraint!, + (CS.CoM, typeof(inner_constraint), typeof(inner_constraint.std.fct), typeof(inner_constraint.std.set)), + ) + return init_constraint!(com, inner_constraint, inner_constraint.std.fct, inner_constraint.std.set) + end + # still feasible + return true +end + """ prune_constraint!( com::CS.CoM, diff --git a/src/constraints/table.jl b/src/constraints/table.jl index e477ed99..4f78610b 100644 --- a/src/constraints/table.jl +++ b/src/constraints/table.jl @@ -2,6 +2,21 @@ include("table/support.jl") include("table/residues.jl") include("table/RSparseBitSet.jl") +function init_constraint_struct(::Type{TableSetInternal}, internals) + TableConstraint( + internals, + RSparseBitSet(), + TableSupport(), # will be filled in init_constraint! + Int[], # will be changes later as it needs the number of words + TableResidues(), + Vector{TableBacktrackInfo}(), + Int[], # changed_vars + Int[], # unfixed_vars + Int[], # sum_min + Int[] # sum_max + ) +end + """ init_constraint!(com::CS.CoM, constraint::TableConstraint, fct::MOI.VectorOfVariables, set::TableSetInternal) diff --git a/test/constraints/indicator.jl b/test/constraints/indicator.jl index 23124d56..16cc6e51 100644 --- a/test/constraints/indicator.jl +++ b/test/constraints/indicator.jl @@ -177,4 +177,25 @@ end com = JuMP.backend(m).optimizer.model.inner @test is_solved(com) end + +@testset "Basic Table" begin + m = Model(CSJuMPTestOptimizer()) + @variable(m, 0 <= x <= 1, Int) + @variable(m, 0 <= y <= 1, Int) + @variable(m, a, Bin) + @constraint(m, x +y <= 1) + @constraint(m, a => {[x,y] in CS.TableSet([ + 0 1; + 1 1 + ])}) + @objective(m, Max, a) + optimize!(m) + @test JuMP.termination_status(m) == MOI.OPTIMAL + @test JuMP.objective_value(m) ≈ 1.0 + @test JuMP.value(a) ≈ 1.0 + @test JuMP.value(y) ≈ 1.0 + @test JuMP.value(x) ≈ 0.0 + com = JuMP.backend(m).optimizer.model.inner + @test is_solved(com) +end end \ No newline at end of file