Skip to content

Commit 407798c

Browse files
authored
Provide state/error handling for linear algebra (#774)
2 parents e19d4b6 + dfd03fc commit 407798c

9 files changed

+701
-6
lines changed

Diff for: doc/specs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This is an index/directory of the specifications (specs) for each new module/fea
2222
- [io](./stdlib_io.html) - Input/output helper & convenience
2323
- [kinds](./stdlib_kinds.html) - Kind parameters
2424
- [linalg](./stdlib_linalg.html) - Linear Algebra
25+
- [linalg_state_type](./stdlib_linalg_state_type.html) - Linear Algebra state and error handling
2526
- [logger](./stdlib_logger.html) - Runtime logging system
2627
- [math](./stdlib_math.html) - General purpose mathematical functions
2728
- [optval](./stdlib_optval.html) - Fallback value for optional arguments

Diff for: doc/specs/stdlib_linalg_state_type.md

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: linalg_state_type
3+
---
4+
5+
# Linear Algebra -- State and Error Handling Module
6+
7+
[TOC]
8+
9+
## Introduction
10+
11+
The `stdlib_linalg_state` module provides a derived type holding information on the
12+
state of linear algebra operations, and procedures for expert control of linear algebra workflows.
13+
All linear algebra procedures are engineered to support returning an optional `linalg_state_type`
14+
variable to holds such information, as a form of expert API. If the user does not require state
15+
information, but fatal errors are encountered during the execution of linear algebra routines, the
16+
program will undergo a hard stop.
17+
Instead, if the state argument is present, the program will never stop, but will return detailed error
18+
information into the state handler.
19+
20+
## Derived types provided
21+
22+
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
23+
### The `linalg_state_type` derived type
24+
25+
The `linalg_state_type` is defined as a derived type containing an integer error flag, and
26+
fixed-size character strings to store an error message and the location of the error state change.
27+
Fixed-size string storage was chosen to facilitate the compiler's memory allocation and ultimately
28+
ensure maximum computational performance.
29+
30+
A similarly named generic interface, `linalg_state_type`, is provided to allow the developer to
31+
create diagnostic messages and raise error flags easily. The call starts with an error flag or
32+
the location of the event, and is followed by an arbitrary list of `integer`, `real`, `complex` or
33+
`character` variables. Numeric variables may be provided as either scalars or rank-1 (array) inputs.
34+
35+
#### Type-bound procedures
36+
37+
The following convenience type-bound procedures are provided:
38+
- `print()` returns an allocatable character string containing state location, message, and error flag;
39+
- `print_message()` returns an allocatable character string containing the state message;
40+
- `ok()` returns a `logical` flag that is `.true.` in case of successful state (`flag==LINALG_SUCCESS`);
41+
- `error()` returns a `logical` flag that is `.true.` in case of error state (`flag/=LINALG_SUCCESS`).
42+
43+
#### Status
44+
45+
Experimental
46+
47+
#### Example
48+
49+
```fortran
50+
{!example/linalg/example_state1.f90!}
51+
```
52+
53+
## Error flags provided
54+
55+
The module provides the following state flags:
56+
- `LINALG_SUCCESS`: Successful execution
57+
- `LINALG_VALUE_ERROR`: Numerical errors (such as infinity, not-a-number, range bounds) are encountered.
58+
- `LINALG_ERROR`: Linear Algebra errors are encountered, such as: non-converging iterations, impossible operations, etc.
59+
- `LINALG_INTERNAL_ERROR`: Provided as a developer safeguard for internal errors that should never occur.
60+
61+
## Comparison operators provided
62+
63+
The module provides overloaded comparison operators for all comparisons of a `linalg_state_type` variable
64+
with an integer error flag: `<`, `<=`, `==`, `>=`, `>`, `/=`.

Diff for: example/linalg/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ ADD_EXAMPLE(is_symmetric)
1414
ADD_EXAMPLE(is_triangular)
1515
ADD_EXAMPLE(outer_product)
1616
ADD_EXAMPLE(trace)
17+
ADD_EXAMPLE(state1)
18+
ADD_EXAMPLE(state2)
19+
ADD_EXAMPLE(blas_gemv)
20+
ADD_EXAMPLE(lapack_getrf)

Diff for: example/linalg/example_state1.f90

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
program example_state1
2+
use stdlib_linalg_state, only: linalg_state_type, LINALG_SUCCESS, LINALG_VALUE_ERROR, &
3+
operator(/=)
4+
implicit none
5+
type(linalg_state_type) :: err
6+
7+
! To create a state variable, we enter its integer state flag, followed by a list of variables
8+
! that will be automatically assembled into a formatted error message. No need to provide string formats
9+
err = linalg_state_type(LINALG_VALUE_ERROR,'just an example with scalar ',&
10+
'integer=',1,'real=',2.0,'complex=',(3.0,1.0),'and array ',[1,2,3],'inputs')
11+
12+
! Print flag
13+
print *, err%print()
14+
15+
! Check success
16+
print *, 'Check error: ',err%error()
17+
print *, 'Check flag : ',err /= LINALG_SUCCESS
18+
19+
20+
end program example_state1

Diff for: example/linalg/example_state2.f90

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
program example_state2
2+
!! This example shows how to set a `type(linalg_state_type)` variable to process output conditions
3+
!! out of a simple division routine. The example is meant to highlight:
4+
!! 1) the different mechanisms that can be used to initialize the `linalg_state` variable providing
5+
!! strings, scalars, or arrays, on input to it;
6+
!! 2) `pure` setup of the error control
7+
use stdlib_linalg_state, only: linalg_state_type, LINALG_VALUE_ERROR, LINALG_SUCCESS, &
8+
linalg_error_handling
9+
implicit none
10+
integer :: info
11+
type(linalg_state_type) :: err
12+
real :: a_div_b
13+
14+
! OK
15+
call very_simple_division(0.0,2.0,a_div_b,err)
16+
print *, err%print()
17+
18+
! Division by zero
19+
call very_simple_division(1.0,0.0,a_div_b,err)
20+
print *, err%print()
21+
22+
! Out of bounds
23+
call very_simple_division(huge(0.0),0.001,a_div_b,err)
24+
print *, err%print()
25+
26+
contains
27+
28+
!> Simple division returning an integer flag (LAPACK style)
29+
elemental subroutine very_simple_division(a,b,a_div_b,err)
30+
real, intent(in) :: a,b
31+
real, intent(out) :: a_div_b
32+
type(linalg_state_type), optional, intent(out) :: err
33+
34+
type(linalg_state_type) :: err0
35+
real, parameter :: MAXABS = huge(0.0)
36+
character(*), parameter :: this = 'simple division'
37+
38+
!> Check a
39+
if (b==0.0) then
40+
! Division by zero
41+
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'Division by zero trying ',a,'/',b)
42+
elseif (.not.abs(b)<MAXABS) then
43+
! B is out of bounds
44+
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'B is infinity in a/b: ',[a,b]) ! use an array
45+
elseif (.not.abs(a)<MAXABS) then
46+
! A is out of bounds
47+
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'A is infinity in a/b: a=',a,' b=',b)
48+
else
49+
a_div_b = a/b
50+
if (.not.abs(a_div_b)<MAXABS) then
51+
! Result is out of bounds
52+
err0 = linalg_state_type(this,LINALG_VALUE_ERROR,'A/B is infinity in a/b: a=',a,' b=',b)
53+
else
54+
err0%state = LINALG_SUCCESS
55+
end if
56+
end if
57+
58+
! Return error flag, or hard stop on failure
59+
call linalg_error_handling(err0,err)
60+
61+
end subroutine very_simple_division
62+
63+
64+
end program example_state2

Diff for: src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ set(fppFiles
2424
stdlib_linalg_outer_product.fypp
2525
stdlib_linalg_kronecker.fypp
2626
stdlib_linalg_cross_product.fypp
27+
stdlib_linalg_state.fypp
2728
stdlib_optval.fypp
2829
stdlib_selection.fypp
2930
stdlib_sorting.fypp

Diff for: src/stdlib_linalg_constants.fypp

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ module stdlib_linalg_constants
77
public
88

99

10-
1110
! Integer size support for ILP64 builds should be done here
1211
integer, parameter :: ilp = int32
1312
private :: int32, int64

0 commit comments

Comments
 (0)