Skip to content

Commit 9dea5a0

Browse files
authored
add schmidt normalization (#15)
* add schmidt normalization * update docs with norm details * mention Ambix norm
1 parent 22ad408 commit 9dea5a0

File tree

5 files changed

+264
-101
lines changed

5 files changed

+264
-101
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "LegendrePolynomials"
22
uuid = "3db4a2ba-fc88-11e8-3e01-49c72059a882"
3-
version = "0.4.2"
3+
version = "0.4.3"
44

55
[deps]
66
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"

docs/src/index.md

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ DocTestSetup = quote
55
end
66
```
77

8-
# Introduction
8+
## Definition
99

10-
Compute [Legendre polynomials](https://en.wikipedia.org/wiki/Legendre_polynomials), [Associated Legendre polynomials](https://en.wikipedia.org/wiki/Associated_Legendre_polynomials), and their derivatives using a 3-term recursion relation (Bonnet’s recursion formula).
10+
### Legendre polynomials
11+
12+
[Legendre polynomials](https://en.wikipedia.org/wiki/Legendre_polynomials) satisfy the differential equation
1113

1214
```math
13-
P_\ell(x) = \left((2\ell-1) x P_{\ell-1}(x) - (\ell-1)P_{\ell - 2}(x)\right)/\ell
15+
\frac{d}{dx}\left(\left(1-x^{2}\right)\frac{d}{dx}\right)P_{\ell}\left(x\right)=-\ell\left(\ell+1\right)P_{\ell}\left(x\right)
1416
```
1517

1618
By default this package evaluates the standard polynomials that satisfy ``P_\ell(1) = 1`` and ``P_0(x) = 1``. These are normalized as
@@ -19,43 +21,29 @@ By default this package evaluates the standard polynomials that satisfy ``P_\ell
1921
\int_{-1}^1 P_m(x) P_n(x) dx = \frac{2}{2n+1} \delta_{mn}.
2022
```
2123

22-
Optionally, normalized polynomials may be evaluated that have an L2 norm of `1`.
23-
24-
Analogous to Legendre polynomials, one may evaluate associated Legendre polynomials using a 3-term recursion relation. This is evaluated by iterating over the normalized associated Legendre functions, and multiplying the norm at the final stage. Such an iteration avoids floating-point overflow.
24+
### Associated Legendre polynomials
2525

26-
The relation used in evaluating normalized associated Legendre polynomials ``\bar{P}_{\ell}^{m}\left(x\right)`` is
27-
28-
```math
29-
\bar{P}_{\ell}^{m}\left(x\right)=\alpha_{\ell m}\left(x\bar{P}_{\ell-1}^{m}\left(x\right)-\frac{1}{\alpha_{\ell-1m}}\bar{P}_{\ell-2}^{m}\left(x\right)\right),
30-
```
26+
[Associated Legendre polynomials](https://en.wikipedia.org/wiki/Associated_Legendre_polynomials) satisfy the differential equation
3127

32-
where
3328
```math
34-
\alpha_{\ell m}=\sqrt{\frac{\left(2\ell+1\right)\left(2\ell-1\right)}{\left(\ell-m\right)\left(\ell+m\right)}},
29+
\left[\frac{d}{dx}\left(\left(1-x^{2}\right)\frac{d}{dx}\right)-\frac{m^{2}}{1-x^{2}}\right]P_{\ell}^{m}\left(x\right)=-\ell\left(\ell+1\right)P_{\ell}^{m}\left(x\right),
3530
```
3631

37-
The starting value for a specific ``m`` is obtained by setting ``\ell = m``, in which case we use the explicit relation
32+
and are related to the Legendre polynomials through
3833

3934
```math
40-
\bar{P}_{m}^{m}\left(x\right)=\left(-1\right)^{m}\left(2m-1\right)!!\sqrt{\frac{\left(2m+1\right)}{2\left(2m\right)!}}\left(1-x^{2}\right)^{\left(m/2\right)}.
35+
P_{\ell}^{m}\left(x\right)=\left(-1\right)^{m}\left(1-x^{2}\right)^{m/2}\frac{d^{m}}{dx^{m}}\left(P_\ell\left(x\right)\right).
4136
```
4237

43-
The polynomials, thus defined, include the Condon-Shortley phase ``(-1)^m``, and may be evaluated using the standard normalization as
38+
For ``m=0``, we obtain
4439

4540
```math
46-
P_{\ell}^{m}\left(x\right)=\sqrt{\frac{2\left(\ell+m\right)!}{\left(2\ell+1\right)\left(\ell-m\right)!}}\bar{P}_{\ell}^{m}\left(x\right).
41+
P_{\ell}^{0}\left(x\right)=P_\ell\left(x\right).
4742
```
4843

49-
There are six main functions:
50-
51-
* [`Pl(x,l; [norm = Val(:standard)])`](@ref Pl): this evaluates the Legendre polynomial for a given degree `l` at the argument `x`. The argument needs to satisfy `-1 <= x <= 1`.
52-
* [`collectPl(x; lmax, [lmin = 0], [norm = Val(:standard)])`](@ref collectPl): this evaluates all the polynomials for `l` lying in `0:lmax` at the argument `x`. As before the argument needs to lie in the domain of validity. Functionally this is equivalent to `Pl.(x, lmin:lmax)`, except `collectPl` evaluates the result in one pass, and is therefore faster. There is also the in-place version [`collectPl!`](@ref) that uses a pre-allocated array.
53-
* [`Plm(x, l, m; [norm = Val(:standard)])`](@ref Plm): this evaluates the associated Legendre polynomial ``P_\ell^m(x)`` at the argument ``x``. The argument needs to satisfy `-1 <= x <= 1`.
54-
* [`collectPlm(x; m, lmax, [lmin = abs(m)], [norm = Val(:standard)])`](@ref collectPlm): this evaluates the associated Legendre polynomials with coefficient `m` for `l = lmin:lmax`. There is also an in-place version [`collectPlm!`](@ref) that uses a pre-allocated array.
55-
* [`dnPl(x, l, n)`](@ref dnPl): this evaluates the ``n``-th derivative of the Legendre polynomial ``P_\ell(x)`` at the argument ``x``. The argument needs to satisfy `-1 <= x <= 1`.
56-
* [`collectdnPl(x; n, lmax)`](@ref collectdnPl): this evaluates the ``n``-th derivative of all the Legendre polynomials for `l = 0:lmax`. There is also an in-place version [`collectdnPl!`](@ref) that uses a pre-allocated array.
44+
The Condon-Shortley phase is included by default, and may be toggled by using the keyword argument `csphase`.
5745

58-
# Quick Start
46+
## Quick Start
5947

6048
Evaluate the Legendre polynomial for one `l` at an argument`x` as `Pl(x, l)`:
6149

@@ -120,7 +108,75 @@ julia> collectdnPl(0.5, lmax = 5, n = 3)
120108
65.625
121109
```
122110

123-
# Reference
111+
## Recursion relations
112+
113+
Compute [Legendre polynomials](https://en.wikipedia.org/wiki/Legendre_polynomials), [Associated Legendre polynomials](https://en.wikipedia.org/wiki/Associated_Legendre_polynomials), and their derivatives using a 3-term recursion relation (Bonnet’s recursion formula).
114+
115+
```math
116+
P_\ell(x) = \left((2\ell-1) x P_{\ell-1}(x) - (\ell-1)P_{\ell - 2}(x)\right)/\ell
117+
```
118+
119+
Analogous to Legendre polynomials, one may evaluate associated Legendre polynomials using a 3-term recursion relation. This is evaluated by iterating over the normalized associated Legendre functions, and multiplying the norm at the final stage. Such an iteration avoids floating-point overflow.
120+
121+
The relation used in evaluating normalized associated Legendre polynomials ``\bar{P}_{\ell}^{m}\left(x\right)`` is
122+
123+
```math
124+
\bar{P}_{\ell}^{m}\left(x\right)=\alpha_{\ell m}\left(x\bar{P}_{\ell-1}^{m}\left(x\right)-\frac{1}{\alpha_{\ell-1m}}\bar{P}_{\ell-2}^{m}\left(x\right)\right),
125+
```
126+
127+
where
128+
```math
129+
\alpha_{\ell m}=\sqrt{\frac{\left(2\ell+1\right)\left(2\ell-1\right)}{\left(\ell-m\right)\left(\ell+m\right)}},
130+
```
131+
132+
The starting value for a specific ``m`` is obtained by setting ``\ell = m``, in which case we use the explicit relation
133+
134+
```math
135+
\bar{P}_{m}^{m}\left(x\right)=\left(-1\right)^{m}\left(2m-1\right)!!\sqrt{\frac{\left(2m+1\right)}{2\left(2m\right)!}}\left(1-x^{2}\right)^{\left(m/2\right)}.
136+
```
137+
138+
The polynomials, thus defined, include the Condon-Shortley phase ``(-1)^m``, and may be evaluated using the standard normalization as
139+
140+
```math
141+
P_{\ell}^{m}\left(x\right)=\sqrt{\frac{2\left(\ell+m\right)!}{\left(2\ell+1\right)\left(\ell-m\right)!}}\bar{P}_{\ell}^{m}\left(x\right).
142+
```
143+
144+
## Main functions
145+
146+
There are six main functions:
147+
148+
* [`Pl(x,l; [norm = Val(:standard)])`](@ref Pl): this evaluates the Legendre polynomial for a given degree `l` at the argument `x`. The argument needs to satisfy `-1 <= x <= 1`.
149+
* [`collectPl(x; lmax, [lmin = 0], [norm = Val(:standard)])`](@ref collectPl): this evaluates all the polynomials for `l` lying in `0:lmax` at the argument `x`. As before the argument needs to lie in the domain of validity. Functionally this is equivalent to `Pl.(x, lmin:lmax)`, except `collectPl` evaluates the result in one pass, and is therefore faster. There is also the in-place version [`collectPl!`](@ref) that uses a pre-allocated array.
150+
* [`Plm(x, l, m; [norm = Val(:standard)], [csphase = true])`](@ref Plm): this evaluates the associated Legendre polynomial ``P_\ell^m(x)`` at the argument ``x``. The argument needs to satisfy `-1 <= x <= 1`.
151+
* [`collectPlm(x; m, lmax, [lmin = abs(m)], [norm = Val(:standard)], [csphase = true])`](@ref collectPlm): this evaluates the associated Legendre polynomials with coefficient `m` for `l = lmin:lmax`. There is also an in-place version [`collectPlm!`](@ref) that uses a pre-allocated array.
152+
* [`dnPl(x, l, n)`](@ref dnPl): this evaluates the ``n``-th derivative of the Legendre polynomial ``P_\ell(x)`` at the argument ``x``. The argument needs to satisfy `-1 <= x <= 1`.
153+
* [`collectdnPl(x; n, lmax)`](@ref collectdnPl): this evaluates the ``n``-th derivative of all the Legendre polynomials for `l = 0:lmax`. There is also an in-place version [`collectdnPl!`](@ref) that uses a pre-allocated array.
154+
155+
## Normalization options
156+
157+
By default, the Legendre and associated Legendre polynomials are not normalized.
158+
One may specify the normalization through the keyword argument `norm`.
159+
The normalization options accepted are
160+
161+
* `norm = Val(:standard)`: standard, unnormalized polynomials. This is the default option. Choosing this option will lead to the standard Legendre or associated Legendre polynomials that satisfy ``P_\ell(0)=1`` and ``P_{\ell0}(0)=1``
162+
* `norm = Val(:normalized)`: fully normalized polynomials, with an L2 norm of 1. These are defined as
163+
```math
164+
\bar{P}_{\ell m}\left(x\right)=\sqrt{\frac{2\ell+1}{2}\frac{\left(\ell-m\right)!}{\left(\ell+m\right)!}}P_{\ell m}\left(x\right)
165+
```
166+
* `norm = Val(:schmidtquasi)`: Schmidt quasi-normalized polynomials, also known as Schmidt semi-normalized polynomials. These have an L2 norm of ``\sqrt{2(2-\delta_{m0})/(2\ell+1)}``. These are defined as
167+
```math
168+
\bar{P}_{\ell m}\left(x\right)=\sqrt{\left(2-\delta_{m0}\right)\frac{\left(\ell-m\right)!}{\left(\ell+m\right)!}}P_{\ell m}\left(x\right)
169+
```
170+
* `norm = Val(:schmidt)`: Schmidt normalized polynomials. These have an L2 norm of ``\sqrt{2(2-\delta_{m0})}``. These are defined as
171+
```math
172+
\bar{P}_{\ell m}\left(x\right)=\sqrt{\left(2-\delta_{m0}\right)\left(2\ell+1\right)\frac{\left(\ell-m\right)!}{\left(\ell+m\right)!}}P_{\ell m}\left(x\right)
173+
```
174+
175+
!!! note
176+
Irrespective of the norm specified, the 3-term recursion relations used are stable ones,
177+
so polynomials of high degrees may be computed without overflow.
178+
179+
## Reference
124180

125181
```@autodocs
126182
Modules = [LegendrePolynomials]

docs/src/tutorial.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,7 @@ DocTestSetup = :(using LegendrePolynomials)
44

55
## Computing normalized Legendre polynomials
66

7-
By default, the Legendre and associated Legendre polynomials are not normalized.
8-
One may specify the normalization through the keyword argument `norm`.
9-
The normalization options accepted are
10-
11-
* `norm = Val(:standard)`: standard, unnormalized polynomials. This is the default option.
12-
* `norm = Val(:normalized)`: fully normalized polynomials with an L2 norm of 1
13-
14-
!!! note
15-
Irrespective of the norm specified, the 3-term recursion relations used are stable ones,
16-
so polynomials of high degrees may be computed without overflow.
7+
The norm of the polynomials may be specified through the keyword argument `norm`. The various normalization options are listed in the [Normalization options](@ref) section.
178

189
```jldoctest
1910
julia> l = 3;
@@ -33,6 +24,16 @@ julia> Plm(0.5, l, m, norm = Val(:normalized))
3324
2.172276347346834e-187
3425
```
3526

27+
Starting from these, other normalization such as the [Ambix SN3D format](https://en.wikipedia.org/wiki/Ambisonic_data_exchange_formats#SN3D) may be constructed as
28+
29+
```julia
30+
julia> Plmambix(x, l, m) = Plm(x, l, m, norm=Val(:schmidtquasi)) / (4pi)
31+
Plmambix (generic function with 1 method)
32+
33+
julia> Plmambix(0.5, 2, 1)
34+
-0.21157109383040862
35+
```
36+
3637
## Condon-Shortley phase
3738

3839
By default, the associated Legendre polynomials are computed by including the Condon-Shortley phase ``(-1)^m``.

src/LegendrePolynomials.jl

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -200,25 +200,31 @@ function Base.iterate(iter::LegendrePolynomialIterator{T,<:Integer}, state) wher
200200
return Pl, (l+1, nextstate)
201201
end
202202

203+
throw_normerr(norm) =
204+
throw(ArgumentError("norm = $norm undefined,"*
205+
" valid norms are Val(:standard), Val(:normalized), Val(:schmidtquasi) or Val(:schmidt)"))
206+
203207
maybenormalize(P, l, ::Val{:normalized}) = oftype(P, P * (l+1/2))
204-
maybenormalize(P, l, ::Val{:standard}) = P
205-
maybenormalize(P, l, norm) = throw(ArgumentError("norm = $norm undefined, valid norms are Val(:standard) and Val(:normalized)"))
208+
maybenormalize(P, l, ::Union{Val{:standard}, Val{:schmidtquasi}}) = P
209+
maybenormalize(P, l, ::Val{:schmidt}) = oftype(P, P * (2l+1))
210+
maybenormalize(P, l, norm) = throw_normerr(norm)
206211

207-
function maybenormalize(P, l, m, norm::Val{:normalized}, csphase = true)
208-
if m == 0
209-
return maybenormalize(P, l, norm)
210-
else
211-
return P
212-
end
212+
plm_norm(::Val{:normalized}, lm...) = 1.0
213+
plm_norm(::Val{:standard}, lm...) = plm_norm(lm...)
214+
plm_norm(::Val{:schmidtquasi}, l, m) = (2*(2 - iszero(m))/(2l + 1))
215+
function plm_norm(::Val{:schmidt}, l, m)
216+
two = first(promote(2, l, m))
217+
(two*(two - iszero(m)))
213218
end
214-
function maybenormalize(P, l, m, norm::Val{:standard}, csphase = true)
219+
plm_norm(norm, args...) = throw_normerr(norm)
220+
221+
function maybenormalize(P, l, m, norm, csphase = true)
215222
if m == 0
216223
return maybenormalize(P, l, norm)
217224
else
218-
return (m < 0 ? neg1pow(m) : 1) * plm_norm(l, m) * P
225+
return (m < 0 ? neg1pow(m) : 1) * plm_norm(norm, l, m) * P
219226
end
220227
end
221-
maybenormalize(P, l, m, norm, args...) = throw(ArgumentError("norm = $norm undefined, valid norms are Val(:standard) and Val(:normalized)"))
222228

223229
"""
224230
Pl(x, l::Integer; [norm = Val(:standard)])
@@ -268,8 +274,10 @@ For `m == 0` this function returns Legendre polynomials.
268274
269275
# Optional keyword arguments
270276
* `norm`: The normalization used in the function. Possible
271-
options are `Val(:standard)` (default) and `Val(:normalized)`.
272-
The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
277+
options are `Val(:standard)` (default), `Val(:normalized)`, `Val(:schmidtquasi)` or `Val(:schmidt)`.
278+
* The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
279+
* The functions obtained with `norm = Val(:schmidtquasi)` have an L2 norm of ``\\sqrt{\\frac{2(2-\\delta_{m0})}{2l+1}}``.
280+
* The functions obtained with `norm = Val(:schmidt)` have an L2 norm of ``\\sqrt{2(2-\\delta_{m0})}``.
273281
* `csphase::Bool`: The Condon-shortley phase ``(-1)^m``, which is included by default.
274282
275283
# Examples
@@ -474,8 +482,10 @@ Returns `v` with indices `lmin:lmax`, with `v[l] == Plm(x, l, m; kwargs...)`.
474482
475483
# Optional keyword arguments
476484
* `norm`: The normalization used in the function. Possible
477-
options are `Val(:standard)` (default) and `Val(:normalized)`.
478-
The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
485+
options are `Val(:standard)` (default), `Val(:normalized)`, `Val(:schmidtquasi)` or `Val(:schmidt)`.
486+
* The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
487+
* The functions obtained with `norm = Val(:schmidtquasi)` have an L2 norm of ``\\sqrt{\\frac{2(2-\\delta_{m0})}{2l+1}}``.
488+
* The functions obtained with `norm = Val(:schmidt)` have an L2 norm of ``\\sqrt{2(2-\\delta_{m0})}``.
479489
* `csphase::Bool`: The Condon-shortley phase ``(-1)^m``, which is included by default.
480490
* `Tnorm`: Type of the normalization factor, which is dynamicall inferred by default,
481491
and is used in allocating an appropriate container.
@@ -525,8 +535,10 @@ At output, `v[l + firstindex(v)] == Plm(x, l, m; kwargs...)` for `l = lmin:lmax`
525535
526536
# Optional keyword arguments
527537
* `norm`: The normalization used in the function. Possible
528-
options are `Val(:standard)` (default) and `Val(:normalized)`.
529-
The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
538+
options are `Val(:standard)` (default), `Val(:normalized)`, `Val(:schmidtquasi)` or `Val(:schmidt)`.
539+
* The functions obtained with `norm = Val(:normalized)` have an L2 norm of ``1``.
540+
* The functions obtained with `norm = Val(:schmidtquasi)` have an L2 norm of ``\\sqrt{\\frac{2(2-\\delta_{m0})}{2l+1}}``.
541+
* The functions obtained with `norm = Val(:schmidt)` have an L2 norm of ``\\sqrt{2(2-\\delta_{m0})}``.
530542
* `csphase::Bool`: The Condon-shortley phase ``(-1)^m``, which is included by default.
531543
532544
# Examples

0 commit comments

Comments
 (0)