Skip to content
Snippets Groups Projects
Commit 89bedcae authored by Michele Maris's avatar Michele Maris
Browse files

u

parent 2ae6a7f8
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:a79b0eaa tags:
# Numba cubic spline
M.Maris - 2025 May 9 -
Better to use NumbaNaturalCubicSpline
Derivatives have some problems
%% Cell type:code id:73b19d27 tags:
``` python
import numpy as np
import time
from matplotlib import pyplot as plt
from yapsut.numba_cubic_spline import NumbaCubicSpline, NumbaNaturalCubicSpline
```
%% Cell type:code id:f843337d tags:
``` python
xx=np.linspace(0,2*np.pi,100)
yy=np.sin(xx)
```
%% Cell type:code id:d00fd513 tags:
``` python
```
%% Cell type:code id:31d22314 tags:
``` python
spl=NumbaCubicSpline(xx,yy)
spl_der=spl.as_derivative()
x_new = np.linspace(0, 2*np.pi, 33)
y_new = spl(x_new)
y_der = spl_der(x_new)
yy_der = spl.derivative(x_new)
plt.plot(xx,np.sin(xx),'-')
plt.plot(x_new,y_new,'+')
plt.plot(xx,np.cos(xx),'-')
plt.plot(x_new,y_der,'x')
plt.plot(x_new,yy_der,'.')
```
%% Output
[<matplotlib.lines.Line2D at 0x7faa9b6b10a0>]
%% Cell type:code id:0774180b tags:
``` python
spl=NumbaNaturalCubicSpline(xx,yy)
x_new = np.linspace(0, 2*np.pi, 33)
y_new = spl(x_new)
y_der = spl.derivative(x_new)
plt.plot(xx,np.sin(xx),'-')
plt.plot(x_new,y_new,'+')
plt.plot(xx,np.cos(xx),'-')
plt.plot(x_new,y_der,'x')
```
%% Output
[<matplotlib.lines.Line2D at 0x7faa9b155e50>]
%% Cell type:code id:07c44b10 tags:
``` python
y_der-np.cos(x_new)
```
%% Output
array([-9.01808748e-08, -3.61131530e-07, -8.16514769e-07, -1.05347279e-06,
-8.68221946e-07, -2.56088616e-07, 6.18383030e-07, 1.47649559e-06,
1.99846874e-06, 1.90557548e-06, 1.04140781e-06, -4.53878964e-07,
-1.18976568e-06, -1.11274057e-06, -6.60453420e-07, -2.15422938e-07,
-3.66177375e-08, -2.15422938e-07, -6.60453421e-07, -1.11274057e-06,
-1.18976568e-06, -4.53878964e-07, 1.04140781e-06, 1.90557548e-06,
1.99846874e-06, 1.47649559e-06, 6.18383031e-07, -2.56088615e-07,
-8.68221948e-07, -1.05347279e-06, -8.16514770e-07, -3.61131530e-07,
-9.01808749e-08])
%% Cell type:markdown id:7daec340 tags:
# Appendix
Generated with chat gpt
```
```
%% Cell type:code id:4ef193cf tags:
``` python
```
%% Cell type:markdown id:a79b0eaa tags:
# Numba cubic spline
M.Maris - 2025 May 9 -
Better to use NumbaNaturalCubicSpline
Derivatives have some problems
%% Cell type:code id:73b19d27 tags:
``` python
import numpy as np
import time
from matplotlib import pyplot as plt
from yapsut.numba_cubic_spline import NumbaCubicSpline, NumbaNaturalCubicSpline
```
%% Cell type:code id:f843337d tags:
``` python
xx=np.linspace(0,2*np.pi,100)
yy=np.sin(xx)
```
%% Cell type:code id:d00fd513 tags:
``` python
```
%% Cell type:code id:31d22314 tags:
``` python
spl=NumbaCubicSpline(xx,yy)
spl_der=spl.as_derivative()
x_new = np.linspace(0, 2*np.pi, 33)
y_new = spl(x_new)
y_der = spl_der(x_new)
yy_der = spl.derivative(x_new)
plt.plot(xx,np.sin(xx),'-')
plt.plot(x_new,y_new,'+')
plt.plot(xx,np.cos(xx),'-')
plt.plot(x_new,y_der,'x')
plt.plot(x_new,yy_der,'.')
```
%% Output
[<matplotlib.lines.Line2D at 0x7faa9b6b10a0>]
%% Cell type:code id:0774180b tags:
``` python
spl=NumbaNaturalCubicSpline(xx,yy)
x_new = np.linspace(0, 2*np.pi, 33)
y_new = spl(x_new)
y_der = spl.derivative(x_new)
plt.plot(xx,np.sin(xx),'-')
plt.plot(x_new,y_new,'+')
plt.plot(xx,np.cos(xx),'-')
plt.plot(x_new,y_der,'x')
```
%% Output
[<matplotlib.lines.Line2D at 0x7faa9b155e50>]
%% Cell type:code id:07c44b10 tags:
``` python
y_der-np.cos(x_new)
```
%% Output
array([-9.01808748e-08, -3.61131530e-07, -8.16514769e-07, -1.05347279e-06,
-8.68221946e-07, -2.56088616e-07, 6.18383030e-07, 1.47649559e-06,
1.99846874e-06, 1.90557548e-06, 1.04140781e-06, -4.53878964e-07,
-1.18976568e-06, -1.11274057e-06, -6.60453420e-07, -2.15422938e-07,
-3.66177375e-08, -2.15422938e-07, -6.60453421e-07, -1.11274057e-06,
-1.18976568e-06, -4.53878964e-07, 1.04140781e-06, 1.90557548e-06,
1.99846874e-06, 1.47649559e-06, 6.18383031e-07, -2.56088615e-07,
-8.68221948e-07, -1.05347279e-06, -8.16514770e-07, -3.61131530e-07,
-9.01808749e-08])
%% Cell type:markdown id:7daec340 tags:
# Appendix
Generated with chat gpt
```
```
%% Cell type:code id:4ef193cf tags:
``` python
```
__description__="""
cubic spline with numba
as produced by chat gpt
"""
import numpy as np
from numba import njit
@njit
def cubic_spline_natural_coeffs(x, y):
n = len(x) - 1
h = np.diff(x)
alpha = np.zeros(n)
for i in range(1, n):
alpha[i] = (3/h[i]) * (y[i+1] - y[i]) - (3/h[i-1]) * (y[i] - y[i-1])
l = np.ones(n+1)
mu = np.zeros(n+1)
z = np.zeros(n+1)
for i in range(1, n):
l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*mu[i-1]
mu[i] = h[i]/l[i]
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i]
b = np.zeros(n)
c = np.zeros(n+1)
d = np.zeros(n)
for j in range(n-1, -1, -1):
c[j] = z[j] - mu[j]*c[j+1]
b[j] = (y[j+1] - y[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3
d[j] = (c[j+1] - c[j])/(3*h[j])
return b, c[:-1], d # a = y[:-1]
@njit
def evaluate_spline(x, y, b, c, d, x_eval):
n = len(x) - 1
y_eval = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_eval[j] = y[i] + b[i]*dx + c[i]*dx**2 + d[i]*dx**3
break
return y_eval
@njit
def evaluate_spline_derivative(x, b, c, d, x_eval):
n = len(x) - 1
y_deriv = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_deriv[j] = b[i] + 2 * c[i] * dx + 3 * d[i] * dx**2
break
return y_deriv
""" ===================== """
@njit
def _compute_coeffs(x, y):
n = len(x) - 1
h = np.diff(x)
alpha = np.zeros(n)
for i in range(1, n):
alpha[i] = (3/h[i]) * (y[i+1] - y[i]) - (3/h[i-1]) * (y[i] - y[i-1])
l = np.ones(n+1)
mu = np.zeros(n+1)
z = np.zeros(n+1)
for i in range(1, n):
l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*mu[i-1]
mu[i] = h[i]/l[i]
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i]
b = np.zeros(n)
c = np.zeros(n+1)
d = np.zeros(n)
for j in range(n-1, -1, -1):
c[j] = z[j] - mu[j]*c[j+1]
b[j] = (y[j+1] - y[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3
d[j] = (c[j+1] - c[j])/(3*h[j])
return b, c[:-1], d # a = y[:-1]
@njit
def _evaluate(x, y, b, c, d, x_eval):
n = len(x) - 1
y_eval = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_eval[j] = y[i] + b[i]*dx + c[i]*dx**2 + d[i]*dx**3
break
return y_eval
@njit
def _evaluate_derivative(x, b, c, d, x_eval):
n = len(x) - 1
y_deriv = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_deriv[j] = b[i] + 2 * c[i] * dx + 3 * d[i] * dx**2
break
return y_deriv
# Classe wrapper
class NumbaNaturalCubicSpline:
""" natural cubic spline with numba """
def __init__(self, x, y):
self.x = np.asarray(x)
self.y = np.asarray(y)
self.b, self.c, self.d = _compute_coeffs(self.x, self.y)
def __call__(self, x_eval):
x_eval = np.asarray(x_eval)
return _evaluate(self.x, self.y, self.b, self.c, self.d, x_eval)
def derivative(self, x_eval):
x_eval = np.asarray(x_eval)
return _evaluate_derivative(self.x, self.b, self.c, self.d, x_eval)
""" ===================== """
@njit
def _xt_compute_clamped_coeffs(x, y, fp0, fpn):
n = len(x) - 1
h = np.diff(x)
alpha = np.zeros(n+1)
alpha[0] = 3 * (y[1] - y[0]) / h[0] - 3 * fp0
alpha[n] = 3 * fpn - 3 * (y[n] - y[n-1]) / h[n-1]
for i in range(1, n):
alpha[i] = (3/h[i]) * (y[i+1] - y[i]) - (3/h[i-1]) * (y[i] - y[i-1])
l = np.ones(n+1)
mu = np.zeros(n+1)
z = np.zeros(n+1)
l[0] = 2 * h[0]
mu[0] = 0.5
z[0] = alpha[0] / l[0]
for i in range(1, n):
l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*mu[i-1]
mu[i] = h[i]/l[i]
z[i] = (alpha[i] - h[i-1]*z[i-1])/l[i]
l[n] = h[n-1]*(2 - mu[n-1])
z[n] = (alpha[n] - h[n-1]*z[n-1])/l[n]
c = np.zeros(n+1)
b = np.zeros(n)
d = np.zeros(n)
c[n] = z[n]
for j in range(n-1, -1, -1):
c[j] = z[j] - mu[j]*c[j+1]
b[j] = (y[j+1] - y[j])/h[j] - h[j]*(c[j+1] + 2*c[j])/3
d[j] = (c[j+1] - c[j])/(3*h[j])
return b, c[:-1], d
@njit
def _xt_evaluate(x, y, b, c, d, x_eval):
n = len(x) - 1
y_eval = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_eval[j] = y[i] + b[i]*dx + c[i]*dx**2 + d[i]*dx**3
break
return y_eval
@njit
def _xt_evaluate_derivative(x, b, c, d, x_eval):
n = len(x) - 1
y_deriv = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_deriv[j] = b[i] + 2*c[i]*dx + 3*d[i]*dx**2
break
return y_deriv
@njit
def _xt_evaluate_second_derivative(x, c, d, x_eval):
n = len(x) - 1
y_sec = np.zeros_like(x_eval)
for j in range(len(x_eval)):
xi = x_eval[j]
for i in range(n):
if x[i] <= xi <= x[i+1]:
dx = xi - x[i]
y_sec[j] = 2*c[i] + 6*d[i]*dx
break
return y_sec
class NumbaCubicSpline:
def __init__(self, x, y, bc_type='natural', fp0=0.0, fpn=0.0):
self.x = np.asarray(x)
self.y = np.asarray(y)
if bc_type == 'natural':
self.b, self.c, self.d = _xt_compute_clamped_coeffs(self.x, self.y, 0.0, 0.0)
elif bc_type == 'clamped':
self.b, self.c, self.d = _xt_compute_clamped_coeffs(self.x, self.y, fp0, fpn)
else:
raise ValueError("bc_type must be 'natural' or 'clamped'")
def __call__(self, x_eval):
x_eval = np.asarray(x_eval)
return _xt_evaluate(self.x, self.y, self.b, self.c, self.d, x_eval)
def derivative(self, x_eval):
x_eval = np.asarray(x_eval)
return _xt_evaluate_derivative(self.x, self.b, self.c, self.d, x_eval)
def second_derivative(self, x_eval):
x_eval = np.asarray(x_eval)
return _xt_evaluate_second_derivative(self.x, self.c, self.d, x_eval)
def coefficients(self):
return self.b.copy(), self.c.copy(), self.d.copy()
def as_derivative(self):
x_new = self.x.copy()
y_new = self.derivative(self.x)
return NumbaCubicSpline(x_new, y_new, bc_type='natural')
def antiderivative(self):
n = len(self.x) - 1
x_new = self.x.copy()
y_new = np.zeros_like(self.x)
for i in range(n):
h = self.x[i+1] - self.x[i]
a = self.y[i]
b = self.b[i]
c = self.c[i]
d = self.d[i]
integral = (
a * h +
b * h**2 / 2 +
c * h**3 / 3 +
d * h**4 / 4
)
y_new[i+1] = y_new[i] + integral
return NumbaCubicSpline(x_new, y_new, bc_type='natural')
def integrate(self, a, b):
if a == b:
return 0.0
if a > b:
return -self.integrate(b, a)
a = max(self.x[0], a)
b = min(self.x[-1], b)
total = 0.0
n = len(self.x) - 1
for i in range(n):
x0 = self.x[i]
x1 = self.x[i+1]
if b <= x0 or a >= x1:
continue
xl = max(a, x0)
xr = min(b, x1)
dxl = xl - x0
dxr = xr - x0
a0 = self.y[i]
b0 = self.b[i]
c0 = self.c[i]
d0 = self.d[i]
def F(x):
return (
a0 * x +
b0 * x**2 / 2 +
c0 * x**3 / 3 +
d0 * x**4 / 4
)
total += F(dxr) - F(dxl)
return total
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment