Skip to content

Commit 917382f

Browse files
committed
supporting variable dt with optimize
1 parent 6161d35 commit 917382f

4 files changed

Lines changed: 21 additions & 9 deletions

File tree

pynumdiff/finite_difference/_finite_difference.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def finitediff(x, dt, num_iterations, order):
3636
dxdt_hat[0] = x_hat[1] - x_hat[0]
3737
dxdt_hat[-1] = x_hat[-1] - x_hat[-2] # use first-order endpoint formulas so as not to amplify noise. See #104
3838

39-
x_hat = utility.integrate_dxdt_hat(dxdt_hat, dt=1) # estimate new x_hat by integrating derivative
39+
x_hat = utility.integrate_dxdt_hat(dxdt_hat, 1) # estimate new x_hat by integrating derivative
4040
# We can skip dividing by dt here and pass dt=1, because the integration multiplies dt back in.
4141
# No need to find integration constant until the very end, because we just differentiate again.
4242
# Note that I also tried integrating with Simpson's rule here, and it seems to do worse. See #104

pynumdiff/linear_model/_linear_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def rbfdiff(x, _t, sigma=1, lmbd=0.01):
252252
if len(x) != len(_t): raise ValueError("If `_t` is given as array-like, must have same length as `x`.")
253253
t = _t
254254

255-
# The below does the approximate equivalent of this code, but sparsely in O(N sigma), since the rbf falls off rapidly
255+
# The below does the approximate equivalent of this code, but sparsely in O(N sigma^2), since the rbf falls off rapidly
256256
# t_i, t_j = np.meshgrid(t,t)
257257
# r = t_j - t_i # radius
258258
# rbf = np.exp(-(r**2) / (2 * sigma**2)) # radial basis function kernel

pynumdiff/tests/test_optimize.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..linear_model import spectraldiff
77
from ..polynomial_fit import polydiff, savgoldiff, splinediff
88
from ..total_variation_regularization import velocity, acceleration, iterative_velocity
9+
from ..kalman_smooth import rtsdiff
910
from ..optimize import optimize
1011
from ..utils.simulate import pi_cruise_control
1112

@@ -92,3 +93,12 @@ def test_polydiff():
9293
params2, val2 = optimize(polydiff, x, dt, tvgamma=tvgamma, search_space_updates={'step_size':1}, padding='auto')
9394
assert (params1['degree'], params1['window_size'], params1['kernel']) == (6, 50, 'friedrichs')
9495
assert (params2['degree'], params2['window_size'], params2['kernel']) == (3, 10, 'gaussian')
96+
97+
def test_rtsdiff_with_irregular_step():
98+
t = np.arange(len(x))*dt
99+
t_irreg = t + np.random.uniform(-dt/10, dt/10, *t.shape) # add jostle
100+
params1, val1 = optimize(rtsdiff, x, t, dxdt_truth=dxdt_truth)
101+
params2, val2 = optimize(rtsdiff, x, t_irreg, dxdt_truth=dxdt_truth)
102+
assert val2 < 1.1*val1 # optimization works and comes out similar, since jostle is small
103+
assert params1['qr_ratio']*0.9 < params2['qr_ratio'] < params1['qr_ratio']*1.1
104+

pynumdiff/utils/utility.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import os, sys, copy, scipy
1+
import os, sys, copy
22
import numpy as np
3+
from scipy.integrate import cumulative_trapezoid
4+
from scipy.optimize import minimize
35

46

57
def hankel_matrix(x, num_delays, pad=False): # fixed delay step of 1
@@ -95,16 +97,16 @@ def peakdet(x, delta, t=None):
9597

9698

9799
# Trapazoidal integration, with 0 first value so that the lengths match. See #88.
98-
def integrate_dxdt_hat(dxdt_hat, dt):
99-
"""Wrapper for scipy.integrate.cumulative_trapezoid to integrate dxdt_hat that ensures
100-
the integral has the same length
100+
def integrate_dxdt_hat(dxdt_hat, _t):
101+
"""Wrapper for scipy.integrate.cumulative_trapezoid
101102
102103
:param np.array[float] dxdt_hat: estimate derivative of timeseries
103-
:param float dt: time step in seconds
104+
:param float _t: stepsize if given as a scalar or a vector of sample locations
104105
105106
:return: **x_hat** (np.array[float]) -- integral of dxdt_hat
106107
"""
107-
return np.hstack((0, scipy.integrate.cumulative_trapezoid(dxdt_hat)))*dt
108+
return cumulative_trapezoid(dxdt_hat, initial=0)*_t if np.isscalar(_t) \
109+
else cumulative_trapezoid(dxdt_hat, x=_t, initial=0)
108110

109111

110112
# Optimization routine to estimate the integration constant.
@@ -118,7 +120,7 @@ def estimate_integration_constant(x, x_hat):
118120
119121
:return: **integration constant** (float) -- initial condition that best aligns x_hat with x
120122
"""
121-
return scipy.optimize.minimize(lambda x0, x, xhat: np.linalg.norm(x - (x_hat+x0)), # fn to minimize in 1st argument
123+
return minimize(lambda x0, x, xhat: np.linalg.norm(x - (x_hat+x0)), # fn to minimize in 1st argument
122124
0, args=(x, x_hat), method='SLSQP').x[0] # result is a vector, even if initial guess is just a scalar
123125

124126

0 commit comments

Comments
 (0)