v0.3.0 step 4: type map + particle_type.target = "onehot"/"embedding"
CI / Lint (ruff check) (push) Successful in 26s
CI / Format (ruff format) (push) Successful in 28s
CI / Sync project version with tag (push) Has been skipped
CI / Lint (ruff check) (pull_request) Successful in 28s
CI / Type check (ty) (push) Successful in 31s
CI / Format (ruff format) (pull_request) Successful in 35s
CI / Sync project version with tag (pull_request) Has been skipped
CI / Type check (ty) (pull_request) Successful in 36s
CI / Tests (pull_request) Successful in 1m41s
CI / Tests (push) Successful in 1m47s

Builds the shared top-N-plus-other PDG/material maps (pooling both primary
and secondary occurrences for PDG, directly targeting the meeting's
species-collapse failure mode) and wires up conditioning.{particle,material}
= "onehot" plus stage2_model.particle_type.target in ("onehot", "embedding")
end-to-end: setup-cache persistence, Stage2OneShot's type_head (flow/ddpm)
vs. folded+ST-Gumbel-relaxed adversarial slice (wgan), and the corresponding
CE/MSE training losses. particle_type.target = "physical" stays byte-for-byte
unchanged, keeping the v0.2 migration shim's bit-identical guarantee intact.
giant predict/rollout fail loudly on a onehot/embedding checkpoint until
full decode support lands in step 6.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 15:43:48 +02:00
parent 9112e845e0
commit 4fc15ecdfc
16 changed files with 1277 additions and 102 deletions
+60
View File
@@ -6,7 +6,9 @@ from giant.data.loader import (
EVENT_ID_FILE_STRIDE,
build_index_maps,
build_index_maps_from_files,
build_pdg_topn_map_from_files,
build_process_map_from_files,
build_topn_map_from_files,
event_id_offset,
find_parquet_files,
iter_cond_chunks,
@@ -178,6 +180,64 @@ def test_build_process_map_from_files_three_files_partial_overlap(tmp_path):
assert proc_map["compt"] == 2
# ── build_topn_map_from_files / build_pdg_topn_map_from_files ──────────────
def test_build_topn_map_from_files_keeps_most_frequent(tmp_path):
materials = ["G4_AIR"] * 5 + ["PbWO4"] * 3 + ["G4_Fe"] * 2 + ["G4_Pb"] * 1
path = tmp_path / "a.parquet"
pd.DataFrame({"material": materials}).to_parquet(path)
m = build_topn_map_from_files([path], "material", n_classes=3, cast=str)
assert m.class_map["G4_AIR"] == 0
assert m.class_map["PbWO4"] == 1
assert m.class_map["G4_Fe"] == 2 # "other" (n_classes - 1)
assert m.class_map["G4_Pb"] == 2
assert m.other_members == {"G4_Fe": 2, "G4_Pb": 1}
def test_build_topn_map_from_files_fewer_values_than_n_classes(tmp_path):
path = tmp_path / "a.parquet"
pd.DataFrame({"material": ["G4_AIR", "PbWO4"]}).to_parquet(path)
m = build_topn_map_from_files([path], "material", n_classes=5, cast=str)
assert m.class_map == {"G4_AIR": 0, "PbWO4": 1}
assert m.other_members == {}
def test_build_pdg_topn_map_from_files_pools_primary_and_secondary_pdg(tmp_path):
"""A species that's rare as a primary but common as a secondary must
still rank by its pooled (primary + secondary) count, not just its
primary-role count alone — the whole point of pooling both roles
(docs/v0.3.0-design.md §8)."""
path = tmp_path / "a.parquet"
# primary pdg: mostly 11 (electron), one lone 22 (photon)
pdg = [11] * 5 + [22] * 1
# secondaries: 22 (photon) appears often as a secondary despite being
# rare as a primary above
sec_pdg_list = [[22, 22]] * 5 + [[]] * 1
pd.DataFrame({"pdg": pdg, "sec_pdg_list": sec_pdg_list}).to_parquet(path)
m = build_pdg_topn_map_from_files([path], n_classes=3)
# pooled: 11 -> 5, 22 -> 1 (primary) + 10 (secondary) = 11
assert m.class_map[22] == 0
assert m.class_map[11] == 1
def test_build_pdg_topn_map_from_files_missing_sec_pdg_list_column(tmp_path):
"""Files predating the parent->child join have no sec_pdg_list column —
must not raise, just count the primary pdg column alone."""
path = tmp_path / "a.parquet"
pd.DataFrame({"pdg": [11, 11, 22]}).to_parquet(path)
m = build_pdg_topn_map_from_files([path], n_classes=3)
assert m.class_map == {11: 0, 22: 1}
# ── build_index_maps (in-memory) ────────────────────────────────────────────