Skip to content

Commit

Permalink
add support for sparse-direct-mkl-pardiso solver (#255)
Browse files Browse the repository at this point in the history
* define scs_version for each solver, independently

* add MKLDirectSolver based on SCS_MKL_jll

* bump to 1.1.2

* hide MKLDirectSolver behind @require

* remove dlopen magic opening libmkl_gnu_thread

* only import SCS_MKL on linux

SCS_MKL_jll is build only for linux platforms, but MKL_jll is also
available on Windows/Macos causing warnings on these
platforms.

* bring SCS_*_jlls to 3.2.1

* bump to 1.1.3

* load SCS_MKL_jll only on x86_64-linux

* add copyright notice to mkl_direct.jl

* add section on Linear solvers to README
  • Loading branch information
Marek Kaluba authored Nov 2, 2022
1 parent 762b65f commit 85d87ea
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 22 deletions.
8 changes: 5 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
name = "SCS"
uuid = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13"
repo = "https://github.com/jump-dev/SCS.jl"
version = "1.1.2"
version = "1.1.3"

[deps]
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SCS_GPU_jll = "af6e375f-46ec-5fa0-b791-491b0dfa44a4"
SCS_MKL_jll = "3f2553a9-4106-52be-b7dd-865123654657"
SCS_jll = "f4f2fc5b-1d94-523c-97ea-2ab488bedf4b"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
MathOptInterface = "1"
Requires = "1"
SCS_GPU_jll = "=3.2.0"
SCS_jll = "=3.2.0"
SCS_GPU_jll = "=3.2.1"
SCS_MKL_jll = "=3.2.2"
SCS_jll = "=3.2.1"
julia = "1.6"

[extras]
Expand Down
70 changes: 55 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Install SCS.jl using the Julia package manager:
```julia
import Pkg; Pkg.add("SCS")
```
In addition to installing the SCS.jl package, this will also download and
In addition to installing the `SCS.jl` package, this will also download and
install the SCS binaries. (You do not need to install SCS separately.)

To use a custom binary, read the [Custom solver binaries](https://jump.dev/JuMP.jl/stable/developers/custom_solver_binaries/)
Expand Down Expand Up @@ -85,14 +85,60 @@ Common options are:
See the [`glbopts.h` header](https://github.com/cvxgrp/scs/blob/3aaa93c7aa04c7001df5e51b81f21b126dfa99b3/include/glbopts.h#L35)
for other options.

Select one of the linear solvers using the `linear_solver` option. The values
available are `SCS.DirectSolver` (the default) and `SCS.IndirectSolver`. A third
option for using a GPU is experimental, see the section below.
### Linear solvers

#### SCS on GPU
`SCS` uses a linear solver internally, see
[this section](https://www.cvxgrp.org/scs/linear_solver/index.html#linear-system-solver)
of `SCS` documentation. `SCS.jl` ships with
* `SCS.DirectSolver` (sparse direct, the default) and
* `SCS.LinearSolver` (sparse indirect, by conjugate gradient)
enabled.

The find currently available linear solvers one can inspect `SCS.available_solvers`:
```julia
julia> using SCS

julia> SCS.available_solvers
2-element Vector{DataType}:
SCS.DirectSolver
SCS.IndirectSolver
```

To select the linear solver of choice
* pass the `linear_solver` option to `JuMP.optimizer_with_attributes`, or to `MOI.OptimizerWithAttributes`;
* specify the solver as the first argument when using `scs_solve` directly (see scetion Low-level wrapper below).

#### SCS with MKL Pardiso linear solver

To enable the MKL Pardiso (direct sparse) solver one needs to load `MKL_jll`
**before** `SCS`:

```julia
julia> import Pkg

julia> Pkg.add(Pkg.PackageSpec(name = "MKL_jll", version = "2022.2"))

julia> using MKL_jll # This must be called before `using SCS`.

julia> using SCS

julia> SCS.available_solvers
3-element Vector{DataType}:
SCS.DirectSolver
SCS.IndirectSolver
SCS.MKLDirectSolver
```

The `MKLDirectSolver` is available on `Linux x86_64` platform only.

#### SCS with Sparse GPU indirect solver (CUDA only)

> Note: as of version 1.0 the support for the GPU solver is broken (see
[this issue](https://github.com/jump-dev/SCS.jl/issues/245)).

To enable the indirect linear solver on gpu one needs to load `CUDA_jll`
**before** `SCS`:

An experimental `SCS.GpuIndirectSolver` can be used on Linux. Note that
`CUDA_jll` must be installed and loaded **before* `SCS`.
```julia
julia> import Pkg

Expand All @@ -107,16 +153,10 @@ julia> SCS.available_solvers
SCS.DirectSolver
SCS.IndirectSolver
SCS.GpuIndirectSolver

julia> optimizer = SCS.Optimizer();

julia> MOI.set(
optimizer,
MOI.RawParameter("linear_solver"),
SCS.GpuIndirectSolver,
)
```

The `GpuIndirectSolver` is available on `Linux x86_64` platform only.

### Low-level wrapper

SCS.jl provides a low-level interface to solve a problem directly, without
Expand Down
14 changes: 14 additions & 0 deletions src/SCS.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SparseArrays
function __init__()
global indirect = SCS_jll.libscsindir
global direct = SCS_jll.libscsdir

Requires.@require(
CUDA_jll = "e9e359dc-d701-5aa8-82ae-09bbf812ea83",
begin
Expand All @@ -21,13 +22,26 @@ function __init__()
push!(available_solvers, GpuIndirectSolver)
end
)
Requires.@require(
MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7",
begin
if Sys.islinux() && Sys.ARCH == :x86_64
import SCS_MKL_jll
import SCS_MKL_jll.MKL_jll
global mkldirect = SCS_MKL_jll.libscsmkl

push!(available_solvers, MKLDirectSolver)
end
end
)
return
end

include("c_wrapper.jl")
include("linear_solvers/direct.jl")
include("linear_solvers/indirect.jl")
include("linear_solvers/gpu_indirect.jl")
include("linear_solvers/mkl_direct.jl")
include("MOI_wrapper/MOI_wrapper.jl")

const available_solvers = [DirectSolver, IndirectSolver]
Expand Down
4 changes: 3 additions & 1 deletion src/linear_solvers/direct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ function scs_finish(::Type{DirectSolver}, work::Ptr{Cvoid})
return @ccall direct.scs_finish(work::Ptr{Cvoid})::Cvoid
end

function scs_version()
function scs_version(::Type{DirectSolver})
return unsafe_string(@ccall direct.scs_version()::Cstring)
end

scs_version() = scs_version(DirectSolver)
4 changes: 4 additions & 0 deletions src/linear_solvers/gpu_indirect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ end
function scs_finish(::Type{GpuIndirectSolver}, work::Ptr{Cvoid})
return @ccall gpuindirect.scs_finish(work::Ptr{Cvoid})::Cvoid
end

function scs_version(::Type{GpuIndirectSolver})
return unsafe_string(@ccall gpuindirect.scs_version()::Cstring)
end
4 changes: 4 additions & 0 deletions src/linear_solvers/indirect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ end
function scs_finish(::Type{IndirectSolver}, work::Ptr{Cvoid})
return @ccall indirect.scs_finish(work::Ptr{Cvoid})::Cvoid
end

function scs_version(::Type{IndirectSolver})
return unsafe_string(@ccall indirect.scs_version()::Cstring)
end
66 changes: 66 additions & 0 deletions src/linear_solvers/mkl_direct.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright (c) 2022: SCS.jl contributors
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

struct MKLDirectSolver <: LinearSolver end

scsint_t(::Type{MKLDirectSolver}) = Clonglong

function scs_set_default_settings(
::Type{MKLDirectSolver},
stgs::ScsSettings{I},
) where {I<:Clonglong}
return @ccall(
mkldirect.scs_set_default_settings(stgs::Ref{ScsSettings{I}})::Cvoid,
)
end

function scs_init(
::Type{MKLDirectSolver},
data::ScsData{I},
cone::ScsCone{I},
stgs::ScsSettings{I},
) where {I<:Clonglong}
return @ccall mkldirect.scs_init(
data::Ref{ScsData{I}},
cone::Ref{ScsCone{I}},
stgs::Ref{ScsSettings{I}},
)::Ptr{Cvoid}
end

function scs_update(
::Type{MKLDirectSolver},
work::Ptr{Cvoid},
b::Vector{Float64},
c::Vector{Float64},
)
return @ccall mkldirect.scs_update(
work::Ptr{Cvoid},
b::Ref{Float64},
c::Ref{Float64},
)::Clonglong
end

function scs_solve(
::Type{MKLDirectSolver},
work::Ptr{Cvoid},
solution::ScsSolution,
info::ScsInfo{I},
warm_start::Integer,
) where {I<:Clonglong}
return @ccall mkldirect.scs_solve(
work::Ptr{Cvoid},
solution::Ref{ScsSolution},
info::Ref{ScsInfo{I}},
warm_start::Clonglong,
)::Clonglong
end

function scs_finish(::Type{MKLDirectSolver}, work::Ptr{Cvoid})
return @ccall mkldirect.scs_finish(work::Ptr{Cvoid})::Cvoid
end

function scs_version(::Type{MKLDirectSolver})
return unsafe_string(@ccall mkldirect.scs_version()::Cstring)
end
4 changes: 4 additions & 0 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ test_DirectSolver() = _test_runtests(SCS.DirectSolver)

test_IndirectSolver() = _test_runtests(SCS.IndirectSolver)

@static if Sys.islinux() && Sys.ARCH == :x86_64
test_MKLDirectSolver() = _test_runtests(SCS.MKLDirectSolver)
end

function _test_runtests(linear_solver)
optimizer = SCS.Optimizer()
MOI.set(
Expand Down
19 changes: 16 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,25 @@ if get(ENV, "BUILDKITE", "false") == "true"
end

using Test
# MKL_jll is not in our Project.toml, so we need to install it.
import Pkg
Pkg.add(Pkg.PackageSpec(name = "MKL_jll", version = "2022"))
using MKL_jll # MKL_jll must be loaded _before_ SCS!
using SCS

if Sys.islinux() && Sys.ARCH == :x86_64
@test SCS.MKLDirectSolver in SCS.available_solvers
end

include("test_problems.jl")
for s in SCS.available_solvers
feasible_basic_problems(s)
test_options(s)
@test SCS.scs_version() isa String
@test VersionNumber(SCS.scs_version()) >= v"3.2.0"
for solver in SCS.available_solvers
@test SCS.scs_version(solver) isa String
@test VersionNumber(SCS.scs_version(solver)) >= v"3.2.0"

feasible_basic_problems(solver)
test_options(solver)
end

include("MOI_wrapper.jl")

2 comments on commit 85d87ea

@kalmarek
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/71489

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.1.3 -m "<description of version>" 85d87ea49214678d42d15b43c59dbca070a05342
git push origin v1.1.3

Please sign in to comment.