Skip to content

Commit

Permalink
Add docstring (#19)
Browse files Browse the repository at this point in the history
* add docstring to xfunction

* remove dependency on Optional

* add docstring to bspline function module

* add docstring to indicator function

* add docstring to poly class

* add docstring for the main xspline class
  • Loading branch information
zhengp0 committed Jul 25, 2023
1 parent 5b576c9 commit cf357ed
Show file tree
Hide file tree
Showing 5 changed files with 432 additions and 65 deletions.
109 changes: 93 additions & 16 deletions src/xspline/bspl.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,24 @@


def cache_bspl(function: RawFunction) -> RawFunction:
"""Cache implementation for bspline basis functions, to avoid repetitively
evaluate functions.
Parameters
----------
function
Raw value, derivative and definite integral functions.
Returns
-------
describe
Cached version of the raw functions.
"""
cache = {}

def wrapper_function(*args, **kwargs) -> NDArray:
key = tuple(
tuple(x.ravel()) if isinstance(x, np.ndarray) else x for x in args
)
key = tuple(tuple(x.ravel()) if isinstance(x, np.ndarray) else x for x in args)
if key in cache:
return cache[key]
result = function(*args, **kwargs)
Expand All @@ -28,6 +40,22 @@ def cache_clear():

@cache_bspl
def bspl_val(params: BsplParams, x: NDArray) -> NDArray:
"""Value of the bspline function.
Parameters
----------
params
Bspline function parameters as a tuple including, knots, degree and the
index of the spline basis.
x
Data points.
Returns
-------
describe
Function value of the bspline function.
"""
# knots, degree, and index
t, k, i = params

Expand All @@ -44,10 +72,10 @@ def bspl_val(params: BsplParams, x: NDArray) -> NDArray:

if t[ii[0]] != t[ii[2]]:
n0 = bspl_val((t, k - 1, i), x)
val0 = (x - t[ii[0]])*n0/(t[ii[2]] - t[ii[0]])
val0 = (x - t[ii[0]]) * n0 / (t[ii[2]] - t[ii[0]])
if t[ii[1]] != t[ii[3]]:
n1 = bspl_val((t, k - 1, i + 1), x)
val1 = (t[ii[3]] - x)*n1/(t[ii[3]] - t[ii[1]])
val1 = (t[ii[3]] - x) * n1 / (t[ii[3]] - t[ii[1]])

val = val0 + val1

Expand All @@ -56,6 +84,22 @@ def bspl_val(params: BsplParams, x: NDArray) -> NDArray:

@cache_bspl
def bspl_der(params: BsplParams, x: NDArray, order: int) -> NDArray:
"""Derivative of the bspline function.
Parameters
----------
params
Bspline function parameters as a tuple including, knots, degree and the
index of the spline basis.
x
Data points.
Returns
-------
describe
Derivative of the bspline function.
"""
# knots, degree, and index
t, k, i = params

Expand All @@ -72,10 +116,10 @@ def bspl_der(params: BsplParams, x: NDArray, order: int) -> NDArray:

if t[ii[0]] != t[ii[2]]:
n0 = bspl_der((t, k - 1, i), x, order - 1)
val0 = k*n0/(t[ii[2]] - t[ii[0]])
val0 = k * n0 / (t[ii[2]] - t[ii[0]])
if t[ii[1]] != t[ii[3]]:
n1 = bspl_der((t, k - 1, i + 1), x, order - 1)
val1 = k*n1/(t[ii[3]] - t[ii[1]])
val1 = k * n1 / (t[ii[3]] - t[ii[1]])

val = val0 - val1

Expand All @@ -84,6 +128,22 @@ def bspl_der(params: BsplParams, x: NDArray, order: int) -> NDArray:

@cache_bspl
def bspl_int(params: BsplParams, x: NDArray, order: int) -> NDArray:
"""Definite integral of the bspline function.
Parameters
----------
params
Bspline function parameters as a tuple including, knots, degree and the
index of the spline basis.
x
Data points.
Returns
-------
describe
Definite integral of the bspline function.
"""
# knots, degree, and index
t, k, i = params

Expand All @@ -101,21 +161,25 @@ def bspl_int(params: BsplParams, x: NDArray, order: int) -> NDArray:

if t[ii[0]] != t[ii[2]]:
val0 = (
(x - t[ii[0]])*bspl_int((t, k - 1, i), x, order) +
order*bspl_int((t, k - 1, i), x, order - 1)
)/(t[ii[2]] - t[ii[0]])
(x - t[ii[0]]) * bspl_int((t, k - 1, i), x, order)
+ order * bspl_int((t, k - 1, i), x, order - 1)
) / (t[ii[2]] - t[ii[0]])
if t[ii[1]] != t[ii[3]]:
val1 = (
(t[ii[3]] - x)*bspl_int((t, k - 1, i + 1), x, order) -
order*bspl_int((t, k - 1, i + 1), x, order - 1)
)/(t[ii[3]] - t[ii[1]])
(t[ii[3]] - x) * bspl_int((t, k - 1, i + 1), x, order)
- order * bspl_int((t, k - 1, i + 1), x, order - 1)
) / (t[ii[3]] - t[ii[1]])

val = val0 + val1

return val


def clear_bspl_cache() -> None:
"""Clear all cache of the value, derivative and definite integral for
bspline function.
"""
bspl_val.cache_clear()
bspl_der.cache_clear()
bspl_int.cache_clear()
Expand Down Expand Up @@ -149,6 +213,19 @@ def __init__(self, params: BsplParams) -> None:


def get_bspl_funs(knots: tuple[float, ...], degree: int) -> tuple[Bspl]:
return tuple(
Bspl((knots, degree, i)) for i in range(-degree, len(knots) - 1)
)
"""Create the bspline basis functions give knots and degree.
Parameters
----------
knots
Bspline knots.
degree
Bspline degree.
Returns
-------
describe
A full set of bspline functions.
"""
return tuple(Bspl((knots, degree, i)) for i in range(-degree, len(knots) - 1))
60 changes: 54 additions & 6 deletions src/xspline/indi.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
from math import factorial

import numpy as np
from numpy.typing import NDArray

from xspline.typing import IndiParams
from xspline.typing import IndiParams, NDArray
from xspline.xfunction import BundleXFunction


def indi_val(params: IndiParams, x: NDArray) -> NDArray:
"""Indicator value function,
Parameters
----------
params
Indicator parameters as a tuple consists of lower and upper bound of
the interval corresponding to the indicator function.
x
Data points.
Returns
-------
describe
Indicator function value.
"""
# lower and upper bounds
lb, ub = params

Expand All @@ -22,21 +37,54 @@ def indi_val(params: IndiParams, x: NDArray) -> NDArray:


def indi_der(params: IndiParams, x: NDArray, order: int) -> NDArray:
"""Indicator derivative function. Since indicator function is a piecewise
constant function, its derivative will always be zero.
Parameters
----------
params
Indicator parameters as a tuple consists of lower and upper bound of
the interval corresponding to the indicator function.
x
Data points.
Returns
-------
describe
Indicator deviative value.
"""
return np.zeros(x.size, dtype=x.dtype)


def indi_int(params: IndiParams, x: NDArray, order: int) -> NDArray:
"""Indicator definite integral function. It is a piecewise polynomial
function.
Parameters
----------
params
Indicator parameters as a tuple consists of lower and upper bound of
the interval corresponding to the indicator function.
x
Data points.
Returns
-------
describe
Indicator definite integral value.
"""
# lower and upper bounds
lb, ub = params

val = np.zeros(x.size, dtype=x.dtype)
ind0 = (x >= lb[0]) & (x <= ub[0])
ind1 = x > ub[0]
val[ind0] = (x[ind0] - lb[0])**(-order)/factorial(-order)
val[ind0] = (x[ind0] - lb[0]) ** (-order) / factorial(-order)
for i in range(-order):
val[ind1] += (
((ub[0] - lb[0])**(-order - i)/factorial(-order - i)) *
((x[ind1] - ub[0])**i/factorial(i))
val[ind1] += ((ub[0] - lb[0]) ** (-order - i) / factorial(-order - i)) * (
(x[ind1] - ub[0]) ** i / factorial(i)
)
return val

Expand Down
89 changes: 86 additions & 3 deletions src/xspline/poly.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,70 @@
from math import factorial

import numpy as np
from numpy.typing import NDArray

from xspline.typing import PolyParams
from xspline.typing import PolyParams, NDArray
from xspline.xfunction import BundleXFunction, XFunction


def poly_val(params: PolyParams, x: NDArray) -> NDArray:
"""Polynomial value function.
Parameters
----------
params
Polynomial coefficients.
x
Data points.
Returns
-------
describe
Polynomial function values.
"""
return np.polyval(params, x)


def poly_der(params: PolyParams, x: NDArray, order: int) -> NDArray:
"""Polynomial derivative function.
Parameters
----------
params
Polynomial coefficients.
x
Data points.
order
Order of differentiation.
Returns
-------
describe
Polynomial derivative values.
"""
return np.polyval(np.polyder(params, order), x)


def poly_int(params: PolyParams, x: NDArray, order: int) -> NDArray:
"""Polynomial definite integral function.
Parameters
----------
params
Polynomial coefficients.
x
Data points.
order
Order of integration. Here we use negative integer.
Returns
-------
describe
Polynomial definite integral values.
"""

return np.polyval(np.polyint(params, -order), x)


Expand Down Expand Up @@ -46,16 +95,50 @@ def __init__(self, params: PolyParams) -> None:


def get_poly_params(fun: XFunction, x: float, degree: int) -> tuple[float, ...]:
"""Solve polynomial (taylor) coefficients provided the ``XFunction``.
Parameters
----------
fun
Provided ``XFunction`` to be approximated.
x
The point where we want to approximate ``XFunction`` by the polynomial.
degree
Degree of the approximation polynomial.
Returns
-------
describe
The approximation polynomial coefficients.
"""
if degree < 0:
return (0.0,)
rhs = np.array([fun(x, order=i) for i in range(degree, -1, -1)])
mat = np.zeros((degree + 1, degree + 1))
for i in range(degree + 1):
for j in range(i + 1):
mat[i, j] = factorial(degree - j) / factorial(i - j) * x**(i - j)
mat[i, j] = factorial(degree - j) / factorial(i - j) * x ** (i - j)
return tuple(np.linalg.solve(mat, rhs))


def get_poly_fun(fun: XFunction, x: float, degree: int) -> Poly:
"""Get the approximation polynomial function.
Parameters
----------
fun
Provided ``XFunction`` to be approximated.
x
The point where we want to approximate ``XFunction`` by the polynomial.
degree
Degree of the approximation polynomial.
Returns
-------
describe
Instance of the ``Poly`` class to approximate provided ``XFunction``.
"""
params = get_poly_params(fun, x, degree)
return Poly(params)
Loading

0 comments on commit cf357ed

Please sign in to comment.