forked from sigma-py/quadpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_silvester.py
93 lines (75 loc) · 2.72 KB
/
_silvester.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import numpy as np
import sympy
from ..helpers import article, get_all_exponents, prod
from ._helpers import TnScheme
source = article(
authors=["P. Silvester"],
title="Symmetric quadrature formulae for simplexes",
journal="Math. Comp.",
volume="24",
pages="95-100",
year="1970",
url="https://doi.org/10.1090/S0025-5718-1970-0258283-6",
)
def integrate_bary(k, symbolic):
# This is very similar to integrate_monomial_over_unit_simplex, except that n =
# len(k) - 1, not len(k). This is because we're integrating over a polynomial in
# barycentric coordinates here. Also the integration for [0, ..., 0] is set equal to
# 1. This ensures that the weights add up to 1, not the volume of the unit simplex.
frac = sympy.Rational if symbolic else lambda a, b: a / b
n = len(k) - 1
assert all(kk >= 0 for kk in k)
if all(kk == 0 for kk in k):
# return frac(1, math.factorial(n))
return 1
# find first nonzero
idx = next(i for i, j in enumerate(k) if j > 0)
alpha = frac(k[idx], sum(k) + n)
k2 = k.copy()
k2[idx] -= 1
return integrate_bary(k2, symbolic) * alpha
def _get_data(dim, n, point_fun, symbolic):
# points
idxs = np.array(get_all_exponents(dim + 1, n)[-1])
points = point_fun(idxs)
# weights
weights = np.empty(len(points))
kk = 0
for idx in idxs:
# Define the polynomial which to integrate over the simplex.
t = sympy.DeferredVector("t")
g = prod(
sympy.poly((t[i] - point_fun(k)) / (point_fun(m) - point_fun(k)))
for i, m in enumerate(idx)
for k in range(m)
)
if isinstance(g, int):
exp = [0] * (dim + 1)
coeffs = g
else:
# make sure the exponents are all of the correct length
exp = [list(m) + [0] * (dim - len(m) + 1) for m in g.monoms()]
coeffs = g.coeffs()
weights[kk] = sum(c * integrate_bary(m, symbolic) for c, m in zip(coeffs, exp))
kk += 1
return weights, points
def silvester(dim, variant, n, symbolic=False):
frac = np.vectorize(sympy.Rational) if symbolic else lambda a, b: a / b
def points1d_closed(k):
return frac(k, n)
def points1d_open(k):
return frac(k + 1, n + 1 + dim)
if variant == "closed":
degree = n
tol = 1.034e-14
points1d = points1d_closed
else:
assert variant == "open"
degree = 1 if n == 0 else n
tol = 1.040e-13
points1d = points1d_open
weights, points = _get_data(dim, n, points1d, symbolic)
points = np.ascontiguousarray(points.T)
return TnScheme(
f"Silvester ({variant}, dim={n})", dim, weights, points, degree, source, tol
)