Skip to content

Commit 3a6433a

Browse files
committed
moved rest of linear_model optimization
1 parent aa5f4b7 commit 3a6433a

6 files changed

Lines changed: 69 additions & 185 deletions

File tree

pynumdiff/linear_model/_linear_model.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
#########################
1414
# Savitzky-Golay filter #
1515
#########################
16-
def savgoldiff(x, dt, params=None, options=None, polynomial_order=None, window_size=None, smoothing_win=None):
16+
def savgoldiff(x, dt, params=None, options=None, poly_order=None, window_size=None, smoothing_win=None):
1717
"""Use the Savitzky-Golay to smooth the data and calculate the first derivative. It wses
1818
scipy.signal.savgol_filter. The Savitzky-Golay is very similar to the sliding polynomial fit,
1919
but slightly noisier, and much faster
2020
2121
:param np.array[float] x: data to differentiate
2222
:param float dt: step size
23-
:param list params: (**deprecated**, prefer :code:`polynomial_order`, :code:`window_size`, and :code:`smoothing_win`)
23+
:param list params: (**deprecated**, prefer :code:`poly_order`, :code:`window_size`, and :code:`smoothing_win`)
2424
:param dict options: (**deprecated**)
25-
:param int polynomial_order: order of the polynomial
25+
:param int poly_order: order of the polynomial
2626
:param int window_size: size of the sliding window, must be odd (if not, 1 is added)
2727
:param int smoothing_win: size of the window used for gaussian smoothing, a good default is
2828
window_size, but smaller for high frequnecy data
@@ -32,17 +32,17 @@ def savgoldiff(x, dt, params=None, options=None, polynomial_order=None, window_s
3232
- **dxdt_hat** -- estimated derivative of x
3333
"""
3434
if params != None: # Warning to support old interface for a while. Remove these lines along with params in a future release.
35-
warn("`params` and `options` parameters will be removed in a future version. Use `polynomial_order`, " +
35+
warn("`params` and `options` parameters will be removed in a future version. Use `poly_order`, " +
3636
"`window_size`, and `smoothing_win` instead.", DeprecationWarning)
37-
polynomial_order, window_size, smoothing_win = params
38-
elif polynomial_order == None or window_size == None or smoothing_win == None:
39-
raise ValueError("`polynomial_order`, `window_size`, and `smoothing_win` must be given.")
37+
poly_order, window_size, smoothing_win = params
38+
elif poly_order == None or window_size == None or smoothing_win == None:
39+
raise ValueError("`poly_order`, `window_size`, and `smoothing_win` must be given.")
4040

41-
window_size = np.clip(window_size, polynomial_order + 1, len(x)-1)
41+
window_size = np.clip(window_size, poly_order + 1, len(x)-1)
4242
if not window_size % 2: window_size += 1 # window_size needs to be odd
4343
smoothing_win = min(smoothing_win, len(x)-1)
4444

45-
dxdt_hat = scipy.signal.savgol_filter(x, window_size, polynomial_order, deriv=1)/dt
45+
dxdt_hat = scipy.signal.savgol_filter(x, window_size, poly_order, deriv=1)/dt
4646

4747
kernel = utility.gaussian_kernel(smoothing_win)
4848
dxdt_hat = utility.convolutional_smoother(dxdt_hat, kernel)
@@ -57,16 +57,16 @@ def savgoldiff(x, dt, params=None, options=None, polynomial_order=None, window_s
5757
######################
5858
# Polynomial fitting #
5959
######################
60-
def polydiff(x, dt, params=None, options=None, polynomial_order=None, window_size=None, step_size=1,
60+
def polydiff(x, dt, params=None, options=None, poly_order=None, window_size=None, step_size=1,
6161
kernel='friedrichs'):
6262
"""Fit polynomials to the data, and differentiate the polynomials.
6363
6464
:param np.array[float] x: data to differentiate
6565
:param float dt: step size
66-
:param list[int] params: (**deprecated**, prefer :code:`polynomial_order` and :code:`window_size`)
66+
:param list[int] params: (**deprecated**, prefer :code:`poly_order` and :code:`window_size`)
6767
:param dict options: (**deprecated**, prefer :code:`step_size` and :code:`kernel`)
6868
a dictionary consisting of {'sliding': (bool), 'step_size': (int), 'kernel_name': (str)}
69-
:param int polynomial_order: order of the polynomial
69+
:param int poly_order: order of the polynomial
7070
:param int window_size: size of the sliding window, if not given no sliding
7171
:param int step_size: step size for sliding
7272
:param str kernel: name of kernel to use for weighting and smoothing windows ('gaussian' or 'friedrichs')
@@ -76,24 +76,27 @@ def polydiff(x, dt, params=None, options=None, polynomial_order=None, window_siz
7676
- **dxdt_hat** -- estimated derivative of x
7777
"""
7878
if params != None:
79-
warn("`params` and `options` parameters will be removed in a future version. Use `polynomial_order` " +
79+
warn("`params` and `options` parameters will be removed in a future version. Use `poly_order` " +
8080
"and `window_size` instead.", DeprecationWarning)
81-
polynomial_order = params[0]
81+
poly_order = params[0]
8282
if len(params) > 1: window_size = params[1]
8383
if options != None:
8484
if 'sliding' in options and not options['sliding']: window_size = None
8585
if 'step_size' in options: step_size = options['step_size']
8686
if 'kernel_name' in options: kernel = options['kernel_name']
87-
elif polynomial_order == None or window_size == None:
88-
raise ValueError("`polynomial_order` and `window_size` must be given.")
87+
elif poly_order == None or window_size == None:
88+
raise ValueError("`poly_order` and `window_size` must be given.")
8989

90-
if window_size < polynomial_order*3:
91-
window_size = polynomial_order*3+1
90+
if window_size < poly_order*3:
91+
window_size = poly_order*3+1
92+
if window_size % 2 == 0:
93+
window_size += 1
94+
warn("Kernel window size should be odd. Added 1 to length.")
9295

93-
def _polydiff(x, dt, polynomial_order, weights=None):
96+
def _polydiff(x, dt, poly_order, weights=None):
9497
t = np.arange(len(x))*dt
9598

96-
r = np.polyfit(t, x, polynomial_order, w=weights) # polyfit returns highest order first
99+
r = np.polyfit(t, x, poly_order, w=weights) # polyfit returns highest order first
97100
dr = np.polyder(r) # power rule already implemented for us
98101

99102
dxdt_hat = np.polyval(dr, t) # evaluate the derivative and original polynomials at points t
@@ -102,10 +105,10 @@ def _polydiff(x, dt, polynomial_order, weights=None):
102105
return x_hat, dxdt_hat
103106

104107
if not window_size:
105-
return _polydiff(x, dt, polynomial_order)
108+
return _polydiff(x, dt, poly_order)
106109

107110
kernel = {'gaussian':utility.gaussian_kernel, 'friedrichs':utility.friedrichs_kernel}[kernel](window_size)
108-
return utility.slide_function(_polydiff, x, dt, kernel, polynomial_order, stride=step_size, pass_weights=True)
111+
return utility.slide_function(_polydiff, x, dt, kernel, poly_order, stride=step_size, pass_weights=True)
109112

110113

111114
#############

pynumdiff/optimize/_optimize.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,43 @@
33
import numpy as np
44
from itertools import product
55
from functools import partial
6+
from warnings import filterwarnings
67
from multiprocessing import Pool
78

89
from pynumdiff.utils import evaluate
910

10-
from ..linear_model import spectraldiff, polydiff
11+
from ..linear_model import spectraldiff, polydiff, savgoldiff, lineardiff
1112

1213

1314
# Map from method -> (init_conds, type_low_hi)
1415
method_params_and_bounds = {
1516
spectraldiff: ({'even_extension': True,
1617
'pad_to_zero_dxdt': True,
1718
'high_freq_cutoff': [1e-3, 5e-2, 1e-2, 5e-2, 1e-1]},
18-
{'high_freq_cutoff': [1e-5, 1-1e-5]}),
19-
polydiff: ({'sliding': True,
20-
'step_size': 1,
19+
{'high_freq_cutoff': (1e-5, 1-1e-5)}),
20+
polydiff: ({'step_size': 1,
2121
'kernel': 'friedrichs',
22-
'order': [2, 3, 5, 7],
23-
'window_size': [10, 30, 50, 90, 130]},
24-
{'order': [1, 8],
25-
'window_size': [10, 1000]})
22+
'poly_order': [2, 3, 5, 7],
23+
'window_size': [11, 31, 51, 91, 131]},
24+
{'poly_order': (1, 8),
25+
'window_size': (10, 1000)}),
26+
savgoldiff: ({'poly_order': [2, 3, 5, 7, 9, 11, 13],
27+
'window_size': [3, 10, 30, 50, 90, 130, 200, 300],
28+
'smoothing_win': [3, 10, 30, 50, 90, 130, 200, 300]},
29+
{'poly_order': (1, 12),
30+
'window_size': (3, 1000),
31+
'smoothing_win': (3, 1000)}),
32+
#chebydiff ({order: [3, 5, 7, 9],
33+
# window_size: [10, 30, 50, 90, 130],
34+
# kernel: 'friedrichs'},
35+
# {order: (1, 10),
36+
# window_size: (10, 1000)})
37+
lineardiff: ({'kernel': 'gaussian',
38+
'order': 3,
39+
'gamma': [1e-1, 1, 10, 100],
40+
'window_size': [10, 30, 50, 90, 130]},
41+
{'gamma': (1e-3, 1000),
42+
'window_size': (15, 1000)}),
2643
}
2744

2845

@@ -110,7 +127,7 @@ def optimize(func, x, dt, init_conds={}, dxdt_truth=None, tvgamma=1e-2, padding=
110127
padding=padding)
111128
_minimize = partial(scipy.optimize.minimize, _obj_fun, method=opt_method, bounds=bounds, options=opt_kwargs)
112129

113-
with Pool() as pool: # The heavy lifting
130+
with Pool(initializer=filterwarnings, initargs=["ignore", '', UserWarning]) as pool: # The heavy lifting
114131
results = pool.map(_minimize, search_space) # returns a bunch of OptimizeResult objects
115132

116133
opt_idx = np.nanargmin([r.fun for r in results])

pynumdiff/optimize/linear_model/__init__.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

pynumdiff/optimize/linear_model/__linear_model__.py

Lines changed: 0 additions & 120 deletions
This file was deleted.

pynumdiff/tests/test_diff_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
3434
(iterated_first_order, {'num_iterations':5}), (iterated_first_order, [5], {'iterate':True}),
3535
(second_order, {}),
3636
(lineardiff, {'order':3, 'gamma':5, 'window_size':11, 'solver':'CLARABEL'}), (lineardiff, [3, 5, 11], {'solver':'CLARABEL'}),
37-
(polydiff, {'polynomial_order':2, 'window_size':3}), (polydiff, [2, 3]),
38-
(savgoldiff, {'polynomial_order':2, 'window_size':4, 'smoothing_win':4}), (savgoldiff, [2, 4, 4]),
37+
(polydiff, {'poly_order':2, 'window_size':3}), (polydiff, [2, 3]),
38+
(savgoldiff, {'poly_order':2, 'window_size':4, 'smoothing_win':4}), (savgoldiff, [2, 4, 4]),
3939
(spectraldiff, {'high_freq_cutoff':0.1}), (spectraldiff, [0.1]),
4040
(mediandiff, {'window_size':3, 'num_iterations':2}), (mediandiff, [3, 2], {'iterate':True}),
4141
(meandiff, {'window_size':3, 'num_iterations':2}), (meandiff, [3, 2], {'iterate':True}),

pynumdiff/tests/test_optimize.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
"""
2-
Unit tests for optimization module
3-
"""
4-
# pylint: skip-file
51
import numpy as np
6-
import pytest
2+
from pytest import mark, skip
73

84
#from pynumdiff.optimize.finite_difference import first_order
95
#from pynumdiff.optimize.smooth_finite_difference import mediandiff, meandiff, gaussiandiff, \
@@ -14,7 +10,7 @@
1410
# constant_jerk
1511
from pynumdiff.utils.simulate import pi_control
1612

17-
from ..linear_model import spectraldiff, polydiff
13+
from ..linear_model import spectraldiff, polydiff, savgoldiff
1814
from ..optimize import optimize
1915

2016

@@ -29,10 +25,6 @@
2925
log_gamma = -1.6 * np.log(cutoff_frequency) - 0.71 * np.log(dt) - 5.1
3026
tvgamma = np.exp(log_gamma)
3127

32-
def get_err_msg(actual_params, desired_params):
33-
err_msg = 'Actual params were: ' + ', '.join(map(str, actual_params)) + ' instead of: ' + ', '.join(map(str, desired_params))
34-
return err_msg
35-
3628

3729
# def test_first_order():
3830
# params_1, val_1 = first_order(x, dt, params=None, options={'iterate': True},
@@ -78,10 +70,10 @@ def get_err_msg(actual_params, desired_params):
7870
# params_1, val_1 = butterdiff(x, dt, params=None, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
7971
# params_2, val_2 = butterdiff(x, dt, params=None, tvgamma=0, dxdt_truth=None)
8072

81-
# np.testing.assert_array_less( np.abs(params_1[0] - 9), 1.001, err_msg=get_err_msg(params_1, [9, 0.157]))
82-
# np.testing.assert_array_less( np.abs(params_1[1] - 0.157), 0.01, err_msg=get_err_msg(params_1, [9, 0.157]))
83-
# #np.testing.assert_almost_equal(params_1, [9, 0.157], decimal=3, err_msg=get_err_msg(params_1, [9, 0.157]))
84-
# np.testing.assert_almost_equal(params_2, [3, 0.99], decimal=3, err_msg=get_err_msg(params_2, [3, 0.99]))
73+
# np.testing.assert_array_less( np.abs(params_1[0] - 9), 1.001)
74+
# np.testing.assert_array_less( np.abs(params_1[1] - 0.157), 0.01)
75+
# #np.testing.assert_almost_equal(params_1, [9, 0.157], decimal=3)
76+
# np.testing.assert_almost_equal(params_2, [3, 0.99], decimal=3)
8577

8678
# def test_splinediff():
8779
# params_1, val_1 = splinediff(x, dt, params=None, options={'iterate': True},
@@ -131,23 +123,23 @@ def get_err_msg(actual_params, desired_params):
131123
# np.testing.assert_array_less(param_1_error, 2)
132124
# np.testing.assert_array_less(param_2_error, 2)
133125

134-
# def test_savgoldiff():
135-
# params_1, val_1 = savgoldiff(x, dt, params=None, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
136-
# params_2, val_2 = savgoldiff(x, dt, params=None, tvgamma=0, dxdt_truth=None)
137-
# assert params_1 == [10, 59, 3]
138-
# assert params_2 == [9, 3, 3]
126+
def test_savgoldiff():
127+
params_1, val_1 = optimize(savgoldiff, x, dt, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
128+
params_2, val_2 = optimize(savgoldiff, x, dt, tvgamma=0, dxdt_truth=None)
129+
assert (params_1['poly_order'], params_1['window_size'], params_1['smoothing_win']) == (10, 57, 3)
130+
assert (params_2['poly_order'], params_2['window_size'], params_2['smoothing_win']) == (9, 4, 3)
139131

140132
def test_spectraldiff():
141133
params_1, val_1 = optimize(spectraldiff, x, dt, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
142134
params_2, val_2 = optimize(spectraldiff, x, dt, tvgamma=0)
143135
np.testing.assert_almost_equal(params_1['high_freq_cutoff'], 0.0913, decimal=3)
144136
np.testing.assert_almost_equal(params_2['high_freq_cutoff'], 0.575, decimal=3)
145137

146-
# def test_polydiff():
147-
# params_1, val_1 = polydiff(x, dt, params=None, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
148-
# params_2, val_2 = polydiff(x, dt, params=None, tvgamma=0, dxdt_truth=None)
149-
# assert params_1 == [2, 10]
150-
# assert params_2 == [2, 10]
138+
def test_polydiff():
139+
params_1, val_1 = optimize(polydiff, x, dt, tvgamma=tvgamma, dxdt_truth=dxdt_truth)
140+
params_2, val_2 = optimize(polydiff, x, dt, tvgamma=0, dxdt_truth=None)
141+
assert (params_1['poly_order'], params_1['window_size']) == (6, 50)
142+
assert (params_2['poly_order'], params_2['window_size']) == (4, 10)
151143

152144
# def test_chebydiff(self):
153145
# try:

0 commit comments

Comments
 (0)