Repository: scipy/scipy
File: scipy/stats/_quantile.py (Python)

Code before:
    xp_capabilities,
    xp_ravel,
    array_namespace,
    xp_promote,
    xp_device,
    _length_nonmasked,
    is_torch,
)
import scipy._lib.array_api_extra as xpx
from scipy.stats._axis_nan_policy import _broadcast_arrays, _contains_nan


def _quantile_iv(x, p, method, axis, nan_policy, keepdims, weights):
    xp = array_namespace(x, p, weights)

    if not xp.isdtype(xp.asarray(x).dtype, ('integral', 'real floating')):
        raise ValueError("`x` must have real dtype.")

    if not xp.isdtype(xp.asarray(p).dtype, 'real floating'):
        raise ValueError("`p` must have real floating dtype.")

    if not (weights is None
            or xp.isdtype(xp.asarray(weights).dtype, ('integral', 'real floating'))):
        raise ValueError("`weights` must have real dtype.")

    x, p, weights = xp_promote(x, p, weights, force_floating=True, xp=xp)
    dtype = x.dtype

    axis_none = axis is None
    ndim = max(x.ndim, p.ndim)
    if axis_none:
        x = xp_ravel(x)
        p = xp_ravel(p)
        axis = 0
    elif np.iterable(axis) or int(axis) != axis:
        message = "`axis` must be an integer or None."
        raise ValueError(message)
    elif (axis >= ndim) or (axis < -ndim):
        message = "`axis` is not compatible with the shapes of the inputs."
        raise ValueError(message)
    axis = int(axis)

    methods = {'inverted_cdf', 'averaged_inverted_cdf', 'closest_observation',
               'hazen', 'interpolated_inverted_cdf', 'linear',
               'median_unbiased', 'normal_unbiased', 'weibull',
               'harrell-davis', '_lower', '_midpoint', '_higher', '_nearest',
               'round_outward', 'round_inward', 'round_nearest'}
    if method not in methods:
        message = f"`method` must be one of {methods}"
        raise ValueError(message)


Review comment (suggestion):
@j-bowhay I think this is needed because `p` can be a Python scalar, so the device of `x` and `weights` is preserved, but `p` becomes an array on the default device. We could make `xp_promote` smarter in a separate PR.
```suggestion
    x, p, weights = xp_promote(x, p, weights, force_floating=True, xp=xp)
    p = xp.asarray(p, device=xp_device(x))
```


Code after:
    xp_capabilities,
    xp_ravel,
    array_namespace,
    xp_promote,
    xp_device,
    _length_nonmasked,
    is_torch,
)
import scipy._lib.array_api_extra as xpx
from scipy.stats._axis_nan_policy import _broadcast_arrays, _contains_nan


def _quantile_iv(x, p, method, axis, nan_policy, keepdims, weights):
    xp = array_namespace(x, p, weights)

    if not xp.isdtype(xp.asarray(x).dtype, ('integral', 'real floating')):
        raise ValueError("`x` must have real dtype.")

    if not xp.isdtype(xp.asarray(p).dtype, 'real floating'):
        raise ValueError("`p` must have real floating dtype.")

    if not (weights is None
            or xp.isdtype(xp.asarray(weights).dtype, ('integral', 'real floating'))):
        raise ValueError("`weights` must have real dtype.")

    x, p, weights = xp_promote(x, p, weights, force_floating=True, xp=xp)
    p = xp.asarray(p, device=xp_device(x))
    dtype = x.dtype

    axis_none = axis is None
    ndim = max(x.ndim, p.ndim)
    if axis_none:
        x = xp_ravel(x)
        p = xp_ravel(p)
        axis = 0
    elif np.iterable(axis) or int(axis) != axis:
        message = "`axis` must be an integer or None."
        raise ValueError(message)
    elif (axis >= ndim) or (axis < -ndim):
        message = "`axis` is not compatible with the shapes of the inputs."
        raise ValueError(message)
    axis = int(axis)

    methods = {'inverted_cdf', 'averaged_inverted_cdf', 'closest_observation',
               'hazen', 'interpolated_inverted_cdf', 'linear',
               'median_unbiased', 'normal_unbiased', 'weibull',
               'harrell-davis', '_lower', '_midpoint', '_higher', '_nearest',
               'round_outward', 'round_inward', 'round_nearest'}
    if method not in methods:
        message = f"`method` must be one of {methods}"
        raise ValueError(message)