Skip to content

Add generic auxiliary array bundles to the fit input HDF5#145

Merged
davidwalter2 merged 4 commits into
WMass:mainfrom
lucalavezzo:auxiliary-group
Jun 23, 2026
Merged

Add generic auxiliary array bundles to the fit input HDF5#145
davidwalter2 merged 4 commits into
WMass:mainfrom
lucalavezzo:auxiliary-group

Conversation

@lucalavezzo

Copy link
Copy Markdown
Collaborator

What

Adds a generic auxiliary group to the input HDF5: a side channel for
ParamModels to carry pre-computed arrays inside the datacard rather than
reading them from a separately-versioned external file at fit time. It is not
used by the fit itself.

This mirrors the existing external_terms mechanism (rabbit/external_likelihood.py):
the TensorWriter collects named bundles and serializes them in write();
FitInputData exposes them on read.

Motivation

A downstream ParamModel (WRemnants SCETlibNPParamModel) currently reads its
reco×gen response matrix R (and the gen-total N_gen) from a hand-made skim
file that has to be kept in sync with the histmaker output by hand — a staleness
risk. With this, the extraction can be run once at datacard-build time and the
finished arrays stored in the fit input, so R is always consistent with the run
that produced the datacard. The feature itself is fully generic — "scetlib_np"
is just one entry name.

API

  • TensorWriter.add_auxiliary(name, datasets)datasets is a dict
    {key: np.ndarray | list[str]}. Duplicate names are rejected.
  • FitInputData.auxiliary{name: {key: ndarray | list[str]}}; an empty dict
    when no group is present (back-compatible with existing datacards).
  • New module rabbit/auxiliary.py: write_auxiliary_group /
    read_auxiliary_from_h5.

Round-trip guarantees

  • Numeric arrays survive bit-for-bit, including dtype and shape (incl. >2D),
    via writeFlatInChunks (original_shape attr) + maketensor.
  • 1-D string lists (e.g. axis names) survive as list[str] via a vlen-str
    dataset, like external_terms' params.

Testing

New tests/test_auxiliary.py (4 cases, all pass):

  1. a response-matrix-shaped bundle (multi-dim float64 R, N_gen, axis-name
    lists, edges) round-tripped through add_auxiliary → write() → FitInputData;
  2. a datacard with no auxiliary reads back as {};
  3. add_auxiliary rejects a duplicate bundle name;
  4. a direct write_auxiliary_group/read_auxiliary_from_h5 round-trip with mixed
    dtypes (float32, int64, >2D) and the None → {} case.

autoflake/isort/black/flake8 clean.

Notes

  • No change to the fit, the tensor layout, or any existing reader path; purely
    additive.
  • Consumed by WRemnants PR #701 (which will bump the rabbit submodule to the merge
    commit).

🤖 Generated with Claude Code

Mirror the external_terms mechanism so ParamModels can carry pre-computed
arrays (e.g. a reco x gen response matrix) inside the datacard rather than
reading them from a separately-versioned file at fit time.

- rabbit/auxiliary.py: write_auxiliary_group / read_auxiliary_from_h5.
  Numeric arrays round-trip bit-for-bit (dtype + shape, incl. >2D) via
  writeFlatInChunks/maketensor; 1-D string lists round-trip as list[str]
  via a vlen-str dataset.
- TensorWriter.add_auxiliary(name, datasets) + write() serialization under
  a top-level "auxiliary" group (duplicate-name guarded).
- FitInputData.auxiliary: {name: {key: ndarray|list[str]}}; {} when the
  group is absent (back-compatible read).
- tests/test_auxiliary.py: round-trip through write()+FitInputData, the
  no-auxiliary and duplicate-name cases, and a direct mixed-dtype round-trip.

Not used by the fit itself; a side channel for ParamModels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lucalavezzo added a commit to lucalavezzo/WRemnants that referenced this pull request Jun 23, 2026
Carry the reco x gen response matrix R (and the gen-total N_gen) through the
setupRabbit datacard instead of a separate file, so the fit is self-contained
and R is always consistent with the run that built the card (review comment WMass#6).

- setupRabbit: after the input loop, extract R + N_gen once from the unfolding
  histmaker output and embed them in the fit input via rabbit add_auxiliary
  (the "scetlib_np" group). Presence-based lenient guard
  (response_matrix.has_response): embed only when an input carries both the
  response hist and the prefsr gen-total; raise if more than one qualifies.
- response_matrix: add has_response() (cheap both-present check, never raises).
- param_model: read R only from indata.auxiliary["scetlib_np"]; drop the
  unfolding_hdf5_path argument and the sigma_gen(lambda_c) N_gen fallback (raise
  if N_gen is absent -- the proxy made the central closure circular).
- scetlib_np package __init__: import SCETlibNPParamModel lazily (PEP 562) so
  response_matrix can be imported by setupRabbit without pulling in TensorFlow.

Requires a rabbit with TensorWriter.add_auxiliary (WMass/rabbit#145); the
submodule bump is a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removed redundant explanation about the auxiliary entry not being used by the fit itself.
@lucalavezzo lucalavezzo requested a review from davidwalter2 June 23, 2026 15:56
@davidwalter2 davidwalter2 merged commit 84eb4d7 into WMass:main Jun 23, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants