44from warnings import warn
55
66from ..linear_model import lineardiff , polydiff , savgoldiff , spectraldiff
7- from ..total_variation_regularization import velocity , acceleration , jerk , iterative_velocity
8- from ..kalman_smooth import constant_velocity , constant_acceleration , constant_jerk , known_dynamics
7+ from ..total_variation_regularization import velocity , acceleration , jerk , iterative_velocity , smooth_acceleration
8+ from ..kalman_smooth import constant_velocity , constant_acceleration , constant_jerk
99from ..smooth_finite_difference import mediandiff , meandiff , gaussiandiff , friedrichsdiff , butterdiff , splinediff
1010from ..finite_difference import first_order , second_order
1111# Function aliases for testing cases where parameters change the behavior in a big way
@@ -47,6 +47,12 @@ def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
4747 (constant_acceleration , {'r' :1e-4 , 'q' :1e-1 }), (constant_acceleration , [1e-4 , 1e-1 ]),
4848 (constant_jerk , {'r' :1e-4 , 'q' :10 }), (constant_jerk , [1e-4 , 10 ]),
4949 # TODO (known_dynamics), but presently it doesn't calculate a derivative
50+ (velocity , {'gamma' :0.5 }), (velocity , [0.5 ]),
51+ (acceleration , {'gamma' :1 }), (acceleration , [1 ]),
52+ (jerk , {'gamma' :10 }), (jerk , [10 ]),
53+ (iterative_velocity , {'num_iterations' :5 , 'gamma' :0.05 }), (iterative_velocity , [5 , 0.05 ]),
54+ (smooth_acceleration , {'gamma' :2 , 'window_size' :5 }), (smooth_acceleration , [2 , 5 ])
55+ # TODO (jerk_sliding), because with the test cases here (len < 1000) it would just be a duplicate of jerk
5056 ]
5157
5258# All the testing methodology follows the exact same pattern; the only thing that changes is the
@@ -148,7 +154,37 @@ def iterated_first_order(*args, **kwargs): return first_order(*args, **kwargs)
148154 [(- 4 , - 4 ), (- 2 , - 2 ), (0 , 0 ), (1 , 0 )],
149155 [(- 1 , - 2 ), (1 , 0 ), (0 , 0 ), (1 , 0 )],
150156 [(1 , 0 ), (2 , 2 ), (1 , 0 ), (2 , 2 )],
151- [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]]
157+ [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]],
158+ velocity : [[(- 25 , - 25 ), (- 18 , - 19 ), (0 , - 1 ), (1 , 0 )],
159+ [(- 12 , - 12 ), (- 11 , - 12 ), (- 1 , - 1 ), (- 1 , - 2 )],
160+ [(0 , 0 ), (1 , 0 ), (0 , 0 ), (1 , 0 )],
161+ [(0 , - 1 ), (1 , 1 ), (0 , 0 ), (1 , 0 )],
162+ [(1 , 1 ), (2 , 2 ), (1 , 1 ), (2 , 2 )],
163+ [(1 , 0 ), (3 , 3 ), (1 , 0 ), (3 , 3 )]],
164+ acceleration : [[(- 25 , - 25 ), (- 18 , - 18 ), (0 , - 1 ), (0 , 0 )],
165+ [(- 10 , - 10 ), (- 9 , - 9 ), (- 1 , - 1 ), (0 , - 1 )],
166+ [(- 10 , - 10 ), (- 9 , - 10 ), (- 1 , - 1 ), (0 , - 1 )],
167+ [(0 , - 1 ), (1 , 0 ), (0 , - 1 ), (1 , 0 )],
168+ [(1 , 1 ), (2 , 2 ), (1 , 1 ), (2 , 2 )],
169+ [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]],
170+ jerk : [[(- 25 , - 25 ), (- 18 , - 18 ), (- 1 , - 1 ), (0 , 0 )],
171+ [(- 9 , - 10 ), (- 9 , - 9 ), (- 1 , - 1 ), (0 , 0 )],
172+ [(- 10 , - 10 ), (- 9 , - 10 ), (- 1 , - 1 ), (0 , 0 )],
173+ [(0 , 0 ), (1 , 1 ), (0 , 0 ), (1 , 1 )],
174+ [(1 , 1 ), (2 , 2 ), (1 , 1 ), (2 , 2 )],
175+ [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]],
176+ iterative_velocity : [[(- 9 , - 10 ), (- 25 , - 25 ), (0 , - 1 ), (0 , 0 )],
177+ [(0 , 0 ), (0 , 0 ), (0 , 0 ), (1 , 0 )],
178+ [(0 , 0 ), (1 , 0 ), (1 , 0 ), (1 , 0 )],
179+ [(1 , 0 ), (1 , 1 ), (1 , 0 ), (1 , 1 )],
180+ [(2 , 1 ), (2 , 2 ), (2 , 1 ), (2 , 2 )],
181+ [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]],
182+ smooth_acceleration : [[(- 9 , - 10 ), (- 18 , - 18 ), (0 , - 1 ), (0 , 0 )],
183+ [(- 9 , - 9 ), (- 10 , - 10 ), (- 1 , - 1 ), (- 1 , - 1 )],
184+ [(- 2 , - 2 ), (- 1 , - 1 ), (- 1 , - 1 ), (0 , - 1 )],
185+ [(0 , 0 ), (1 , 0 ), (0 , - 1 ), (1 , 0 )],
186+ [(1 , 1 ), (2 , 2 ), (1 , 1 ), (2 , 2 )],
187+ [(1 , 1 ), (3 , 3 ), (1 , 1 ), (3 , 3 )]]
152188}
153189
154190# Essentially run the cartesian product of [diff methods] x [test functions] through this one test
@@ -162,13 +198,14 @@ def test_diff_method(diff_method_and_params, test_func_and_deriv, request): # re
162198 i , latex_name , f , df = test_func_and_deriv
163199
164200 # some methods rely on cvxpy, and we'd like to allow use of pynumdiff without convex optimization
165- if diff_method in [lineardiff , velocity ]:
201+ if diff_method in [lineardiff , velocity , acceleration , jerk , smooth_acceleration ]:
166202 try : import cvxpy
167203 except : warn (f"Cannot import cvxpy, skipping { diff_method } test." ); return
168204
169- x = f (t ) # sample the function
170- x_noisy = x + noise # add a little noise
171- dxdt = df (t ) # true values of the derivative at samples
205+ # sample the true function and make noisy samples, and sample true derivative
206+ x = f (t )
207+ x_noisy = x + noise
208+ dxdt = df (t )
172209
173210 # differentiate without and with noise, accounting for new and old styles of calling functions
174211 x_hat , dxdt_hat = diff_method (x , dt , ** params ) if isinstance (params , dict ) \
@@ -178,6 +215,7 @@ def test_diff_method(diff_method_and_params, test_func_and_deriv, request): # re
178215 else diff_method (x_noisy , dt , params ) if (isinstance (params , list ) and len (diff_method_and_params ) < 3 ) \
179216 else diff_method (x_noisy , dt , params , options )
180217
218+ # plotting code
181219 if request .config .getoption ("--plot" ) and not isinstance (params , list ): # Get the plot flag from pytest configuration
182220 fig , axes = request .config .plots [diff_method ] # get the appropriate plot, set up by the store_plots fixture in conftest.py
183221 axes [i , 0 ].plot (t , f (t ), label = r"$x(t)$" )
@@ -198,16 +236,20 @@ def test_diff_method(diff_method_and_params, test_func_and_deriv, request): # re
198236 if i == 0 : axes [i , 1 ].set_title ('with noise' )
199237
200238 # check x_hat and x_hat_noisy are close to x and that dxdt_hat and dxdt_hat_noisy are close to dxdt
201- if request .config .getoption ("--bounds" ): print ("] \n [" , end = "" )
239+ if request .config .getoption ("--bounds" ): print ("\n [" , end = "" )
202240 for j ,(a ,b ) in enumerate ([(x ,x_hat ), (dxdt ,dxdt_hat ), (x ,x_hat_noisy ), (dxdt ,dxdt_hat_noisy )]):
241+ l2_error = np .linalg .norm (a - b )
242+ linf_error = np .max (np .abs (a - b ))
243+
244+ # bounds-printing for establishing bounds
203245 if request .config .getoption ("--bounds" ):
204- l2_error = np .linalg .norm (a - b )
205- linf_error = np .max (np .abs (a - b ))
206246 #print(f"({l2_error},{linf_error})", end=", ")
207247 print (f"({ int (np .ceil (np .log10 (l2_error ))) if l2_error > 0 else - 25 } , { int (np .ceil (np .log10 (linf_error ))) if linf_error > 0 else - 25 } )" , end = ", " )
248+ # bounds checking
208249 else :
209250 log_l2_bound , log_linf_bound = error_bounds [diff_method ][i ][j ]
210- assert np .linalg .norm (a - b ) < 10 ** log_l2_bound
211- assert np .max (np .abs (a - b )) < 10 ** log_linf_bound
212- if 0 < np .linalg .norm (a - b ) < 10 ** (log_l2_bound - 1 ) or 0 < np .max (np .abs (a - b )) < 10 ** (log_linf_bound - 1 ):
251+ assert l2_error < 10 ** log_l2_bound
252+ assert linf_error < 10 ** log_linf_bound
253+ # methods that get super duper close can converge to different very small limits on different runs
254+ if 1e-18 < l2_error < 10 ** (log_l2_bound - 1 ) or 1e-18 < linf_error < 10 ** (log_linf_bound - 1 ):
213255 print (f"Improvement detected for method { diff_method .__name__ } " )
0 commit comments