From 139776862815d17e162ed606918c4354fbd783e2 Mon Sep 17 00:00:00 2001 From: Wolfgang Kastaun <wolfgang.kastaun@aei.mpg.de> Date: Fri, 14 Feb 2025 12:00:07 +0100 Subject: [PATCH] clarify notation in documentation of shift inversion algorithm --- lisainstrument/shift_inversion_numpy.py | 48 +++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lisainstrument/shift_inversion_numpy.py b/lisainstrument/shift_inversion_numpy.py index ec82d5e..af70ce4 100644 --- a/lisainstrument/shift_inversion_numpy.py +++ b/lisainstrument/shift_inversion_numpy.py @@ -85,25 +85,26 @@ class ShiftInverseNumpy: # pylint: disable=too-few-public-methods r"""Invert time coordinate transformation given as numpy array The purpose of this class is the inversion of a 1D coordinate transform - between some coordinates :math:`u` and :math:`v`. + :math:`v \rightarrow U(v)` between some coordinates :math:`u` and :math:`v`. + The inverse is denoted :math:`u \rightarrow V(u)`, i.e. :math:`V(U(v)) = v`. The transform is expressed as a shift - :math:`S(v) = u(v) - v`. + :math:`\delta U(v) = U(v) - v`. It will be provided as samples - :math:`S_k = S(v_k)` + :math:`\delta U_k = \delta U(v_k)` at regularly spaced sample locations :math:`v_k = v_0 + k \Delta v`. The inverse will be expressed as - :math:`\hat{S}(u) = u - v(u)`. + :math:`\delta V(u) = u - V(u)`. It will be computed for locations :math:`u_l = u_0 + l \Delta u` regularly spaced with respect to the :math:`u`-coordinate, i.e. the output will be the sequence - :math:`\hat{S}_l = u_l - v(u_l)`. + :math:`\delta V_l = u_l - V(u_l)`. Currently, we restrict to the special case where :math:`u_k = v_k`, - i.e. :math:`v_0 = u_0` and :math:`\Delta u = \Delta v`. + i.e. identical offsets :math:`v_0 = u_0` and spacings :math:`\Delta u = \Delta v`. By convention, the coordinates refer to times, and coordinate shift, tolerance, and sample rate are all given in SI units. @@ -124,7 +125,7 @@ class ShiftInverseNumpy: # pylint: disable=too-few-public-methods assume regularly sampled sequences with :math:`\Delta u = \Delta v = 1 / f_s`. For technical reasons, one also needs to provide a limit for the maximum - shift between the coordinates, i.e. :math:`|S(v)| < S_\mathrm{max}`. + shift between the coordinates, i.e. :math:`|\delta U(v)| < S_\mathrm{max}`. Another parameter is an interpolation operator to be used in the internal fixed-point iteration algorithm. One also needs to provide the desired @@ -178,12 +179,12 @@ class ShiftInverseNumpy: # pylint: disable=too-few-public-methods r"""Compute the inverse coordinate transform The coordinate transform is given an array with the sequence - :math:`S_k = S(v_k)`. The sample locations :math:`v_k` do not have to be provided + :math:`\delta U_k = \delta U(v_k)`. The sample locations :math:`v_k` do not have to be provided but are assumed to be regularly spaced, i.e. :math:`v_k = v_0 + k \Delta v`, and the first array element corresponds to :math:`k=0`. The output is given as an array with the sequence - :math:`\hat{S}_l` (see class docstring) providing the shift + :math:`\delta V_l = u_l - V(u_l)` providing the shift at sample locations regularly spaced in the transformed coordinate :math:`u`, that is, at points :math:`u_l = u_0 + l \Delta u`. The first element of the output corresponds to :math:`l=0`. @@ -192,29 +193,30 @@ class ShiftInverseNumpy: # pylint: disable=too-few-public-methods The algorithm works using fixed point iteration - :math:`\hat{S}_l^{n+1} = f(l,\hat{S}_l^{n})`, + :math:`\delta V_l^{n+1} = f(l,\delta V_l^{n})`, where - :math:`f(l,d) = I[v_k, S_k](v_l - d)`, and :math:`I[v_k, S_k]` is a - function obtained by interpolating the samples :math:`v_k, S_k`, - approximating :math:`I[v_k, S_k](v) \approx S(v)`. + :math:`f(l,d) = I[v_k, \delta U_k](v_l - d)`, and :math:`I[v_k, \delta U_k]` is a + function obtained by interpolating the samples :math:`v_k, \delta U_k`, + approximating :math:`I[v_k, \delta U_k](v) \approx \delta U(v)`. On the technical level, the interpolation operator is implemented using shifts directly, with an interface of the form - :math:`I[S_k](d_l) \approx S(v_l + d_l)`. + :math:`I[\delta U_k](d_l) \approx \delta U(v_l + d_l)`. + Hence, :math:`f(l,d_l) = I[\delta U_k](-d_l) \approx \delta U(v_l - d_l)`. If the iteration converges, it converges to a solution - :math:`\bar{S}_l = f(l,\bar{S}_l) \approx S(v_l - \bar{S}_l) = S(u_l - \bar{S}_l)`, + :math:`\delta \bar{V}_l = f(l,\delta \bar{V}_l) \approx \delta U(v_l - \delta\bar{V}_l) = \delta U(u_l - \delta\bar{V}_l)`, where we used the implicit convention that :math:`u_l = v_l`. - This equation fulfilled for :math:`\bar{S}_l` is indeed the one that - needs to be fulfilled for the desired quantity :math:`\hat{S}_l`, which + This equation fulfilled for :math:`\delta\bar{V}_l` is indeed the one that + needs to be fulfilled for the desired quantity :math:`\delta V_l`, which can be shown as follows: - :math:`u_l = u(v(u_l)) = S(v(u_l)) + v(u_l)` and hence - :math:`\hat{S}_l = u_l - v(u_l) = S(v(u_l)) = S(u_l - \hat{S}_l)`. + :math:`u_l = U(V(u_l)) = \delta U(V(u_l)) + V(u_l)` and hence + :math:`\delta V_l = u_l - V(u_l) = \delta U(V(u_l)) = \delta U(u_l - \delta V_l)`. This shows that the iteration converges to the correct solution if it converges. - The initial value is :math:`\hat{S}_l^0 = S_l`. + The initial value is :math:`\delta V_l^0 = \delta U_l`. The iteration is repeated until - :math:`\max_l |\hat{S}_l^{n+1} - \hat{S}_l^{n} | < \epsilon`, + :math:`\max_l |\delta V_l^{n+1} - \delta V_l^{n} | < \epsilon`, where :math:`\epsilon` is the tolerance specified when constructing the operator. @@ -226,10 +228,10 @@ class ShiftInverseNumpy: # pylint: disable=too-few-public-methods Arguments: - shift: 1D numpy array with shifts of the coordinate transform [s] + shift: 1D numpy array with shifts :math:`\delta U_k` [s] Returns: - 1D numpy array with shift [s] at transformed coordinate + 1D numpy array with shifts :math:`\delta V_l` [s] """ shift_idx = shift * self._fsample -- GitLab