Skip to content

Commit 753e31b

Browse files
committed
sync task with NumPy: no order-2, default=Frobenius
1 parent 8b98031 commit 753e31b

File tree

4 files changed

+78
-38
lines changed

4 files changed

+78
-38
lines changed

doc/specs/stdlib_linalg.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,12 +1585,12 @@ matrix norms are computed over specified dimensions.
15851585

15861586
`a`: Shall be a rank-n `real` or `complex` array containing the data, where n ³ 2. It is an `intent(in)` argument.
15871587

1588-
`order`: Shall be an `integer` value or a `character` flag that specifies the norm type, as follows. It is an `intent(in)` argument.
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.
15891589

15901590
| Integer input | Character Input | Norm type |
15911591
|------------------|---------------------------|---------------------------------------------------------|
15921592
| `1` | `'1'` | 1-norm (maximum column sum) \( \max_j \sum_i{ \left|a_{i,j}\right| } \) |
1593-
| `2` | `'2','Euclidean'` | 2-norm/Euclidean/Frobenius norm \( \sqrt{\sum_{i,j}{ \left|a_{i,j}\right|^2 }} \) |
1593+
| (not prov.) | `'Euclidean','Frobenius','Fro'` | Frobenius norm \( \sqrt{\sum_{i,j}{ \left|a_{i,j}\right|^2 }} \) |
15941594
| `huge(0)` | `'inf', 'Inf', 'INF'` | Infinity norm (maximum row sum) \( \max_i \sum_j{ \left|a_{i,j}\right| } \) |
15951595

15961596
`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.

src/stdlib_linalg.fypp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,8 @@ module stdlib_linalg
13331333
!! that can have rank 2 or higher. For rank-2 arrays, the matrix norm is returned.
13341334
!! If rank>2 and the optional input dimensions `dim` are specified,
13351335
!! 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.
1336+
!! matrix norms evaluated over the specified dimensions only. `dim==[1,2]` are assumed as default
1337+
!! dimensions if not specified.
13371338
!!
13381339
!!### Description
13391340
!!
@@ -1345,12 +1346,10 @@ module stdlib_linalg
13451346
!! This can be provided as either an `integer` value or a `character` string.
13461347
!! Allowed metrics are:
13471348
!! - 1-norm: `order` = 1 or '1'
1348-
!! - 2-norm/Euclidean: `order` = 2 or '2' or 'Euclidean'
1349-
!! - p-norm: `order` >= 3
1349+
!! - Euclidean/Frobenius: `order` = 'Euclidean','Frobenius', or argument not specified
13501350
!! - Infinity norm: `order` = huge(0) or 'Inf'
1351-
!! - Minus-infinity norm: `order` = '-Inf'
13521351
!!
1353-
!! If an invalid norm type is provided, the routine defaults to 1-norm and returns an error state.
1352+
!! If an invalid norm type is provided, the routine returns an error state.
13541353
!!
13551354
!!### Example
13561355
!!
@@ -1359,14 +1358,15 @@ module stdlib_linalg
13591358
!! real(sp) :: b(3,3,4), nb(4) ! Array of 4 3x3 matrices
13601359
!! a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
13611360
!!
1362-
!! ! 2-norm/Euclidean norm of single matrix
1361+
!! ! Euclidean/Frobenius norm of single matrix
1362+
!! na = mnorm(a)
13631363
!! na = mnorm(a, 'Euclidean')
13641364
!!
13651365
!! ! 1-norm of each 3x3 matrix in b
13661366
!! nb = mnorm(b, 1, dim=[1,2])
13671367
!!
1368-
!! ! p-norm with p=3
1369-
!! na = mnorm(a, 3)
1368+
!! ! Infinity-norm
1369+
!! na = mnorm(b, 'inf', dim=[3,2])
13701370
!!```
13711371
!!
13721372
#:for rk,rt,ri in RC_KINDS_TYPES
@@ -1379,7 +1379,7 @@ module stdlib_linalg
13791379
!> Norm of the matrix.
13801380
real(${rk}$) :: nrm
13811381
!> Order of the matrix norm being computed.
1382-
${it}$, intent(in) :: order
1382+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
13831383
!> [optional] state return flag. On error if not requested, the code will stop
13841384
type(linalg_state_type), intent(out), optional :: err
13851385
end function matrix_norm_${ii}$_${ri}$
@@ -1392,7 +1392,7 @@ module stdlib_linalg
13921392
!> Norm of the matrix.
13931393
real(${rk}$), allocatable :: nrm${ranksuffix(rank-2)}$
13941394
!> Order of the matrix norm being computed.
1395-
${it}$, intent(in) :: order
1395+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
13961396
!> [optional] dimensions of the sub-matrices the norms should be evaluated at (default = [1,2])
13971397
integer(ilp), optional, intent(in) :: dim(2)
13981398
!> [optional] state return flag. On error if not requested, the code will stop

src/stdlib_linalg_norms.fypp

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ submodule(stdlib_linalg) stdlib_linalg_norms
2929
character, parameter :: LANGE_NORM_MAT = 'M' ! maxval(sum(abs(a))) ! over whole matrix: unused
3030
character, parameter :: LANGE_NORM_ONE = '1' ! maxval(sum(abs(a),1)) ! over columns
3131
character, parameter :: LANGE_NORM_INF = 'I' ! maxval(sum(abs(a),2)) ! over rows
32-
character, parameter :: LANGE_NORM_TWO = 'E' ! "Euclidean" or "Frobenius"
32+
character, parameter :: LANGE_NORM_FRO = 'E' ! sqrt(sum(a**2)) ! "Euclidean" or "Frobenius"
3333

3434
interface parse_norm_type
3535
module procedure parse_norm_type_integer
3636
module procedure parse_norm_type_character
3737
end interface parse_norm_type
3838

39+
interface lange_task_request
40+
module procedure lange_task_request_integer
41+
module procedure lange_task_request_character
42+
end interface lange_task_request
43+
3944

4045
interface stride_1d
4146
#:for rk,rt,ri in ALL_KINDS_TYPES
@@ -107,25 +112,61 @@ submodule(stdlib_linalg) stdlib_linalg_norms
107112
end subroutine parse_norm_type_character
108113

109114
!> From a user norm request, generate a *LANGE task command
110-
pure subroutine lange_task_request(norm_type,lange_task,err)
115+
pure subroutine lange_task_request_integer(order,lange_task,err)
111116
!> Parsed matrix norm type
112-
integer(ilp), intent(in) :: norm_type
117+
integer(ilp), optional, intent(in) :: order
113118
!> LANGE task
114119
character, intent(out) :: lange_task
115120
!> Error flag
116121
type(linalg_state_type), intent(inout) :: err
117122

118-
select case (norm_type)
119-
case (NORM_INF)
120-
lange_task = LANGE_NORM_INF
121-
case (NORM_ONE)
123+
if (present(order)) then
124+
125+
select case (order)
126+
case (NORM_INF)
127+
lange_task = LANGE_NORM_INF
128+
case (NORM_ONE)
129+
lange_task = LANGE_NORM_ONE
130+
case default
131+
err = linalg_state_type(this,LINALG_VALUE_ERROR,'Integer order ',order,' is not a valid matrix norm input.')
132+
end select
133+
134+
else
135+
136+
! No user input: Frobenius norm
137+
lange_task = LANGE_NORM_FRO
138+
139+
endif
140+
end subroutine lange_task_request_integer
141+
142+
pure subroutine lange_task_request_character(order,lange_task,err)
143+
!> User input value
144+
character(len=*), intent(in) :: order
145+
!> Return value: norm type
146+
character, intent(out) :: lange_task
147+
!> State return flag
148+
type(linalg_state_type), intent(out) :: err
149+
150+
integer(ilp) :: int_order,read_err
151+
152+
select case (order)
153+
case ('inf','Inf','INF')
154+
lange_task = LANGE_NORM_INF
155+
case ('Euclidean','euclidean','EUCLIDEAN','Frobenius','frobenius','FROBENIUS','Fro','fro','frob')
156+
lange_task = LANGE_NORM_FRO
157+
case default
158+
159+
! Check if this input can be read as an integer
160+
read(order,*,iostat=read_err) int_order
161+
if (read_err/=0 .or. int_order/=1) then
162+
! Cannot read as an integer
163+
err = linalg_state_type(this,LINALG_ERROR,'Matrix norm input',order,' is not recognized.')
164+
endif
122165
lange_task = LANGE_NORM_ONE
123-
case (NORM_TWO)
124-
lange_task = LANGE_NORM_TWO
125-
case default
126-
err = linalg_state_type(this,LINALG_VALUE_ERROR,'Order ',norm_type,' is not a valid matrix norm input.')
127-
end select
128-
end subroutine lange_task_request
166+
167+
end select
168+
169+
end subroutine lange_task_request_character
129170

130171
#:for rk,rt,ri in ALL_KINDS_TYPES
131172

@@ -399,12 +440,12 @@ ${loop_variables_end(rank-1," "*12)}$
399440
!> Norm of the matrix.
400441
real(${rk}$) :: nrm
401442
!> Order of the matrix norm being computed.
402-
${it}$, intent(in) :: order
443+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
403444
!> [optional] state return flag. On error if not requested, the code will stop
404445
type(linalg_state_type), intent(out), optional :: err
405446

406447
type(linalg_state_type) :: err_
407-
integer(ilp) :: m,n,norm_request
448+
integer(ilp) :: m,n
408449
character :: lange_task
409450
real(${rk}$), target :: work1(1)
410451
real(${rk}$), pointer :: work(:)
@@ -422,8 +463,7 @@ ${loop_variables_end(rank-1," "*12)}$
422463
end if
423464

424465
! Check norm request: user + *LANGE support
425-
call parse_norm_type(order,norm_request,err_)
426-
call lange_task_request(norm_request,lange_task,err_)
466+
call lange_task_request(order,lange_task,err_)
427467
if (err_%error()) then
428468
call linalg_error_handling(err_,err)
429469
return
@@ -451,7 +491,7 @@ ${loop_variables_end(rank-1," "*12)}$
451491
!> Norm of the matrix.
452492
real(${rk}$), allocatable :: nrm${ranksuffix(rank-2)}$
453493
!> Order of the matrix norm being computed.
454-
${it}$, intent(in) :: order
494+
${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
455495
!> [optional] dimensions of the sub-matrices the norms should be evaluated at (default = [1,2])
456496
integer(ilp), optional, intent(in) :: dim(2)
457497
!> [optional] state return flag. On error if not requested, the code will stop
@@ -485,8 +525,7 @@ ${loop_variables_end(rank-1," "*12)}$
485525
endif
486526

487527
! Check norm request: user + *LANGE support
488-
call parse_norm_type(order,norm_request,err_)
489-
call lange_task_request(norm_request,lange_task,err_)
528+
call lange_task_request(order,lange_task,err_)
490529
if (err_%error()) then
491530
allocate(nrm${emptyranksuffix(rank-2)}$)
492531
call linalg_error_handling(err_,err)

test/linalg/test_linalg_mnorm.fypp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ module test_linalg_mnorm
4949
if (allocated(error)) return
5050

5151
! 2-norm (Frobenius norm)
52-
call check(error, abs(mnorm(A, '2', err) - sqrt(sum(A**2))) < tol*mnorm(A, '2', err), &
52+
call check(error, abs(mnorm(A, err=err) - sqrt(sum(A**2))) < tol*mnorm(A, err=err), &
5353
'Matrix Frobenius norm does not match expected value')
5454
if (allocated(error)) return
5555

@@ -65,15 +65,16 @@ module test_linalg_mnorm
6565
subroutine test_mnorm_${ri}$_${rank}$d(error)
6666
type(error_type), allocatable, intent(out) :: error
6767

68-
integer(ilp) :: i,j,k,l,dim1,dim2,dim(2),dim_sizes(2),order,ptr(${rank}$)
69-
integer(ilp), parameter :: orders(*) = [1_ilp,2_ilp,huge(0_ilp)]
68+
integer(ilp) :: i,j,k,l,dim1,dim2,dim(2),dim_sizes(2),ptr(${rank}$)
69+
character(3), parameter :: orders(*) = ['1 ','fro','inf']
7070
integer(ilp), parameter :: ndim = ${rank}$
7171
integer(ilp), parameter :: n = 2_ilp**ndim
7272
integer(ilp), parameter :: dims(*) = [(dim1, dim1=1,ndim)]
7373
real(${rk}$), parameter :: tol = 10*sqrt(epsilon(0.0_${rk}$))
7474
real(${rk}$) :: one_nrm
7575
real(${rk}$), allocatable :: bnrm${ranksuffix(rank-2)}$
7676
${rt}$, allocatable :: a(:), b${ranksuffix(rank)}$, one_mat(:,:)
77+
character(:), allocatable :: order
7778

7879
character(64) :: msg
7980

@@ -86,7 +87,7 @@ module test_linalg_mnorm
8687

8788
! Test norm as collapsed around dimensions
8889
do k = 1, size(orders)
89-
order = orders(k)
90+
order = trim(orders(k))
9091
do dim1 = 1, ndim
9192
do dim2 = dim1+1, ndim
9293

@@ -97,7 +98,7 @@ module test_linalg_mnorm
9798
bnrm = mnorm(b,order,dim)
9899

99100
! Assert size
100-
write(msg,"('dim=[',i0,',',i0,'] order=',i0,' ${rk}$ norm returned wrong shape')") dim, order
101+
write(msg,"('dim=[',i0,',',i0,'] order=',a,' ${rk}$ norm returned wrong shape')") dim, order
101102
call check(error,all(shape(bnrm)==pack(shape(b),dims/=dim1 .and. dims/=dim2) ), trim(msg))
102103
if (allocated(error)) return
103104

@@ -117,7 +118,7 @@ module test_linalg_mnorm
117118
end do
118119
one_nrm = mnorm(one_mat,order)
119120

120-
write(msg,"('dim=[',i0,',',i0,'] order=',i0,' ${rk}$ ',i0,'-th norm is wrong')") dim, order, l
121+
write(msg,"('dim=[',i0,',',i0,'] order=',a,' ${rk}$ ',i0,'-th norm is wrong')") dim, order, l
121122
call check(error, abs(one_nrm-bnrm(${fixedranksuffix(rank-2,'l')}$))<tol*one_nrm, trim(msg))
122123
if (allocated(error)) return
123124
deallocate(one_mat)

0 commit comments

Comments
 (0)