Commit Graph

189 Commits

Author SHA1 Message Date
lars f81263fa6e Add event-level shower observables to giant.analysis
Aggregates giant predict --coord local output per event_id into total
deposited energy, longitudinal/transverse shower profiles, and shower-max
depth, reconstructed into world-frame physical units (mm, MeV). Streams the
file in two polars passes rather than building a SampleCollection, since
per-event sums would be corrupted by row subsampling on these large files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 15:52:47 +02:00
lars 6fc68fe1aa Export plots for knowledge base 2026-06-22 14:41:45 +02:00
lars 936249a8a0 Rerun validation 2026-06-22 14:40:58 +02:00
lars ae1b565ca3 Add KL bar plots and sample_frac to load_predicted_local; ignore root parquet scratch files
Adds plot_kl_bars/plot_kl_bars_pl (numpy/polars variants) for ranking which
target dimension or pdg/material stratum drives KL regressions, with the
same kl*n group capping as plot_marginals. Switches existing histogram
plots to step-type/log-scale. Adds sample_frac to load_predicted_local for
subsampling large predict parquets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 11:52:41 +02:00
lars 9d7b1ca9bb Buffer predict rows across row-group boundaries before inference
Inference batches were capped at the source parquet's row-group size
(e.g. 122,880 rows) because the old loop only sliced within a single
chunk read from disk. --batch-size (including auto) had no effect once
it exceeded that, leaving most estimated GPU memory unused. Accumulate
rows across row groups and files into a buffer and slice exactly
batch-size pieces off it, so inference always uses the requested batch
size regardless of how the file happens to be chunked.
2026-06-22 09:19:56 +02:00
lars 903227d2df Skip rows with unknown PDG codes during predict
Checkpoints have a fixed PDG embedding vocab sized at train time, so a
code unseen during training has no embedding index. Drop those rows
and report a per-code skip count instead of raising a KeyError.
2026-06-22 09:06:05 +02:00
lars c057693d90 Add tqdm progress bar to predict 2026-06-22 08:53:04 +02:00
lars 74012fe049 Calibrate auto batch size separately for inference vs training
Inference has no backward graph or optimizer state, so it has a much
lower per-sample memory footprint than training. estimate_batch_size
now takes a training flag selecting between two calibration points;
predict uses the inference one (hidden_dim=1024, n_blocks=8,
batch_size=65536 measured at ~2037 MiB).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 08:40:38 +02:00
lars 92d38cbed4 Add --batch-size auto to predict, matching train
Estimates batch size from free GPU memory using the checkpoint's
hidden_dim/n_blocks, same as the train command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 08:23:58 +02:00
lars 46068f356f Add hyperparameter scan 2026-06-22 07:27:02 +02:00
lars aef0a588ce Add --batch-size auto to estimate batch size from free GPU memory
Calibrated against a measured reference point (hidden_dim=512,
n_blocks=6, batch_size=131072 -> ~8 GiB VRAM), assuming activation
memory scales linearly with batch_size * hidden_dim * n_blocks.
CUDA-only for now since it relies on torch.cuda.mem_get_info.
2026-06-19 13:24:42 +02:00
lars 74d0883868 Remove scripts/train.py in favor of the giant train CLI
The Typer-based giant/cli.py train command now has full feature
parity (dropout, warmup-epochs, validate-steps, shorthand flags),
making the standalone argparse script redundant.
2026-06-19 13:17:15 +02:00
lars cf653f7664 Add linear warmup before cosine LR decay
Replaces CosineAnnealingLR with a LambdaLR that linearly ramps the LR
from lr/warmup_epochs to lr over the first warmup_epochs steps, then
applies cosine decay for the remainder. Default warmup_epochs=5;
overridable via --warmup-epochs CLI flag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 10:43:31 +02:00
lars 7f62141445 Make sampler step count configurable for validation
validate_marginals and collect_samples could already vary flow ODE
steps for inference (giant predict --steps), but training-time
marginal validation and DDIM evaluation were stuck at hardcoded
defaults. Add a validate_steps config/CLI option and forward steps to
sample_ddim consistently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:52:14 +02:00
lars e505e2d141 Fix miniCaloSim link in README
The relative path assumed a sibling checkout; point to the actual
GitLab repo instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:43:24 +02:00
lars 0cdb5db947 Update README to match current architecture and tooling
Output space is 9D (post_dir + travel_dir), not 6D; documents the
giant CLI, analysis/validate modules, ROOT-to-parquet conversion
script, cpu/cuda install extras, and the ruff/ty dev tooling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:43:04 +02:00
lars 8cebc4809d Apply ruff format and document lint/type tooling in CLAUDE.md
First repo-wide ruff format pass, plus a note in CLAUDE.md to run
ruff and ty periodically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:40:27 +02:00
lars 53fd2e4405 Add ruff and ty as dev dependencies, fix lint/type findings
ruff removed unused imports across analysis.py and several test files.
ty caught a wrong dict[int, int] annotation on StreamingStepsDataset's
mat_map (materials are strings) and a real bug in steps_to_parquet.py
where --compression none passed None to polars' write_parquet, which
only accepts the literal "uncompressed". Also narrows a few
Optional-typed attributes (ddpm_schedule, Normalizer.mean/std) with
asserts and aligns __getitem__'s parameter name with torch's Dataset
base class.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:38:40 +02:00
lars c1a8a900d3 Split torch into cpu/cuda extras and pin dependency version bounds
Pins torch to 2.3.x via mutually-exclusive cpu/cuda uv extras (newer
torch requires newer NVIDIA drivers), and adds upper bounds to the
other dependencies based on current PyPI releases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:30:35 +02:00
lars 80b198c1a5 Add lazy polars I/O and duplicate KL/constraint checks for giant.analysis
load_predicted_local now reads predict parquet via a lazy polars scan with
column projection pushed into the reader, instead of materializing the
whole file as a pandas DataFrame. Also adds marginal_table_pl and
constraint_report_pl, polars-native duplicates that read straight from a
predict parquet path/LazyFrame and stay lazy per (group, dim) pair, so
peak memory is one column slice rather than the whole SampleCollection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:06:58 +02:00
lars a867fc4aae Add giant.analysis module for notebook-based model quality diagnostics
Provides stratified marginal comparisons, joint-structure checks (correlation
matrices, physically-coupled pairwise plots, direction alignment), and
physical-constraint validation (unit-norm directions, non-negative raw
targets) for a trained model's generated samples, building on the aggregate
marginal/KL check already in giant.validate.

Supports two entry points: live sampling against a checkpoint + val data
(load_model_bundle/collect_samples), or loading a precomputed
`giant predict --coord local` parquet directly (load_predicted_local) without
needing the checkpoint at all. Predict output is now tagged with parquet
schema metadata so the loader can verify a file's format and reject
coord=global or untagged files with a clear error instead of guessing from
column names.

Also extends the config git-hash mismatch warning (added for --config
loading) to checkpoint loading: both `giant predict` and
analysis.load_model_bundle now look for a config.toml next to the checkpoint
and warn (without failing) if it was generated from a different git commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:36:19 +02:00
lars da84801625 Add configurable dropout to ResBlocks
Wire a dropout hyperparameter (default 0.1) through the config, model,
training pipeline, and CLI. Persisted in saved model_config so checkpoints
reconstruct the architecture correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 15:59:20 +02:00
lars 5d161a52b2 Add graceful shutdown on SIGINT/SIGTERM
A Ctrl-C or job-scheduler kill signal during training used to crash with a
raw KeyboardInterrupt mid-batch, abandoning whatever checkpoint state was
in flight. Now a signal sets a flag instead: the loop discards an
in-progress epoch's partial work (since lr_sched hasn't stepped and there's
no validation pass yet for it), but lets an epoch that's already past its
training loop finish normally — checkpoint, metrics row, and all — before
stopping. A second signal force-kills immediately for an unresponsive run.

Verified against a backgrounded run: SIGINT mid-training stopped cleanly
with a consistent last.pt/metrics.csv, and --resume picked up exactly at
the next epoch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:00:01 +02:00
lars 893d91e749 Add KL divergence to marginal validation and hook it into the training loop
validate_marginals now estimates a per-dimension KL(real || generated) via
a shared histogram, alongside the existing mean/std comparison, so
distribution-shape drift shows up even when the first two moments match.

Wire it into giant/train.py: every validate_every epochs (default 10, 0
disables), the training loop runs validate_marginals against val_loader and
prints the table. validate_every flows through DEFAULT_CONFIG/config.toml
and is exposed as --validate-every on both giant train and scripts/train.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:45:12 +02:00
lars 43634ef77a Dedup training pipeline, add seeding/resume and per-epoch metrics logging
cli.py and scripts/train.py duplicated ~140 lines of training setup and had
drifted (scripts/train.py forgot to save model_config, breaking predict on
those checkpoints). Extract shared logic into giant/constants.py (X_DIM,
target names), giant/config.py (device/git/TOML/seeding helpers, run
metadata), and giant/pipeline.py (the actual training-job orchestration),
so both entry points become thin CLI wrappers around the same code path.

Also adds --seed/--resume support (checkpoints now carry optimizer/scheduler
state, epoch, and best_val_loss), a richer [meta] section in the saved
config.toml (git hash, seed, versions, timestamp, invocation, dataset
stats), and a metrics.csv (train/val loss, lr, epoch time) written every
epoch and append-safe across resumes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:32:37 +02:00
lars f1a82b5853 Add --coord local mode to predict for raw-space prediction debugging
Outputs the model's 9D prediction (denormalised only — still local
frame, log-scaled scalars) alongside the matching ground-truth target
for the same input rows, so they're directly comparable in the space
the loss is actually computed in. Also fixes mat_map keys being cast
with int() instead of str() when loading a checkpoint in predict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:55:58 +02:00
lars 72bd65ff9f Add post_pos as a model target via travel_dir decomposition
step_length already encodes |post_pos - pre_pos| by definition, so a raw
post_pos target would duplicate that magnitude and could drift inconsistent
with step_length during sampling. Instead add travel_dir, a unit vector
(local frame) giving only the direction of pre_pos->post_pos; post_pos is
reconstructed at inference as pre_pos + step_length * travel_dir, keeping
the two self-consistent. Target grows from 6D to 9D.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:36:55 +02:00
lars c3b7b2744c Batch StreamingStepsDataset internally instead of per-row collate
The dataset yielded one row at a time, forcing DataLoader's default
collate to Python-loop over every row to assemble each batch. That
loop scales with batch size and was pinning a CPU core at 100% while
the GPU sat idle. Now the dataset yields whole batches via vectorized
numpy slicing, used with DataLoader(batch_size=None).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:08:36 +02:00
lars 6e2fe12a6d Fix installed torch version to be compatible with cuda drivers 2026-06-18 10:06:53 +02:00
lars 9e97fb8159 Rename direction columns from pre_dir_x/y/z to pre_dx/dy/dz
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:45:27 +02:00
lars d402cdace3 Rename pre_energy/post_energy columns to pre_E/post_E
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:31:41 +02:00
lars 877f83a5cd Handle material column as string type
material is a string literal (e.g. "G4_PbWO4"), not an integer. Store as
object array and key mat_map on str throughout loader and transforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:29:11 +02:00
lars 78c2a61ecd Fix column names to match actual parquet schema
material_id → material, n_secondaries derived from child_track_ids.list.len()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:26:57 +02:00
lars 2e4b4d91d1 Add ROOT-to-parquet conversion script with convert dependency group
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:27:49 +02:00
lars 646a9d7a72 Add giant predict command
- iter_cond_chunks: column-projected row-group streaming; post-step
  variables are never read from disk during inference
- build_cond_features: assembles conditioning arrays without any target
  or post-step fields
- inv_local_frame_rotation: Rodrigues R^T (negative angle) to rotate
  predicted post_dir back from local frame to world frame
- giant predict: loads checkpoint, streams input, runs flow matching
  sampler, inverse-normalises and inverse-rotates outputs, writes
  predictions incrementally as parquet via PyArrow ParquetWriter
- train now saves model_config in checkpoint so predict can reconstruct
  the architecture without extra CLI flags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:10:27 +02:00
lars 93c4d6b74d Add streaming data pipeline and giant CLI entry point
- Streaming pipeline: row-group-level parquet reading (PyArrow) so
  large files never fully land in RAM; Welford online algorithm for
  normalizer fitting; StreamingStepsDataset with shuffle buffer and
  multi-worker file striping; event-ID scan and vocab scan via cheap
  single-column reads
- giant/cli.py: typer-based CLI with `giant train` subcommand, mirroring
  scripts/train.py; --shuffle-buffer flag for RAM control
- pyproject.toml: add typer>=0.12 dependency and giant entry point
- train.py: replace len(loader.dataset) with local counters (compatible
  with IterableDataset which has no __len__)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:03:48 +02:00
lars 9277d79dff Implement Phase 1: full data pipeline, model, training, and config support
- Data pipeline: loader (parquet→numpy), transforms (log, local-frame
  Rodrigues rotation, Normalizer), StepsDataset with event-ID-based split
- Model: SinusoidalEmbedding, ConditionEncoder, ResBlock, DenoisingMLP
- Schedule: cosine DDPM and conditional flow matching loss (Lipman 2022)
- Samplers: flow (Euler ODE), DDPM ancestral, DDIM deterministic
- Training loop: AdamW + cosine LR, grad clipping, best-val checkpoint
- Validation: per-dimension marginal summary (normalised space)
- CLI: TOML config support with CLI-overrides; hyperparam-encoded output
  directory; config.toml with git hash saved into each run's checkpoint dir
- 21 unit tests covering transforms, network, flow/DDPM losses, dataset splits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 10:48:03 +02:00
lars c3bf3abebf Add CLAUDE.md with architecture overview and dev commands
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 09:31:45 +02:00
lars 943a044608 Initial commit: giant surrogate model with two-phase roadmap in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 09:29:19 +02:00