Skip to content

Commit f5de00f

Browse files
committed
copy in code
1 parent 68524b3 commit f5de00f

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(fppFiles
3232
stdlib_linalg_determinant.fypp
3333
stdlib_linalg_qr.fypp
3434
stdlib_linalg_inverse.fypp
35+
stdlib_linalg_pinv.fypp
3536
stdlib_linalg_norms.fypp
3637
stdlib_linalg_state.fypp
3738
stdlib_linalg_svd.fypp

src/stdlib_linalg_pinv.fypp

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#:include "common.fypp"
2+
! Compute the (Moore-Penrose) pseudo-inverse of a matrix.
3+
module stdlib_linalg_pseudoinverse
4+
use stdlib_linalg_constants
5+
use stdlib_linalg_blas
6+
use stdlib_linalg_lapack
7+
use stdlib_linalg_state
8+
use stdlib_linalg_svd, only: svd
9+
use iso_fortran_env,only:real32,real64,real128,int8,int16,int32,int64,stderr => error_unit
10+
implicit none(type,external)
11+
private
12+
13+
!> Pseudo-inverse: Function interface
14+
public :: pinv
15+
!> Pseudo-inverse: Subroutine interface (pre-allocated)
16+
public :: pseudoinvert
17+
!> Operator interface: .pinv.A returns the pseudo-inverse of A
18+
public :: operator(.pinv.)
19+
20+
! Function interface
21+
interface pinv
22+
#:for rk,rt,ri in ALL_KINDS_TYPES
23+
module procedure stdlib_linalg_pseudoinverse_${ri}$
24+
#:endfor
25+
end interface pinv
26+
27+
! Subroutine interface
28+
interface pseudoinvert
29+
#:for rk,rt,ri in ALL_KINDS_TYPES
30+
module procedure stdlib_linalg_pseudoinvert_${ri}$
31+
#:endfor
32+
end interface pseudoinvert
33+
34+
! Operator interface
35+
interface operator(.pinv.)
36+
#:for rk,rt,ri in ALL_KINDS_TYPES
37+
module procedure stdlib_linalg_pinv_${ri}$_operator
38+
#:endfor
39+
end interface operator(.pinv.)
40+
41+
character(*), parameter :: this = 'pseudo-inverse'
42+
43+
contains
44+
45+
#:for rk,rt,ri in ALL_KINDS_TYPES
46+
47+
! Compute the in-place pseudo-inverse of matrix a
48+
subroutine stdlib_linalg_pseudoinvert_${ri}$(a,pinva,rtol,err)
49+
!> Input matrix a[m,n]
50+
${rt}$, intent(inout) :: a(:,:)
51+
!> Output pseudo-inverse matrix
52+
${rt}$, intent(inout) :: pinva(:,:)
53+
!> [optional] ....
54+
real(${rk}$), optional, intent(in) :: rtol
55+
!> [optional] state return flag. On error if not requested, the code will stop
56+
type(linalg_state), optional, intent(out) :: err
57+
58+
! Local variables
59+
real(${rk}$) :: tolerance,cutoff
60+
real(${rk}$), allocatable :: s(:)
61+
${rt}$, allocatable :: u(:,:),vt(:,:)
62+
type(linalg_state) :: err0
63+
integer(ilp) :: m,n,k,i,j
64+
65+
! Problem size
66+
m = size(a,1,kind=ilp)
67+
n = size(a,2,kind=ilp)
68+
k = min(m,n)
69+
if (m<1 .or. n<1) then
70+
err0 = linalg_state(this,LINALG_VALUE_ERROR,'invalid matrix size: a=',[m,n])
71+
call linalg_error_handling(err0,err)
72+
return
73+
end if
74+
75+
if (any(shape(pinva,kind=ilp)/=[n,m])) then
76+
err0 = linalg_state(this,LINALG_VALUE_ERROR,'invalid pinv size:',shape(pinva),'should be',[n,m])
77+
call linalg_error_handling(err0,err)
78+
return
79+
end if
80+
81+
! Singular value threshold
82+
tolerance = max(m,n)*epsilon(0.0_${rk}$)
83+
84+
! User threshold: fallback to default if <=0
85+
if (present(rtol)) then
86+
if (rtol>0.0_${rk}$) tolerance = rtol
87+
end if
88+
89+
allocate(s(k),u(m,k),vt(k,n))
90+
call svd(a,s,u,vt,overwrite_a=.false.,full_matrices=.false.,err=err0)
91+
if (err0%error()) then
92+
err0 = linalg_state(this,LINALG_ERROR,'svd failure -',err0%message)
93+
call linalg_error_handling(err0,err)
94+
return
95+
endif
96+
97+
!> Discard singular values
98+
cutoff = tolerance*maxval(s)
99+
s = merge(1/s,0.0_${rk}$,s>cutoff)
100+
101+
! Get pseudo-inverse: A_pinv = V * (diag(1/s) * U^H) = V * (U * diag(1/s))^H
102+
103+
! 1) compute (U * diag(1/s)) in-place
104+
forall (i=1:m,j=1:k) u(i,j) = s(j)*u(i,j)
105+
106+
! 2) commutate matmul: A_pinv = V^H * (U * diag(1/s))^H = ((U * diag(1/s)) * V^H)^H.
107+
! This avoids one matrix transpose
108+
#:if rt.startswith('complex')
109+
pinva = conjg(transpose(matmul(u,vt)))
110+
#:else
111+
pinva = transpose(matmul(u,vt))
112+
#:endif
113+
114+
end subroutine stdlib_linalg_pseudoinvert_${ri}$
115+
116+
! Function interface
117+
function stdlib_linalg_pseudoinverse_${ri}$(a,rtol,err) result(pinva)
118+
!> Input matrix a[m,n]
119+
${rt}$, intent(in), target :: a(:,:)
120+
!> [optional] ....
121+
real(${rk}$), optional, intent(in) :: rtol
122+
!> [optional] state return flag. On error if not requested, the code will stop
123+
type(linalg_state), optional, intent(out) :: err
124+
!> Matrix pseudo-inverse
125+
${rt}$ :: pinva(size(a,2,kind=ilp),size(a,1,kind=ilp))
126+
127+
! Use pointer to circumvent svd intent(inout) restriction
128+
${rt}$, pointer :: ap(:,:)
129+
ap => a
130+
131+
call stdlib_linalg_pseudoinvert_${ri}$(ap,pinva,rtol,err)
132+
133+
end function stdlib_linalg_pseudoinverse_${ri}$
134+
135+
! Inverse matrix operator
136+
function stdlib_linalg_pinv_${ri}$_operator(a) result(pinva)
137+
!> Input matrix a[m,n]
138+
${rt}$, intent(in), target :: a(:,:)
139+
!> Result matrix
140+
${rt}$ :: pinva(size(a,2,kind=ilp),size(a,1,kind=ilp))
141+
142+
! Use pointer to circumvent svd intent(inout) restriction
143+
${rt}$, pointer :: ap(:,:)
144+
ap => a
145+
146+
call stdlib_linalg_pseudoinvert_${ri}$(ap,pinva)
147+
148+
end function stdlib_linalg_pinv_${ri}$_operator
149+
150+
#:endfor
151+
152+
end module stdlib_linalg_pseudoinverse

0 commit comments

Comments
 (0)