- What are practical lower bounds for $s$, $\theta_{\alpha}$, and $D$ for the method above, given a function $f$, with or without additional assumptions on $f$ (such as being $C^2$ continuous, concave, convex, piecewise polynomial, real analytic, or any combination of these)?
- What are other practical ways to apply the Holtz method to certain functions to find polynomials that converge to those functions?
- To help me and others (especially programmers) understand and apply the Holtz method, can you provide examples of the method (in terms of Bernstein polynomial approximation schemes) for various functions or classes of functions (such as $C^2$ continuous, twice differentiableconcave, orconvex, piecewise polynomial, real analytic functions, or any combination of these)?
TAURJ_CACHE = {}
TNJ_CACHE = {}
TAURJ_SYMBOL = symbols("T")
def taurj_x(r, j, n):
if j == 0:
return S(1)
if j == 1:
return S(0)
if (r, j, n) in TAURJ_CACHE:
return TAURJ_CACHE[r, j, n]
ret = 0
for l in range(2, j + 1):
tnj = None
if (n, l) in TNJ_CACHE:
tnj = TNJ_CACHE[(n, l)]
else:
tnj = sum(
(k - n * TAURJ_SYMBOL) ** l
* binomial(n, k)
* TAURJ_SYMBOL ** k
* (1 - TAURJ_SYMBOL) ** (n - k)
for k in range(0, n + 1)
)
ret -= tnj * taurj_x(r - l, j - l, n) / factorial(l)
TAURJ_CACHE[(r, j, n)] = ret.simplify()
return ret
def taurj(r, j, x, n):
if j == 0:
return 1
if j == 1:
return 0
return taurj_x(r, j, n).subs(TAURJ_SYMBOL, x)
def lorentz_n_k(func, x, n, r, k, pt=None):
# kth Bernstein coefficient of the Lorentz operator
# of degree n and smoothness r at the point
# pt (or at x if not given).
if pt == None:
pt = x
ret = 0
for j in range(0, r + 1):
ret += diff(func, (x, j)).subs(x, S(k) / n) * taurj(r, j, pt, n) / n ** j
return ret
def lorentz_n(func, x, n, r, pt=None):
# Lorentz operator.
# Create a polynomial that approximates func, which in turn uses
# the symbol x. The polynomial's degree is n, is assumed to
# be at least r times differentiable, and is evaluated
# at the point pt (or at x if not given).
# NOTE: If r is 0, this is simply the Bernstein operator.
if pt == None:
pt = x
ret = 0
diffs = [diff(func, (x, j)) for j in range(0, r + 1)]
for k in range(0, n + 1):
rm = 0
for j in range(0, r + 1):
rm += diffs[j].subs(x, S(k) / n) * taurj(r, j, pt, n) / n ** j
ret += rm * bernstein_n_k(n, k, pt)
return ret
def ncfo_lowerbernstein_n_k(func, x, n, pt, alpha, thetak, dx):
# NOTE: Assumes func is bounded away from 0 and 1,
# but it's trivial to bound any function this way, such as
# by doing a linear transformation such as
# $f(x) * \frac{8}{10} + \frac{1}{10}$, then undoing
# the transformation once the polynomials are found
r = floor(alpha)
return lorentz_nbinomial(func, x, n, r, ptk) - d * bernstein_n(
phi_n(n, theta, alpha, x), x, n
)
def ncfo_upper(func, x, n, pt, alpha, theta, d):
r = floor(alpha)
return lorentz_n(func, x, n, r, pt) +** dk * bernstein_n(
phi_n(n, theta,1 alpha,- x), x,** (n
- k)