Skip to content

Commit 20932bc

Browse files
committed
made first order actually first order and added fourth order
1 parent e483a7c commit 20932bc

3 files changed

Lines changed: 61 additions & 33 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""This module implements some common finite difference schemes
22
"""
3-
from ._finite_difference import first_order, second_order
3+
from ._finite_difference import first_order, second_order, fourth_order
44

5-
__all__ = ['first_order', 'second_order'] # So these get treated as direct members of the module by sphinx
5+
__all__ = ['first_order', 'second_order', 'fourth_order'] # So these get treated as direct members of the module by sphinx

pynumdiff/finite_difference/_finite_difference.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""This is handy for this module https://web.media.mit.edu/~crtaylor/calculator.html"""
12
import numpy as np
23
from pynumdiff.utils import utility
34
from warnings import warn
@@ -25,29 +26,10 @@ def first_order(x, dt, params=None, options={}, num_iterations=None):
2526
return _iterate_first_order(x, dt, num_iterations)
2627

2728
dxdt_hat = np.diff(x) / dt # Calculate the finite difference
28-
dxdt_hat = np.hstack((dxdt_hat[0], dxdt_hat, dxdt_hat[-1])) # Pad the data
29-
dxdt_hat = np.mean((dxdt_hat[0:-1], dxdt_hat[1:]), axis=0) # Re-finite dxdt_hat using linear interpolation
29+
dxdt_hat = np.hstack((dxdt_hat, dxdt_hat[-1])) # using stencil -1,0, you get expression for previous value
3030

3131
return x, dxdt_hat
3232

33-
34-
def second_order(x, dt):
35-
"""Second-order centered difference method
36-
37-
:param np.array[float] x: data to differentiate
38-
:param float dt: step size
39-
40-
:return: tuple[np.array, np.array] of\n
41-
- **x_hat** -- estimated (smoothed) x
42-
- **dxdt_hat** -- estimated derivative of x
43-
"""
44-
dxdt_hat = (x[2:] - x[0:-2]) / (2 * dt)
45-
first_dxdt_hat = (-3 * x[0] + 4 * x[1] - x[2]) / (2 * dt)
46-
last_dxdt_hat = (3 * x[-1] - 4 * x[-2] + x[-3]) / (2 * dt)
47-
dxdt_hat = np.hstack((first_dxdt_hat, dxdt_hat, last_dxdt_hat))
48-
return x, dxdt_hat
49-
50-
5133
def _x_hat_using_finite_difference(x, dt):
5234
"""Find a smoothed estimate of the true function by taking FD and then integrating with trapezoids
5335
"""
@@ -56,7 +38,6 @@ def _x_hat_using_finite_difference(x, dt):
5638
x0 = utility.estimate_initial_condition(x, x_hat)
5739
return x_hat + x0
5840

59-
6041
def _iterate_first_order(x, dt, num_iterations):
6142
"""Iterative first order centered finite difference.
6243
@@ -79,3 +60,43 @@ def _iterate_first_order(x, dt, num_iterations):
7960
x_hat, dxdt_hat = first_order(x, dt)
8061

8162
return x_hat, dxdt_hat
63+
64+
65+
def second_order(x, dt):
66+
"""Second-order centered difference method, with special endpoint formulas.
67+
68+
:param np.array[float] x: data to differentiate
69+
:param float dt: step size
70+
71+
:return: tuple[np.array, np.array] of\n
72+
- **x_hat** -- estimated (smoothed) x
73+
- **dxdt_hat** -- estimated derivative of x
74+
"""
75+
dxdt_hat = np.zeros(x.shape)
76+
dxdt_hat[1:-1] = x[2:] - x[:-2]
77+
dxdt_hat[0] = -3 * x[0] + 4 * x[1] - x[2]
78+
dxdt_hat[-1] = 3 * x[-1] - 4 * x[-2] + x[-3]
79+
dxdt_hat /= 2*dt
80+
81+
return x, dxdt_hat
82+
83+
84+
def fourth_order(x, dt):
85+
"""Fourth-order centered difference method, with special endpoint formulas.
86+
87+
:param np.array[float] x: data to differentiate
88+
:param float dt: step size
89+
90+
:return: tuple[np.array, np.array] of\n
91+
- **x_hat** -- estimated (smoothed) x
92+
- **dxdt_hat** -- estimated derivative of x
93+
"""
94+
dxdt_hat = np.zeros(x.shape)
95+
dxdt_hat[2:-2] = (8*(x[3:-1] - x[1:-3]) - x[4:] + x[:-4])
96+
dxdt_hat[0] = -25*x[0] + 48*x[1] - 36*x[2] + 16*x[3] - 3*x[4]
97+
dxdt_hat[1] = -3*x[0] - 10*x[1] + 18*x[2] - 6*x[3] + x[4]
98+
dxdt_hat[-2] = 3*x[-1] + 10*x[-2] - 18*x[-3] + 6*x[-4] - x[-5]
99+
dxdt_hat[-1] = 25*x[-1] - 48*x[-2] + 36*x[-3] - 16*x[-4] + 3*x[-5]
100+
dxdt_hat /= 12*dt
101+
102+
return x, dxdt_hat

pynumdiff/tests/test_diff_methods.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from pytest import mark
44
from warnings import warn
55

6+
from ..finite_difference import first_order, second_order, fourth_order
67
from ..linear_model import lineardiff, polydiff, savgoldiff, spectraldiff
78
from ..total_variation_regularization import velocity, acceleration, jerk, iterative_velocity, smooth_acceleration, jerk_sliding
89
from ..kalman_smooth import constant_velocity, constant_acceleration, constant_jerk
910
from ..smooth_finite_difference import mediandiff, meandiff, gaussiandiff, friedrichsdiff, butterdiff, splinediff
10-
from ..finite_difference import first_order, second_order
11-
# Function aliases for testing cases where parameters change the behavior in a big way
11+
# Function alias for testing a case where parameters change the behavior in a big way
1212
def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
1313

1414
dt = 0.1
@@ -33,6 +33,7 @@ def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
3333
(first_order, {}), # empty dictionary for the case of no parameters. no params -> no diff in new vs old
3434
(iterated_first_order, {'num_iterations':5}), (iterated_first_order, [5], {'iterate':True}),
3535
(second_order, {}),
36+
(fourth_order, {}),
3637
(lineardiff, {'order':3, 'gamma':5, 'window_size':11, 'solver':'CLARABEL'}), (lineardiff, [3, 5, 11], {'solver':'CLARABEL'}),
3738
(polydiff, {'polynomial_order':2, 'window_size':3}), (polydiff, [2, 3]),
3839
(savgoldiff, {'polynomial_order':2, 'window_size':4, 'smoothing_win':4}), (savgoldiff, [2, 4, 4]),
@@ -60,23 +61,29 @@ def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
6061
# big ol' table by the method, then the test function, then the pair of quantities we're comparing.
6162
error_bounds = {
6263
first_order: [[(-25, -25), (-25, -25), (0, 0), (1, 1)],
63-
[(-25, -25), (-13, -14), (0, 0), (1, 1)],
64-
[(-25, -25), (0, 0), (0, 0), (1, 0)],
64+
[(-25, -25), (-13, -13), (0, 0), (1, 1)],
6565
[(-25, -25), (0, 0), (0, 0), (1, 1)],
66+
[(-25, -25), (1, 0), (0, 0), (1, 1)],
6667
[(-25, -25), (2, 2), (0, 0), (2, 2)],
6768
[(-25, -25), (3, 3), (0, 0), (3, 3)]],
68-
iterated_first_order: [[(-8, -9), (-11, -11), (0, -1), (0, 0)],
69-
[(-6, -6), (-6, -7), (0, -1), (0, 0)],
70-
[(-1, -1), (0, 0), (0, -1), (0, 0)],
71-
[(0, 0), (1, 0), (0, 0), (1, 0)],
72-
[(1, 1), (2, 2), (1, 1), (2, 2)],
73-
[(1, 1), (3, 3), (1, 1), (3, 3)]],
69+
iterated_first_order: [[(-8, -9), (-25, -25), (0, 0), (1, 1)],
70+
[(-6, -6), (-6, -7), (0, 0), (1, 1)],
71+
[(1, 0), (1, 0), (1, 1), (1, 1)],
72+
[(1, 0), (1, 1), (1, 0), (1, 1)],
73+
[(2, 2), (3, 2), (2, 2), (3, 2)],
74+
[(2, 2), (3, 3), (2, 2), (3, 3)]],
7475
second_order: [[(-25, -25), (-25, -25), (0, 0), (1, 1)],
7576
[(-25, -25), (-13, -13), (0, 0), (1, 1)],
7677
[(-25, -25), (-13, -13), (0, 0), (1, 1)],
7778
[(-25, -25), (0, -1), (0, 0), (1, 1)],
7879
[(-25, -25), (1, 1), (0, 0), (1, 1)],
7980
[(-25, -25), (3, 3), (0, 0), (3, 3)]],
81+
fourth_order: [[(-25, -25), (-25, -25), (0, 0), (1, 1)],
82+
[(-25, -25), (-13, -13), (0, 0), (1, 1)],
83+
[(-25, -25), (-13, -13), (0, 0), (1, 1)],
84+
[(-25, -25), (-2, -2), (0, 0), (1, 1)],
85+
[(-25, -25), (1, 0), (0, 0), (1, 1)],
86+
[(-25, -25), (2, 2), (0, 0), (2, 2)]],
8087
lineardiff: [[(-6, -6), (-5, -6), (0, -1), (0, 0)],
8188
[(0, 0), (1, 1), (0, 0), (1, 1)],
8289
[(1, 0), (2, 2), (1, 0), (2, 2)],

0 commit comments

Comments
 (0)