Skip to content

Commit d3979ea

Browse files
authored
Merge branch 'fortran-lang:master' into fix-hash-big-endian
2 parents 3c3ecd2 + c016c6d commit d3979ea

18 files changed

+855
-67
lines changed

.github/workflows/fpm-deployment.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ jobs:
4646
python config/fypp_deployment.py --with_xdp --with_qp
4747
fpm test --profile release --flag '-DWITH_XDP -DWITH_QP'
4848
49+
- run: | # Tests without xdp and qp
50+
python config/fypp_deployment.py
51+
fpm test --profile release
52+
4953
# Update and deploy the f90 files generated by github-ci to the `stdlib-fpm` branch.
5054
- name: Deploy 🚀
5155
uses: JamesIves/github-pages-deploy-action@4.1.5

doc/specs/stdlib_linalg.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,100 @@ call [stdlib_linalg(module):constrained_lstsq_space(interface)]`(a, c, lwork [
11111111
`c`: Shall be a rank-2 `real` or `complex` array of the same kind as `a` defining the linear equality constraints. It is an `intent(in)` argument.
11121112
`lwork`: Shall be an `integer` scalar returning the optimal size required for the workspace array to solve the constrained least-squares problem.
11131113

1114+
## `weighted_lstsq` - Computes the weighted least squares solution to a linear matrix equation {#weighted-lstsq}
1115+
1116+
### Status
1117+
1118+
Experimental
1119+
1120+
### Description
1121+
1122+
This function computes the weighted least-squares solution to a linear matrix equation \( A \cdot x = b \) where each observation has a different weight.
1123+
1124+
The solver minimizes the weighted 2-norm \( \| D(Ax - b) \|_2^2 \) where \( D = \mathrm{diag}(\sqrt{w}) \) is a diagonal matrix formed from the square roots of the weight vector. This is equivalent to transforming the problem to ordinary least-squares through row scaling. The solver is based on LAPACK's `*GELSD` backends.
1125+
1126+
### Syntax
1127+
1128+
`x = ` [[stdlib_linalg(module):weighted_lstsq(interface)]] `(w, a, b [, cond, overwrite_a, rank, err])`
1129+
1130+
### Arguments
1131+
1132+
`w`: Shall be a rank-1 `real` array containing the weight vector. For complex `a` and `b`, `w` shall use the same real kind as the components of `a`. All weights must be positive. It is an `intent(in)` argument.
1133+
1134+
`a`: Shall be a rank-2 `real` or `complex` array containing the coefficient matrix. It is an `intent(inout)` argument.
1135+
1136+
`b`: Shall be a rank-1 array of the same kind as `a`, containing the right-hand-side vector. It is an `intent(in)` argument.
1137+
1138+
`cond` (optional): Shall be a scalar `real` value cut-off threshold for rank evaluation: singular values with `s_i <= cond*maxval(s)` are treated as zero, and the rank counts values with `s_i > cond*maxval(s), i=1:rank`. Shall be a scalar, `intent(in)` argument.
1139+
1140+
`overwrite_a` (optional): Shall be an input `logical` flag. If `.true.`, input matrix `a` will be used as temporary storage and overwritten. This avoids internal data allocation. This is an `intent(in)` argument.
1141+
1142+
`rank` (optional): Shall be an `integer` scalar value, that contains the rank of input matrix `a`. This is an `intent(out)` argument.
1143+
1144+
`err` (optional): Shall be a `type(linalg_state_type)` value. This is an `intent(out)` argument.
1145+
1146+
### Return value
1147+
1148+
Returns an array value of the same kind as `a`, containing the weighted least-squares solution.
1149+
1150+
Raises `LINALG_VALUE_ERROR` if any weight is non-positive or if the matrix and weight/right-hand-side have incompatible sizes.
1151+
Raises `LINALG_ERROR` if the underlying Singular Value Decomposition process did not converge.
1152+
Exceptions trigger an `error stop`.
1153+
1154+
### Example
1155+
1156+
```fortran
1157+
{!example/linalg/example_weighted_lstsq.f90!}
1158+
```
1159+
1160+
## `solve_weighted_lstsq` - Compute the weighted least squares solution to a linear matrix equation (subroutine interface). {#solve-weighted-lstsq}
1161+
1162+
### Status
1163+
1164+
Experimental
1165+
1166+
### Description
1167+
1168+
This subroutine computes the weighted least-squares solution to a linear matrix equation \( A \cdot x = b \) where each observation has a different weight.
1169+
1170+
The solver minimizes the weighted 2-norm \( \| D(Ax - b) \|_2^2 \) where \( D = \mathrm{diag}(\sqrt{w}) \) is a diagonal matrix formed from the square roots of the weight vector. This is equivalent to transforming the problem to ordinary least-squares through row scaling. The solver is based on LAPACK's `*GELSD` backends.
1171+
1172+
### Syntax
1173+
1174+
`call ` [[stdlib_linalg(module):solve_weighted_lstsq(interface)]] `(w, a, b, x [, cond, overwrite_a, rank, err])`
1175+
1176+
### Arguments
1177+
1178+
`w`: Shall be a rank-1 `real` array containing the weight vector. For complex `a` and `b`, `w` shall use the same real kind as the components of `a`. All weights must be positive. It is an `intent(in)` argument.
1179+
1180+
`a`: Shall be a rank-2 `real` or `complex` array containing the coefficient matrix. It is an `intent(inout)` argument.
1181+
1182+
`b`: Shall be a rank-1 array of the same kind as `a`, containing the right-hand-side vector. It is an `intent(in)` argument.
1183+
1184+
`x`: Shall be a rank-1 array of the same kind as `a`, and size of at least `n`, containing the solution to the weighted least squares system. It is an `intent(inout)` argument.
1185+
1186+
`cond` (optional): Shall be a scalar `real` value cut-off threshold for rank evaluation: singular values with `s_i <= cond*maxval(s)` are treated as zero, and the rank counts values with `s_i > cond*maxval(s), i=1:rank`. Shall be a scalar, `intent(in)` argument.
1187+
1188+
`overwrite_a` (optional): Shall be an input `logical` flag. If `.true.`, input matrix `a` will be used as temporary storage and overwritten. This avoids internal data allocation. This is an `intent(in)` argument.
1189+
1190+
`rank` (optional): Shall be an `integer` scalar value, that contains the rank of input matrix `a`. This is an `intent(out)` argument.
1191+
1192+
`err` (optional): Shall be a `type(linalg_state_type)` value. This is an `intent(out)` argument.
1193+
1194+
### Return value
1195+
1196+
Returns an array value of the same kind as `a`, containing the weighted least-squares solution.
1197+
1198+
Raises `LINALG_VALUE_ERROR` if any weight is non-positive or if the matrix and weight/right-hand-side have incompatible sizes.
1199+
Raises `LINALG_ERROR` if the underlying Singular Value Decomposition process did not converge.
1200+
Exceptions trigger an `error stop`.
1201+
1202+
### Example
1203+
1204+
```fortran
1205+
{!example/linalg/example_weighted_lstsq.f90!}
1206+
```
1207+
11141208
## `det` - Computes the determinant of a square matrix
11151209

11161210
### Status

doc/specs/stdlib_str2num.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ The accuracy of the conversion is implementation dependent; it is recommended th
7575

7676
`sp` : exact match
7777

78-
`dp` : precision up-to epsilon(0.0_dp)
78+
`dp` : precision up-to 10*epsilon(0.0_dp)
7979

80-
`qp` : precision around 200*epsilon(0.0_qp)
80+
`qp` : precision around 100*epsilon(0.0_qp)
8181

8282
Where precision refers to the relative difference between `to_num` and `read`. On the other hand, `to_num` provides speed-ups ranging from 4x to >10x compared to the intrinsic `read`.

example/linalg/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ADD_EXAMPLE(lstsq1)
3737
ADD_EXAMPLE(lstsq2)
3838
ADD_EXAMPLE(constrained_lstsq1)
3939
ADD_EXAMPLE(constrained_lstsq2)
40+
ADD_EXAMPLE(weighted_lstsq)
4041
ADD_EXAMPLE(norm)
4142
ADD_EXAMPLE(mnorm)
4243
ADD_EXAMPLE(get_norm)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
! Weighted least-squares solver: function and subroutine interfaces
2+
program example_weighted_lstsq
3+
use stdlib_linalg_constants, only: dp
4+
use stdlib_linalg, only: weighted_lstsq, solve_weighted_lstsq
5+
implicit none
6+
7+
real(dp) :: A(4,2), b(4), w(4), x_sub(2)
8+
real(dp), allocatable :: x(:)
9+
10+
! Design matrix: intercept + slope
11+
A(:,1) = 1.0_dp
12+
A(:,2) = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp]
13+
14+
! Observations (one outlier at position 3)
15+
b = [2.1_dp, 4.0_dp, 10.0_dp, 7.9_dp]
16+
17+
! Weights: downweight the outlier
18+
w = [1.0_dp, 1.0_dp, 0.1_dp, 1.0_dp]
19+
20+
! Solve weighted least-squares (function interface)
21+
x = weighted_lstsq(w, A, b)
22+
23+
print '("Function interface: intercept = ",f8.4,", slope = ",f8.4)', x(1), x(2)
24+
! Function interface: intercept = 0.1500, slope = 1.9911
25+
26+
! Solve weighted least-squares (subroutine interface)
27+
call solve_weighted_lstsq(w, A, b, x_sub)
28+
29+
print '("Subroutine interface: intercept = ",f8.4,", slope = ",f8.4)', x_sub(1), x_sub(2)
30+
! Subroutine interface: intercept = 0.1500, slope = 1.9911
31+
32+
end program example_weighted_lstsq

src/core/stdlib_error.fypp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ module stdlib_error
8181

8282
!> Handle optional error message
8383
procedure :: handle => error_handling
84+
85+
!> Update the location of the error message
86+
procedure :: update_location => state_update_location
8487

8588
end type state_type
8689

@@ -252,6 +255,17 @@ contains
252255

253256
end subroutine error_handling
254257

258+
!> Update the location of the error message
259+
pure subroutine state_update_location(this, where_at)
260+
class(state_type), intent(inout) :: this
261+
character(len=*), optional, intent(in) :: where_at
262+
263+
if (present(where_at)) then
264+
if (len_trim(where_at) > 0) this%where_at = adjustl(where_at)
265+
end if
266+
267+
end subroutine state_update_location
268+
255269
!> Produce a nice error string
256270
pure function state_print(this) result(msg)
257271
class(state_type),intent(in) :: this

src/lapack/stdlib_lapack_base.fypp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,27 @@ interface
13101310
#:endfor
13111311
end interface
13121312

1313+
! LASCL2 performs diagonal scaling: X(i,:) = D(i) * X(i,:)
1314+
interface
1315+
#:for ik,it,ii in LINALG_INT_KINDS_TYPES
1316+
#:for rk,rt,ri in REAL_KINDS_TYPES
1317+
pure module subroutine stdlib${ii}$_${ri}$lascl2( m, n, d, x, ldx )
1318+
integer(${ik}$), intent(in) :: m, n, ldx
1319+
real(${rk}$), intent(in) :: d(*)
1320+
real(${rk}$), intent(inout) :: x(ldx,*)
1321+
end subroutine stdlib${ii}$_${ri}$lascl2
1322+
1323+
#:endfor
1324+
#:for ck,ct,ci in CMPLX_KINDS_TYPES
1325+
pure module subroutine stdlib${ii}$_${ci}$lascl2( m, n, d, x, ldx )
1326+
integer(${ik}$), intent(in) :: m, n, ldx
1327+
real(${ck}$), intent(in) :: d(*)
1328+
complex(${ck}$), intent(inout) :: x(ldx,*)
1329+
end subroutine stdlib${ii}$_${ci}$lascl2
1330+
1331+
#:endfor
1332+
#:endfor
1333+
end interface
13131334

13141335
interface
13151336
#:for ik,it,ii in LINALG_INT_KINDS_TYPES

src/lapack/stdlib_lapack_blas_like_l2.fypp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5681,4 +5681,51 @@ submodule(stdlib_lapack_base) stdlib_lapack_blas_like_l2
56815681

56825682

56835683
#:endfor
5684+
5685+
! LASCL2 performs diagonal scaling on a matrix: X(i,:) = D(i) * X(i,:)
5686+
#:for ik,it,ii in LINALG_INT_KINDS_TYPES
5687+
#:for rk,rt,ri in REAL_KINDS_TYPES
5688+
pure module subroutine stdlib${ii}$_${ri}$lascl2( m, n, d, x, ldx )
5689+
!! LASCL2 performs a diagonal scaling on a matrix:
5690+
!! X(i,j) = D(i) * X(i,j), for i = 1,...,M and j = 1,...,N.
5691+
!! D is a vector of length M containing the diagonal scaling factors.
5692+
! Scalar Arguments
5693+
integer(${ik}$), intent(in) :: m, n, ldx
5694+
! Array Arguments
5695+
real(${rk}$), intent(in) :: d(*)
5696+
real(${rk}$), intent(inout) :: x(ldx,*)
5697+
! =====================================================================
5698+
! Local Scalars
5699+
integer(${ik}$) :: i, j
5700+
! Executable Statements
5701+
do concurrent(j=1:n, i=1:m)
5702+
x(i,j) = d(i) * x(i,j)
5703+
end do
5704+
return
5705+
end subroutine stdlib${ii}$_${ri}$lascl2
5706+
5707+
#:endfor
5708+
#:for ck,ct,ci in CMPLX_KINDS_TYPES
5709+
pure module subroutine stdlib${ii}$_${ci}$lascl2( m, n, d, x, ldx )
5710+
!! LASCL2 performs a diagonal scaling on a matrix:
5711+
!! X(i,j) = D(i) * X(i,j), for i = 1,...,M and j = 1,...,N.
5712+
!! D is a vector of length M containing the diagonal scaling factors.
5713+
! Scalar Arguments
5714+
integer(${ik}$), intent(in) :: m, n, ldx
5715+
! Array Arguments
5716+
real(${ck}$), intent(in) :: d(*)
5717+
complex(${ck}$), intent(inout) :: x(ldx,*)
5718+
! =====================================================================
5719+
! Local Scalars
5720+
integer(${ik}$) :: i, j
5721+
! Executable Statements
5722+
do concurrent(j=1:n, i=1:m)
5723+
x(i,j) = d(i) * x(i,j)
5724+
end do
5725+
return
5726+
end subroutine stdlib${ii}$_${ci}$lascl2
5727+
5728+
#:endfor
5729+
#:endfor
5730+
56845731
end submodule stdlib_lapack_blas_like_l2

src/lapack/stdlib_linalg_lapack.fypp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16228,6 +16228,20 @@ module stdlib_linalg_lapack
1622816228
#:endfor
1622916229
end interface lascl
1623016230

16231+
interface lascl2
16232+
!! LASCL2 performs a diagonal scaling on a matrix:
16233+
!! X(i,j) = D(i) * X(i,j), for i = 1,...,M and j = 1,...,N.
16234+
!! D is a vector of length M containing the diagonal scaling factors.
16235+
#:for ik,it,ii in LINALG_INT_KINDS_TYPES
16236+
#:for rk,rt,ri in REAL_KINDS_TYPES
16237+
module procedure stdlib${ii}$_${ri}$lascl2
16238+
#:endfor
16239+
#:for ck,ct,ci in CMPLX_KINDS_TYPES
16240+
module procedure stdlib${ii}$_${ci}$lascl2
16241+
#:endfor
16242+
#:endfor
16243+
end interface lascl2
16244+
1623116245
interface lasd0
1623216246
!! Using a divide and conquer approach, LASD0: computes the singular
1623316247
!! value decomposition (SVD) of a real upper bidiagonal N-by-M

src/linalg/stdlib_linalg.fypp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ module stdlib_linalg
4040
public :: lstsq_space
4141
public :: constrained_lstsq
4242
public :: constrained_lstsq_space
43+
public :: weighted_lstsq
44+
public :: solve_weighted_lstsq
4345
public :: norm
4446
public :: mnorm
4547
public :: get_norm
@@ -797,6 +799,90 @@ module stdlib_linalg
797799
#:endfor
798800
end interface
799801

802+
! Weighted least-squares: minimize ||D(Ax - b)||^2 where D = diag(sqrt(w))
803+
interface weighted_lstsq
804+
!! version: experimental
805+
!!
806+
!! Computes the weighted least-squares solution to \( \min_x \|D(Ax - b)\|_2^2 \)
807+
!! ([Specification](../page/specs/stdlib_linalg.html#weighted-lstsq))
808+
!!
809+
!!### Summary
810+
!! Function interface for computing weighted least-squares via row scaling.
811+
!!
812+
!!### Description
813+
!!
814+
!! This interface provides methods for computing weighted least-squares by
815+
!! transforming to ordinary least-squares through row scaling.
816+
!! Supported data types include `real` and `complex`.
817+
!!
818+
!!@note The solution is based on LAPACK's `*GELSD` after applying diagonal weights.
819+
!!@warning Avoid extreme weight ratios (e.g., max(w)/min(w) > 1e6) as this may
820+
!! cause loss of precision in the SVD-based solver.
821+
!!
822+
#:for rk,rt,ri in RC_KINDS_TYPES
823+
module function stdlib_linalg_${ri}$_weighted_lstsq(w,a,b,cond,overwrite_a,rank,err) result(x)
824+
!> Weight vector (must be positive, always real)
825+
real(${rk}$), intent(in) :: w(:)
826+
!> Input matrix a(m,n)
827+
${rt}$, intent(inout), target :: a(:,:)
828+
!> Right hand side vector b(m)
829+
${rt}$, intent(in) :: b(:)
830+
!> [optional] cutoff for rank evaluation: singular values s(i)<=cond*maxval(s) are considered 0.
831+
real(${rk}$), optional, intent(in) :: cond
832+
!> [optional] Can A data be overwritten and destroyed?
833+
logical(lk), optional, intent(in) :: overwrite_a
834+
!> [optional] Return rank of A
835+
integer(ilp), optional, intent(out) :: rank
836+
!> [optional] state return flag. On error if not requested, the code will stop
837+
type(linalg_state_type), optional, intent(out) :: err
838+
!> Result array x(n)
839+
${rt}$, allocatable :: x(:)
840+
end function stdlib_linalg_${ri}$_weighted_lstsq
841+
#:endfor
842+
end interface weighted_lstsq
843+
844+
! Weighted least-squares subroutine: minimize ||D(Ax - b)||^2 where D = diag(sqrt(w))
845+
interface solve_weighted_lstsq
846+
!! version: experimental
847+
!!
848+
!! Computes the weighted least-squares solution to \( \min_x \|D(Ax - b)\|_2^2 \)
849+
!! ([Specification](../page/specs/stdlib_linalg.html#solve-weighted-lstsq-compute-the-weighted-least-squares-solution-to-a-linear-matrix-equation-subroutine-interface))
850+
!!
851+
!!### Summary
852+
!! Subroutine interface for computing weighted least-squares via row scaling.
853+
!!
854+
!!### Description
855+
!!
856+
!! This interface provides methods for computing weighted least-squares by
857+
!! transforming to ordinary least-squares through row scaling, using a subroutine.
858+
!! Supported data types include `real` and `complex`.
859+
!!
860+
!!@note The solution is based on LAPACK's `*GELSD` after applying diagonal weights.
861+
!!@warning Avoid extreme weight ratios (e.g., max(w)/min(w) > 1e6) as this may
862+
!! cause loss of precision in the SVD-based solver.
863+
!!
864+
#:for rk,rt,ri in RC_KINDS_TYPES
865+
module subroutine stdlib_linalg_${ri}$_solve_weighted_lstsq(w,a,b,x,cond,overwrite_a,rank,err)
866+
!> Weight vector (must be positive, always real)
867+
real(${rk}$), intent(in) :: w(:)
868+
!> Input matrix a(m,n)
869+
${rt}$, intent(inout), target :: a(:,:)
870+
!> Right hand side vector b(m)
871+
${rt}$, intent(in) :: b(:)
872+
!> Result array x(n)
873+
${rt}$, intent(inout), contiguous, target :: x(:)
874+
!> [optional] cutoff for rank evaluation: singular values s(i)<=cond*maxval(s) are considered 0.
875+
real(${rk}$), optional, intent(in) :: cond
876+
!> [optional] Can A data be overwritten and destroyed?
877+
logical(lk), optional, intent(in) :: overwrite_a
878+
!> [optional] Return rank of A
879+
integer(ilp), optional, intent(out) :: rank
880+
!> [optional] state return flag. On error if not requested, the code will stop
881+
type(linalg_state_type), optional, intent(out) :: err
882+
end subroutine stdlib_linalg_${ri}$_solve_weighted_lstsq
883+
#:endfor
884+
end interface solve_weighted_lstsq
885+
800886
! QR factorization of rank-2 array A
801887
interface qr
802888
!! version: experimental

0 commit comments

Comments
 (0)