Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/CompatHelper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CompatHelper

on:
schedule:
- cron: '00 00 * * *'
workflow_dispatch:

jobs:
CompatHelper:
runs-on: ${{ matrix.os }}
strategy:
matrix:
julia-version: [1.2.0]
julia-arch: [x86]
os: [ubuntu-latest]
steps:
- name: Pkg.add("CompatHelper")
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
- name: CompatHelper.main()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: julia -e 'using CompatHelper; CompatHelper.main()'
15 changes: 15 additions & 0 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: TagBot
on:
issue_comment:
types:
- created
workflow_dispatch:
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI
on:
push:
branches: [master]
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- version: '1.0'
os: ubuntu-latest
arch: x64
- version: '1'
os: ubuntu-latest
arch: x64
- version: '1'
os: ubuntu-latest
arch: x86
# - version: 'nightly'
# os: ubuntu-latest
# arch: x64
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
with:
depwarn: error
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
file: lcov.info
8 changes: 0 additions & 8 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
MathOptInterface = "0.9"
MathOptInterface = "0.10.5"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
156 changes: 85 additions & 71 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,47 +48,6 @@ function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual,
return optimizer.y
end

function MOI.is_empty(optimizer::Optimizer)
return optimizer.sense === nothing && optimizer.objective_function === nothing &&
optimizer.set === nothing
end

function MOI.empty!(optimizer::Optimizer)
optimizer.sense = nothing
optimizer.objective_function = nothing
optimizer.set = nothing
optimizer.X = nothing
optimizer.x = nothing
optimizer.y = nothing
optimizer.results = nothing
end

MOI.Utilities.supports_default_copy_to(::Optimizer, names::Bool) = !names

function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kwargs...)
return MOI.Utilities.automatic_copy_to(dest, src; kwargs...)
end

function MOI.get(optimizer::Optimizer, ::MOI.ListOfModelAttributesSet)
attributes = MOI.AbstractModelAttribute[]
if optimizer.sense !== nothing
push!(optimizer, optimizer.sense)
end
if optimizer.objective_function !== nothing
F = typeof(optimizer.objective_function)
push!(optimizer, MOI.ObjectiveFunction{F}())
end
return attributes
end

function MOI.get(::Optimizer, ::MOI.ListOfVariableAttributesSet)
return MOI.AbstractVariableAttribute[]
end

function MOI.get(::Optimizer, ::MOI.ListOfConstraintAttributesSet)
return MOI.AbstractConstraintAttribute[]
end

function MOI.supports(
optimizer::Optimizer{T},
::Union{MOI.ObjectiveSense,
Expand All @@ -108,35 +67,6 @@ end
function MOI.is_valid(optimizer::Optimizer, ci::MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope})
return optimizer.set !== nothing && isone(ci.value)
end
function MOI.get(optimizer::Optimizer, ::MOI.ConstraintFunction,
ci::MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope})
MOI.throw_if_not_valid(optimizer, ci)
return MOI.VectorOfVariables(MOI.get(optimizer, MOI.ListOfVariableIndices()))
end
function MOI.get(optimizer::Optimizer, ::MOI.NumberOfVariables)
return optimizer.set === nothing ? 0 : MOI.dimension(optimizer.set)
end
function MOI.get(optimizer::Optimizer, ::MOI.ListOfVariableIndices)
return MOI.VariableIndex.(1:MOI.get(optimizer, MOI.NumberOfVariables()))
end
MOI.get(optimizer::Optimizer, ::MOI.VariableName, ::MOI.VariableIndex) = ""
function MOI.get(optimizer::Optimizer, ::MOI.ListOfConstraints)
constraint_types = Tuple{DataType, DataType}[]
if optimizer.set !== nothing
push!(constraint_types, (MOI.VectorOfVariables, Elliptope))
end
return constraint_types
end
function MOI.get(optimizer::Optimizer, ::MOI.NumberOfConstraints{MOI.VectorOfVariables, Elliptope})
return optimizer.set === nothing ? 0 : 1
end
function MOI.get(optimizer::Optimizer, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables, Elliptope})
indices = MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope}[]
if optimizer.set !== nothing
push!(indices, MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope}(1))
end
return indices
end

function MOI.set(optimizer::Optimizer, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense)
if sense == MOI.FEASIBILITY_SENSE
Expand All @@ -155,14 +85,15 @@ MOI.get(optimizer::Optimizer{T}, ::MOI.ObjectiveFunctionType) where {T} = MOI.Sc
function MOI.get(optimizer::Optimizer{T}, ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}) where T
return optimizer.objective_function
end

function MOI.optimize!(optimizer::Optimizer{T}) where T
!iszero(optimizer.objective_function.constant) && error("Nonzero objective constant not supported")
mapping = [(i, j) for j in 2:optimizer.set.side_dimension for i in 1:(j - 1)]
I = Int[]
J = Int[]
V = T[]
for term in optimizer.objective_function.terms
i, j = mapping[term.variable_index.value]
i, j = mapping[term.variable.value]
push!(I, i)
push!(I, j)
push!(J, j)
Expand All @@ -181,3 +112,86 @@ function MOI.optimize!(optimizer::Optimizer{T}) where T
optimizer.x = [optimizer.X[i, j] for j in 2:optimizer.set.side_dimension for i in 1:(j - 1)]
return
end

# Needed to copy to the optimizer
function MOI.is_empty(optimizer::Optimizer)
return optimizer.sense === nothing && optimizer.objective_function === nothing &&
optimizer.set === nothing
end

function MOI.empty!(optimizer::Optimizer)
optimizer.sense = nothing
optimizer.objective_function = nothing
optimizer.set = nothing
optimizer.X = nothing
optimizer.x = nothing
optimizer.y = nothing
optimizer.results = nothing
end

MOI.supports_incremental_interface(::Optimizer) = true

function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
return MOI.Utilities.default_copy_to(dest, src)
end

# Needed to copy from the optimizer
function MOI.get(optimizer::Optimizer, ::MOI.ListOfOptimizerAttributesSet)
return collect(keys(optimizer.options))
end

function MOI.get(optimizer::Optimizer, ::MOI.ListOfModelAttributesSet)
attributes = MOI.AbstractModelAttribute[]
if optimizer.sense !== nothing
push!(attributes, MOI.ObjectiveSense())
end
if optimizer.objective_function !== nothing
F = typeof(optimizer.objective_function)
push!(attributes, MOI.ObjectiveFunction{F}())
end
return attributes
end

function MOI.get(::Optimizer, ::MOI.ListOfVariableAttributesSet)
return MOI.AbstractVariableAttribute[]
end

function MOI.get(::Optimizer, ::MOI.ListOfConstraintAttributesSet)
return MOI.AbstractConstraintAttribute[]
end

function MOI.get(optimizer::Optimizer, ::MOI.ListOfConstraintTypesPresent)
constraint_types = Tuple{DataType, DataType}[]
if optimizer.set !== nothing
push!(constraint_types, (MOI.VectorOfVariables, Elliptope))
end
return constraint_types
end
function MOI.get(optimizer::Optimizer, ::MOI.NumberOfConstraints{MOI.VectorOfVariables, Elliptope})
return optimizer.set === nothing ? 0 : 1
end
function MOI.get(optimizer::Optimizer, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables, Elliptope})
indices = MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope}[]
if optimizer.set !== nothing
push!(indices, MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope}(1))
end
return indices
end
function MOI.get(optimizer::Optimizer, ::MOI.ConstraintFunction,
ci::MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope})
MOI.throw_if_not_valid(optimizer, ci)
return MOI.VectorOfVariables(MOI.get(optimizer, MOI.ListOfVariableIndices()))
end
function MOI.get(optimizer::Optimizer, ::MOI.ConstraintSet,
ci::MOI.ConstraintIndex{MOI.VectorOfVariables, Elliptope})
MOI.throw_if_not_valid(optimizer, ci)
return optimizer.set
end

function MOI.get(optimizer::Optimizer, ::MOI.NumberOfVariables)
return optimizer.set === nothing ? 0 : MOI.dimension(optimizer.set)
end
function MOI.get(optimizer::Optimizer, ::MOI.ListOfVariableIndices)
return MOI.VariableIndex.(1:MOI.get(optimizer, MOI.NumberOfVariables()))
end
MOI.get(optimizer::Optimizer, ::MOI.VariableName, ::MOI.VariableIndex) = ""
2 changes: 1 addition & 1 deletion src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const SUPPORTED_GETTABLE_ATTRIBUTES = Union{
MOI.TerminationStatus,
MOI.PrimalStatus,
MOI.DualStatus,
MOI.SolveTime,
MOI.SolveTimeSec,
NumberOfIterations,
NumberOfCholeskyFactorizations
}
2 changes: 1 addition & 1 deletion src/mc_psd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function mc_psd(L::Symmetric{T};
end # end of main loop

results = Dict{MOI.AbstractModelAttribute, Any}(
MOI.SolveTime() => (current - start) / 1e9,
MOI.SolveTimeSec() => (current - start) / 1e9,
NumberOfIterations() => iter,
NumberOfCholeskyFactorizations() => cholcnt,
)
Expand Down
28 changes: 10 additions & 18 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,16 @@ const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

import MCPSD
const OPTIMIZER_CONSTRUCTOR = MOI.OptimizerWithAttributes(MCPSD.Optimizer, MOI.Silent() => true)
const OPTIMIZER = MOI.instantiate(OPTIMIZER_CONSTRUCTOR)

@testset "SolverName" begin
@test MOI.get(OPTIMIZER, MOI.SolverName()) == "MCPSD"
@test MOI.get(MCPSD.Optimizer(), MOI.SolverName()) == "MCPSD"
end

@testset "supports_default_copy_to" begin
@test MOIU.supports_default_copy_to(OPTIMIZER, false)
@test !MOIU.supports_default_copy_to(OPTIMIZER, true)
@testset "supports_incremental_interface" begin
@test MOI.supports_incremental_interface(MCPSD.Optimizer())
end

const BRIDGED = MOI.instantiate(OPTIMIZER_CONSTRUCTOR, with_bridge_type = Float64)
const CONFIG = MOIT.TestConfig(atol=1e-6, rtol=1e-6)

function moi_test(optimizer, L::Matrix{T}, expected_X, expected_y, expected_obj, tol) where T
MOI.empty!(optimizer)
@test MOI.is_empty(optimizer)
@test MOI.get(optimizer, MOI.SolverName()) == "MCPSD"
MOI.supports(optimizer, MCPSD.Digits())
Expand All @@ -36,16 +29,15 @@ function moi_test(optimizer, L::Matrix{T}, expected_X, expected_y, expected_obj,
MOI.set(optimizer, MOI.Silent(), true)
@test MOI.get(optimizer, MOI.Silent())
@test MOI.is_empty(optimizer)
@test isempty(MOI.get(optimizer, MOI.ListOfConstraints()))
@test isempty(MOI.get(optimizer, MOI.ListOfConstraintTypesPresent()))
F = MOI.VectorOfVariables
S = MCPSD.Elliptope
@test MOI.get(optimizer, MOI.NumberOfConstraints{F, S}()) == 0
@test isempty(MOI.get(optimizer, MOI.ListOfConstraintIndices{F, S}()))
@test MOI.supports_add_constrained_variables(optimizer, MCPSD.Elliptope)
x, cx = MOI.add_constrained_variables(optimizer, MCPSD.Elliptope(size(L, 1)))
@test !MOI.is_empty(optimizer)
@test cx == MOI.ConstraintIndex{F, S}(1)
@test MOI.get(optimizer, MOI.ListOfConstraints()) == [(F, S)]
@test MOI.get(optimizer, MOI.ListOfConstraintTypesPresent()) == [(F, S)]
@test MOI.get(optimizer, MOI.NumberOfConstraints{F, S}()) == 1
@test MOI.get(optimizer, MOI.ListOfConstraintIndices{F, S}()) == [cx]
@test all(x -> MOI.is_valid(optimizer, x), x)
Expand All @@ -71,15 +63,15 @@ function moi_test(optimizer, L::Matrix{T}, expected_X, expected_y, expected_obj,
@test MOI.get(optimizer, MOI.ConstraintPrimal(), cx) ≈ expected_x atol=tol rtol=tol
@test MOI.get(optimizer, MOI.ConstraintDual(), cx) ≈ expected_y atol=tol rtol=tol
@test MOI.get(optimizer, MCPSD.NumberOfIterations()) == 19
@test MOI.get(optimizer, MOI.SolveTime()) isa Float64
@show MOI.get(optimizer, MOI.SolveTime())
@test MOI.get(optimizer, MOI.SolveTimeSec()) isa Float64
@show MOI.get(optimizer, MOI.SolveTimeSec())
end

@testset "[MOI_wrapper] Wikipedia example with $T" for T in [Float64, BigFloat]
optimizer = MCPSD.Optimizer{T}()
moi_test(optimizer, wikipedia_example(T)...)
cached = MOIU.CachingOptimizer(MCPSD.Optimizer{T}(), MOIU.AUTOMATIC)
MOI.empty!(optimizer)
cached = MOIU.CachingOptimizer(MCPSD.Optimizer{T}(), optimizer)
moi_test(optimizer, wikipedia_example(T)...)
MOIU.reset_optimizer(cached, optimizer)
moi_test(cached, wikipedia_example(T)...)
end

4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ function direct_test(L::Matrix{T}, expected_X, expected_y, expected_obj, tol) wh
@test y isa Vector{T}
@test y ≈ expected_y atol=tol rtol=tol
@test results[MCPSD.NumberOfIterations()] == 19
@test results[MOI.SolveTime()] isa Float64
@show results[MOI.SolveTime()]
@test results[MOI.SolveTimeSec()] isa Float64
@show results[MOI.SolveTimeSec()]
end
end

Expand Down