Skip to content

Commit 38485d8

Browse files
authored
linalg: Matrix norms (#885)
2 parents 662ad1a + f52d57a commit 38485d8

File tree

8 files changed

+630
-1
lines changed

8 files changed

+630
-1
lines changed

doc/specs/stdlib_linalg.md

+49
Original file line numberDiff line numberDiff line change
@@ -1565,4 +1565,53 @@ If `err` is not present, exceptions trigger an `error stop`.
15651565
{!example/linalg/example_norm.f90!}
15661566
```
15671567

1568+
## `mnorm` - Computes the matrix norm of a generic-rank array.
1569+
1570+
### Status
1571+
1572+
Experimental
1573+
1574+
### Description
1575+
1576+
This function computes one of several matrix norms of `real` or `complex` array \( A \), depending on
1577+
the value of the `order` input argument. \( A \) must be an array of rank 2 or higher. For arrays of rank > 2,
1578+
matrix norms are computed over specified dimensions.
1579+
1580+
### Syntax
1581+
1582+
`x = ` [[stdlib_linalg(module):mnorm(interface)]] `(a [, order, dim, err])`
1583+
1584+
### Arguments
1585+
1586+
`a`: Shall be a rank-n `real` or `complex` array containing the data, where n >= 2. It is an `intent(in)` argument.
1587+
1588+
`order` (optional): Shall be an `integer` value or a `character` flag that specifies the norm type, as follows. It is an `intent(in)` argument.
1589+
1590+
| Integer input | Character Input | Norm type |
1591+
|------------------|---------------------------------|-----------------------------------------------------------------------------|
1592+
| `1` | `'1'` | 1-norm (maximum column sum) \( \max_j \sum_i{ \left|a_{i,j}\right| } \) |
1593+
| `2` | `'2'` | 2-norm (largest singular value) |
1594+
| (not prov.) | `'Euclidean','Frobenius','Fro'` | Frobenius norm \( \sqrt{\sum_{i,j}{ \left|a_{i,j}\right|^2 }} \) |
1595+
| `huge(0)` | `'inf', 'Inf', 'INF'` | Infinity norm (maximum row sum) \( \max_i \sum_j{ \left|a_{i,j}\right| } \) |
1596+
1597+
`dim` (optional): For arrays of rank > 2, shall be an integer array of size 2 specifying the dimensions over which to compute the matrix norm. Default value is `[1,2]`. It is an `intent(in)` argument.
1598+
1599+
`err` (optional): Shall be a `type(linalg_state_type)` value. This is an `intent(out)` argument.
1600+
1601+
### Return value
1602+
1603+
For rank-2 input arrays, the return value `x` is a scalar containing the matrix norm.
1604+
For arrays of rank > 2, if the optional `dim` argument is present, `x` is a rank `n-2` array with the same shape as \( A \) except
1605+
for dimensions `dim(1)` and `dim(2)`, which are dropped. Each element of `x` contains the matrix norm of the corresponding submatrix of \( A \),
1606+
evaluated over the specified dimensions only, with the given order.
1607+
1608+
If an invalid norm type is provided, defaults to 1-norm and raises `LINALG_ERROR`.
1609+
Raises `LINALG_VALUE_ERROR` if any of the arguments has an invalid size.
1610+
If `err` is not present, exceptions trigger an `error stop`.
1611+
1612+
### Example
1613+
1614+
```fortran
1615+
{!example/linalg/example_mnorm.f90!}
1616+
```
15681617

example/linalg/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ADD_EXAMPLE(lapack_getrf)
2929
ADD_EXAMPLE(lstsq1)
3030
ADD_EXAMPLE(lstsq2)
3131
ADD_EXAMPLE(norm)
32+
ADD_EXAMPLE(mnorm)
3233
ADD_EXAMPLE(get_norm)
3334
ADD_EXAMPLE(solve1)
3435
ADD_EXAMPLE(solve2)

example/linalg/example_mnorm.f90

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
program example_mnorm
2+
use stdlib_linalg, only: mnorm
3+
use stdlib_kinds, only: sp
4+
implicit none
5+
real(sp) :: a(3,3), na
6+
real(sp) :: b(3,3,4), nb(4) ! Array of 4 3x3 matrices
7+
8+
! Initialize example matrix
9+
a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
10+
11+
! Compute Euclidean norm of single matrix
12+
na = mnorm(a, 'Euclidean')
13+
print *, "Euclidean norm of matrix a:", na
14+
15+
! Initialize array of matrices
16+
b(:,:,1) = a
17+
b(:,:,2) = 2*a
18+
b(:,:,3) = 3*a
19+
b(:,:,4) = 4*a
20+
21+
! Compute infinity norm of each 3x3 matrix in b
22+
nb = mnorm(b, 'inf', dim=[1,2])
23+
24+
! 18.0000000 36.0000000 54.0000000 72.0000000
25+
print *, "Infinity norms of matrices in b:", nb
26+
end program example_mnorm

include/common.fypp

+45-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,22 @@ $:"s" if cmplx=="c" else "d" if cmplx=="z" else "x" if cmplx=="y" else "q" if cm
157157
#{if rank > 0}#(${"0" + ",0" * (rank - 1)}$)#{endif}#
158158
#:enddef
159159

160+
#! Generates an array rank suffix with a fixed integer size for all dimensions.
161+
#!
162+
#! Args:
163+
#! rank (int): Rank of the variable
164+
#! size (int): Size along each dimension
165+
#!
166+
#! Returns:
167+
#! Array rank suffix string
168+
#! E.g.,
169+
#! fixedranksuffix(3,4)
170+
#! -> (4,4,4)
171+
#!
172+
#:def fixedranksuffix(rank,size)
173+
#{if rank > 0}#(${str(size) + (","+str(size)) * (rank - 1)}$)#{endif}#
174+
#:enddef
175+
160176
#! Joins stripped lines with given character string
161177
#!
162178
#! Args:
@@ -227,7 +243,7 @@ ${prefix + joinstr.join([line.strip() for line in txt.split("\n")]) + suffix}$
227243
#! Array rank suffix string enclosed in braces
228244
#!
229245
#! E.g.,
230-
#! select_subarray(5 , [(4, 'i'), (5, 'j')])}$
246+
#! select_subarray(5 , [(4, 'i'), (5, 'j')])
231247
#! -> (:, :, :, i, j)
232248
#!
233249
#:def select_subarray(rank, selectors)
@@ -327,6 +343,34 @@ ${prefix + joinstr.join([line.strip() for line in txt.split("\n")]) + suffix}$
327343
#:endcall
328344
#:enddef
329345

346+
#!
347+
#! Generates a list of loop variables from an array
348+
#!
349+
#! Args:
350+
#! varname(str): Name of the array variable to be used as prefix
351+
#! n (int): Number of loop variables to be created
352+
#! offset (int): Optional index offset
353+
#!
354+
#! Returns:
355+
#! Variable definition string
356+
#!
357+
#! E.g.,
358+
#! loop_array_variables('j', 5)
359+
#! -> "j(1), j(2), j(3), j(4), j(5)
360+
#!
361+
#! loop_array_variables('j', 5, 2)
362+
#! -> "j(3), j(4), j(5), j(6), j(7)
363+
#!
364+
#:def loop_array_variables(varname, n, offset=0)
365+
#:assert n > 0
366+
#:call join_lines(joinstr=", ")
367+
#:for i in range(1, n + 1)
368+
${varname}$(${i+offset}$)
369+
#:endfor
370+
#:endcall
371+
#:enddef
372+
373+
330374
#! Generates an array shape specifier from an N-D array size
331375
#!
332376
#! Args:

src/stdlib_linalg.fypp

+84
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module stdlib_linalg
3232
public :: lstsq
3333
public :: lstsq_space
3434
public :: norm
35+
public :: mnorm
3536
public :: get_norm
3637
public :: solve
3738
public :: solve_lu
@@ -1320,6 +1321,89 @@ module stdlib_linalg
13201321
#:endfor
13211322
end interface get_norm
13221323

1324+
!> Matrix norms: function interface
1325+
interface mnorm
1326+
!! version: experimental
1327+
!!
1328+
!! Computes the matrix norm of a generic-rank array \( A \).
1329+
!! ([Specification](../page/specs/stdlib_linalg.html#mnorm-computes-the-matrix-norm-of-a-generic-rank-array))
1330+
!!
1331+
!!### Summary
1332+
!! Return one of several matrix norm metrics of a `real` or `complex` input array \( A \),
1333+
!! that can have rank 2 or higher. For rank-2 arrays, the matrix norm is returned.
1334+
!! If rank>2 and the optional input dimensions `dim` are specified,
1335+
!! a rank `n-2` array is returned with dimensions `dim(1),dim(2)` collapsed, containing all
1336+
!! matrix norms evaluated over the specified dimensions only. `dim==[1,2]` are assumed as default
1337+
!! dimensions if not specified.
1338+
!!
1339+
!!### Description
1340+
!!
1341+
!! This interface provides methods for computing the matrix norm(s) of an array.
1342+
!! Supported data types include `real` and `complex`.
1343+
!! Input arrays must have rank >= 2.
1344+
!!
1345+
!! Norm type input is optional, and it is provided via the `order` argument.
1346+
!! This can be provided as either an `integer` value or a `character` string.
1347+
!! Allowed metrics are:
1348+
!! - 1-norm: `order` = 1 or '1'
1349+
!! - 2-norm: `order` = 2 or '2'
1350+
!! - Euclidean/Frobenius: `order` = 'Euclidean','Frobenius', or argument not specified
1351+
!! - Infinity norm: `order` = huge(0) or 'Inf'
1352+
!!
1353+
!! If an invalid norm type is provided, the routine returns an error state.
1354+
!!
1355+
!!### Example
1356+
!!
1357+
!!```fortran
1358+
!! real(sp) :: a(3,3), na
1359+
!! real(sp) :: b(3,3,4), nb(4) ! Array of 4 3x3 matrices
1360+
!! a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
1361+
!!
1362+
!! ! Euclidean/Frobenius norm of single matrix
1363+
!! na = mnorm(a)
1364+
!! na = mnorm(a, 'Euclidean')
1365+
!!
1366+
!! ! 1-norm of each 3x3 matrix in b
1367+
!! nb = mnorm(b, 1, dim=[1,2])
1368+
!!
1369+
!! ! Infinity-norm
1370+
!! na = mnorm(b, 'inf', dim=[3,2])
1371+
!!```
1372+
!!
1373+
#:for rk,rt,ri in RC_KINDS_TYPES
1374+
#:for it,ii in NORM_INPUT_OPTIONS
1375+
1376+
!> Matrix norms: ${rt}$ rank-2 arrays
1377+
module function matrix_norm_${ii}$_${ri}$(a, order, err) result(nrm)
1378+
!> Input matrix a(m,n)
1379+
${rt}$, intent(in), target :: a(:,:)
1380+
!> Norm of the matrix.
1381+
real(${rk}$) :: nrm
1382+
!> Order of the matrix norm being computed.
1383+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
1384+
!> [optional] state return flag. On error if not requested, the code will stop
1385+
type(linalg_state_type), intent(out), optional :: err
1386+
end function matrix_norm_${ii}$_${ri}$
1387+
1388+
!> Matrix norms: ${rt}$ higher rank arrays
1389+
#:for rank in range(3, MAXRANK + 1)
1390+
module function matrix_norm_${rank}$D_to_${rank-2}$D_${ii}$_${ri}$(a, order, dim, err) result(nrm)
1391+
!> Input matrix a(m,n)
1392+
${rt}$, intent(in), contiguous, target :: a${ranksuffix(rank)}$
1393+
!> Norm of the matrix.
1394+
real(${rk}$), allocatable :: nrm${ranksuffix(rank-2)}$
1395+
!> Order of the matrix norm being computed.
1396+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
1397+
!> [optional] dimensions of the sub-matrices the norms should be evaluated at (default = [1,2])
1398+
integer(ilp), optional, intent(in) :: dim(2)
1399+
!> [optional] state return flag. On error if not requested, the code will stop
1400+
type(linalg_state_type), intent(out), optional :: err
1401+
end function matrix_norm_${rank}$D_to_${rank-2}$D_${ii}$_${ri}$
1402+
#:endfor
1403+
#:endfor
1404+
#:endfor
1405+
end interface mnorm
1406+
13231407
contains
13241408

13251409

0 commit comments

Comments
 (0)