Skip to content

Commit 7b3f3b1

Browse files
authored
Implement sametype_transpose and use it for A -> At (#35)
1 parent e42ee8d commit 7b3f3b1

File tree

9 files changed

+84
-11
lines changed

9 files changed

+84
-11
lines changed

src/CoolPDLP.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ end
5050

5151
include("MOI_wrapper.jl")
5252

53+
@public sametype_transpose
54+
5355
export GPUSparseMatrixCOO, GPUSparseMatrixCSR, GPUSparseMatrixELL
5456

5557
export MILP, nbvar, nbvar_int, nbvar_cont, nbcons, nbcons_eq, nbcons_ineq

src/problems/milp.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Represent a Mixed Integer Linear Program in "cuPDLPx form":
1010
1111
MILP(;
1212
c, lv, uv, A, lc, uc,
13+
At=sametype_transpose(A),
1314
[D1, D2, int_var, var_names, dataset, name, path]
1415
)
1516
@@ -21,6 +22,7 @@ struct MILP{
2122
T <: Number,
2223
V <: DenseVector{T},
2324
M <: AbstractMatrix{T},
25+
Mt <: AbstractMatrix{T},
2426
Vb <: DenseVector{Bool},
2527
}
2628
"objective vector"
@@ -32,7 +34,7 @@ struct MILP{
3234
"constraint matrix"
3335
A::M
3436
"transposed constraint matrix"
35-
At::M
37+
At::Mt
3638
"constraint lower bound"
3739
lc::V
3840
"constraint upper bound"
@@ -57,7 +59,7 @@ struct MILP{
5759
lv,
5860
uv,
5961
A,
60-
At = convert(typeof(A), transpose(A)),
62+
At = sametype_transpose(A),
6163
lc,
6264
uc,
6365
D1 = Diagonal(one!(similar(lc))),
@@ -77,13 +79,15 @@ struct MILP{
7779

7880
T = Base.promote_eltype(c, lv, uv, A, At, lc, uc, D1, D2)
7981
V = promote_type(typeof(c), typeof(lv), typeof(uv), typeof(lc), typeof(uc))
80-
M = promote_type(typeof(A), typeof(At))
82+
M = typeof(A)
83+
Mt = typeof(At)
8184
Vb = typeof(int_var)
8285

8386
if (
8487
!isconcretetype(T) ||
8588
!isconcretetype(V) ||
8689
!isconcretetype(M) ||
90+
!isconcretetype(Mt) ||
8791
!isconcretetype(Vb)
8892
)
8993
throw(ArgumentError("Abstract type parameter"))
@@ -95,7 +99,7 @@ struct MILP{
9599
name = splitext(splitpath(path)[end])[1]
96100
end
97101

98-
return new{T, V, M, Vb}(
102+
return new{T, V, M, Mt, Vb}(
99103
c,
100104
lv,
101105
uv,
@@ -136,14 +140,14 @@ function MILP(qps::QPSData; kwargs...)
136140
)
137141
end
138142

139-
function Base.show(io::IO, milp::MILP{T, V, M}) where {T, V, M}
143+
function Base.show(io::IO, milp::MILP{T, V, M, Mt}) where {T, V, M, Mt}
140144
return print(
141145
io, """
142146
MILP instance $(milp.name) from dataset $(milp.dataset):
143147
- types:
144148
- values $T
145149
- vectors $V
146-
- matrices $M
150+
- matrices $(M == Mt ? M : (M, Mt))
147151
- variables: $(nbvar(milp))
148152
- $(nbvar_cont(milp)) continuous
149153
- $(nbvar_int(milp)) integer

src/utils/linalg.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
"""
2+
sametype_transpose(A::AbstractMatrix)
3+
4+
Return a matrix of the same type of `A` containing `transpose(A)` (as opposed to a `Transpose{...}` wrapper).
5+
6+
The default implementation is just `convert(typeof(A), transpose(A))` but it may need to be overloaded for certain matrix types.
7+
"""
8+
sametype_transpose(A::AbstractMatrix) = convert(typeof(A), transpose(A))
9+
110
zero!(x::AbstractArray) = fill!(x, zero(eltype(x)))
211
one!(x::AbstractArray) = fill!(x, one(eltype(x)))
312

src/utils/mat_coo.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,19 @@ function Adapt.adapt_structure(to, A::GPUSparseMatrixCOO)
4747
)
4848
end
4949

50+
function SparseArrays.SparseMatrixCSC(A::GPUSparseMatrixCOO)
51+
return sparse(Vector(A.rowval), Vector(A.colval), Vector(A.nzval), A.m, A.n)
52+
end
53+
5054
function GPUSparseMatrixCOO(A::SparseMatrixCSC{T, Ti}) where {T, Ti}
5155
rowval, colval, nzval = findnz(A)
5256
return GPUSparseMatrixCOO(A.m, A.n, rowval, colval, nzval)
5357
end
5458

59+
function sametype_transpose(A::GPUSparseMatrixCOO)
60+
return GPUSparseMatrixCOO(A.n, A.m, A.colval, A.rowval, A.nzval)
61+
end
62+
5563
@kernel function spmv_coo!(
5664
c::DenseVector{T},
5765
A_rowval::DenseVector{Ti},

src/utils/mat_csr.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,21 @@ function Adapt.adapt_structure(to, A::GPUSparseMatrixCSR)
5656
end
5757

5858
function GPUSparseMatrixCSR(A::SparseMatrixCSC{T, Ti}) where {T, Ti}
59-
At = sparse(transpose(A))
60-
return GPUSparseMatrixCSR(At.n, At.m, At.colptr, At.rowval, At.nzval)
59+
At_csc = SparseMatrixCSC(transpose(A))
60+
return GPUSparseMatrixCSR(At_csc.n, At_csc.m, At_csc.colptr, At_csc.rowval, At_csc.nzval)
61+
end
62+
63+
function SparseArrays.SparseMatrixCSC(A::GPUSparseMatrixCSR)
64+
At_csc = SparseMatrixCSC(A.n, A.m, Vector(A.rowptr), Vector(A.colval), Vector(A.nzval))
65+
return SparseMatrixCSC(transpose(At_csc))
66+
end
67+
68+
function sametype_transpose(A::GPUSparseMatrixCSR)
69+
A_csc = SparseMatrixCSC(A)
70+
return adapt(
71+
get_backend(A),
72+
GPUSparseMatrixCSR(A_csc.n, A_csc.m, A_csc.colptr, A_csc.rowval, A_csc.nzval)
73+
)
6174
end
6275

6376
@kernel function spmv_csr!(

src/utils/mat_ell.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ function GPUSparseMatrixELL(A::SparseMatrixCSC{T, Ti}) where {T, Ti}
6969
return GPUSparseMatrixELL(m, n, colval, nzval)
7070
end
7171

72+
function SparseArrays.SparseMatrixCSC(A::GPUSparseMatrixELL)
73+
I = Matrix(map(ind -> Tuple(ind)[1], CartesianIndices(A.colval)))
74+
J = Matrix(A.colval)
75+
V = Matrix(A.nzval)
76+
inds = J .> 0
77+
return sparse(I[inds], J[inds], V[inds], A.m, A.n)
78+
end
79+
80+
function sametype_transpose(A::GPUSparseMatrixELL)
81+
At = SparseMatrixCSC(transpose(SparseMatrixCSC(A)))
82+
return adapt(get_backend(A), GPUSparseMatrixELL(At))
83+
end
84+
7285
@kernel function spmv_ell!(
7386
c::DenseVector{T},
7487
A_colval::AbstractMatrix{Ti},

test/problems/modify.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,21 @@ end
1919

2020
@testset "Change backend" begin
2121
milp_flexible = CoolPDLP.set_matrix_type(GPUSparseMatrixCSR, milp)
22-
@test milp_flexible isa MILP{Float64, Vector{Float64}, GPUSparseMatrixCSR{Float64, Int, Vector{Float64}, Vector{Int}}, Vector{Bool}}
22+
@test milp_flexible isa MILP{
23+
Float64,
24+
Vector{Float64},
25+
GPUSparseMatrixCSR{Float64, Int, Vector{Float64}, Vector{Int}},
26+
GPUSparseMatrixCSR{Float64, Int, Vector{Float64}, Vector{Int}},
27+
Vector{Bool},
28+
}
2329
milp_gpu = adapt(JLBackend(), milp_flexible)
24-
@test milp_gpu isa MILP{Float64, JLVector{Float64}, GPUSparseMatrixCSR{Float64, Int, JLVector{Float64}, JLVector{Int}}, JLVector{Bool}}
30+
@test milp_gpu isa MILP{
31+
Float64,
32+
JLVector{Float64},
33+
GPUSparseMatrixCSR{Float64, Int, JLVector{Float64}, JLVector{Int}},
34+
GPUSparseMatrixCSR{Float64, Int, JLVector{Float64}, JLVector{Int}},
35+
JLVector{Bool},
36+
}
2537

2638
sol_gpu = adapt(JLBackend(), sol)
2739
@test sol_gpu isa PrimalDualSolution{Float64, JLVector{Float64}}

test/utils/linalg.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using CoolPDLP
22
using LinearAlgebra
33
using SparseArrays
44
using Test
5+
using Random: Xoshiro
56

67
@testset "Simple projections" begin
78
for x in randn(100)
@@ -67,3 +68,10 @@ end
6768
end
6869
end
6970
end
71+
72+
@testset "Same-type transpose" begin
73+
for A in Any[rand(10, 10), sprand(10, 10, 0.6)]
74+
@test CoolPDLP.sametype_transpose(A) == transpose(A)
75+
@test CoolPDLP.sametype_transpose(A) isa typeof(A)
76+
end
77+
end

test/utils/matrices.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ c_candidates = [rand(size(A, 1)) for A in A_candidates];
1919

2020
function test_sparse_matrix(::Type{M}; A, b, c, α, β) where {M}
2121
A_jl = adapt(JLBackend(), M(A))
22+
At_jl = adapt(JLBackend(), M(sparse(transpose(A))))
2223
b_jl, c_jl = jl(b), jl(c)
2324
@test @allowscalar Matrix(A_jl) == A
25+
@test @allowscalar SparseMatrixCSC(A_jl) == A
2426
@test nnz(A_jl) == nnz(A)
2527
@test get_backend(A_jl) isa JLBackend
2628
@test mul!(copy(c_jl), A_jl, b_jl, α, β) α * A * b + β * c
29+
@test @allowscalar Matrix(CoolPDLP.sametype_transpose(A_jl)) == transpose(A)
30+
@test typeof(CoolPDLP.sametype_transpose(A_jl)) == typeof(At_jl)
2731
return nothing
2832
end
2933

3034
@testset for M in (GPUSparseMatrixCOO, GPUSparseMatrixCSR, GPUSparseMatrixELL)
31-
for (A, b, c) in zip(A_candidates, b_candidates, c_candidates)
35+
for (A, b, c) in collect(zip(A_candidates, b_candidates, c_candidates))
3236
test_sparse_matrix(M; A, b, c, α, β)
3337
end
3438
end

0 commit comments

Comments
 (0)