hipSOLVER examples#
hipSOLVER is a thin
portability layer over rocSOLVER on AMD GPUs and cuSOLVER on NVIDIA GPUs. Its API
mirrors cuSOLVER, so the same source builds against either backend. hipFORT
exposes it through the hipfort_hipsolver module.
Every program on this page is a complete, self-contained example that is built
and run as part of the hipFORT test suite. The Fortran 2008 tests live in
test/f2008/hipsolver and the equivalent Fortran 2003 sources, which use
type(c_ptr) device pointers and explicit byte counts instead of Fortran
array pointers, live in test/f2003/hipsolver.
hipSOLVER is a portability layer; for direct access to rocSOLVER on AMD GPUs,
the equivalent programs are written against the hipfort_rocsolver module.
Each example is provided in the four LAPACK precisions where the routine has
them: s (real single), d (real double), c (complex single), and
z (complex double). This page shows the double-precision program of each
group; the other precisions differ only in the host data type and the
hipsolver prefix letter.
Solver workflow#
Unlike LAPACK, hipSOLVER routines need an explicit GPU workspace. A typical call follows the same sequence as cuSOLVER:
Create a handle with
hipsolverCreate.Query the workspace size with the routine’s
_bufferSizecompanion (for examplehipsolverDgetrf_bufferSize), then allocate that many bytes on the device.Run the routine, passing the workspace and its size.
Read back the device
infooutput to check for success.Release the handle with
hipsolverDestroy.
Keep the following conventions in mind:
Column-major storage. hipSOLVER matrices are column-major, which matches Fortran’s native array layout, so a Fortran 2-D array maps directly onto a matrix with leading dimension
lda = size(A, 1).An explicit workspace. Most routines take a device work buffer and its length. Size it with the matching
_bufferSizequery rather than guessing; the buffer stays a baretype(c_ptr)in both dialects.The
infooutput lives in device memory. hipSOLVER writes the factorization status to a device pointer, so it must be backed by a device allocation, not a host scalar. For the batched routinesinfois an array ofbatch_countintegers on the device.Enumerators select variants.
HIPSOLVER_FILL_MODE_UPPER/HIPSOLVER_FILL_MODE_LOWERchoose the stored triangle, andHIPSOLVER_EIG_MODE_NOVECTOR/HIPSOLVER_EIG_MODE_VECTORchoose whether eigenvectors are computed. The SVD job arguments arecharacter(c_char)job codes ('N','A','S','V') passed by value.Every call returns a status code. The examples wrap hipSOLVER calls in
hipsolverCheckand HIP calls inhipCheckfrom thehipfort_checkmodule, both of which abort on failure.
Building an example#
The examples only need the hipsolver and hip hipFORT components:
find_package(hipfort REQUIRED COMPONENTS hip hipsolver)
add_executable(my_solver hipsolver_dgetrf.f08)
target_link_libraries(my_solver PRIVATE hipfort::hipsolver hipfort::hip)
See Using hipFORT in your application for the full set of build options.
LU factorization and solve#
getrf computes the LU factorization A = P*L*U with partial pivoting,
writing the factors in place over A and the pivot indices into ipiv. The
example queries the workspace with hipsolverDgetrf_bufferSize, factorizes,
and reconstructs L*U to confirm the result.
!!!!!!!!!!!!!!
! hipsolver dgetrf example (double-precision LU factorization, Fortran 2008 interfaces)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! f2008 style: device buffers are native Fortran array pointers allocated with
! hipMalloc(source=/mold=); A and the pivots are passed as typed arrays. The
! workspace stays a bare type(c_ptr); devInfo is a device-backed scalar pointer.
!!!!!!!!!!!!!!
!
program hipsolver_dgetrf
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: i, j
integer(c_int), parameter :: M = 3, N = 3, lda = 3
! Input matrix (column-major) and the expected packed LU (same reference as
! the rocSOLVER dgetrf test; hipSOLVER wraps rocSOLVER)
real(c_double) :: hA(3,3) = reshape((/12, 6, -4, -51, 167, 24, 4, -68, -41/), (/3, 3/))
real(c_double) :: hResult(3,3) = reshape((/&
12.0000000000000000d0, 0.500000000000000000d0, -0.333333333333333315d0,&
-51.0000000000000000d0, 192.500000000000000d0, 0.363636363636363688d-01,&
4.00000000000000000d0, -70.0000000000000000d0, -37.1212121212121176d0/), shape(hResult), order=(/2,1/))
integer(c_int) :: hIpiv_ref(3) = (/1, 2, 3/)
integer(c_int) :: hIpiv(3), hInfo
real(c_double), pointer :: dA(:,:) ! GPU buffer for A (holds packed LU on output)
integer(c_int), pointer :: dIpiv(:) ! GPU buffer for pivots
integer(c_int), pointer :: dInfo ! GPU scalar for devInfo (written on device)
type(c_ptr) :: dWork ! opaque workspace
type(c_ptr) :: handle = c_null_ptr
integer(c_int) :: lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 10 * epsilon(error_max)
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dgetrf' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
! Allocate device memory as native arrays and copy A to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dIpiv, mold=hIpiv))
call hipCheck(hipMalloc(dInfo))
! Query and allocate the workspace
call hipsolverCheck(hipsolverDgetrf_bufferSize(handle, M, N, dA, lda, lwork))
call hipCheck(hipMalloc(dWork, max(int(lwork,c_size_t) * 8, 1_c_size_t)))
! Compute the LU factorization (A/pivots as native arrays, devInfo by reference)
call hipsolverCheck(hipsolverDgetrf(handle, M, N, dA, lda, dWork, lwork, dIpiv, dInfo))
! Copy results back to host
call hipCheck(hipMemcpy(hA, dA, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(hIpiv, dIpiv, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(hInfo, dInfo, hipMemcpyDeviceToHost))
! Check info
if(hInfo /= 0) then
write(*,*) "FAILED! info = ", hInfo, " (expected 0)"
call exit
end if
! Check factor values
do j = 1,size(hA,2)
do i = 1,size(hA,1)
error = abs(hA(i,j) - hResult(i,j)) / max(abs(hResult(i,j)), 1.0_c_double)
if(error .gt. error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " hA(", i, ",", j, ") = ", hA(i,j)
call exit
end if
end do
end do
! Check pivots
do i = 1,3
if(hIpiv(i) .ne. hIpiv_ref(i)) then
write(*,*) "FAILED! Pivot mismatch at ", i, " got ", hIpiv(i), " expected ", hIpiv_ref(i)
call exit
end if
end do
! Clean up
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dIpiv))
call hipCheck(hipFree(dInfo))
call hipCheck(hipFree(dWork))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program hipsolver_dgetrf
getrs uses the factors and pivots from getrf to solve A*X = B. The
example picks a known solution x, forms b = A*x, factorizes, solves, and
checks that the recovered X matches x.
!!!!!!!!!!!!!!
! hipsolver dgetrs example (solve A*X=B after LU, Fortran 2008 interfaces)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Factorizes A with getrf, then solves A*X=B with getrs and checks X.
! Native-array f2008 form; workspaces via *_bufferSize; devInfo device-backed.
!!!!!!!!!!!!!!
!
program hipsolver_dgetrs
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: i
integer(c_int), parameter :: N = 3, nrhs = 1, lda = 3, ldb = 3
real(c_double) :: hA(N,N) = reshape((/2, 1, 0, 1, 2, 1, 0, 1, 2/), (/N,N/))
real(c_double) :: hB(N,nrhs) = reshape((/3, 4, 3/), (/N,nrhs/))
real(c_double) :: hX_ref(N) = (/1, 1, 1/)
real(c_double), pointer :: dA(:,:), dB(:,:)
integer(c_int), pointer :: dIpiv(:)
integer(c_int), pointer :: dInfo
integer(c_int) :: hIpiv(N)
type(c_ptr) :: dWork1, dWork2, handle = c_null_ptr
integer(c_int) :: lwork1, lwork2
real(c_double) :: error
real(c_double), parameter :: error_max = 100 * epsilon(error_max)
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dgetrs' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dB, source=hB))
call hipCheck(hipMalloc(dIpiv, mold=hIpiv))
call hipCheck(hipMalloc(dInfo))
! LU factorization
call hipsolverCheck(hipsolverDgetrf_bufferSize(handle, N, N, dA, lda, lwork1))
call hipCheck(hipMalloc(dWork1, max(int(lwork1,c_size_t) * 8, 1_c_size_t)))
call hipsolverCheck(hipsolverDgetrf(handle, N, N, dA, lda, dWork1, lwork1, dIpiv, dInfo))
! Solve
call hipsolverCheck(hipsolverDgetrs_bufferSize(handle, HIPSOLVER_OP_N, N, nrhs, dA, lda, dIpiv, dB, ldb, lwork2))
call hipCheck(hipMalloc(dWork2, max(int(lwork2,c_size_t) * 8, 1_c_size_t)))
call hipsolverCheck(hipsolverDgetrs(handle, HIPSOLVER_OP_N, N, nrhs, dA, lda, dIpiv, dB, ldb, dWork2, lwork2, dInfo))
call hipCheck(hipMemcpy(hB, dB, hipMemcpyDeviceToHost))
do i = 1, N
error = abs(hB(i,1) - hX_ref(i)) / max(abs(hX_ref(i)), 1.0d0)
if (error > error_max) then
write(*,*) "FAILED! X(", i, ") = ", hB(i,1), " expected ", hX_ref(i)
call exit(1)
end if
end do
call hipCheck(hipFree(dA)); call hipCheck(hipFree(dB)); call hipCheck(hipFree(dIpiv))
call hipCheck(hipFree(dInfo)); call hipCheck(hipFree(dWork1)); call hipCheck(hipFree(dWork2))
call hipsolverCheck(hipsolverDestroy(handle))
write(*,*) "PASSED!"
end program hipsolver_dgetrs
Cholesky factorization and solve#
potrf computes the Cholesky factorization of a symmetric (or Hermitian)
positive-definite matrix, writing the factor into the triangle chosen by the
fill mode. The example uses HIPSOLVER_FILL_MODE_UPPER and checks the factor
against the known Cholesky root.
!!!!!!!!!!!!!!
! hipsolver dpotrf example (Cholesky factorization, Fortran 2008 interfaces)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Native-array f2008 form: A is a Fortran array pointer; the workspace is a
! type(c_ptr) sized by hipsolverDpotrf_bufferSize; devInfo is device-backed.
!!!!!!!!!!!!!!
!
program hipsolver_dpotrf
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: i, j
integer(c_int), parameter :: N = 3, lda = 3
real(c_double) :: hA(3,3) = reshape((/4, 12, -16, 12, 37, -43, -16, -43, 98/), (/3, 3/))
real(c_double) :: hResult(3,3) = reshape((/&
2.0000000000000000d0, 12.0000000000000000d0, -16.0000000000000000d0, &
6.0000000000000000d0, 1.0000000000000000d0, -43.0000000000000000d0, &
-8.0000000000000000d0, 5.0000000000000000d0, 3.0000000000000000d0/), (/3, 3/))
real(c_double), pointer :: dA(:,:)
integer(c_int), pointer :: dInfo
type(c_ptr) :: dWork, handle = c_null_ptr
integer(c_int) :: lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 10 * epsilon(error_max)
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dpotrf' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dInfo))
call hipsolverCheck(hipsolverDpotrf_bufferSize(handle, HIPSOLVER_FILL_MODE_UPPER, N, dA, lda, lwork))
call hipCheck(hipMalloc(dWork, max(int(lwork,c_size_t) * 8, 1_c_size_t)))
call hipsolverCheck(hipsolverDpotrf(handle, HIPSOLVER_FILL_MODE_UPPER, N, dA, lda, dWork, lwork, dInfo))
call hipCheck(hipMemcpy(hA, dA, hipMemcpyDeviceToHost))
do j = 1,3
do i = 1,3
error = abs(hA(i,j) - hResult(i,j)) / max(abs(hResult(i,j)), 1.0_c_double)
if (error > error_max) then
write(*,*) "FAILED! hA(", i, ",", j, ") = ", hA(i,j)
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA)); call hipCheck(hipFree(dInfo)); call hipCheck(hipFree(dWork))
call hipsolverCheck(hipsolverDestroy(handle))
write(*,*) "PASSED!"
end program hipsolver_dpotrf
potrs solves A*X = B from a potrf factorization. The example forms
b = A*x for a known x and confirms the solve recovers it.
!!!!!!!!!!!!!/
! hipsolverDpotrs example (double-precision Cholesky solve)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: pick a known solution x, form b = A*x for a symmetric positive
! definite A, factorize with potrf, solve A*X = b with potrs, and confirm X
! recovers x.
!!!!!!!!!!!!!!/
!
program dpotrs
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
use hipfort_hipsolver_enums
implicit none
integer :: i ! index for iterating over results
integer(c_int), parameter :: N = 3
integer(c_int), parameter :: nrhs = 1
integer(c_int), parameter :: lda = 3
integer(c_int), parameter :: ldb = 3
! Symmetric positive definite input (column-major)
real(c_double) :: hA(3,3) = reshape((/4, 2, 2, 2, 5, 3, 2, 3, 6/), (/3, 3/))
real(c_double) :: hX(3,1) = reshape((/1, 2, 3/), (/3, 1/)) ! known solution
real(c_double) :: hB(3,1) ! RHS = A*x
type(c_ptr) :: handle = c_null_ptr
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dB(:,:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork_f, lwork_s, lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 100 * epsilon(error_max)
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDpotrs' (Fortran 2008 interfaces) - "
! Build a consistent RHS so that A*x = b
hB = matmul(hA, hX)
call hipsolverCheck(hipsolverCreate(handle))
! Allocate device-side memory & copy memory from host to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dB, source=hB))
call hipCheck(hipMalloc(dInfo, 1))
! Workspace big enough for both potrf and potrs
call hipsolverCheck(hipsolverDpotrf_bufferSize(handle, HIPSOLVER_FILL_MODE_LOWER, N, dA, lda, lwork_f))
call hipsolverCheck(hipsolverDpotrs_bufferSize(handle, HIPSOLVER_FILL_MODE_LOWER, N, nrhs, dA, lda, dB, ldb, lwork_s))
lwork = max(lwork_f, lwork_s)
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Factorize A = L*L**T, then solve A*X = B in place
call hipsolverCheck(hipsolverDpotrf(handle, HIPSOLVER_FILL_MODE_LOWER, N, dA, lda, dWork, lwork, dInfo(1)))
call hipsolverCheck(hipsolverDpotrs(handle, HIPSOLVER_FILL_MODE_LOWER, N, nrhs, dA, lda, dB, ldb, dWork, lwork, dInfo(1)))
! Copy result from device to host
call hipCheck(hipMemcpy(hB, dB, hipMemcpyDeviceToHost))
! Verify the recovered solution matches x
do i = 1,N
error = abs(hB(i,1) - hX(i,1)) / max(abs(hX(i,1)), 1.0_c_double)
if(error .gt. error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " X(", i, ") = ", hB(i,1)
call exit
end if
end do
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dpotrs
Batched Cholesky#
potrfBatched factorizes many matrices with one call. The batched API takes
A as an array of device pointers that itself lives in device memory: each
matrix is allocated on the device, their device addresses are collected in a
host array, and that array is copied to a device buffer whose address is passed
as A. info is a device array indexed by batch.
!!!!!!!!!!!!!!
! hipsolver dpotrfBatched example (batched Cholesky factorization)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Factorizes a batch of SPD matrices. The batched API takes A as an array of
! device pointers that itself lives in DEVICE memory: each matrix is allocated on
! the device, their device addresses are collected in a host array, and that
! array is copied to a device buffer whose address (by value) is passed as A.
! Checks info == 0 for every batch entry and the Cholesky diagonal.
!!!!!!!!!!!!!!
!
program dpotrfbatched
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: b
integer(c_int), parameter :: N = 3, lda = 3, batch = 2
! SPD A = diag(4, 9, 16) -> upper Cholesky diag = 2, 3, 4
real(c_double), target :: hA(N,N) = reshape((/ &
4.0d0, 0.0d0, 0.0d0, &
0.0d0, 9.0d0, 0.0d0, &
0.0d0, 0.0d0,16.0d0/), (/N,N/))
real(c_double), target :: hOut(N,N)
integer(c_int) :: hInfo(batch)
type(c_ptr), target :: hostPtrs(batch)
type(c_ptr) :: dA1, dA2, dPtrArray, dWork
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: handle
integer(c_int) :: lwork
integer(c_size_t) :: ptrbytes
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dpotrfbatched' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
call hipCheck(hipMalloc(dA1, int(N*N,c_size_t) * 8))
call hipCheck(hipMalloc(dA2, int(N*N,c_size_t) * 8))
call hipCheck(hipMemcpy(dA1, c_loc(hA(1,1)), int(N*N,c_size_t) * 8, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dA2, c_loc(hA(1,1)), int(N*N,c_size_t) * 8, hipMemcpyHostToDevice))
! Host array of device addresses, copied to a device-resident pointer array.
hostPtrs(1) = dA1
hostPtrs(2) = dA2
ptrbytes = int(batch,c_size_t) * c_sizeof(c_null_ptr)
call hipCheck(hipMalloc(dPtrArray, ptrbytes))
call hipCheck(hipMemcpy(dPtrArray, c_loc(hostPtrs), ptrbytes, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dInfo, batch))
call hipsolverCheck(hipsolverDpotrfBatched_bufferSize(handle, HIPSOLVER_FILL_MODE_UPPER, &
N, dPtrArray, lda, lwork, batch))
call hipCheck(hipMalloc(dWork, int(max(lwork,1),c_size_t) * 8))
call hipsolverCheck(hipsolverDpotrfBatched(handle, HIPSOLVER_FILL_MODE_UPPER, &
N, dPtrArray, lda, dWork, lwork, dInfo(1), batch))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hInfo, dInfo, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(c_loc(hOut(1,1)), dA1, int(N*N,c_size_t) * 8, hipMemcpyDeviceToHost))
do b = 1, batch
if (hInfo(b) /= 0) then
write(*,*) "FAILED! info(", b, ") = ", hInfo(b), " (expected 0)"; call exit(1)
end if
end do
if (abs(hOut(1,1)-2.0d0) > 1.0d-9 .or. abs(hOut(2,2)-3.0d0) > 1.0d-9 .or. &
abs(hOut(3,3)-4.0d0) > 1.0d-9) then
write(*,*) "FAILED! chol diag = ", hOut(1,1), hOut(2,2), hOut(3,3), " expected 2 3 4"; call exit(1)
end if
call hipCheck(hipFree(dA1)); call hipCheck(hipFree(dA2)); call hipCheck(hipFree(dPtrArray))
call hipCheck(hipFree(dWork)); call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle)); call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dpotrfbatched
QR factorization#
geqrf computes A = Q*R, storing R in the upper triangle of A and
the Householder vectors that represent Q below it, with their scalar factors
in tau.
!!!!!!!!!!!!!/
! hipsolverDgeqrf example (double-precision QR factorization)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: geqrf overwrites the upper triangle of A with R. Because
! A = Q*R with Q orthogonal, A**T * A = R**T * R, so we recover R from the
! output, form R**T * R, and compare against A0**T * A0 computed on the host.
!
! hipSOLVER uses an explicit workspace sized by the matching _bufferSize query,
! and devInfo lives in device memory.
!!!!!!!!!!!!!!/
!
program dgeqrf
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: i, j, l ! indices for iterating over results
integer(c_int), parameter :: M = 3
integer(c_int), parameter :: N = 3
integer(c_int), parameter :: lda = 3
! Input matrix (column-major)
real(c_double) :: hA(3,3) = reshape((/1, 4, 7, 2, 5, 8, 3, 6, 10/), (/3, 3/))
real(c_double) :: hA0(3,3) ! original kept for verification
real(c_double) :: hTau(3)
real(c_double) :: R(3,3), lhs, rhs
type(c_ptr) :: handle = c_null_ptr
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dTau(:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 1.0d-9
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDgeqrf' (Fortran 2008 interfaces) - "
hA0 = hA ! keep original for the A**T*A = R**T*R check
call hipsolverCheck(hipsolverCreate(handle))
! Allocate device-side memory & copy memory from host to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dTau, mold=hTau))
call hipCheck(hipMalloc(dInfo, 1))
! Query workspace size and allocate it
call hipsolverCheck(hipsolverDgeqrf_bufferSize(handle, M, N, dA, lda, lwork))
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Compute the QR factorization
call hipsolverCheck(hipsolverDgeqrf(handle, M, N, dA, lda, dTau(1), dWork, lwork, dInfo(1)))
! Copy the factorized matrix back to host
call hipCheck(hipMemcpy(hA, dA, hipMemcpyDeviceToHost))
! Extract R (upper triangle of the geqrf output)
R = 0.0_c_double
do j = 1,N
do i = 1,j
R(i,j) = hA(i,j)
end do
end do
! Verify A0**T * A0 = R**T * R
do j = 1,N
do i = 1,N
lhs = 0.0_c_double
rhs = 0.0_c_double
do l = 1,M
lhs = lhs + hA0(l,i) * hA0(l,j)
rhs = rhs + R(l,i) * R(l,j)
end do
error = abs(lhs - rhs)
if(error .gt. error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " (", i, ",", j, ")"
call exit
end if
end do
end do
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dTau))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dgeqrf
Q is never formed explicitly by geqrf. Two follow-on routines use its
compact representation: orgqr (ungqr for complex) generates the explicit
orthogonal matrix Q, and ormqr (unmqr for complex) multiplies a
given matrix by Q or Q**T without forming it.
!!!!!!!!!!!!!/
! hipsolverDorgqr example (double-precision generation of Q from a QR
! factorization)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: factorize A with geqrf, generate the orthogonal factor Q with
! orgqr, and confirm Q**T * Q = I. Orthogonality is sign-convention independent,
! so no reference matrix is needed.
!!!!!!!!!!!!!!/
!
program dorgqr
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer :: i, j ! indices for iterating over results
integer(c_int), parameter :: M = 3
integer(c_int), parameter :: N = 3
integer(c_int), parameter :: K = 3
integer(c_int), parameter :: lda = 3
! Input matrix (column-major)
real(c_double) :: hA(3,3) = reshape((/1, 4, 7, 2, 5, 8, 3, 6, 10/), (/3, 3/))
real(c_double) :: hTau(3)
real(c_double) :: gram
type(c_ptr) :: handle = c_null_ptr
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dTau(:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork_qr, lwork_or, lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 1.0d-9
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDorgqr' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
! Allocate device-side memory & copy memory from host to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dTau, mold=hTau))
call hipCheck(hipMalloc(dInfo, 1))
! Workspace big enough for both geqrf and orgqr
call hipsolverCheck(hipsolverDgeqrf_bufferSize(handle, M, N, dA, lda, lwork_qr))
call hipsolverCheck(hipsolverDorgqr_bufferSize(handle, M, N, K, dA, lda, dTau(1), lwork_or))
lwork = max(lwork_qr, lwork_or)
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Factorize A = Q*R, then form the explicit Q in place
call hipsolverCheck(hipsolverDgeqrf(handle, M, N, dA, lda, dTau(1), dWork, lwork, dInfo(1)))
call hipsolverCheck(hipsolverDorgqr(handle, M, N, K, dA, lda, dTau(1), dWork, lwork, dInfo(1)))
! Copy Q back to host
call hipCheck(hipMemcpy(hA, dA, hipMemcpyDeviceToHost))
! Verify Q**T * Q = I
do j = 1,N
do i = 1,N
gram = sum(hA(:,i) * hA(:,j))
if(i .eq. j) then
error = abs(gram - 1.0_c_double)
else
error = abs(gram)
end if
if(error .gt. error_max) then
write(*,*) "FAILED! Q not orthogonal! Error = ", error, " (", i, ",", j, ")"
call exit
end if
end do
end do
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dTau))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dorgqr
!!!!!!!!!!!!!/
! hipsolverDormqr example (double-precision multiply by Q from a QR
! factorization)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: factorize A with geqrf to obtain Q (as Householder vectors),
! then form Q*C with ormqr. Q is orthogonal, so it preserves the Frobenius norm:
! ||Q*C||_F = ||C||_F. Norm preservation is sign-convention independent, so no
! reference matrix is needed.
!!!!!!!!!!!!!!/
!
program dormqr
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
use hipfort_hipsolver_enums
implicit none
integer :: i, j
integer(c_int), parameter :: M = 3
integer(c_int), parameter :: N = 2 ! number of columns of C
integer(c_int), parameter :: K = 3 ! number of reflectors
integer(c_int), parameter :: lda = 3
integer(c_int), parameter :: ldc = 3
! Matrix to factorize (column-major) and a separate C to multiply
real(c_double) :: hA(3,3) = reshape((/1, 4, 7, 2, 5, 8, 3, 6, 10/), (/3, 3/))
real(c_double) :: hC(3,2) = reshape((/1, 2, 3, 4, 5, 6/), (/3, 2/))
real(c_double) :: hTau(3)
real(c_double) :: norm_in, norm_out
type(c_ptr) :: handle = c_null_ptr
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dC(:,:)
real(c_double), pointer :: dTau(:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork_qr, lwork_mq, lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 1.0d-9
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDormqr' (Fortran 2008 interfaces) - "
! Norm of the input C (Frobenius)
norm_in = sqrt(sum(hC*hC))
call hipsolverCheck(hipsolverCreate(handle))
! Allocate device-side memory & copy inputs to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dC, source=hC))
call hipCheck(hipMalloc(dTau, mold=hTau))
call hipCheck(hipMalloc(dInfo, 1))
! Workspace big enough for both geqrf and ormqr
call hipsolverCheck(hipsolverDgeqrf_bufferSize(handle, M, K, dA, lda, lwork_qr))
call hipsolverCheck(hipsolverDormqr_bufferSize(handle, HIPSOLVER_SIDE_LEFT, HIPSOLVER_OP_N, &
M, N, K, dA, lda, dTau(1), dC, ldc, lwork_mq))
lwork = max(lwork_qr, lwork_mq)
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Factorize A = Q*R (Q stored as reflectors), then form C <- Q*C in place
call hipsolverCheck(hipsolverDgeqrf(handle, M, K, dA, lda, dTau(1), dWork, lwork, dInfo(1)))
call hipsolverCheck(hipsolverDormqr(handle, HIPSOLVER_SIDE_LEFT, HIPSOLVER_OP_N, &
M, N, K, dA, lda, dTau(1), dC, ldc, dWork, lwork, dInfo(1)))
! Copy the transformed C back to host
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
! Verify ||Q*C||_F = ||C||_F (Q is orthogonal)
norm_out = sqrt(sum(hC*hC))
error = abs(norm_out - norm_in) / max(norm_in, 1.0_c_double)
if(error .gt. error_max) then
write(*,*) "FAILED! Norm not preserved! ||C|| = ", norm_in, " ||Q*C|| = ", norm_out
call exit
end if
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dC))
call hipCheck(hipFree(dTau))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dormqr
Symmetric eigenvalues#
syevd (heevd for Hermitian matrices) computes the eigenvalues, and
optionally the eigenvectors, of a symmetric matrix with a divide-and-conquer
algorithm. The HIPSOLVER_EIG_MODE_* argument selects whether eigenvectors are
produced; the example requests eigenvalues only and checks their sum against the
trace.
!!!!!!!!!!!!!!
! hipsolver dsyevd example (symmetric eigenvalues, Fortran 2008 interfaces)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Checks sum(eigenvalues) == trace(A). Native-array f2008 form; workspace via
! hipsolverDsyevd_bufferSize; devInfo device-backed.
!!!!!!!!!!!!!!
!
program hipsolver_dsyevd
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer(c_int), parameter :: N = 4, lda = 4
real(c_double) :: hA(N,N) = reshape((/ &
10.0d0, 2.0d0, 3.0d0, 6.0d0, &
2.0d0, 11.0d0, 1.0d0, 0.0d0, &
3.0d0, 1.0d0, 12.0d0, 2.0d0, &
6.0d0, 0.0d0, 2.0d0, 13.0d0/), (/N,N/))
real(c_double) :: hD(N) = 0.0d0
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dD(:)
integer(c_int), pointer :: dInfo
type(c_ptr) :: dWork, handle = c_null_ptr
integer(c_int) :: lwork
real(c_double) :: trace_A, error
real(c_double), parameter :: rtol = 1.0d-9
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dsyevd' (Fortran 2008 interfaces) - "
trace_A = hA(1,1) + hA(2,2) + hA(3,3) + hA(4,4)
call hipsolverCheck(hipsolverCreate(handle))
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dD, source=hD))
call hipCheck(hipMalloc(dInfo))
call hipsolverCheck(hipsolverDsyevd_bufferSize(handle, HIPSOLVER_EIG_MODE_NOVECTOR, &
HIPSOLVER_FILL_MODE_UPPER, N, dA, lda, dD, lwork))
call hipCheck(hipMalloc(dWork, max(int(lwork,c_size_t) * 8, 1_c_size_t)))
call hipsolverCheck(hipsolverDsyevd(handle, HIPSOLVER_EIG_MODE_NOVECTOR, HIPSOLVER_FILL_MODE_UPPER, &
N, dA, lda, dD, dWork, lwork, dInfo))
call hipCheck(hipMemcpy(hD, dD, hipMemcpyDeviceToHost))
error = abs(sum(hD) - trace_A) / abs(trace_A)
if (error > rtol) then
write(*,*) "FAILED! sum(eigenvalues) = ", sum(hD), " expected trace = ", trace_A
call exit(1)
end if
call hipCheck(hipFree(dA)); call hipCheck(hipFree(dD)); call hipCheck(hipFree(dInfo)); call hipCheck(hipFree(dWork))
call hipsolverCheck(hipsolverDestroy(handle))
write(*,*) "PASSED!"
end program hipsolver_dsyevd
syevj/heevj solve the same problem with a Jacobi algorithm, which is
often faster for small matrices. With HIPSOLVER_EIG_MODE_VECTOR the matrix is
overwritten with the eigenvectors; the example confirms each eigenpair satisfies
A*v = lambda*v.
!!!!!!!!!!!!!/
! hipsolverDsyevj example (double-precision Jacobi symmetric eigensolver)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: with jobz=vector, A is overwritten with the eigenvectors (as
! columns) and W holds the eigenvalues. We confirm A0*v_k = lambda_k*v_k.
!
! syevj has no generated array-pointer overload, so A and W are passed to the
! generic interface via c_loc of their device pointers.
!!!!!!!!!!!!!!/
!
program dsyevj
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
use hipfort_hipsolver_enums
implicit none
integer :: i, k ! indices for iterating over results
integer(c_int), parameter :: N = 3
integer(c_int), parameter :: lda = 3
! Symmetric input (column-major); eigenvalues are 2-sqrt(2), 2, 2+sqrt(2)
real(c_double) :: hA(3,3) = reshape((/2, -1, 0, -1, 2, -1, 0, -1, 2/), (/3, 3/))
real(c_double) :: hA0(3,3) ! original kept for verification
real(c_double) :: hW(3) ! eigenvalues
real(c_double) :: lhs(3), rhs(3)
type(c_ptr) :: handle = c_null_ptr
type(c_ptr) :: params = c_null_ptr
real(c_double), pointer :: dA(:,:)
real(c_double), pointer :: dW(:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 1.0d-9
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDsyevj' (Fortran 2008 interfaces) - "
hA0 = hA ! keep original for the A*v = lambda*v check
call hipsolverCheck(hipsolverCreate(handle))
call hipsolverCheck(hipsolverCreateSyevjInfo(params))
! Allocate device-side memory & copy memory from host to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dW, mold=hW))
call hipCheck(hipMalloc(dInfo, 1))
! Query workspace size and allocate it
call hipsolverCheck(hipsolverDsyevj_bufferSize(handle, HIPSOLVER_EIG_MODE_VECTOR, &
HIPSOLVER_FILL_MODE_UPPER, N, c_loc(dA(1,1)), lda, c_loc(dW(1)), lwork, params))
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Compute eigenvalues and eigenvectors (A overwritten with eigenvectors)
call hipsolverCheck(hipsolverDsyevj(handle, HIPSOLVER_EIG_MODE_VECTOR, &
HIPSOLVER_FILL_MODE_UPPER, N, c_loc(dA(1,1)), lda, c_loc(dW(1)), dWork, lwork, dInfo(1), params))
! Copy results back to host
call hipCheck(hipMemcpy(hA, dA, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(hW, dW, hipMemcpyDeviceToHost))
! Verify A0 * v_k = lambda_k * v_k for each eigenpair
do k = 1,N
lhs = matmul(hA0, hA(:,k))
rhs = hW(k) * hA(:,k)
do i = 1,N
error = abs(lhs(i) - rhs(i))
if(error .gt. error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " eigenpair ", k
call exit
end if
end do
end do
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dW))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroySyevjInfo(params))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dsyevj
Singular value decomposition#
gesvd computes the singular value decomposition A = U*S*V**T. The
character(c_char) job codes choose which singular-vector matrices are
computed. The example requests singular values only ('N') and checks the
convention-independent invariant sum(sigma_i**2) == ||A||_F**2.
!!!!!!!!!!!!!!
! hipsolver dgesvd example (singular value decomposition)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Computes the singular values of A (jobu = jobv = 'N', values only) and checks
! the convention-independent invariant sum(sigma_i^2) == ||A||_F^2.
!
! Note: jobu/jobv are `signed char` job codes passed by value; the hipfort
! binding now types them as character(c_char) (they were previously type(c_ptr),
! which made this routine uncallable). devInfo lives on the DEVICE.
!!!!!!!!!!!!!!
!
program hipsolver_dgesvd
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
implicit none
integer(c_int), parameter :: M = 2, N = 2, lda = 2, ldu = 2, ldv = 2
integer(c_int), parameter :: mn = 2 ! min(M,N)
! A = [[1,2],[3,4]] (column-major); ||A||_F^2 = 1+9+4+16 = 30.
real(c_double), target :: hA(M,N) = reshape((/1.0d0, 3.0d0, 2.0d0, 4.0d0/), (/M,N/))
real(c_double), target :: hS(mn)
type(c_ptr) :: dA, dS, dU, dV, dWork, dRwork, dInfo, handle = c_null_ptr
integer(c_int) :: lwork
integer(c_size_t) :: szA = M*N, szS = mn, szU = M*M, szV = N*N, szR = mn
real(c_double) :: frob, ssum, error
real(c_double), parameter :: rtol = 1.0d-9
integer :: i
write(*,"(a)",advance="no") "-- Running test 'hipsolver_dgesvd' (Fortran 2008 interfaces) - "
call hipsolverCheck(hipsolverCreate(handle))
call hipCheck(hipMalloc(dA, szA * 8))
call hipCheck(hipMalloc(dS, szS * 8))
call hipCheck(hipMalloc(dU, szU * 8))
call hipCheck(hipMalloc(dV, szV * 8))
call hipCheck(hipMalloc(dRwork, szR * 8))
call hipCheck(hipMalloc(dInfo, 4_c_size_t))
call hipCheck(hipMemcpy(dA, c_loc(hA(1,1)), szA * 8, hipMemcpyHostToDevice))
call hipsolverCheck(hipsolverDgesvd_bufferSize(handle, 'N', 'N', M, N, lwork))
call hipCheck(hipMalloc(dWork, max(int(lwork,c_size_t) * 8, 1_c_size_t)))
! Singular values only (jobu = jobv = 'N').
call hipsolverCheck(hipsolverDgesvd(handle, 'N', 'N', M, N, dA, lda, dS, &
dU, ldu, dV, ldv, dWork, lwork, dRwork, dInfo))
call hipCheck(hipMemcpy(c_loc(hS(1)), dS, szS * 8, hipMemcpyDeviceToHost))
frob = 1.0d0 + 9.0d0 + 4.0d0 + 16.0d0 ! ||A||_F^2
ssum = 0.0d0
do i = 1, mn
ssum = ssum + hS(i)**2
end do
error = abs(ssum - frob) / frob
if (error > rtol) then
write(*,*) "FAILED! sum(sigma^2) = ", ssum, " expected ||A||_F^2 = ", frob
call exit(1)
end if
call hipCheck(hipFree(dA)); call hipCheck(hipFree(dS)); call hipCheck(hipFree(dU))
call hipCheck(hipFree(dV)); call hipCheck(hipFree(dRwork)); call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroy(handle))
write(*,*) "PASSED!"
end program hipsolver_dgesvd
gesvdj computes the same decomposition with a Jacobi algorithm. The example
requests all vectors and reconstructs A from the factors, which avoids the
sign and order ambiguity of the singular vectors.
!!!!!!!!!!!!!/
! hipsolverDgesvdj example (double-precision Jacobi SVD)
! see: https:!rocm.docs.amd.com/projects/hipSOLVER/en/latest/
!
! Self-verifying: compute the singular value decomposition A = U*S*V**T with the
! Jacobi method and confirm the factors reconstruct the original matrix. Using
! the reconstruction avoids sign/order ambiguity in the singular vectors.
!
! gesvdj has no generated array-pointer overload, so the matrix/vector arguments
! are passed to the generic interface via c_loc of their device pointers.
!!!!!!!!!!!!!!/
!
program dgesvdj
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipsolver
use hipfort_hipsolver_enums
implicit none
integer :: i, j ! indices for iterating over results
integer(c_int), parameter :: M = 3
integer(c_int), parameter :: N = 3
integer(c_int), parameter :: lda = 3
integer(c_int), parameter :: ldu = 3
integer(c_int), parameter :: ldv = 3
integer(c_int), parameter :: econ = 0
! Nonsingular input matrix (column-major)
real(c_double) :: hA(3,3) = reshape((/1, 4, 7, 2, 5, 8, 3, 6, 10/), (/3, 3/))
real(c_double) :: hA0(3,3) ! original kept for verification
real(c_double) :: hS(3) ! singular values
real(c_double) :: hU(3,3) ! left singular vectors
real(c_double) :: hV(3,3) ! right singular vectors
real(c_double) :: recon(3,3)
type(c_ptr) :: handle = c_null_ptr
type(c_ptr) :: params = c_null_ptr
real(c_double), pointer :: dA(:,:), dU(:,:), dV(:,:)
real(c_double), pointer :: dS(:)
integer(c_int), pointer :: dInfo(:)
type(c_ptr) :: dWork
integer(c_int) :: lwork
real(c_double) :: error
real(c_double), parameter :: error_max = 1.0d-9
!
write(*,"(a)",advance="no") "-- Running test 'hipsolverDgesvdj' (Fortran 2008 interfaces) - "
hA0 = hA ! keep original for the reconstruction check
call hipsolverCheck(hipsolverCreate(handle))
call hipsolverCheck(hipsolverCreateGesvdjInfo(params))
! Allocate device-side memory & copy memory from host to device
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dS, mold=hS))
call hipCheck(hipMalloc(dU, mold=hU))
call hipCheck(hipMalloc(dV, mold=hV))
call hipCheck(hipMalloc(dInfo, 1))
! Query workspace size and allocate it
call hipsolverCheck(hipsolverDgesvdj_bufferSize(handle, HIPSOLVER_EIG_MODE_VECTOR, econ, &
M, N, c_loc(dA(1,1)), lda, c_loc(dS(1)), c_loc(dU(1,1)), ldu, c_loc(dV(1,1)), ldv, lwork, params))
call hipCheck(hipMalloc(dWork, int(lwork,c_size_t) * 8))
! Compute the singular value decomposition
call hipsolverCheck(hipsolverDgesvdj(handle, HIPSOLVER_EIG_MODE_VECTOR, econ, &
M, N, c_loc(dA(1,1)), lda, c_loc(dS(1)), c_loc(dU(1,1)), ldu, c_loc(dV(1,1)), ldv, &
dWork, lwork, dInfo(1), params))
! Copy results back to host
call hipCheck(hipMemcpy(hS, dS, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(hU, dU, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(hV, dV, hipMemcpyDeviceToHost))
! Reconstruct A = U * diag(S) * V**T and compare with the original
do j = 1,N
do i = 1,M
recon(i,j) = sum(hU(i,:) * hS(:) * hV(j,:))
end do
end do
do j = 1,N
do i = 1,M
error = abs(recon(i,j) - hA0(i,j))
if(error .gt. error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " (", i, ",", j, ")"
call exit
end if
end do
end do
! Clean up
call hipCheck(hipFree(dWork))
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dS))
call hipCheck(hipFree(dU))
call hipCheck(hipFree(dV))
call hipCheck(hipFree(dInfo))
call hipsolverCheck(hipsolverDestroyGesvdjInfo(params))
call hipsolverCheck(hipsolverDestroy(handle))
call hipCheck(hipDeviceReset())
write(*,*) "PASSED!"
end program dgesvdj