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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
- 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>