Skip to content

Vector Distance

Spectral distance measures between LPC filter pairs (Itakura, Itakura–Saito, COSH, Euclidean).

v_disteusq

V_DISTEUSQ - Squared Euclidean distance matrix.

v_disteusq

v_disteusq(x, y, mode='', w=None) -> ndarray

Calculate squared Euclidean or Mahalanobis distance.

Parameters:

Name Type Description Default
x array_like

First set of vectors, shape (nx, p).

required
y array_like

Second set of vectors, shape (ny, p).

required
mode str

'x' = full distance matrix; 'd' = pairwise; 's' = take sqrt.

''
w array_like

Weighting matrix or vector.

None

Returns:

Name Type Description
d ndarray

Distance matrix or vector.

Source code in pyvoicebox/v_disteusq.py
def v_disteusq(x, y, mode='', w=None) -> np.ndarray:
    """Calculate squared Euclidean or Mahalanobis distance.

    Parameters
    ----------
    x : array_like
        First set of vectors, shape (nx, p).
    y : array_like
        Second set of vectors, shape (ny, p).
    mode : str, optional
        'x' = full distance matrix; 'd' = pairwise; 's' = take sqrt.
    w : array_like, optional
        Weighting matrix or vector.

    Returns
    -------
    d : ndarray
        Distance matrix or vector.
    """
    x = np.asarray(x, dtype=float)
    y = np.asarray(y, dtype=float)
    if x.ndim == 1:
        x = x.reshape(1, -1)
    if y.ndim == 1:
        y = y.reshape(1, -1)
    nx, p = x.shape
    ny = y.shape[0]

    if 'd' in mode or ('x' not in mode and nx == ny):
        # Pairwise distance
        nn = min(nx, ny)
        z = x[:nn, :] - y[:nn, :]
        if w is None:
            d = np.sum(z * z, axis=1)
        else:
            w = np.asarray(w, dtype=float)
            if w.ndim == 1:
                d = np.sum(z * w[np.newaxis, :] * z, axis=1)
            else:
                d = np.sum((z @ w) * z, axis=1)
    else:
        # Full distance matrix (nx, ny)
        if w is None:
            # Efficient computation: ||x-y||^2 = ||x||^2 + ||y||^2 - 2*x*y'
            xx = np.sum(x * x, axis=1, keepdims=True)
            yy = np.sum(y * y, axis=1, keepdims=True)
            d = xx + yy.T - 2.0 * (x @ y.T)
            d = np.maximum(d, 0.0)  # avoid tiny negatives from rounding
        else:
            w = np.asarray(w, dtype=float)
            if w.ndim == 1:
                xw = x * w[np.newaxis, :]
                xx = np.sum(xw * x, axis=1, keepdims=True)
                yy = np.sum(y * w[np.newaxis, :] * y, axis=1, keepdims=True)
                d = xx + yy.T - 2.0 * (xw @ y.T)
            else:
                xw = x @ w
                xx = np.sum(xw * x, axis=1, keepdims=True)
                yw = y @ w
                yy = np.sum(yw * y, axis=1, keepdims=True)
                d = xx + yy.T - 2.0 * (xw @ y.T)
            d = np.maximum(d, 0.0)

    if 's' in mode:
        d = np.sqrt(d)

    return d

v_distitar

V_DISTITAR - Itakura distance between AR coefficients.

v_distitar

v_distitar(ar1, ar2, mode='') -> ndarray

Calculate the Itakura distance between AR coefficients.

Parameters:

Name Type Description Default
ar1 (array_like, shape(nf1, p + 1))

AR coefficient sets.

required
ar2 (array_like, shape(nf2, p + 1))

AR coefficient sets.

required
mode str

'x' for full distance matrix, 'd' for diagonal only. 'e' to return exp(d) instead of d.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distitar.py
def v_distitar(ar1, ar2, mode='') -> np.ndarray:
    """Calculate the Itakura distance between AR coefficients.

    Parameters
    ----------
    ar1 : array_like, shape (nf1, p+1)
        AR coefficient sets.
    ar2 : array_like, shape (nf2, p+1)
        AR coefficient sets.
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.
        'e' to return exp(d) instead of d.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    ar1 = np.atleast_2d(np.asarray(ar1, dtype=float))
    ar2 = np.atleast_2d(np.asarray(ar2, dtype=float))
    nf1 = ar1.shape[0]
    nf2 = ar2.shape[0]

    m2 = v_lpcar2ra(ar2)
    m2[:, 0] *= 0.5

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        d = 2.0 * np.sum(v_lpcar2rr(ar1[:nx, :]) * m2[:nx, :], axis=1) * (ar1[:nx, 0] / ar2[:nx, 0]) ** 2
    else:
        d = 2.0 * v_lpcar2rr(ar1) @ m2.T * (ar1[:, 0:1] * (1.0 / ar2[:, 0:1].T)) ** 2

    if 'e' not in mode:
        d = np.log(d)
    return d

v_distitpf

V_DISTITPF - Itakura distance between power spectra.

v_distitpf

v_distitpf(pf1, pf2, mode='') -> ndarray

Calculate the Itakura spectral distance between power spectra.

Parameters:

Name Type Description Default
pf1 (array_like, shape(nf1, p + 1))

Power spectra (DC to Nyquist).

required
pf2 (array_like, shape(nf2, p + 1))

Power spectra (DC to Nyquist).

required
mode str

'x' for full distance matrix, 'd' for diagonal only.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distitpf.py
def v_distitpf(pf1, pf2, mode='') -> np.ndarray:
    """Calculate the Itakura spectral distance between power spectra.

    Parameters
    ----------
    pf1 : array_like, shape (nf1, p+1)
        Power spectra (DC to Nyquist).
    pf2 : array_like, shape (nf2, p+1)
        Power spectra (DC to Nyquist).
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    pf1 = np.atleast_2d(np.asarray(pf1, dtype=float))
    pf2 = np.atleast_2d(np.asarray(pf2, dtype=float))
    nf1, p2 = pf1.shape
    p1 = p2 - 1
    nf2 = pf2.shape[0]

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        r = pf1[:nx, :] / pf2[:nx, :]
        q = np.log(r)
        d = (np.log((np.sum(r[:, 1:p1], axis=1) + 0.5 * (r[:, 0] + r[:, p1])) / p1)
             - (np.sum(q[:, 1:p1], axis=1) + 0.5 * (q[:, 0] + q[:, p1])) / p1)
    else:
        r = pf1[:, np.newaxis, :] / pf2[np.newaxis, :, :]
        q = np.log(r)
        d = (np.log((np.sum(r[:, :, 1:p1], axis=2) + 0.5 * (r[:, :, 0] + r[:, :, p1])) / p1)
             - (np.sum(q[:, :, 1:p1], axis=2) + 0.5 * (q[:, :, 0] + q[:, :, p1])) / p1)
    return d

v_distisar

V_DISTISAR - Itakura-Saito distance between AR coefficients.

v_distisar

v_distisar(ar1, ar2, mode='') -> ndarray

Calculate the Itakura-Saito distance between AR coefficients.

Parameters:

Name Type Description Default
ar1 (array_like, shape(nf1, p + 1))

AR coefficient sets.

required
ar2 (array_like, shape(nf2, p + 1))

AR coefficient sets.

required
mode str

'x' for full distance matrix, 'd' for diagonal only.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distisar.py
def v_distisar(ar1, ar2, mode='') -> np.ndarray:
    """Calculate the Itakura-Saito distance between AR coefficients.

    Parameters
    ----------
    ar1 : array_like, shape (nf1, p+1)
        AR coefficient sets.
    ar2 : array_like, shape (nf2, p+1)
        AR coefficient sets.
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    ar1 = np.atleast_2d(np.asarray(ar1, dtype=float))
    ar2 = np.atleast_2d(np.asarray(ar2, dtype=float))
    nf1 = ar1.shape[0]
    nf2 = ar2.shape[0]

    m2 = v_lpcar2ra(ar2)
    m2[:, 0] *= 0.5

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        d = 2.0 * np.sum(v_lpcar2rr(ar1[:nx, :]) * m2[:nx, :], axis=1) - np.log((ar2[:nx, 0] / ar1[:nx, 0]) ** 2) - 1.0
    else:
        d = 2.0 * v_lpcar2rr(ar1) @ m2.T - np.log((ar1[:, 0:1] ** (-1) * ar2[:, 0:1].T) ** 2) - 1.0
    return d

v_distispf

V_DISTISPF - Itakura-Saito distance between power spectra.

v_distispf

v_distispf(pf1, pf2, mode='') -> ndarray

Calculate the Itakura-Saito spectral distance between power spectra.

Parameters:

Name Type Description Default
pf1 (array_like, shape(nf1, p + 1))

Power spectra (DC to Nyquist).

required
pf2 (array_like, shape(nf2, p + 1))

Power spectra (DC to Nyquist).

required
mode str

'x' for full distance matrix, 'd' for diagonal only.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distispf.py
def v_distispf(pf1, pf2, mode='') -> np.ndarray:
    """Calculate the Itakura-Saito spectral distance between power spectra.

    Parameters
    ----------
    pf1 : array_like, shape (nf1, p+1)
        Power spectra (DC to Nyquist).
    pf2 : array_like, shape (nf2, p+1)
        Power spectra (DC to Nyquist).
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    pf1 = np.atleast_2d(np.asarray(pf1, dtype=float))
    pf2 = np.atleast_2d(np.asarray(pf2, dtype=float))
    nf1, p2 = pf1.shape
    p1 = p2 - 1
    nf2 = pf2.shape[0]

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        r = pf1[:nx, :] / pf2[:nx, :]
        q = r - np.log(r)
        d = (np.sum(q[:, 1:p1], axis=1) + 0.5 * (q[:, 0] + q[:, p1])) / p1 - 1.0
    else:
        r = pf1[:, np.newaxis, :] / pf2[np.newaxis, :, :]
        q = r - np.log(r)
        d = (np.sum(q[:, :, 1:p1], axis=2) + 0.5 * (q[:, :, 0] + q[:, :, p1])) / p1 - 1.0
    return d

v_distchar

V_DISTCHAR - COSH spectral distance between AR coefficients.

v_distchar

v_distchar(ar1, ar2, mode='') -> ndarray

Calculate the COSH spectral distance between AR coefficients.

Parameters:

Name Type Description Default
ar1 (array_like, shape(nf1, p + 1))

AR coefficient sets.

required
ar2 (array_like, shape(nf2, p + 1))

AR coefficient sets.

required
mode str

'x' for full distance matrix, 'd' for diagonal only.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distchar.py
def v_distchar(ar1, ar2, mode='') -> np.ndarray:
    """Calculate the COSH spectral distance between AR coefficients.

    Parameters
    ----------
    ar1 : array_like, shape (nf1, p+1)
        AR coefficient sets.
    ar2 : array_like, shape (nf2, p+1)
        AR coefficient sets.
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    ar1 = np.atleast_2d(np.asarray(ar1, dtype=float))
    ar2 = np.atleast_2d(np.asarray(ar2, dtype=float))
    nf1, p1 = ar1.shape
    nf2 = ar2.shape[0]
    p2 = p1 + 1

    m1 = np.zeros((nf1, 2 * p1))
    m2 = np.zeros((nf2, 2 * p1))
    m1[:, :p1] = v_lpcar2rr(ar1)
    m1[:, p1:] = v_lpcar2ra(ar1)
    m1[:, 0] *= 0.5
    m1[:, p1] *= 0.5
    m2[:, p1:] = v_lpcar2rr(ar2)
    m2[:, :p1] = v_lpcar2ra(ar2)

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        d = np.sum(m1[:nx, :] * m2[:nx, :], axis=1) - 1.0
    else:
        d = m1 @ m2.T - 1.0
    return d

v_distchpf

V_DISTCHPF - COSH spectral distance between power spectra.

v_distchpf

v_distchpf(pf1, pf2, mode='') -> ndarray

Calculate the COSH spectral distance between power spectra.

Parameters:

Name Type Description Default
pf1 (array_like, shape(nf1, p + 1))

Power spectra (DC to Nyquist).

required
pf2 (array_like, shape(nf2, p + 1))

Power spectra (DC to Nyquist).

required
mode str

'x' for full distance matrix, 'd' for diagonal only.

''

Returns:

Name Type Description
d ndarray

Distance values.

Source code in pyvoicebox/v_distchpf.py
def v_distchpf(pf1, pf2, mode='') -> np.ndarray:
    """Calculate the COSH spectral distance between power spectra.

    Parameters
    ----------
    pf1 : array_like, shape (nf1, p+1)
        Power spectra (DC to Nyquist).
    pf2 : array_like, shape (nf2, p+1)
        Power spectra (DC to Nyquist).
    mode : str, optional
        'x' for full distance matrix, 'd' for diagonal only.

    Returns
    -------
    d : ndarray
        Distance values.
    """
    pf1 = np.atleast_2d(np.asarray(pf1, dtype=float))
    pf2 = np.atleast_2d(np.asarray(pf2, dtype=float))
    nf1, p2 = pf1.shape
    p1 = p2 - 1
    nf2 = pf2.shape[0]

    if 'd' in mode or ('x' not in mode and nf1 == nf2):
        nx = min(nf1, nf2)
        r = pf1[:nx, :] / pf2[:nx, :]
        q = r + 1.0 / r
        d = (2.0 * np.sum(q[:, 1:p1], axis=1) + q[:, 0] + q[:, p1]) / (4.0 * p1) - 1.0
    else:
        # Full cross-distance matrix
        r = pf1[:, np.newaxis, :] / pf2[np.newaxis, :, :]
        q = r + 1.0 / r
        d = (2.0 * np.sum(q[:, :, 1:p1], axis=2) + q[:, :, 0] + q[:, :, p1]) / (4.0 * p1) - 1.0
    return d