def _warping_envelope(X, region):
    """Compute the warping envelope.

    Parameters
    ----------
    X : array
        Input data. It must be two- or three-dimensional.

    region : array, shape = (2, n_timestamps)
        Constraint region. The first row consists of the starting indices
        (included) and the second row consists of the ending indices (excluded)
        of the valid rows for each column.

    Returns
    -------
    lower : array
        The lower envelope.

    upper : array
        The upper envelope.

    """
    X = check_array(X, ensure_2d=False, allow_nd=True)
    region = check_array(region, ensure_min_samples=2)
    n_dims = X.ndim
    if n_dims not in (2, 3):
        raise ValueError("X must be a two- or three-dimensional.")
    if n_dims == 2:
        n_samples, n_timestamps = X.shape
        lower, upper = _warping_envelope_2d(
            X, n_samples, n_timestamps, region
        )
    else:
        n_samples_X, n_samples_Y, n_timestamps = X.shape
        lower, upper = _warping_envelope_3d(
            X, n_samples_X, n_samples_Y, n_timestamps, region
        )
    return lower, upper
Compute the warping envelope.

    Parameters
    ----------
    X : array
        Input data. It must be two- or three-dimensional.

    region : array, shape = (2, n_timestamps)
        Constraint region. The first row consists of the starting indices
        (included) and the second row consists of the ending indices (excluded)
        of the valid rows for each column.

    Returns
    -------
    lower : array
        The lower envelope.

    upper : array
        The upper envelope.

    
pyts/metrics/lower_bounds.py