|
3 | 3 | from scipy.linalg import expm, sqrtm |
4 | 4 | from scipy.stats import norm |
5 | 5 | from time import time |
6 | | - |
7 | 6 | try: import cvxpy |
8 | 7 | except ImportError: pass |
9 | 8 |
|
| 9 | +from pynumdiff.utils.utility import huber_const |
| 10 | + |
10 | 11 |
|
11 | 12 | def kalman_filter(y, dt_or_t, xhat0, P0, A, Q, C, R, B=None, u=None, save_P=True): |
12 | 13 | """Run the forward pass of a Kalman filter, with regular or irregular step size. |
@@ -278,7 +279,7 @@ def robustdiff(x, dt, order, log_q, log_r, proc_huberM=6, meas_huberM=0): |
278 | 279 | proper :math:`\\ell_1` normalization. |
279 | 280 |
|
280 | 281 | Note that :code:`log_q` and :code:`proc_huberM` are coupled, as are :code:`log_r` and :code:`meas_huberM`, via the relation |
281 | | - :math:`\\text{Huber}(q^{-1/2}v, M) = q^{-1}\\text{Huber}(v, Mq^{-1/2})`, but these are still independent enough that for |
| 282 | + :math:`\\text{Huber}(q^{-1/2}v, M) = q^{-1}\\text{Huber}(v, Mq^{1/2})`, but these are still independent enough that for |
282 | 283 | the purposes of optimization we cannot collapse them. Nor can :code:`log_q` and :code:`log_r` be combined into |
283 | 284 | :code:`log_qr_ratio` as in RTS smoothing without the addition of a new absolute scale parameter, becausee :math:`q` and |
284 | 285 | :math:`r` interact with the distinct Huber :math:`M` parameters. |
@@ -329,11 +330,6 @@ def convex_smooth(y, A, Q, C, R, B=None, u=None, proc_huberM=6, meas_huberM=0): |
329 | 330 | x_states = cvxpy.Variable((A.shape[0], N)) # each column is [position, velocity, acceleration, ...] at step n |
330 | 331 | control = isinstance(B, np.ndarray) and isinstance(u, np.ndarray) # whether there is a control input |
331 | 332 |
|
332 | | - def huber_const(M): # from https://jmlr.org/papers/volume14/aravkin13a/aravkin13a.pdf, with correction for missing sqrt |
333 | | - a = 2*np.exp(-M**2 / 2)/M # huber_const smoothly transitions Huber between 1-norm and 2-norm squared cases |
334 | | - b = np.sqrt(2*np.pi)*(2*norm.cdf(M) - 1) |
335 | | - return np.sqrt((2*a*(1 + M**2)/M**2 + b)/(a + b)) |
336 | | - |
337 | 333 | # It is extremely important to run time that CVXPY expressions be in vectorized form |
338 | 334 | proc_resids = np.linalg.inv(sqrtm(Q)) @ (x_states[:,1:] - A @ x_states[:,:-1] - (0 if not control else B @ u[1:].T)) # all Q^(-1/2)(x_n - (A x_{n-1} + B u_n)) |
339 | 335 | meas_resids = np.linalg.inv(sqrtm(R)) @ (y.reshape(C.shape[0],-1) - C @ x_states) # all R^(-1/2)(y_n - C x_n) |
|
0 commit comments