An objective you can read
The image minimizes χ²(x) + Σ λk Rk(x) under constraints you choose. Inspect every term's value at every iterate, test it, change it.
Objective functions →Open source · Python · CPU, GPU and Dask clusters
Pyralysis turns visibilities into images by minimizing an objective you write (χ², closure phases or the bispectrum, plus the priors you trust), with modern first-order solvers. Every step is explicit, testable and swappable. No black-box CLEAN.
pip install pyralysis
Dirty image
Pyralysis reconstruction
Why Pyralysis
Most interferometric imaging still runs on CLEAN, a deconvolution heuristic. Pyralysis states the problem instead, and gives you the tools to solve it at scale.
The image minimizes χ²(x) + Σ λk Rk(x) under constraints you choose. Inspect every term's value at every iterate, test it, change it.
Objective functions →Closure phases, closure amplitudes and the bispectrum are first-class data terms, next to complex, amplitude and phase χ²: the EHT-style toolbox, in the same library.
Closures →The same script runs on NumPy, on CuPy on a GPU, or across a Dask cluster, with out-of-core Zarr spill when the data don't fit, in float32 or float64, end to end.
Array backends →216 antenna configurations, parametric or FITS skies, protoplanetary disks, Faraday screens, thermal noise and gain errors: test a reconstruction against a known truth.
Simulation →Every optimizer records each χ² and regularizer at every accepted iterate, and warns when the data are fitted to the thermal noise level: the discrepancy principle, built in.
Optimizers →Data terms, regularizers, kernels, line searches and optimizers are small Python classes. A new idea is a subclass, not a fork of a monolithic imager.
Extensibility →Real data · EHT 2017 · M87*
The same Event Horizon Telescope visibilities of M87*, reconstructed again and again. Swap the prior, or the forward model and the solver: the image and the line of code change together.
terms = [ ChiSquared(model_visibility=degridding, normalize=True), ]
Degridding with a PSWF1 kernel; SDMM with an inner Hager–Zhang solver (Hager–Zhang alone for χ² only and Entropy). Rebuilt by examples/scripts/m87_gallery_matrices.py.
Forward model
Solver
terms = [ ChiSquared(model_visibility=model, normalize=True), L1Norm(penalization_factor=0.01, normalize=True), ]
Same χ² + L1 objective in every panel: only the estimator and the optimizer change. 3 forward models × 10 solvers, from examples/scripts/m87_gallery_matrices.py.
Approach
CLEAN is effective and deeply tied to calibration practice. Pyralysis takes the other road: regularized maximum likelihood, written as an explicit optimization problem.
| CLEAN-style deconvolution | Pyralysis | |
|---|---|---|
| What is solved | Iterative peeling of bright components from the dirty image | An explicit objective f(x) with its gradient or proximal map |
| Sky model | Point or multi-scale components, convolved with a restoring beam | A pixel image under the priors you choose; the model is not beam-convolved |
| Data | Complex visibilities | Complex, amplitude or phase χ²; closure phases, closure amplitudes, bispectrum |
| Prior knowledge | Masks or boxes, scales, loop gain | L1, TSV, TV, entropy, Huber, Tikhonov, flux and positivity constraints |
| Stopping | Threshold and iteration count | Convergence tolerances, and each χ² against its noise level |
| Extending it | Inside the imaging package | Subclass a term, kernel, line search or optimizer in your own code |
Background: the inverse problem · compressed sensing · why Pyralysis
Where it fits
Pyralysis sits between specialized imaging packages and domain-agnostic toolkits: it speaks interferometry (measurement sets, uvw, Stokes, closures) and keeps every method interchangeable.
Regularization is an explicit part of the objective, not hidden in an algorithm's steps, so physical priors and constraints are principled, not ad hoc.
Many optimizers, regularization families and forward models in one framework, instead of one method wired through the whole code.
Classical optimization needs no training set, behaves predictably on a new observation, and every term of the result can be inspected.
Unlike general inverse-problem toolkits, the coordinates, measurement sets, polarization and closure quantities are first-class, and simulation lives next to reconstruction.
Io extensionsEnd to end
One library for the whole loop: build the sky, observe it with ALMA, image it, and compare with the truth.
DiskSource rings and three background sources
docs/scripts/generate_readme_figures.py.Real data
Each image was made with the Pyralysis optimization CLI on one workstation. Drag to compare the dirty image with the reconstruction; each side has its own colour scale.
Dirty image
Pyralysis
Dirty image
Pyralysis
Dirty image
Pyralysis
Dirty image
Pyralysis
Dirty image
Pyralysis
Dirty image
Pyralysis
Settings for every dataset, and every variant tried: gallery/generate.py.
Under the hood
Three core frameworks, Dataset, Optimization and Simulation, share one data model, organized with classic design patterns so new ideas plug in instead of being bolted on.
Optimizers, line searches, step-size seeders, estimators and kernels are interchangeable objects.
An objective is a sum of terms: χ² variants, regularizers and constraints, combined freely.
Solvers share one iteration skeleton and override only their step, so logging, stopping and snapshots come for free.
An interferometer follows its antenna array: filter antennas by radius, dish size or ID, and what depends on them updates.

Heritage
Pyralysis grew out of gpuvmem, a C++/CUDA maximum-entropy imager that ran 1000 to 5000 times faster than a sequential CPU version. Pyralysis keeps that optimization-first approach and rebuilds it in Python, so it is open to extension, to the community, and to the data volumes of the next generation of telescopes.
Cárcamo, Román, Casassus, Moral & Rannou (2018), Multi-GPU maximum entropy image synthesis for radio astronomy, Astronomy and Computing 22, 16–27.
In code
Objects you put together, not a parameter file: an array, a sky, noise, a weighting and an imager.
from importlib.resources import files from astropy import units as u from pyralysis.io.antenna_config_io import AntennaConfigurationIo from pyralysis.simulation import Simulator from pyralysis.models.sky import PointSource from pyralysis.injectors import ThermalNoiseInjector from pyralysis.transformers.weighting_schemes import Natural from pyralysis.transformers import DirtyMapper cfg = files("pyralysis.simulation") / "antenna_configs" / "vla.c.cfg" interferometer = AntennaConfigurationIo(input_name=str(cfg)).read() interferometer.configure_observation( min_frequency=1e9 * u.Hz, max_frequency=1.1e9 * u.Hz, frequency_step=1e7 * u.Hz, right_ascension="12h00m00s", declination="45d00m00s", integration_time=10 * u.s, observation_time="1h", ) source = PointSource( reference_intensity=1.0 * u.Jy, sky_position="12h00m00s 45d00m00s", reference_frequency=1e9 * u.Hz, ) dataset = Simulator(interferometer=interferometer, sources=source).simulate(create_dataset=True) dataset = ThermalNoiseInjector(system_temperature=50, integration_time=10, channel_bandwidth=1e6).apply(dataset) imsize, cellsize = 128, dataset.theo_resolution / 3.5 Natural(imsize=imsize, cellsize=cellsize, input_data=dataset).transform() dataset.calculate_psf() dirty, _beam = DirtyMapper(input_data=dataset, imsize=imsize, cellsize=cellsize, stokes=["I"]).transform()
Python 3.11–3.13. A few dependencies come from the SKA package index.
pip install --extra-index-url https://artefact.skao.int/repository/pypi-internal/simple pyralysis[all]
GPU: micromamba create -f envs/cuda13.yml (Maxwell/Pascal/Volta: cuda12.yml, Kepler: cuda11.yml). See the installation guide.
Updated 2026-10-20 09:08 UTC, daily and on every merge. Who forks Pyralysis →
Contribute
Pyralysis is developed in the open on GitLab. A new regularizer, data term, kernel, line search or solver is a small class with its own tests, so a first contribution can be a focused one. Bug reports, documentation and reviews count too.
development; the fast test suite runs on Python 3.11 to 3.13.
Read the contributing guideSetup, quality checks, commit prefixes, the MR checklist and label conventions.
git clone https://gitlab.com/clirai/pyralysis.git && cd pyralysis uv venv --python 3.12 # or 3.11 / 3.13 uv pip install -e ".[test]" pre-commit install pytest -n auto -m "not gpu and not slow" tests/unit/
Extending Pyralysis: write your own term or strategy. Coding conventions: CONVENTIONS.md.
If Pyralysis helps your research, please cite it.
@software{carcamo2021pyralysis, author = {Miguel Cárcamo}, title = {Pyralysis: A Python framework for radio interferometric imaging and simulation}, year = {2021}, url = {https://gitlab.com/clirai/pyralysis}, note = {https://pyralysis.readthedocs.io/} }