diff --git a/Project.toml b/Project.toml index d0082c4..ae113f2 100644 --- a/Project.toml +++ b/Project.toml @@ -10,22 +10,24 @@ Constraints = "30f324ab-b02d-43f0-b619-e131c61659f7" Intervals = "d8418881-c3e1-53bb-8760-2df7ec849ed5" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" [compat] -Aqua = "0.8.7" -ConstraintCommons = "0.2.3" -ConstraintDomains = "0.3.13" -Constraints = "0.5.8" -ExplicitImports = "1.9.0" -Intervals = "1.10.0" -JET = "0.9.9" -JuMP = "1.23.2" -MathOptInterface = "1.31.2" -Test = "1.11.0" -TestItemRunner = "1.0.5" -TestItems = "1.0.0" +Aqua = "0.8" +ConstraintCommons = "0.2" +ConstraintDomains = "0.3.14" +Constraints = "0.5" +ExplicitImports = "1" +Intervals = "1" +JET = "0.9" +JuMP = "1" +MathOptInterface = "1" +Pkg = "1" +Test = "1" +TestItemRunner = "1" +TestItems = "1" julia = "1.10" [extras] diff --git a/src/ConstraintExplorer.jl b/src/ConstraintExplorer.jl index 2abec7e..d3b799b 100644 --- a/src/ConstraintExplorer.jl +++ b/src/ConstraintExplorer.jl @@ -2,12 +2,14 @@ module ConstraintExplorer #SECTION - Imports import ConstraintCommons -import ConstraintDomains: AbstractDomain, domain, RangeDomain, intersect_domains +import ConstraintDomains: AbstractDomain, RangeDomain +import ConstraintDomains: intersect_domains, domain import ConstraintDomains: Explorer, ExploreSettings, ExplorerState, explore! -import Constraints: concept +import Constraints: concept, USUAL_CONSTRAINTS import JuMP import MathOptInterface as MOI import Intervals: Interval, Closed, Open +import Pkg import TestItems: @testitem @@ -31,8 +33,7 @@ const VAR_TYPES = Union{MOI.ZeroOne, MOI.Integer} export DiscreteSet # Export: Constraints -export Error -export Intention, Predicate +export Intention export AllDifferent export AllEqual @@ -54,6 +55,9 @@ export Ordered export Regular export Sum +#Exports: Explorer +export configurations, solutions, non_solutions + #SECTION - Includes include("MOI_wrapper.jl") include("variables.jl") @@ -82,15 +86,23 @@ include("constraints/sum.jl") @testitem "ConstraintExplorer" default_imports=false begin using ConstraintDomains using ConstraintExplorer + import MathOptInterface as MOI using JuMP + using Test explorer = Model(ConstraintExplorer.Optimizer) @variable(explorer, 1≤X[1:4]≤4, Int) - # @constraint(explorer, X in AllDifferent()) + @constraint(explorer, X in AllDifferent()) + @constraint(explorer, X in DistDifferent()) optimize!(explorer) + + @test MOI.get(explorer, MOI.SolverName()) == "Constraint Explorer" + + X, X̅ = configurations(explorer) + @info "All Different" X X̅ end #SECTION - Main function (optional) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 2cf9b37..9bfb6c8 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -34,7 +34,7 @@ end MOI.is_empty(model::Optimizer) = model.c_max == 0 && model.d_max == 0 -struct CompleteSearchLimit <: MOI.AbstractModelAttribute end +struct CompleteSearchLimit <: MOI.AbstractOptimizerAttribute end function MOI.set(model::Optimizer, ::CompleteSearchLimit, n::Integer) settings = ExploreSettings( @@ -51,7 +51,7 @@ function MOI.get(model::Optimizer, ::CompleteSearchLimit) return model.explorer.settings.complete_search_limit end -struct MaxSamplings <: MOI.AbstractModelAttribute end +struct MaxSamplings <: MOI.AbstractOptimizerAttribute end function MOI.set(model::Optimizer, ::MaxSamplings, n::Integer) settings = ExploreSettings( @@ -68,7 +68,7 @@ function MOI.get(model::Optimizer, ::MaxSamplings) return model.explorer.settings.max_samplings end -struct Search <: MOI.AbstractModelAttribute end +struct Search <: MOI.AbstractOptimizerAttribute end function MOI.set(model::Optimizer, ::Search, s::Symbol) settings = ExploreSettings( @@ -85,7 +85,7 @@ function MOI.get(model::Optimizer, ::Search) return model.explorer.settings.search end -struct NumberOfSolutions <: MOI.AbstractModelAttribute end +struct NumberOfSolutions <: MOI.AbstractOptimizerAttribute end function MOI.set(model::Optimizer, ::NumberOfSolutions, n::Integer) settings = ExploreSettings( @@ -102,10 +102,6 @@ function MOI.get(model::Optimizer, ::NumberOfSolutions) return model.explorer.settings.solutions_limit end -struct Configurations <: MOI.AbstractModelAttribute end - -MOI.get(model::Optimizer, ::Configurations) = model.configurations - MOI.supports_incremental_interface(::Optimizer) = true function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike) @@ -119,3 +115,9 @@ function MOI.get(::Optimizer, ::MOI.SolverVersion) _uuid = Base.UUID("5800fd60-8556-4464-8d61-84ebf7a0bedb") return "v" * string(deps[_uuid].version) end + +solutions(model) = model.moi_backend.optimizer.model.explorer.state.solutions + +non_solutions(model) = model.moi_backend.optimizer.model.explorer.state.non_solutions + +configurations(model) = solutions(model), non_solutions(model) diff --git a/src/constraints.jl b/src/constraints.jl index 2a88b93..5d9a76a 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -1,10 +1,4 @@ -function constraint!(optimizer::Optimizer, f, vars) - cid = optimizer.c_max + 1 - optimizer.c_max = cid - @info "debug" f vars - push!(optimizer.concepts, cid => (f, vars)) - return cid -end +constraint!(model::Optimizer, f, vars) = push!(model.explorer, (f, vars)) struct MOIIntention{F <: Function} <: MOI.AbstractVectorSet f::F diff --git a/src/constraints/all_equal.jl b/src/constraints/all_equal.jl index c13bcee..110465a 100644 --- a/src/constraints/all_equal.jl +++ b/src/constraints/all_equal.jl @@ -29,8 +29,8 @@ function MOI.add_constraint( end function Base.copy(set::MOIAllEqual) - return MOIAllEqual( - copy(set.op), copy(set.pair_vars), copy(set.val), copy(set.dimension),) + val = set.val === nothing ? nothing : copy(set.val) + return MOIAllEqual(set.op, copy(set.pair_vars), val, copy(set.dimension)) end struct AllEqual{F <: Function, T1 <: Number, T2 <: Union{Nothing, Number}} <: diff --git a/src/constraints/circuit.jl b/src/constraints/circuit.jl index 487b854..a3234c2 100644 --- a/src/constraints/circuit.jl +++ b/src/constraints/circuit.jl @@ -26,7 +26,7 @@ function MOI.add_constraint( end function Base.copy(set::MOICircuit) - return MOICircuit(copy(set.op), copy(set.val), copy(set.dimension)) + return MOICircuit(set.op, copy(set.val), copy(set.dimension)) end struct Circuit{F <: Function, T <: Number} <: JuMP.AbstractVectorSet diff --git a/src/constraints/count.jl b/src/constraints/count.jl index b94bc1e..72e6c41 100644 --- a/src/constraints/count.jl +++ b/src/constraints/count.jl @@ -38,7 +38,7 @@ end function Base.copy(set::MOICount) return MOICount( - copy(set.op), copy(set.val), copy(set.vals), copy(set.dimension),) + set.op, copy(set.val), copy(set.vals), copy(set.dimension),) end struct Count{F <: Function, T1 <: Number, T2 <: Number} <: JuMP.AbstractVectorSet diff --git a/src/constraints/cumulative.jl b/src/constraints/cumulative.jl index ab56ef4..d72d011 100644 --- a/src/constraints/cumulative.jl +++ b/src/constraints/cumulative.jl @@ -35,7 +35,7 @@ end function Base.copy(set::MOICumulative) return MOICumulative( - copy(set.op), copy(set.pair_vars), copy(set.val), copy(set.dimension),) + set.op, copy(set.pair_vars), copy(set.val), copy(set.dimension),) end struct Cumulative{F <: Function, T1 <: Number, T2 <: Number, V <: VecOrMat{T1}} <: diff --git a/src/constraints/element.jl b/src/constraints/element.jl index 6542849..05c1e4d 100644 --- a/src/constraints/element.jl +++ b/src/constraints/element.jl @@ -29,7 +29,8 @@ function MOI.add_constraint( end function Base.copy(set::MOIElement) - return MOIElement(copy(set.id), copy(set.op), copy(set.val), copy(set.dimension)) + val = set.val === nothing ? nothing : copy(set.val) + return MOIElement(copy(set.id), set.op, val, copy(set.dimension)) end struct Element{I <: Integer, F <: Function, T <: Union{Nothing, Number}} <: diff --git a/src/constraints/maximum.jl b/src/constraints/maximum.jl index 6e6d211..ee36142 100644 --- a/src/constraints/maximum.jl +++ b/src/constraints/maximum.jl @@ -26,7 +26,7 @@ function MOI.add_constraint( end function Base.copy(set::MOIMaximum) - return MOIMaximum(copy(set.op), copy(set.val), copy(set.dimension)) + return MOIMaximum(set.op, copy(set.val), copy(set.dimension)) end struct Maximum{F <: Function, T <: Number} <: JuMP.AbstractVectorSet diff --git a/src/constraints/minimum.jl b/src/constraints/minimum.jl index ff38864..9bba592 100644 --- a/src/constraints/minimum.jl +++ b/src/constraints/minimum.jl @@ -26,7 +26,7 @@ function MOI.add_constraint( end function Base.copy(set::MOIMinimum) - return MOIMinimum(copy(set.op), copy(set.val), copy(set.dimension)) + return MOIMinimum(set.op, copy(set.val), copy(set.dimension)) end struct Minimum{F <: Function, T <: Number} <: JuMP.AbstractVectorSet diff --git a/src/constraints/n_values.jl b/src/constraints/n_values.jl index ca899ca..c117874 100644 --- a/src/constraints/n_values.jl +++ b/src/constraints/n_values.jl @@ -32,7 +32,7 @@ function MOI.add_constraint( end function Base.copy(set::MOINValues) - return MOINValues(copy(set.op), copy(set.val), copy(set.vals), copy(set.dimension)) + return MOINValues(set.op, copy(set.val), copy(set.vals), copy(set.dimension)) end struct NValues{F <: Function, T1 <: Number, T2 <: Number, V <: Vector{T2}} <: diff --git a/src/constraints/ordered.jl b/src/constraints/ordered.jl index cc3edaf..2ecd9f8 100644 --- a/src/constraints/ordered.jl +++ b/src/constraints/ordered.jl @@ -34,7 +34,7 @@ function MOI.add_constraint( end function Base.copy(set::MOIOrdered) - return MOIOrdered(copy(set.op), copy(set.pair_vars), copy(set.dimension)) + return MOIOrdered(set.op, copy(set.pair_vars), copy(set.dimension)) end struct Ordered{F <: Function, T <: Number, V <: Vector{T}} <: diff --git a/src/constraints/sum.jl b/src/constraints/sum.jl index 5b1c22d..87fb666 100644 --- a/src/constraints/sum.jl +++ b/src/constraints/sum.jl @@ -35,7 +35,7 @@ function MOI.add_constraint( end function Base.copy(set::MOISum) - return MOISum(copy(set.op), copy(set.pair_vars), copy(set.val), copy(set.dimension)) + return MOISum(set.op, copy(set.pair_vars), copy(set.val), copy(set.dimension)) end struct Sum{F <: Function, T1 <: Number, T2 <: Number, V <: Vector{T1}} <: diff --git a/src/variables.jl b/src/variables.jl index bf87c5c..850537b 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -9,7 +9,6 @@ function update_domain!(model::Optimizer, vidx, d) if haskey(model.explorer.domains, vidx) d₁ = model.explorer.domains[vidx] d₂ = isempty(d₁) ? d : intersect_domains(d₁, d) - @info "debug 3" d d₁ d₂ model.explorer.domains[vidx] = d₂ else push!(model.explorer.domains, vidx => d) @@ -21,7 +20,9 @@ struct DiscreteSet{T <: Number} <: MOI.AbstractScalarSet values::Vector{T} end DiscreteSet(values) = DiscreteSet(collect(values)) -DiscreteSet(values::T...) where {T <: Number} = DiscreteSet(collect(values)) +function DiscreteSet(v::T, values::T...) where {T <: Number} + return DiscreteSet(collect(Iterators.flatten([v, values]))) +end Base.copy(set::DiscreteSet) = DiscreteSet(copy(set.values)) @@ -64,7 +65,6 @@ end function MOI.add_constraint(optimizer::Optimizer, v::VI, lt::MOI.LessThan{T}, ) where {T <: AbstractFloat} - @info "Entering LessThan" v.value vidx = v.value push!(optimizer.compare_vars, vidx) if vidx ∈ optimizer.int_vars @@ -73,14 +73,12 @@ function MOI.add_constraint(optimizer::Optimizer, v::VI, lt::MOI.LessThan{T}, a = Float64(floatmin(Float32)) d = domain(Interval{Open, Closed}(a, lt.upper)) end - @info "debug" d update_domain!(optimizer, vidx, d) return CI{VI, MOI.LessThan{T}}(vidx) end function MOI.add_constraint(optimizer::Optimizer, v::VI, gt::MOI.GreaterThan{T}, ) where {T <: AbstractFloat} - @info "Entering GreaterThan" v.value vidx = v.value push!(optimizer.compare_vars, vidx) if vidx ∈ optimizer.int_vars @@ -96,16 +94,20 @@ end function MOI.add_constraint(optimizer::Optimizer, v::VI, i::MOI.Interval{T}, ) where {T <: Real} vidx = v.value - is_int = MOI.is_valid(optimizer, CI{VI, MOI.Integer}(vidx)) - d = make_domain(i.lower, i.upper, Val(is_int ? :range : :inter)) - _set_domain!(optimizer, vidx, d) + a, b = i.lower, i.upper + d = if MOI.is_valid(optimizer, CI{VI, MOI.Integer}(vidx)) + domain(Int(a):Int(b)) + else + domain(Interval{Closed, Closed}(a, b)) + end + optimizer.explorer.domains[vidx] = d return CI{VI, MOI.Interval{T}}(vidx) end function MOI.add_constraint(optimizer::Optimizer, v::VI, et::MOI.EqualTo{T}, ) where {T <: Number} vidx = v.value - _set_domain!(optimizer, vidx, et.value) + optimizer.explorer.domains[vidx] = domain(et.value) return CI{VI, MOI.EqualTo{T}}(vidx) end @@ -116,7 +118,6 @@ function MOI.add_constraint(model::Optimizer, v::VI, ::MOI.Integer) push!(model.int_vars, vidx) if vidx ∈ model.compare_vars x = model.explorer.domains[vidx] - @info x.domain model.compare_vars model.int_vars model.explorer.domains[vidx] = convert(RangeDomain, x) end return MOI.ConstraintIndex{VI, MOI.Integer}(vidx) diff --git a/test/runtests.jl b/test/runtests.jl index 82782f0..4c78e58 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,8 +8,8 @@ using Test using TestItemRunner @testset "Package tests: ConstraintExplorer" begin - # include("Aqua.jl") - # include("ExplicitImports.jl") - # include("JET.jl") + include("Aqua.jl") + include("ExplicitImports.jl") + include("JET.jl") include("TestItemRunner.jl") end