├── .codecov.yml ├── test ├── REQUIRE ├── test_jump.jl ├── .dontuse-probablyexpired.lic ├── jump_lp.jl ├── jump_soc.jl ├── runtests.jl └── jump_sdp.jl ├── .gitignore ├── REQUIRE ├── .travis.yml ├── LICENSE ├── README.md ├── src ├── objective.jl ├── variable.jl ├── LinkedInts.jl ├── attributes.jl ├── MathOptInterfaceMosek.jl └── constraint.jl └── examples └── chainsing.jl /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /test/REQUIRE: -------------------------------------------------------------------------------- 1 | #JuMP 0.2.0 0.3 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs/build/ 2 | docs/site/ 3 | -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.7 2 | MathOptInterface 0.8 0.9 3 | Mosek 0.9.9 4 | Compat 1 5 | -------------------------------------------------------------------------------- /test/test_jump.jl: -------------------------------------------------------------------------------- 1 | import MathOptInterfaceMosek 2 | using Base.Test 3 | using JuMP 4 | const MOI = MathOptInterface 5 | 6 | include("jump_sdp.jl") 7 | include("jump_lp.jl") 8 | include("jump_soc.jl") 9 | 10 | @testset "JuMP tests" begin 11 | test_jump_lp(MathOptInterfaceMosek.MosekSolver(LOG=0)) 12 | test_jump_soc(MathOptInterfaceMosek.MosekSolver(LOG=0)) 13 | test_jump_sdp(MathOptInterfaceMosek.MosekSolver(LOG=0)) 14 | end 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | ## Documentation: http://docs.travis-ci.com/user/languages/julia/ 2 | language: julia 3 | os: 4 | - linux 5 | julia: 6 | - 0.7 7 | - 1.0 8 | - 1.1 9 | notifications: 10 | email: false 11 | git: 12 | depth: 99999999 13 | before-install: 14 | - sudo apt-get install curl 15 | script: 16 | - julia -e 'import Pkg; Pkg.add(Pkg.PackageSpec(url=pwd()));' 17 | - julia -e 'import Pkg; Pkg.test("MathOptInterfaceMosek")' 18 | 19 | -------------------------------------------------------------------------------- /test/.dontuse-probablyexpired.lic: -------------------------------------------------------------------------------- 1 | VENDOR MOSEKLM 2 | FEATURE PTS MOSEKLM 8.00 22-aug-2018 uncounted \ 3 | VENDOR_STRING=-1*-1*-1*0 HOSTID=ANY SN=1 TS_OK SIGN="0A39 DBA9 \ 4 | 3685 CF88 9547 A613 78F9 FE1B 9B6D D4EB E75C 44AE 7076 6864 \ 5 | 06B0 1742 94EC B55B 8549 EC68 E930 C501 4A26 46F7 0E47 1A6E \ 6 | 471A AE14 63C2 C090" SIGN2="1C1F 03B7 518B 3B47 1B16 EFC4 DC09 \ 7 | BB60 2110 4C29 1B53 AEFA DF52 4FC8 B87E 0DBE 95B9 4730 AC15 \ 8 | 6BF5 C6AE 0E26 EE81 B187 1027 845A D709 6C0A 95F3 F9B7" 9 | FEATURE PTON MOSEKLM 8.00 22-aug-2018 uncounted \ 10 | VENDOR_STRING=-1*-1*-1*0 HOSTID=ANY SN=1 TS_OK SIGN="12DE CCB9 \ 11 | B780 3F2A C895 2432 2ECF A255 A5C9 219B 90D1 F52B CB46 7B2E \ 12 | 5A2E 0C4D CEED 1E61 342C 686F 029A 1D7B 5718 392E D9C1 8D2B \ 13 | C9BA 8051 6080 A5DA" SIGN2="1A61 14FE 0FD8 1E84 0204 7386 D296 \ 14 | 501D 836A B2EC C858 EB73 A89D 4AB0 700C 1222 D938 302A 0BE5 \ 15 | 10C8 B062 8EB4 5FA7 DE8D B607 4055 C72E 8E8D E87D 6694" 16 | -------------------------------------------------------------------------------- /test/jump_lp.jl: -------------------------------------------------------------------------------- 1 | function test_jump_lp(solver) 2 | @testset "Linear Programming" begin 3 | @testset "LP1" begin 4 | # simple 2 variable, 1 constraint problem 5 | # min -x 6 | # st x + y <= 1 (x + y - 1 ∈ Nonpositives) 7 | # x, y >= 0 (x, y ∈ Nonnegatives) 8 | 9 | m = Model(solver=solver) 10 | @variable(m, x >= 0.0) 11 | @variable(m, y >= 0.0) 12 | @objective(m, Min, -x) 13 | 14 | @constraint(m, x + y <= 1) 15 | 16 | JuMP.solve(m) 17 | 18 | @test JuMP.isattached(m) 19 | @test JuMP.hasvariableresult(m) 20 | 21 | @test JuMP.terminationstatus(m) == MOI.Success 22 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 23 | 24 | @test JuMP.resultvalue(x) ≈ 1.0 atol=1e-6 25 | @test JuMP.resultvalue(y) ≈ 0.0 atol=1e-6 26 | @test JuMP.objectivevalue(m) ≈ -1.0 atol=1e-6 27 | 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ulf Worsøe, Mosek ApS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``MathOptInterfaceMosek`` is the 2 | [MathOptInterface.jl](https://github.com/JuliaOpt/MathOptInterface.jl) 3 | implementation for the MOSEK solver. The low-level solver API for MOSEK is 4 | found in the package [Mosek.jl](https://github.com/JuliaOpt/Mosek.jl). 5 | 6 | The ``Mosek`` specific model object (used for example with JuMP) is created as 7 | ```julia 8 | using MathOptInterfaceMosek 9 | model = MosekOptimizer() 10 | ``` 11 | hence to use Mosek in a JuMP model, do, e.g., 12 | ```julia 13 | using JuMP 14 | using MathOptInterfaceMosek 15 | model = Model(with_optimizer(MosekOptimizer, QUIET=false, INTPNT_CO_TOL_DFEAS=1e-7)) 16 | ``` 17 | The parameter `QUIET` is a special parameter that when set to `true` 18 | disables all Mosek printing output. 19 | All other parameters can be found in the [Mosek doc](https://docs.mosek.com/8.1/capi/param-groups.html#doc-param-groups). 20 | Note that the prefix `MSK_IPAR_` (for integer parameters), `MSK_DPAR_` (for 21 | floating point parameters) or `MSK_SPAR_` (for string parameters) are optional. 22 | If they are not given, they are inferred from the type of the value. For 23 | instance, in the example above, as `1e-7` is a floating point number, the 24 | parameters name used is `MSK_DPAR_INTPNT_CO_TOL_DFEAS`. 25 | -------------------------------------------------------------------------------- /test/jump_soc.jl: -------------------------------------------------------------------------------- 1 | function test_jump_soc(solver) 2 | @testset "Second-order Cone Programming" begin 3 | @testset "SOC1" begin 4 | m = Model(solver=solver) 5 | @variable(m, x) 6 | @variable(m, y) 7 | @variable(m, t >= 0) 8 | @objective(m, Min, t) 9 | @constraint(m, x + y >= 1) 10 | @constraint(m, [t,x,y] in MOI.SecondOrderCone(3)) 11 | 12 | JuMP.solve(m) 13 | 14 | @test JuMP.isattached(m) 15 | @test JuMP.hasvariableresult(m) 16 | @test JuMP.terminationstatus(m) == MOI.Success 17 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 18 | 19 | @test JuMP.objectivevalue(m) ≈ sqrt(1/2) atol=1e-6 20 | 21 | @test JuMP.resultvalue.([x,y,t]) ≈ [0.5,0.5,sqrt(1/2)] atol=1e-3 22 | 23 | end 24 | 25 | @testset "RotatedSOC1" begin 26 | m = Model(solver=solver) 27 | 28 | @variable(m, x[1:5] >= 0) 29 | @variable(m, 0 <= u <= 5) 30 | @variable(m, v) 31 | @variable(m, t1 == 1) 32 | @variable(m, t2 == 1) 33 | 34 | @objective(m, Max, v) 35 | 36 | @constraint(m, [t1/sqrt(2),t2/sqrt(2),x...] in MOI.RotatedSecondOrderCone(7)) 37 | @constraint(m, [x[1]/sqrt(2), u/sqrt(2), v] in MOI.RotatedSecondOrderCone(3)) 38 | 39 | JuMP.solve(m) 40 | 41 | @test JuMP.isattached(m) 42 | @test JuMP.hasvariableresult(m) 43 | @test JuMP.terminationstatus(m) == MOI.Success 44 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 45 | 46 | @test JuMP.resultvalue.(x) ≈ [1,0,0,0,0] atol=1e-2 47 | @test JuMP.resultvalue(u) ≈ 5 atol=1e-4 48 | @test JuMP.resultvalue(v) ≈ sqrt(5) atol=1e-6 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /src/objective.jl: -------------------------------------------------------------------------------- 1 | const ObjF = Union{MOI.SingleVariable, MOI.ScalarAffineFunction{Float64}} 2 | 3 | # TODO get with SingleVariable 4 | function MathOptInterface.get(m::MosekModel, ::MathOptInterface.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}) 5 | vid = allocatedlist(m.x_block) 6 | subj = getindexes(m.x_block, vid) 7 | coeffs = getclist(m.task, subj) 8 | constant = getcfix(m.task) 9 | MOI.ScalarAffineFunction(MOI.VariableIndex.(vid), coeffs, constant) 10 | end 11 | 12 | MOI.supports(::MosekModel,::MOI.ObjectiveFunction{<:ObjF}) = true 13 | MOI.supports(::MosekModel,::MOI.ObjectiveSense) = true 14 | 15 | 16 | function MOI.set(m::MosekModel, ::MOI.ObjectiveFunction, func::MOI.SingleVariable) 17 | numvar = getnumvar(m.task) 18 | c = zeros(Float64,numvar) 19 | vid = ref2id(func.variable) 20 | subj = getindex(m.x_block, vid) 21 | 22 | c[subj] = 1.0 23 | 24 | putclist(m.task,Int32[1:numvar...],c) 25 | putcfix(m.task,0.0) 26 | end 27 | 28 | function MOI.set(m::MosekModel, ::MOI.ObjectiveFunction, func::MOI.ScalarAffineFunction{Float64}) 29 | numvar = getnumvar(m.task) 30 | c = zeros(Float64,numvar) 31 | subj = getindexes(m.x_block, ref2id.(map(t -> t.variable_index, func.terms))) 32 | for i in 1:length(subj) 33 | c[subj[i]] += func.terms[i].coefficient 34 | end 35 | 36 | putclist(m.task,Int32[1:numvar...],c) 37 | putcfix(m.task,func.constant) 38 | end 39 | 40 | function MOI.modify(m::MosekModel, ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}, change :: MOI.ScalarConstantChange) 41 | putcfix(m.task,change.new_constant) 42 | end 43 | 44 | function MOI.modify(m::MosekModel, ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}, change :: MOI.ScalarCoefficientChange) 45 | vid = ref2id(change.variable) 46 | subj = getindexes(m.x_block, vid) 47 | putcj(m.task, Int32(subj), change.new_coefficient) 48 | end 49 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | # To ensure that tests can run on travis we have to do a little 2 | # hackadoodle here. The tests require a license file. We include 3 | # a license file that is only valid for one day (the day when 4 | # change is submitted). 5 | # If there is no valid license file, we default to that file. 6 | 7 | using MathOptInterfaceMosek 8 | 9 | 10 | using Test 11 | 12 | using MathOptInterface 13 | const MOI = MathOptInterface 14 | const MOIT = MOI.Test 15 | const MOIU = MOI.Utilities 16 | const MOIB = MOI.Bridges 17 | 18 | const optimizer = Mosek.Optimizer(QUIET = true, service = "mosek://solve.mosek.com:30080") 19 | # 1e-3 needed for rotatedsoc3 test 20 | const config = MOIT.TestConfig(atol=1e-3, rtol=1e-3, query=false) 21 | 22 | # Mosek does not support names 23 | MOIU.@model(Model, 24 | (MOI.Integer,), 25 | (MOI.EqualTo, MOI.LessThan, MOI.GreaterThan), 26 | (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.RotatedSecondOrderCone), 27 | (), 28 | (MOI.SingleVariable,), 29 | (MOI.ScalarAffineFunction,), 30 | (MOI.VectorOfVariables,), 31 | (MOI.VectorAffineFunction,)) 32 | 33 | @testset "SolverName" begin 34 | @test MOI.get(optimizer, MOI.SolverName()) == "Mosek" 35 | end 36 | 37 | @testset "Copy" begin 38 | # Currently does not work because get is missing for ConstraintSet 39 | # and ConstraintFunction, see https://github.com/JuliaOpt/MathOptInterfaceMosek.jl/issues/50 40 | #MOIT.copytest(optimizer, Model{Float64}()) 41 | end 42 | 43 | @testset "Unit" begin 44 | # Mosek does not support names 45 | cached = MOIU.CachingOptimizer(Model{Float64}(), optimizer) 46 | MOIT.unittest(MOIB.QuadtoSOC{Float64}(MOIB.SplitInterval{Float64}(cached)), 47 | config, 48 | [# Does not support quadratic objective yet, needs 49 | # https://github.com/JuliaOpt/MathOptInterface.jl/issues/529 50 | "solve_qp_edge_cases", 51 | # Find objective bound of 0.0 which is lower than 4.0 52 | "solve_objbound_edge_cases", 53 | # Cannot put multiple bound sets of the same type on a variable 54 | "solve_integer_edge_cases" 55 | ]) 56 | end 57 | 58 | @testset "Continuous linear problems" begin 59 | # linear1 is failing for two reasons 60 | # * it does not remove constraints using a variable if this variable is deleted, see https://github.com/JuliaOpt/MathOptInterface.jl/issues/511 61 | # * it does not support duplicated terms, see https://github.com/JuliaOpt/MathOptInterfaceMosek.jl/issues/41 62 | MOIT.contlineartest(optimizer, config, ["linear1"]) 63 | end 64 | 65 | # include("contquadratic.jl") 66 | # @testset "Continuous quadratic problems" begin 67 | # # contquadratictest(GurobiSolver()) 68 | # end 69 | 70 | @testset "Continuous conic problems" begin 71 | MOIT.contconictest(MOIB.SquarePSD{Float64}(MOIB.RootDet{Float64}(MOIB.GeoMean{Float64}(optimizer))), 72 | config, ["exp", "rootdets", "logdet"]) 73 | end 74 | 75 | @testset "Mixed-integer linear problems" begin 76 | MOIT.intlineartest(optimizer, config, ["int2"]) 77 | end 78 | 79 | #include("test_jump.jl") 80 | -------------------------------------------------------------------------------- /src/variable.jl: -------------------------------------------------------------------------------- 1 | 2 | candelete(m::MosekModel,ref::MOI.VariableIndex) = isvalid(m,ref) && m.x_numxc[ref2id(ref)] == 0 3 | isvalid(m::MosekModel, ref::MOI.VariableIndex) = allocated(m.x_block,ref2id(ref)) 4 | 5 | 6 | MOI.add_variables(m::MosekModel, N :: I) where { I <: Integer } = MOI.add_variables(m,UInt(N)) 7 | function MOI.add_variables(m::MosekModel, N :: UInt) 8 | ids = [ allocatevariable(m, 1) for i in 1:N ] 9 | 10 | m.publicnumvar += N 11 | 12 | idxs = Vector{Int}(undef,N) 13 | for i in 1:Int(N) 14 | getindexes(m.x_block,ids[i],idxs,i) 15 | end 16 | 17 | bnd = zeros(Float64,N) 18 | putvarboundlist(m.task, 19 | convert(Vector{Int32}, idxs), 20 | fill(MSK_BK_FR,N), 21 | bnd,bnd) 22 | 23 | # if DEBUG 24 | if DEBUG 25 | for i in idxs 26 | putvarname(m.task,Int32(i),"x$i") 27 | end 28 | end 29 | 30 | [ id2vref(id) for id in ids] 31 | end 32 | 33 | function MOI.add_variable(m::MosekModel) 34 | N = 1 35 | id = allocatevariable(m, 1) 36 | m.publicnumvar += N 37 | bnd = Vector{Float64}(undef,N) 38 | subj = convert(Vector{Int32}, getindexes(m.x_block, id)) 39 | putvarboundlist(m.task, 40 | subj, 41 | fill(MSK_BK_FR,N), 42 | bnd,bnd) 43 | # if DEBUG 44 | if DEBUG 45 | for i in subj 46 | putvarname(m.task,Int32(i),"x$i") 47 | end 48 | end 49 | 50 | id2vref(id) 51 | end 52 | 53 | 54 | function MOI.delete(m::MosekModel, refs::Vector{MOI.VariableIndex}) 55 | ids = Int[ ref2id(ref) for ref in refs ] 56 | 57 | if ! all(id -> m.x_numxc[id] == 0, ids) 58 | error("Cannot delete a variable while a bound constraint is defined on it") 59 | elseif ! all(ref -> candelete(m,ref),refs) 60 | throw(CannotDelete()) 61 | else 62 | sizes = Int[blocksize(m.x_block,id) for id in ids] 63 | N = sum(sizes) 64 | m.publicnumvar -= length(refs) 65 | indexes = Array{Int}(undef,N) 66 | offset = 1 67 | for i in 1:length(ids) 68 | getindexes(m.x_block,ids[i],indexes,offset) 69 | offset += sizes[i] 70 | end 71 | 72 | # clear all non-zeros in columns 73 | putacollist(m.task, 74 | indexes, 75 | zeros(Int64,N), 76 | zeros(Int64,N), 77 | Int32[], 78 | Float64[]) 79 | putclist(m.task,indexes,zeros(Int64,N)) 80 | # clear bounds 81 | bnd = zeros(Float64,N) 82 | putvarboundlist(m.task, 83 | indexes, 84 | fill(MSK_BK_FX,N), 85 | bnd,bnd) 86 | 87 | if DEBUG 88 | for i in ids 89 | putvarname(m.task,Int32(ids),"deleted$i") 90 | end 91 | end 92 | 93 | for i in 1:length(ids) 94 | deleteblock(m.x_block,ids[i]) 95 | end 96 | end 97 | end 98 | 99 | function MOI.delete(m::MosekModel, ref::MOI.VariableIndex) 100 | if m.x_numxc[ref2id(ref)] != 0 101 | error("Cannot delete a variable while a bound constraint is defined on it") 102 | elseif ! candelete(m,ref) 103 | throw(CannotDelete()) 104 | else 105 | id = ref2id(ref) 106 | 107 | m.publicnumvar -= 1 108 | 109 | indexes = convert(Array{Int32,1},getindexes(m.x_block,id)) 110 | N = blocksize(m.x_block,id) 111 | 112 | # clear all non-zeros in columns 113 | for i in indexes 114 | putcj(m.task,i,0.0) 115 | end 116 | putacollist(m.task, 117 | indexes, 118 | zeros(Int64,N), 119 | zeros(Int64,N), 120 | Int32[], 121 | Float64[]) 122 | # clear bounds 123 | bnd = zeros(Float64,N) 124 | putvarboundlist(m.task, 125 | indexes, 126 | fill(MSK_BK_FX,N), 127 | bnd,bnd) 128 | if DEBUG 129 | for i in indexes 130 | putvarname(m.task,Int32(i),"deleted$i") 131 | end 132 | end 133 | 134 | deleteblock(m.x_block,id) 135 | end 136 | end 137 | 138 | 139 | 140 | ############################################################################### 141 | ## ATTRIBUTES 142 | -------------------------------------------------------------------------------- /src/LinkedInts.jl: -------------------------------------------------------------------------------- 1 | using Printf # 2 | 3 | mutable struct LinkedInts 4 | next :: Vector{Int} 5 | prev :: Vector{Int} 6 | 7 | free_ptr :: Int 8 | free_cap :: Int 9 | root :: Int 10 | 11 | block :: Vector{Int} 12 | size :: Vector{Int} 13 | end 14 | 15 | LinkedInts(capacity=128) = 16 | LinkedInts(Int[],Int[], 17 | 0,0,0, 18 | Int[], 19 | Int[]) 20 | 21 | allocatedlist(s::LinkedInts) = findall(s.block .> 0) 22 | allocated(s::LinkedInts, id :: Int) = id > 0 && id <= length(s.block) && s.block[id] > 0 23 | blocksize(s::LinkedInts, id :: Int) = s.size[id] 24 | Base.length(s::LinkedInts) = length(s.next) 25 | numblocks(s::LinkedInts) = length(s.block) 26 | 27 | function Base.show(f::IO, s::LinkedInts) 28 | print(f,"LinkedInts(\n") 29 | @printf(f," Number of blocks: %d\n", length(s.block)) 30 | @printf(f," Number of elements: %d\n", length(s.next)) 31 | print(f," Blocks:\n") 32 | for i in 1:length(s.block) 33 | if s.block[i] > 0 34 | idxs = getindexes(s,i) 35 | print(f," #$i: $idxs\n") 36 | end 37 | end 38 | p = s.free_ptr 39 | freelst = Int[] 40 | while p > 0 41 | push!(freelst,p) 42 | p = s.prev[p] 43 | end 44 | print(f," Free: $freelst\n") 45 | 46 | println(f," free_ptr = $(s.free_ptr)") 47 | println(f," root = $(s.root)") 48 | println(f," next = $(s.next)") 49 | println(f," prev = $(s.prev)") 50 | 51 | print(f,")") 52 | end 53 | 54 | """ 55 | ensurefree(s::LinkedInts, N :: Int) 56 | 57 | Ensure that there are at least `N` elements free, and allocate as necessary. 58 | """ 59 | function ensurefree(s::LinkedInts, N :: Int) 60 | if s.free_cap < N 61 | num = N - s.free_cap 62 | 63 | cap = length(s.next) 64 | first = cap+1 65 | last = cap+num 66 | 67 | append!(s.next, Int[i+1 for i in first:last]) 68 | append!(s.prev, Int[i-1 for i in first:last]) 69 | 70 | s.next[last] = 0 71 | s.prev[first] = s.free_ptr 72 | if s.prev[first] > 0 73 | s.next[s.prev[first]] = first 74 | end 75 | s.free_ptr = last 76 | s.free_cap += num 77 | 78 | return num 79 | else 80 | return 0 81 | end 82 | end 83 | 84 | """ 85 | newblock(s::LinkedInts, N :: Int) 86 | 87 | Add a new block in list `idx` 88 | """ 89 | function newblock(s::LinkedInts, N :: Int) :: Int 90 | @assert(N > 0) 91 | ensurefree(s, N) 92 | # remove from free list 93 | ptre = s.free_ptr 94 | ptrb = ptre 95 | for i = 1:N-1 96 | ptrb = s.prev[ptrb] 97 | end 98 | 99 | prev = s.prev[ptrb] 100 | 101 | if prev > 0 102 | s.next[prev] = 0 103 | end 104 | 105 | s.free_ptr = s.prev[ptrb] 106 | s.free_cap -= N 107 | 108 | # insert into list `idx` 109 | s.prev[ptrb] = s.root 110 | if s.root > 0 111 | s.next[s.root] = ptrb 112 | end 113 | s.root = ptre 114 | push!(s.block, ptrb) 115 | push!(s.size, N) 116 | 117 | id = length(s.block) 118 | 119 | #if ! checkconsistency(s) 120 | # println("List = ",s) 121 | # assert(false) 122 | #end 123 | 124 | return id 125 | end 126 | 127 | """ 128 | Move a block to the free list. 129 | """ 130 | function deleteblock(s::LinkedInts, id :: Int) 131 | if s.size[id] > 0 132 | ptrb = s.block[id] 133 | N = s.size[id] 134 | ptre = ptrb 135 | for i in 2:N 136 | ptre = s.next[ptre] 137 | end 138 | prev = s.prev[ptrb] 139 | next = s.next[ptre] 140 | 141 | # remove from list and clear the block id 142 | if s.root == ptre s.root = prev end 143 | if prev > 0 s.next[prev] = next end 144 | if next > 0 s.prev[next] = prev end 145 | 146 | s.size[id] = 0 147 | s.block[id] = 0 148 | 149 | # add to free list 150 | if s.free_ptr > 0 151 | s.next[s.free_ptr] = ptrb 152 | end 153 | s.prev[ptrb] = s.free_ptr 154 | s.free_ptr = ptre 155 | s.next[ptre] = 0 156 | 157 | s.free_cap += N 158 | end 159 | end 160 | 161 | """ 162 | getindex(s::LinkedInts, id::Int) 163 | 164 | Shortcut for `getindexes(s, id)[1]` when `s.size[id]` is 1. 165 | """ 166 | function getindex(s::LinkedInts, id::Int) 167 | @assert s.size[id] == 1 168 | return s.block[id] 169 | end 170 | 171 | """ 172 | getindexes(s::LinkedInts, id :: Int) 173 | 174 | Return the vector of indices for the block `id`. 175 | """ 176 | function getindexes(s::LinkedInts, id :: Int) 177 | N = s.size[id] 178 | r = Vector{Int}(undef, N) 179 | p = s.block[id] 180 | for i in 1:N 181 | r[i] = p 182 | p = s.next[p] 183 | end 184 | return r 185 | end 186 | 187 | function getindexes(s::LinkedInts, id::Int, target::Vector{Int}, offset::Int) 188 | N = s.size[id] 189 | p = s.block[id] 190 | for i in 1:N 191 | target[i+offset-1] = p 192 | p = s.next[p] 193 | end 194 | return N 195 | end 196 | 197 | function getindexes(s::LinkedInts, ids::Vector{Int}) 198 | N = sum(map(id -> s.size[id], ids)) 199 | r = Vector{Int}(undef,N) 200 | offset = 1 201 | for id in ids 202 | offset += getindexes(s, id, r, offset) 203 | end 204 | return r 205 | end 206 | 207 | function getoneindex(s::LinkedInts, id :: Int) 208 | N = s.size[id] 209 | if N < 1 210 | error("No values at id") 211 | end 212 | 213 | s.block[i] 214 | end 215 | 216 | 217 | """ 218 | Get a list if the currently free elements. 219 | """ 220 | function getfreeindexes(s::LinkedInts) 221 | N = s.free_cap 222 | r = Array{Int}(undef,N) 223 | ptr = s.free_ptr 224 | for i in 1:N 225 | r[N-i+1] = ptr 226 | ptr = s.prev[ptr] 227 | end 228 | r 229 | end 230 | 231 | 232 | 233 | """ 234 | Get a list if the currently used elements. 235 | """ 236 | function getusedindexes(s::LinkedInts) 237 | N = length(s.next) - s.free_cap 238 | r = Array{Int}(undef,N) 239 | ptr = s.root 240 | for i in 1:N 241 | r[N-i+1] = ptr 242 | ptr = s.prev[ptr] 243 | end 244 | r 245 | end 246 | 247 | 248 | 249 | """ 250 | Check consistency of the internal structures. 251 | """ 252 | function checkconsistency(s::LinkedInts) :: Bool 253 | if length(s.prev) != length(s.next) 254 | return false 255 | end 256 | 257 | N = length(s.prev) 258 | 259 | 260 | if ! (all(i -> s.prev[i] == 0 || s.next[s.prev[i]] == i, 1:N) && 261 | all(i -> s.next[i] == 0 || s.prev[s.next[i]] == i, 1:N)) 262 | @assert(false) 263 | end 264 | 265 | mark = fill(false,length(s.prev)) 266 | 267 | p = s.free_ptr 268 | while p != 0 269 | mark[p] = true 270 | p = s.prev[p] 271 | end 272 | 273 | p = s.root 274 | while p != 0 275 | @assert(!mark[p]) 276 | mark[p] = true 277 | p = s.prev[p] 278 | end 279 | 280 | if !all(mark) 281 | println(s) 282 | println(mark) 283 | @assert(all(mark)) 284 | end 285 | 286 | return true 287 | end 288 | -------------------------------------------------------------------------------- /examples/chainsing.jl: -------------------------------------------------------------------------------- 1 | using JuMP 2 | using MathOptInterfaceMosek 3 | import MathOptInterface 4 | const MOI = MathOptInterface 5 | const MOIU = MathOptInterface.Utilities 6 | 7 | """ 8 | Formulate the problem 9 | 10 | min. sum_{j=0,...,(n-2)/2} s[j] + t[j] + p[j] + q[j] 11 | 12 | s.t. (1/2, s[j], x[i]+10x[i+1]) \in Qr, 13 | (1/2, t[j], 5^{1/2}*(x[i+2]-x[i+3])) \in Qr 14 | (1/2, r[j], (x[i+1]-2x[i+2])) \in Qr 15 | (1/2, u[j], 10^{1/4}*(x[i]-10x[i+3])) \in Qr 16 | (1/2, p[j], r[j]) \in Qr 17 | (1/2, q[j], u[j]) \in Qr, j=0,...,(n-2)/2, i = 2j 18 | 19 | 0.1 <= x[i] <= 1.1, i=0,2,...,n-2 20 | 21 | """ 22 | function chainsing1(M :: Model,n :: Int) 23 | m = (n-2) >> 1 24 | 25 | @variable(M,x[1:n]) 26 | @variable(M,p[1:m]) 27 | @variable(M,q[1:m]) 28 | @variable(M,r[1:m]) 29 | @variable(M,s[1:m]) 30 | @variable(M,t[1:m]) 31 | @variable(M,u[1:m]) 32 | 33 | for j in 1:m 34 | i = ((j - 1) << 1) + 1 35 | 36 | # s[j] >= (x[i] + 10*x[i+1])^2 37 | @constraint(M, [ 0.5, s[j], x[i]+10*x[i+1] ] in MOI.RotatedSecondOrderCone(3)) 38 | # t[j] >= 5*(x[i+2] - x[i+3])^2 39 | @constraint(M, [ 0.5, t[j], sqrt(5.0)*(x[i+2] - x[i+3]) ] in MOI.RotatedSecondOrderCone(3)) 40 | # r[j] >= (x[i+1] - 2*x[i+2])^2 41 | @constraint(M, [ 0.5, r[j], x[i+1] - 2.0*x[i+2] ] in MOI.RotatedSecondOrderCone(3)) 42 | # 1/10 * u[j] >= (x[i] - 10*x[i+3])^2 43 | @constraint(M, [ 0.5 * 10^(-1.0), u[j], x[i] - 10.0*x[i+3] ] in MOI.RotatedSecondOrderCone(3)) 44 | # p[j] >= r[j]^2 45 | @constraint(M, [ 0.5, p[j], r[j] ] in MOI.RotatedSecondOrderCone(3)) 46 | # q[j] >= u[j]^2 47 | @constraint(M, [ 0.5, q[j], u[j] ] in MOI.RotatedSecondOrderCone(3)) 48 | end 49 | 50 | #0.1 <= x[i] <= 1.1, i=0,2,...,n-2 51 | for i in 1:2:n-1 52 | @constraint(M,x[i] <= 1.1) 53 | @constraint(M,x[i] >= 0.1) 54 | end 55 | 56 | @objective(M, Min, sum(s) + sum(t) + sum(p) + sum(q)) 57 | end 58 | 59 | 60 | 61 | function chainsing2(M :: Model,n :: Int) 62 | m = (n-2) >> 1 63 | 64 | @variable(M,x[1:n]) 65 | @variable(M,p[1:m]) 66 | @variable(M,q[1:m]) 67 | @variable(M,r[1:m]) 68 | @variable(M,s) 69 | @variable(M,u[1:m]) 70 | 71 | 72 | se = Array{Any}(2 * m + 2) 73 | 74 | 75 | #@constraint(M, [ 0.5, s, 76 | # @expression(M, x[i] + 10*x[i+1] for i in 1:2:n-3 )..., 77 | # @expression(M, 5*(x[i+2] - x[i+3]) for i in 1:2:n-3 )... ] in MOI.RotatedSecondOrderCone(2*m+2)) 78 | @constraint(M, @expression(M, [ 0.5; s; [ x[i] + 10*x[i+1] for i in 1:2:n-3 ] ; [ 5*(x[i+2] - x[i+3]) for i in 1:2:n-3 ] ] ) in MOI.RotatedSecondOrderCone(2*m+2)) 79 | for j in 1:m 80 | i = ((j - 1) << 1) + 1 81 | 82 | # r[j] >= (x[i+1] - 2*x[i+2])^2 83 | @constraint(M, [ 0.5, r[j], x[i+1] - 2*x[i+2] ] in MOI.RotatedSecondOrderCone(3)) 84 | # u[j] >= sqrt(10)*(x[i] - 10*x[i+3])^2 85 | @constraint(M, [ 0.5 * 10.0^(-1.0), u[j], x[i] - 10*x[i+3] ] in MOI.RotatedSecondOrderCone(3)) 86 | # p[j] >= r[j]^2 87 | @constraint(M, [ 0.5, p[j], r[j] ] in MOI.RotatedSecondOrderCone(3)) 88 | # q[j] >= u[j]^2 89 | @constraint(M, [ 0.5, q[j], u[j] ] in MOI.RotatedSecondOrderCone(3)) 90 | end 91 | 92 | #0.1 <= x[i] <= 1.1, i=0,2,...,n-2 93 | for i in 1:2:n-1 94 | @constraint(M,x[i] <= 1.1) 95 | @constraint(M,x[i] >= 0.1) 96 | end 97 | 98 | @objective(M, Min, sum(s) + sum(p) + sum(q)) 99 | end 100 | 101 | function chainsing3(M :: Model,n :: Int) 102 | m = (n-2) >> 1 103 | 104 | @variable(M,x[1:n]) 105 | @variable(M,p[1:m]) 106 | @variable(M,q[1:m]) 107 | @variable(M,r[1:m]) 108 | @variable(M,s) 109 | @variable(M,u[1:m]) 110 | 111 | 112 | se = Array{Any}(2 * m + 2) 113 | 114 | 115 | @constraint(M, @expression(M, [ 0.5; s; 116 | [ x[i] + 10*x[i+1] for i in 1:2:n-3 ] ; 117 | [ 5*(x[i+2] - x[i+3]) for i in 1:2:n-3 ] ; 118 | p ; q ]) in MOI.RotatedSecondOrderCone(4*m+2)) 119 | for j in 1:m 120 | i = ((j - 1) << 1) + 1 121 | 122 | # r[j] >= (x[i+1] - 2*x[i+2])^2 123 | @constraint(M, [ 0.5, r[j], x[i+1] - 2*x[i+2] ] in MOI.RotatedSecondOrderCone(3)) 124 | # u[j] >= sqrt(10)*(x[i] - 10*x[i+3])^2 125 | @constraint(M, [ 0.5 * 10.0^(-1.0), u[j], x[i] - 10*x[i+3] ] in MOI.RotatedSecondOrderCone(3)) 126 | end 127 | 128 | #0.1 <= x[i] <= 1.1, i=0,2,...,n-2 129 | for i in 1:2:n-1 130 | @constraint(M,x[i] <= 1.1) 131 | @constraint(M,x[i] >= 0.1) 132 | end 133 | 134 | @objective(M, Min, s) 135 | end 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | function main(argv :: Vector{String}) 147 | n = 8192 148 | backend = :generic 149 | method = 1 150 | timefile = nothing 151 | taskfile = nothing 152 | opt = false 153 | 154 | for a in argv 155 | m = match(r"--([^=]+)(?:=(.*))?",a) 156 | if m == nothing 157 | n = parse(Int,argv[1]) 158 | else 159 | key = m.captures[1] 160 | val = m.captures[2] 161 | 162 | if key == "optimize" 163 | opt = true 164 | elseif key == "method" 165 | method = parse(Int,val) 166 | elseif key == "timefile" 167 | timefile = val 168 | elseif key == "out" 169 | taskfile = val 170 | elseif key == "backend" 171 | if val == "generic" val == nothing 172 | backend = :generic 173 | elseif val == "mock" 174 | backend = :mock 175 | elseif val == "mosek" 176 | backend = :mosek 177 | end 178 | elseif key == "help" 179 | println("Usage: chainsing n [options]") 180 | println(" n The problem scale, the default being 8192. Must be even.") 181 | println("Options:") 182 | println("\t--method=[1|2|3] Which formulation to use") 183 | println("\t--timefile=filename Which formulation to use") 184 | println("\t--backend=[generic|mosek|mock] Which backend to use.") 185 | println("\t\tgeneric Use generic backend, then copy to MOSEK optimizer") 186 | println("\t\tmosek Use mosek as backend") 187 | println("\t\tmock Use generic backend, then copy to JuMP mock optimizer") 188 | println("\t--out=filename Write task to this file") 189 | println("\t--optimize Call optimizer") 190 | return 191 | end 192 | end 193 | end 194 | 195 | 196 | #println("Chainsing") 197 | #println("\tn : $n") 198 | #println("\tmethod : $method") 199 | #println("\ttimefile : $timefile") 200 | #println("\ttaskfile : $taskfile") 201 | #println("\toptimize : $opt") 202 | #println("\tbackend : $backend") 203 | 204 | T = 0.0 205 | 206 | m = 207 | if backend == :generic 208 | solver = MosekOptimizer() 209 | Model( optimizer = solver) 210 | elseif backend == :mosek 211 | solver = MosekOptimizer() 212 | Model( mode = JuMP.Direct, backend = solver) 213 | elseif backend == :mock 214 | solver = MOIU.MockOptimizer(JuMP.JuMPMOIModel{Float64}()) 215 | Model( optimizer = solver) 216 | end 217 | 218 | 219 | 220 | if backend == :generic 221 | println("Use: Generic backend, then copy to Mosek") 222 | elseif backend == :mosek 223 | println("Use: Mosek directly as backend") 224 | elseif backend == :mock 225 | println("Use: Generic backend, then copy to mock solver") 226 | end 227 | 228 | T0 = time() 229 | if method == 1 230 | chainsing1(m,n) 231 | elseif method == 2 232 | chainsing2(m,n) 233 | elseif method == 3 234 | chainsing3(m,n) 235 | end 236 | 237 | if backend != :mosek 238 | MOIU.attachoptimizer!(m) 239 | end 240 | T1 = time() 241 | T = T1-T0 242 | 243 | @printf("Model build time: %.2f secs\n",T) 244 | if timefile != nothing 245 | open(timefile, "w") do f 246 | @printf(f,"%.2f", T) 247 | end 248 | end 249 | 250 | end 251 | 252 | 253 | main(ARGS) 254 | -------------------------------------------------------------------------------- /test/jump_sdp.jl: -------------------------------------------------------------------------------- 1 | using JuMP 2 | function test_jump_sdp(solver) 3 | @testset "Semidefinite Programming" begin 4 | @testset "SDP1" begin 5 | # Problem SDP1 - sdo1 from MOSEK docs 6 | # From Mosek.jl/test/mathprogtestextra.jl, under license: 7 | # Copyright (c) 2013 Ulf Worsoe, Mosek ApS 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 9 | # software and associated documentation files (the "Software"), to deal in the Software 10 | # without restriction, including without limitation the rights to use, copy, modify, merge, 11 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 12 | # to whom the Software is furnished to do so, subject to the following conditions: 13 | # The above copyright notice and this permission notice shall be included in all copies or 14 | # substantial portions of the Software. 15 | # 16 | # | 2 1 0 | 17 | # min | 1 2 1 | . X + x1 18 | # | 0 1 2 | 19 | # 20 | # 21 | # s.t. | 1 0 0 | 22 | # | 0 1 0 | . X + x1 = 1 23 | # | 0 0 1 | 24 | # 25 | # | 1 1 1 | 26 | # | 1 1 1 | . X + x2 + x3 = 1/2 27 | # | 1 1 1 | 28 | # 29 | # (x1,x2,x3) in C^3_q 30 | # X in C_sdp 31 | 32 | m = Model(solver=solver) 33 | 34 | @variable(m, x[1:3]) 35 | @constraint(m, x in MOI.SecondOrderCone(3)) 36 | @variable(m, X[1:3, 1:3], PSD) 37 | 38 | C = [2 1 0 39 | 1 2 1 40 | 0 1 2] 41 | A1 = [1 0 0 42 | 0 1 0 43 | 0 0 1] 44 | A2 = [1 1 1 45 | 1 1 1 46 | 1 1 1] 47 | 48 | @objective(m, Min, vecdot(C, X) + x[1]) 49 | 50 | @constraint(m, vecdot(A1, X) + x[1] == 1) 51 | @constraint(m, vecdot(A2, X) + x[2] + x[3] == 1/2) 52 | 53 | JuMP.solve(m) 54 | 55 | @test JuMP.isattached(m) 56 | @test JuMP.hasvariableresult(m) 57 | @test JuMP.terminationstatus(m) == MOI.Success 58 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 59 | 60 | @test JuMP.objectivevalue(m) ≈ 0.705710509 atol=1e-6 61 | 62 | xv = JuMP.resultvalue.(x) 63 | Xv = JuMP.resultvalue.(X) 64 | @test vecdot(C, Xv) + xv[1] ≈ 0.705710509 atol=1e-6 65 | 66 | @test eigmin(Xv) > -1e-6 67 | end 68 | 69 | @testset "Nonsensical SDPs" begin 70 | m = Model() 71 | @test_throws ErrorException @variable(m, unequal[1:5,1:6], PSD) 72 | # Some of these errors happen at compile time, so we can't use @test_throws 73 | @test macroexpand(:(@variable(m, notone[1:5,2:6], PSD))).head == :error 74 | @test macroexpand(:(@variable(m, oneD[1:5], PSD))).head == :error 75 | @test macroexpand(:(@variable(m, threeD[1:5,1:5,1:5], PSD))).head == :error 76 | @test macroexpand(:(@variable(m, psd[2] <= rand(2,2), PSD))).head == :error 77 | @test macroexpand(:(@variable(m, -ones(3,4) <= foo[1:4,1:4] <= ones(4,4), PSD))).head == :error 78 | @test macroexpand(:(@variable(m, -ones(3,4) <= foo[1:4,1:4] <= ones(4,4), Symmetric))).head == :error 79 | @test macroexpand(:(@variable(m, -ones(4,4) <= foo[1:4,1:4] <= ones(4,5), Symmetric))).head == :error 80 | @test macroexpand(:(@variable(m, -rand(5,5) <= nonsymmetric[1:5,1:5] <= rand(5,5), Symmetric))).head == :error 81 | end 82 | 83 | # min o max y + X11 84 | # Q11 - 1 = Q22 [y-X12-X21 0 [0 0 85 | # 0 -y] <= 0 0] 86 | # [1 Q11 87 | # Q11 o ] >= 0 -X[2,2] = 1 88 | # Q >= 0 y free 89 | # o free X <= 0 90 | # @testset "Just another SDP" begin 91 | # model = Model(solver=CSDPSolver(printlevel=0)) 92 | # @variable(model, Q[1:2, 1:2], PSD) 93 | # c1 = @constraint(model, Q[1,1] - 1 == Q[2,2]) 94 | # @variable(model, objective) 95 | # T = [1 Q[1,1]; Q[1,1] objective] 96 | # @test_throws ErrorException SDConstraint(T, 1) 97 | # c2 = JuMP.addconstraint(model, SDConstraint(T, 0)) 98 | # @objective(model, Min, objective) 99 | # 100 | # JuMP.solve(m) 101 | # 102 | # @test JuMP.terminationstatus(m) == MOI.Success 103 | # @test JuMP.primalstatus(m) == MOI.FeasiblePoint 104 | # 105 | # @test JuMP.resultvalue(Q) ≈ [1 0; 0 0] atol=1e-3 106 | # @test JuMP.objectivevalue(model) ≈ 1 atol=1e-4 107 | # @test JuMP.resultvalue(objective) ≈ 1 atol=1e-4 108 | # @test getdual(objective) ≈, 0, atol=1e-5 109 | # @test getdual(Q) ≈ [0 0; 0 2] atol=1e-3 110 | # @test getdual(c1) ≈ 2, atol=1e-4 # y 111 | # @test getdual(c2) ≈ [-1 1; 1 -1] atol=1e-3 # X 112 | # end 113 | 114 | # The four following tests are from Example 2.11, Example 2.13 and Example 2.27 of: 115 | # Blekherman, G., Parrilo, P. A., & Thomas, R. R. (Eds.). 116 | # Semidefinite optimization and convex algebraic geometry SIAM 2013 117 | 118 | # Example 2.11 119 | @testset "SDP variable and optimal objective not rational" begin 120 | # solver = fixscs(solver, 7000000) 121 | m = Model(solver=solver) 122 | @variable(m, X[1:2,1:2], PSD) 123 | c = @constraint(m, X[1,1]+X[2,2] == 1) 124 | @objective(m, Min, 2*X[1,1]+2*X[1,2]) 125 | # @test all(isnan.(getdual(X))) 126 | 127 | JuMP.solve(m) 128 | 129 | @test JuMP.terminationstatus(m) == MOI.Success 130 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 131 | 132 | @test JuMP.objectivevalue(m) ≈ 1-sqrt(2) atol=1e-5 133 | @test JuMP.resultvalue.(X) ≈ [(2-sqrt(2))/4 -1/(2*sqrt(2)); -1/(2*sqrt(2)) (2+sqrt(2))/4] atol=1e-4 134 | # @test getdual(X) ≈ [1+sqrt(2) 1; 1 sqrt(2)-1] atol=1e-4 135 | # @test getdual(c) ≈ 1-sqrt(2) atol=1e-5 136 | end 137 | 138 | # # Example 2.13 139 | # @testset "SDP constraint and optimal objective not rational with $solver" for solver in sdp_solvers 140 | # solver = fixscs(solver, 7000000) 141 | # m = Model(solver=solver) 142 | # @variable(m, y) 143 | # c = @SDconstraint(m, [2-y 1; 1 -y] >= 0) 144 | # @objective(m, Max, y) 145 | # @test all(isnan, getdual(c)) 146 | # status = solve(m) 147 | # 148 | # @test status == :Optimal 149 | # @test isapprox(getobjectivevalue(m), 1-sqrt(2), atol=1e-5) 150 | # @test isapprox(getvalue(y), 1-sqrt(2), atol=1e-5) 151 | # 152 | # X = getdual(c) 153 | # @test isapprox(getdual(c), [(2-sqrt(2))/4 -1/(2*sqrt(2)); -1/(2*sqrt(2)) (2+sqrt(2))/4], atol=1e-4) 154 | # @test isapprox(getdual(y), 0, atol=1e-5) 155 | # end 156 | # 157 | # # Example 2.27 158 | # # min X[1,1] max y 159 | # # 2X[1,2] = 1 [0 y [1 0 160 | # # X ⪰ 0 y 0] ⪯ 0 0] 161 | # # The dual optimal solution is y=0 and there is a primal solution 162 | # # [ eps 1/2 163 | # # 1/2 1/eps] 164 | # # for any eps > 0 however there is no primal solution with objective value 0. 165 | # @testset "SDP with dual solution not attained with $solver" for solver in sdp_solvers 166 | # solver = fixscs(solver, 7000000) 167 | # m = Model(solver=solver) 168 | # @variable(m, y) 169 | # c = @SDconstraint(m, [0 y; y 0] <= [1 0; 0 0]) 170 | # @objective(m, Max, y) 171 | # @test all(isnan, getdual(c)) 172 | # status = solve(m) 173 | # 174 | # if contains(string(typeof(solver)),"MosekSolver") 175 | # # Mosek returns Stall on this instance 176 | # # Hack until we fix statuses in MPB 177 | # JuMP.fillConicDuals(m) 178 | # else 179 | # @test status == :Optimal 180 | # end 181 | # @test isapprox(getobjectivevalue(m), 0, atol=1e-5) 182 | # @test isapprox(getvalue(y), 0, atol=1e-5) 183 | # 184 | # X = getdual(c) 185 | # @test isapprox(X[1,1], 0, atol=1e-5) 186 | # @test isapprox(X[1,2], 1/2, atol=1e-5) 187 | # @test isapprox(X[2,1], 1/2, atol=1e-5) 188 | # @test isapprox(getdual(y), 0, atol=1e-5) 189 | # end 190 | 191 | @testset "SDP with primal solution not attained" begin 192 | # solver = fixscs(solver, 7000000) 193 | m = Model(solver=solver) 194 | @variable(m, X[1:2,1:2], PSD) 195 | c = @constraint(m, 2*X[1,2] == 1) 196 | @objective(m, Min, X[1,1]) 197 | # @test all(isnan, getdual(X)) 198 | status = solve(m) 199 | 200 | @test JuMP.terminationstatus(m) in [ MOI.Success, MOI.SlowProgress ] 201 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 202 | 203 | 204 | @test JuMP.objectivevalue(m) ≈ 0 atol=1e-5 205 | Xval = JuMP.resultvalue.(X) 206 | #Mosek.writedata(m.solverinstance.task,"jump_sdo_1_stalls.task") 207 | #showall(m.solverinstance.task) 208 | #showall(m.solverinstance.task[Sol(Mosek.MSK_SOL_ITR)]) 209 | 210 | @test Xval[1,1] ≈ 0 atol=1e-5 211 | @test Xval[1,2] ≈ 1/2 atol=1e-5 212 | @test Xval[2,1] ≈ 1/2 atol=1e-5 213 | 214 | # @test isapprox(getdual(X), [1 0; 0 0], atol=1e-4) 215 | # @test isapprox(getdual(c), 0, atol=1e-5) 216 | end 217 | 218 | @testset "No constraint" begin 219 | m = Model(solver=solver) 220 | @variable(m, X[1:3,1:3], PSD) 221 | @objective(m, Min, trace(X)) 222 | 223 | JuMP.solve(m) 224 | 225 | status = solve(m) 226 | 227 | @test JuMP.terminationstatus(m) == MOI.Success 228 | @test JuMP.primalstatus(m) == MOI.FeasiblePoint 229 | 230 | @test abs(JuMP.objectivevalue(m)) < 1e-5 231 | @test norm(JuMP.resultvalue.(X)) < 1e-5 232 | #@test isapprox(getdual(X), eye(3), atol=1e-5) 233 | end 234 | 235 | end 236 | end 237 | -------------------------------------------------------------------------------- /src/attributes.jl: -------------------------------------------------------------------------------- 1 | 2 | #### solver attributes 3 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsDuals) = true 4 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsAddConstraintAfterSolve) = true 5 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsAddVariableAfterSolve) = true 6 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsDeleteConstraint) = true 7 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsDeleteVariable) = true 8 | #MOI.get(m::Union{MosekSolver,MosekModel},::MOI.SupportsConicThroughQuadratic) = false # though actually the solver does 9 | 10 | #### objective 11 | MOI.get(m::MosekModel,attr::MOI.ObjectiveValue) = getprimalobj(m.task,m.solutions[attr.resultindex].whichsol) 12 | 13 | MOI.get(m::MosekModel,attr::MOI.ObjectiveBound) = getdouinf(m.task,MSK_DINF_MIO_OBJ_BOUND) 14 | 15 | MOI.get(m::MosekModel,attr::MOI.RelativeGap) = getdouinf(m.task,MSK_DINF_MIO_OBJ_REL_GAP) 16 | 17 | MOI.get(m::MosekModel,attr::MOI.SolveTime) = getdouinf(m.task,MSK_DINF_OPTIMIZER_TIME) 18 | 19 | 20 | # NOTE: The MOSEK interface currently only supports Min and Max 21 | function MOI.get(model::MosekModel, ::MOI.ObjectiveSense) 22 | if model.feasibility 23 | return MOI.FEASIBILITY_SENSE 24 | else 25 | sense = getobjsense(model.task) 26 | if sense == MSK_OBJECTIVE_SENSE_MINIMIZE 27 | MOI.MIN_SENSE 28 | else 29 | MOI.MAX_SENSE 30 | end 31 | end 32 | end 33 | 34 | function MOI.set(model::MosekModel, 35 | attr::MOI.ObjectiveSense, 36 | sense::MOI.OptimizationSense) 37 | if sense == MOI.MIN_SENSE 38 | model.feasibility = false 39 | putobjsense(model.task,MSK_OBJECTIVE_SENSE_MINIMIZE) 40 | elseif sense == MOI.MAX_SENSE 41 | model.feasibility = false 42 | putobjsense(model.task,MSK_OBJECTIVE_SENSE_MAXIMIZE) 43 | else 44 | @assert sense == MOI.FEASIBILITY_SENSE 45 | model.feasibility = true 46 | putobjsense(model.task,MSK_OBJECTIVE_SENSE_MINIMIZE) 47 | MOI.set(model, 48 | MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), 49 | MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{Float64}[], 0.0)) 50 | end 51 | end 52 | 53 | #### Solver/Solution information 54 | 55 | function MOI.get(m::MosekModel,attr::MOI.SimplexIterations) 56 | miosimiter = getlintinf(m.task,MSK_LIINF_MIO_SIMPLEX_ITER) 57 | if miosimiter > 0 58 | Int(miosimiter) 59 | else 60 | Int(getintinf(m.task,MSK_IINF_SIM_PRIMAL_ITER) + getintinf(m.task,MSK_IINF_SIM_DUAL_ITER)) 61 | end 62 | end 63 | 64 | function MOI.get(m::MosekModel,attr::MOI.BarrierIterations) 65 | miosimiter = getlintinf(m.task,MSK_LIINF_MIO_INTPNT_ITER) 66 | if miosimiter > 0 67 | Int(miosimiter) 68 | else 69 | Int(getintinf(m.task,MSK_IINF_INTPNT_ITER)) 70 | end 71 | end 72 | 73 | function MOI.get(m::MosekModel,attr::MOI.NodeCount) 74 | Int(getintinf(m.task,MSK_IINF_MIO_NUM_BRANCH)) 75 | end 76 | 77 | MOI.get(m::MosekModel,attr::MOI.RawSolver) = m.task 78 | 79 | MOI.get(m::MosekModel,attr::MOI.ResultCount) = length(m.solutions) 80 | 81 | #### Problem information 82 | 83 | MOI.get(m::MosekModel,attr::MOI.NumberOfVariables) = m.publicnumvar 84 | 85 | function MOI.get(m::MosekModel, ::MOI.NumberOfConstraints{F,D}) where {F,D} 86 | return length(select(m.constrmap, F, D)) 87 | end 88 | 89 | function MOI.get(model::MosekModel, 90 | ::MOI.ListOfConstraints) 91 | list = Tuple{DataType, DataType}[] 92 | for F in [MOI.SingleVariable, MOI.ScalarAffineFunction{Float64}] 93 | for D in [MOI.LessThan{Float64}, MOI.GreaterThan{Float64}, 94 | MOI.EqualTo{Float64}, MOI.Interval{Float64}] 95 | if !isempty(select(model.constrmap, F, D)) 96 | push!(list, (F, D)) 97 | end 98 | end 99 | end 100 | for F in [MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64}] 101 | for D in [MOI.Nonpositives, MOI.Nonnegatives, MOI.Reals, MOI.Zeros, 102 | MOI.SecondOrderCone, MOI.RotatedSecondOrderCone, 103 | MOI.PowerCone{Float64}, MOI.DualPowerCone{Float64}, 104 | MOI.ExponentialCone, MOI.DualExponentialCone, 105 | MOI.PositiveSemidefiniteConeTriangle] 106 | if !isempty(select(model.constrmap, F, D)) 107 | push!(list, (F, D)) 108 | end 109 | end 110 | end 111 | return list 112 | end 113 | 114 | function MOI.get(model::MosekModel, 115 | ::MOI.ListOfConstraintIndices{F, D}) where {F, D} 116 | return MOI.ConstraintIndex{F, D}.(keys(select(model.constrmap, F, D))) 117 | end 118 | 119 | #### Warm start values 120 | 121 | function MOI.set(m::MosekModel, attr::MOI.VariablePrimalStart, 122 | v::MOI.VariableIndex, val::Float64) 123 | subj = getindex(m.x_block, ref2id(v)) 124 | 125 | xx = Float64[val] 126 | for sol in [ MSK_SOL_BAS, MSK_SOL_ITG ] 127 | putxxslice(m.task, sol, Int(subj), Int(subj+1), xx) 128 | end 129 | end 130 | 131 | function MOI.set(m::MosekModel,attr::MOI.VariablePrimalStart, vs::Vector{MOI.VariableIndex}, vals::Vector{Float64}) 132 | subj = Array{Int}(undef,length(vs)) 133 | for i in 1:length(subj) 134 | getindexes(m.x_block, ref2id(vs[i]), subj, i) 135 | end 136 | 137 | for sol in [ MSK_SOL_BAS, MSK_SOL_ITG ] 138 | if solutiondef(m.task,sol) 139 | xx = getxx(m.task,sol) 140 | xx[subj] = vals 141 | putxx(m.task,sol,xx) 142 | else 143 | xx = zeros(Float64,getnumvar(m.task)) 144 | xx[subj] = vals 145 | putxx(m.task,sol,xx) 146 | end 147 | end 148 | end 149 | 150 | 151 | function MOI.set(m::MosekModel, attr::MOI.VariableName, 152 | index :: MOI.VariableIndex, value :: String) 153 | subj = getindex(m.x_block, ref2id(index)) 154 | putvarname(m.task, subj, value) 155 | end 156 | 157 | # function MOI.set(m::MosekModel,attr::MOI.ConstraintDualStart, vs::Vector{MOI.ConstraintIndex}, vals::Vector{Float64}) 158 | # subj = Array{Int}(length(vs)) 159 | # for i in 1:length(subj) 160 | # getindexes(m.x_block,ref2id(vs[i]),subj,i) 161 | # end 162 | 163 | # for sol in [ MSK_SOL_BAS, MSK_SOL_ITG ] 164 | # if solutiondef(m.task,sol) 165 | # xx = getxx(m.task,sol) 166 | # xx[subj] = vals 167 | # putxx(m.task,sol,xx) 168 | # else 169 | # xx = zeros(Float64,getnumvar(m.task)) 170 | # xx[subj] = vals 171 | # putxx(m.task,sol,xx) 172 | # end 173 | # end 174 | # end 175 | 176 | 177 | 178 | #### Variable solution values 179 | 180 | function MOI.get!(output::Vector{Float64}, m::MosekModel, 181 | attr::MOI.VariablePrimal, vs::Vector{MOI.VariableIndex}) 182 | subj = Array{Int}(undef,length(vs)) 183 | for i in 1:length(subj) 184 | getindexes(m.x_block, ref2id(vs[i]), subj, i) 185 | end 186 | 187 | output[1:length(output)] = m.solutions[attr.N].xx[subj] 188 | end 189 | 190 | function MOI.get(m::MosekModel, attr::MOI.VariablePrimal, 191 | vs::Vector{MOI.VariableIndex}) 192 | output = Vector{Float64}(undef,length(vs)) 193 | MOI.get!(output, m, attr, vs) 194 | return output 195 | end 196 | 197 | function MOI.get(m::MosekModel, attr::MOI.VariablePrimal, 198 | vref::MOI.VariableIndex) 199 | subj = getindex(m.x_block,ref2id(vref)) 200 | m.solutions[attr.N].xx[subj] 201 | end 202 | 203 | 204 | 205 | 206 | 207 | 208 | #### Constraint solution values 209 | 210 | function MOI.get( 211 | m ::MosekModel, 212 | attr ::MOI.ConstraintPrimal, 213 | cref ::MOI.ConstraintIndex{MOI.SingleVariable,D}) where D 214 | 215 | conid = ref2id(cref) 216 | idx = getindex(m.xc_block, conid) 217 | subj = m.xc_idxs[idx] 218 | 219 | m.solutions[attr.N].xx[subj] 220 | end 221 | 222 | # Semidefinite domain for a variable 223 | function MOI.get!( 224 | output::Vector{Float64}, 225 | m ::MosekModel, 226 | attr ::MOI.ConstraintPrimal, 227 | cref ::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.PositiveSemidefiniteConeTriangle}) 228 | 229 | whichsol = getsolcode(m,attr.N) 230 | cid = ref2id(cref) 231 | @assert(cid < 0) 232 | 233 | # It is in fact a real constraint and cid is the id of an ordinary constraint 234 | barvaridx = - m.c_block_slack[-cid] 235 | output[1:length(output)] = sympackedLtoU(getbarxj(m.task,whichsol,barvaridx)) 236 | end 237 | 238 | # Any other domain for variable vector 239 | function MOI.get!( 240 | output::Vector{Float64}, 241 | m ::MosekModel, 242 | attr ::MOI.ConstraintPrimal, 243 | cref ::MOI.ConstraintIndex{MOI.VectorOfVariables,D}) where D 244 | 245 | xcid = ref2id(cref) 246 | @assert(xcid > 0) 247 | 248 | mask = m.xc_bounds[xcid] 249 | idxs = getindexes(m.xc_block, xcid) 250 | subj = m.xc_idxs[idxs] 251 | 252 | output[1:length(output)] = m.solutions[attr.N].xx[subj] 253 | end 254 | 255 | function MOI.get(m ::MosekModel, 256 | attr ::MOI.ConstraintPrimal, 257 | cref ::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D}) where D 258 | cid = ref2id(cref) 259 | subi = getindex(m.c_block,cid) 260 | m.solutions[attr.N].xc[subi] + m.c_constant[subi] 261 | end 262 | 263 | 264 | 265 | function MOI.get!( 266 | output::Vector{Float64}, 267 | m ::MosekModel, 268 | attr ::MOI.ConstraintPrimal, 269 | cref ::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D}) where D 270 | 271 | cid = ref2id(cref) 272 | subi = getindexes(m.c_block, cid) 273 | 274 | if m.c_block_slack[cid] == 0 # no slack 275 | output[1:length(output)] = m.solutions[attr.N].xc[subi] + m.c_constant[subi] 276 | elseif m.c_block_slack[cid] > 0 # qcone slack 277 | xid = m.c_block_slack[cid] 278 | xsubj = getindexes(m.x_block, xid) 279 | output[1:length(output)] = m.solutions[attr.N].xx[xsubj] 280 | else # psd slack 281 | xid = - m.c_block_slack[cid] 282 | output[1:length(output)] = sympackedLtoU(getbarxj(m.task,m.solutions[attr.N].whichsol,Int32(xid))) 283 | end 284 | end 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | function MOI.get( 299 | m ::MosekModel, 300 | attr ::MOI.ConstraintDual, 301 | cref ::MOI.ConstraintIndex{MOI.SingleVariable,D}) where { D <: MOI.AbstractSet } 302 | 303 | 304 | xcid = ref2id(cref) 305 | idx = getindex(m.xc_block, xcid) # variable id 306 | 307 | @assert(blocksize(m.xc_block,xcid) > 0) 308 | 309 | subj = getindex(m.x_block, m.xc_idxs[idx]) 310 | if (getobjsense(m.task) == MSK_OBJECTIVE_SENSE_MINIMIZE) 311 | if m.xc_bounds[xcid] & boundflag_lower != 0 && m.xc_bounds[xcid] & boundflag_upper != 0 312 | m.solutions[attr.N].slx[subj] - m.solutions[attr.N].sux[subj] 313 | elseif (m.xc_bounds[xcid] & boundflag_lower) != 0 314 | m.solutions[attr.N].slx[subj] 315 | elseif (m.xc_bounds[xcid] & boundflag_upper) != 0 316 | - m.solutions[attr.N].sux[subj] 317 | elseif (m.xc_bounds[xcid] & boundflag_cone) != 0 318 | m.solutions[attr.N].snx[subj] 319 | else 320 | error("Dual value available for this constraint") 321 | end 322 | else 323 | if m.xc_bounds[xcid] & boundflag_lower != 0 && m.xc_bounds[xcid] & boundflag_upper != 0 324 | m.solutions[attr.N].sux[subj] - m.solutions[attr.N].slx[subj] 325 | elseif (m.xc_bounds[xcid] & boundflag_lower) != 0 326 | - m.solutions[attr.N].slx[subj] 327 | elseif (m.xc_bounds[xcid] & boundflag_upper) != 0 328 | m.solutions[attr.N].sux[subj] 329 | elseif (m.xc_bounds[xcid] & boundflag_cone) != 0 330 | - m.solutions[attr.N].snx[subj] 331 | else 332 | error("Dual value available for this constraint") 333 | end 334 | end 335 | end 336 | 337 | getsolcode(m::MosekModel, N) = m.solutions[N].whichsol 338 | 339 | # Semidefinite domain for a variable 340 | function MOI.get!( 341 | output::Vector{Float64}, 342 | m ::MosekModel, 343 | attr ::MOI.ConstraintDual, 344 | cref ::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.PositiveSemidefiniteConeTriangle}) 345 | 346 | whichsol = getsolcode(m,attr.N) 347 | cid = ref2id(cref) 348 | @assert(cid < 0) 349 | 350 | # It is in fact a real constraint and cid is the id of an ordinary constraint 351 | barvaridx = - m.c_block_slack[-cid] 352 | dual = sympackedLtoU(getbarsj(m.task,whichsol,barvaridx)) 353 | if (getobjsense(m.task) == MSK_OBJECTIVE_SENSE_MINIMIZE) 354 | output[1:length(output)] = dual 355 | else 356 | output[1:length(output)] = -dual 357 | end 358 | end 359 | 360 | # Any other domain for variable vector 361 | function MOI.get!( 362 | output::Vector{Float64}, 363 | m ::MosekModel, 364 | attr ::MOI.ConstraintDual, 365 | cref ::MOI.ConstraintIndex{MOI.VectorOfVariables,D}) where D 366 | 367 | xcid = ref2id(cref) 368 | @assert(xcid > 0) 369 | 370 | mask = m.xc_bounds[xcid] 371 | idxs = getindexes(m.xc_block,xcid) 372 | subj = m.xc_idxs[idxs] 373 | 374 | if (getobjsense(m.task) == MSK_OBJECTIVE_SENSE_MINIMIZE) 375 | if mask & boundflag_lower != 0 && mask & boundflag_upper != 0 376 | output[1:length(output)] = m.solutions[attr.N].slx[subj] - m.solutions[attr.N].sux[subj] 377 | elseif (mask & boundflag_lower) != 0 378 | output[1:length(output)] = m.solutions[attr.N].slx[subj] 379 | elseif (mask & boundflag_upper) != 0 380 | output[1:length(output)] = - m.solutions[attr.N].sux[subj] 381 | elseif (mask & boundflag_cone) != 0 382 | output[1:length(output)] = m.solutions[attr.N].snx[subj] 383 | else 384 | error("Dual value available for this constraint") 385 | end 386 | else 387 | if mask & boundflag_lower != 0 && mask & boundflag_upper != 0 388 | output[1:length(output)] = m.solutions[attr.N].sux[subj] - m.solutions[attr.N].slx[subj] 389 | elseif (mask & boundflag_lower) != 0 390 | output[1:length(output)] = - m.solutions[attr.N].slx[subj] 391 | elseif (mask & boundflag_upper) != 0 392 | output[1:length(output)] = m.solutions[attr.N].sux[subj] 393 | elseif (mask & boundflag_cone) != 0 394 | output[1:length(output)] = - m.solutions[attr.N].snx[subj] 395 | else 396 | error("Dual value available for this constraint") 397 | end 398 | end 399 | end 400 | 401 | function MOI.get(m ::MosekModel, 402 | attr ::MOI.ConstraintDual, 403 | cref ::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D}) where D 404 | 405 | cid = ref2id(cref) 406 | subi = getindex(m.c_block, cid) 407 | 408 | if getobjsense(m.task) == MSK_OBJECTIVE_SENSE_MINIMIZE 409 | m.solutions[attr.N].y[subi] 410 | else 411 | - m.solutions[attr.N].y[subi] 412 | end 413 | end 414 | 415 | 416 | function MOI.get!( 417 | output::Vector{Float64}, 418 | m ::MosekModel, 419 | attr ::MOI.ConstraintDual, 420 | cref ::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D}) where { D <: MOI.AbstractSet } 421 | 422 | cid = ref2id(cref) 423 | subi = getindexes(m.c_block, cid) 424 | 425 | if getobjsense(m.task) == MSK_OBJECTIVE_SENSE_MINIMIZE 426 | if m.c_block_slack[cid] == 0 # no slack 427 | output[1:length(output)] = m.solutions[attr.N].y[subi] 428 | elseif m.c_block_slack[cid] > 0 # qcone slack 429 | xid = m.c_block_slack[cid] 430 | xsubj = getindexes(m.x_block, xid) 431 | output[1:length(output)] = m.solutions[attr.N].snx[xsubj] 432 | else # psd slack 433 | whichsol = getsolcode(m,attr.N) 434 | xid = - m.c_block_slack[cid] 435 | output[1:length(output)] = sympackedLtoU(getbarsj(m.task,whichsol,Int32(xid))) 436 | end 437 | else 438 | if m.c_block_slack[cid] == 0 # no slack 439 | output[1:length(output)] = - m.solutions[attr.N].y[subi] 440 | elseif m.c_block_slack[cid] > 0 # qcone slack 441 | xid = m.c_block_slack[cid] 442 | subj = getindexes(m.x_block, xid) 443 | output[1:length(output)] = - m.solutions[attr.N].snx[subj] 444 | else # psd slack 445 | whichsol = getsolcode(m,attr.N) 446 | xid = - m.c_block_slack[cid] 447 | output[1:length(output)] = sympackedLtoU(- getbarsj(m.task,whichsol,Int32(xid))) 448 | end 449 | end 450 | end 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | solsize(m::MosekModel, cref :: MOI.ConstraintIndex{<:MOI.AbstractScalarFunction}) = 1 466 | function solsize(m::MosekModel, cref :: MOI.ConstraintIndex{MOI.VectorOfVariables}) 467 | cid = ref2id(cref) 468 | if cid < 0 469 | blocksize(m.c_block,-cid) 470 | else 471 | blocksize(m.xc_block,cid) 472 | end 473 | end 474 | 475 | function solsize(m::MosekModel, cref :: MOI.ConstraintIndex{<:MOI.VectorAffineFunction}) 476 | cid = ref2id(cref) 477 | blocksize(m.c_block,cid) 478 | end 479 | 480 | function MOI.get(m::MosekModel, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual}, cref :: MOI.ConstraintIndex{F,D}) where {F <: MOI.AbstractVectorFunction,D} 481 | cid = ref2id(cref) 482 | output = Vector{Float64}(undef,solsize(m,cref)) 483 | MOI.get!(output,m,attr,cref) 484 | output 485 | end 486 | 487 | 488 | 489 | 490 | 491 | 492 | #### Status codes 493 | function MOI.get(m::MosekModel,attr::MOI.TerminationStatus) 494 | if m.trm === nothing 495 | MOI.OPTIMIZE_NOT_CALLED 496 | elseif m.trm == MSK_RES_OK 497 | if any(sol -> sol.solsta == MSK_SOL_STA_PRIM_INFEAS_CER, m.solutions) 498 | MOI.INFEASIBLE 499 | elseif any(sol -> sol.solsta == MSK_SOL_STA_DUAL_INFEAS_CER, 500 | m.solutions) 501 | MOI.DUAL_INFEASIBLE 502 | else 503 | MOI.OPTIMAL 504 | end 505 | elseif m.trm == MSK_RES_TRM_MAX_ITERATIONS 506 | MOI.ITERATION_LIMIT 507 | elseif m.trm == MSK_RES_TRM_MAX_TIME 508 | MOI.TIME_LIMIT 509 | elseif m.trm == MSK_RES_TRM_OBJECTIVE_RANGE 510 | MOI.OBJECTIVE_LIMIT 511 | elseif m.trm == MSK_RES_TRM_MIO_NEAR_REL_GAP 512 | MOI.ALMOST_OPTIMAL 513 | elseif m.trm == MSK_RES_TRM_MIO_NEAR_ABS_GAP 514 | MOI.ALMOST_OPTIMAL 515 | elseif m.trm == MSK_RES_TRM_MIO_NUM_RELAXS 516 | MOI.OTHER_LIMIT 517 | elseif m.trm == MSK_RES_TRM_MIO_NUM_BRANCHES 518 | MOI.NODE_LIMIT 519 | elseif m.trm == MSK_RES_TRM_NUM_MAX_NUM_INT_SOLUTIONS 520 | MOI.SOLUTION_LIMIT 521 | elseif m.trm == MSK_RES_TRM_STALL 522 | println("STALL") 523 | MOI.SLOW_PROGRESS 524 | elseif m.trm == MSK_RES_TRM_USER_CALLBACK 525 | MOI.INTERRUPTED 526 | elseif m.trm == MSK_RES_TRM_MAX_NUM_SETBACKS 527 | MOI.OTHER_LIMIT 528 | elseif m.trm == MSK_RES_TRM_NUMERICAL_PROBLEM 529 | println("NUMERICAL_PROBLEM") 530 | MOI.SLOW_PROGRESS 531 | elseif m.trm == MSK_RES_TRM_INTERNAL 532 | MOI.OTHER_ERROR 533 | elseif m.trm == MSK_RES_TRM_INTERNAL_STOP 534 | MOI.OTHER_ERROR 535 | else 536 | MOI.OTHER_ERROR 537 | end 538 | end 539 | 540 | function MOI.get(m::MosekModel, attr::MOI.PrimalStatus) 541 | solsta = m.solutions[attr.N].solsta 542 | if solsta == MSK_SOL_STA_UNKNOWN 543 | MOI.UNKNOWN_RESULT_STATUS 544 | elseif solsta == MSK_SOL_STA_OPTIMAL 545 | MOI.FEASIBLE_POINT 546 | elseif solsta == MSK_SOL_STA_PRIM_FEAS 547 | MOI.FEASIBLE_POINT 548 | elseif solsta == MSK_SOL_STA_DUAL_FEAS 549 | MOI.UNKNOWN_RESULT_STATUS 550 | elseif solsta == MSK_SOL_STA_PRIM_AND_DUAL_FEAS 551 | MOI.FEASIBLE_POINT 552 | elseif solsta == MSK_SOL_STA_PRIM_INFEAS_CER 553 | MOI.NO_SOLUTION 554 | elseif solsta == MSK_SOL_STA_DUAL_INFEAS_CER 555 | MOI.INFEASIBILITY_CERTIFICATE 556 | elseif solsta == MSK_SOL_STA_PRIM_ILLPOSED_CER 557 | MOI.NO_SOLUTION 558 | elseif solsta == MSK_SOL_STA_DUAL_ILLPOSED_CER 559 | MOI.REDUCTION_CERTIFICATE 560 | elseif solsta == MSK_SOL_STA_INTEGER_OPTIMAL 561 | MOI.FEASIBLE_POINT 562 | else 563 | MOI.UNKNOWN_RESULT_STATUS 564 | end 565 | end 566 | 567 | function MOI.get(m::MosekModel,attr::MOI.DualStatus) 568 | solsta = m.solutions[attr.N].solsta 569 | if solsta == MSK_SOL_STA_UNKNOWN 570 | MOI.UNKNOWN_RESULT_STATUS 571 | elseif solsta == MSK_SOL_STA_OPTIMAL 572 | MOI.FEASIBLE_POINT 573 | elseif solsta == MSK_SOL_STA_PRIM_FEAS 574 | MOI.UNKNOWN_RESULT_STATUS 575 | elseif solsta == MSK_SOL_STA_DUAL_FEAS 576 | MOI.FEASIBLE_POINT 577 | elseif solsta == MSK_SOL_STA_PRIM_AND_DUAL_FEAS 578 | MOI.FEASIBLE_POINT 579 | elseif solsta == MSK_SOL_STA_PRIM_INFEAS_CER 580 | MOI.INFEASIBILITY_CERTIFICATE 581 | elseif solsta == MSK_SOL_STA_DUAL_INFEAS_CER 582 | MOI.NO_SOLUTION 583 | elseif solsta == MSK_SOL_STA_PRIM_ILLPOSED_CER 584 | MOI.REDUCTION_CERTIFICATE 585 | elseif solsta == MSK_SOL_STA_DUAL_ILLPOSED_CER 586 | MOI.NO_SOLUTION 587 | elseif solsta == MSK_SOL_STA_INTEGER_OPTIMAL 588 | MOI.NO_SOLUTION 589 | else 590 | MOI.UNKNOWN_RESULT_STATUS 591 | end 592 | end 593 | -------------------------------------------------------------------------------- /src/MathOptInterfaceMosek.jl: -------------------------------------------------------------------------------- 1 | module MathOptInterfaceMosek 2 | 3 | import MathOptInterface 4 | const MOI = MathOptInterface 5 | const MOIU = MOI.Utilities 6 | using Mosek 7 | #using Mosek.Ext 8 | 9 | using Compat # for findall 10 | 11 | include("LinkedInts.jl") 12 | 13 | const DEBUG = false 14 | 15 | mosek_block_type_unallocated = 0 16 | mosek_block_type_zero = 1 17 | mosek_block_type_nonneg = 2 18 | mosek_block_type_nonpos = 3 19 | mosek_block_type_range = 4 20 | mosek_block_type_qcone = 5 21 | mosek_block_type_rqcone = 6 22 | mosek_block_type_psd = 7 23 | mosek_block_type_integer = 8 24 | 25 | problemtype_linear = 0 26 | problemtype_conic = 1 27 | problemtype_quadratic = 2 28 | 29 | import MathOptInterface 30 | 31 | 32 | 33 | boundflag_lower = 0x1 34 | boundflag_upper = 0x2 35 | boundflag_cone = 0x4 36 | boundflag_int = 0x8 37 | boundflag_all = 0x0f 38 | 39 | 40 | # Mapping of all constraint types to its index 41 | struct ConstraintMap 42 | x_lessthan :: Dict{Int64,Int} # (SingleVariable,LessThan) -> constraint number 43 | x_greaterthan :: Dict{Int64,Int} # (SingleVariable,GreaterThan) -> constraint number 44 | x_equalto :: Dict{Int64,Int} # (SingleVariable,EqualTo) -> constraint number 45 | x_interval :: Dict{Int64,Int} # (SingleVariable,Interval) -> constraint number 46 | x_nonpositives :: Dict{Int64,Int} # (SingleVariable,Nonpositives) -> constraint number 47 | x_nonnegatives :: Dict{Int64,Int} # (SingleVariable,Nonnegatives) -> constraint number 48 | x_binary :: Dict{Int64,Int} # (SingleVariable,ZeroOne) -> constraint number 49 | x_integer :: Dict{Int64,Int} # (SingleVariable,Integer) -> constraint number 50 | 51 | xs_nonpositives :: Dict{Int64,Int} # (VectorOfVariables,Nonpositives) -> constraint number 52 | xs_nonnegatives :: Dict{Int64,Int} # (VectorOfVariables,Nonnegatives) -> constraint number 53 | xs_zeros :: Dict{Int64,Int} # (VectorOfVariables,Zeros) -> constraint number 54 | xs_reals :: Dict{Int64,Int} # (VectorOfVariables,Reals) -> constraint number 55 | xs_qcone :: Dict{Int64,Int} # (VectorOfVariables,SecondOrderCone) -> constraint number 56 | xs_rqcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 57 | xs_psdconetriangle :: Dict{Int64,Int} # (VectorOfVariables,PositiveSemidefiniteConeTriangle) -> constraint number 58 | xs_ppowcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 59 | xs_dpowcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 60 | xs_pexpcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 61 | xs_dexpcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 62 | 63 | axb_lessthan :: Dict{Int64,Int} # (ScalarAffineFunction,LessThan) -> constraint number 64 | axb_greaterthan :: Dict{Int64,Int} # (ScalarAffineFunction,GreaterThan) -> constraint number 65 | axb_equalto :: Dict{Int64,Int} # (ScalarAffineFunction,EqualTo) -> constraint number 66 | axb_interval :: Dict{Int64,Int} # (ScalarAffineFunction,Interval) -> constraint number 67 | axb_nonpositives :: Dict{Int64,Int} # (ScalarAffineFunction,Nonpositives) -> constraint number 68 | axb_nonnegatives :: Dict{Int64,Int} # (ScalarAffineFunction,Nonnegatives) -> constraint number 69 | axb_binary :: Dict{Int64,Int} # (ScalarAffineFunction,ZeroOne) -> constraint number 70 | axb_integer :: Dict{Int64,Int} # (ScalarAffineFunction,Integer) -> constraint number 71 | 72 | axbs_nonpositives :: Dict{Int64,Int} # (VectorAffineFunction,Nonpositives) -> constraint number 73 | axbs_nonnegatives :: Dict{Int64,Int} # (VectorAffineFunction,Nonnegatives) -> constraint number 74 | axbs_zeros :: Dict{Int64,Int} # (VectorAffineFunction,Zeros) -> constraint number 75 | axbs_reals :: Dict{Int64,Int} # (VectorAffineFunction,Reals) -> constraint number 76 | axbs_qcone :: Dict{Int64,Int} # (VectorAffineFunction,SecondOrderCone) -> constraint number 77 | axbs_rqcone :: Dict{Int64,Int} # (VectorAffineFunction,RotatedSecondOrderCone) -> constraint number 78 | axbs_psdconetriangle :: Dict{Int64,Int} # (VectorAffineFunction,PositiveSemidefiniteConeTriangle) -> constraint number 79 | axbs_ppowcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 80 | axbs_dpowcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 81 | axbs_pexpcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 82 | axbs_dexpcone :: Dict{Int64,Int} # (VectorOfVariables,RotatedSecondOrderCone) -> constraint number 83 | end 84 | 85 | ConstraintMap() = ConstraintMap([Dict{Int64,Int}() for i in 1:38]...) 86 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.LessThan{Float64}}) = cm.x_lessthan 87 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.GreaterThan{Float64}}) = cm.x_greaterthan 88 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.EqualTo{Float64}}) = cm.x_equalto 89 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.Interval{Float64}}) = cm.x_interval 90 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.Nonpositives}) = cm.x_nonpositives 91 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.Nonnegatives}) = cm.x_nonnegatives 92 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.ZeroOne}) = cm.x_binary 93 | select(cm::ConstraintMap,::Type{MOI.SingleVariable}, ::Type{MOI.Integer}) = cm.x_integer 94 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonpositives}) = cm.xs_nonpositives 95 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}) = cm.xs_nonnegatives 96 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.Zeros}) = cm.xs_zeros 97 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.Reals}) = cm.xs_reals 98 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.SecondOrderCone}) = cm.xs_qcone 99 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.RotatedSecondOrderCone}) = cm.xs_rqcone 100 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.PositiveSemidefiniteConeTriangle}) = cm.xs_psdconetriangle 101 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.PowerCone{Float64}}) = cm.xs_ppowcone 102 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.DualPowerCone{Float64}}) = cm.xs_dpowcone 103 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.ExponentialCone}) = cm.xs_pexpcone 104 | select(cm::ConstraintMap,::Type{MOI.VectorOfVariables}, ::Type{MOI.DualExponentialCone}) = cm.xs_dexpcone 105 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.LessThan{Float64}}) = cm.axb_lessthan 106 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.GreaterThan{Float64}}) = cm.axb_greaterthan 107 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.EqualTo{Float64}}) = cm.axb_equalto 108 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.Interval{Float64}}) = cm.axb_interval 109 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.ZeroOne}) = cm.axb_binary 110 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.Integer}) = cm.axb_integer 111 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.Nonpositives}) = cm.axb_nonpositives 112 | select(cm::ConstraintMap,::Type{MOI.ScalarAffineFunction{Float64}},::Type{MOI.Nonnegatives}) = cm.axb_nonnegatives 113 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.Nonpositives}) = cm.axbs_nonpositives 114 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.Nonnegatives}) = cm.axbs_nonnegatives 115 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.Zeros}) = cm.axbs_zeros 116 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.Reals}) = cm.axbs_reals 117 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.SecondOrderCone}) = cm.axbs_qcone 118 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.RotatedSecondOrderCone}) = cm.axbs_rqcone 119 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.PositiveSemidefiniteConeTriangle}) = cm.axbs_psdconetriangle 120 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.PowerCone{Float64}}) = cm.axbs_ppowcone 121 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.DualPowerCone{Float64}}) = cm.axbs_dpowcone 122 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.ExponentialCone}) = cm.axbs_pexpcone 123 | select(cm::ConstraintMap,::Type{MOI.VectorAffineFunction{Float64}},::Type{MOI.DualExponentialCone}) = cm.axbs_dexpcone 124 | 125 | Base.getindex(cm::ConstraintMap,r :: MOI.ConstraintIndex{F,D}) where {F,D} = select(cm,F,D)[r.value] 126 | 127 | 128 | struct MosekSolution 129 | whichsol :: Soltype 130 | solsta :: Solsta 131 | prosta :: Prosta 132 | 133 | xxstatus :: Vector{Stakey} 134 | xx :: Vector{Float64} 135 | slx :: Vector{Float64} 136 | sux :: Vector{Float64} 137 | snx :: Vector{Float64} 138 | 139 | cstatus :: Vector{Stakey} 140 | xc :: Vector{Float64} 141 | slc :: Vector{Float64} 142 | suc :: Vector{Float64} 143 | y :: Vector{Float64} 144 | end 145 | 146 | """ 147 | MosekModel <: MathOptInterface.AbstractModel 148 | 149 | Linear variables and constraint can be deleted. For some reason MOSEK 150 | does not support deleting PSD variables. 151 | 152 | Note also that adding variables and constraints will permanently add 153 | some (currently between 1 and 3) Int64s that a `delete!` will not 154 | remove. This ensures that Indices (Variable and constraint) that 155 | are deleted are thereafter invalid. 156 | """ 157 | mutable struct MosekModel <: MOI.AbstractOptimizer 158 | task :: Mosek.MSKtask 159 | ## Options passed in `MosekOptimizer` that are used to create a new task 160 | ## in `MOI.empty!`: 161 | # Should Mosek output be ignored or printed ? 162 | be_quiet :: Bool 163 | # Integer parameters, i.e. parameters starting with `MSK_IPAR_` 164 | ipars :: Dict{String, Integer} 165 | # Floating point parameters, i.e. parameters starting with `MSK_DPAR_` 166 | dpars :: Dict{String, AbstractFloat} 167 | # String parameters, i.e. parameters starting with `MSK_SPAR_` 168 | spars :: Dict{String, AbstractString} 169 | 170 | """ 171 | Number of variables explicitly created by user 172 | """ 173 | publicnumvar :: Int 174 | 175 | """ 176 | """ 177 | constrmap :: ConstraintMap 178 | 179 | """ 180 | """ 181 | constrnames :: Dict{String,Vector{MOI.ConstraintIndex}} 182 | 183 | """ 184 | The total length of `x_block` matches the number of variables in 185 | the underlying task, and the number of blocks corresponds to the 186 | number variables allocated in the Model. 187 | """ 188 | x_block :: LinkedInts 189 | 190 | """ 191 | One entry per scalar variable in the task indicating which bound 192 | types are defined 193 | """ 194 | x_boundflags :: Vector{Int} 195 | 196 | """ 197 | One entry per scalar variable in the task defining the number of 198 | variable constraints imposed on that variable. It is not allowed 199 | to delete a variable without deleting the constraints on it first 200 | (to get around the problem of deleting roots in conic constraints). 201 | """ 202 | x_numxc :: Vector{Int} 203 | 204 | """ 205 | One entry per variable-constraint 206 | """ 207 | xc_block :: LinkedInts 208 | 209 | """ 210 | One entry per variable-constraint block indicating which bound 211 | types it defines. The values are binary ORed `boundflag_...` values. 212 | """ 213 | xc_bounds :: Vector{UInt8} 214 | """ 215 | One entry per variable-constraint block indicating which cone it belongs to, 0 if none. 216 | """ 217 | xc_coneid :: Vector{Int} 218 | 219 | """ 220 | One entry per scalar variable-constraint, defining which native 221 | variables the bound block covers. 222 | """ 223 | xc_idxs :: Vector{Int} 224 | 225 | ########################### 226 | """ 227 | One scalar entry per constraint in the underlying task. One block 228 | per constraint allocated in the Model. 229 | """ 230 | c_block :: LinkedInts 231 | 232 | """ 233 | One entry per allocated scalar constraint. Defines the fixed term 234 | on the left-hand for each scalar constraint. 235 | """ 236 | c_constant :: Vector{Float64} 237 | 238 | """ 239 | One entry per allocated scalar constraint. 240 | Each element is either 241 | - 0: meaning: no slack, when domain is defined directly as a bound, 242 | - positive: a `x_block` reference, e.g. for qcones, or 243 | - negative: Negated index of a PSD variable in the underlying task 244 | """ 245 | c_block_slack :: Vector{Int} 246 | """ 247 | One entry per variable-constraint block indicating which cone it belongs to, 0 if none. 248 | """ 249 | c_coneid :: Vector{Int} 250 | 251 | ########################### 252 | conecounter :: Int 253 | 254 | ########################### 255 | trm :: Union{Nothing, Rescode} 256 | solutions :: Vector{MosekSolution} 257 | 258 | ########################### 259 | """ 260 | Indicating whether the objective sense is MOI.FEASIBILITY_SENSE. It is 261 | encoded as a MOI.MIN_SENSE with a zero objective internally but this allows 262 | MOI.get(::MosekModel, ::ObjectiveSense) to still return the right value 263 | """ 264 | feasibility :: Bool 265 | 266 | fallback :: Union{String,Nothing} 267 | service :: Union{String,Nothing} 268 | end 269 | 270 | function parametrized_task(be_quiet::Bool, 271 | ipars::Dict{String, Integer}, 272 | dpars::Dict{String, AbstractFloat}, 273 | spars::Dict{String, AbstractString}) 274 | task = maketask() 275 | try 276 | for (name, value) in ipars 277 | Mosek.putnaintparam(task, name, value) 278 | end 279 | for (name, value) in dpars 280 | Mosek.putnadouparam(task, name, value) 281 | end 282 | for (name, value) in spars 283 | Mosek.putnastrparam(task, name, value) 284 | end 285 | if !be_quiet 286 | Mosek.putstreamfunc(task, Mosek.MSK_STREAM_LOG, m -> print(m)) 287 | end 288 | catch 289 | Mosek.deletetask(task) 290 | rethrow() 291 | end 292 | return task 293 | end 294 | 295 | function parse_parameters(kws) 296 | be_quiet = false 297 | fallback = nothing 298 | service = nothing 299 | ipars = Dict{String, Integer}() 300 | dpars = Dict{String, AbstractFloat}() 301 | spars = Dict{String, AbstractString}() 302 | for (option, val) in kws 303 | parname = string(option) 304 | if parname == "QUIET" 305 | be_quiet = be_quiet || convert(Bool,val) 306 | elseif parname == "fallback" 307 | fallback = val 308 | elseif parname == "service" 309 | service = val 310 | elseif parname == "verbose" 311 | # pass 312 | elseif startswith(parname, "MSK_IPAR_") 313 | ipars[parname] = val 314 | elseif startswith(parname, "MSK_DPAR_") 315 | dpars[parname] = val 316 | elseif startswith(parname, "MSK_SPAR_") 317 | spars[parname] = val 318 | elseif isa(val, Integer) 319 | ipars["MSK_IPAR_$(uppercase(parname))"] = val 320 | elseif isa(val, AbstractFloat) 321 | dpars["MSK_DPAR_$(uppercase(parname))"] = val 322 | elseif isa(val, AbstractString) 323 | spars["MSK_SPAR_$(uppercase(parname))"] = val 324 | else 325 | error("Value $val for parameter $option has unrecognized type") 326 | end 327 | end 328 | return be_quiet,fallback,service,ipars, dpars, spars 329 | end 330 | 331 | export Mosek 332 | function Mosek.Optimizer(; kws...) 333 | be_quiet, fallback, service,ipars, dpars, spars = parse_parameters(kws) 334 | return MosekModel(parametrized_task(be_quiet, ipars, dpars, spars), # task 335 | be_quiet, 336 | ipars, 337 | dpars, 338 | spars, 339 | 0, # public numvar 340 | ConstraintMap(), # public constraints 341 | Dict{String,MOI.ConstraintIndex}(), 342 | LinkedInts(),# c_block 343 | Int[], # x_boundflags 344 | Int[], # x_numxc 345 | LinkedInts(), # xc_block 346 | UInt8[], # xc_bounds 347 | Int[], # xc_coneid 348 | Int[], # xc_idxs 349 | LinkedInts(), # c_block 350 | Float64[], # c_constant 351 | Int[], # c_block_slack 352 | Int[], # c_coneid 353 | 0, # cone counter 354 | nothing,# trm 355 | MosekSolution[], 356 | true,# feasibility_sense 357 | fallback, 358 | service) 359 | end 360 | 361 | 362 | 363 | function MOI.optimize!(m::MosekModel) 364 | m.trm = 365 | if m.service === nothing 366 | if m.fallback === nothing 367 | optimize(m.task) 368 | else 369 | optimize(m.task,m.fallback) 370 | end 371 | else 372 | r = match(r"mosek://([a-zA-Z-]+(\.[a-zA-Z-]+)*)(:([0-9]+))?$",m.service) 373 | if r == nothing 374 | error("Invalid server specification $service") 375 | else 376 | server = r.captures[1] 377 | port = if r.captures[4] == nothing "30080" else r.captures[4] end 378 | optimizermt(m.task,server,port) 379 | end 380 | end 381 | 382 | m.solutions = MosekSolution[] 383 | if solutiondef(m.task,MSK_SOL_ITG) 384 | push!(m.solutions, 385 | MosekSolution(MSK_SOL_ITG, 386 | getsolsta(m.task,MSK_SOL_ITG), 387 | getprosta(m.task,MSK_SOL_ITG), 388 | getskx(m.task,MSK_SOL_ITG), 389 | getxx(m.task,MSK_SOL_ITG), 390 | Float64[], 391 | Float64[], 392 | Float64[], 393 | getskc(m.task,MSK_SOL_ITG), 394 | getxc(m.task,MSK_SOL_ITG), 395 | Float64[], 396 | Float64[], 397 | Float64[])) 398 | end 399 | if solutiondef(m.task,MSK_SOL_BAS) 400 | push!(m.solutions, 401 | MosekSolution(MSK_SOL_BAS, 402 | getsolsta(m.task,MSK_SOL_BAS), 403 | getprosta(m.task,MSK_SOL_BAS), 404 | getskx(m.task,MSK_SOL_BAS), 405 | getxx(m.task,MSK_SOL_BAS), 406 | getslx(m.task,MSK_SOL_BAS), 407 | getsux(m.task,MSK_SOL_BAS), 408 | Float64[], 409 | getskc(m.task,MSK_SOL_BAS), 410 | getxc(m.task,MSK_SOL_BAS), 411 | getslc(m.task,MSK_SOL_BAS), 412 | getsuc(m.task,MSK_SOL_BAS), 413 | gety(m.task,MSK_SOL_BAS))) 414 | end 415 | if solutiondef(m.task,MSK_SOL_ITR) 416 | push!(m.solutions, 417 | MosekSolution(MSK_SOL_ITR, 418 | getsolsta(m.task,MSK_SOL_ITR), 419 | getprosta(m.task,MSK_SOL_ITR), 420 | getskx(m.task,MSK_SOL_ITR), 421 | getxx(m.task,MSK_SOL_ITR), 422 | getslx(m.task,MSK_SOL_ITR), 423 | getsux(m.task,MSK_SOL_ITR), 424 | getsnx(m.task,MSK_SOL_ITR), 425 | getskc(m.task,MSK_SOL_ITR), 426 | getxc(m.task,MSK_SOL_ITR), 427 | getslc(m.task,MSK_SOL_ITR), 428 | getsuc(m.task,MSK_SOL_ITR), 429 | gety(m.task,MSK_SOL_ITR))) 430 | end 431 | end 432 | 433 | function MOI.is_empty(m::MosekModel) 434 | getnumvar(m.task) == 0 && getnumcon(m.task) == 0 && getnumcone(m.task) == 0 && getnumbarvar(m.task) == 0 435 | end 436 | 437 | function MOI.empty!(model::MosekModel) 438 | model.task = parametrized_task(model.be_quiet, model.ipars, 439 | model.dpars, model.spars) 440 | model.publicnumvar = 0 441 | model.constrmap = ConstraintMap() 442 | model.x_block = LinkedInts() 443 | model.x_boundflags = Int[] 444 | model.x_numxc = Int[] 445 | model.xc_block = LinkedInts() 446 | model.xc_bounds = UInt8[] 447 | model.xc_coneid = Int[] 448 | model.xc_idxs = Int[] 449 | model.c_block = LinkedInts() 450 | model.c_constant = Float64[] 451 | model.c_block_slack = Int[] 452 | model.c_coneid = Int[] 453 | model.conecounter = 0 454 | model.trm = nothing 455 | model.solutions = MosekSolution[] 456 | model.feasibility = true 457 | end 458 | 459 | MOI.get(::MosekModel, ::MOI.SolverName) = "Mosek" 460 | 461 | MOIU.supports_default_copy_to(::MosekModel, copy_names::Bool) = !copy_names 462 | function MOI.copy_to(dest::MosekModel, src::MOI.ModelLike; kws...) 463 | return MOIU.automatic_copy_to(dest, src; kws...) 464 | end 465 | 466 | function MOI.write_to_file(m::MosekModel, filename :: String) 467 | putintparam(m.task,MSK_IPAR_OPF_WRITE_SOLUTIONS, MSK_ON) 468 | writedata(m.task,filename) 469 | end 470 | 471 | # For linear objectives we accept: 472 | # EITER affine left-hand side and ranged, unbounded, half-open, fixed (equality), PSD or SOC domains 473 | # OR affine and quadratic left-hand side, and ranged, unbounded, half-open, fixed (equality) domains (quadratic constraints must be unbounded or half-open) 474 | # 475 | # For non-quadratic problems we allow binary and integer variables (but not constraints) 476 | #function supportsconstraints(m::MosekSolver, constraint_types) :: Bool 477 | # for (fun,dom) in constraint_types 478 | # if fun in [MOI.ScalarAffineFunction{Float64}, 479 | # MOI.SingleVariable, 480 | # MOI.VectorAffineFunction{Float64}, 481 | # MOI.VectorOfVariables] && 482 | # dom in [MOI.Zeros, 483 | # MOI.Reals, 484 | # MOI.Nonnegatives, 485 | # MOI.Nonpositives, 486 | # MOI.GreaterThan{Float64}, 487 | # MOI.LessThan{Float64}, 488 | # MOI.EqualTo{Float64}, 489 | # MOI.Interval{Float64}, 490 | # MOI.SecondOrderCone, 491 | # MOI.RotatedSecondOrderCone, 492 | # MOI.PositiveSemidefiniteConeTriangle, 493 | # MOI.PositiveSemidefiniteConeScaled ] 494 | # # ok 495 | # elseif dom in [MOI.ZeroOne, 496 | # MOI.Integer] && 497 | # fun in [MOI.SingleVariable, 498 | # MOI.VectorOfVariables] 499 | # # ok 500 | # else 501 | # return false 502 | # end 503 | # end 504 | # true 505 | #end 506 | 507 | 508 | #MOI.supportsproblem(m::MosekSolver, ::Type{MOI.SingleVariable}, constraint_types) :: Bool = supportsconstraints(m,constraint_types) 509 | #MOI.supportsproblem(m::MosekSolver, ::Type{MOI.ScalarAffineFunction{Float64}}, constraint_types) :: Bool = supportsconstraints(m,constraint_types) 510 | #MOI.supportsproblem{F}(m::MosekSolver, ::Type{F}, constraint_types) :: Bool = false 511 | 512 | ref2id(ref :: MOI.VariableIndex) :: Int = Int(ref.value) 513 | 514 | ref2id(ref :: MOI.ConstraintIndex) :: Int = 515 | if ref.value & 1 == 0 516 | Int(ref.value >> 1) 517 | else 518 | - Int(ref.value >> 1) 519 | end 520 | 521 | function id2vref(id :: Int) :: MOI.VariableIndex 522 | @assert(id > 0) 523 | MOI.VariableIndex(id) 524 | end 525 | 526 | include("objective.jl") 527 | include("variable.jl") 528 | include("constraint.jl") 529 | include("attributes.jl") 530 | 531 | 532 | export MosekModel 533 | 534 | 535 | end # module 536 | -------------------------------------------------------------------------------- /src/constraint.jl: -------------------------------------------------------------------------------- 1 | const VectorCone = Union{MOI.SecondOrderCone, 2 | MOI.RotatedSecondOrderCone, 3 | MOI.PowerCone, 4 | MOI.DualPowerCone, 5 | MOI.ExponentialCone, 6 | MOI.DualExponentialCone} 7 | const PositiveSemidefiniteCone = MOI.PositiveSemidefiniteConeTriangle 8 | const LinearFunction = Union{MOI.SingleVariable, 9 | MOI.VectorOfVariables, 10 | MOI.ScalarAffineFunction, 11 | MOI.VectorAffineFunction} 12 | const AffineFunction = Union{MOI.ScalarAffineFunction, 13 | MOI.VectorAffineFunction} 14 | const VariableFunction = Union{MOI.ScalarAffineFunction, 15 | MOI.VectorAffineFunction} 16 | 17 | const ScalarLinearDomain = Union{MOI.LessThan{Float64}, 18 | MOI.GreaterThan{Float64}, 19 | MOI.EqualTo{Float64}, 20 | MOI.Interval{Float64}} 21 | const VectorLinearDomain = Union{MOI.Nonpositives, 22 | MOI.Nonnegatives, 23 | MOI.Reals, 24 | MOI.Zeros} 25 | const LinearDomain = Union{ScalarLinearDomain,VectorLinearDomain} 26 | const ScalarIntegerDomain = Union{MOI.ZeroOne, MOI.Integer} 27 | ################################################################################ 28 | # ADD CONSTRAINT ############################################################### 29 | ################################################################################ 30 | 31 | MOI.supports_constraint(m::MosekModel, ::Type{<:Union{MOI.SingleVariable, MOI.ScalarAffineFunction}}, ::Type{<:ScalarLinearDomain}) = true 32 | MOI.supports_constraint(m::MosekModel, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction}}, ::Type{<:Union{VectorCone, PositiveSemidefiniteCone, VectorLinearDomain}}) = true 33 | MOI.supports_constraint(m::MosekModel, ::Type{MOI.SingleVariable}, ::Type{<:ScalarIntegerDomain}) = true 34 | #MOI.canaddconstraint(m::MosekModel, ::Type{<:Union{MOI.SingleVariable, MOI.ScalarAffineFunction}}, ::Type{<:ScalarLinearDomain}) = true 35 | #MOI.canaddconstraint(m::MosekModel, ::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction}}, ::Type{<:Union{VectorCone, PositiveSemidefiniteCone, VectorLinearDomain}}) = true 36 | #MOI.canaddconstraint(m::MosekModel, ::Type{MOI.SingleVariable}, ::Type{<:ScalarIntegerDomain}) = true 37 | 38 | function MOI.add_constraint(m :: MosekModel, 39 | axb :: MOI.ScalarAffineFunction{Float64}, 40 | dom :: D) where {D <: MOI.AbstractScalarSet} 41 | 42 | # Duplicate indices not supported 43 | axb = MOIU.canonical(axb) 44 | 45 | N = 1 46 | conid = allocateconstraints(m,N) 47 | addlhsblock!(m, 48 | conid, 49 | fill(1, length(axb.terms)), 50 | axb.terms) 51 | 52 | if length(m.c_constant) < length(m.c_block) 53 | append!(m.c_constant, 54 | zeros(Float64, length(m.c_block) - length(m.c_constant))) 55 | end 56 | conidxs = getindexes(m.c_block, conid) 57 | m.c_constant[conidxs] .= axb.constant 58 | 59 | addbound!(m, conid, conidxs, Float64[axb.constant], dom) 60 | conref = MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64}, D}(UInt64(conid) << 1) 61 | select(m.constrmap, MOI.ScalarAffineFunction{Float64}, D)[conref.value] = conid 62 | 63 | return conref 64 | end 65 | 66 | function MOI.add_constraint(m :: MosekModel, 67 | axb :: MOI.VectorAffineFunction{Float64}, 68 | dom :: D) where { D <: MOI.AbstractVectorSet } 69 | 70 | # Duplicate indices not supported 71 | axb = MOIU.canonical(axb) 72 | 73 | N = MOI.dimension(dom) 74 | conid = allocateconstraints(m,N) 75 | addlhsblock!(m, 76 | conid, 77 | map(t -> t.output_index, axb.terms), 78 | map(t -> t.scalar_term, axb.terms)) 79 | 80 | conidxs = getindexes(m.c_block, conid) 81 | m.c_constant[conidxs] .= axb.constants 82 | 83 | addbound!(m, conid, conidxs, axb.constants, dom) 84 | conref = MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64}, D}(UInt64(conid) << 1) 85 | select(m.constrmap, MOI.VectorAffineFunction{Float64}, D)[conref.value] = conid 86 | 87 | return conref 88 | end 89 | 90 | function trimapL(i, j, n) 91 | if i < j 92 | trimapL(j, i, n) 93 | else 94 | i + div((2n-j) * (j-1), 2) 95 | end 96 | end 97 | 98 | function trimapU(i::Integer, j::Integer) 99 | if i < j 100 | trimapU(j, i) 101 | else 102 | div((i-1)*i, 2) + j 103 | end 104 | end 105 | 106 | # Vectorized length for matrix dimension n 107 | sympackedlen(n) = (n*(n+1)) >> 1 108 | # Matrix dimension for vectorized length n 109 | sympackeddim(n) = div(isqrt(1+8n) - 1, 2) 110 | 111 | function _sympackedto(x, n, mapfrom, mapto) 112 | @assert length(x) == sympackedlen(n) 113 | y = similar(x) 114 | for i in 1:n, j in 1:i 115 | y[mapto(i, j)] = x[mapfrom(i, j)] 116 | end 117 | y 118 | end 119 | sympackedLtoU(x, n=sympackeddim(length(x))) = _sympackedto(x, n, (i, j) -> trimapL(i, j, n), trimapU) 120 | sympackedUtoL(x, n=sympackeddim(length(x))) = _sympackedto(x, n, trimapU, (i, j) -> trimapL(i, j, n)) 121 | 122 | function sympackedUtoLidx(x::AbstractVector{<:Integer}, n) 123 | y = similar(x) 124 | map = sympackedLtoU(1:sympackedlen(n), n) 125 | for i in eachindex(y) 126 | y[i] = map[x[i]] 127 | end 128 | y 129 | end 130 | 131 | function MOI.add_constraint(m :: MosekModel, 132 | axb :: MOI.VectorAffineFunction{Float64}, 133 | dom :: PSDCone) where { PSDCone <: PositiveSemidefiniteCone } 134 | 135 | # Duplicate indices not supported 136 | axb = MOIU.canonical(axb) 137 | 138 | N = dom.side_dimension 139 | NN = sympackedlen(N) 140 | 141 | conid = allocateconstraints(m,NN) 142 | addlhsblock!(m, 143 | conid, 144 | sympackedUtoLidx(map(t -> t.output_index, axb.terms), N), 145 | map(t -> t.scalar_term, axb.terms)) 146 | conidxs = getindexes(m.c_block,conid) 147 | constant = sympackedUtoL(axb.constants, N) 148 | m.c_constant[conidxs] = constant 149 | 150 | addbound!(m,conid,conidxs,constant,dom) 151 | conref = MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},PSDCone}(UInt64(conid) << 1) 152 | select(m.constrmap,MOI.VectorAffineFunction{Float64},PSDCone)[conref.value] = conid 153 | conref 154 | end 155 | 156 | ################################################################################ 157 | # Variable constraints 158 | 159 | # We allow following. Each variable can have 160 | # - at most most upper and one lower bound 161 | # - belong to at most one non-semidefinite cone 162 | # - any number of semidefinite cones, which are implemented as ordinary constraints 163 | # This is when things get a bit funky; By default a variable has no 164 | # bounds, i.e. "free". Adding a `GreaterThan` or `Nonnegatives` 165 | # constraint causes it to have a defined lower bound but no upper 166 | # bound, allowing a `LessThan` or ``Nonpositives` constraint to be 167 | # added later. Adding a `Interval` constraint defines both upper and 168 | # lower bounds. Adding a `Reals` constraint will effectively be the 169 | # same as an interval ]-infty;infty[, in that it will define both 170 | # upper and lower bounds, and not allow those to be set afterwards. 171 | 172 | domain_type_mask(dom :: MOI.Reals) = (boundflag_lower | boundflag_upper) 173 | domain_type_mask(dom :: MOI.Interval) = (boundflag_lower | boundflag_upper) 174 | domain_type_mask(dom :: MOI.EqualTo) = (boundflag_lower | boundflag_upper) 175 | domain_type_mask(dom :: MOI.Zeros) = (boundflag_lower | boundflag_upper) 176 | domain_type_mask(dom :: MOI.GreaterThan) = boundflag_lower 177 | domain_type_mask(dom :: MOI.Nonnegatives) = boundflag_lower 178 | domain_type_mask(dom :: MOI.LessThan) = boundflag_upper 179 | domain_type_mask(dom :: MOI.Nonpositives) = boundflag_upper 180 | 181 | domain_type_mask(dom :: MOI.SecondOrderCone) = boundflag_cone 182 | domain_type_mask(dom :: MOI.RotatedSecondOrderCone) = boundflag_cone 183 | domain_type_mask(dom :: MOI.ExponentialCone) = boundflag_cone 184 | domain_type_mask(dom :: MOI.PowerCone) = boundflag_cone 185 | domain_type_mask(dom :: MOI.DualExponentialCone) = boundflag_cone 186 | domain_type_mask(dom :: MOI.DualPowerCone) = boundflag_cone 187 | 188 | domain_type_mask(dom :: MOI.PositiveSemidefiniteConeTriangle) = 0 189 | 190 | domain_type_mask(dom :: MOI.Integer) = boundflag_int 191 | domain_type_mask(dom :: MOI.ZeroOne) = (boundflag_int | boundflag_upper | boundflag_lower) 192 | 193 | is_positivesemidefinite_set(dom :: PositiveSemidefiniteCone) = true 194 | is_positivesemidefinite_set(dom) = false 195 | is_conic_set(dom :: VectorCone) = true 196 | is_conic_set(dom) = false 197 | 198 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.Interval) 199 | putvarbound(m.task, subj, MSK_BK_RA, dom.lower, dom.upper) 200 | end 201 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.EqualTo) 202 | putvarbound(m.task, subj, MSK_BK_FX, dom.value, dom.value) 203 | end 204 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.Integer) 205 | putvartype(m.task, subj, MSK_VAR_TYPE_INT) 206 | end 207 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.ZeroOne) 208 | putvartype(m.task, subj, MSK_VAR_TYPE_INT) 209 | putvarbound(m.task, subj, MSK_BK_RA, 0.0, 1.0) 210 | end 211 | 212 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.LessThan) 213 | bk, lo, up = getvarbound(m.task, subj) 214 | bk = if (bk == MSK_BK_FR) MSK_BK_UP else MSK_BK_RA end 215 | putvarbound(m.task, Int32(subj), bk, lo, dom.upper) 216 | end 217 | 218 | function addvarconstr(m::MosekModel, subj::Int, dom::MOI.GreaterThan) 219 | bk, lo, up = getvarbound(m.task, subj) 220 | bk = if (bk == MSK_BK_FR) MSK_BK_LO else MSK_BK_RA end 221 | putvarbound(m.task, Int32(subj), bk, dom.lower, up) 222 | end 223 | 224 | addvarconstr(m :: MosekModel, subj::Vector{Int}, dom :: MOI.Reals) = nothing 225 | function addvarconstr(m :: MosekModel, subj::Vector{Int}, dom :: MOI.Zeros) 226 | bnd = zeros(Float64, length(subj)) 227 | putvarboundlist(m.task, subj, fill(MSK_BK_FX, length(subj)), bnd, bnd) 228 | end 229 | function addvarconstr(m :: MosekModel, subj::Vector{Int}, dom :: MOI.Nonnegatives) 230 | bkx = Vector{Boundkey}(undef, length(subj)) 231 | blx = zeros(Float64, length(subj)) 232 | bux = zeros(Float64, length(subj)) 233 | for (i,j) in enumerate(subj) 234 | bk,lo,up = getvarbound(m.task, j) 235 | bkx[i] = if (bk == MSK_BK_FR) MSK_BK_RA else MSK_BK_LO end 236 | bux[i] = up 237 | end 238 | putvarboundlist(m.task, subj, bkx, blx, bux) 239 | end 240 | 241 | function addvarconstr(m :: MosekModel, subj::Vector{Int}, dom :: MOI.Nonpositives) 242 | bkx = Vector{Boundkey}(undef, length(subj)) 243 | blx = zeros(Float64, length(subj)) 244 | bux = zeros(Float64, length(subj)) 245 | for (i,j) in enumerate(subj) 246 | bk,lo,up = getvarbound(m.task, j) 247 | bkx[i] = if (bk == MSK_BK_FR) MSK_BK_RA else MSK_BK_UP end 248 | blx[i] = lo 249 | end 250 | putvarboundlist(m.task, subj, bkx, blx, bux) 251 | end 252 | 253 | abstractset2ct(dom::MOI.ExponentialCone) = MSK_CT_PEXP 254 | abstractset2ct(dom::MOI.DualExponentialCone) = MSK_CT_DEXP 255 | abstractset2ct(dom::MOI.PowerCone) = MSK_CT_PPOW 256 | abstractset2ct(dom::MOI.DualPowerCone) = MSK_CT_DPOW 257 | abstractset2ct(dom::MOI.SecondOrderCone) = MSK_CT_QUAD 258 | abstractset2ct(dom::MOI.RotatedSecondOrderCone) = MSK_CT_RQUAD 259 | 260 | function MOI.add_constraint( 261 | m :: MosekModel, 262 | xs :: MOI.SingleVariable, 263 | dom :: D) where {D <: MOI.AbstractScalarSet} 264 | 265 | subj = getindex(m.x_block, ref2id(xs.variable)) 266 | 267 | mask = domain_type_mask(dom) 268 | if mask & m.x_boundflags[subj] != 0 269 | error("Cannot put multiple bound sets of the same type on a variable") 270 | end 271 | 272 | xcid = allocatevarconstraints(m, 1) 273 | 274 | xc_sub = getindex(m.xc_block,xcid) 275 | 276 | m.xc_bounds[xcid] = mask 277 | m.xc_idxs[xc_sub] = subj 278 | 279 | addvarconstr(m, subj, dom) 280 | 281 | m.x_boundflags[subj] |= mask 282 | 283 | conref = MOI.ConstraintIndex{MOI.SingleVariable,D}(UInt64(xcid) << 1) 284 | 285 | select(m.constrmap,MOI.SingleVariable,D)[conref.value] = xcid 286 | conref 287 | end 288 | 289 | function MOI.add_constraint(m :: MosekModel, 290 | xs :: MOI.VectorOfVariables, 291 | dom :: D) where { D <: PositiveSemidefiniteCone } 292 | N = dom.side_dimension 293 | vars = sympackedUtoL(xs.variables, N) 294 | subj = Vector{Int}(undef, length(vars)) 295 | for i in 1:length(subj) 296 | getindexes(m.x_block, ref2id(vars[i]), subj, i) 297 | end 298 | 299 | mask = domain_type_mask(dom) 300 | if any(mask .& m.x_boundflags[subj[1]] .> 0) 301 | error("Cannot put multiple bound sets of the same type to a variable") 302 | end 303 | 304 | if N < 2 305 | error("Invalid dimension for semidefinite constraint") 306 | end 307 | 308 | NN = sympackedlen(N) 309 | 310 | if length(subj) != NN 311 | error("Mismatching variable length for semidefinite constraint") 312 | end 313 | 314 | id = allocateconstraints(m,NN) 315 | 316 | subi = getindexes(m.c_block,id) 317 | 318 | subii32 = convert(Vector{Int32},subi) 319 | putaijlist(m.task, 320 | subii32, 321 | convert(Vector{Int32},subj), 322 | ones(Float64,NN)) 323 | 324 | addbound!(m,id,subi,zeros(Float64,NN),dom) 325 | 326 | #putconboundlist(m.task,subii32,fill(MSK_BK_FX,NN),zeros(Float64,NN),zeros(Float64,NN)) 327 | #idx = 1 328 | #for j in 1:N 329 | # for i in 1:N 330 | # symmatidx = appendsparsesymmat(m.task,N,Int32[i],Int32[j],Float64[-1.0]) 331 | # putbaraij(m.task,subii32[idx],barvaridx,[symmatidx],Float64[1.0]) 332 | # idx += 1 333 | # end 334 | #end 335 | 336 | # HACK: We need to return a negative to indicate that this is 337 | # not, in fact, a real variable constraint, but rather a real 338 | # constraint, but to resolve return value at compile time we 339 | # need to disguise it as a variable constraint. 340 | #id2cref{MOI.VectorOfVariables,D}(-id) 341 | 342 | conref = MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.PositiveSemidefiniteConeTriangle}(UInt64((id << 1) | 1)) 343 | select(m.constrmap,MOI.VectorOfVariables,MOI.PositiveSemidefiniteConeTriangle)[conref.value] = id 344 | 345 | conref 346 | end 347 | 348 | 349 | 350 | coneparfromset(dom :: MOI.PowerCone{Float64}) = dom.exponent 351 | coneparfromset(dom :: MOI.DualPowerCone{Float64}) = dom.exponent 352 | coneparfromset(dom :: C) where C <: MOI.AbstractSet = 0.0 353 | 354 | function aux_setvardom(m::MosekModel, 355 | xcid::Int, 356 | subj::Vector{Int}, 357 | dom::D) where { D <: VectorCone } 358 | appendcone(m.task,abstractset2ct(dom), coneparfromset(dom), subj) 359 | coneidx = getnumcone(m.task) 360 | m.conecounter += 1 361 | putconename(m.task,coneidx,"$(m.conecounter)") 362 | m.xc_coneid[xcid] = m.conecounter 363 | end 364 | function aux_setvardom(m::MosekModel, xcid::Int, subj::Vector{Int}, 365 | dom :: D) where {D <: MOI.AbstractSet} 366 | addvarconstr(m, subj, dom) 367 | end 368 | 369 | function MOI.add_constraint(m :: MosekModel, xs :: MOI.VectorOfVariables, dom :: D) where {D <: MOI.AbstractSet} 370 | subj = Vector{Int}(undef,length(xs.variables)) 371 | for i in 1:length(subj) 372 | getindexes(m.x_block, ref2id(xs.variables[i]),subj,i) 373 | end 374 | 375 | mask = domain_type_mask(dom) 376 | if any(mask .& m.x_boundflags[subj] .> 0) 377 | error("Cannot multiple bound sets of the same type to a variable") 378 | end 379 | 380 | N = MOI.dimension(dom) 381 | xcid = allocatevarconstraints(m,N) 382 | xc_sub = getindexes(m.xc_block,xcid) 383 | 384 | m.xc_bounds[xcid] = mask 385 | m.xc_idxs[xc_sub] = subj 386 | 387 | aux_setvardom(m, xcid, subj, dom) 388 | 389 | m.x_boundflags[subj] .|= mask 390 | 391 | conref = MOI.ConstraintIndex{MOI.VectorOfVariables,D}(UInt64(xcid) << 1) 392 | select(m.constrmap,MOI.VectorOfVariables,D)[conref.value] = xcid 393 | conref 394 | end 395 | 396 | ################################################################################ 397 | ################################################################################ 398 | 399 | 400 | # Put the linear left-hand side 401 | function addlhsblock!(m :: MosekModel, 402 | conid :: Int, 403 | conidxs :: Vector{Int}, 404 | terms :: Vector{MOI.ScalarAffineTerm{Float64}}) 405 | consubi = getindexes(m.c_block,conid) 406 | subj = Vector{Int}(undef,length(terms)) 407 | for i in 1:length(subj) 408 | getindexes(m.x_block,ref2id(terms[i].variable_index),subj,i) 409 | end 410 | 411 | N = length(consubi) 412 | nnz = length(terms) 413 | 414 | msk_subi = convert(Vector{Int32},consubi) 415 | 416 | msk_rowptr = zeros(Int64, N+1) 417 | for i in conidxs 418 | msk_rowptr[i+1] += 1 419 | end 420 | msk_rowptr[1] = 1 421 | for i in 2:N+1 422 | msk_rowptr[i] += msk_rowptr[i-1] 423 | end 424 | 425 | msk_subj = Array{Int32}(undef,nnz) 426 | msk_cof = Array{Float64}(undef,nnz) 427 | 428 | # sort by row 429 | for i in 1:nnz 430 | msk_subj[msk_rowptr[conidxs[i]]] = subj[i] 431 | msk_cof[msk_rowptr[conidxs[i]]] = terms[i].coefficient 432 | msk_rowptr[conidxs[i]] += 1 433 | end 434 | 435 | for i in N:-1:1 436 | msk_rowptr[i+1] = msk_rowptr[i] 437 | end 438 | msk_rowptr[1] = 1 439 | 440 | putarowlist(m.task, msk_subi, msk_rowptr[1:N], msk_rowptr[2:N+1], msk_subj, msk_cof) 441 | end 442 | 443 | 444 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.Reals) = putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FR,MOI.dimension(dom)),-constant,-constant) 445 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.Zeros) = putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FX,MOI.dimension(dom)),-constant,-constant) 446 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.Nonnegatives) = putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_LO,MOI.dimension(dom)),-constant,-constant) 447 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.Nonpositives) = putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_UP,MOI.dimension(dom)),-constant,-constant) 448 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.GreaterThan{Float64}) = putconbound(m.task,Int32(conidxs[1]),MSK_BK_LO,dom.lower-constant[1],dom.lower-constant[1]) 449 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.LessThan{Float64}) = putconbound(m.task,Int32(conidxs[1]),MSK_BK_UP,dom.upper-constant[1],dom.upper-constant[1]) 450 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.EqualTo{Float64}) = putconbound(m.task,Int32(conidxs[1]),MSK_BK_FX,dom.value-constant[1],dom.value-constant[1]) 451 | addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.Interval{Float64}) = putconbound(m.task,Int32(conidxs[1]),MSK_BK_RA,dom.lower-constant[1],dom.upper-constant[1]) 452 | 453 | # need a special case for this since MOI's variable order in PEXP cone is the reverse if Mosek's 454 | function addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: D) where { D <: Union{MOI.ExponentialCone, MOI.DualExponentialCone} } 455 | N = MOI.dimension(dom) 456 | nalloc = ensurefree(m.x_block,N) 457 | 458 | varid = newblock(m.x_block,N) 459 | numvar = getnumvar(m.task) 460 | if nalloc > 0 461 | appendvars(m.task, length(m.x_block) - numvar) 462 | append!(m.x_boundflags, zeros(Int,length(m.x_block) - numvar)) 463 | append!(m.x_numxc, zeros(Int,length(m.x_block) - numvar)) 464 | end 465 | subj = getindexes(m.x_block,varid) 466 | subj = [ subj[3], subj[2], subj[1] ] 467 | 468 | putaijlist(m.task,conidxs,subj,-ones(Float64,N)) 469 | putvarboundlist(m.task,subj,fill(MSK_BK_FR,N),zeros(Float64,N),zeros(Float64,N)) 470 | putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FX,N),-constant,-constant) 471 | 472 | m.c_block_slack[conid] = varid 473 | 474 | appendcone(m.task,abstractset2ct(dom),coneparfromset(dom),subj) 475 | coneidx = getnumcone(m.task) 476 | m.conecounter += 1 477 | #putconename(m.task,coneidx,"$(m.conecounter)") 478 | m.c_coneid[conid] = m.conecounter 479 | end 480 | 481 | 482 | 483 | function addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: D) where { D <: VectorCone } 484 | N = MOI.dimension(dom) 485 | nalloc = ensurefree(m.x_block,N) 486 | 487 | varid = newblock(m.x_block,N) 488 | numvar = getnumvar(m.task) 489 | if nalloc > 0 490 | appendvars(m.task, length(m.x_block) - numvar) 491 | append!(m.x_boundflags, zeros(Int,length(m.x_block) - numvar)) 492 | append!(m.x_numxc, zeros(Int,length(m.x_block) - numvar)) 493 | end 494 | subj = getindexes(m.x_block,varid) 495 | 496 | putaijlist(m.task,conidxs,subj,-ones(Float64,N)) 497 | putvarboundlist(m.task,subj,fill(MSK_BK_FR,N),zeros(Float64,N),zeros(Float64,N)) 498 | putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FX,N),-constant,-constant) 499 | 500 | m.c_block_slack[conid] = varid 501 | 502 | appendcone(m.task,abstractset2ct(dom),coneparfromset(dom),subj) 503 | coneidx = getnumcone(m.task) 504 | m.conecounter += 1 505 | #putconename(m.task,coneidx,"$(m.conecounter)") 506 | m.c_coneid[conid] = m.conecounter 507 | end 508 | 509 | function addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.PositiveSemidefiniteConeTriangle) 510 | dim = dom.side_dimension 511 | appendbarvars(m.task,Int32[dim]) 512 | barvaridx = getnumbarvar(m.task) 513 | 514 | idx = 1 515 | for j in 1:dim 516 | for i in j:dim 517 | matrixid = appendsparsesymmat(m.task,Int32(dim), Int32[i], Int32[j], Float64[1.0]) 518 | if i == j 519 | putbaraij(m.task, conidxs[idx], barvaridx, Int[matrixid], Float64[-1.0]) 520 | else 521 | putbaraij(m.task, conidxs[idx], barvaridx, Int[matrixid], Float64[-0.5]) 522 | end 523 | #putconname(m.task,Int32(conidxs[idx]),"bar_slack[$i,$j]") 524 | idx += 1 525 | end 526 | end 527 | 528 | putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FX,length(constant)),-constant,-constant) 529 | 530 | 531 | m.c_block_slack[conid] = -barvaridx 532 | end 533 | 534 | #function addbound!(m :: MosekModel, conid :: Int, conidxs :: Vector{Int}, constant :: Vector{Float64}, dom :: MOI.PositiveSemidefiniteConeScaled) 535 | # dim = MOI.dimension(dom) 536 | # #dim = floor(Int,-.5 + sqrt(.25+2*MOI.dimension(dom))) 537 | # n = MOI.dimension(dom) 538 | # 539 | # appendbarvars(m.task,Int32[dim]) 540 | # barvaridx = getnumbarvar(m.task) 541 | # 542 | # idx = 1 543 | # for j in 1:dim 544 | # for i in j:dim 545 | # matrixid = appendsparsesymmat(m.task,Int32(dim), Int32[i], Int32[j], Float64[1.0]) 546 | # if i == j 547 | # putbaraij(m.task, conidxs[idx], barvaridx, Int[matrixid], Float64[-1.0]) 548 | # else 549 | # putbaraij(m.task, conidxs[idx], barvaridx, Int[matrixid], Float64[-sqrt(2.0)/2.0]) 550 | # end 551 | # idx += 1 552 | # end 553 | # end 554 | # 555 | # putconboundlist(m.task,convert(Vector{Int32},conidxs),fill(MSK_BK_FX,length(constant)),-constant,-constant) 556 | # 557 | # m.c_block_slack[conid] = -barvaridx 558 | #end 559 | 560 | 561 | 562 | ################################################################################ 563 | ## MODIFY ##################################################################### 564 | ################################################################################ 565 | 566 | #MOI.canset(m::MosekModel, ::MOI.ConstraintSet, ::Type{MOI.ConstraintIndex{F,D}}) where { F <: LinearFunction, D <: LinearDomain } = true 567 | #MOI.canset(m::MosekModel, ::MOI.ConstraintFunction, ::Type{MOI.ConstraintIndex{F,D}}) where { F <: Union{MOI.SingleVariable,MOI.ScalarAffineFunction}, D <: ScalarLinearDomain } = true 568 | #MOI.canmodify(m::MosekModel, ::Type{MOI.ConstraintIndex{F,D}}, ::Type{<:MOI.AbstractFunctionModification}) where { F <: AffineFunction, D <: MOI.AbstractSet } = true 569 | 570 | chgbound(bl::Float64,bu::Float64,k::Float64,dom :: MOI.LessThan{Float64}) = bl,dom.upper-k 571 | chgbound(bl::Float64,bu::Float64,k::Float64,dom :: MOI.GreaterThan{Float64}) = dom.lower-k,bu 572 | chgbound(bl::Float64,bu::Float64,k::Float64,dom :: MOI.EqualTo{Float64}) = dom.value-k,dom.value-k 573 | chgbound(bl::Float64,bu::Float64,k::Float64,dom :: MOI.Interval{Float64}) = dom.lower-k,dom.upper-k 574 | 575 | function MOI.set(m::MosekModel, 576 | ::MOI.ConstraintSet, 577 | xcref::MOI.ConstraintIndex{F,D}, 578 | dom::D) where { F <: MOI.SingleVariable, 579 | D <: ScalarLinearDomain } 580 | xcid = ref2id(xcref) 581 | j = m.xc_idxs[getindex(m.xc_block,xcid)] 582 | bk,bl,bu = getvarbound(m.task,j) 583 | bl,bu = chgbound(bl,bu,0.0,dom) 584 | putvarbound(m.task,j,bk,bl,bu) 585 | end 586 | 587 | 588 | function MOI.set(m::MosekModel, 589 | ::MOI.ConstraintSet, 590 | cref::MOI.ConstraintIndex{F,D}, 591 | dom::D) where { F <: MOI.ScalarAffineFunction, 592 | D <: ScalarLinearDomain } 593 | cid = ref2id(cref) 594 | i = getindex(m.c_block,cid) # since we are in a scalar domain 595 | bk,bl,bu = getconbound(m.task,i) 596 | bl,bu = chgbound(bl,bu,0.0,dom) 597 | putconbound(m.task,i,bk,bl,bu) 598 | end 599 | 600 | 601 | function set_internal_name(m::MosekModel,c::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D},name::AbstractString) where {D} 602 | cid = ref2id(c) 603 | for i in getindexes(m.c_block, cid) 604 | putconname(m.task,i,name) 605 | end 606 | end 607 | function set_internal_name(m::MosekModel,c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D},name::AbstractString) where {D} 608 | cid = ref2id(c) 609 | i = getindex(m.c_block, cid) 610 | putconname(m.task,i,name) 611 | end 612 | function set_internal_name(m::MosekModel, c::MOI.ConstraintIndex{F,D}, name::AbstractString) where {F,D} end 613 | 614 | 615 | function MOI.set(m ::MosekModel, 616 | ::MOI.ConstraintName, 617 | c ::MOI.ConstraintIndex{F,D}, 618 | name ::AbstractString) where {F,D}#{F<:MOI.AbstractFunction,D<:AbstractSet} 619 | if ! haskey(m.constrnames, name) 620 | m.constrnames[name] = MOI.ConstraintIndex[] 621 | end 622 | push!(m.constrnames[name], c) 623 | set_internal_name(m,c,name) 624 | end 625 | 626 | function MOI.modify(m ::MosekModel, 627 | c ::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D}, 628 | func::MOI.ScalarConstantChange{Float64}) where {D <: MOI.AbstractSet} 629 | 630 | cid = ref2id(c) 631 | 632 | i = getindex(m.c_block, cid) 633 | bk,bl,bu = getconbound(m.task,i) 634 | bl += m.c_constant[i] - func.new_constant 635 | bu += m.c_constant[i] - func.new_constant 636 | m.c_constant[i] = func.new_constant 637 | putconbound(m.task,i,bk,bl,bu) 638 | end 639 | 640 | function MOI.modify(m ::MosekModel, 641 | c ::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D}, 642 | func::MOI.ScalarCoefficientChange{Float64}) where {D <: MOI.AbstractSet} 643 | cid = ref2id(c) 644 | xid = ref2id(func.variable) 645 | 646 | i = getindex(m.c_block,cid) 647 | j = getindex(m.x_block,xid) 648 | 649 | putaij(m.task,i,j,func.new_coefficient) 650 | end 651 | 652 | function MOI.modify(m::MosekModel, 653 | c::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D}, 654 | func::MOI.VectorConstantChange{Float64}) where {D <: MOI.AbstractSet} 655 | cid = ref2id(c) 656 | @assert(cid > 0) 657 | 658 | subi = getindexes(m.c_block, cid) 659 | bk = Vector{Int32}(undef,length(subi)) 660 | bl = Vector{Float64}(undef,length(subi)) 661 | bu = Vector{Float64}(undef,length(subi)) 662 | 663 | bk,bl,bu = getconboundlist(m.task,convert(Vector{Int32},subi)) 664 | bl += m.c_constant[subi] - func.new_constant 665 | bu += m.c_constant[subi] - func.new_constant 666 | m.c_constant[subi] = func.new_constant 667 | 668 | putconboundlist(m.task,convert(Vector{Int32},subi),bk,bl,bu) 669 | end 670 | 671 | function MOI.modify(m::MosekModel, 672 | c::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D}, 673 | func::MOI.MultirowChange{Float64}) where {D <: MOI.AbstractSet} 674 | cid = ref2id(c) 675 | @assert(cid > 0) 676 | 677 | subi = getindexes(m.c_block, cid)[getindex.(func.new_coefficients, 1)] 678 | xid = ref2id(func.variable) 679 | j = getindex(m.x_block,xid) 680 | 681 | putaijlist(m.task,convert(Vector{Int32},subi),fill(j,length(subi)),getindex.(func.new_coefficients,2)) 682 | end 683 | 684 | 685 | 686 | #MOI.cantransform(m::MosekModel, c::MOI.ConstraintIndex{F,D1}, newdom::D2) where {F <: VariableFunction, D1, D2 } = false 687 | #MOI.cantransform(m::MosekModel, c::MOI.ConstraintIndex{MOI.VectorAffineFunction,D1}, newdom::D2) where {D1 <: VectorLinearDomain, D2 <: VectorLinearDomain} = false 688 | #function MOI.cantransform(m::MosekModel, 689 | # cref::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D1}, 690 | # newdom::D2) where {D1 <: ScalarLinearDomain, 691 | # D2 <: ScalarLinearDomain} 692 | # haskey(select(m.constrmap,MOI.ScalarAffineFunction{Float64},D1),cref.value) 693 | #end 694 | 695 | 696 | function MOI.transform(m::MosekModel, 697 | cref::MOI.ConstraintIndex{F,D}, 698 | newdom::D) where {F <: MOI.AbstractFunction, 699 | D <: MOI.AbstractSet} 700 | MOI.modify(m,cref,newdom) 701 | cref 702 | end 703 | 704 | function MOI.transform(m::MosekModel, 705 | cref::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},D1}, 706 | newdom::D2) where {D1 <: ScalarLinearDomain, 707 | D2 <: ScalarLinearDomain} 708 | F = MOI.ScalarAffineFunction{Float64} 709 | 710 | cid = ref2id(cref) 711 | 712 | subi = getindexes(m.c_block,cid) 713 | 714 | addbound!(m,cid,subi,m.c_constant[subi], newdom) 715 | 716 | newcref = MOI.ConstraintIndex{F,D2}(UInt64(cid) << 1) 717 | delete!(select(m.constrmap,F,D1), cref.value) 718 | select(m.constrmap, F, D2)[newcref.value] = cid 719 | newcref 720 | end 721 | 722 | function MOI.transform(m::MosekModel, 723 | cref::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D1}, 724 | newdom::D2) where {D1 <: VectorLinearDomain, 725 | D2 <: VectorLinearDomain} 726 | F = MOI.VectorAffineFunction{Float64} 727 | 728 | cid = ref2id(cref) 729 | 730 | subi = getindexes(m.c_block,cid) 731 | 732 | addbound!(m,cid,subi,m.c_constant[subi], newdom) 733 | 734 | newcref = MOI.ConstraintIndex{F,D2}(UInt64(cid) << 1) 735 | delete!(select(m.constrmap,F,D1), cref.value) 736 | select(m.constrmap,F,D2)[newcref.value] = cid 737 | newcref 738 | end 739 | 740 | #MOI.transform(m::MosekModel, c::MOI.ConstraintIndex{F,D1}, newdom::D2) where {F <: MOI.VectorAffineFunction , D1 <: VectorLinearDomain, D2 <: VectorLinearDomain} = false 741 | 742 | ################################################################################ 743 | ## DELETE ##################################################################### 744 | ################################################################################ 745 | 746 | 747 | #MOI.candelete( 748 | # m ::MosekModel, 749 | # cref::MOI.ConstraintIndex{F,D}) where {F <: Union{MOI.ScalarAffineFunction, 750 | # MOI.VectorAffineFunction, 751 | # MOI.SingleVariable, 752 | # MOI.VectorOfVariables}, 753 | # D <: Union{MOI.LessThan, 754 | # MOI.GreaterThan, 755 | # MOI.EqualTo, 756 | # MOI.Interval, 757 | # MOI.Zeros, 758 | # MOI.Nonpositives, 759 | # MOI.Nonnegatives, 760 | # MOI.Reals}} = MOI.isvalid(m,cref) 761 | # 762 | #MOI.candelete( 763 | # m ::MosekModel, 764 | # cref::MOI.ConstraintIndex{F,D}) where {F <: MOI.AbstractFunction, 765 | # D <: Union{MOI.SecondOrderCone, 766 | # MOI.RotatedSecondOrderCone, 767 | # MOI.ExponentialCone, 768 | # MOI.DualExponentialCone, 769 | # MOI.PowerCone, 770 | # MOI.DualPowerCone}} = false 771 | # 772 | #MOI.candelete( 773 | # m ::MosekModel, 774 | # cref::MOI.ConstraintIndex{F,D}) where {F <: Union{MOI.SingleVariable, 775 | # MOI.VectorOfVariables}, 776 | # D <: Union{MOI.SecondOrderCone, 777 | # MOI.RotatedSecondOrderCone, 778 | # MOI.ExponentialCone, 779 | # MOI.DualExponentialCone, 780 | # MOI.PowerCone, 781 | # MOI.DualPowerCone}} = true 782 | # 783 | 784 | function MOI.delete( 785 | m::MosekModel, 786 | cref::MOI.ConstraintIndex{F,D}) where {F <: Union{MOI.ScalarAffineFunction, 787 | MOI.VectorAffineFunction}, 788 | D <: Union{MOI.LessThan, 789 | MOI.GreaterThan, 790 | MOI.EqualTo, 791 | MOI.Interval, 792 | MOI.Zeros, 793 | MOI.Nonpositives, 794 | MOI.Nonnegatives, 795 | MOI.Reals}} 796 | 797 | delete!(select(m.constrmap, F, D), cref.value) 798 | 799 | cid = ref2id(cref) 800 | subi = getindexes(m.c_block,cid) 801 | 802 | n = length(subi) 803 | subi_i32 = convert(Vector{Int32},subi) 804 | ptr = fill(Int64(0),n) 805 | putarowlist(m.task,subi_i32,ptr,ptr,Int32[],Float64[]) 806 | b = fill(0.0,n) 807 | putconboundlist(m.task,subi_i32,fill(MSK_BK_FX,n),b,b) 808 | 809 | m.c_constant[subi] .= 0.0 810 | deleteblock(m.c_block,cid) 811 | end 812 | 813 | function MOI.delete( 814 | m::MosekModel, 815 | cref::MOI.ConstraintIndex{F,D}) where {F <: Union{MOI.VectorAffineFunction}, 816 | D <: Union{MOI.SecondOrderCone, 817 | MOI.RotatedSecondOrderCone, 818 | MOI.ExponentialCone, 819 | MOI.DualExponentialCone, 820 | MOI.PowerCone, 821 | MOI.DualPowerCone}} 822 | 823 | delete!(select(m.constrmap, F, D), cref.value) 824 | xcid = ref2id(cref) 825 | sub = getindexes(m.xc_block,xcid) 826 | 827 | subj = [ getindex(m.x_block,i) for i in sub ] 828 | N = length(subj) 829 | m.x_boundflags[subj] .&= ~m.xc_bounds[xcid] 830 | asgn,coneidx = getconenameindex(m.task,"$(m.xc_coneid[xcid])") 831 | m.xc_coneid[xcid] = 0 832 | removecone(m.task,coneidx) 833 | 834 | m.x_numxc[subj] -= 1 835 | m.xc_idxs[sub] = 0 836 | m.xc_bounds[xcid] = 0 837 | 838 | deleteblock(m.xc_block,xcid) 839 | end 840 | 841 | 842 | function MOI.delete( 843 | m::MosekModel, 844 | cref::MOI.ConstraintIndex{F,D}) where {F <: Union{MOI.SingleVariable, 845 | MOI.VectorOfVariables}, 846 | D <: Union{ScalarLinearDomain, 847 | VectorLinearDomain, 848 | ScalarIntegerDomain}} 849 | delete!(select(m.constrmap, F, D), cref.value) 850 | 851 | xcid = ref2id(cref) 852 | sub = getindexes(m.xc_block,xcid) 853 | 854 | subj = [ getindex(m.x_block,i) for i in sub ] 855 | N = length(subj) 856 | 857 | m.x_boundflags[subj] .&= ~m.xc_bounds[xcid] 858 | if m.xc_bounds[xcid] & boundflag_int != 0 859 | for i in 1:length(subj) 860 | putvartype(m.task,subj[i],MSK_VAR_TYPE_CONT) 861 | end 862 | end 863 | 864 | if m.xc_bounds[xcid] & boundflag_lower != 0 && m.xc_bounds[xcid] & boundflag_upper != 0 865 | bnd = fill(0.0, length(N)) 866 | putvarboundlist(m.task,convert(Vector{Int32},subj),fill(MSK_BK_FR,N),bnd,bnd) 867 | elseif m.xc_bounds[xcid] & boundflag_lower != 0 868 | bnd = fill(0.0, length(N)) 869 | bk,bl,bu = getvarboundlist(m.task, convert(Vector{Int32},subj)) 870 | 871 | for i in 1:N 872 | if MSK_BK_RA == bk[i] || MSK_BK_FX == bk[i] 873 | bk[i] = MSK_BK_UP 874 | else 875 | bk[i] = MSK_BK_FR 876 | end 877 | end 878 | 879 | putvarboundlist(m.task,convert(Vector{Int32},subj),bk,bl,bu) 880 | elseif m.xc_bounds[xcid] & boundflag_upper != 0 881 | bnd = fill(0.0, length(N)) 882 | bk,bl,bu = getvarboundlist(m.task, subj) 883 | 884 | for i in 1:N 885 | if MSK_BK_RA == bk[i] || MSK_BK_FX == bk[i] 886 | bk[i] = MSK_BK_LO 887 | else 888 | bk[i] = MSK_BK_FR 889 | end 890 | end 891 | 892 | putvarboundlist(m.task,convert(Vector{Int32},subj),bk,bl,bu) 893 | else 894 | @assert(false) 895 | # should not happen 896 | end 897 | 898 | m.x_numxc[subj] .-= 1 899 | m.xc_idxs[sub] .= 0 900 | m.xc_bounds[xcid] = 0 901 | 902 | deleteblock(m.xc_block, xcid) 903 | end 904 | 905 | 906 | 907 | 908 | 909 | 910 | ################################################################################ 911 | ################################################################################ 912 | ################################################################################ 913 | 914 | function allocateconstraints(m :: MosekModel, 915 | N :: Int) 916 | numcon = getnumcon(m.task) 917 | alloced = ensurefree(m.c_block,N) 918 | id = newblock(m.c_block,N) 919 | 920 | M = numblocks(m.c_block) - length(m.c_block_slack) 921 | if alloced > 0 922 | appendcons(m.task, alloced) 923 | append!(m.c_constant, zeros(Float64,alloced)) 924 | end 925 | if M > 0 926 | append!(m.c_block_slack, zeros(Float64,M)) 927 | append!(m.c_coneid, zeros(Float64,M)) 928 | end 929 | id 930 | end 931 | 932 | 933 | function allocatevarconstraints(m :: MosekModel, 934 | N :: Int) 935 | nalloc = ensurefree(m.xc_block,N) 936 | id = newblock(m.xc_block,N) 937 | 938 | M = numblocks(m.xc_block) - length(m.xc_bounds) 939 | if M > 0 940 | append!(m.xc_bounds,zeros(Float64,M)) 941 | append!(m.xc_coneid,zeros(Float64,M)) 942 | end 943 | if nalloc > 0 944 | append!(m.xc_idxs, zeros(Float64,nalloc)) 945 | end 946 | 947 | return id 948 | end 949 | 950 | function allocatevariable(m :: MosekModel, N :: Int) 951 | @assert(length(m.x_boundflags) == length(m.x_block)) 952 | numvar = getnumvar(m.task) 953 | alloced = ensurefree(m.x_block, N) 954 | if alloced > 0 955 | appendvars(m.task, length(m.x_block) - numvar) 956 | append!(m.x_boundflags, zeros(Int,length(m.x_block) - numvar)) 957 | append!(m.x_numxc, zeros(Int,length(m.x_block) - numvar)) 958 | end 959 | return newblock(m.x_block, N) 960 | end 961 | 962 | function MOI.is_valid(model::MosekModel, 963 | ref::MOI.ConstraintIndex{F, D}) where {F, D} 964 | return haskey(select(model.constrmap, F, D), ref.value) 965 | end 966 | function MOI.is_valid(model::MosekModel, ref::MOI.VariableIndex) 967 | return allocated(model.x_block, ref2id(ref)) 968 | end 969 | 970 | 971 | function getvarboundlist(t::Mosek.Task, subj :: Vector{Int32}) 972 | n = length(subj) 973 | bk = Vector{Boundkey}(undef,n) 974 | bl = Vector{Float64}(undef,n) 975 | bu = Vector{Float64}(undef,n) 976 | for i in 1:n 977 | bki,bli,bui = getvarbound(t,subj[i]) 978 | bk[i] = bki 979 | bl[i] = bli 980 | bu[i] = bui 981 | end 982 | bk,bl,bu 983 | end 984 | 985 | function getconboundlist(t::Mosek.Task, subj :: Vector{Int32}) 986 | n = length(subj) 987 | bk = Vector{Boundkey}(undef,n) 988 | bl = Vector{Float64}(undef,n) 989 | bu = Vector{Float64}(undef,n) 990 | for i in 1:n 991 | bki,bli,bui = getconbound(t,subj[i]) 992 | bk[i] = bki 993 | bl[i] = bli 994 | bu[i] = bui 995 | end 996 | bk,bl,bu 997 | end 998 | --------------------------------------------------------------------------------