Skip to content

Commit be3c1e0

Browse files
authored
Update polynomials (#308)
* working on jacobi polynomials * use jacobi polynomials to compute orthogonal polynomials * orthonormal polynomials on pyramids * full polynomials on pyramids * update caching dates
1 parent 8d7f05e commit be3c1e0

File tree

15 files changed

+328
-577
lines changed

15 files changed

+328
-577
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
run: |
3636
sudo apt-get update
3737
sudo apt-get install -y texlive-latex-base texlive-latex-extra
38-
- run: python3 -m pytest -n=auto --durations=50 test -W error
38+
- run: python3 -m pytest --durations=50 test -W error -xvs
3939
name: Run unit tests
4040
- run: python3 -m pytest demo/test_demos.py -W error
4141
name: Run demos

symfem/elements/bernstein.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def __init__(
116116
super().__init__(reference, entity, "identity")
117117
orth = [
118118
o / sympy.sqrt((o * o).integral(integral_domain))
119-
for o in orthogonal_basis(integral_domain.name, degree, 0, t[: integral_domain.tdim])[0]
119+
for o in orthogonal_basis(integral_domain.name, degree, t[: integral_domain.tdim])
120120
]
121121
self.ref = integral_domain
122122
self.index = index

symfem/elements/lagrange.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
3838
"""
3939
dofs: ListOfFunctionals = []
4040
if variant == "legendre":
41-
basis = orthonormal_basis(reference.name, order, 0)[0]
41+
basis = orthonormal_basis(reference.name, order)
4242
for f in basis:
4343
dofs.append(IntegralAgainst(reference, f, (reference.tdim, 0)))
4444
elif order == 0:
@@ -112,7 +112,7 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
112112
min_order = 0
113113
continuity = "C0"
114114
value_type = "scalar"
115-
last_updated = "2023.09"
115+
last_updated = "2025.06"
116116

117117

118118
class VectorLagrange(CiarletElement):
@@ -189,7 +189,7 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
189189
min_order = 0
190190
continuity = "C0"
191191
value_type = "vector"
192-
last_updated = "2023.09"
192+
last_updated = "2025.06"
193193

194194

195195
class MatrixLagrange(CiarletElement):
@@ -259,7 +259,7 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
259259
min_order = 0
260260
continuity = "L2"
261261
value_type = "matrix"
262-
last_updated = "2023.09"
262+
last_updated = "2025.06"
263263

264264

265265
class SymmetricMatrixLagrange(CiarletElement):
@@ -352,4 +352,4 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
352352
min_order = 0
353353
continuity = "L2"
354354
value_type = "symmetric matrix"
355-
last_updated = "2023.09"
355+
last_updated = "2025.06"

symfem/elements/lagrange_prism.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
4040

4141
dofs: ListOfFunctionals = []
4242
if variant == "legendre":
43-
basis = orthonormal_basis(reference.name, order, 0)[0]
43+
basis = orthonormal_basis(reference.name, order)
4444
for f in basis:
4545
dofs.append(IntegralAgainst(reference, f, (reference.tdim, 0)))
4646
elif order == 0:
@@ -141,7 +141,7 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
141141
min_order = 0
142142
continuity = "C0"
143143
value_type = "scalar"
144-
last_updated = "2023.07"
144+
last_updated = "2025.06"
145145

146146

147147
class VectorLagrange(CiarletElement):
@@ -206,4 +206,4 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
206206
min_order = 0
207207
continuity = "C0"
208208
value_type = "vector"
209-
last_updated = "2023.05"
209+
last_updated = "2025.06"

symfem/elements/lagrange_pyramid.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
3535

3636
dofs: ListOfFunctionals = []
3737
if variant == "legendre":
38-
basis = orthonormal_basis(reference.name, order, 0)[0]
38+
basis = orthonormal_basis(reference.name, order)
3939
for f in basis:
4040
dofs.append(IntegralAgainst(reference, f, (reference.tdim, 0)))
4141
elif order == 0:
@@ -136,4 +136,4 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
136136
min_order = 0
137137
continuity = "C0"
138138
value_type = "scalar"
139-
last_updated = "2024.09"
139+
last_updated = "2025.06"

symfem/elements/q.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
4444
"""
4545
dofs: ListOfFunctionals = []
4646
if variant == "legendre":
47-
basis = orthonormal_basis(reference.name, order, 0)[0]
47+
basis = orthonormal_basis(reference.name, order)
4848
for f in basis:
4949
dofs.append(IntegralAgainst(reference, f, (reference.tdim, 0)))
5050
elif order == 0:

symfem/elements/tnt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def p(k: int, v: sympy.core.symbol.Symbol) -> ScalarFunction:
3838
Returns:
3939
The kth Legendre polynomial
4040
"""
41-
return orthogonal_basis("interval", k, 0, [v])[0][-1]
41+
return orthogonal_basis("interval", k, [v])[-1]
4242

4343

4444
def b(k: int, v: sympy.core.symbol.Symbol) -> ScalarFunction:

symfem/polynomials/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Polynomials."""
22

33
from symfem.polynomials.dual import l2_dual
4+
from symfem.polynomials.jacobi import jacobi_polynomial, monic_jacobi_polynomial
45
from symfem.polynomials.legendre import orthogonal_basis, orthonormal_basis
56
from symfem.polynomials.lobatto import lobatto_basis, lobatto_dual_basis
67
from symfem.polynomials.polysets import (

symfem/polynomials/jacobi.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""Jacobi polynomials."""
2+
3+
import typing
4+
from functools import cache
5+
6+
from math import prod, factorial
7+
import sympy
8+
9+
from symfem.functions import ScalarFunction
10+
from symfem.symbols import x
11+
12+
__all__: typing.List[str] = []
13+
14+
15+
def _jrc(
16+
a: int, b: int, n: int
17+
) -> typing.Tuple[
18+
sympy.core.expr.Expr,
19+
sympy.core.expr.Expr,
20+
sympy.core.expr.Expr,
21+
]:
22+
"""Get the Jacobi recurrence relation coefficients.
23+
24+
Args:
25+
a: The parameter a
26+
b: The parameter b
27+
n: The parameter n
28+
29+
Returns:
30+
The Jacobi coefficients
31+
"""
32+
if b != 0:
33+
raise NotImplementedError()
34+
return (
35+
sympy.Rational((a + 2 * n + 1) * (a + 2 * n + 2), 2 * (n + 1) * (a + n + 1)),
36+
sympy.Rational(a * a * (a + 2 * n + 1), 2 * (n + 1) * (a + n + 1) * (a + 2 * n)),
37+
sympy.Rational(n * (a + n) * (a + 2 * n + 2), (n + 1) * (a + n + 1) * (a + 2 * n)),
38+
)
39+
40+
41+
def _jrc_monic(
42+
a: int, b: int, n: int
43+
) -> typing.Tuple[
44+
sympy.core.expr.Expr,
45+
sympy.core.expr.Expr,
46+
]:
47+
"""Get the Jacobi recurrence relation coefficients for monic polynomials.
48+
49+
Args:
50+
a: The parameter a
51+
b: The parameter b
52+
n: The parameter n
53+
54+
Returns:
55+
The Jacobi coefficients
56+
"""
57+
return (
58+
sympy.Rational(a**2 - b**2, (2 * n + a + b) * (2 * n + a + b + 2)),
59+
sympy.Rational(
60+
4 * n * (n + a) * (n + b) * (n + a + b),
61+
(2 * n + a + b + 1) * (2 * n + a + b) ** 2 * (2 * n + a + b - 1),
62+
),
63+
)
64+
65+
66+
@cache
67+
def jacobi_polynomial_x(n: int, a: int, b: int) -> sympy.core.expr.Expr:
68+
"""Get a Jacobi polynomial.
69+
70+
Args:
71+
n: Polynomial degree
72+
a: The parameter a
73+
b: The parameter b
74+
"""
75+
if a < 0:
76+
if b <= 0 and n + a >= 0:
77+
# See https://mathoverflow.net/questions/298661/jacobi-polynomials-with-negative-integer-parameters
78+
return (
79+
((x[0] - 1) / 2) ** -a
80+
* ((x[0] + 1) / 2) ** -b
81+
* jacobi_polynomial(n + a + b, -a, -b)
82+
)
83+
raise NotImplementedError()
84+
if n == 0:
85+
return sympy.Integer(1)
86+
if n == 1:
87+
return (a + 1) + (a + b + 2) * (x[0] - 1) / 2
88+
i, j, k = _jrc(a, b, n - 1)
89+
return (i * x[0] + j) * jacobi_polynomial(n - 1, a, b) - k * jacobi_polynomial(n - 2, a, b)
90+
91+
92+
def jacobi_polynomial(n: int, a: int, b: int, variable: sympy.Symbol = x[0]) -> ScalarFunction:
93+
"""Get a Jacobi polynomial.
94+
95+
Args:
96+
n: Polynomial degree
97+
a: The parameter a
98+
b: The parameter b
99+
variable: Variable to use
100+
"""
101+
return ScalarFunction(jacobi_polynomial_x(n, a, b).subs(x[0], variable))
102+
103+
104+
def choose(n: int, r: int) -> int:
105+
"""Choose function."""
106+
return prod(range(n - r + 1, n + 1)) // factorial(r)
107+
108+
109+
@cache
110+
def monic_jacobi_polynomial_x(n: int, a: int, b: int) -> sympy.core.expr.Expr:
111+
"""Get a monic Jacobi polynomial.
112+
113+
Args:
114+
n: Polynomial degree
115+
a: The parameter a
116+
b: The parameter b
117+
"""
118+
if n == 0:
119+
return sympy.Integer(1)
120+
if a + b == -n:
121+
return sum(
122+
choose(n + a, i) * choose(n + b, n - i) * (x[0] - 1) ** (n - i) * (x[0] + 1) ** i
123+
for i in range(n + 1)
124+
) / choose(2 * n + a + b, n)
125+
if n == 1:
126+
return x[0] + sympy.Rational(2 * (a + 1), a + b + 2) - 1
127+
i, j = _jrc_monic(a, b, n - 1)
128+
return (x[0] + i) * monic_jacobi_polynomial(n - 1, a, b) - j * monic_jacobi_polynomial(
129+
n - 2, a, b
130+
)
131+
132+
133+
def monic_jacobi_polynomial(
134+
n: int, a: int, b: int, variable: sympy.Symbol = x[0]
135+
) -> ScalarFunction:
136+
"""Get a monic Jacobi polynomial.
137+
138+
Args:
139+
n: Polynomial degree
140+
a: The parameter a
141+
b: The parameter b
142+
variable: Variable to use
143+
"""
144+
return ScalarFunction(monic_jacobi_polynomial_x(n, a, b).subs(x[0], variable))

0 commit comments

Comments
 (0)