def dtw(
    series1: TimeSeries,
    series2: TimeSeries,
    window: Optional[Window] = None,
    distance: Union[DistanceFunc, None] = None,
    multi_grid_radius: int = -1,
) -> DTWAlignment:
    """
    Determines the optimal alignment between two time series `series1` and `series2`, according to the Dynamic Time
    Warping algorithm.
    The alignment minimizes the distance between pair-wise elements after warping.
    All elements in the two series are matched and are in strictly monotonically increasing order.
    Considers only the values in the series, ignoring the time axis.

    Dynamic Time Warping can be applied to determine how closely two time series correspond,
    irrespective of phase, length or speed differences.

    Parameters
    ----------
    series1
        A `TimeSeries` to align with `series2`.
    series2
        A `TimeSeries` to align with `series1`.
    window
        Optionally, a `Window` used to constrain the search for the optimal alignment: see `SakoeChiba` and `Itakura`.
        Default considers all possible alignments (`NoWindow`).
    distance
        Function taking as input either two `floats` for univariate series or two `np.ndarray`,
        and returning the distance between them.

        Defaults to the abs difference for univariate-data and the
        sum of the abs difference for multi-variate series.
    multi_grid_radius
        Default radius of `-1` results in an exact evaluation of the dynamic time warping algorithm.
        Without constraints DTW runs in O(nxm) time where n,m are the size of the series.
        Exact evaluation with no constraints, will result in a performance warning on large datasets.

        Setting `multi_grid_radius` to a value other than `-1`, will enable the approximate multi-grid solver,
        which executes in linear time, vs quadratic time for exact evaluation.
        Increasing radius trades solution accuracy for performance.

    Returns
    -------
    DTWAlignment
        Helper object for getting warp path, mean_distance, distance and warped time series
    """
    if window is None:
        window = NoWindow()

    if (
        multi_grid_radius == -1
        and type(window) is NoWindow
        and len(series1) * len(series2) > 10**6
    ):
        logger.warning(
            "Exact evaluation will result in poor performance on large datasets."
            " Consider enabling multi-grid or using a window."
        )

    both_univariate = series1.is_univariate and series2.is_univariate

    if distance is None:
        raise_if_not(
            series1.n_components == series2.n_components,
            "Expected series to have same number of components, or to supply custom distance function",
            logger,
        )

        distance = _default_distance_uni if both_univariate else _default_distance_multi

    if both_univariate:
        values_x = series1.univariate_values(copy=False)
        values_y = series2.univariate_values(copy=False)
    else:
        values_x = series1.values(copy=False)
        values_y = series2.values(copy=False)

    raise_if(
        np.any(np.isnan(values_x)),
        "Dynamic Time Warping does not support nan values. "
        "You can use the module darts.utils.missing_values to fill them, "
        "before passing them to dtw.",
        logger,
    )
    raise_if(
        np.any(np.isnan(values_y)),
        "Dynamic Time Warping does not support nan values. "
        "You can use the module darts.utils.missing_values to fill them,"
        "before passing it into dtw",
        logger,
    )

    window = copy.deepcopy(window)
    window.init_size(len(values_x), len(values_y))

    raise_if(multi_grid_radius < -1, "Expected multi-grid radius to be positive or -1")

    if multi_grid_radius >= 0:
        raise_if_not(
            isinstance(window, NoWindow),
            "Multi-grid solver does not currently support windows",
            logger,
        )
        cost_matrix = _fast_dtw(values_x, values_y, distance, multi_grid_radius)
    else:
        cost_matrix = _dtw_cost_matrix(values_x, values_y, distance, window)

    return DTWAlignment(series1, series2, cost_matrix)

    Determines the optimal alignment between two time series `series1` and `series2`, according to the Dynamic Time
    Warping algorithm.
    The alignment minimizes the distance between pair-wise elements after warping.
    All elements in the two series are matched and are in strictly monotonically increasing order.
    Considers only the values in the series, ignoring the time axis.

    Dynamic Time Warping can be applied to determine how closely two time series correspond,
    irrespective of phase, length or speed differences.

    Parameters
    ----------
    series1
        A `TimeSeries` to align with `series2`.
    series2
        A `TimeSeries` to align with `series1`.
    window
        Optionally, a `Window` used to constrain the search for the optimal alignment: see `SakoeChiba` and `Itakura`.
        Default considers all possible alignments (`NoWindow`).
    distance
        Function taking as input either two `floats` for univariate series or two `np.ndarray`,
        and returning the distance between them.

        Defaults to the abs difference for univariate-data and the
        sum of the abs difference for multi-variate series.
    multi_grid_radius
        Default radius of `-1` results in an exact evaluation of the dynamic time warping algorithm.
        Without constraints DTW runs in O(nxm) time where n,m are the size of the series.
        Exact evaluation with no constraints, will result in a performance warning on large datasets.

        Setting `multi_grid_radius` to a value other than `-1`, will enable the approximate multi-grid solver,
        which executes in linear time, vs quadratic time for exact evaluation.
        Increasing radius trades solution accuracy for performance.

    Returns
    -------
    DTWAlignment
        Helper object for getting warp path, mean_distance, distance and warped time series
    
darts/dataprocessing/dtw/dtw.py