RHEPLICANT
A REPLICa of an ANTenna — a JAX + Equinox framework for building differentiable replicas of single-antenna radio telescopes: horns, dipoles, and dishes alike. Documentation: rheplicant.readthedocs.io
A RHEPLICANT twin is one pure function from sky and instrument parameters to raw data. Because every stage — foregrounds, ionosphere, beam, receiver reflections, gain drifts, digitisation — is differentiable, the same twin that simulates an observation also calibrates it: gradients, Bayesian posteriors, Fisher forecasts, and neural surrogates all run through the instrument model itself, with no re-implementation.
from rheplicant.radio import assemble, GlobalSignalOperator, ForegroundOperator, GainOperator
twin = assemble(GlobalSignalOperator(...), ForegroundOperator(...), GainOperator(...))
observation = twin(state) # simulate — and differentiate, fit, sample
First deployed for RHINO (a horn antenna targeting the 21 cm global signal); the core is domain-agnostic by construction.
The name
RHEPLICANT is REPLICANT wearing RHINO's horn. REPLICANT is itself a
portmanteau of REPLICa and ANTenna — a digital twin is a replica, and
this one is of a radio antenna — the two words overlapping on their shared A.
Slip an H in behind the first letter and R… becomes RH…, the mark of
RHINO, the horn antenna the framework was first built for:
R E P L I C A replica
A N T antenna
─────────────────
R E P L I C A N T replicant
+ H → RH… (for RHINO)
─────────────────
R H E P L I C A N T rheplicant
One-line gloss: a differentiable replica of a radio antenna — first, of RHINO.
Philosophy
-
Everything is an operator acting on a state. One contract —
State in, State out— covers sky models, instrument effects, data processing, filters, even neural networks. If it transforms the scientific context, it is an operator; there is nothing else to learn. -
The twin is a differentiable function. Every physical parameter is a pytree leaf, so
jit,grad, andvmapapply to the entire instrument. Systematics stop being nuisances you correct for and become parameters you infer, forecast, and marginalise. -
Composition is physics — and it is implicit in the signal path. Sequential effects chain (
Pipeline), independent contributions add (SumOperator), switched paths select (SelectOperator). The canonical signal-path graph knows how elements connect, soassemble(*operators)builds the right composition from a set: provide only a sky and a beam, get exactly the beam-convolved sky — partial models come free. -
Purity everywhere. States are immutable (functional updates only), randomness is data flowing through the state (one seed reproduces an entire run), and operators have no hidden side effects. This is what makes the whole twin safe to transform.
-
Forward models never contain inference. A single seam —
build_forward_fn— turns any twin intof(params) -> prediction. Gradient and Adam calibrators, NumPyro posteriors, Fisher forecasts, and surrogate training all connect there; calibration never contaminates the instrument description. -
Interfaces first, physics second. Every operator ships as a trivial-but-runnable placeholder whose contract (shapes, PRNG consumption, linearity in calibration parameters) is real and tested. Real physics replaces function bodies, never interfaces — the native differentiable limTOD sky engine arrived exactly this way.
-
Loud failure over silent wrongness. Structural validation at every boundary, trace-time (jit-safe) shape checks, provenance-tagged covariance matrices, assembly-time graph errors. In a framework built to chase 0.1 % systematics, a wrong number is worse than an exception.
-
The core is domain-agnostic.
rheplicant.corenever imports the radio layer (a test enforces it). Radio astronomy is the first application, not the design center.
Install
pip install rheplicant
# or, for development:
git clone https://github.com/zzhang0123/rheplicant
cd rheplicant && uv sync # extras: uv sync --extra numpyro
Requires Python ≥ 3.11, jax ≥ 0.5, equinox ≥ 0.13. Distribution and import
name are the same: rheplicant.
Sixty seconds of RHEPLICANT
import jax, jax.numpy as jnp, equinox as eqx
from rheplicant import State, Coordinates
from rheplicant.radio import assemble, SkyOperator, GainOperator, NoiseOperator
from rheplicant.inference import build_forward_fn, GradientCalibrator
state = State(
coords=Coordinates(time=jnp.linspace(0, 60, 128),
freq=jnp.linspace(60e6, 85e6, 32)),
key=jax.random.key(0),
meta={"telescope": "my-antenna"},
)
# 1. Simulate: provide operators; the signal-path graph composes them.
twin = assemble(
SkyOperator(amplitude=jnp.array(1e3)),
GainOperator(gain=jnp.array(1.1)), # the truth to recover
NoiseOperator(sigma=jnp.array(0.5)),
)
observed = eqx.filter_jit(twin)(state)
# 2. Calibrate: freeze everything except the gain, descend the gradient.
model = twin.replace_node("gain", GainOperator(gain=jnp.array(1.0)))
spec = jax.tree.map(lambda _: False, model)
spec = eqx.tree_at(lambda p: p["gain"].gain, spec, replace=True)
forward, params0 = build_forward_fn(model, state, filter_spec=spec)
params_fit, losses = GradientCalibrator(learning_rate=2e-7, n_steps=200).fit(
forward, params0, observed.data
)
print(jax.tree.leaves(params_fit)[0]) # ~1.1
The same forward plugs into NUTS posteriors (to_numpyro_model), Fisher
forecasts (fisher_information), and neural-surrogate training — see the
guided tour.
What is in the box
- Core —
State(immutable pytree context),Pipeline/SumOperator/SelectOperatorcomposition,SignalGraph+assemble(graph-guided auto-composition with lit/dim mermaid & HTML rendering). - Radio — a 29-node canonical signal-path graph covering every element of a single-antenna experiment: sky components, ionosphere, RFI, shared chromatic beam, noise-wave/reflection terms, CW tone and switched calibration loads, gain, thermal noise, EMI, ADC, flagging, averaging — plus a modular sky engine (limTOD bridge / projection matrices / m-mode / native differentiable limTOD) and linear analysis filters (sidereal, sky-space map-making, fringe-rate/delay).
- Inference — gradient & Adam calibrators, NumPyro bridge with pytree
priors and posterior predictive, Fisher / Cramér-Rao / delta-method
uncertainty propagation, Monte Carlo pushforward,
NeuralOperatorsurrogate stages, MomentRFI flagging bridge, masked likelihoods.
Documentation
Rendered docs: rheplicant.readthedocs.io
(Sphinx + furo; build locally with
uv run sphinx-build -b html docs docs/_build/html).
| Document | What it covers |
|---|---|
| Guided tour | The complete API, top to bottom, with runnable snippets |
| Operator catalog | Every operator: graph node, role, parameters |
| Architecture | Design decisions D1–D13, element taxonomy, physics roadmap |
| Changelog | What arrived when |
examples/ |
Four end-to-end runnable demos |
Status
The architecture and inference layer are complete and tested end-to-end
(330+ tests, ~96 % coverage, jit+grad+vmap through the full twin; assembly
is regression-tested bitwise against hand-built composition). Radio operator
physics is deliberately placeholder pending ports from limTOD and friends
— except the native differentiable sky engine, which is real. Conventions:
degrees in public APIs, radians internally; strings in meta (static),
numbers in coords/env/aux (traced); one seed reproduces a run.
No CI yet — run uv run pytest and uv run ruff check before pushing.
Developers and maintainers
- Zheng Zhang
- Phil Bull
- Jordan Norris
- Rashi Srivastava
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rheplicant-0.1.2.tar.gz.
File metadata
- Download URL: rheplicant-0.1.2.tar.gz
- Upload date:
- Size: 115.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ba6e120e9e1a0974c96c33cd1aef02209615ae251944d7b22a94028f2d41b2c
|
|
| MD5 |
92a635689e8dea7eadb00eb3378d35d3
|
|
| BLAKE2b-256 |
d7913dd990d0d21c41a125b653937d97329a7c08160709e594619e1253d40690
|
Provenance
The following attestation bundles were made for rheplicant-0.1.2.tar.gz:
Publisher:
publish.yml on zzhang0123/rheplicant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rheplicant-0.1.2.tar.gz -
Subject digest:
4ba6e120e9e1a0974c96c33cd1aef02209615ae251944d7b22a94028f2d41b2c - Sigstore transparency entry: 2251549184
- Sigstore integration time:
-
Permalink:
zzhang0123/rheplicant@efb5ed2c115fa32ef4247dfc72cb68b67faeb06a -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/zzhang0123
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@efb5ed2c115fa32ef4247dfc72cb68b67faeb06a -
Trigger Event:
release
-
Statement type:
File details
Details for the file rheplicant-0.1.2-py3-none-any.whl.
File metadata
- Download URL: rheplicant-0.1.2-py3-none-any.whl
- Upload date:
- Size: 87.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6180bf0bf6cd560652188be698fbe5ff61597184fc9c6f6e1b304af4da9b3ef6
|
|
| MD5 |
4e791845d871d94a80c8b651666d0669
|
|
| BLAKE2b-256 |
1f4ea44862682a94b26f18b8315f6481a4aee08f124be8e3dcf5fdc4f388ed55
|
Provenance
The following attestation bundles were made for rheplicant-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on zzhang0123/rheplicant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rheplicant-0.1.2-py3-none-any.whl -
Subject digest:
6180bf0bf6cd560652188be698fbe5ff61597184fc9c6f6e1b304af4da9b3ef6 - Sigstore transparency entry: 2251549579
- Sigstore integration time:
-
Permalink:
zzhang0123/rheplicant@efb5ed2c115fa32ef4247dfc72cb68b67faeb06a -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/zzhang0123
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@efb5ed2c115fa32ef4247dfc72cb68b67faeb06a -
Trigger Event:
release
-
Statement type: