Make config dataclasses the single source of truth for DEFAULT_CONFIG
CI / Format (ruff format) (push) Successful in 31s
CI / Lint (ruff check) (push) Successful in 31s
CI / Sync project version with tag (push) Has been skipped
CI / Lint (ruff check) (pull_request) Successful in 44s
CI / Type check (ty) (push) Successful in 46s
CI / Format (ruff format) (pull_request) Successful in 39s
CI / Sync project version with tag (pull_request) Has been skipped
CI / Type check (ty) (pull_request) Successful in 39s
CI / Tests (pull_request) Successful in 3m38s
CI / Tests (push) Successful in 3m45s
CI / Format (ruff format) (push) Successful in 31s
CI / Lint (ruff check) (push) Successful in 31s
CI / Sync project version with tag (push) Has been skipped
CI / Lint (ruff check) (pull_request) Successful in 44s
CI / Type check (ty) (push) Successful in 46s
CI / Format (ruff format) (pull_request) Successful in 39s
CI / Sync project version with tag (pull_request) Has been skipped
CI / Type check (ty) (pull_request) Successful in 39s
CI / Tests (pull_request) Successful in 3m38s
CI / Tests (push) Successful in 3m45s
DEFAULT_CONFIG and build_models/build_critics/StageSpec.from_config's inline .get(key, default) fallbacks had already drifted: two keys (stage2_model.decoder, stage2_model.particle_type.target) resolved differently depending on whether a config dict came from merge_cli_overrides (fully populated, correct) or was hand-built and partial (fell back to stale v0.2-shaped literals). Introduce frozen dataclasses (GiantConfig and its nested blocks) in giant/config.py as the actual single declaration of every default; DEFAULT_CONFIG is now generated from them instead of hand-maintained, and build_models, build_critics, and StageSpec.from_config consume the dataclasses instead of duplicating literal fallbacks, so this class of drift can't recur. Router/n_sec sub-blocks keep an `extra` catch-all for their genuinely dynamic keys (composed-router axes, runtime-seeded centers_init, legacy_owner). Fixing the fallback surfaced the same latent bug in two existing partial-config callers that had been silently depending on it: a test fixture in test_train.py and scripts/warm_setup_cache.py's minimal cfg (now merged against DEFAULT_CONFIG instead of hand-rolled, closing the gap for good). See issues.md Issue 1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ from giant.model.network import (
|
||||
Stage1Model,
|
||||
Stage2Autoregressive,
|
||||
Stage2OneShot,
|
||||
build_critics,
|
||||
build_models,
|
||||
cat_col_layout,
|
||||
stage2_trunk_sec_dim,
|
||||
@@ -652,3 +653,58 @@ def test_build_models_share_stages_true_shared_params_are_in_both_stage_paramete
|
||||
assert shared_ids
|
||||
assert shared_ids <= {id(p) for p in stage1.parameters()}
|
||||
assert shared_ids <= {id(p) for p in stage2.parameters()}
|
||||
|
||||
|
||||
# ── build_models/build_critics: DEFAULT_CONFIG fallback drift (issues.md #1) ─
|
||||
|
||||
|
||||
def _partial_model_config() -> dict:
|
||||
"""A hand-built model_config that omits stage2_model.decoder and
|
||||
stage2_model.particle_type — deliberately not derived from
|
||||
DEFAULT_CONFIG, unlike _minimal_model_config above. Regression fixture
|
||||
for issues.md Issue 1: build_models/build_critics/StageSpec.from_config's
|
||||
own fallback defaults for these two keys must equal DEFAULT_CONFIG's
|
||||
("autoregressive" / "onehot"), not the old, now-wrong v0.2-shaped
|
||||
("one_shot" / "physical") literals that used to live in three separate
|
||||
.get(key, default) call sites."""
|
||||
return {
|
||||
"pdg_vocab": 3,
|
||||
"mat_vocab": 2,
|
||||
"conditioning": {
|
||||
"particle": {"type": "physical", "emb_dim": 4, "n_layers": 1},
|
||||
"material": {"type": "physical", "emb_dim": 4, "n_layers": 1},
|
||||
},
|
||||
"stage1_model": {"active": False},
|
||||
"stage2_model": {
|
||||
"generator": "wgan",
|
||||
"hidden_dim": 8,
|
||||
"n_res_blocks": 1,
|
||||
"k_max": 3,
|
||||
# decoder and particle_type deliberately omitted
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_build_models_omitted_decoder_and_particle_type_match_default_config():
|
||||
built = build_models(_partial_model_config())
|
||||
assert isinstance(built["stage2"], Stage2Autoregressive)
|
||||
assert built["stage2"].particle_type_cfg["target"] == "onehot"
|
||||
|
||||
|
||||
def test_build_critics_omitted_particle_type_matches_default_config():
|
||||
cfg = _partial_model_config()
|
||||
cfg["stage2_model"]["generator"] = "wgan"
|
||||
onehot_critic = build_critics(cfg)["stage2"]
|
||||
assert onehot_critic is not None
|
||||
onehot_in_dim = onehot_critic.input_proj.in_features
|
||||
|
||||
cfg["stage2_model"]["particle_type"] = {"target": "physical"}
|
||||
physical_critic = build_critics(cfg)["stage2"]
|
||||
assert physical_critic is not None
|
||||
physical_in_dim = physical_critic.input_proj.in_features
|
||||
|
||||
# onehot's per-slot type width is emb_dim classes vs. physical's fixed
|
||||
# (log-mass, charge) pair — different unless emb_dim happens to be 2, so
|
||||
# this also confirms the critic was actually built in onehot mode by
|
||||
# default, not silently falling back to physical.
|
||||
assert onehot_in_dim != physical_in_dim
|
||||
|
||||
Reference in New Issue
Block a user