Skip to content

Commit 519f85d

Browse files
Merge pull request #130 from SciML/multipackage
Remove Requires.jl and make a multi-package setup
2 parents 1bb2a3a + 158072b commit 519f85d

File tree

16 files changed

+245
-196
lines changed

16 files changed

+245
-196
lines changed

.buildkite/pipeline.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
steps:
2-
- label: "GPU"
2+
- label: "LinearSolveCUDA"
33
plugins:
44
- JuliaCI/julia#v1:
55
version: "1"
@@ -9,7 +9,7 @@ steps:
99
queue: "juliagpu"
1010
cuda: "*"
1111
env:
12-
GROUP: 'GPU'
12+
GROUP: 'LinearSolveCUDA'
1313
JULIA_PKG_SERVER: "" # it often struggles with our large artifacts
1414
# SECRET_CODECOV_TOKEN: "..."
1515
timeout_in_minutes: 30

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
matrix:
1414
group:
1515
- All
16+
- LinearSolvePardiso
1617
version:
1718
- '1'
1819
- '1.6'

Project.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ version = "1.15.0"
66
[deps]
77
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
88
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
9+
GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7"
910
IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153"
1011
KLU = "ef3ab10e-7fda-4108-b977-705223b18434"
1112
Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7"
1213
KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77"
1314
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1415
RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4"
1516
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
16-
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1717
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1818
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
1919
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
@@ -23,24 +23,23 @@ UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
2323
[compat]
2424
ArrayInterface = "3, 4, 5"
2525
DocStringExtensions = "0.8"
26+
GPUArrays = "8"
2627
IterativeSolvers = "0.9.2"
2728
KLU = "0.3.0"
2829
Krylov = "0.7.11, 0.8"
2930
KrylovKit = "0.5"
3031
RecursiveFactorization = "0.2.8"
3132
Reexport = "1"
32-
Requires = "1"
3333
SciMLBase = "1.25"
3434
Setfield = "0.7, 0.8"
3535
UnPack = "1"
3636
julia = "1.6"
3737

3838
[extras]
39-
Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2"
4039
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
4140
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
4241
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
4342
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4443

4544
[targets]
46-
test = ["Test", "Pardiso", "Pkg", "Random", "SafeTestsets"]
45+
test = ["Test", "Pkg", "Random", "SafeTestsets"]

docs/src/solvers/solvers.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ customized per-package, details given below describe a subset of important array
7777
factorization does not use pivoting during the numerical factorization and therefore the
7878
procedure can fail even for invertible matrices.
7979

80+
### LinearSolve.jl
81+
82+
LinearSolve.jl contains some linear solvers built in.
83+
84+
- `SimpleLUFactorization`: a simple LU-factorization implementation without BLAS. Fast for small matrices.
85+
8086
### SuiteSparse.jl
8187

8288
By default, the SuiteSparse.jl are implemented for efficiency by caching the
@@ -92,8 +98,11 @@ be used in a context where that assumption does not hold, set `reuse_symbolic=fa
9298

9399
### Pardiso.jl
94100

95-
This package is not loaded by default. Thus in order to use this package you
96-
must first `using Pardiso`. The following algorithms are pre-specified:
101+
!!! note
102+
103+
Using this solver requires adding the package LinearSolvePardiso.jl
104+
105+
The following algorithms are pre-specified:
97106

98107
- `MKLPardisoFactorize(;kwargs...)`: A sparse factorization method.
99108
- `MKLPardisoIterate(;kwargs...)`: A mixed factorization+iterative method.
@@ -128,7 +137,11 @@ end
128137
Note that `CuArrays` are supported by `GenericFactorization` in the "normal" way.
129138
The following are non-standard GPU factorization routines.
130139

131-
- `GPUOffloadFactorization()`: An offloading technique used to GPU-accelerate CPU-based
140+
!!! note
141+
142+
Using this solver requires adding the package LinearSolveCUDA.jl
143+
144+
- `CudaOffloadFactorization()`: An offloading technique used to GPU-accelerate CPU-based
132145
computations. Requires a sufficiently large `A` to overcome the data transfer
133146
costs.
134147

lib/LinearSolveCUDA/Project.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name = "LinearSolveCUDA"
2+
uuid = "0d26bed2-170e-468a-8415-1cfcbba6e180"
3+
authors = ["SciML"]
4+
version = "0.1"
5+
6+
[deps]
7+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
8+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9+
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
10+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
11+
12+
[compat]
13+
CUDA = "3"
14+
SciMLBase = "1.25"
15+
LinearSolve = "1"
16+
julia = "1.6"
17+
18+
[extras]
19+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
20+
21+
[targets]
22+
test = ["Test"]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module LinearSolveCUDA
2+
3+
using CUDA, LinearAlgebra, LinearSolve, SciMLBase
4+
5+
struct CudaOffloadFactorization <: LinearSolve.AbstractFactorization end
6+
7+
function SciMLBase.solve(cache::LinearSolve.LinearCache, alg::CudaOffloadFactorization; kwargs...)
8+
if cache.isfresh
9+
fact = LinearSolve.do_factorization(alg, CUDA.CuArray(cache.A), cache.b, cache.u)
10+
cache = LinearSolve.set_cacheval(cache, fact)
11+
end
12+
13+
copyto!(cache.u, cache.b)
14+
y = Array(ldiv!(cache.cacheval, CUDA.CuArray(cache.u)))
15+
SciMLBase.build_linear_solution(alg, y, nothing, cache)
16+
end
17+
18+
function LinearSolve.do_factorization(alg::CudaOffloadFactorization, A, b, u)
19+
A isa Union{AbstractMatrix,SciMLBase.AbstractDiffEqOperator} ||
20+
error("LU is not defined for $(typeof(A))")
21+
22+
if A isa SciMLBase.AbstractDiffEqOperator
23+
A = A.A
24+
end
25+
fact = qr(CUDA.CuArray(A))
26+
return fact
27+
end
28+
29+
export CudaOffloadFactorization
30+
31+
end
Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,41 @@
1-
using LinearSolve, LinearAlgebra, SparseArrays
2-
using CUDA
3-
using Test
4-
5-
n = 8
6-
A = Matrix(I,n,n)
7-
b = ones(n)
8-
A1 = A/1; b1 = rand(n); x1 = zero(b)
9-
A2 = A/2; b2 = rand(n); x2 = zero(b)
10-
11-
prob1 = LinearProblem(A1, b1; u0=x1)
12-
prob2 = LinearProblem(A2, b2; u0=x2)
13-
14-
cache_kwargs = (;verbose=true, abstol=1e-8, reltol=1e-8, maxiter=30,)
15-
16-
function test_interface(alg, prob1, prob2)
17-
A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
18-
A2 = prob2.A; b2 = prob2.b; x2 = prob2.u0
19-
20-
y = solve(prob1, alg; cache_kwargs...)
21-
@test A1 * y b1
22-
23-
cache = SciMLBase.init(prob1,alg; cache_kwargs...) # initialize cache
24-
y = solve(cache)
25-
@test A1 * y b1
26-
27-
cache = LinearSolve.set_A(cache,copy(A2))
28-
y = solve(cache)
29-
@test A2 * y b1
30-
31-
cache = LinearSolve.set_b(cache,b2)
32-
y = solve(cache)
33-
@test A2 * y b2
34-
35-
return
36-
end
37-
38-
test_interface(GPUOffloadFactorization(), prob1, prob2)
39-
40-
A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
41-
y = solve(prob1)
42-
@test A1 * y b1
1+
using LinearSolve, LinearSolveCUDA, LinearAlgebra, SparseArrays
2+
using Test
3+
4+
n = 8
5+
A = Matrix(I,n,n)
6+
b = ones(n)
7+
A1 = A/1; b1 = rand(n); x1 = zero(b)
8+
A2 = A/2; b2 = rand(n); x2 = zero(b)
9+
10+
prob1 = LinearProblem(A1, b1; u0=x1)
11+
prob2 = LinearProblem(A2, b2; u0=x2)
12+
13+
cache_kwargs = (;verbose=true, abstol=1e-8, reltol=1e-8, maxiter=30,)
14+
15+
function test_interface(alg, prob1, prob2)
16+
A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
17+
A2 = prob2.A; b2 = prob2.b; x2 = prob2.u0
18+
19+
y = solve(prob1, alg; cache_kwargs...)
20+
@test A1 * y b1
21+
22+
cache = SciMLBase.init(prob1,alg; cache_kwargs...) # initialize cache
23+
y = solve(cache)
24+
@test A1 * y b1
25+
26+
cache = LinearSolve.set_A(cache,copy(A2))
27+
y = solve(cache)
28+
@test A2 * y b1
29+
30+
cache = LinearSolve.set_b(cache,b2)
31+
y = solve(cache)
32+
@test A2 * y b2
33+
34+
return
35+
end
36+
37+
test_interface(CudaOffloadFactorization(), prob1, prob2)
38+
39+
A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
40+
y = solve(prob1)
41+
@test A1 * y b1

lib/LinearSolvePardiso/Project.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name = "LinearSolvePardiso"
2+
uuid = "a6722589-28b8-4472-944e-bde9ee6da670"
3+
authors = ["SciML"]
4+
version = "0.1"
5+
6+
[deps]
7+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
8+
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
9+
Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2"
10+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
11+
12+
[compat]
13+
SciMLBase = "1.25"
14+
LinearSolve = "1"
15+
Pardiso = "0.5"
16+
julia = "1.6"
17+
18+
[extras]
19+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
20+
21+
[targets]
22+
test = ["Test"]

src/pardiso.jl renamed to lib/LinearSolvePardiso/src/LinearSolvePardiso.jl

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,37 @@
1-
Base.@kwdef struct PardisoJL <: SciMLLinearSolveAlgorithm
2-
nprocs::Union{Int, Nothing} = nothing
3-
solver_type::Union{Int, Pardiso.Solver, Nothing} = nothing
4-
matrix_type::Union{Int, Pardiso.MatrixType, Nothing} = nothing
5-
iparm::Union{Vector{Tuple{Int,Int}}, Nothing} = nothing
6-
dparm::Union{Vector{Tuple{Int,Int}}, Nothing} = nothing
1+
module LinearSolvePardiso
2+
3+
using Pardiso, LinearSolve, SciMLBase
4+
5+
Base.@kwdef struct PardisoJL <: SciMLBase.SciMLLinearSolveAlgorithm
6+
nprocs::Union{Int,Nothing} = nothing
7+
solver_type::Union{Int,Pardiso.Solver,Nothing} = nothing
8+
matrix_type::Union{Int,Pardiso.MatrixType,Nothing} = nothing
9+
iparm::Union{Vector{Tuple{Int,Int}},Nothing} = nothing
10+
dparm::Union{Vector{Tuple{Int,Int}},Nothing} = nothing
711
end
812

9-
MKLPardisoFactorize(;kwargs...) = PardisoJL(;solver_type = 0,kwargs...)
10-
MKLPardisoIterate(;kwargs...) = PardisoJL(;solver_type = 1,kwargs...)
11-
needs_concrete_A(alg::PardisoJL) = true
13+
MKLPardisoFactorize(; kwargs...) = PardisoJL(; solver_type=0, kwargs...)
14+
MKLPardisoIterate(; kwargs...) = PardisoJL(; solver_type=1, kwargs...)
15+
LinearSolve.needs_concrete_A(alg::PardisoJL) = true
1216

1317
# TODO schur complement functionality
1418

15-
function init_cacheval(alg::PardisoJL, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
19+
function LinearSolve.init_cacheval(alg::PardisoJL, A, b, u, Pl, Pr, maxiters, abstol, reltol, verbose)
1620
@unpack nprocs, solver_type, matrix_type, iparm, dparm = alg
17-
A = convert(AbstractMatrix,A)
21+
A = convert(AbstractMatrix, A)
1822

1923
solver =
20-
if Pardiso.PARDISO_LOADED[]
21-
solver = Pardiso.PardisoSolver()
22-
solver_type !== nothing && Pardiso.set_solver!(solver, solver_type)
24+
if Pardiso.PARDISO_LOADED[]
25+
solver = Pardiso.PardisoSolver()
26+
solver_type !== nothing && Pardiso.set_solver!(solver, solver_type)
2327

24-
solver
25-
else
26-
solver = Pardiso.MKLPardisoSolver()
27-
nprocs !== nothing && Pardiso.set_nprocs!(solver, nprocs)
28+
solver
29+
else
30+
solver = Pardiso.MKLPardisoSolver()
31+
nprocs !== nothing && Pardiso.set_nprocs!(solver, nprocs)
2832

29-
solver
30-
end
33+
solver
34+
end
3135

3236
Pardiso.pardisoinit(solver) # default initialization
3337

@@ -58,7 +62,7 @@ function init_cacheval(alg::PardisoJL, A, b, u, Pl, Pr, maxiters, abstol, reltol
5862
end
5963

6064
# Make sure to say it's transposed because its CSC not CSR
61-
Pardiso.set_iparm!(solver,12, 1)
65+
Pardiso.set_iparm!(solver, 12, 1)
6266

6367
#=
6468
Note: It is recommended to use IPARM(11)=1 (scaling) and IPARM(13)=1 (matchings) for
@@ -71,8 +75,8 @@ function init_cacheval(alg::PardisoJL, A, b, u, Pl, Pr, maxiters, abstol, reltol
7175
be changed to Pardiso.ANALYSIS_NUM_FACT in the solver loop otherwise instabilities
7276
occur in the example https://github.com/SciML/OrdinaryDiffEq.jl/issues/1569
7377
=#
74-
Pardiso.set_iparm!(solver,11, 0)
75-
Pardiso.set_iparm!(solver,13, 0)
78+
Pardiso.set_iparm!(solver, 11, 0)
79+
Pardiso.set_iparm!(solver, 13, 0)
7680

7781
Pardiso.set_phase!(solver, Pardiso.ANALYSIS)
7882

@@ -81,17 +85,17 @@ function init_cacheval(alg::PardisoJL, A, b, u, Pl, Pr, maxiters, abstol, reltol
8185
# applies these exact factors L and U for the next steps in a
8286
# preconditioned Krylov-Subspace iteration. If the iteration does not
8387
# converge, the solver will automatically switch back to the numerical factorization.
84-
Pardiso.set_iparm!(solver,3,round(Int,abs(log10(reltol)),RoundDown) * 10 + 1)
88+
Pardiso.set_iparm!(solver, 3, round(Int, abs(log10(reltol)), RoundDown) * 10 + 1)
8589
end
8690

8791
Pardiso.pardiso(solver, u, A, b)
8892

8993
return solver
9094
end
9195

92-
function SciMLBase.solve(cache::LinearCache, alg::PardisoJL; kwargs...)
96+
function SciMLBase.solve(cache::LinearSolve.LinearCache, alg::PardisoJL; kwargs...)
9397
@unpack A, b, u = cache
94-
A = convert(AbstractMatrix,A)
98+
A = convert(AbstractMatrix, A)
9599

96100
if cache.isfresh
97101
Pardiso.set_phase!(cache.cacheval, Pardiso.NUM_FACT)
@@ -100,10 +104,12 @@ function SciMLBase.solve(cache::LinearCache, alg::PardisoJL; kwargs...)
100104

101105
Pardiso.set_phase!(cache.cacheval, Pardiso.SOLVE_ITERATIVE_REFINE)
102106
Pardiso.pardiso(cache.cacheval, u, A, b)
103-
return SciMLBase.build_linear_solution(alg,cache.u,nothing,cache)
107+
return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache)
104108
end
105109

106110
# Add finalizer to release memory
107111
# Pardiso.set_phase!(cache.cacheval, Pardiso.RELEASE_ALL)
108112

109113
export PardisoJL, MKLPardisoFactorize, MKLPardisoIterate
114+
115+
end

0 commit comments

Comments
 (0)