diff --git a/Project.toml b/Project.toml index 8dbb0d3..da75854 100644 --- a/Project.toml +++ b/Project.toml @@ -1,17 +1,17 @@ -name = "ToQUBO" -uuid = "9a412ddf-83fa-43b6-9748-7843c851aa65" +name = "ToQUBO" +uuid = "9a412ddf-83fa-43b6-9748-7843c851aa65" authors = ["pedromxavier ", "pedroripper ", "joaquimg ", "AndradeTiago ", "bernalde "] -version = "0.1.7" +version = "0.1.8-dev" [deps] -MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" PseudoBooleanOptimization = "c8fa9a04-bc42-452d-8558-dc51757be744" -QUBOTools = "60eb5b62-0a39-4ddc-84c5-97d2adff9319" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +QUBOTools = "60eb5b62-0a39-4ddc-84c5-97d2adff9319" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" [compat] -MathOptInterface = "1" +MathOptInterface = "1" PseudoBooleanOptimization = "0.2" -QUBOTools = "0.9" -julia = "1.9" +QUBOTools = "0.9" +julia = "1.9" diff --git a/src/attributes/compiler.jl b/src/attributes/compiler.jl index e080571..b1775c2 100644 --- a/src/attributes/compiler.jl +++ b/src/attributes/compiler.jl @@ -162,7 +162,7 @@ When set, this boolean flag guarantees that every coefficient in the final formu struct Discretize <: CompilerAttribute end function MOI.get(model::Optimizer, ::Discretize)::Bool - return get(model.compiler_settings, :discretize, false) + return get(model.compiler_settings, :discretize, true) end function MOI.set(model::Optimizer, ::Discretize, flag::Bool) diff --git a/src/compiler/build.jl b/src/compiler/build.jl index 37e70db..e860ce8 100644 --- a/src/compiler/build.jl +++ b/src/compiler/build.jl @@ -29,7 +29,7 @@ function objective_function(model::Virtual.Model{T}, ::AbstractArchitecture) whe end for (ci, g) in model.g - ρ = model.ρ[ci] + ρ = MOI.get(model, Attributes.ConstraintEncodingPenalty(), ci) for (ω, c) in g model.H[ω] += ρ * c @@ -37,7 +37,7 @@ function objective_function(model::Virtual.Model{T}, ::AbstractArchitecture) whe end for (vi, h) in model.h - θ = model.θ[vi] + θ = MOI.get(model, Attributes.VariableEncodingPenalty(), vi) for (ω, c) in h model.H[ω] += θ * c @@ -45,7 +45,7 @@ function objective_function(model::Virtual.Model{T}, ::AbstractArchitecture) whe end for (ci, s) in model.s - η = model.η[ci] + η = MOI.get(model, Attributes.SlackVariableEncodingPenalty(), ci) for (ω, c) in s model.H[ω] += η * c diff --git a/src/compiler/compiler.jl b/src/compiler/compiler.jl index 8876305..f51548d 100644 --- a/src/compiler/compiler.jl +++ b/src/compiler/compiler.jl @@ -63,9 +63,6 @@ function compile!(model::Virtual.Model{T}, arch::AbstractArchitecture) where {T} # Add Regular Constraints constraints!(model, arch) - # Add Encoding Constraints - encoding_constraints!(model, arch) - # Compute penalties penalties!(model, arch) diff --git a/src/compiler/constraints.jl b/src/compiler/constraints.jl index 7621bab..e912281 100644 --- a/src/compiler/constraints.jl +++ b/src/compiler/constraints.jl @@ -88,7 +88,9 @@ function constraint( # Scalar Affine Equality Constraint: g(x) = a'x - b = 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -141,7 +143,9 @@ function constraint( # Scalar Affine Inequality Constraint: g(x) = a'x - b ≤ 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -157,9 +161,12 @@ function constraint( end # Slack Variable - e = Attributes.slack_variable_encoding_method(model, ci) S = (zero(T), abs(l)) - z = Encoding.encode!(model, ci, e, S) + z = if Attributes.discretize(model) + variable_ℤ!(model, ci, S) + else + variable_ℝ!(model, ci, S) + end for (ω, c) in Virtual.expansion(z) g[ω] += c @@ -217,7 +224,9 @@ function constraint( # Scalar Affine Inequality Constraint: g(x) = a'x - b ≥ 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -233,9 +242,12 @@ function constraint( end # Slack Variable - e = Attributes.slack_variable_encoding_method(model, ci) S = (zero(T), abs(u)) - z = Encoding.encode!(model, ci, e, S) + z = if Attributes.discretize(model) + variable_ℤ!(model, ci, S) + else + variable_ℝ!(model, ci, S) + end for (ω, c) in Virtual.expansion(z) g[ω] -= c @@ -279,7 +291,9 @@ function constraint( # Scalar Quadratic Equality Constraint: g(x) = x' Q x + a' x - b = 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -339,7 +353,9 @@ function constraint( # Scalar Quadratic Inequality Constraint: g(x) = x' Q x + a' x - b ≤ 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -358,9 +374,12 @@ function constraint( end # Slack Variable - e = Attributes.slack_variable_encoding_method(model, ci) S = (zero(T), abs(l)) - z = Encoding.encode!(model, ci, e, S) + z = if Attributes.discretize(model) + variable_ℤ!(model, ci, S) + else + variable_ℝ!(model, ci, S) + end for (ω, c) in Virtual.expansion(z) g[ω] += c @@ -407,7 +426,9 @@ function constraint( # Scalar Quadratic Inequality Constraint: g(x) = x' Q x + a' x - b ≥ 0 g = _parse(model, f, s, arch) - PBO.discretize!(g) + if Attributes.discretize(model) + PBO.discretize!(g) + end # Bounds & Slack Variable l, u = PBO.bounds(g) @@ -423,9 +444,12 @@ function constraint( end # Slack Variable - e = Attributes.slack_variable_encoding_method(model, ci) S = (zero(T), abs(u)) - z = Encoding.encode!(model, ci, e, S) + z = if Attributes.discretize(model) + variable_ℤ!(model, ci, S) + else + variable_ℝ!(model, ci, S) + end for (ω, c) in Virtual.expansion(z) g[ω] -= c @@ -488,12 +512,14 @@ function constraint( end g[w] = one(T) + + # Tell the compiler that quadratization is necessary + MOI.set(model, Attributes.Quadratize(), true) end end # Slack variable - e = Encoding.Mirror{T}() - z = Encoding.encode!(model, ci, e) + z = variable_𝔹!(model, ci) for (ω, c) in Virtual.expansion(z) g[ω] += c @@ -503,6 +529,7 @@ function constraint( return g^2 + h end +<<<<<<< HEAD function constraint( model::Virtual.Model{T}, @@ -592,3 +619,5 @@ function encoding_constraints!(model::Virtual.Model{T}, ::AbstractArchitecture) return nothing end +======= +>>>>>>> origin/master diff --git a/src/compiler/error.jl b/src/compiler/error.jl index 874370a..e2ee944 100644 --- a/src/compiler/error.jl +++ b/src/compiler/error.jl @@ -21,4 +21,20 @@ end function compilation_error(msg::Union{Nothing,String} = nothing) throw(CompilationError(msg)) +<<<<<<< HEAD +======= + + return nothing +end + +function compilation_error!(model::Virtual.Model, msg::Union{Nothing,String} = nothing; status::AbstractString = "") + # Update model status + MOI.set(model, Attributes.CompilationStatus(), MOI.OTHER_ERROR) + MOI.set(model, MOI.RawStatusString(), status) + + # Throw error + compilation_error(msg) + + return nothing +>>>>>>> origin/master end diff --git a/src/compiler/penalties.jl b/src/compiler/penalties.jl index acb6431..4ff65c2 100644 --- a/src/compiler/penalties.jl +++ b/src/compiler/penalties.jl @@ -13,7 +13,7 @@ function penalties!(model::Virtual.Model{T}, ::AbstractArchitecture) where {T} ρ = σ * (δ / ϵ + β) end - model.ρ[ci] = ρ + MOI.set(model, Attributes.ConstraintEncodingPenalty(), ci, ρ) end for (vi, h) in model.h @@ -24,7 +24,7 @@ function penalties!(model::Virtual.Model{T}, ::AbstractArchitecture) where {T} θ = σ * (δ / ϵ + β) end - model.θ[vi] = θ + MOI.set(model, Attributes.VariableEncodingPenalty(), vi, θ) end for (ci, s) in model.s @@ -35,7 +35,7 @@ function penalties!(model::Virtual.Model{T}, ::AbstractArchitecture) where {T} η = σ * (δ / ϵ + β) end - model.η[ci] = η + MOI.set(model, Attributes.SlackVariableEncodingPenalty(), ci, η) end return nothing diff --git a/src/compiler/variables.jl b/src/compiler/variables.jl index 7b3d235..9b5e4ef 100644 --- a/src/compiler/variables.jl +++ b/src/compiler/variables.jl @@ -71,12 +71,17 @@ function variables!(model::Virtual.Model{T}, ::AbstractArchitecture) where {T} end end - # Encode Variables if Attributes.stable_compilation(model) sort!(Ω; by = x -> x.value) end - + + # Encode Variables for x in Ω + # If variable was already encoded, skip + if haskey(model.source, x) + continue + end + if haskey(ℤ, x) variable_ℤ!(model, x, ℤ[x]) elseif haskey(ℝ, x) @@ -89,18 +94,24 @@ function variables!(model::Virtual.Model{T}, ::AbstractArchitecture) where {T} return nothing end -function variable_𝔹!(model::Virtual.Model{T}, x::VI) where {T} - Encoding.encode!(model, x, Encoding.Mirror{T}()) - - return nothing +function variable_𝔹!(model::Virtual.Model{T}, i::Union{VI,CI}) where {T} + return Encoding.encode!(model, i, Encoding.Mirror{T}()) end +<<<<<<< HEAD function variable_ℤ!(model::Virtual.Model{T}, x::VI, (a, b)::Tuple{A,B}) where {T,A<:Union{T,Nothing},B<:Union{T,Nothing}} if !isnothing(a) && !isnothing(b) let e = Attributes.variable_encoding_method(model, x) +======= +function variable_ℤ!(model::Virtual.Model{T}, vi::VI, (a, b)::Tuple{T,T}) where {T} + if isnothing(a) || isnothing(b) + error("Unbounded variable $(vi) ∈ ℤ") + else + let e = Attributes.variable_encoding_method(model, vi) +>>>>>>> origin/master S = (a, b) - Encoding.encode!(model, x, e, S) + return Encoding.encode!(model, vi, e, S) end elseif !isnothing(b) error("Unbounded variable $(x) ∈ (-∞, $(b)] ⊂ ℤ ") @@ -109,12 +120,29 @@ function variable_ℤ!(model::Virtual.Model{T}, x::VI, (a, b)::Tuple{A,B}) where else error("Unbounded variable $(x) ∈ ℤ") end +end - return nothing +function variable_ℤ!(model::Virtual.Model{T}, ci::CI, (a, b)::Tuple{T,T}) where {T} + if isnothing(a) || isnothing(b) + error("Unbounded variable $(ci) ∈ ℤ") + else + let e = Attributes.slack_variable_encoding_method(model, ci) + S = (a, b) + + return Encoding.encode!(model, ci, e, S) + end + end end +<<<<<<< HEAD function variable_ℝ!(model::Virtual.Model{T}, x::VI, (a, b)::Tuple{A,B}) where {T,A<:Union{T,Nothing},B<:Union{T,Nothing}} if !isnothing(a) && !isnothing(b) +======= +function variable_ℝ!(model::Virtual.Model{T}, vi::VI, (a, b)::Tuple{T,T}) where {T} + if isnothing(a) || isnothing(b) + error("Unbounded variable $(vi) ∈ ℝ") + else +>>>>>>> origin/master # TODO: Solve this bit-guessing magic??? (DONE) # IDEA: # Let x̂ ~ U[a, b], K = 2ᴺ, γ = [a, b] @@ -124,20 +152,20 @@ function variable_ℝ!(model::Virtual.Model{T}, x::VI, (a, b)::Tuple{A,B}) where # # For 𝔼[|xᵢ - x̂|] ≤ τ we have # N ≥ log₂(1 + |b - a| / 4τ) - # + # # where τ is the (absolute) tolerance # TODO: Add τ as parameter (DONE) # TODO: Move this comment to the documentation - let e = Attributes.variable_encoding_method(model, x) - n = Attributes.variable_encoding_bits(model, x) + let e = Attributes.variable_encoding_method(model, vi) + n = Attributes.variable_encoding_bits(model, vi) S = (a, b) if !isnothing(n) - Encoding.encode!(model, x, e, S, n) + return Encoding.encode!(model, vi, e, S, n) else - tol = Attributes.variable_encoding_atol(model, x) + tol = Attributes.variable_encoding_atol(model, vi) - Encoding.encode!(model, x, e, S; tol) + return Encoding.encode!(model, vi, e, S; tol) end end elseif !isnothing(b) @@ -150,6 +178,23 @@ function variable_ℝ!(model::Virtual.Model{T}, x::VI, (a, b)::Tuple{A,B}) where error("Unbounded variable $(x) ∈ ℝ") end +end - return nothing +function variable_ℝ!(model::Virtual.Model{T}, ci::CI, (a, b)::Tuple{T,T}) where {T} + if isnothing(a) || isnothing(b) + error("Unbounded slack variable $(ci) ∈ ℝ") + else + let e = Attributes.slack_variable_encoding_method(model, ci) + n = Attributes.slack_variable_encoding_bits(model, ci) + S = (a, b) + + if !isnothing(n) + return Encoding.encode!(model, ci, e, S, n) + else + tol = Attributes.slack_variable_encoding_atol(model, ci) + + return Encoding.encode!(model, ci, e, S; tol) + end + end + end end diff --git a/src/encoding/variables/interval/binary.jl b/src/encoding/variables/interval/binary.jl index 8e956d4..6bcefb6 100644 --- a/src/encoding/variables/interval/binary.jl +++ b/src/encoding/variables/interval/binary.jl @@ -38,7 +38,14 @@ function encode(var::Function, e::Binary{T}, S::Tuple{T,T}; tol::Union{T,Nothing a, b = integer_interval(S) if a == b +<<<<<<< HEAD return (VI[], PBO.PBF{VI,T}(a), nothing) +======= + y = VI[] + ξ = PBO.PBF{VI,T}(a) + + return (y, ξ, nothing) +>>>>>>> origin/master end M = trunc(Int, b - a) @@ -53,7 +60,7 @@ function encode(var::Function, e::Binary{T}, S::Tuple{T,T}; tol::Union{T,Nothing ], ) - return (y, ξ, nothing) # No penalty function + return (y, ξ, nothing) end function encoding_bits(::Binary{T}, S::Tuple{T,T}, tol::T) where {T} diff --git a/src/encoding/variables/set/one_hot.jl b/src/encoding/variables/set/one_hot.jl index 0a847b9..3bcde3b 100644 --- a/src/encoding/variables/set/one_hot.jl +++ b/src/encoding/variables/set/one_hot.jl @@ -99,7 +99,11 @@ function encode( a, b = S - Γ = collect(range(a, b; length = p)) + Γ = if p == 1 + T[(a + b) / 2] + else + collect(T, range(a, b; length = p)) + end return encode(var, e, Γ) end diff --git a/src/virtual/encoding.jl b/src/virtual/encoding.jl index 89073c4..fb58101 100644 --- a/src/virtual/encoding.jl +++ b/src/virtual/encoding.jl @@ -1,10 +1,19 @@ function Encoding.encode!(model::Model{T}, v::Variable{T}) where {T} x = source(v) + χ = penaltyfn(v) if x isa VI model.source[x] = v + + if !isnothing(χ) + model.h[x] = χ + end elseif x isa CI model.slack[x] = v + + if !isnothing(χ) + model.s[x] = χ + end end for y in target(v) diff --git a/src/wrapper.jl b/src/wrapper.jl index aea3831..8f87ed6 100644 --- a/src/wrapper.jl +++ b/src/wrapper.jl @@ -29,6 +29,7 @@ function MOI.optimize!(model::Optimizer) # De facto JuMP to QUBO Compilation let t = @elapsed ToQUBO.Compiler.compile!(model) + MOI.set(model, Attributes.CompilationStatus(), MOI.LOCALLY_SOLVED) MOI.set(model, Attributes.CompilationTime(), t) end @@ -36,7 +37,6 @@ function MOI.optimize!(model::Optimizer) MOI.optimize!(model.optimizer, model.target_model) MOI.set(model, MOI.RawStatusString(), MOI.get(model.optimizer, MOI.RawStatusString())) else - MOI.set(model, Attributes.CompilationStatus(), MOI.LOCALLY_SOLVED) MOI.set(model, MOI.RawStatusString(), "Compilation complete without an internal solver") end diff --git a/test/integration/examples/quadratic/quadratic.jl b/test/integration/examples/quadratic/quadratic.jl index 949f2e1..f95a6a3 100644 --- a/test/integration/examples/quadratic/quadratic.jl +++ b/test/integration/examples/quadratic/quadratic.jl @@ -1,9 +1,11 @@ include("quadratic_1.jl") +include("quadratic_2.jl") include("primes.jl") function test_quadratic() @testset "Quadratic Programs" verbose = true begin test_quadratic_1() + test_quadratic_2() test_primes() end end diff --git a/test/integration/examples/quadratic/quadratic_2.jl b/test/integration/examples/quadratic/quadratic_2.jl new file mode 100644 index 0000000..a1c942b --- /dev/null +++ b/test/integration/examples/quadratic/quadratic_2.jl @@ -0,0 +1,34 @@ +QUBOTools.PBO.varshow(v::VI) = QUBOTools.PBO.varshow(v.value) + + +""" +min x₁ + x₂ + st x₁² + x₂² >= 1 + x₁, x₂ ∈ {0, 1} + +min x₁ + x₂ + ρ (x₁² + x₂² - 1 + s)² + st x₁, x₂ ∈ {0, 1} + s ∈ [0, 1] + +""" +function test_quadratic_2() + model = Model(() -> ToQUBO.Optimizer()) + + @variable(model, x[1:2], Bin) + @objective(model, Min, x[1] + x[2]) + @constraint(model, c, x[1] * x[2] >= 1) + + optimize!(model) + + n, l, q, α, β = QUBOTools.qubo(model, :dense) + + Q = q + diagm(l) + + @show n + @show Q + + @test termination_status(model) === MOI.LOCALLY_SOLVED + @test get_attribute(model, Attributes.CompilationStatus()) === MOI.LOCALLY_SOLVED + + return nothing +end diff --git a/test/integration/interface.jl b/test/integration/interface.jl index b030811..aad9f3d 100644 --- a/test/integration/interface.jl +++ b/test/integration/interface.jl @@ -89,16 +89,16 @@ function test_interface_moi() # max x1 + x2 + x3 # st x1 + x2 <= 1 (c1) # x2 + x3 <= 1 (c2) - # x1 ∈ {0, 1} - # x2 ∈ {0, 1} - # x3 ∈ {0, 1} + # 0 <= x1 <= 1 + # 0 <= x2 <= 1 + # 0 <= x3 <= 1 model = MOI.instantiate( () -> ToQUBO.Optimizer(RandomSampler.Optimizer); with_bridge_type = Float64, ) - x, _ = MOI.add_constrained_variables(model, fill(MOI.ZeroOne(), 3)) + x, _ = MOI.add_constrained_variables(model, fill(MOI.Interval{Float64}(0.0, 1.0), 3)) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) @@ -150,6 +150,8 @@ function test_interface_moi() end # Solver Attributes + @test MOI.get(model, MOI.RawSolver()) isa RandomSampler.Optimizer + @test MOI.get(model, RandomSampler.RandomSeed()) === nothing MOI.set(model, RandomSampler.RandomSeed(), 13) @test MOI.get(model, RandomSampler.RandomSeed()) == 13 @@ -177,9 +179,9 @@ function test_interface_moi() MOI.set(model, Attributes.Optimization(), 3) @test MOI.get(model, Attributes.Optimization()) === 3 - @test MOI.get(model, Attributes.Discretize()) === false - MOI.set(model, Attributes.Discretize(), true) @test MOI.get(model, Attributes.Discretize()) === true + MOI.set(model, Attributes.Discretize(), false) + @test MOI.get(model, Attributes.Discretize()) === false @test MOI.get(model, Attributes.Quadratize()) === false MOI.set(model, Attributes.Quadratize(), true) @@ -205,10 +207,10 @@ function test_interface_moi() @test MOI.get(model, Attributes.VariableEncodingMethod(), x[1]) === nothing @test MOI.get(model, Attributes.VariableEncodingMethod(), x[2]) === nothing - MOI.set(model, Attributes.VariableEncodingMethod(), x[1], Encoding.Arithmetic()) + MOI.set(model, Attributes.VariableEncodingMethod(), x[1], Encoding.OneHot()) MOI.set(model, Attributes.VariableEncodingMethod(), x[2], Encoding.Arithmetic()) - @test MOI.get(model, Attributes.VariableEncodingMethod(), x[1]) isa Encoding.Arithmetic + @test MOI.get(model, Attributes.VariableEncodingMethod(), x[1]) isa Encoding.OneHot @test MOI.get(model, Attributes.VariableEncodingMethod(), x[2]) isa Encoding.Arithmetic # Variable Encoding ATol @@ -233,11 +235,11 @@ function test_interface_moi() @test MOI.get(model, Attributes.VariableEncodingBits(), x[1]) === nothing @test MOI.get(model, Attributes.VariableEncodingBits(), x[2]) === nothing - MOI.set(model, Attributes.VariableEncodingBits(), x[1], 1) - MOI.set(model, Attributes.VariableEncodingBits(), x[2], 2) + MOI.set(model, Attributes.VariableEncodingBits(), x[1], 10) + MOI.set(model, Attributes.VariableEncodingBits(), x[2], 20) - @test MOI.get(model, Attributes.VariableEncodingBits(), x[1]) == 1 - @test MOI.get(model, Attributes.VariableEncodingBits(), x[2]) == 2 + @test MOI.get(model, Attributes.VariableEncodingBits(), x[1]) == 10 + @test MOI.get(model, Attributes.VariableEncodingBits(), x[2]) == 20 # Variable Encoding Penalty @test MOI.get(model, Attributes.VariableEncodingPenaltyHint(), x[1]) === nothing @@ -297,44 +299,83 @@ function test_interface_moi() MOI.optimize!(model) let virtual_model = model.model.optimizer - @test MOI.get(virtual_model, Attributes.Architecture()) isa SuperArchitecture - @test MOI.get(virtual_model, Attributes.Architecture()).super === true + # MOI Attributes + @test MOI.get(model, MOI.ResultCount()) > 0 + @test MOI.get(model, MOI.SolveTimeSec()) > 0.0 + @test MOI.get(model, MOI.TerminationStatus()) isa MOI.TerminationStatusCode + @test MOI.get(model, MOI.RawStatusString()) isa String - @test MOI.get(virtual_model, Attributes.Optimization()) === 3 - - @test MOI.get(virtual_model, Attributes.Discretize()) === true - @test MOI.get(virtual_model, Attributes.Quadratize()) === true - @test MOI.get(virtual_model, Attributes.Warnings()) === false - - @test MOI.get(virtual_model, Attributes.QuadratizationMethod()) isa PBO.PTR_BG - @test MOI.get(virtual_model, Attributes.StableQuadratization()) === true + # MOI Variable Attributes + @test MOI.get(model, MOI.PrimalStatus()) isa MOI.ResultStatusCode + @test MOI.get(model, MOI.DualStatus()) isa MOI.ResultStatusCode - @test MOI.get(virtual_model, Attributes.DefaultVariableEncodingMethod()) isa Encoding.Unary - @test MOI.get(virtual_model, Attributes.VariableEncodingMethod(), x[1]) isa Encoding.Arithmetic - @test MOI.get(virtual_model, Attributes.VariableEncodingMethod(), x[2]) isa Encoding.Arithmetic - @test MOI.get(virtual_model, Attributes.VariableEncodingMethod(), x[3]) === nothing + @test MOI.get(model, MOI.VariablePrimal(), x[1]) >= 0.0 + @test MOI.get(model, MOI.VariablePrimal(), x[2]) >= 0.0 + @test MOI.get(model, MOI.VariablePrimal(), x[3]) >= 0.0 - @test MOI.get(virtual_model, Attributes.DefaultVariableEncodingATol()) ≈ 1E-6 - @test MOI.get(virtual_model, Attributes.VariableEncodingATol(), x[1]) ≈ 1 / 2 - @test MOI.get(virtual_model, Attributes.VariableEncodingATol(), x[2]) ≈ 1 / 3 - @test MOI.get(virtual_model, Attributes.VariableEncodingATol(), x[3]) === nothing + # ToQUBO Attribtues + @test MOI.get(model, Attributes.Optimization()) == 3 + @test Attributes.optimization(virtual_model) == 3 - @test MOI.get(virtual_model, Attributes.DefaultVariableEncodingBits()) == 3 - @test MOI.get(virtual_model, Attributes.VariableEncodingBits(), x[1]) == 1 - @test MOI.get(virtual_model, Attributes.VariableEncodingBits(), x[2]) == 2 - @test MOI.get(virtual_model, Attributes.VariableEncodingBits(), x[3]) === nothing + @test MOI.get(model, Attributes.Discretize()) === false + @test Attributes.discretize(virtual_model) === false - @test MOI.get(virtual_model, Attributes.VariableEncodingPenaltyHint(), x[1]) == -1.0 - @test MOI.get(virtual_model, Attributes.VariableEncodingPenaltyHint(), x[2]) === nothing - @test MOI.get(virtual_model, Attributes.VariableEncodingPenaltyHint(), x[3]) === nothing + @test MOI.get(model, Attributes.Quadratize()) === true + @test Attributes.quadratize(virtual_model) === true - @test MOI.get(virtual_model, Attributes.ConstraintEncodingPenaltyHint(), c[1]) == -10.0 - @test MOI.get(virtual_model, Attributes.ConstraintEncodingPenaltyHint(), c[2]) === nothing + @test MOI.get(model, Attributes.Warnings()) === false + @test Attributes.warnings(virtual_model) === false - @test MOI.get(virtual_model, Attributes.ConstraintEncodingPenalty(), c[1]) == -10.0 - @test MOI.get(virtual_model, Attributes.ConstraintEncodingPenalty(), c[2]) == -4.0 + @test MOI.get(model, Attributes.Architecture()) isa SuperArchitecture + @test MOI.get(model, Attributes.Architecture()).super === true + @test Attributes.architecture(virtual_model) isa SuperArchitecture + @test Attributes.architecture(virtual_model).super === true + + @test MOI.get(model, Attributes.QuadratizationMethod()) isa PBO.PTR_BG + @test MOI.get(model, Attributes.StableQuadratization()) === true + + @test MOI.get(model, Attributes.DefaultVariableEncodingMethod()) isa Encoding.Unary + @test MOI.get(model, Attributes.VariableEncodingMethod(), x[1]) isa Encoding.OneHot + @test MOI.get(model, Attributes.VariableEncodingMethod(), x[2]) isa Encoding.Arithmetic + @test MOI.get(model, Attributes.VariableEncodingMethod(), x[3]) === nothing + + @test MOI.get(model, Attributes.DefaultVariableEncodingATol()) ≈ 1E-6 + @test MOI.get(model, Attributes.VariableEncodingATol(), x[1]) ≈ 1 / 2 + @test MOI.get(model, Attributes.VariableEncodingATol(), x[2]) ≈ 1 / 3 + @test MOI.get(model, Attributes.VariableEncodingATol(), x[3]) === nothing + + @test MOI.get(model, Attributes.DefaultVariableEncodingBits()) == 3 + @test MOI.get(model, Attributes.VariableEncodingBits(), x[1]) == 10 + @test MOI.get(model, Attributes.VariableEncodingBits(), x[2]) == 20 + @test MOI.get(model, Attributes.VariableEncodingBits(), x[3]) === nothing + + @test MOI.get(model, Attributes.VariableEncodingPenaltyHint(), x[1]) == -1.0 + @test Attributes.variable_encoding_penalty_hint(virtual_model, x[1]) == -1.0 + @test MOI.get(model, Attributes.VariableEncodingPenaltyHint(), x[2]) === nothing + @test Attributes.variable_encoding_penalty_hint(virtual_model, x[2]) === nothing + @test MOI.get(model, Attributes.VariableEncodingPenaltyHint(), x[3]) === nothing + @test Attributes.variable_encoding_penalty_hint(virtual_model, x[3]) === nothing + + @test MOI.get(model, Attributes.VariableEncodingPenalty(), x[1]) == -1.0 + @test Attributes.variable_encoding_penalty(virtual_model, x[1]) == -1.0 + @test MOI.get(model, Attributes.VariableEncodingPenalty(), x[2]) === nothing + @test Attributes.variable_encoding_penalty(virtual_model, x[2]) === nothing + @test MOI.get(model, Attributes.VariableEncodingPenalty(), x[3]) === nothing + @test Attributes.variable_encoding_penalty(virtual_model, x[3]) === nothing + + @test MOI.get(model, Attributes.ConstraintEncodingPenaltyHint(), c[1]) == -10.0 + @test MOI.get(model, Attributes.ConstraintEncodingPenaltyHint(), c[2]) === nothing + + @test MOI.get(model, Attributes.ConstraintEncodingPenalty(), c[1]) == -10.0 + @test MOI.get(model, Attributes.ConstraintEncodingPenalty(), c[2]) <= 0.0 @test MOI.get(model, Attributes.SlackVariableEncodingPenalty(), c[1]) == -100.0 + + @test MOI.get(model, Attributes.CompilationStatus()) === MOI.LOCALLY_SOLVED + @test Attributes.compilation_status(virtual_model) === MOI.LOCALLY_SOLVED + + @test MOI.get(model, Attributes.CompilationTime()) > 0.0 + @test Attributes.compilation_time(virtual_model) > 0.0 end end end @@ -437,9 +478,9 @@ function test_interface_jump() JuMP.set_attribute(model, Attributes.Optimization(), 3) @test JuMP.get_attribute(model, Attributes.Optimization()) == 3 - @test JuMP.get_attribute(model, Attributes.Discretize()) === false - JuMP.set_attribute(model, Attributes.Discretize(), true) @test JuMP.get_attribute(model, Attributes.Discretize()) === true + JuMP.set_attribute(model, Attributes.Discretize(), false) + @test JuMP.get_attribute(model, Attributes.Discretize()) === false @test JuMP.get_attribute(model, Attributes.Quadratize()) === false JuMP.set_attribute(model, Attributes.Quadratize(), true) @@ -521,7 +562,7 @@ function test_interface_jump() @test JuMP.get_attribute(model, Attributes.Architecture()).super === true @test JuMP.get_attribute(model, Attributes.Optimization()) === 3 - @test JuMP.get_attribute(model, Attributes.Discretize()) === true + @test JuMP.get_attribute(model, Attributes.Discretize()) === false @test JuMP.get_attribute(model, Attributes.Quadratize()) === true @test JuMP.get_attribute(model, Attributes.Warnings()) === false diff --git a/test/unit/compiler/compiler.jl b/test/unit/compiler/compiler.jl index 5aec856..79bcf6f 100644 --- a/test/unit/compiler/compiler.jl +++ b/test/unit/compiler/compiler.jl @@ -1,8 +1,10 @@ include("constraints.jl") +include("error.jl") function test_compiler() @testset "□ Compiler" verbose = true begin test_compiler_constraints() + test_compiler_error() end return nothing diff --git a/test/unit/compiler/error.jl b/test/unit/compiler/error.jl new file mode 100644 index 0000000..1713d2c --- /dev/null +++ b/test/unit/compiler/error.jl @@ -0,0 +1,23 @@ +function test_compiler_error() + @testset "Compilation Error" begin + model = ToQUBO.Optimizer() + + @test_throws( + ToQUBO.Compiler.CompilationError, + ToQUBO.Compiler.compilation_error!( + model, + "Test Message"; + status = "Testing Compilation Error", + ) + ) + + @test MOI.get(model, Attributes.CompilationStatus()) == MOI.OTHER_ERROR + @test MOI.get(model, MOI.RawStatusString()) == "Testing Compilation Error" + + let e = ToQUBO.Compiler.CompilationError("Test Message") + @show sprint(Base.showerror, e) + end + end + + return nothing +end \ No newline at end of file