PR: Implement support for *Python Array API Standard*. - #1406
PR: Implement support for *Python Array API Standard*.#1406thomasmansencal wants to merge 6 commits into
Conversation
bc223a6 to
3163508
Compare
d825253 to
dcdf17c
Compare
f876179 to
1ea7990
Compare
0f6eb2d to
533292f
Compare
ac82265 to
e01afc0
Compare
e6ec62c to
c4b44ed
Compare
015ab6f to
d894c8f
Compare
a233cc9 to
d94467a
Compare
d62e565 to
3d257b3
Compare
3d257b3 to
928d8c2
Compare
*Colour* now dispatches array operations to the caller's backend (*NumPy*, *JAX*, *PyTorch*) through the array-namespace machinery in `colour.utilities.array`. Beyond the mechanical *NumPy* to namespace conversion, this commit bundles the behaviour and public API changes documented below so that they remain discoverable under `git blame` and `git bisect`. - Support for the *Python Array API Standard* was implemented: array operations dispatch to the input backend (*NumPy*, *JAX*, *PyTorch*) through the new `colour.utilities.array_namespace` and `xp_*` utilities, toggled with `colour.utilities.is_array_api_enabled` and `colour.utilities.set_array_api_enabled`. - `colour.utilities.is_array_api_compat_installed` and `colour.utilities.is_array_api_extra_installed` were added. - `colour.colorimetry.interpolate_signal`, `colour.colorimetry.extrapolate_signal` and `colour.colorimetry.trim_signal` were added, sharing the spectral distribution and multi-spectral distributions resampling implementation. - `colour.colorimetry.msds_blackbody`, `colour.colorimetry.msds_rayleigh_jeans`, `colour.colorimetry.CIE_illuminant_D_series`, `colour.colorimetry.msds_CIE_illuminant_D_series` and `colour.colorimetry.msds_to_XYZ_tristimulus_weighting_factors_ASTME308` were added. - `colour.appearance.eccentricity_factor_Hellwig2022` and `colour.appearance.hue_angle_dependency_Hellwig2022` were added. - `colour.appearance.XYZ_to_Nayatani95` now computes the hue quadrature `H` correlate, previously left unset. - The multi-spectral distributions paths of `colour.colour_fidelity_index`, `colour.colour_quality_scale` and `colour.colour_rendering_index` were vectorised. - The `colour.temperature` correlated colour temperature solvers were vectorised, replacing the *SciPy* `minimize` calls with closed-form Gauss-Newton iterations. - `colour.colour_rendering_index`: the *"CIE 2024"* `Q_a` general index now averages test colour samples 1 to 8, it was averaging all 15. - `colour.adaptation.chromatic_adaptation_Li2025` now applies domain and range scaling. - The `COLOUR_SCIENCE__FILTER_COLOUR_WARNINGS` environment variable is now honoured correctly. - `colour.utilities.set_caching_enable`, `colour.utilities.set_ndarray_copy_enable` and `colour.algebra.set_spow_enable` were renamed to `set_caching_enabled`, `set_ndarray_copy_enabled` and `set_spow_enabled` respectively, without aliases. - *Multiprocessing* support was removed: `disable_multiprocessing`, `multiprocessing_pool` and `ParallelForMultiprocess`. - Around 80 internal appearance helpers were removed from the `colour.appearance` modules `__all__` (`ciecam02`, `ciecam16`, `hellwig2022`, `hunt`, `nayatani95`, `llab`, `atd95`). - `colour.quality.cfi2017.sd_reference_illuminant` and `colour.quality.cfi2017.CCT_reference_illuminant` were removed, orphaned by the vectorised reference illuminant path. - The appearance models `compute_H` argument now defaults to `False`. - `colour.continuous.Signal` and `colour.continuous.MultiSignals` now default to `colour.algebra.LinearInterpolator` instead of `colour.algebra.KernelInterpolator`: the default *Lanczos* kernel assumes uniformly-spaced data, returns incorrect values on non-uniformly-spaced domains and overshoots the input range, e.g. by 11% on a step, which are surprising properties for the generic continuous signal containers. `colour.colorimetry.SpectralDistribution` and `colour.colorimetry.MultiSpectralDistributions` are unaffected: they select `colour.algebra.SpragueInterpolator` or `colour.algebra.CubicSplineInterpolator` according to the domain uniformity, as recommended for spectral data. - The `*_to_msds` definitions now return a `MultiSpectralDistributions` instance by default instead of a `numpy.ndarray`. - `colour.algebra.least_square_mapping_MoorePenrose` now uses batched, greater than 2-D, matrix multiplication semantics. - The *Jiang et al. (2013)* principal component analysis dropped its covariance-matrix path; its reference basis functions were regenerated. - The `colour.temperature` solvers reference values were regenerated to match the new Gauss-Newton implementation. - *Filmic Pro*: the look-up table domain start was changed from `0` to `EPSILON` and a `left=0` clamp was added. - The `_SPOW_ENABLED` and `_SDIV_MODE` module states were migrated to `contextvars.ContextVar` for thread and async-task safety. - The minimum *NumPy* version was raised from 2.0 to 2.1: the array operations dispatch through `numpy.cumulative_sum` and the keyword form of `numpy.clip`, both introduced in *NumPy* 2.1.
928d8c2 to
b6e63a9
Compare
|
Benchmarks on my system with AMD Ryzen Threadripper 2950X / 64 GiB RAM / Radeon RX 7900 XT.
Looks very much in line with your results @thomasmansencal . Do we need dynamic opt-in for the API? I wonder if it is enough to have the runtime check for the available backends. The opt-in seems mostly useful for backwards compatibility, but I think we need to raise our version number for these changes anyway. I'll do a more thorough review tomorrow. |
|
First of all : great job ! I had a look at this from an autodiff angle (trying to see if computational graph breaks even though it shouldn't). As in trying to see where we can compute gradients based on function inputs and where not -- this would be a huge unlock for everyone working on inverse models. More than happy to propose fixes for the below mentioned breaks in the computational graph, if you are willing to include this in the scope for our upcoming release.
Full evaluation details available here : drilldown.md Accidental graph breaks to fix, in priority order
Suggested regression-test structureThe PR currently demonstrates cross-backend numerical execution. Autodiff needs separate assertions. A compact test helper should:
|
Summary
This PR implements support for the Python Array API Standard, enabling computations to dispatch onto alternative array backends:
Dispatch is currently opt-in and NumPy-only behaviour is unchanged by default. Once enabled, the backend is selected from the type of the input array. It can be enabled three ways:
1. Environment variable, set before importing Colour:
2. Programmatically, toggle the global state at runtime:
3. Scoped context manager (also usable as a decorator), enable for a block only:
What's added
xp_*operation surface incolour.utilities:array_namespace,is_numpy_namespace,is_non_ndarray,trace_array_namespaceas_ndarray,cast_non_ndarray,xp_as_array/xp_as_float_array/xp_as_int_array,xp_astype,xp_ascontiguousarrayxp_reshape,xp_squeeze,xp_atleast_1d/xp_atleast_2d,xp_broadcast_to,xp_matrix_transpose,xp_resize,xp_pad,xp_insertxp_average,xp_median,xp_nanmean,xp_trapezoid,xp_gradientxp_degrees/xp_radians,xp_sinc,xp_round,xp_nan_to_numxp_lstsq,xp_eig/xp_eigh,xp_create_diagonalxp_linspace,xp_interp,xp_select,xp_isin,xp_setxor1d,xp_uniquexp_isclose,xp_assert_close,xp_assert_equalcontextvars-backed global state (Array API enablement, domain-range scale,ndarraycopy, caching) for thread/async safety.colour.temperature.common), Jakob and Hanika (2019) trilinear interpolation, etc.COLOUR_SCIENCE__DEFAULT_COMPLEX_DTYPE/set_default_complex_dtype.CIE_illuminant_D_series,msds_CIE_illuminant_D_series,msds_blackbody,msds_rayleigh_jeans.xppytest fixture parametrising numpy/jax/torch/torch-mps, withmps_tolerance_absoluteandmps_xfailmarkers for float32 precision, plus a cross-backend benchmark suite (utilities/benchmark.py).advanced.rst.Performance
Per-suite speed-up vs NumPy (best-of-3, HD inputs): speed-up = NumPy ÷ backend over cases succeeding on both, so higher = faster (e.g.
3.0×= 3× faster than NumPy;< 1.0×= slower).numpy (ms)is the summed best-of-3 over the suite's cases.NumPy is the baseline (1.00×). JAX dispatches asynchronously, so every timed operation is synchronised with
jax.block_until_readybefore the clock stops: the jax column measures completed computation, not enqueue latency.Measured on an Apple M1 Max (10-core, 32 GB), macOS 15.7, Python 3.13, NumPy 2.3, PyTorch 2.9, JAX 0.8; 409 cases across 22 suites.
Preflight
Code Style and Quality
colour,colour.models.Documentation