ψ Vikshep
← Back to recipes

Recipe 01

HEP Tagging (DisCo)

Mass-decorrelated boosted-object tagging.

When to use this

Use this recipe when you are running a resonance search with boosted objects — heavy Higgs → ZV, W′/Z′, X → tt̄, and similar — and you need a jet tagger that does not deform the background mass spectrum. Any standard neural tagger trained on jet features will implicitly learn the jet mass, making cuts on its score sculpt the background and create fake bumps in your bump-hunt.

This recipe replaces the learned feature extractor with the wavelet scattering transform, producing dimensionless r₂ = S₂/S₁ features that are scale-invariant by construction. A DisCo penalty in the classifier training then drives the residual statistical dependence between the tagger score and the resonance mass to zero on background events.

The result is validated by two numbers: the significance gain Δσ over the NN baseline, and the Jensen–Shannon divergence ΔJSD measuring background mass-shape distortion before and after the cut. Both are computed against your existing Wilks Δχ² pipeline — no re-optimisation of the analysis required.

What it needs

Jet constituent four-vectors (ROOT TTree or HDF5) or pre-rasterised 64×64 η×φ jet images. The following branch names are expected by default and can be overridden via config:

# ROOT TTree expected branches fatjet_constituent_pt # float[N] — per-constituent pT in GeV fatjet_constituent_eta # float[N] — pseudorapidity fatjet_constituent_phi # float[N] — azimuthal angle (radians) fatjet_constituent_e # float[N] — energy in GeV reco_zv_mass # float — reconstructed resonance mass (GeV) FullEventWeight # float — MC event weight isSignal # int — 1 for signal, 0 for background # Alternatively: pre-rasterised jet image # shape: (N_events, 64, 64, 1) — float32, HDF5 dataset "jet_image"

How it works

MCP tool call sequence

01

vikshep/ingest

Loader reads constituent four-vectors from the ROOT TTree (or HDF5), writes them to a POSIX shared-memory segment, and returns a 28-char SHA3-256 hex OID. No serialisation crossing the Python–Rust boundary — the Rust data plane maps the same shared memory page directly.
in: ROOT TTree / HDF5 file pathout: 28-hex OID (shm://...)
02

vikshep/compute_scattering

Calls the C++/CUDA scattering engine via the Rust FFI bridge. Runs 2-D oriented SE(2) scattering with J=4 scales, Q=1 voice per octave, and L=8 orientations. Each jet image is convolved with the full oriented Morlet filter bank; the cascade modulus produces first- and second-order coefficients. Output is a coefficient tensor OID in shared memory.
in: jet image OIDout: scattering coefficients OID · shape (N, J·L, J·L)
03

vikshep/reduce_scattering

Reduction with method="ratio". Computes r₂[λ₁, λ₂] = S₂[λ₁, λ₂] / S₁[λ₁] for all valid (λ₁, λ₂) pairs (j₂ > j₁). Drops S₀ and the lowest-j S₁ entries (they carry the pT scale). The result is a dimensionless feature vector invariant to overall energy scale.
in: scattering coefficients OIDout: r₂ feature OID · shape (N, n_pairs)
04

vikshep/train_classifier_disco

Fits a lightweight MLP on the r₂ features under the weighted DisCo training objective: wBCE(ŷ, y) + λ · dCorr²_w(ŷ, m | bkg). The distance correlation term is computed over background events only and penalises any statistical dependence between the tagger output and the reconstructed resonance mass. λ is a hyperparameter swept over a grid; the optimal value maximises signal efficiency at fixed JSD budget.
in: r₂ feature OID + mass branch + isSignal branch + FullEventWeightout: classifier model OID + AUC + mass-JSD pre/post cut
05

(export)

Writes the classifier model and benchmark numbers to disk. Output format: HDF5 containing the model weights and the two benchmark numbers (Δσ and ΔJSD) computed against the analysis’s existing Wilks Δχ² significance pipeline. A ROOT TFile with the post-cut mass histogram is also produced for visual inspection.
in: classifier OIDout: classifier.h5 + benchmark.json + mass_hist.root

What you get

  • classifier.h5 — trained MLP weights (portable, no runtime dependency on Vikshep)
  • benchmark.json — { auc, jsd_precut, jsd_postcut, delta_sigma, lambda_optimal }
  • mass_hist.root — background mass histograms pre/post cut, for visual inspection
  • feature_oids.txt — SHA3-256 OIDs of intermediate scattering tensors (reusable)
  • r2_features.h5 — exported r₂ feature matrix, shape (N_events, n_pairs)

Configure it

paramdefaultdescription
dim2Input dimensionality (jet images are 2-D)
groupso2Symmetry group — SE(2) roto-translation for 2-D images
J4Number of octaves (scales). Higher J = longer range correlations
Q1Voices per octave. Q=1 is sufficient for jet substructure
L8Orientations in [0, π). L=8 gives 22.5° resolution
order2Maximum scattering order. order=2 captures >99% of jet energy
pad_phiCircularAzimuthal axis is periodic — use circular convolution
pad_etaZeroPadη axis is non-periodic — zero-pad to next power of two
lambda_grid0,0.1,0.5,1,2,5,10DisCo penalty strengths swept during training
mass_branchreco_zv_massBranch name for the resonance mass (override as needed)

Run it — CLI

bun run main.ts process \ --input data/jets.root \ --recipe hep-tagging-disco \ --mass-branch reco_zv_mass \ --lambda-grid "0,0.1,0.5,1,2,5,10" \ --output results/hep-tagging/

Run it — Dashboard

Dashboard screenshot — coming in Pilot

The Vikshep dashboard shows the mass histogram before and after the cut, the JSD live readout as λ is swept, and a side-by-side significance comparison against the existing NN baseline. Available in the Pilot release.

Related

The math behind r₂Deformation stabilityBSM Anomaly DetectionGeneral Feature ExtractionPilot — UoE deploymentDisCo paperGitHub

Get started

Ready to run this recipe?

Talk to founders →Request a feature ↗View on GitHub ↗