Browse Source

Bulk commit:

- Move LibHYPRE submodule to it's own file
 - Move Internals stub module to it's own file
 - Add conversions from PartitionedArrays.(PSparseMatrix|PVector)
 - More tests...
fe/wip
Fredrik Ekre 3 years ago
parent
commit
6d0f338540
  1. 4
      Project.toml
  2. 373
      src/HYPRE.jl
  3. 14
      src/Internals.jl
  4. 74
      src/LibHYPRE.jl
  5. 156
      test/runtests.jl

4
Project.toml

@ -7,11 +7,13 @@ CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
HYPRE_jll = "0a602bbd-b08b-5d75-8d32-0de6eef44785" HYPRE_jll = "0a602bbd-b08b-5d75-8d32-0de6eef44785"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1"
[extras] [extras]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets] [targets]
test = ["Test"] test = ["LinearAlgebra", "Test"]

373
src/HYPRE.jl

@ -1,78 +1,97 @@
# SPDX-License-Identifier: MIT
module HYPRE module HYPRE
module LibHYPRE using MPI: MPI
include("../lib/LibHYPRE.jl") using PartitionedArrays: IndexRange, MPIData, PSparseMatrix, PVector, PartitionedArrays,
SequentialData, map_parts
using SparseArrays: SparseArrays, SparseMatrixCSC, nnz, nonzeros, nzrange, rowvals
using SparseMatricesCSR: SparseMatrixCSR, colvals, getrowptr
# Add manual methods for some ::Function signatures where the library wants function export HYPREMatrix, HYPREVector
# pointers. Instead of creating function pointers to the Julia wrappers we can just look
# up the pointer in the library and pass that.
# TODO: Maybe this can be done automatically as post-process pass in Clang.jl
import Libdl: dlsym
function HYPRE_PCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver) # Clang.jl auto-generated bindings and some manual methods
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond)) include("LibHYPRE.jl")
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup)) using .LibHYPRE
return HYPRE_PCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver) using .LibHYPRE: @check
end
function HYPRE_GMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver) # Internal namespace to hide utility functions
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond)) include("Internals.jl")
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_GMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_FlexGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_FlexGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_COGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_COGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_BiCGSTABSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_BiCGSTABSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_CGNRSetPrecond(solver, precond::Function, precondT::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precondT_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precondT))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_CGNRSetPrecond(solver, precond_ptr, precondT_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LOBPCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LOBPCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
###############################
# HYPREMatrix and HYPREVector #
###############################
# Export everything with HYPRE_ prefix mutable struct HYPREMatrix # <: AbstractMatrix{HYPRE_Complex}
for name in names(@__MODULE__; all=true) IJMatrix::HYPRE_IJMatrix
if startswith(string(name), "HYPRE_") ParCSRMatrix::HYPRE_ParCSRMatrix
@eval export $name HYPREMatrix() = new(C_NULL, C_NULL)
end end
mutable struct HYPREVector # <: AbstractVector{HYPRE_Complex}
IJVector::HYPRE_IJVector
ParVector::HYPRE_ParVector
HYPREVector() = new(C_NULL, C_NULL)
end end
# Create a new IJMatrix, set the object type, prepare for setting values
function Internals.init_matrix(comm::MPI.Comm, ilower, iupper)
# Create the IJ matrix
A = HYPREMatrix()
IJMatrixRef = Ref{HYPRE_IJMatrix}(C_NULL)
@check HYPRE_IJMatrixCreate(comm, ilower, iupper, ilower, iupper, IJMatrixRef)
A.IJMatrix = IJMatrixRef[]
# Attach a finalizer
finalizer(x -> HYPRE_IJMatrixDestroy(x.IJMatrix), A)
# Set storage type
@check HYPRE_IJMatrixSetObjectType(A.IJMatrix, HYPRE_PARCSR)
# Initialize to make ready for setting values
@check HYPRE_IJMatrixInitialize(A.IJMatrix)
return A
end end
using SparseArrays: SparseArrays, SparseMatrixCSC, nnz, rowvals, nonzeros, nzrange # Finalize the matrix and fetch the assembled matrix
using SparseMatricesCSR: SparseMatrixCSR, colvals # This should be called after setting all the values
using MPI: MPI function Internals.assemble_matrix(A::HYPREMatrix)
using .LibHYPRE # Finalize after setting all values
@check HYPRE_IJMatrixAssemble(A.IJMatrix)
# Fetch the assembled CSR matrix
ParCSRMatrixRef = Ref{Ptr{Cvoid}}(C_NULL)
@check HYPRE_IJMatrixGetObject(A.IJMatrix, ParCSRMatrixRef)
A.ParCSRMatrix = convert(Ptr{HYPRE_ParCSRMatrix}, ParCSRMatrixRef[])
return A
end
export HYPREMatrix, HYPREVector function Internals.init_vector(comm::MPI.Comm, ilower, iupper)
# Create the IJ vector
b = HYPREVector()
b_ref = Ref{HYPRE_IJVector}(C_NULL)
@check HYPRE_IJVectorCreate(comm, ilower, iupper, b_ref)
b.IJVector = b_ref[]
# Attach a finalizer
finalizer(x -> HYPRE_IJVectorDestroy(x.IJVector), b) # Set storage type
# Set storage type
@check HYPRE_IJVectorSetObjectType(b.IJVector, HYPRE_PARCSR)
# Initialize to make ready for setting values
@check HYPRE_IJVectorInitialize(b.IJVector)
return b
end
module Internals function Internals.assemble_vector(b::HYPREVector)
function check_n_rows end # Finalize after setting all values
function to_hypre_data end @check HYPRE_IJVectorAssemble(b.IJVector)
# Fetch the assembled vector
par_b_ref = Ref{Ptr{Cvoid}}(C_NULL)
@check HYPRE_IJVectorGetObject(b.IJVector, par_b_ref)
b.ParVector = convert(Ptr{HYPRE_ParVector}, par_b_ref[])
return b
end end
######################################
# SparseMatrixCS(C|R) -> HYPREMatrix #
######################################
function Internals.check_n_rows(A, ilower, iupper) function Internals.check_n_rows(A, ilower, iupper)
if size(A, 1) != (iupper - ilower + 1) if size(A, 1) != (iupper - ilower + 1)
throw(ArgumentError("number of rows in matrix does not match global start/end rows ilower and iupper")) throw(ArgumentError("number of rows in matrix does not match global start/end rows ilower and iupper"))
@ -85,7 +104,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSC, ilower, iupper)
A_rows = rowvals(A) A_rows = rowvals(A)
A_vals = nonzeros(A) A_vals = nonzeros(A)
# Initialize data as HYPRE expects # Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
@ -123,7 +142,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper)
A_cols = colvals(A) A_cols = colvals(A)
A_vals = nonzeros(A) A_vals = nonzeros(A)
# Initialize data as HYPRE expects # Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = Vector{HYPRE_Int}(undef, nrows) # Number of colums for each row ncols = Vector{HYPRE_Int}(undef, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
@ -147,42 +166,17 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper)
return nrows, ncols, rows, cols, values return nrows, ncols, rows, cols, values
end end
mutable struct HYPREMatrix # <: AbstractMatrix{HYPRE_Complex}
IJMatrix::HYPRE_IJMatrix
ParCSRMatrix::HYPRE_ParCSRMatrix
HYPREMatrix() = new(C_NULL, C_NULL)
end
function HYPREMatrix(B::Union{SparseMatrixCSC,SparseMatrixCSR}, ilower, iupper, comm::MPI.Comm=MPI.COMM_WORLD) function HYPREMatrix(B::Union{SparseMatrixCSC,SparseMatrixCSR}, ilower, iupper, comm::MPI.Comm=MPI.COMM_WORLD)
# Compute indices/values in the format SetValues expect A = Internals.init_matrix(comm, ilower, iupper)
nrows, ncols, rows, cols, values = Internals.to_hypre_data(B, ilower, iupper) nrows, ncols, rows, cols, values = Internals.to_hypre_data(B, ilower, iupper)
# Create the IJ matrix @check HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
A = HYPREMatrix() Internals.assemble_matrix(A)
IJMatrixRef = Ref{HYPRE_IJMatrix}(C_NULL)
HYPRE_IJMatrixCreate(comm, ilower, iupper, ilower, iupper, IJMatrixRef)
A.IJMatrix = IJMatrixRef[]
# Attach a finalizer
finalizer(x -> HYPRE_IJMatrixDestroy(x.IJMatrix), A)
# Set storage type
HYPRE_IJMatrixSetObjectType(A.IJMatrix, HYPRE_PARCSR)
# Initialize to make ready for setting values
HYPRE_IJMatrixInitialize(A.IJMatrix)
# Set all the values
HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
# Finalize
HYPRE_IJMatrixAssemble(A.IJMatrix)
# Fetch the assembled CSR matrix
ParCSRMatrixRef = Ref{Ptr{Cvoid}}(C_NULL)
HYPRE_IJMatrixGetObject(A.IJMatrix, ParCSRMatrixRef)
A.ParCSRMatrix = convert(Ptr{HYPRE_ParCSRMatrix}, ParCSRMatrixRef[])
return A return A
end end
mutable struct HYPREVector # <: AbstractVector{HYPRE_Complex} #########################
IJVector::HYPRE_IJVector # Vector -> HYPREVector #
ParVector::HYPRE_ParVector #########################
HYPREVector() = new(C_NULL, C_NULL)
end
function Internals.to_hypre_data(x::Vector, ilower, iupper) function Internals.to_hypre_data(x::Vector, ilower, iupper)
Internals.check_n_rows(x, ilower, iupper) Internals.check_n_rows(x, ilower, iupper)
@ -190,25 +184,188 @@ function Internals.to_hypre_data(x::Vector, ilower, iupper)
values = convert(Vector{HYPRE_Complex}, x) values = convert(Vector{HYPRE_Complex}, x)
return HYPRE_Int(length(indices)), indices, values return HYPRE_Int(length(indices)), indices, values
end end
# TODO: Internals.to_hypre_data(x::SparseVector, ilower, iupper) (?)
function HYPREVector(x::Vector, ilower, iupper, comm=MPI.COMM_WORLD) function HYPREVector(x::Vector, ilower, iupper, comm=MPI.COMM_WORLD)
b = Internals.init_vector(comm, ilower, iupper)
nvalues, indices, values = Internals.to_hypre_data(x, ilower, iupper) nvalues, indices, values = Internals.to_hypre_data(x, ilower, iupper)
b = HYPREVector() @check HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
b_ref = Ref{HYPRE_IJVector}(C_NULL) Internals.assemble_vector(b)
HYPRE_IJVectorCreate(comm, ilower, iupper, b_ref) return b
b.IJVector = b_ref[] end
finalizer(x -> HYPRE_IJVectorDestroy(x.IJVector), b) # Set storage type
HYPRE_IJVectorSetObjectType(b.IJVector, HYPRE_PARCSR)
# Initialize to make ready for setting values ##################################################
HYPRE_IJVectorInitialize(b.IJVector) # PartitionedArrays.PSparseMatrix -> HYPREMatrix #
# Set the values ##################################################
HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
# TODO: This has some duplicated code with to_hypre_data(::SparseMatrixCSC, ilower, iupper)
function Internals.to_hypre_data(A::SparseMatrixCSC, r::IndexRange, c::IndexRange)
@assert r.oid_to_lid isa UnitRange && r.oid_to_lid.start == 1
ilower = r.lid_to_gid[r.oid_to_lid.start]
iupper = r.lid_to_gid[r.oid_to_lid.stop]
a_rows = rowvals(A)
a_vals = nonzeros(A)
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
# cols = Vector{HYPRE_BigInt}(undef, nnz) # The column indices
# values = Vector{HYPRE_Complex}(undef, nnz) # The values
# First pass to count nnz per row (note that the fact that columns are permuted
# doesn't matter for this pass)
a_rows = rowvals(A)
a_vals = nonzeros(A)
@inbounds for j in 1:size(A, 2)
for i in nzrange(A, j)
row = a_rows[i]
row > r.oid_to_lid.stop && continue # Skip ghost rows
# grow = r.lid_to_gid[lrow]
ncols[row] += 1
end
end
# Initialize remaining buffers now that nnz is known
nnz = sum(ncols)
cols = Vector{HYPRE_BigInt}(undef, nnz)
values = Vector{HYPRE_Complex}(undef, nnz)
# Keep track of the last index used for every row
lastinds = zeros(Int, nrows)
cumsum!((@view lastinds[2:end]), (@view ncols[1:end-1]))
# Second pass to populate the output -- here we need to take care of the permutation
# of columns. TODO: Problem that they are not sorted?
@inbounds for j in 1:size(A, 2)
for i in nzrange(A, j)
row = a_rows[i]
row > r.oid_to_lid.stop && continue # Skip ghost rows
k = lastinds[row] += 1
val = a_vals[i]
cols[k] = c.lid_to_gid[j]
values[k] = val
end
end
return nrows, ncols, rows, cols, values
end
# TODO: Possibly this can be optimized if it is possible to pass overlong vectors to HYPRE.
# At least values should be possible to directly share, but cols needs to translated
# to global ids.
function Internals.to_hypre_data(A::SparseMatrixCSR, r::IndexRange, c::IndexRange)
@assert r.oid_to_lid isa UnitRange && r.oid_to_lid.start == 1
ilower = r.lid_to_gid[r.oid_to_lid.start]
iupper = r.lid_to_gid[r.oid_to_lid.stop]
a_cols = colvals(A)
a_vals = nonzeros(A)
nnz = getrowptr(A)[r.oid_to_lid.stop + 1] - 1
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
cols = Vector{HYPRE_BigInt}(undef, nnz) # The column indices
values = Vector{HYPRE_Complex}(undef, nnz) # The values
# Loop over the (owned) rows and collect all values
k = 0
@inbounds for i in r.oid_to_lid
nzr = nzrange(A, i)
ncols[i] = length(nzr)
for j in nzr
k += 1
col = a_cols[j]
val = a_vals[j]
cols[k] = c.lid_to_gid[col]
values[k] = val
end
end
@assert nnz == k
return nrows, ncols, rows, cols, values
end
function Internals.get_comm(A::Union{PSparseMatrix{<:Any,<:M}, PVector{<:Any,<:M}}) where M <: MPIData
return A.rows.partition.comm
end
Internals.get_comm(_::Union{PSparseMatrix,PVector}) = MPI.COMM_WORLD
function Internals.get_proc_rows(A::Union{PSparseMatrix{<:Any,<:M}, PVector{<:Any,<:M}}) where M <: MPIData
r = A.rows.partition.part
ilower::HYPRE_BigInt = r.lid_to_gid[r.oid_to_lid[1]]
iupper::HYPRE_BigInt = r.lid_to_gid[r.oid_to_lid[end]]
return ilower, iupper
end
function Internals.get_proc_rows(A::Union{PSparseMatrix{<:Any,<:S}, PVector{<:Any,<:S}}) where S <: SequentialData
ilower::HYPRE_BigInt = typemax(HYPRE_BigInt)
iupper::HYPRE_BigInt = typemin(HYPRE_BigInt)
for r in A.rows.partition.parts
ilower = min(r.lid_to_gid[r.oid_to_lid[1]], ilower)
iupper = max(r.lid_to_gid[r.oid_to_lid[end]], iupper)
end
return ilower, iupper
end
function HYPREMatrix(B::PSparseMatrix)
# Use the same communicator as the matrix
comm = Internals.get_comm(B)
# Fetch rows owned by this process
ilower, iupper = Internals.get_proc_rows(B)
# Create the IJ matrix
A = Internals.init_matrix(comm, ilower, iupper)
# Set all the values
map_parts(B.values, B.rows.partition, B.cols.partition) do Bv, Br, Bc
nrows, ncols, rows, cols, values = Internals.to_hypre_data(Bv, Br, Bc)
@check HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
return nothing
end
# Finalize # Finalize
HYPRE_IJVectorAssemble(b.IJVector) Internals.assemble_matrix(A)
# Fetch the assembled object return A
par_b_ref = Ref{Ptr{Cvoid}}(C_NULL) end
HYPRE_IJVectorGetObject(b.IJVector, par_b_ref)
b.ParVector = convert(Ptr{HYPRE_ParVector}, par_b_ref[]) ############################################
# PartitionedArrays.PVector -> HYPREVector #
############################################
#
function HYPREVector(v::PVector)
# Use the same communicator as the matrix
comm = Internals.get_comm(v)
# Fetch rows owned by this process
ilower, iupper = Internals.get_proc_rows(v)
# Create the IJ vector
b = Internals.init_vector(comm, ilower, iupper)
# Set all the values
map_parts(v.values, v.owned_values, v.rows.partition) do vv, vo, vr
ilower_part = vr.lid_to_gid[vr.oid_to_lid.start]
iupper_part = vr.lid_to_gid[vr.oid_to_lid.stop]
# Option 1: Set all values
nvalues = HYPRE_Int(iupper_part - ilower_part + 1)
indices = collect(HYPRE_BigInt, ilower_part:iupper_part)
# TODO: Could probably just pass the full vector even if it is too long
# values = convert(Vector{HYPRE_Complex}, vv)
values = collect(HYPRE_Complex, vo)
# # Option 2: Set only non-zeros
# indices = HYPRE_BigInt[]
# values = HYPRE_Complex[]
# for (i, vi) in zip(ilower_part:iupper_part, vo)
# if !iszero(vi)
# push!(indices, i)
# push!(values, vi)
# end
# end
# nvalues = length(indices)
@check HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
return nothing
end
# Finalize
Internals.assemble_vector(b)
return b return b
end end

14
src/Internals.jl

@ -0,0 +1,14 @@
# SPDX-License-Identifier: MIT
module Internals
function check_n_rows end
function to_hypre_data end
function get_comm end
function get_proc_rows end
function init_matrix end
function init_vector end
function assemble_matrix end
function assemble_vector end
end # module Internals

74
src/LibHYPRE.jl

@ -0,0 +1,74 @@
module LibHYPRE
using Libdl: dlsym
# Clang.jl auto-generated bindings
include("../lib/LibHYPRE.jl")
# Add manual methods for some ::Function signatures where the library wants function
# pointers. Instead of creating function pointers to the Julia wrappers we can just look
# up the pointer in the library and pass that.
# TODO: Maybe this can be done automatically as post-process pass in Clang.jl
function HYPRE_PCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_PCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_GMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_GMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_FlexGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_FlexGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_COGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_COGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_BiCGSTABSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_BiCGSTABSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_CGNRSetPrecond(solver, precond::Function, precondT::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precondT_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precondT))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_CGNRSetPrecond(solver, precond_ptr, precondT_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LOBPCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LOBPCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
# Macro for checking LibHYPRE return codes
macro check(arg)
Meta.isexpr(arg, :call) || throw(ArgumentError("wrong usage of @check"))
msg = "LibHYPRE.$(arg.args[1]) returned non-zero return code: "
return quote
r = $(esc(arg))
if r != 0
error(string($msg, r))
end
r
end
end
# Export everything with HYPRE_ prefix
for name in names(@__MODULE__; all=true)
if startswith(string(name), "HYPRE_")
@eval export $name
end
end
end

156
test/runtests.jl

@ -1,10 +1,15 @@
# SPDX-License-Identifier: MIT
using HYPRE using HYPRE
using HYPRE.Internals
using HYPRE.LibHYPRE using HYPRE.LibHYPRE
using Test using LinearAlgebra
using MPI
using PartitionedArrays
using SparseArrays using SparseArrays
using SparseMatricesCSR using SparseMatricesCSR
using Test
using MPI
MPI.Init() MPI.Init()
@testset "HYPREMatrix" begin @testset "HYPREMatrix" begin
@ -12,6 +17,15 @@ MPI.Init()
@test H.IJMatrix == HYPRE_IJMatrix(C_NULL) @test H.IJMatrix == HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix == HYPRE_ParCSRMatrix(C_NULL) @test H.ParCSRMatrix == HYPRE_ParCSRMatrix(C_NULL)
H = Internals.init_matrix(MPI.COMM_WORLD, 1, 5)
@test H.IJMatrix != HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix == HYPRE_ParCSRMatrix(C_NULL)
Internals.assemble_matrix(H)
@test H.IJMatrix != HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix != HYPRE_ParCSRMatrix(C_NULL)
end
@testset "HYPREMatrix(::SparseMatrixCS(C|R))" begin
ilower, iupper = 4, 6 ilower, iupper = 4, 6
CSC = convert(SparseMatrixCSC{HYPRE_Complex, HYPRE_Int}, sparse([ CSC = convert(SparseMatrixCSC{HYPRE_Complex, HYPRE_Int}, sparse([
1 2 0 0 3 1 2 0 0 3
@ -20,8 +34,8 @@ MPI.Init()
])) ]))
CSR = sparsecsr(findnz(CSC)..., size(CSC)...) CSR = sparsecsr(findnz(CSC)..., size(CSC)...)
@test CSC == CSR @test CSC == CSR
csc = HYPRE.Internals.to_hypre_data(CSC, ilower, iupper) csc = Internals.to_hypre_data(CSC, ilower, iupper)
csr = HYPRE.Internals.to_hypre_data(CSR, ilower, iupper) csr = Internals.to_hypre_data(CSR, ilower, iupper)
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == 3 # nrows @test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == 3 # nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == [3, 2, 3] # ncols @test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == [3, 2, 3] # ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == [4, 5, 6] # rows @test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == [4, 5, 6] # rows
@ -34,10 +48,9 @@ MPI.Init()
@test csr[5] == CSR.nzval @test csr[5] == CSR.nzval
@test_broken csr[5]::Vector{HYPRE_Complex} === CSR.nzval @test_broken csr[5]::Vector{HYPRE_Complex} === CSR.nzval
@test_throws ArgumentError HYPRE.Internals.to_hypre_data(CSC, ilower, iupper-1) @test_throws ArgumentError Internals.to_hypre_data(CSC, ilower, iupper-1)
@test_throws ArgumentError HYPRE.Internals.to_hypre_data(CSR, ilower, iupper+1) @test_throws ArgumentError Internals.to_hypre_data(CSR, ilower, iupper+1)
# Converting SparseMatrixCS(C|R) to HYPREMatrix
ilower, iupper = 6, 10 ilower, iupper = 6, 10
CSC = sprand(5, 10, 0.3) CSC = sprand(5, 10, 0.3)
CSR = sparsecsr(findnz(CSC)..., size(CSC)...) CSR = sparsecsr(findnz(CSC)..., size(CSC)...)
@ -50,29 +63,140 @@ MPI.Init()
@test H.ParCSRMatrix != HYPRE_ParCSRMatrix(C_NULL) @test H.ParCSRMatrix != HYPRE_ParCSRMatrix(C_NULL)
end end
function tomain(x)
g = gather(x)
be = get_backend(g.values)
if be isa SequentialBackend
return g.values.parts[1]
else # if be isa MPIBackend
return g.values.part
end
end
@testset "HYPREMatrix(::PSparseMatrix)" begin
# Sequential backend
function diag_data(backend, parts)
is_seq = backend isa SequentialBackend
rows = PRange(parts, 10)
cols = PRange(parts, 10)
I, J, V = map_parts(parts) do p
i = Int[]
j = Int[]
v = Float64[]
if (is_seq && p == 1) || !is_seq
append!(i, [1, 2, 3, 4, 5, 6])
append!(j, [1, 2, 3, 4, 5, 6])
append!(v, [1, 2, 3, 4, 5, 6])
end
if (is_seq && p == 2) || !is_seq
append!(i, [4, 5, 6, 7, 8, 9, 10])
append!(j, [4, 5, 6, 7, 8, 9, 10])
append!(v, [4, 5, 6, 7, 8, 9, 10])
end
return i, j, v
end
add_gids!(rows, I)
assemble!(I, J, V, rows)
add_gids!(cols, J)
return I, J, V, rows, cols
end
backend = SequentialBackend()
parts = get_part_ids(backend, 2)
CSC = PSparseMatrix(diag_data(backend, parts)...; ids=:global)
CSR = PSparseMatrix(sparsecsr, diag_data(backend, parts)...; ids=:global)
@test tomain(copy(CSC)) == tomain(copy(CSR)) ==
Diagonal([1, 2, 3, 8, 10, 12, 7, 8, 9, 10])
map_parts(CSC.values, CSC.rows.partition, CSC.cols.partition,
CSR.values, CSR.rows.partition, CSR.cols.partition, parts) do args...
cscvalues, cscrows, csccols, csrvalues, csrrows, csrcols, p = args
csc = Internals.to_hypre_data(cscvalues, cscrows, csccols)
csr = Internals.to_hypre_data(csrvalues, csrrows, csrcols)
if p == 1
nrows = 5
ncols = [1, 1, 1, 1, 1]
rows = [1, 2, 3, 4, 5]
cols = [1, 2, 3, 4, 5]
values = [1, 2, 3, 8, 10]
else # if p == 1
nrows = 5
ncols = [1, 1, 1, 1, 1]
rows = [6, 7, 8, 9, 10]
cols = [6, 7, 8, 9, 10]
values = [12, 7, 8, 9, 10]
end
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == rows
@test csc[4]::Vector{HYPRE_BigInt} == csr[4]::Vector{HYPRE_BigInt} == cols
@test csc[5]::Vector{HYPRE_Complex} == csr[5]::Vector{HYPRE_Complex} == values
end
# MPI backend
backend = MPIBackend()
parts = MPIData(1, MPI.COMM_WORLD, (1,)) # get_part_ids duplicates the comm
CSC = PSparseMatrix(diag_data(backend, parts)...; ids=:global)
CSR = PSparseMatrix(sparsecsr, diag_data(backend, parts)...; ids=:global)
@test tomain(copy(CSC)) == tomain(copy(CSR)) ==
Diagonal([1, 2, 3, 8, 10, 12, 7, 8, 9, 10])
map_parts(CSC.values, CSC.rows.partition, CSC.cols.partition,
CSR.values, CSR.rows.partition, CSR.cols.partition, parts) do args...
cscvalues, cscrows, csccols, csrvalues, csrrows, csrcols, p = args
csc = Internals.to_hypre_data(cscvalues, cscrows, csccols)
csr = Internals.to_hypre_data(csrvalues, csrrows, csrcols)
nrows = 10
ncols = fill(1, 10)
rows = collect(1:10)
cols = collect(1:10)
values = [1, 2, 3, 8, 10, 12, 7, 8, 9, 10]
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == rows
@test csc[4]::Vector{HYPRE_BigInt} == csr[4]::Vector{HYPRE_BigInt} == cols
@test csc[5]::Vector{HYPRE_Complex} == csr[5]::Vector{HYPRE_Complex} == values
end
end
@testset "HYPREVector" begin @testset "HYPREVector" begin
h = HYPREVector() h = HYPREVector()
@test h.IJVector == HYPRE_IJVector(C_NULL) @test h.IJVector == HYPRE_IJVector(C_NULL)
@test h.ParVector == HYPRE_ParVector(C_NULL) @test h.ParVector == HYPRE_ParVector(C_NULL)
h = Internals.init_vector(MPI.COMM_WORLD, 1, 5)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector == HYPRE_ParVector(C_NULL)
Internals.assemble_vector(h)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
end
@testset "HYPREVector(::Vector)" begin
ilower, iupper = 1, 10
b = rand(HYPRE_Complex, 10)
h = HYPREVector(b, ilower, iupper)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
@test_throws ArgumentError HYPREVector([1, 2], ilower, iupper)
ilower, iupper = 1, 10 ilower, iupper = 1, 10
b = rand(HYPRE_Complex, 10) b = rand(HYPRE_Complex, 10)
nvalues, indices, values = HYPRE.Internals.to_hypre_data(b, ilower, iupper) nvalues, indices, values = Internals.to_hypre_data(b, ilower, iupper)
@test nvalues::HYPRE_Int == 10 @test nvalues::HYPRE_Int == 10
@test indices::Vector{HYPRE_Int} == collect(1:10) @test indices::Vector{HYPRE_Int} == collect(1:10)
@test values::Vector{HYPRE_Complex} === b # === for correct eltype @test values::Vector{HYPRE_Complex} === b # === for correct eltype
b = rand(1:10, 10) b = rand(1:10, 10)
nvalues, indices, values = HYPRE.Internals.to_hypre_data(b, ilower, iupper) nvalues, indices, values = Internals.to_hypre_data(b, ilower, iupper)
@test nvalues::HYPRE_Int == 10 @test nvalues::HYPRE_Int == 10
@test indices::Vector{HYPRE_Int} == collect(1:10) @test indices::Vector{HYPRE_Int} == collect(1:10)
@test values::Vector{HYPRE_Complex} == b # == for other eltype @test values::Vector{HYPRE_Complex} == b # == for other eltype
@test_throws ArgumentError HYPRE.Internals.to_hypre_data([1, 2], ilower, iupper) @test_throws ArgumentError Internals.to_hypre_data([1, 2], ilower, iupper)
end
# Converting Vector to HYPREVector @testset "HYPREMatrix(::PVector)" begin
b = rand(HYPRE_Complex, 10)
h = HYPREVector(b, ilower, iupper)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
@test_throws ArgumentError HYPREVector([1, 2], ilower, iupper)
end end

Loading…
Cancel
Save