Skip to content

Commit 07e3b94

Browse files
committed
collapsed and enhanced the 2a and 2b notebooks into a single one and linked readme appropriately, sorted out optimization for robustdiff, reran notebooks, re-added tests for robustdiff
1 parent ab4c42f commit 07e3b94

10 files changed

Lines changed: 921 additions & 1346 deletions

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ The following heuristic works well for choosing `tvgamma`, where `cutoff_frequen
8383

8484
Much more extensive usage is demonstrated in Jupyter notebooks:
8585
* Differentiation with different methods: [1_basic_tutorial.ipynb](https://github.com/florisvb/PyNumDiff/blob/master/examples/1_basic_tutorial.ipynb)
86-
* Parameter Optimization with known ground truth (only for demonstration purpose): [2a_optimizing_parameters_with_dxdt_known.ipynb](https://github.com/florisvb/PyNumDiff/blob/master/examples/2a_optimizing_parameters_with_dxdt_known.ipynb)
87-
* Parameter Optimization with unknown ground truth: [2b_optimizing_parameters_with_dxdt_unknown.ipynb](https://github.com/florisvb/PyNumDiff/blob/master/examples/2b_optimizing_parameters_with_dxdt_unknown.ipynb)
86+
* Parameter Optimization: [2_optimizing_hyperparameters.ipynb](https://github.com/florisvb/PyNumDiff/blob/master/examples/2_optimizing_hyperparameters.ipynb)
8887
* Automatic method suggestion: [3_automatic_method_suggestion.ipynb](https://github.com/florisvb/PyNumDiff/blob/master/examples/3_automatic_method_suggestion.ipynb)
8988

9089
## Repo Structure

examples/1_basic_tutorial.ipynb

Lines changed: 23 additions & 39 deletions
Large diffs are not rendered by default.

examples/2_optimizing_hyperparameters.ipynb

Lines changed: 871 additions & 0 deletions
Large diffs are not rendered by default.

examples/2a_optimizing_parameters_with_dxdt_known.ipynb

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

examples/2b_optimizing_parameters_with_dxdt_unknown.ipynb

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

examples/4_performance_analysis.ipynb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
},
7575
{
7676
"cell_type": "code",
77-
"execution_count": 3,
77+
"execution_count": null,
7878
"id": "1eba51f1-bf31-4f84-b82b-176461319de6",
7979
"metadata": {},
8080
"outputs": [],
@@ -90,9 +90,7 @@
9090
"\t\t\t(tvrdiff, 'TVRDiff'),\n",
9191
"\t\t\t(smooth_acceleration, 'SmoothAccelTVR'),\n",
9292
"\t\t\t(rtsdiff, 'RTSDiff'),\n",
93-
"\t\t\t(robustdiff, 'RobustDiff'),\n",
94-
"\t\t (robustdiff, 'RobustDiff2'),\n",
95-
"\t\t (robustdiff, 'RobustDiff3')]\n",
93+
"\t\t\t(robustdiff, 'RobustDiff')]\n",
9694
"sims = [(pi_cruise_control, 'Cruise Control'),\n",
9795
"\t\t(sine, 'Sum of Sines'),\n",
9896
"\t\t(triangle, 'Triangles'),\n",

pynumdiff/kalman_smooth/_kalman_smooth.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ def robustdiff(x, dt, order, log_q, log_r, proc_huberM=6, meas_huberM=0):
276276
norm case, because :math:`c_2` approaches :math:`\\frac{\\sqrt{2}}{M}`, cancelling the :math:`M` multiplying :math:`|\\cdot|`
277277
and leaving behind :math:`\\sqrt{2}`, the proper normalization.
278278
279+
Note that :code:`log_q` and :code:`proc_huberM` are coupled, as are :code:`log_r` and :code:`meas_huberM`, via the relation
280+
:math:`\\text{Huber}(q^{-1/2}v, M) = q^{-1}\\text{Huber}(v, Mq^{-1/2})`, but they are still independent enough that for
281+
the purposes of optimization we cannot collapse them.
282+
279283
:param np.array[float] x: data series to differentiate
280284
:param float dt: step size
281285
:param int order: which derivative to stabilize in the constant-derivative model (1=velocity, 2=acceleration, 3=jerk)
@@ -337,7 +341,7 @@ def huber_const(M): # from https://jmlr.org/papers/volume14/aravkin13a/aravkin13
337341
else huber_const(meas_huberM)*cvxpy.sum(cvxpy.huber(meas_resids, meas_huberM)) # CVXPY quirk: norm(, 1) != sum(abs()) for matrices
338342

339343
problem = cvxpy.Problem(cvxpy.Minimize(objective))
340-
try: problem.solve(solver=cvxpy.CLARABEL); print("CLARABEL succeeded")
344+
try: problem.solve(solver=cvxpy.CLARABEL)
341345
except cvxpy.error.SolverError: pass # Could try another solver here, like SCS, but slows things down
342346

343347
if x_states.value is None: return np.full((A.shape[0], N), np.nan) # There can be solver failure, even without error

pynumdiff/optimize/_optimize.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@
6060
'pad_to_zero_dxdt': {True, False},
6161
'high_freq_cutoff': [1e-3, 5e-2, 1e-2, 5e-2, 1e-1]}, # give numerical params in a list to scipy.optimize over them
6262
{'high_freq_cutoff': (1e-5, 1-1e-5)}),
63-
rbfdiff: ({'sigma': [1e-3, 1e-2, 1e-1, 1],
63+
rbfdiff: ({'sigma': [1e-2, 1e-1, 1],
6464
'lmbd': [1e-3, 1e-2, 1e-1]},
65-
{'sigma': (1e-3, 1e3),
66-
'lmbd': (1e-4, 0.5)}),
65+
{'sigma': (1e-2, 1e3),
66+
'lmbd': (1e-3, 0.5)}),
6767
tvrdiff: ({'gamma': [1e-2, 1e-1, 1, 10, 100, 1000],
6868
'order': {1, 2, 3}}, # warning: order 1 hacks the loss function when tvgamma is used, tends to win but is usually suboptimal choice in terms of true RMSE
6969
{'gamma': (1e-4, 1e7)}),
@@ -87,23 +87,15 @@
8787
'forwardbackward': {True, False}},
8888
{'q': (1e-10, 1e10),
8989
'r': (1e-10, 1e10)}),
90-
# robustdiff: ({'order': {1, 2, 3}, # warning: order 1 hacks the loss function when tvgamma is used, tends to win but is usually suboptimal choice in terms of true RMSE
91-
# 'log_q': [1., 4, 8, 12], # decimal after first entry ensure this is treated as float type
92-
# 'log_r': [-1., 1, 4, 8],
93-
# #'proc_huberM': [0., 2, 6], # 0 is l1 norm, 1.345 is Huber 95% "efficiency", 2 assumes about 5% outliers,
94-
# 'meas_huberM': [0., 2, 6]}, # and 6 assumes basically no outliers -> l2 norm. Try (1 - norm.cdf(M))*2 to see outlier portion
95-
# {'log_q': (-1, 18),
96-
# 'log_r': (-5, 18),
97-
# 'proc_huberM': (0, 6),
98-
# 'meas_huberM': (0, 6)}),
9990
robustdiff: ({'order': {1, 2, 3}, # warning: order 1 hacks the loss function when tvgamma is used, tends to win but is usually suboptimal choice in terms of true RMSE
100-
'log_q': [1., 4, 8, 12], # decimal after first entry ensure this is treated as float type
101-
'log_r': [-1., 1, 4, 8],
102-
#'qr_ratio': [10**k for k in range(-1, 16, 4)],
103-
'huberM': [0., 5, 10]}, # 0. so type is float. Good choices here really depend on the data scale
104-
{'log_q': (0, 18),
105-
'log_r': (-5, 10),
106-
'huberM': (0, 20)}), # really only want to use quadratic when nearby; 20sigma is a huge distance
91+
'log_q': [1., 4, 7, 10, 13, 16], # decimal after first entry ensure this is treated as float type
92+
'log_r': [-1., 2, 5, 8, 11],
93+
'proc_huberM': [0., 2, 6], # 0 is l1 norm, 1.345 is Huber 95% "efficiency", 2 assumes about 5% outliers,
94+
'meas_huberM': [0., 2, 6]}, # 6 assumes basically no outliers per outlier_portion = (1 - norm.cdf(M))*2
95+
{'log_q': (-5, 18),
96+
'log_r': (-5, 18),
97+
'proc_huberM': (0, 6),
98+
'meas_huberM': (0, 6)}),
10799
lineardiff: ({'kernel': 'gaussian',
108100
'order': 3,
109101
'gamma': [1e-1, 1, 10, 100],

pynumdiff/tests/test_diff_methods.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def spline_irreg_step(*args, **kwargs): return splinediff(*args, **kwargs)
5151
(constant_acceleration, {'r':1e-3, 'q':1e4}), (constant_acceleration, [1e-3, 1e4]),
5252
(constant_jerk, {'r':1e-4, 'q':1e5}), (constant_jerk, [1e-4, 1e5]),
5353
(rtsdiff, {'order':2, 'log_qr_ratio':7, 'forwardbackward':True}),
54-
#(robustdiff, {'order':3, 'log_q':8, 'log_r':0}), # Add back when design stabilizes
54+
(robustdiff, {'order':3, 'log_q':7, 'log_r':2}),
5555
(velocity, {'gamma':0.5}), (velocity, [0.5]),
5656
(acceleration, {'gamma':1}), (acceleration, [1]),
5757
(jerk, {'gamma':10}), (jerk, [10]),
@@ -223,10 +223,10 @@ def spline_irreg_step(*args, **kwargs): return splinediff(*args, **kwargs)
223223
[(-2, -3), (0, 0), (0, -1), (1, 1)],
224224
[(-1, -2), (1, 1), (0, -1), (1, 1)],
225225
[(0, 0), (3, 3), (0, 0), (3, 3)]],
226-
robustdiff: [[(-14, -15), (-17, -17), (0, -1), (1, 1)],
227-
[(-14, -14), (-13, -13), (0, -1), (1, 1)],
228-
[(-14, -14), (-13, -13), (0, -1), (1, 1)],
229-
[(-12, -12), (-2, -2), (0, -1), (1, 1)],
226+
robustdiff: [[(-15, -15), (-14, -15), (0, -1), (1, 1)],
227+
[(-14, -15), (-13, -13), (0, -1), (1, 1)],
228+
[(-14, -15), (-13, -13), (0, -1), (1, 1)],
229+
[(-9, -9), (-2, -2), (0, -1), (1, 1)],
230230
[(0, 0), (2, 2), (0, 0), (2, 2)],
231231
[(1, 1), (3, 3), (1, 1), (3, 3)]],
232232
lineardiff: [[(-7, -8), (-14, -14), (0, -1), (0, 0)],

pynumdiff/utils/evaluate.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ def plot(x, dt, x_hat, dxdt_hat, x_truth, dxdt_truth, xlim=None, show_error=True
4848
axes[1].legend(loc='lower right', fontsize=12)
4949

5050
if show_error:
51-
rms_dxdt = rmse(dxdt_truth, dxdt_hat)
51+
rmse_dxdt = rmse(dxdt_truth, dxdt_hat)
5252
R_sqr = error_correlation(dxdt_truth, dxdt_hat)
53-
axes[1].text(0.05, 0.95, f"RMSE = {rms_dxdt:.2f}\n$R^2$ = {R_sqr:.2g}",
53+
axes[1].text(0.05, 0.95, f"RMSE = {rmse_dxdt:.2f}\n$R^2$ = {R_sqr:.2g}",
5454
transform=axes[1].transAxes, fontsize=15, verticalalignment='top')
5555

5656
return fig, axes
@@ -80,7 +80,7 @@ def plot_comparison(dt, dxdt_truth, dxdt_hat1, title1, dxdt_hat2, title2, dxdt_h
8080

8181
def robust_rme(x, x_hat, padding=0, M=6):
8282
"""Robustified/Huberized Root Mean Error metric, used to determine fit between smooth estimate and data.
83-
Equals np.linalg.norm(x[s] - x_hat[s]) / np.sqrt(N) if M=float('inf'), and dang close for even M=6 or even 2.
83+
Equals np.linalg.norm(x[s] - x_hat[s]) / np.sqrt(N) if M=float('inf'), and dang close for M=6 or even 2.
8484
8585
:param np.array[float] x: noisy data
8686
:param np.array[float] x_hat: estimated smoothed signal, returned by differentiation algorithms in addition

0 commit comments

Comments
 (0)