Metadata-Version: 2.4
Name: pyralysis
Version: 2.6.0
Summary: PYthon Radio Astronomy anaLYSis and Image Synthesis
Author-email: Miguel Cárcamo <miguel.carcamo@usach.cl>
Maintainer-email: Miguel Cárcamo <miguel.carcamo@usach.cl>
License-Expression: GPL-3.0-only
Project-URL: Documentation, https://pyralysis.readthedocs.io/
Project-URL: Issues, https://gitlab.com/clirai/pyralysis/-/issues
Project-URL: Repository, https://gitlab.com/clirai/pyralysis
Project-URL: Source, https://gitlab.com/clirai/pyralysis
Keywords: radio,radio-interferometry,interferometry,radioastronomy,imaging,deconvolution,calibration,CASA,ALMA,SKA,visibility,synthesis,python,astronomy,science,data-analysis,signal-processing,high-performance-computing,simulation,image-reconstruction,radio-telescope,big data,big-data,big computing,compressed sensing,gridding,optimization
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: <3.13,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: astropy==6.1.0
Requires-Dist: autopep8==2.2.0
Requires-Dist: dafits==2.0.0
Requires-Dist: dask==2024.10.0
Requires-Dist: dask-ms[complete]>=0.2.29
Requires-Dist: distributed==2024.10.0
Requires-Dist: matplotlib==3.10.8
Requires-Dist: more-itertools==10.2.0
Requires-Dist: multimethod==1.11.2
Requires-Dist: numba>=0.59.1
Requires-Dist: numcodecs==0.15.1
Requires-Dist: numpy<3.0.0,>=2.0.0
Requires-Dist: psutil==6.1.1
Requires-Dist: python-casacore==3.7.1
Requires-Dist: radio-beam==0.3.7
Requires-Dist: scipy==1.15.0
Requires-Dist: ska-ost-array-config==4.5.0
Requires-Dist: snakeviz==2.2.0
Requires-Dist: spectral-cube==0.6.5
Requires-Dist: xarray==2024.9
Provides-Extra: all
Requires-Dist: pyfftw; extra == "all"
Requires-Dist: cmcrameri; extra == "all"
Requires-Dist: ipykernel; extra == "all"
Requires-Dist: seaborn; extra == "all"
Requires-Dist: six==1.17.0; extra == "all"
Requires-Dist: pytest==9.0.2; extra == "all"
Requires-Dist: pytest-cov==7.1.0; extra == "all"
Requires-Dist: pytest-xdist==3.8.0; extra == "all"
Provides-Extra: cupy
Requires-Dist: cupy-cuda13x<15,>=14.0; extra == "cupy"
Provides-Extra: cupy12
Requires-Dist: cupy-cuda12x<15,>=13.2; extra == "cupy12"
Provides-Extra: cupy13
Requires-Dist: cupy-cuda13x<15,>=14.0; extra == "cupy13"
Provides-Extra: dev
Requires-Dist: tox>=4.21.0; extra == "dev"
Provides-Extra: notebook
Requires-Dist: cmcrameri; extra == "notebook"
Requires-Dist: ipykernel; extra == "notebook"
Requires-Dist: seaborn; extra == "notebook"
Provides-Extra: pyfftw
Requires-Dist: pyfftw; extra == "pyfftw"
Provides-Extra: slurm
Requires-Dist: dask-jobqueue>=0.9.0; extra == "slurm"
Provides-Extra: test
Requires-Dist: six==1.17.0; extra == "test"
Requires-Dist: pytest==9.0.2; extra == "test"
Requires-Dist: pytest-cov==7.1.0; extra == "test"
Requires-Dist: pytest-xdist==3.8.0; extra == "test"
Dynamic: license-file

<div align="center">

<img src="https://gitlab.com/miguelcarcamov/pyralysis/-/wikis/uploads/fd40975ce0601b5c52a3cf8b92f385a5/Pyralisis-25.png" height="280" alt="Pyralysis logo">

# Pyralysis

**PYthon Radio Astronomy anaLYSis and Image Synthesis**

*Simulate, optimize, and reconstruct — with a Python toolkit built for modern interferometry.*

<br>

<!-- Status & quality (RTD/pre-commit URLs match legacy README badge assets) -->
[![Pipeline Status](https://gitlab.com/clirai/pyralysis/badges/development/pipeline.svg)](https://gitlab.com/clirai/pyralysis/-/pipelines?ref=development)
[![codecov](https://codecov.io/gl/clirai/pyralysis/branch/development/graph/badge.svg?token=MVFAA1YKC7)](https://codecov.io/gl/clirai/pyralysis)
[![Documentation Status](https://readthedocs.org/projects/pyralysis/badge/)](https://pyralysis.readthedocs.io/)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
[![License](https://img.shields.io/static/v1?label=License&message=GPL-3.0-only&color=blue&labelColor=1a1a2e)](https://www.gnu.org/licenses/gpl-3.0)

<br>

<!-- Distribution & discoverability (PyPI badge image URLs match legacy README) -->
[![PyPI Version](https://img.shields.io/pypi/v/pyralysis)](https://pypi.org/project/pyralysis)
[![Python Version](https://img.shields.io/badge/python-3.11%20|%203.12-blue)](https://www.python.org/)
[![PyPI Wheel](https://img.shields.io/pypi/wheel/pyralysis)](https://pypi.org/project/pyralysis/#files)
[![PyPI Downloads](https://img.shields.io/pypi/dm/pyralysis)](https://pypi.org/project/pyralysis/)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/clirai%2Fpyralysis/release?urlpath=lab%2Ftree%2Fexamples%2Fnotebooks)

<br>

<!-- Community (GitLab) -->
[![GitLab stars](https://img.shields.io/gitlab/stars/clirai%2Fpyralysis?logo=gitlab&label=stars)](https://gitlab.com/clirai/pyralysis/-/starrers)
[![GitLab forks](https://img.shields.io/gitlab/forks/clirai%2Fpyralysis?logo=gitlab&label=forks)](https://gitlab.com/clirai/pyralysis/-/forks)
[![GitLab open issues](https://img.shields.io/gitlab/issues/open/clirai%2Fpyralysis?logo=gitlab&label=issues)](https://gitlab.com/clirai/pyralysis/-/issues)
[![GitLab merge requests](https://img.shields.io/gitlab/merge-requests/open/clirai%2Fpyralysis?logo=gitlab&label=MRs)](https://gitlab.com/clirai/pyralysis/-/merge_requests)
[![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=fff)](https://gitlab.com/clirai/pyralysis/container_registry)

<br>

<!-- Core stack (libraries this project is built around) -->
[![Dask](https://img.shields.io/badge/Dask-FFC107?logo=dask&logoColor=1a1a2e)](https://www.dask.org/)
[![Xarray](https://img.shields.io/badge/Xarray-013243?logo=xarray&logoColor=white)](https://xarray.dev/)
[![NumPy](https://img.shields.io/badge/NumPy-4DABCF?logo=numpy&logoColor=white)](https://numpy.org/)
[![Astropy](https://img.shields.io/badge/Astropy-FF6B00?logo=astropy&logoColor=white)](https://www.astropy.org/)
[![Numba](https://img.shields.io/badge/Numba-00A3E0?logo=llvm&logoColor=white)](https://numba.pydata.org/)
[![SciPy](https://img.shields.io/badge/SciPy-8CAAE6?logo=scipy&logoColor=white)](https://scipy.org/)

<br>

<!-- Developer workflow -->
[![pytest](https://img.shields.io/badge/pytest-tested-0A9EDC?logo=pytest&logoColor=white)](https://pytest.org/)
[![yapf](https://img.shields.io/badge/code%20style-yapf-222222)](https://github.com/google/yapf)
[![isort](https://img.shields.io/badge/imports-isort-ef8336)](https://pycqa.github.io/isort/)
[![Commitizen](https://img.shields.io/badge/commits-Commitizen-ff69b4?logo=conventionalcommits&logoColor=white)](https://github.com/commitizen-tools/commitizen)
[![Read the Docs](https://img.shields.io/badge/Read%20the%20Docs-8CA1AF?logo=readthedocs&logoColor=fff)](https://pyralysis.readthedocs.io/)
[![ReadMe](https://img.shields.io/badge/ReadMe-018EF5?logo=readme&logoColor=fff)](https://gitlab.com/clirai/pyralysis/-/blob/release/README.md)

</div>

---

## Why Pyralysis?

Whether you are **prototyping a simulation**, **studying optimization for imaging**, or **pipelining large visibility sets**, Pyralysis aims to meet you where you work: clear APIs, lazy evaluation where it matters, and documentation you can actually read.

- **Interferometric imaging and simulation** in one coherent library.
- **Composable, flexible, and extensible** — not the same thing (see below): plug pieces together, choose what fits your science, and add your own classes without forking the core.
- **Dask-friendly** — large arrays and visibilities are handled as **lazy, chunked** work: Pyralysis builds a task graph instead of loading everything into RAM at once, then Dask runs those chunks in parallel on your laptop, a local cluster, or optional **SLURM** workers. You keep writing normal Python; scaling is mostly configuration, not a rewrite. Details: [Pipelines and distributed Dask](https://pyralysis.readthedocs.io/pipelines_distributed.html).
- **Documented** user guides, API reference, and runnable examples — try them locally, on **Binder**, or from a full install (see below).

If you prefer to dive straight in, open the **[documentation on Read the Docs](https://pyralysis.readthedocs.io/)**.

---

## Flexible, composable, and extensible

These words get mixed up; in Pyralysis they mean different jobs:

| Idea | Meaning here |
|------|----------------|
| **Composable** | Build a workflow from small, named pieces (objective terms, measurement operators, pipeline steps, I/O classes) instead of one black-box imager. |
| **Flexible** | **Choose** among those pieces for your experiment — e.g. L1 vs TSV, nearest-neighbor vs degridding, components script vs pipeline, NumPy vs CuPy — without changing the rest of the stack. |
| **Extensible** | **Add** your own piece (a new regularizer, sky model, injector, or `Io` backend) by subclassing / registering, without rewriting optimizers or pipelines. |

The same ideas show up in scripts, notebooks, and pipelines.

| You can… | How (sketch) | For more |
|----------|--------------|----------|
| Mix data fidelity + regularizers | `ObjectiveFunction` + `ChiSquared`, `L1Norm`, `TSV`, … | [Objective functions](https://pyralysis.readthedocs.io/objective_functions.html), [Regularization](https://pyralysis.readthedocs.io/regularization.html) |
| Choose how visibilities are modeled | `NearestNeighbor`, `BilinearInterpolation`, `Degridding`, … | [Measurement operator](https://pyralysis.readthedocs.io/measurement_operator.html) |
| Run explicit classes *or* a pipeline | `*_components.py` vs `*_pipeline.py` | [Repository examples](https://pyralysis.readthedocs.io/examples.html) |
| Stay on CPU or move to GPU | `array_backend="cupy"` — same imaging APIs | [Array backends](https://pyralysis.readthedocs.io/array_backend.html) |
| Scale with Dask | Lazy chunked arrays; local or SLURM workers via pipeline / runtime helpers | [Pipelines and distributed Dask](https://pyralysis.readthedocs.io/pipelines_distributed.html) |
| Swap I/O and sky / noise models | `DaskMS` / `ZarrArray` / `FITS`; injectors; sky sources | [I/O](https://pyralysis.readthedocs.io/io_operations.html), [Simulation](https://pyralysis.readthedocs.io/simulation.html) |

**Show, don’t hide:** adding or swapping a regularizer is a list change, not a fork of the optimizer.
``ChiSquared.model_visibility`` expects a **measurement operator** (e.g. ``BilinearInterpolation``), not a separate model-visibility class:

```python
from pyralysis.estimators import BilinearInterpolation
from pyralysis.optimization import ObjectiveFunction
from pyralysis.optimization.terms import ChiSquared, L1Norm, TSV

# MeasurementOperator subclass: image → model visibilities
model_visibility = BilinearInterpolation(input_data=dataset, image=image)

# Start simple…
terms = [
    ChiSquared(model_visibility=model_visibility, penalization_factor=1.0),
    L1Norm(penalization_factor=0.01),
]
# …or swap / extend without touching L-BFGS / FISTA wiring:
terms = [
    ChiSquared(model_visibility=model_visibility, penalization_factor=1.0),
    TSV(penalization_factor=0.001),  # replace L1, or append both
]
objective = ObjectiveFunction(terms=terms, parameter=image)
```

**Two ways to structure a workflow** (same science, different abstraction — flexibility of *how* you orchestrate):

- **Components** — you wire `Dataset`, operator, `ObjectiveFunction`, optimizer by hand (`examples/scripts/*_components.py`).
- **Pipelines** — step + context orchestration (`examples/scripts/*_pipeline.py`).

Motivation and architecture (OOP, why not a monolith): **[Why Pyralysis?](https://pyralysis.readthedocs.io/context.html)**.
Hands-on walkthroughs: **[Quickstart](https://pyralysis.readthedocs.io/quickstart.html)** · **[Examples](https://pyralysis.readthedocs.io/examples.html)** · **[Binder](#binder)**.

---

<a id="binder"></a>

## Try Pyralysis in your browser (Binder)

[**Binder**](https://mybinder.org) builds a short-lived JupyterLab session with Pyralysis checked out from the **`release`** branch, so you can run the project **without installing anything** on your machine. After the environment starts, open the file browser and work through the notebooks under **`examples/notebooks/`** — small simulations, toy datasets, and walkthroughs that mirror the written guides.

Binder is ideal when you want to **peek at the API**, **follow a tutorial cell by cell**, or **share a reproducible link** with a colleague. Sessions run on shared infrastructure with finite RAM and CPU, and the image tracks the **`release`** branch (not every commit on `development`), so treat it as **exploration and teaching**, not a substitute for HPC or production-scale imaging. For install paths, SLURM-backed Dask, and heavier workflows, use a local or cluster environment (see **Install** below) and the [repository examples](https://pyralysis.readthedocs.io/examples.html) page on Read the Docs.

**Launch:** [Open Pyralysis on Binder](https://mybinder.org/v2/gl/clirai%2Fpyralysis/release?urlpath=lab%2Ftree%2Fexamples%2Fnotebooks) (same target as the Binder badge above).

---

## Install in three steps

Stable releases live on **[PyPI](https://pypi.org/project/pyralysis/)** (pin `pyralysis==X.Y.Z` when you need a reproducible version).
For commits that are not yet released, install from GitLab (see the installation guide).

1. **Create a Python environment** (Python 3.11–3.12; `requires-python` is `>=3.11,<3.13`).
   Use `conda`, `mamba`, `micromamba`, or plain `venv` — whatever fits your stack.

2. **Install from PyPI using the SKA extra index** (needed so pip can resolve some dependencies).

```bash
pip install --extra-index-url https://artefact.skao.int/repository/pypi-internal/simple pyralysis[all]
```

This pulls the **latest published** release plus common optional pieces (FFT helpers, notebooks, tests). For **GPU**, prefer `micromamba create -f environment_cuda13.yml` (conda CUDA + CuPy, then `pip install -e .`); Pascal: `environment_cuda12.yml`. Pip-only CuPy wheels: `pyralysis[cupy13]` or `[cupy12]` — see the [installation guide](https://pyralysis.readthedocs.io/installation.html).
For **SLURM**-backed Dask workers (`dask-jobqueue`, used when `SetupDaskCluster` uses `dask_cluster_backend="slurm"`), add `pyralysis[slurm]` (see [Optional: SLURM](https://pyralysis.readthedocs.io/installation.html#optional-slurm)).

Simulation and imaging **pipelines** can attach a local cluster, SLURM, or an existing Dask `Client` via context configuration — see [Pipelines and distributed Dask](https://pyralysis.readthedocs.io/pipelines_distributed.html).

For editable installs, conda environment files, or Docker images:

- **[Installation guide](https://pyralysis.readthedocs.io/installation.html)**

Want to explore first without pip? Use **[Binder](#binder)** above.

### GPU imaging (optional; recommended for large images and datasets)

CUDA / CuPy is optional for small CPU workflows. For large measurement sets or large images, GPU imaging is recommended when a CUDA-capable environment is available.

With a CUDA-capable environment (`environment_cuda13.yml` / `environment_cuda12.yml`, or `pyralysis[cupy13]`), you can keep the **same imaging APIs** while visibilities live on **CuPy-backed Dask** collections:

```python
from pyralysis.io import DaskMS

dataset = DaskMS("observation.ms").read(
    array_backend="cupy",
    calculate_psf=True,
)
dataset.calculate_theoretical_noise(per_field=True, per_spw=True)
# Forward model, Mask(dataset=...), ObjectiveFunction, optimizers — same as CPU.
```

The pipeline **follows the measurement set**: a CPU image is promoted to GPU during `transform()` / gradients. **Simulation** on GPU is not supported yet — simulate on CPU, then `with_array_backend(..., "cupy")` if needed.

- **Guide:** [Array backends](https://pyralysis.readthedocs.io/array_backend.html)
- **Notebook:** `examples/notebooks/optimization_sandbox_gpu.ipynb` (masked χ² + LBFGS on a real MS)

---

## Minimal example

Simulate a small dataset and add thermal noise:

```python
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

# Load array configuration (packaged CFGs live under
# src/pyralysis/simulation/antenna_configs/, e.g. vla.c.cfg)
interferometer = AntennaConfigurationIo(input_name="path/to/array.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",
)

# Define a source and simulate
source = PointSource(
    reference_intensity=1.0 * u.Jy,
    sky_position="12h00m00s 45d00m00s",
    reference_frequency=1e9 * u.Hz,
)
sim = Simulator(interferometer=interferometer, sources=source)
dataset = sim.simulate(create_dataset=True)

# Add thermal noise
thermal = ThermalNoiseInjector(
    system_temperature=50,  # K
    integration_time=10,  # s
    channel_bandwidth=1e6,  # Hz
)
noisy_dataset = thermal.apply(dataset)
```

More simulation patterns (arrays, sky models, injectors, I/O):

- **[Simulation guide](https://pyralysis.readthedocs.io/simulation.html)**

### Reconstruction example

Reconstruct an image from visibilities with a measurement operator, objective, and L-BFGS.
``ChiSquared`` takes a ``MeasurementOperator`` subclass (here ``BilinearInterpolation``);
there is no ``ModelVisibility`` class:

```python
import dask.array as da
import numpy as np
import xarray as xr

from pyralysis.estimators import BilinearInterpolation
from pyralysis.optimization import ObjectiveFunction
from pyralysis.optimization.linesearch import BacktrackingArmijo
from pyralysis.optimization.optimizer import LBFGS
from pyralysis.optimization.terms import ChiSquared, L1Norm
from pyralysis.reconstruction import Image

# Image size / cellsize should follow the dataset (beam ~ theo_resolution)
imsize = 128
cellsize = noisy_dataset.theo_resolution / 3.5
image = Image(
    data=xr.DataArray(
        da.zeros((imsize, imsize), chunks=(64, 64), dtype=np.float32),
        dims=["x", "y"],
    ),
    cellsize=cellsize,
)

# Forward model: image → MODEL_DATA at observed UV coordinates
model_visibility = BilinearInterpolation(
    input_data=noisy_dataset,
    image=image,
)

terms = [
    ChiSquared(model_visibility=model_visibility, normalize=True),
    L1Norm(penalization_factor=0.01),
]
objective = ObjectiveFunction(terms=terms, parameter=image, persist_gradient=True)

linesearch = BacktrackingArmijo(objective_function=objective)
optimizer = LBFGS(
    objective_function=objective,
    parameter=image,
    linesearch=linesearch,
    max_iter=10,
)
reconstructed_image = optimizer.optimize()
```

Deeper reading:

- **[Optimization](https://pyralysis.readthedocs.io/optimization.html)**
- **[Measurement operator](https://pyralysis.readthedocs.io/measurement_operator.html)**

---

## Examples in this repository

| Location | What you will find |
|----------|-------------------|
| `examples/notebooks/` | Jupyter notebooks (CPU and GPU optimization sandboxes) |
| `examples/notebooks/optimization_sandbox_gpu.ipynb` | CuPy MS read, mask, and masked optimization on HD142527 |
| `examples/images/` | Demo + curated ngVLA FITS sky models (`NonParametricSource`) |
| `examples/scripts/*_components.py` | Explicit class composition |
| `examples/scripts/*_pipeline.py` | Pipeline-style orchestration |

Paired scripts include `dirtymapper`, `optimization`, and `simulation` (components vs pipeline variants).

- **[Repository examples](https://pyralysis.readthedocs.io/examples.html)**

---

## Learn more

| Topic | Link |
|-------|------|
| User guide (home) | [pyralysis.readthedocs.io](https://pyralysis.readthedocs.io/) |
| Why / architecture (flexible · composable · extensible) | [context](https://pyralysis.readthedocs.io/context.html) |
| Compose objectives and regularizers | [objective_functions](https://pyralysis.readthedocs.io/objective_functions.html), [regularization](https://pyralysis.readthedocs.io/regularization.html) |
| Components vs pipelines (examples) | [examples](https://pyralysis.readthedocs.io/examples.html) |
| NumPy / CuPy array backends (GPU imaging) | [array_backend](https://pyralysis.readthedocs.io/array_backend.html) |
| Pipelines and distributed Dask | [pipelines_distributed](https://pyralysis.readthedocs.io/pipelines_distributed.html) |
| I/O and simulation | [io_operations](https://pyralysis.readthedocs.io/io_operations.html), [simulation](https://pyralysis.readthedocs.io/simulation.html) |
| Data model and measurement operator | [data_model](https://pyralysis.readthedocs.io/data_model.html), [measurement_operator](https://pyralysis.readthedocs.io/measurement_operator.html) |
| Binder (JupyterLab, no install) | [Launch Binder](https://mybinder.org/v2/gl/clirai%2Fpyralysis/release?urlpath=lab%2Ftree%2Fexamples%2Fnotebooks) |
| API reference | [api/index](https://pyralysis.readthedocs.io/api/index.html) |
| Versioning policy | [versioning](https://pyralysis.readthedocs.io/versioning.html) |

---

## Contributing and development

| Resource | Link |
|----------|------|
| Contribution guide | [CONTRIBUTING.md](CONTRIBUTING.md) |
| Changelog | [CHANGELOG.md](CHANGELOG.md) |
| New issue | [Open an issue](https://gitlab.com/clirai/pyralysis/-/issues/new) |
| Issue tracker | [GitLab issues](https://gitlab.com/clirai/pyralysis/-/issues) |
| Testing and QA | [Testing docs](https://pyralysis.readthedocs.io/testing.html) |
| Versioning and releases | [Versioning](https://pyralysis.readthedocs.io/versioning.html) |

Pull requests and bug reports are welcome. If you are unsure where to start, open an issue and we can point you to the right part of the codebase.

---

## Citation and license

If Pyralysis supports your research, a citation is appreciated:

```bibtex
@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/}
}
```

Pyralysis is distributed under the **GNU General Public License v3.0**; see the `LICENSE` file in this repository.

---

## Contact

- **Documentation:** [pyralysis.readthedocs.io](https://pyralysis.readthedocs.io/)
- **Issues:** [gitlab.com/clirai/pyralysis/-/issues](https://gitlab.com/clirai/pyralysis/-/issues)
- **Lead developer:** [miguel.carcamo@usach.cl](mailto:miguel.carcamo@usach.cl)
