From 33c7e03c3a0f12f632af17bca993df583190a98e Mon Sep 17 00:00:00 2001
From: Wolfgang Kastaun <wolfgang.kastaun@aei.mpg.de>
Date: Tue, 11 Feb 2025 16:13:29 +0100
Subject: [PATCH] Added dask version of the FixedShiftNumpy wrapper

---
 lisainstrument/fixed_shift_dask.py | 91 +++++++++++++++++++++++++++++-
 1 file changed, 90 insertions(+), 1 deletion(-)

diff --git a/lisainstrument/fixed_shift_dask.py b/lisainstrument/fixed_shift_dask.py
index 23dad21..42d3418 100644
--- a/lisainstrument/fixed_shift_dask.py
+++ b/lisainstrument/fixed_shift_dask.py
@@ -5,7 +5,7 @@ Use make_fixed_shift_lagrange_dask to create a Lagrange interpolator for dask ar
 
 from __future__ import annotations
 
-from typing import Final
+from typing import Callable, Final
 
 import dask
 import dask.array as da
@@ -166,3 +166,92 @@ def make_fixed_shift_lagrange_dask(
     """
     fac = FixedShiftLagrange.factory(order)
     return FixedShiftDask(left_bound, right_bound, fac)
+
+
+class AdaptiveShiftDask:  # pylint: disable=too-few-public-methods
+    """Time shifting that accepts fixed or dynamic shifts as well as constant data
+
+    Instances  act as interpolator function, which delegates to interpolation
+    methods for fixed or dynamic time shifting. The specialised interpolation
+    functions are provided during construction.
+
+    In addition, instances store a fixed sample rate that is assumed for any
+    interpolated data, allowing time shifts to be given in time units.
+    """
+
+    def __init__(
+        self,
+        delay_const: Callable[[da.Array, float], da.Array],
+        delay_dynamic: Callable[[da.Array, da.Array], da.Array],
+        fsample: float,
+    ):
+        """Construct from fixed and dynamic interpolator functions and sample rate
+
+        Arguments:
+            delay_const: Interpolator function with same interface as FixedShiftDask
+            delay_dynamic: Interpolator function with same interface as DynamicShiftDask
+            fsample: Sample rate [Hz]
+        """
+        self._delay_const = delay_const
+        self._delay_dynamic = delay_dynamic
+        self._fsample = float(fsample)
+
+    def fixed(self, x: da.Array | float, shift_time: float) -> da.Array | float:
+        """Apply fixed timeshift
+
+        Arguments:
+            x: the data to be shifted, as scalar or 1D array
+            shift_time: scalar time shift [s]
+        """
+        if isinstance(x, da.Array):
+            shift_samps = float(shift_time * self._fsample)
+            if shift_samps == 0:
+                return x
+            return self._delay_const(x, shift_samps)
+        return float(x)
+
+    def dynamic(self, x: da.Array | float, shift_time: da.Array) -> da.Array | float:
+        """Apply dynamic time shift
+
+        The shift is given in time units, and the data is assumed to be sampled
+        with rate fsample given in the constructor.
+
+        Both data and time shift can be scalar or 1D dask arrays. Scalars
+        are interpreted as constant arrays. In case of scalar data, the same
+        scalar is returned. In case of scalar shift, a more efficient algorithm
+        is used, which should yield identical results as for a const shift array.
+
+        Args:
+            x: the data to be shifted, as scalar or 1D array
+            shift_time: time shift [s], as 1D array
+
+        Returns:
+            The shifted data
+        """
+        if isinstance(x, da.Array):
+            return self._delay_dynamic(x, shift_time * self._fsample)
+        return float(x)
+
+    def __call__(
+        self, x: da.Array | float, shift_time: da.Array | float
+    ) -> da.Array | float:
+        """Apply adaptive time shift to sequence, accepting scalars to represent constant arrays.
+
+        The shift is given in time units, and the data is assumed to be sampled
+        with rate fsample given in the constructor.
+
+        Both data and time shift can be scalar or 1D dask arrays. Scalars
+        are interpreted as constant arrays. In case of scalar data, the same
+        scalar is returned. In case of scalar shift, a more efficient algorithm
+        is used, which should yield identical results as for a const shift array.
+
+        Args:
+            x: the data to be shifted, as scalar or 1D array
+            shift_time: time shift [s], as scalar or 1D array
+
+        Returns:
+            The shifted data
+        """
+        if isinstance(shift_time, da.Array):
+            return self.dynamic(x, shift_time)
+        return self.fixed(x, shift_time)
-- 
GitLab