Skip to content

Releases

virgil 0.4.1

Choose a tag to compare

@benjaminpope benjaminpope released this 23 Oct 10:45
ee5757d

virgil 0.4.1

pip install --upgrade virgil-astro==0.4.1

0.4.1 is a consolidation release with no new model families. The code is shorter to read and safer by default on
aperture-masking and DISCO data, and virgil can now write the OIFITS files it reads. The full radio work
is planned for 0.4.2.

What's new

  • Write OIFITS from an OIData. write_oifits(data, path) round-trips through read_oifits, including
    closure-only (AMI) data. The pipelines now write data/processed.oifits for closure-only data and for data with
    flagged visibilities.
  • No more alias-copy detections by default. On lattice uv coverage (aperture masks, DISCO),
    BinaryPipeline and OrbitPipeline cap the default search box at half an alias cell. An explicit
    max_sep_mas is never capped.
  • OrbitPipeline checks its own verdict. It drops the night with the worst χ²/N, refits, and records
    whether the verdict and the winning alias band survive in modes.robustness.
  • Better-conditioned fits.
    • fit(precondition="diag") (provisional).
    • The L-BFGS history defaults to 100 vectors, up from 50.
    • Maximum-entropy imaging takes 30–40% fewer steps.
  • Radio CLEAN (virgil.radio_clean, provisional). This is a Cotton–Schwab CLEAN of ComplexVisData, with
    Högbom and multi-scale minor cycles, masks and a fitted restoring beam.
    See the guide.
  • A public References page lists every source that
    virgil cites or ports.

Upgrading from 0.4.0

Most code runs unchanged. Check these points:

If your code… In 0.4.1… Do this
fits with CalibrationNuisance and relies on its default priors gains and error scales have Jeffreys priors (fit coordinate ln x). Offsets are uniform over offset_beams beams (default 3): a narrower box, now enforced. Check info["at_bound"]. To restore the old boxes, pass priors(gain=, sigma=, offset=).
calls fit with L-BFGS and no lbfgs_memory the history is 100 vectors (was 50), cut to stay under 1 GiB, with a warning. LM ignores it. Pass lbfgs_memory= to choose a value.
runs BinaryPipeline or OrbitPipeline on lattice coverage without max_sep_mas the default box is capped at half an alias cell, recorded in summary["search"]["alias_cap_mas"]. Pass max_sep_mas to search wider.
calls write_oifits(tables=...) the argument is named data. Rename it. tables= warns until 0.5.
calls OrbitPipeline(epochs, path) with the output path positional output is keyword-only. Write OrbitPipeline(epochs, output=path). The positional form warns until 0.5.
calls ComplexVisData.dirty_beam(nuisance, values) values now comes before nuisance, as in dirty_image (a provisional API, no shim). Swap the arguments.
reads virgil.name it is removed. Use virgil.__version__.
relies on import virgil loading matplotlib it no longer does. virgil.plotting and virgil.oifits load on first use. Import matplotlib yourself.
sets t_ref = 0 on an orbit class it still warns, and the warning now says it becomes an error in 0.5. Set a real reference epoch.

Results and summary.json are otherwise unchanged.

Added

  • write_oifits(OIData). Errors, flags, wavelengths, (u, v) and times survive a round trip.
    • Closure phases without station indices get placeholder stations.
    • Channel widths are written as EFF_BAND; unknown widths are estimated and marked BANDEST.
    • Data whose channel widths differ between baselines of one channel are skipped, with the reason in
      processed_oifits_skipped. 0.4.0 wrote them with EFF_BAND = 0.
  • OrbitPipeline drop-a-night check (night_dropped). It is skipped below 3 nights or with
    drop_outlier_night=False.
  • fit(precondition="diag") and gauss_newton_diag (both provisional).
    • MaxEntropy's entropy metric and MaxEntropy.residuals let LM fit an image, in float64 only.
    • Preconditioning does not make weak-weight fits converge in float32.
  • virgil.radio_clean (provisional): clean, RadioCleanResult, fit_beam and restore.
  • CalibrationNuisance(sigmas="profile") and ComplexVisData.sigma_hat.
  • lattice_alias_limit works per channel.

Fixed

  • fit_orbit_aliases no longer runs out of memory on data with gains and many nights. It accumulates the
    Hessian in groups of nights that fit memory_budget, and results agree to rounding.
  • ImproperUniform priors with an event_shape no longer raise in gauss_newton_mass.

Known issues

  • Closure phases near a visibility null (#309). A closure
    phase jumps by π where a model visibility passes through zero, so its gradient is unbounded there. An image fit
    that drifts to such a null can stall. V² and the regularizers are unaffected. This was accepted for 0.4.0 and is
    carried into 0.4.1; a fix is planned.
  • Radio is provisional. virgil.radio_clean and the radio visibility classes are not yet independently
    checked against CASA. That work is the focus of 0.4.2.

Smaller and easier to read

Measured on src/virgil:

0.4.0 0.4.1
Docstrings over 60 lines 19 0 (CI gate)
Docstrings over 40 lines 63 30
Docstring lines in total 15,503 14,040 (−9%)
Private helpers called from one place 456 418 (−8%)
Source lines in total 56,804 56,790
  • Docstrings.
    • Every public docstring is 60 lines or fewer, enforced in CI, and is cut to its calling contract.
    • Derivations moved to the docs (orbit_methods, the guides).
    • Every caveat a caller needs survives, either as one docstring line or as a # deliberate: comment at the
      line it explains.
    • Sources are cited in one Source: line linking to the References page.
  • Private helpers. About 40 single-use helpers were inlined and duplicate helpers merged, with no change in
    results.
  • Size. The source stayed the same length, although about 1,400 lines of features were added.
  • Next. 0.4.2 goes further on single-use helpers.

Other

  • to_kepler runs as one jitted kernel, 2–6× faster for an eager call.
  • CI is faster: the orbit tests are sharded and timings come from CI runs.

Full details are in CHANGELOG.md.

v0.4.0

Choose a tag to compare

@benjaminpope benjaminpope released this 21 Oct 22:38
8c51dc5

What's Changed

  • Development carbon page, ledger and README badge by @benjaminpope in #253
  • Add Claude PR review workflow by @benjaminpope in #254
  • Design note: one argument order (model before data) for 0.4 by @benjaminpope in #255
  • Add the private _deprecate shim for the 0.4 argument order by @benjaminpope in #256
  • Orbits fitted to visibilities across epochs: design and stage 1 (Epochs, OrbitalBinary) by @benjaminpope in #257
  • Add virgil.metrics: image-recovery scores for reconstructions by @benjaminpope in #258
  • Add virgil.ensemble: PYRA/MYTHRA-style reconstruction ensembles by @benjaminpope in #259
  • AGENTS.md: relevant tests locally, zensical docs, direct venv python by @benjaminpope in #260
  • Model before data in the grid, limit and detection tools (argument order PR 2) by @benjaminpope in #261
  • Model before data in the likelihoods; one name per argument (argument order PR 3) by @benjaminpope in #263
  • Visibility orbits stage 2: rank_orbits, start from a positions fit, per-chain starts by @benjaminpope in #262
  • Carry argument order PR 3 (#263) to main by @benjaminpope in #266
  • CI: stop the py3.11 lowest job running out of memory by @benjaminpope in #264
  • OIData.select by wavelength windows and observables; audit of the spectro-interferometry plan by @benjaminpope in #265
  • Design note: plate-scale nuisances can absorb inter-instrument motion by @benjaminpope in #267
  • ensemble: judge the running mean on the members' own grids by @benjaminpope in #272
  • Argument order PR 4: last call sites and pytest filters (stacked on #266) by @benjaminpope in #269
  • CI: drop the legacy mkdocs workflow; lint script uses the venv python by @benjaminpope in #271
  • metrics: rms_convolved without pixel scale; clarify Lawson normalisation by @benjaminpope in #274
  • Orbit tutorial: executed outputs for visibility orbits stage 2 by @benjaminpope in #273
  • CI: two xdist workers on the lowest-deps job by @benjaminpope in #275
  • mwe_sparse_imaging: match the summary to the latest run by @benjaminpope in #276
  • Tests: clear JAX caches after each module (lowest-deps CI OOM) by @benjaminpope in #277
  • Faster CI: lighter test_epochs starts, factorized TruncatedCone render by @benjaminpope in #278
  • Design note: automatic binary orbits from multi-epoch interferometry by @benjaminpope in #279
  • PR B0: orbit benchmark harness (benchmarks/orbits) by @benjaminpope in #280
  • Orbits: noise-marginalised gap and scale-aware ranking (#268) by @benjaminpope in #281
  • Avoid zero-size gain arrays that crash jax.pmap (parallel chains) by @benjaminpope in #282
  • Fix main CI: NaN-robust ensemble selection, macOS-safe memory sampler by @benjaminpope in #283
  • Orbit tutorial: outputs re-executed on main (0 divergences) by @benjaminpope in #284
  • Orbit tutorial: outputs from rerun at 33e1390 by @benjaminpope in #285
  • Add OzSTAR sync gate and stale-notebook-output check by @benjaminpope in #286
  • Design: orbit-fitting review (published practice vs virgil's plan) by @benjaminpope in #287
  • Drop unused limits.chi2ppf by @benjaminpope in #293
  • epochs.marginal_loglike: adaptive ln s integral at a steep s_max bound (F18) by @benjaminpope in #295
  • limits.chi2ppf: work for any df without tensorflow_probability (F17) by @benjaminpope in #291
  • Fit budgets and guards; phase-coherent period grid (orbit review PR B) by @benjaminpope in #288
  • epoch_positions: peak catalogue, refine the top grid peaks before committing (PR C) by @benjaminpope in #289
  • Orbit search design (§14 revision) by @benjaminpope in #292
  • read_oifits: place a T3 leg with no visibility row at the T3 coordinates by @benjaminpope in #300
  • TruncatedCone: azimuthal modulation of the walls (leading-edge brightening) by @benjaminpope in #303
  • ensemble: keep weights below the L-curve corner; widen weight ranges; fields 1–4× by @benjaminpope in #304
  • SVI: virgil.svi.variational, flow guides started from a fit by @benjaminpope in #302
  • Orbit search D1: shared-nuisance scorer (score_orbits) by @benjaminpope in #298
  • virgil.pipeline PR 1: core, BinaryPipeline, docs and tutorial sections by @benjaminpope in #305
  • pipeline: capture stage warnings as checks, quieten quicklook, clean tutorial sections by @benjaminpope in #308
  • One import convention: jax.numpy as np, numpy as onp (#306 s1) by @benjaminpope in #312
  • Use model/data in public signatures (#306 s2) by @benjaminpope in #313
  • Oxford -ize spelling in names, with deprecated -ise keywords (#306 s3) by @benjaminpope in #314
  • Warnings, precision and small inconsistencies (#306 s4, s5, s8) by @benjaminpope in #315
  • Dead code and trivial dedups (part of #310) by @benjaminpope in #311
  • Drifted copies unified, with regression tests (part of #310) by @benjaminpope in #316
  • Render dev carbon badge as an image by @benjaminpope in #318
  • StarPipeline: stellar diameter and limb darkening by @benjaminpope in #307
  • Period aliases of undersampled orbits (fit_orbit_aliases) by @benjaminpope in #319
  • aliases: robust to singular Hessians, per-band checkpoints, flat table scales by @benjaminpope in #321
  • numpyro_model: sample each prior in its flat coordinate, as fit does by @benjaminpope in #301
  • Import zodiax as zdx by @benjaminpope in #322
  • Pre-0.4.0 review: StarPipeline model choice, pipeline and alias robustness, 0.3 regressions, de-spaghettification by @DePrinsT in #323
  • Fix the slow-test failures on main; faster, steadier CI by @benjaminpope in #325
  • Alias fit: sign-free seeds for near-equal binaries, Newton polish by @benjaminpope in #324
  • L-BFGS restarts: fix the macOS-only error_scale="fit" test failure by @benjaminpope in #327
  • Docs: comparison with other packages by @benjaminpope in #326
  • Evidence helpers take several field Images (path=None or a tuple) by @benjaminpope in #330
  • aliases: UD primary, per-epoch V² amplitude, flux flip, init warm start by @benjaminpope in #331
  • Provenance: references file, ADS fetch script, docstring citations by @benjaminpope in #332
  • contributors: indent nested bullets by 4 spaces so they render nested by @benjaminpope in #333
  • docs: remove 'Using these docs' section from index by @benjaminpope in #334
  • P1: PolarDisk engine (M=0) by @benjaminpope in #335
  • PolarDisk P2: height, linear harmonics, TruncatedCone on the engine by @benjaminpope in #336
  • CI: review-response label for unanswered Claude review points by @benjaminpope in #341
  • Design note: composite scenes (identifiability toy) by @benjaminpope in #342
  • Broadcast time-dependent models over samples; batch orbit candidates by default by @benjaminpope in #339
  • spectra.Scaled, chromatic companion-search tests and tutorial by @benjaminpope in #346
  • GN1: dense damped-Newton kernel (_gauss_newton.py) and the Gauss–Newton plan b...
Read more

virgil-astro 0.3.0

Choose a tag to compare

@benjaminpope benjaminpope released this 06 Oct 01:11
0726734

See CHANGELOG.md, section 0.3.0 (2026-10-06).

v0.2.0 - rename drpangloss to virgil

Choose a tag to compare

@benjaminpope benjaminpope released this 03 Oct 23:40
e5b9f41

This is an extensively updated release with major changes - the first of which is the renaming of drpangloss to virgil, which better reflects the scope of our project beyond just a rehashing of CANDID.

New Features

New Features Pre-release
Pre-release

Choose a tag to compare

@benjaminpope benjaminpope released this 29 Sep 03:16
5732399

What's changed

This release covers PRs #62–#68. It reorganises the package, adds composable and chromatic source models, and fixes a large batch of bugs found in code review.

⚠️ Breaking changes. Several functions were renamed or removed, and BinaryModelAngular now takes flux (companion/primary) in place of contrast. There is no deprecation period. See Migration notes below.

Chromatic scenes and likelihood options (#68)

  • Multi-file data: read_oifits and OIData accept a list of files and concatenate them.
  • Chromatic fluxes: new drpangloss.spectra module with PowerLaw(ratio, index, wavel0) as a component flux, giving SPARCO-style V = Σ fᵢ(λ) Vᵢ / Σ fᵢ(λ).
  • Resolved component: an over-resolved background flux that only adds to the normalisation.
  • Physicality checks: SourceModel.is_physical() and System.is_physical() check flux positivity and non-negative rim profiles, and work under tracing.
  • Likelihood options: model_loglike, loglike and numpyro_model take vis_error_rel, phi_error (added in quadrature) and reject_unphysical.
  • render() now works under jax.jit, so you can render posterior samples inside jitted code.

Package restructure (#66, #67)

  • One job per module: likelihood.py, limits.py, bessel.py, amigo.py, with shared helpers in _geometry.py, _utils.py and _grid.py. Legacy I/O (oifits_implaneia, savefits) moved to drpangloss.legacy.
  • Everyday names can be imported from the top-level package.
  • One flux convention: flux always means companion/primary. Reports use contrast = primary/companion and Δmag = 2.5 log₁₀(contrast), through flux_to_contrast, flux_to_delta_mag and units="flux" | "contrast" | "delta_mag" in plots.
  • One grid-plot function: plot_grid_map(values, grid, kind=...) and plot_contrast_curve replace six older plotting functions.
  • Grid and limit functions take a batch_size= keyword (default 256) to bound memory.
  • Bessel derivatives (#67): j0, j1 and bessel_jn have custom JVP rules built on the recurrence identities. Gradients, Jacobians and Hessians are exact and stable, including at x = 0.

Code-review fixes, OIFITS I/O, docs and CI (#65)

  • New OIFITS reader/writer: drpangloss.oifits.read_oifits / write_oifits need only astropy. They support multiple wavelength channels, tables, epochs and targets, FLAG columns, absolute phases from OI_VIS, and T3-only files. OIData accepts a path or any HDUList, with target=.
  • Data handling: flagged and non-finite samples are dropped from the observables, while their baselines stay available to closure phases. Phase residuals are wrapped into [-π, π) in every likelihood and χ². Pre-projected closure phases are no longer projected twice.
  • Grid fitting: a single optimiser core with scaled flux, a Newton polish step, and warnings where points do not converge. nsigma stays finite in float32. ruffio_upperlimit broadcasts.
  • Inference: checks work under jit, the Fisher matrix uses jacfwd, and a Fisher matrix with no information gets finite steps.
  • Plotting: importing no longer changes global rcParams (call set_style() to opt in). Extents are pixel-centred and figures are returned. Applying the style no longer closes open Jupyter figures.
  • cvis_binary(u, v, dra, ddec, flux) argument order now matches the other cvis_* functions.
  • CI tests Python 3.11 and 3.12 and builds the docs on every PR. Docs use mkdocstrings cross-references, and all tutorials were re-run.

Composable source models (#64)

Includes edits by Toon De Prins.

  • Composable components combined into an explicit System, with a flux_param argument, positivity constraints and traced templates. There is a new composition tutorial.
  • New source models: UniformDiskModel and ModulatedGaussianRimModel.
  • Sky orientation: fixed the BinaryModelAngular position-angle sign (North-to-East), grid-plot orientation (East-left/North-up), inclined-rim uv deprojection, and a swapped-argument PA bug in the HMC/Fisher helpers.
  • render() mixes components by integrated flux, not by peak height, and its Fourier transforms are tested against model() visibilities.
  • Fewer recompiles and fast paths for composed models. Added a numpyro_model helper.

Maintenance (#62, #63)

  • Fixed the fork-PR lint job's checkout ref and resynced the tutorial docs.

Migration notes

Old New
optimized_contrast_grid optimized_flux_grid
laplace_contrast_uncertainty_grid(best_idx, data, model, grid) laplace_flux_uncertainty_grid(data, model, grid, flux=None)
absil_limits(...) absil_limits(data, model, grid, sigma)
ruffio_upperlimit (in grid_fit) drpangloss.limits.ruffio_upperlimit
azimuthalAverage, radial_limit_summary limits.radial_profile
plot_likelihood_grid, plot_contrast_limit_map, plot_optimized_and_grid, plot_optimized_and_sigma plot_grid_map(values, grid, kind=...)
plot_radial_limit_summary, plot_contrast_limits plot_contrast_curve
BinaryModelAngular(..., contrast=c) BinaryModelAngular(..., flux=1/c)
OIData.standardize_data / standardize_errors / flatten_model / unpack_all flatten_data, standardize_model
drpangloss.oifits_implaneia, drpangloss.savefits drpangloss.legacy.* (or the new drpangloss.oifits)
GRID_BATCH_SIZE global batch_size= keyword
models.HarmonixAdapter, model_class alias, models re-exports removed

Grid tools now find the flux parameter as the one key ending in flux, or you can pass flux_param=. The old key-order fallback is gone.