Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linalg: vector norms #871

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open

linalg: vector norms #871

wants to merge 33 commits into from

Conversation

perazz
Copy link
Contributor

@perazz perazz commented Sep 13, 2024

Address #820 in joint effort with @jalvesz.
Compute several vector norms. Array $A$, real or complex, has general rank n>=1.

Proposed implementation

  • x = norm(a, order=2 [, dim=dim] [, err=err]): (pure) function interface
  • call get_norm(a, nrm=x, order=2 [, dim=dim] [, err=err]): pure subroutine interface

Key facts

  • The following implementations are provided:

    • 1, '1': 1-norm, $\sum_i{ \left|a_i\right| }$
    • 2, '2', 'Euclidean': 2-norm, $\sqrt{\sum_i{ a_i^2 }}$
    • >=3: p-norm, $\left( \sum_i{ \left|a_i\right| ^p }\right) ^{1/p}$
    • 'inf', huge(0): $\infty$-norm, $\max_i{ \left|a_i\right| }$
    • '-inf', -huge(0): minimum norm, $\min_i{ \left|a_i\right| }$
  • order can either be an integer (1, 2, ..., n, -huge(0), huge(0)) or a character input ('1', '2', '10', 'inf', '-inf', 'euclidean', ...)

  • The implementation currently only uses Fortran intrinsics that handle all dim cases by default: would be hard to unroll all them to enable calls to BLAS backends for all ranks 1:15.

Progress

  • base implementation
  • tests
  • documentation
  • submodule
  • examples
  • all pure subroutine interfaces, pure function interfaces if no error flag is requested.

Prior art

  • Numpy: linalg.norm(x, ord=None, axis=None, keepdims=False)
  • Scipy: norm(a, ord=None, axis=None, keepdims=False, check_finite=True)

cc: @jvdp1 @jalvesz @loiseaujc

@perazz perazz marked this pull request as ready for review September 13, 2024 11:43
Copy link
Member

@jvdp1 jvdp1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM. Thank you @perazz and @jalvesz .
It could be worthwhile to add a note in the specs to highlight the differences (or not) with the intrinsic norm2.

doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
src/stdlib_linalg.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
@perazz
Copy link
Contributor Author

perazz commented Sep 15, 2024

@jvdp1 @jalvesz in 437b96e I've introduced the intrinsic norm2 where possible (for real inputs). This is going to be one of the most used norms. So I'm wondering if we should replace it with a call to the BLAS backend. this:

  • would allow to use a fast implementation, if available
  • for the N-D case (with dim), some data sorting cannot be avoided.

So, should we introduce the BLAS backend at least for the 1D case evaluation?

@awvwgk
Copy link
Member

awvwgk commented Sep 15, 2024

I believe the BLAS API should allow also strided access to array elements, which would support dim != 1, the stride length would need to be computed based on the selected axis and the dimensions.

- add nonstandard-named `complex` norms to `nrm2` interface
- test sliced and reshaped 2-norm
@perazz
Copy link
Contributor Author

perazz commented Sep 18, 2024

The compiler will create a temporary array passing from (:) (may be strided) to the (*) BLAS API, because the latter implies contiguous data. When that will happen is nontrivial. So, it is possible to infer the stride of an (:) array like I did, but there is no guarantee that the compiler will not create a temporary array in between the calls. See this example.

So, in the interest of safety, I suggest to avoid inferring strides and rather create the temporary ourselves, i.e. preprocessing it with a reshape(a, order=sorted_dims)

doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_norms.fypp Outdated Show resolved Hide resolved
example/linalg/example_get_norm.f90 Outdated Show resolved Hide resolved
example/linalg/example_get_norm.f90 Outdated Show resolved Hide resolved
example/linalg/example_norm.f90 Outdated Show resolved Hide resolved
@perazz
Copy link
Contributor Author

perazz commented Sep 27, 2024

  • unify calls to norm evaluation function
  • $+\infty$ norm: replace maxval(abs(.)) with calls to BLAS, add test
  • ND and dim-med versions: always call LAPACK/BLAS rather than Fortran intrinsics
  • address contiguity via reshape(a, order=sorted_dims)

@jalvesz
Copy link
Contributor

jalvesz commented Oct 2, 2024

LGTM @perazz! On my end I have no further comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants