-
Notifications
You must be signed in to change notification settings - Fork 193
linalg: determinant #798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
linalg: determinant #798
Changes from 7 commits
4a4b899
d54cfa3
c987fa1
b9a3b0e
4a29219
3d05cc3
4beab1f
15023e1
5923a54
31cdc84
45a606f
cacb585
5c16ff8
ab030c5
13bd98a
7bf7141
504d90d
e80b508
c6076ea
eaebe5c
5d52d48
cff995d
2fe2428
45447a8
3ee20ad
1e50115
fe933ad
59dae89
3c72b06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,302 @@ | ||
#:include "common.fypp" | ||
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES | ||
module stdlib_linalg_determinant | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could it be a submdodule of use stdlib_linalg, only: det instead of: use stdlib_linalg_determinant, only: det This will avoid an enormous amount of What do you think? What would be the best strategy for the users? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a good approach, everything would fit together smoothly. This might require indeed a large header module but for the end user and even a developer who would like to pick just one method and work on it, it would be easier... I think. |
||
!! Determinant of a rectangular matrix | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it for rectangular, or only square matrices (as mentioned earlier)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Square matrix only. do any of the major packages (numpy, scipy) provide a result for rectangular matrix? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The determinant is defined for squared matrices only. Both numpy https://numpy.org/doc/stable/reference/generated/numpy.linalg.det.html and scipy https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.det.html#scipy.linalg.det propose it for squared matrices. A "determinant" of a rectangular matrix could be computed with something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now we throw a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Seems like the best solution. returning 0 might be misleading IMO. |
||
use stdlib_linalg_constants | ||
use stdlib_linalg_lapack, only: getrf | ||
use stdlib_linalg_state, only: linalg_state_type, linalg_error_handling, LINALG_ERROR, & | ||
LINALG_INTERNAL_ERROR, LINALG_VALUE_ERROR | ||
implicit none(type,external) | ||
private | ||
|
||
! Function interface | ||
public :: det | ||
public :: operator(.det.) | ||
|
||
character(*), parameter :: this = 'determinant' | ||
|
||
interface det | ||
!!### Summary | ||
!! Interface for computing matrix determinant. | ||
!! | ||
!!### Description | ||
!! | ||
!! This interface provides methods for computing the determinant of a matrix. | ||
!! Supported data types include real and complex. | ||
!! | ||
!!@note The provided functions are intended for square matrices. | ||
!! | ||
!!### Example | ||
!! | ||
!!```fortran | ||
!! | ||
!! real(sp) :: a(3,3), d | ||
!! type(linalg_state_type) :: state | ||
!! a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]) | ||
!! | ||
!! d = det(a,err=state) | ||
!! if (state%ok()) then | ||
!! print *, 'Success! det=',d | ||
!! else | ||
!! print *, state%print() | ||
!! endif | ||
!! | ||
!!``` | ||
!! | ||
#:for rk,rt in RC_KINDS_TYPES | ||
#:if rk!="xdp" | ||
module procedure stdlib_linalg_${rt[0]}$${rk}$determinant | ||
module procedure stdlib_linalg_pure_${rt[0]}$${rk}$determinant | ||
#:endif | ||
#:endfor | ||
end interface det | ||
|
||
interface operator(.det.) | ||
!!### Summary | ||
!! Pure operator interface for computing matrix determinant. | ||
!! | ||
!!### Description | ||
!! | ||
!! This pure operator interface provides a convenient way to compute the determinant of a matrix. | ||
!! Supported data types include real and complex. | ||
!! | ||
!!@note The provided functions are intended for square matrices. | ||
!! | ||
!!### Example | ||
!! | ||
!!```fortran | ||
!! | ||
!! real(sp) :: matrix(3,3), d | ||
!! matrix = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]) | ||
!! d = .det.matrix | ||
!! | ||
!!``` | ||
! | ||
#:for rk,rt in RC_KINDS_TYPES | ||
#:if rk!="xdp" | ||
module procedure stdlib_linalg_pure_${rt[0]}$${rk}$determinant | ||
#:endif | ||
#:endfor | ||
end interface operator(.det.) | ||
|
||
contains | ||
|
||
#:for rk,rt in RC_KINDS_TYPES | ||
#:if rk!="xdp" | ||
pure function stdlib_linalg_pure_${rt[0]}$${rk}$determinant(a) result(det) | ||
!!### Summary | ||
!! Compute determinant of a real square matrix (pure interface). | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
!! | ||
!!### Description | ||
!! | ||
!! This function computes the determinant of a real square matrix. | ||
!! | ||
!! param: a Input matrix of size [m,n]. | ||
!! return: det Matrix determinant. | ||
!! | ||
!!### Example | ||
!! | ||
!!```fortran | ||
!! | ||
!! ${rt}$ :: matrix(3,3) | ||
!! ${rt}$ :: determinant | ||
!! matrix = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]) | ||
!! determinant = det(matrix) | ||
!! | ||
!!``` | ||
!> Input matrix a[m,n] | ||
${rt}$, intent(in) :: a(:,:) | ||
!> Matrix determinant | ||
${rt}$ :: det | ||
|
||
!! Local variables | ||
type(linalg_state_type) :: err0 | ||
integer(ilp) :: m,n,info,perm,k | ||
integer(ilp), allocatable :: ipiv(:) | ||
${rt}$, allocatable :: amat(:,:) | ||
|
||
! Matrix determinant size | ||
m = size(a,1,kind=ilp) | ||
n = size(a,2,kind=ilp) | ||
|
||
if (m/=n .or. .not.min(m,n)>=0) then | ||
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'invalid or non-square matrix: a=[',m,',',n,']') | ||
det = 0.0_${rk}$ | ||
goto 1 | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end if | ||
|
||
select case (m) | ||
case (0) | ||
|
||
! Empty array has determinant 1 because math | ||
det = 1.0_${rk}$ | ||
|
||
case (1) | ||
|
||
! Scalar input | ||
det = a(1,1) | ||
|
||
case default | ||
|
||
! Find determinant from LU decomposition | ||
|
||
! Initialize a matrix temporary | ||
allocate(amat(m,n),source=a) | ||
|
||
! Pivot indices | ||
allocate(ipiv(n)) | ||
|
||
! Compute determinant from LU factorization, then calculate the | ||
! product of all diagonal entries of the U factor. | ||
call getrf(m,n,amat,m,ipiv,info) | ||
|
||
select case (info) | ||
case (0) | ||
! Success: compute determinant | ||
|
||
! Start with real 1.0 | ||
det = 1.0_${rk}$ | ||
perm = 0 | ||
do k=1,n | ||
if (ipiv(k)/=k) perm = perm+1 | ||
det = det*amat(k,k) | ||
end do | ||
if (mod(perm,2)/=0) det = -det | ||
|
||
case (:-1) | ||
err0 = linalg_state_type(this,LINALG_ERROR,'invalid matrix size a=[',m,',',n,']') | ||
case (1:) | ||
err0 = linalg_state_type(this,LINALG_ERROR,'singular matrix') | ||
case default | ||
err0 = linalg_state_type(this,LINALG_INTERNAL_ERROR,'catastrophic error') | ||
end select | ||
|
||
deallocate(amat) | ||
|
||
end select | ||
|
||
! Process output and return | ||
1 call linalg_error_handling(err0) | ||
|
||
end function stdlib_linalg_pure_${rt[0]}$${rk}$determinant | ||
|
||
function stdlib_linalg_${rt[0]}$${rk}$determinant(a,overwrite_a,err) result(det) | ||
!!### Summary | ||
!! Compute determinant of a square matrix (with error control). | ||
!! | ||
!!### Description | ||
!! | ||
!! This function computes the determinant of a square matrix with error control. | ||
!! | ||
!! param: a Input matrix of size [m,n]. | ||
!! param: overwrite_a [optional] Flag indicating if the input matrix can be overwritten. | ||
!! param: err State return flag. | ||
!! return: det Matrix determinant. | ||
!! | ||
!!### Example | ||
!! | ||
!!```fortran | ||
!! | ||
!! ${rt}$ :: matrix(3,3) | ||
!! ${rt}$ :: determinant | ||
!! matrix = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3]) | ||
!! determinant = det(matrix, err=err) | ||
!! | ||
!!``` | ||
! | ||
!> Input matrix a[m,n] | ||
${rt}$, intent(inout), target :: a(:,:) | ||
!> [optional] Can A data be overwritten and destroyed? | ||
logical(lk), optional, intent(in) :: overwrite_a | ||
!> State return flag. | ||
type(linalg_state_type), intent(out) :: err | ||
!> Matrix determinant | ||
${rt}$ :: det | ||
|
||
!! Local variables | ||
type(linalg_state_type) :: err0 | ||
integer(ilp) :: m,n,info,perm,k | ||
integer(ilp), allocatable :: ipiv(:) | ||
logical(lk) :: copy_a | ||
${rt}$, pointer :: amat(:,:) | ||
|
||
! Matrix determinant size | ||
m = size(a,1,kind=ilp) | ||
n = size(a,2,kind=ilp) | ||
|
||
if (m/=n .or. .not.min(m,n)>=0) then | ||
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'invalid or non-square matrix: a=[',m,',',n,']') | ||
det = 0.0_${rk}$ | ||
goto 1 | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end if | ||
|
||
! Can A be overwritten? By default, do not overwrite | ||
if (present(overwrite_a)) then | ||
copy_a = .not.overwrite_a | ||
else | ||
copy_a = .true._lk | ||
endif | ||
|
||
select case (m) | ||
case (0) | ||
|
||
! Empty array has determinant 1 because math | ||
det = 1.0_${rk}$ | ||
|
||
case (1) | ||
|
||
! Scalar input | ||
det = a(1,1) | ||
|
||
case default | ||
|
||
! Find determinant from LU decomposition | ||
|
||
! Initialize a matrix temporary | ||
if (copy_a) then | ||
allocate(amat(m,n),source=a) | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else | ||
amat => a | ||
endif | ||
|
||
! Pivot indices | ||
allocate(ipiv(n)) | ||
|
||
! Compute determinant from LU factorization, then calculate the | ||
! product of all diagonal entries of the U factor. | ||
call getrf(m,n,amat,m,ipiv,info) | ||
|
||
select case (info) | ||
case (0) | ||
! Success: compute determinant | ||
|
||
! Start with real 1.0 | ||
det = 1.0_${rk}$ | ||
perm = 0 | ||
do k=1,n | ||
if (ipiv(k)/=k) perm = perm+1 | ||
det = det*amat(k,k) | ||
end do | ||
if (mod(perm,2)/=0) det = -det | ||
|
||
case (:-1) | ||
err0 = linalg_state_type(this,LINALG_ERROR,'invalid matrix size a=[',m,',',n,']') | ||
case (1:) | ||
err0 = linalg_state_type(this,LINALG_ERROR,'singular matrix') | ||
case default | ||
err0 = linalg_state_type(this,LINALG_INTERNAL_ERROR,'catastrophic error') | ||
end select | ||
|
||
if (.not.copy_a) deallocate(amat) | ||
|
||
end select | ||
|
||
! Process output and return | ||
1 call linalg_error_handling(err0,err) | ||
|
||
end function stdlib_linalg_${rt[0]}$${rk}$determinant | ||
|
||
#:endif | ||
#:endfor | ||
|
||
end module stdlib_linalg_determinant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This let me think that we should review the structure of the specs. Currently we have 1 page per module. However, if these become available through
stdlib_linalg
, the specs should be modified accordingly (probably in another PR; I can start to work on a proposition when this is ready).