diff --git a/.gitignore b/.gitignore index d920beb..fdc0eda 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ deps/build.log .vscode/ Manifest.toml +.editorconfig diff --git a/deps/build.jl b/deps/build.jl index f86a7ea..223d260 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -5,7 +5,7 @@ const verbose = "--verbose" in ARGS const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr"))) # Current version -version = "0.5.0" +version = "0.6.0" # Get current operating system osqp_platform = diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index a37531d..17c1d78 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -88,19 +88,37 @@ MOI.get(::Optimizer, ::MOI.SolverName) = "OSQP" MOI.supports(::Optimizer, ::MOI.Silent) = true function MOI.set(optimizer::Optimizer, ::MOI.Silent, value::Bool) - if optimizer.silent != value - optimizer.silent = value - if !MOI.is_empty(optimizer) - if optimizer.silent - OSQP.update_settings!(optimizer.inner; :verbose => false) - else - OSQP.update_settings!(optimizer.inner; :verbose => optimizer.settings[:verbose]) - end + optimizer.silent = value + if !MOI.is_empty(optimizer) + if optimizer.silent + OSQP.update_settings!(optimizer.inner; :verbose => false) + else + OSQP.update_settings!(optimizer.inner; :verbose => optimizer.settings[:verbose]) end end end MOI.get(optimizer::Optimizer, ::MOI.Silent) = optimizer.silent + + +MOI.supports(::Optimizer, ::MOI.TimeLimitSec) = true +function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, limit::Real) + MOI.set(model, OSQPSettings.TimeLimit(), limit) + return +end + +function MOI.set(model::Optimizer, attr::MOI.TimeLimitSec, ::Nothing) + delete!(model.settings, :time_limit) + OSQP.update_settings!(model.inner, time_limit=0.0) + return +end + +function MOI.get(model::Optimizer, ::MOI.TimeLimitSec) + return get(model.settings, :time_limit, nothing) +end + + + hasresults(optimizer::Optimizer) = optimizer.hasresults function MOI.empty!(optimizer::Optimizer) diff --git a/src/OSQP.jl b/src/OSQP.jl index d6aeb2c..4a7f7d5 100644 --- a/src/OSQP.jl +++ b/src/OSQP.jl @@ -2,6 +2,7 @@ module OSQP export OSQPMathProgBaseInterface using SparseArrays +using LinearAlgebra if isfile(joinpath(dirname(@__FILE__), "..", "deps", "deps.jl")) @@ -19,8 +20,8 @@ function __init__() depsdir = realpath(joinpath(dirname(@__FILE__), "..", "deps")) - if (vnum.major != 0 && vnum.minor != 4) - error("Current OSQP version installed is $(osqp_version()), but we require version 0.4.*. Delete the contents of the `$depsdir` directory except for the files `build.jl` and `.gitignore`, then rerun Pkg.build(\"OSQP\").") + if (vnum.major != 0 && vnum.minor != 6) + error("Current OSQP version installed is $(osqp_version()), but we require version 0.6.*. Delete the contents of the `$depsdir` directory except for the files `build.jl` and `.gitignore`, then rerun Pkg.build(\"OSQP\").") end end diff --git a/src/constants.jl b/src/constants.jl index 41ab7c3..3f4c215 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -2,7 +2,7 @@ const QDLDL_SOLVER = 0 const MKL_PARDISO_SOLVER = 1 # Define OSQP infinity constants -const OSQP_INFTY = 1e20 +const OSQP_INFTY = 1e30 # OSQP return values # https://github.com/oxfordcontrol/osqp/blob/master/include/constants.h diff --git a/src/interface.jl b/src/interface.jl index 6789df0..89164fd 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -112,6 +112,11 @@ function setup!(model::OSQP.Model; A = sparse(A) end + # Constructing upper triangular from P + if !istriu(P) + P = triu(P) + end + # Convert lower and upper bounds from Julia infinity to OSQP infinity u = min.(u, OSQP_INFTY) l = max.(l, -OSQP_INFTY) @@ -146,12 +151,16 @@ function setup!(model::OSQP.Model; pointer(q), pointer(l), pointer(u)) + # Perform setup - model.workspace = ccall((:osqp_setup, OSQP.osqp), Ptr{OSQP.Workspace}, - (Ptr{OSQP.Data}, Ptr{OSQP.Settings}), Ref(data), Ref(stgs)) + workspace = Ref{Ptr{OSQP.Workspace}}() + exitflag = ccall((:osqp_setup, OSQP.osqp), Cc_int, + (Ptr{Ptr{OSQP.Workspace}}, Ptr{OSQP.Data}, Ptr{OSQP.Settings}), + workspace, Ref(data), Ref(stgs)) + model.workspace = workspace[] end - if model.workspace == C_NULL + if exitflag != 0 error("Error in OSQP setup") end @@ -160,8 +169,7 @@ end function solve!(model::OSQP.Model, results::Results = Results()) ccall((:osqp_solve, OSQP.osqp), Cc_int, - (Ptr{OSQP.Workspace}, ), - model.workspace) + (Ptr{OSQP.Workspace}, ), model.workspace) workspace = unsafe_load(model.workspace) info = results.info copyto!(info, unsafe_load(workspace.info)) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 40327f0..8ffe830 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -112,9 +112,7 @@ function bridged_optimizer() end @testset "CachingOptimizer: unit" begin - excludes = [# TODO - "time_limit_sec", - # Quadratic constraints are not supported + excludes = [# Quadratic constraints are not supported "solve_qcp_edge_cases", # No method get(::Optimizer, ::MathOptInterface.ConstraintPrimal, ::MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64},MathOptInterface.Nonpositives}) "solve_duplicate_terms_vector_affine", diff --git a/test/basic.jl b/test/basic.jl index a9e31f3..5a5372a 100644 --- a/test/basic.jl +++ b/test/basic.jl @@ -145,14 +145,16 @@ tol = 1e-5 model = OSQP.Model() OSQP.setup!(model; P=problem[:P], q=problem[:q], - A=problem[:A], l=problem[:l], u=problem[:u], options...) + A=problem[:A], l=problem[:l], u=problem[:u], options...) results = OSQP.solve!(model) @test results.info.status == :Solved # Ensure solver will time out - OSQP.update_settings!(model, time_limit=1e-6, max_iter=1000000, check_termination=0) + OSQP.update_settings!(model, + eps_abs=1e-20, eps_rel=1e-20, + time_limit=1e-6, max_iter=1000000, check_termination=0) results_time_limit = OSQP.solve!(model) diff --git a/test/dual_infeasibility.jl b/test/dual_infeasibility.jl index 76411ad..5e75cd2 100644 --- a/test/dual_infeasibility.jl +++ b/test/dual_infeasibility.jl @@ -2,7 +2,7 @@ function setup_dual_infeasibility() options = Dict(:verbose => false, :eps_abs => 1e-05, :eps_rel => 1e-05, - :eps_prim_inf => 1e-18, + :eps_prim_inf => 1e-15, :check_termination => 1) return options end @@ -56,7 +56,7 @@ tol = 1e-5 OSQP.setup!(model; P=P, q=q, A=A, l=l, u=u, options...) # Warm start to avoid infeasibility detection at first step - OSQP.warm_start!(model, x=[25.; 25], y=[-2.; -2; -2; -2]) + OSQP.warm_start!(model, x=[50.; 30.], y=[-2.; -2; -2; -2]) results = OSQP.solve!(model)