CoolFace
Apppublic

undetectable/voice-clone

sourceHugging Facemitupdated 2y agoView on Hugging Face
1likes
sequence.py2060 linesDownload Raw Back to packages
1#!/usr/bin/env python2# -*- encoding: utf-8 -*-3"""4Sequential modeling5===================6 7Sequence alignment8------------------9.. autosummary::10    :toctree: generated/11 12    dtw13    rqa14 15Viterbi decoding16----------------17.. autosummary::18    :toctree: generated/19 20    viterbi21    viterbi_discriminative22    viterbi_binary23 24Transition matrices25-------------------26.. autosummary::27    :toctree: generated/28 29    transition_uniform30    transition_loop31    transition_cycle32    transition_local33"""34from __future__ import annotations35 36import numpy as np37from scipy.spatial.distance import cdist38from numba import jit39from .util import pad_center, fill_off_diagonal, is_positive_int, tiny, expand_to40from .util.exceptions import ParameterError41from .filters import get_window42from typing import Any, Iterable, List, Optional, Tuple, Union, overload43from typing_extensions import Literal44from ._typing import _WindowSpec, _IntLike_co45 46__all__ = [47    "dtw",48    "dtw_backtracking",49    "rqa",50    "viterbi",51    "viterbi_discriminative",52    "viterbi_binary",53    "transition_uniform",54    "transition_loop",55    "transition_cycle",56    "transition_local",57]58 59 60@overload61def dtw(62    X: np.ndarray,63    Y: np.ndarray,64    *,65    metric: str = ...,66    step_sizes_sigma: Optional[np.ndarray] = ...,67    weights_add: Optional[np.ndarray] = ...,68    weights_mul: Optional[np.ndarray] = ...,69    subseq: bool = ...,70    backtrack: Literal[False],71    global_constraints: bool = ...,72    band_rad: float = ...,73    return_steps: Literal[False] = ...,74) -> np.ndarray:75    ...76 77 78@overload79def dtw(80    *,81    C: np.ndarray,82    metric: str = ...,83    step_sizes_sigma: Optional[np.ndarray] = ...,84    weights_add: Optional[np.ndarray] = ...,85    weights_mul: Optional[np.ndarray] = ...,86    subseq: bool = ...,87    backtrack: Literal[False],88    global_constraints: bool = ...,89    band_rad: float = ...,90    return_steps: Literal[False] = ...,91) -> np.ndarray:92    ...93 94 95@overload96def dtw(97    X: np.ndarray,98    Y: np.ndarray,99    *,100    metric: str = ...,101    step_sizes_sigma: Optional[np.ndarray] = ...,102    weights_add: Optional[np.ndarray] = ...,103    weights_mul: Optional[np.ndarray] = ...,104    subseq: bool = ...,105    backtrack: Literal[False],106    global_constraints: bool = ...,107    band_rad: float = ...,108    return_steps: Literal[True],109) -> Tuple[np.ndarray, np.ndarray]:110    ...111 112 113@overload114def dtw(115    *,116    C: np.ndarray,117    metric: str = ...,118    step_sizes_sigma: Optional[np.ndarray] = ...,119    weights_add: Optional[np.ndarray] = ...,120    weights_mul: Optional[np.ndarray] = ...,121    subseq: bool = ...,122    backtrack: Literal[False],123    global_constraints: bool = ...,124    band_rad: float = ...,125    return_steps: Literal[True],126) -> Tuple[np.ndarray, np.ndarray]:127    ...128 129 130@overload131def dtw(132    X: np.ndarray,133    Y: np.ndarray,134    *,135    metric: str = ...,136    step_sizes_sigma: Optional[np.ndarray] = ...,137    weights_add: Optional[np.ndarray] = ...,138    weights_mul: Optional[np.ndarray] = ...,139    subseq: bool = ...,140    backtrack: Literal[True] = ...,141    global_constraints: bool = ...,142    band_rad: float = ...,143    return_steps: Literal[False] = ...,144) -> Tuple[np.ndarray, np.ndarray]:145    ...146 147 148@overload149def dtw(150    *,151    C: np.ndarray,152    metric: str = ...,153    step_sizes_sigma: Optional[np.ndarray] = ...,154    weights_add: Optional[np.ndarray] = ...,155    weights_mul: Optional[np.ndarray] = ...,156    subseq: bool = ...,157    backtrack: Literal[True] = ...,158    global_constraints: bool = ...,159    band_rad: float = ...,160    return_steps: Literal[False] = ...,161) -> Tuple[np.ndarray, np.ndarray]:162    ...163 164 165@overload166def dtw(167    X: np.ndarray,168    Y: np.ndarray,169    *,170    metric: str = ...,171    step_sizes_sigma: Optional[np.ndarray] = ...,172    weights_add: Optional[np.ndarray] = ...,173    weights_mul: Optional[np.ndarray] = ...,174    subseq: bool = ...,175    backtrack: Literal[True] = ...,176    global_constraints: bool = ...,177    band_rad: float = ...,178    return_steps: Literal[True],179) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:180    ...181 182 183@overload184def dtw(185    *,186    C: np.ndarray,187    metric: str = ...,188    step_sizes_sigma: Optional[np.ndarray] = ...,189    weights_add: Optional[np.ndarray] = ...,190    weights_mul: Optional[np.ndarray] = ...,191    subseq: bool = ...,192    backtrack: Literal[True] = ...,193    global_constraints: bool = ...,194    band_rad: float = ...,195    return_steps: Literal[True],196) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:197    ...198 199 200def dtw(201    X: Optional[np.ndarray] = None,202    Y: Optional[np.ndarray] = None,203    *,204    C: Optional[np.ndarray] = None,205    metric: str = "euclidean",206    step_sizes_sigma: Optional[np.ndarray] = None,207    weights_add: Optional[np.ndarray] = None,208    weights_mul: Optional[np.ndarray] = None,209    subseq: bool = False,210    backtrack: bool = True,211    global_constraints: bool = False,212    band_rad: float = 0.25,213    return_steps: bool = False,214) -> Union[215    np.ndarray, Tuple[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray, np.ndarray]216]:217    """Dynamic time warping (DTW).218 219    This function performs a DTW and path backtracking on two sequences.220    We follow the nomenclature and algorithmic approach as described in [#]_.221 222    .. [#] Meinard Mueller223           Fundamentals of Music Processing โ€” Audio, Analysis, Algorithms, Applications224           Springer Verlag, ISBN: 978-3-319-21944-8, 2015.225 226    Parameters227    ----------228    X : np.ndarray [shape=(..., K, N)]229        audio feature matrix (e.g., chroma features)230 231        If ``X`` has more than two dimensions (e.g., for multi-channel inputs), all leading232        dimensions are used when computing distance to ``Y``.233 234    Y : np.ndarray [shape=(..., K, M)]235        audio feature matrix (e.g., chroma features)236 237    C : np.ndarray [shape=(N, M)]238        Precomputed distance matrix. If supplied, X and Y must not be supplied and239        ``metric`` will be ignored.240 241    metric : str242        Identifier for the cost-function as documented243        in `scipy.spatial.distance.cdist()`244 245    step_sizes_sigma : np.ndarray [shape=[n, 2]]246        Specifies allowed step sizes as used by the dtw.247 248    weights_add : np.ndarray [shape=[n, ]]249        Additive weights to penalize certain step sizes.250 251    weights_mul : np.ndarray [shape=[n, ]]252        Multiplicative weights to penalize certain step sizes.253 254    subseq : bool255        Enable subsequence DTW, e.g., for retrieval tasks.256 257    backtrack : bool258        Enable backtracking in accumulated cost matrix.259 260    global_constraints : bool261        Applies global constraints to the cost matrix ``C`` (Sakoe-Chiba band).262 263    band_rad : float264        The Sakoe-Chiba band radius (1/2 of the width) will be265        ``int(radius*min(C.shape))``.266 267    return_steps : bool268        If true, the function returns ``steps``, the step matrix, containing269        the indices of the used steps from the cost accumulation step.270 271    Returns272    -------273    D : np.ndarray [shape=(N, M)]274        accumulated cost matrix.275        D[N, M] is the total alignment cost.276        When doing subsequence DTW, D[N,:] indicates a matching function.277    wp : np.ndarray [shape=(N, 2)]278        Warping path with index pairs.279        Each row of the array contains an index pair (n, m).280        Only returned when ``backtrack`` is True.281    steps : np.ndarray [shape=(N, M)]282        Step matrix, containing the indices of the used steps from the cost283        accumulation step.284        Only returned when ``return_steps`` is True.285 286    Raises287    ------288    ParameterError289        If you are doing diagonal matching and Y is shorter than X or if an290        incompatible combination of X, Y, and C are supplied.291 292        If your input dimensions are incompatible.293 294        If the cost matrix has NaN values.295 296    Examples297    --------298    >>> import numpy as np299    >>> import matplotlib.pyplot as plt300    >>> y, sr = librosa.load(librosa.ex('brahms'), offset=10, duration=15)301    >>> X = librosa.feature.chroma_cens(y=y, sr=sr)302    >>> noise = np.random.rand(X.shape[0], 200)303    >>> Y = np.concatenate((noise, noise, X, noise), axis=1)304    >>> D, wp = librosa.sequence.dtw(X, Y, subseq=True)305    >>> fig, ax = plt.subplots(nrows=2, sharex=True)306    >>> img = librosa.display.specshow(D, x_axis='frames', y_axis='frames',307    ...                                ax=ax[0])308    >>> ax[0].set(title='DTW cost', xlabel='Noisy sequence', ylabel='Target')309    >>> ax[0].plot(wp[:, 1], wp[:, 0], label='Optimal path', color='y')310    >>> ax[0].legend()311    >>> fig.colorbar(img, ax=ax[0])312    >>> ax[1].plot(D[-1, :] / wp.shape[0])313    >>> ax[1].set(xlim=[0, Y.shape[1]], ylim=[0, 2],314    ...           title='Matching cost function')315    """316    # Default Parameters317    default_steps = np.array([[1, 1], [0, 1], [1, 0]], dtype=np.uint32)318    default_weights_add = np.zeros(3, dtype=np.float64)319    default_weights_mul = np.ones(3, dtype=np.float64)320 321    if step_sizes_sigma is None:322        # Use the default steps323        step_sizes_sigma = default_steps324 325        # Use default weights if none are provided326        if weights_add is None:327            weights_add = default_weights_add328 329        if weights_mul is None:330            weights_mul = default_weights_mul331    else:332        # If we have custom steps but no weights, construct them here333        if weights_add is None:334            weights_add = np.zeros(len(step_sizes_sigma), dtype=np.float64)335 336        if weights_mul is None:337            weights_mul = np.ones(len(step_sizes_sigma), dtype=np.float64)338 339        # Make the default step weights infinite so that they are never340        # preferred over custom steps341        default_weights_add.fill(np.inf)342        default_weights_mul.fill(np.inf)343 344        # Append custom steps and weights to our defaults345        step_sizes_sigma = np.concatenate((default_steps, step_sizes_sigma))346        weights_add = np.concatenate((default_weights_add, weights_add))347        weights_mul = np.concatenate((default_weights_mul, weights_mul))348 349    # These asserts are bad, but mypy cannot trace the code paths properly350    assert step_sizes_sigma is not None351    assert weights_add is not None352    assert weights_mul is not None353 354    if np.any(step_sizes_sigma < 0):355        raise ParameterError("step_sizes_sigma cannot contain negative values")356 357    if len(step_sizes_sigma) != len(weights_add):358        raise ParameterError("len(weights_add) must be equal to len(step_sizes_sigma)")359    if len(step_sizes_sigma) != len(weights_mul):360        raise ParameterError("len(weights_mul) must be equal to len(step_sizes_sigma)")361 362    if C is None and (X is None or Y is None):363        raise ParameterError("If C is not supplied, both X and Y must be supplied")364    if C is not None and (X is not None or Y is not None):365        raise ParameterError("If C is supplied, both X and Y must not be supplied")366 367    c_is_transposed = False368 369    # calculate pair-wise distances, unless already supplied.370    # C_local will keep track of whether the distance matrix was supplied371    # by the user (False) or constructed locally (True)372    C_local = False373    if C is None:374        C_local = True375        # mypy can't figure out that this case does not happen376        assert X is not None and Y is not None377        # take care of dimensions378        X = np.atleast_2d(X)379        Y = np.atleast_2d(Y)380 381        # Perform some shape-squashing here382        # Put the time axes around front383        # Suppress types because mypy doesn't know these are ndarrays384        X = np.swapaxes(X, -1, 0)  # type: ignore385        Y = np.swapaxes(Y, -1, 0)  # type: ignore386 387        # Flatten the remaining dimensions388        # Use F-ordering to preserve columns389        X = X.reshape((X.shape[0], -1), order="F")390        Y = Y.reshape((Y.shape[0], -1), order="F")391 392        try:393            C = cdist(X, Y, metric=metric)394        except ValueError as exc:395            raise ParameterError(396                "scipy.spatial.distance.cdist returned an error.\n"397                "Please provide your input in the form X.shape=(K, N) "398                "and Y.shape=(K, M).\n 1-dimensional sequences should "399                "be reshaped to X.shape=(1, N) and Y.shape=(1, M)."400            ) from exc401 402        # for subsequence matching:403        # if N > M, Y can be a subsequence of X404        if subseq and (X.shape[0] > Y.shape[0]):405            C = C.T406            c_is_transposed = True407 408    C = np.atleast_2d(C)409 410    # if diagonal matching, Y has to be longer than X411    # (X simply cannot be contained in Y)412    if np.array_equal(step_sizes_sigma, np.array([[1, 1]])) and (413        C.shape[0] > C.shape[1]414    ):415        raise ParameterError(416            "For diagonal matching: Y.shape[-1] >= X.shape[-11] "417            "(C.shape[1] >= C.shape[0])"418        )419 420    max_0 = step_sizes_sigma[:, 0].max()421    max_1 = step_sizes_sigma[:, 1].max()422 423    # check C here for nans before building global constraints424    if np.any(np.isnan(C)):425        raise ParameterError("DTW cost matrix C has NaN values. ")426 427    if global_constraints:428        # Apply global constraints to the cost matrix429        if not C_local:430            # If C was provided as input, make a copy here431            C = np.copy(C)432        fill_off_diagonal(C, radius=band_rad, value=np.inf)433 434    # initialize whole matrix with infinity values435    D = np.ones(C.shape + np.array([max_0, max_1])) * np.inf436 437    # set starting point to C[0, 0]438    D[max_0, max_1] = C[0, 0]439 440    if subseq:441        D[max_0, max_1:] = C[0, :]442 443    # initialize step matrix with -1444    # will be filled in calc_accu_cost() with indices from step_sizes_sigma445    steps = np.zeros(D.shape, dtype=np.int32)446 447    # these steps correspond to left- (first row) and up-(first column) moves448    steps[0, :] = 1449    steps[:, 0] = 2450 451    # calculate accumulated cost matrix452    D: np.ndarray453    steps: np.ndarray454    D, steps = __dtw_calc_accu_cost(455        C, D, steps, step_sizes_sigma, weights_mul, weights_add, max_0, max_1456    )457 458    # delete infinity rows and columns459    D = D[max_0:, max_1:]460    steps = steps[max_0:, max_1:]461 462    return_values: List[np.ndarray]463    if backtrack:464        wp: np.ndarray465        if subseq:466            if np.all(np.isinf(D[-1])):467                raise ParameterError(468                    "No valid sub-sequence warping path could "469                    "be constructed with the given step sizes."470                )471            start = np.argmin(D[-1, :])472            _wp = __dtw_backtracking(steps, step_sizes_sigma, subseq, start)473        else:474            # perform warping path backtracking475            if np.isinf(D[-1, -1]):476                raise ParameterError(477                    "No valid sub-sequence warping path could "478                    "be constructed with the given step sizes."479                )480 481            _wp = __dtw_backtracking(steps, step_sizes_sigma, subseq)482            if _wp[-1] != (0, 0):483                raise ParameterError(484                    "Unable to compute a full DTW warping path. "485                    "You may want to try again with subseq=True."486                )487 488        wp = np.asarray(_wp, dtype=int)489 490        # since we transposed in the beginning, we have to adjust the index pairs back491        if subseq and (492            (X is not None and Y is not None and X.shape[0] > Y.shape[0])493            or c_is_transposed494            or C.shape[0] > C.shape[1]495        ):496            wp = np.fliplr(wp)497        return_values = [D, wp]498    else:499        return_values = [D]500 501    if return_steps:502        return_values.append(steps)503 504    if len(return_values) > 1:505        # Suppressing type check here because mypy can't506        # infer the exact length of the tuple507        return tuple(return_values)  # type: ignore508    else:509        return return_values[0]510 511 512@jit(nopython=True, cache=False)  # type: ignore513def __dtw_calc_accu_cost(514    C: np.ndarray,515    D: np.ndarray,516    steps: np.ndarray,517    step_sizes_sigma: np.ndarray,518    weights_mul: np.ndarray,519    weights_add: np.ndarray,520    max_0: int,521    max_1: int,522) -> Tuple[np.ndarray, np.ndarray]:  # pragma: no cover523    """Calculate the accumulated cost matrix D.524 525    Use dynamic programming to calculate the accumulated costs.526 527    Parameters528    ----------529    C : np.ndarray [shape=(N, M)]530        pre-computed cost matrix531    D : np.ndarray [shape=(N, M)]532        accumulated cost matrix533    steps : np.ndarray [shape=(N, M)]534        Step matrix, containing the indices of the used steps from the cost535        accumulation step.536    step_sizes_sigma : np.ndarray [shape=[n, 2]]537        Specifies allowed step sizes as used by the dtw.538    weights_add : np.ndarray [shape=[n, ]]539        Additive weights to penalize certain step sizes.540    weights_mul : np.ndarray [shape=[n, ]]541        Multiplicative weights to penalize certain step sizes.542    max_0 : int543        maximum number of steps in step_sizes_sigma in dim 0.544    max_1 : int545        maximum number of steps in step_sizes_sigma in dim 1.546 547    Returns548    -------549    D : np.ndarray [shape=(N, M)]550        accumulated cost matrix.551        D[N, M] is the total alignment cost.552        When doing subsequence DTW, D[N,:] indicates a matching function.553    steps : np.ndarray [shape=(N, M)]554        Step matrix, containing the indices of the used steps from the cost555        accumulation step.556 557    See Also558    --------559    dtw560    """561    for cur_n in range(max_0, D.shape[0]):562        for cur_m in range(max_1, D.shape[1]):563            # accumulate costs564            for cur_step_idx, cur_w_add, cur_w_mul in zip(565                range(step_sizes_sigma.shape[0]), weights_add, weights_mul566            ):567                cur_D = D[568                    cur_n - step_sizes_sigma[cur_step_idx, 0],569                    cur_m - step_sizes_sigma[cur_step_idx, 1],570                ]571                cur_C = cur_w_mul * C[cur_n - max_0, cur_m - max_1]572                cur_C += cur_w_add573                cur_cost = cur_D + cur_C574 575                # check if cur_cost is smaller than the one stored in D576                if cur_cost < D[cur_n, cur_m]:577                    D[cur_n, cur_m] = cur_cost578 579                    # save step-index580                    steps[cur_n, cur_m] = cur_step_idx581 582    return D, steps583 584 585@jit(nopython=True, cache=False)  # type: ignore586def __dtw_backtracking(587    steps: np.ndarray,588    step_sizes_sigma: np.ndarray,589    subseq: bool,590    start: Optional[int] = None,591) -> List[Tuple[int, int]]:  # pragma: no cover592    """Backtrack optimal warping path.593 594    Uses the saved step sizes from the cost accumulation595    step to backtrack the index pairs for an optimal596    warping path.597 598    Parameters599    ----------600    steps : np.ndarray [shape=(N, M)]601        Step matrix, containing the indices of the used steps from the cost602        accumulation step.603    step_sizes_sigma : np.ndarray [shape=[n, 2]]604        Specifies allowed step sizes as used by the dtw.605    subseq : bool606        Enable subsequence DTW, e.g., for retrieval tasks.607    start : int608        Start column index for backtraing (only allowed for ``subseq=True``)609 610    Returns611    -------612    wp : list [shape=(N,)]613        Warping path with index pairs.614        Each list entry contains an index pair615        (n, m) as a tuple616 617    See Also618    --------619    dtw620    """621    if start is None:622        cur_idx = (steps.shape[0] - 1, steps.shape[1] - 1)623    else:624        cur_idx = (steps.shape[0] - 1, start)625 626    wp = []627    # Set starting point D(N, M) and append it to the path628    wp.append((cur_idx[0], cur_idx[1]))629 630    # Loop backwards.631    # Stop criteria:632    # Setting it to (0, 0) does not work for the subsequence dtw,633    # so we only ask to reach the first row of the matrix.634 635    while (subseq and cur_idx[0] > 0) or (not subseq and cur_idx != (0, 0)):636        cur_step_idx = steps[(cur_idx[0], cur_idx[1])]637 638        # save tuple with minimal acc. cost in path639        cur_idx = (640            cur_idx[0] - step_sizes_sigma[cur_step_idx][0],641            cur_idx[1] - step_sizes_sigma[cur_step_idx][1],642        )643 644        # If we run off the side of the cost matrix, break here645        if min(cur_idx) < 0:646            break647 648        # append to warping path649        wp.append((cur_idx[0], cur_idx[1]))650 651    return wp652 653 654def dtw_backtracking(655    steps: np.ndarray,656    *,657    step_sizes_sigma: Optional[np.ndarray] = None,658    subseq: bool = False,659    start: Optional[Union[int, np.integer[Any]]] = None,660) -> np.ndarray:661    """Backtrack a warping path.662 663    Uses the saved step sizes from the cost accumulation664    step to backtrack the index pairs for a warping path.665 666    Parameters667    ----------668    steps : np.ndarray [shape=(N, M)]669        Step matrix, containing the indices of the used steps from the cost670        accumulation step.671    step_sizes_sigma : np.ndarray [shape=[n, 2]]672        Specifies allowed step sizes as used by the dtw.673    subseq : bool674        Enable subsequence DTW, e.g., for retrieval tasks.675    start : int676        Start column index for backtraing (only allowed for ``subseq=True``)677 678    Returns679    -------680    wp : list [shape=(N,)]681        Warping path with index pairs.682        Each list entry contains an index pair683        (n, m) as a tuple684 685    See Also686    --------687    dtw688    """689    if subseq is False and start is not None:690        raise ParameterError(691            f"start is only allowed to be set if subseq is True (start={start}, subseq={subseq})"692        )693 694    # Default Parameters695    default_steps = np.array([[1, 1], [0, 1], [1, 0]], dtype=np.uint32)696 697    if step_sizes_sigma is None:698        # Use the default steps699        step_sizes_sigma = default_steps700    else:701        # Append custom steps and weights to our defaults702        step_sizes_sigma = np.concatenate((default_steps, step_sizes_sigma))703 704    wp = __dtw_backtracking(steps, step_sizes_sigma, subseq, start)705    return np.asarray(wp, dtype=int)706 707 708@overload709def rqa(710    sim: np.ndarray,711    *,712    gap_onset: float = ...,713    gap_extend: float = ...,714    knight_moves: bool = ...,715    backtrack: Literal[False],716) -> np.ndarray:717    ...718 719 720@overload721def rqa(722    sim: np.ndarray,723    *,724    gap_onset: float = ...,725    gap_extend: float = ...,726    knight_moves: bool = ...,727    backtrack: Literal[True] = ...,728) -> Tuple[np.ndarray, np.ndarray]:729    ...730 731 732@overload733def rqa(734    sim: np.ndarray,735    *,736    gap_onset: float = ...,737    gap_extend: float = ...,738    knight_moves: bool = ...,739    backtrack: bool = ...,740) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:741    ...742 743 744def rqa(745    sim: np.ndarray,746    *,747    gap_onset: float = 1,748    gap_extend: float = 1,749    knight_moves: bool = True,750    backtrack: bool = True,751) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:752    """Recurrence quantification analysis (RQA)753 754    This function implements different forms of RQA as described by755    Serra, Serra, and Andrzejak (SSA). [#]_  These methods take as input756    a self- or cross-similarity matrix ``sim``, and calculate the value757    of path alignments by dynamic programming.758 759    Note that unlike dynamic time warping (`dtw`), alignment paths here are760    maximized, not minimized, so the input should measure similarity rather761    than distance.762 763    The simplest RQA method, denoted as `L` (SSA equation 3) and equivalent764    to the method described by Eckman, Kamphorst, and Ruelle [#]_, accumulates765    the length of diagonal paths with positive values in the input:766 767        - ``score[i, j] = score[i-1, j-1] + 1``  if ``sim[i, j] > 0``768        - ``score[i, j] = 0`` otherwise.769 770    The second method, denoted as `S` (SSA equation 4), is similar to the first,771    but allows for "knight moves" (as in the chess piece) in addition to strict772    diagonal moves:773 774        - ``score[i, j] = max(score[i-1, j-1], score[i-2, j-1], score[i-1, j-2]) + 1``  if ``sim[i, j] >775          0``776        - ``score[i, j] = 0`` otherwise.777 778    The third method, denoted as `Q` (SSA equations 5 and 6) extends this by779    allowing gaps in the alignment that incur some cost, rather than a hard780    reset to 0 whenever ``sim[i, j] == 0``.781    Gaps are penalized by two additional parameters, ``gap_onset`` and ``gap_extend``,782    which are subtracted from the value of the alignment path every time a gap783    is introduced or extended (respectively).784 785    Note that setting ``gap_onset`` and ``gap_extend`` to `np.inf` recovers the second786    method, and disabling knight moves recovers the first.787 788    .. [#] Serrร , Joan, Xavier Serra, and Ralph G. Andrzejak.789        "Cross recurrence quantification for cover song identification."790        New Journal of Physics 11, no. 9 (2009): 093017.791 792    .. [#] Eckmann, J. P., S. Oliffson Kamphorst, and D. Ruelle.793        "Recurrence plots of dynamical systems."794        World Scientific Series on Nonlinear Science Series A 16 (1995): 441-446.795 796    Parameters797    ----------798    sim : np.ndarray [shape=(N, M), non-negative]799        The similarity matrix to use as input.800 801        This can either be a recurrence matrix (self-similarity)802        or a cross-similarity matrix between two sequences.803 804    gap_onset : float > 0805        Penalty for introducing a gap to an alignment sequence806 807    gap_extend : float > 0808        Penalty for extending a gap in an alignment sequence809 810    knight_moves : bool811        If ``True`` (default), allow for "knight moves" in the alignment,812        e.g., ``(n, m) => (n + 1, m + 2)`` or ``(n + 2, m + 1)``.813 814        If ``False``, only allow for diagonal moves ``(n, m) => (n + 1, m + 1)``.815 816    backtrack : bool817        If ``True``, return the alignment path.818 819        If ``False``, only return the score matrix.820 821    Returns822    -------823    score : np.ndarray [shape=(N, M)]824        The alignment score matrix.  ``score[n, m]`` is the cumulative value of825        the best alignment sequence ending in frames ``n`` and ``m``.826    path : np.ndarray [shape=(k, 2)] (optional)827        If ``backtrack=True``, ``path`` contains a list of pairs of aligned frames828        in the best alignment sequence.829 830        ``path[i] = [n, m]`` indicates that row ``n`` aligns to column ``m``.831 832    See Also833    --------834    librosa.segment.recurrence_matrix835    librosa.segment.cross_similarity836    dtw837 838    Examples839    --------840    Simple diagonal path enhancement (L-mode)841 842    >>> import numpy as np843    >>> import matplotlib.pyplot as plt844    >>> y, sr = librosa.load(librosa.ex('nutcracker'), duration=30)845    >>> chroma = librosa.feature.chroma_cqt(y=y, sr=sr)846    >>> # Use time-delay embedding to reduce noise847    >>> chroma_stack = librosa.feature.stack_memory(chroma, n_steps=10, delay=3)848    >>> # Build recurrence, suppress self-loops within 1 second849    >>> rec = librosa.segment.recurrence_matrix(chroma_stack, width=43,850    ...                                         mode='affinity',851    ...                                         metric='cosine')852    >>> # using infinite cost for gaps enforces strict path continuation853    >>> L_score, L_path = librosa.sequence.rqa(rec,854    ...                                        gap_onset=np.inf,855    ...                                        gap_extend=np.inf,856    ...                                        knight_moves=False)857    >>> fig, ax = plt.subplots(ncols=2)858    >>> librosa.display.specshow(rec, x_axis='frames', y_axis='frames', ax=ax[0])859    >>> ax[0].set(title='Recurrence matrix')860    >>> librosa.display.specshow(L_score, x_axis='frames', y_axis='frames', ax=ax[1])861    >>> ax[1].set(title='Alignment score matrix')862    >>> ax[1].plot(L_path[:, 1], L_path[:, 0], label='Optimal path', color='c')863    >>> ax[1].legend()864    >>> ax[1].label_outer()865 866    Full alignment using gaps and knight moves867 868    >>> # New gaps cost 5, extending old gaps cost 10 for each step869    >>> score, path = librosa.sequence.rqa(rec, gap_onset=5, gap_extend=10)870    >>> fig, ax = plt.subplots(ncols=2, sharex=True, sharey=True)871    >>> librosa.display.specshow(rec, x_axis='frames', y_axis='frames', ax=ax[0])872    >>> ax[0].set(title='Recurrence matrix')873    >>> librosa.display.specshow(score, x_axis='frames', y_axis='frames', ax=ax[1])874    >>> ax[1].set(title='Alignment score matrix')875    >>> ax[1].plot(path[:, 1], path[:, 0], label='Optimal path', color='c')876    >>> ax[1].legend()877    >>> ax[1].label_outer()878    """879 880    if gap_onset < 0:881        raise ParameterError("gap_onset={} must be strictly positive")882    if gap_extend < 0:883        raise ParameterError("gap_extend={} must be strictly positive")884 885    score: np.ndarray886    pointers: np.ndarray887    score, pointers = __rqa_dp(sim, gap_onset, gap_extend, knight_moves)888    if backtrack:889        path = __rqa_backtrack(score, pointers)890        return score, path891 892    return score893 894 895@jit(nopython=True, cache=False)  # type: ignore896def __rqa_dp(897    sim: np.ndarray, gap_onset: float, gap_extend: float, knight: bool898) -> Tuple[np.ndarray, np.ndarray]:  # pragma: no cover899    """RQA dynamic programming implementation"""900 901    # The output array902    score = np.zeros(sim.shape, dtype=sim.dtype)903 904    # The backtracking array905    backtrack = np.zeros(sim.shape, dtype=np.int8)906 907    # These are place-holder arrays to limit the points being considered908    # at each step of the DP909    #910    # If knight moves are enabled, values are indexed according to911    # [(-1,-1), (-1, -2), (-2, -1)]912    #913    # If knight moves are disabled, then only the first entry is used.914    #915    # Using dummy vectors here makes the code a bit cleaner down below.916    sim_values = np.zeros(3)917    score_values = np.zeros(3)918    vec = np.zeros(3)919 920    if knight:921        # Initial limit is for the base case: diagonal + one knight922        init_limit = 2923 924        # Otherwise, we have 3 positions925        limit = 3926    else:927        init_limit = 1928        limit = 1929 930    # backtracking rubric:931    #   0 ==> diagonal move932    #   1 ==> knight move up933    #   2 ==> knight move left934    #  -1 ==> reset without inclusion935    #  -2 ==> reset with inclusion (ie positive value at init)936 937    # Initialize the first row and column with the data938    score[0, :] = sim[0, :]939    score[:, 0] = sim[:, 0]940 941    # backtracking initialization: the first row and column are all resets942    # if there's a positive link here, it's an inclusive reset943    for i in range(sim.shape[0]):944        if sim[i, 0]:945            backtrack[i, 0] = -2946        else:947            backtrack[i, 0] = -1948 949    for j in range(sim.shape[1]):950        if sim[0, j]:951            backtrack[0, j] = -2952        else:953            backtrack[0, j] = -1954 955    # Initialize the 1-1 case using only the diagonal956    if sim[1, 1] > 0:957        score[1, 1] = score[0, 0] + sim[1, 1]958        backtrack[1, 1] = 0959    else:960        link = sim[0, 0] > 0961        score[1, 1] = max(0, score[0, 0] - (link) * gap_onset - (~link) * gap_extend)962        if score[1, 1] > 0:963            backtrack[1, 1] = 0964        else:965            backtrack[1, 1] = -1966 967    # Initialize the second row with diagonal and left-knight moves968    i = 1969    for j in range(2, sim.shape[1]):970        score_values[:-1] = (score[i - 1, j - 1], score[i - 1, j - 2])971        sim_values[:-1] = (sim[i - 1, j - 1], sim[i - 1, j - 2])972        t_values = sim_values > 0973        if sim[i, j] > 0:974            backtrack[i, j] = np.argmax(score_values[:init_limit])975            score[i, j] = score_values[backtrack[i, j]] + sim[i, j]  # or + 1 for binary976        else:977            vec[:init_limit] = (978                score_values[:init_limit]979                - t_values[:init_limit] * gap_onset980                - (~t_values[:init_limit]) * gap_extend981            )982 983            backtrack[i, j] = np.argmax(vec[:init_limit])984            score[i, j] = max(0, vec[backtrack[i, j]])985            # Is it a reset?986            if score[i, j] == 0:987                backtrack[i, j] = -1988 989    # Initialize the second column with diagonal and up-knight moves990    j = 1991    for i in range(2, sim.shape[0]):992        score_values[:-1] = (score[i - 1, j - 1], score[i - 2, j - 1])993        sim_values[:-1] = (sim[i - 1, j - 1], sim[i - 2, j - 1])994        t_values = sim_values > 0995        if sim[i, j] > 0:996            backtrack[i, j] = np.argmax(score_values[:init_limit])997            score[i, j] = score_values[backtrack[i, j]] + sim[i, j]  # or + 1 for binary998 999        else:1000            vec[:init_limit] = (1001                score_values[:init_limit]1002                - t_values[:init_limit] * gap_onset1003                - (~t_values[:init_limit]) * gap_extend1004            )1005 1006            backtrack[i, j] = np.argmax(vec[:init_limit])1007            score[i, j] = max(0, vec[backtrack[i, j]])1008            # Is it a reset?1009            if score[i, j] == 0:1010                backtrack[i, j] = -11011 1012    # Now fill in the rest of the table1013    for i in range(2, sim.shape[0]):1014        for j in range(2, sim.shape[1]):1015            score_values[:] = (1016                score[i - 1, j - 1],1017                score[i - 1, j - 2],1018                score[i - 2, j - 1],1019            )1020            sim_values[:] = (sim[i - 1, j - 1], sim[i - 1, j - 2], sim[i - 2, j - 1])1021            t_values = sim_values > 01022            if sim[i, j] > 0:1023                # if knight is true, it's max of (-1,-1), (-1, -2), (-2, -1)1024                # otherwise, it's just the diagonal move (-1, -1)1025                # for backtracking purposes, if the max is 0 then it's the start of a new sequence1026                # if the max is non-zero, then we extend the existing sequence1027                backtrack[i, j] = np.argmax(score_values[:limit])1028                score[i, j] = (1029                    score_values[backtrack[i, j]] + sim[i, j]1030                )  # or + 1 for binary1031 1032            else:1033                # if the max of our options is negative, then it's a hard reset1034                # otherwise, it's a skip move1035                vec[:limit] = (1036                    score_values[:limit]1037                    - t_values[:limit] * gap_onset1038                    - (~t_values[:limit]) * gap_extend1039                )1040 1041                backtrack[i, j] = np.argmax(vec[:limit])1042                score[i, j] = max(0, vec[backtrack[i, j]])1043                # Is it a reset?1044                if score[i, j] == 0:1045                    backtrack[i, j] = -11046 1047    return score, backtrack1048 1049 1050def __rqa_backtrack(score, pointers):1051    """RQA path backtracking1052 1053    Given the score matrix and backtracking index array,1054    reconstruct the optimal path.1055    """1056 1057    # backtracking rubric:1058    #   0 ==> diagonal move1059    #   1 ==> knight move up1060    #   2 ==> knight move left1061    #  -1 ==> reset (sim = 0)1062    #  -2 ==> start of sequence (sim > 0)1063 1064    # This array maps the backtracking values to the1065    # relative index offsets1066    offsets = [(-1, -1), (-1, -2), (-2, -1)]1067 1068    # Find the maximum to end the path1069    idx = list(np.unravel_index(np.argmax(score), score.shape))1070 1071    # Construct the path1072    path: List = []1073    while True:1074        bt_index = pointers[tuple(idx)]1075 1076        # A -1 indicates a non-inclusive reset1077        # this can only happen when sim[idx] == 0,1078        # and a reset with zero score should not be included1079        # in the path.  In this case, we're done.1080        if bt_index == -1:1081            break1082 1083        # Other bt_index values are okay for inclusion1084        path.insert(0, idx)1085 1086        # -2 indicates beginning of sequence,1087        # so we can't backtrack any further1088        if bt_index == -2:1089            break1090 1091        # Otherwise, prepend this index and continue1092        idx = [idx[_] + offsets[bt_index][_] for _ in range(len(idx))]1093 1094    # If there's no alignment path at all, eg an empty cross-similarity1095    # matrix, return a properly shaped and typed array1096    if not path:1097        return np.empty((0, 2), dtype=np.uint)1098 1099    return np.asarray(path, dtype=np.uint)1100 1101 1102@jit(nopython=True, cache=False)  # type: ignore1103def _viterbi(1104    log_prob: np.ndarray, log_trans: np.ndarray, log_p_init: np.ndarray1105) -> Tuple[np.ndarray, np.ndarray]:  # pragma: no cover1106    """Core Viterbi algorithm.1107 1108    This is intended for internal use only.1109 1110    Parameters1111    ----------1112    log_prob : np.ndarray [shape=(T, m)]1113        ``log_prob[t, s]`` is the conditional log-likelihood1114        ``log P[X = X(t) | State(t) = s]``1115    log_trans : np.ndarray [shape=(m, m)]1116        The log transition matrix1117        ``log_trans[i, j] = log P[State(t+1) = j | State(t) = i]``1118    log_p_init : np.ndarray [shape=(m,)]1119        log of the initial state distribution1120 1121    Returns1122    -------1123    None1124        All computations are performed in-place on ``state, value, ptr``.1125    """1126    n_steps, n_states = log_prob.shape1127 1128    state = np.zeros(n_steps, dtype=np.uint16)1129    value = np.zeros((n_steps, n_states), dtype=np.float64)1130    ptr = np.zeros((n_steps, n_states), dtype=np.uint16)1131 1132    # factor in initial state distribution1133    value[0] = log_prob[0] + log_p_init1134 1135    for t in range(1, n_steps):1136        # Want V[t, j] <- p[t, j] * max_k V[t-1, k] * A[k, j]1137        #    assume at time t-1 we were in state k1138        #    transition k -> j1139 1140        # Broadcast over rows:1141        #    Tout[k, j] = V[t-1, k] * A[k, j]1142        #    then take the max over columns1143        # We'll do this in log-space for stability1144 1145        trans_out = value[t - 1] + log_trans.T1146 1147        # Unroll the max/argmax loop to enable numba support1148        for j in range(n_states):1149            ptr[t, j] = np.argmax(trans_out[j])1150            # value[t, j] = log_prob[t, j] + np.max(trans_out[j])1151            value[t, j] = log_prob[t, j] + trans_out[j, ptr[t][j]]1152 1153    # Now roll backward1154 1155    # Get the last state1156    state[-1] = np.argmax(value[-1])1157 1158    for t in range(n_steps - 2, -1, -1):1159        state[t] = ptr[t + 1, state[t + 1]]1160 1161    logp = value[-1:, state[-1]]1162 1163    return state, logp1164 1165 1166@overload1167def viterbi(1168    prob: np.ndarray,1169    transition: np.ndarray,1170    *,1171    p_init: Optional[np.ndarray] = ...,1172    return_logp: Literal[True],1173) -> Tuple[np.ndarray, np.ndarray]:1174    ...1175 1176 1177@overload1178def viterbi(1179    prob: np.ndarray,1180    transition: np.ndarray,1181    *,1182    p_init: Optional[np.ndarray] = ...,1183    return_logp: Literal[False] = ...,1184) -> np.ndarray:1185    ...1186 1187 1188def viterbi(1189    prob: np.ndarray,1190    transition: np.ndarray,1191    *,1192    p_init: Optional[np.ndarray] = None,1193    return_logp: bool = False,1194) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:1195    """Viterbi decoding from observation likelihoods.1196 1197    Given a sequence of observation likelihoods ``prob[s, t]``,1198    indicating the conditional likelihood of seeing the observation1199    at time ``t`` from state ``s``, and a transition matrix1200    ``transition[i, j]`` which encodes the conditional probability of

Showing the first 1,200 of 2060 lines. Download the file for the rest.