V2 Strength Model - #26
Merged
Merged
Conversation
SebastianAment
force-pushed
the
v2-strength-gp
branch
26 times, most recently
from
June 8, 2026 22:22
6bf72a3 to
faa857e
Compare
SebastianAment
force-pushed
the
v2-strength-gp
branch
2 times, most recently
from
June 9, 2026 15:04
70fbd2e to
62b675d
Compare
SebastianAment
force-pushed
the
v2-strength-gp
branch
6 times, most recently
from
June 9, 2026 18:19
f87748b to
8d02f44
Compare
Introduces the V2 strength GP — a multiplicatively-gated kernel
architecture that structurally enforces the physics constraint
f(x, t=0) = 0 with zero posterior variance — plus the supporting
public API and the saved model artifact.
Module layout (each module focused on one concept):
* boxcrete/concrete_model.py — SustainableConcreteModel (the
multi-output joint GWP/slump/strength model) and AppendDerivedFeatures.
* boxcrete/strength_model.py — public fit/load API for V2:
fit_strength_gp() and load_pretrained_strength_gp(). Hosts the
V2 fit factory and ChainedInputTransform composition.
* boxcrete/kernels.py — kernel primitives: TimeGatedKernel,
within_group_prior, ard_matern_with_within_group_prior,
additive_time_kernel, build_strength_kernel_for_aug_dim,
make_gated_strength_kernel_builder.
* boxcrete/features.py — features and transforms:
F5_ALLLOG_FEATURES, GATE_TAU, IDX, FEATURE_BUILDERS,
append_engineered_features_callable, augmented_bounds, max_scale_Y.
* boxcrete/likelihoods.py — GatedGaussianLikelihood (heteroscedastic
Gaussian with h(t)² gating, paired with the gated kernel) and
PartialFixedNoiseLikelihood.
* boxcrete/priors.py — WithinGroupShrinkagePrior + the binder /
aggregate group definitions used for ARD lengthscale shrinkage.
* boxcrete/slump_model.py — fit_slump_gp() for the slump model.
* boxcrete/strength_model_legacy.py — V1 helpers retained for
backward compatibility (get_strength_gp_input_transform).
Replaces the legacy boxcrete/models.py (758 lines) with the focused
modules above.
Persisted artifact:
* docs/model/strength_model.pt — PyTorch state_dict for the deployed
V2 strength GP. load_pretrained_strength_gp() reconstructs the
full SingleTaskGP from this without re-fitting.
Float64 throughout the fit (precision fix): explicit dtype=torch.float64
in boxcrete/utils.py (bounds, constraint coefficients), strength_model.py
(time-transform parameters), and priors.py (MVN loc/scale). Without
this, ~7-decimal float32 precision was sensitive enough to per-runner
CPU rounding that L-BFGS-B occasionally landed in different local
optima — including a wb_ratio-railed basin.
Tests:
* test/test_lengthscale_identifiability.py — guards lengthscales
remain identifiable (no rail-at-cap pathologies); also asserts
fresh-fit predictions agree with committed test_vectors.json
within a cross-architecture-portable tolerance.
* test/test_strength_curve_monotonicity.py — physical-constraint
regression for monotonic strength evolution (committed JSON
+ freshly-fit V2).
* test/test_pretrained_loader_fidelity.py — guards the state_dict
round-trip reproduces the trained model's predictions exactly.
* test/test_public_api.py — public API surface guard.
Hooks the V2 strength GP up to the in-browser BOxCrete explorer and
wires up the CI gates that enforce coherence between the deployed
boxcrete model and the published model artifacts.
JS-side V2 implementation (docs/explorer):
* docs/gp_v2_fast.mjs — pure-JS implementation of the V2 gated-kernel
posterior, optimised for the in-browser explorer.
* docs/feature_registry.mjs — JS port of the F5_alllog feature
builders to keep the JS path byte-identical to the Python path.
* docs/gp.mjs / docs/ui.mjs / docs/units.mjs — explorer integration.
* docs/generate_mix_analyses.py — produces per-mix analysis pages
consumed by the explorer.
* docs/model/README.md — documents the docs/model/ artifact layout.
* docs/model/strength.json + compositions.json + test_vectors.json —
refreshed model artifacts produced by the V2 fit.
CI gates:
* .github/workflows/strength-parity.yml — runs
test_pretrained_loader_fidelity.py to guard that
load_pretrained_strength_gp() faithfully reconstructs the deployed
V2 strength GP from docs/model/strength_model.pt.
* .github/workflows/model-artifacts-coherence.yml — guards that the
published docs/model/ artifacts match the boxcrete fit output
(catches stale artifacts after model code changes). Uses a
cross-architecture-portable numerical drift check
(experiments/check_artifacts_drift.py) instead of byte-level
git diff.
* .github/workflows/notebooks.yml + tests.yml — Python version
bumps to match pyproject.toml's requires-python = ">=3.11" and
BOXCRETE_SMOKE_TEST=1 env wiring for the notebook matrix.
Regen pipeline (invoked by the artifacts-coherence gate):
* experiments/regenerate_all_artifacts.sh — orchestrator.
* experiments/regenerate_strength_json.py — Python regen of
strength.json + test_vectors.json from a fresh V2 fit.
* experiments/augment_test_vectors_with_gwp_cost.mjs — adds GWP /
cost columns via the JS predictors.
* experiments/regenerate_compositions_strength_predictions.mjs —
regen of the static Pareto scatter dots.
CI helpers:
* experiments/check_artifacts_drift.py — numerical drift check.
* experiments/run_notebook_with_progress.py — cell-by-cell timing
helper used as a fast-fail step before nbconvert.
* experiments/measure_fit_stability.py — empirical run-to-run fit
determinism measurement (used during precision debugging).
JS-side regression tests:
* test/test_js_strength_v2.mjs — V2 posterior parity (Python vs JS).
* test/test_js_physical_constraints.mjs — JS-side f(x, t=0) = 0 guard.
* test/test_lengthscales_v2.mjs — JS-side lengthscale parity.
* test/test_curve_monotonicity.mjs — strength-curve monotonicity.
* test/test_data_freshness.mjs — guards that test_vectors.json is
derived from the same data the deployed model was fit on.
* test/test_js_ui_smoke.mjs — explorer UI smoke test.
SebastianAment
force-pushed
the
v2-strength-gp
branch
from
June 9, 2026 18:36
8d02f44 to
8ec0f9f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR open-sources the V2 strength GP architecture — the deployed model behind the explorer's strength predictions and recommended-mix Pareto frontier. The V2 architecture replaces the V1 single-Matern + day-zero anchor pseudo-observations with a multi-Matern + multiplicative time-gate that structurally enforces
f(x, t=0) = 0(with zero variance), plus a heteroscedastic gated noise likelihood and 7 engineered F5_alllog chemistry features. End-to-end the change is a ~300 psi block-LOO RMSE improvement on the 647-row public dataset.The PR is organised as 2 logical commits (each is independently reviewable + buildable):
Commit 1 —
boxcrete: V2 strength GP — final-form module layoutReplaces the legacy
boxcrete/models.py(~800 lines) with an 8-module split where each module hosts one concept (kernels, features, likelihoods, priors, slump, strength). Public API:boxcrete.fit_strength_gp(V2 production fit) andboxcrete.load_pretrained_strength_gp(state-dict round-trip from the shipped.pt). Includes a test_pretrained_loader_fidelity.py regression gate (run by the Strength GP Parity workflow) that asserts the state-dict round-trip from docs/model/strength_model.pt reproduces the deployed model's predictions exactly. The previous research-vs-production parity check (test_strength_model_parity.py) and its required experiments/model_variant_study.py companion are deferred to a separate "research catalog" follow-up PR.Commit 2 —
explorer + ci: V2 strength GP web hookup + artifact / parity gatesJS port (
docs/gp.mjs,docs/gp_v2_fast.mjs) of the V2 inference path; the schema rewrite that brought the strength.json artifact from 5.6 MB → 177 KB by shipping kernel ingredients (X_train,Y_train) instead of the precomputed Cholesky factor; CI gates (strength-parity.yml,model-artifacts-coherence.yml) that catch any future Python↔JS or Python↔shipped-artifact drift.Architecture (deployed)
Variant identifier:
B''+F5_alllog+gated_t+gated_noise+maxscale_zeromeanblind_matern + source_specific_matern + additive_rbf_time(within-group lengthscale prior on Cement/FA/Slag and Fine/Coarse Aggregate)h(t) = 1 - exp(-t/τ)with τ=0.05 (kernel + likelihood)Y/y_maxscaling with ZeroMean prior (preserves the f(x, t=0) = 0 invariant under un-standardisation)Tests
test_js_gp.mjs(Python-JS equivalence, 296 assertions),test_js_predictor_parity.mjs(single-vs-batch contract, 8 assertions),test_js_strength_v2.mjs,test_lengthscales_v2.mjs,test_data_freshness.mjs,test_curve_monotonicity.mjs,test_js_physical_constraints.mjs,test_js_ui_smoke.mjslengthscale-identifiability.spec.tsplus the explorer smoke suite — all greentests.yml,js-sync.yml,e2e.yml,notebooks.yml,lighthouse.yml,strength-parity.yml,model-artifacts-coherence.yml) — all gated paths verified locallyMigration / breaking changes
boxcrete/models.pyis deleted; users importing from it should migrate to the new submodules. The publicfrom boxcrete import fit_strength_gpAPI is preserved.docs/model/strength.jsonschema_version bumped to 2 (dropsL_factor/alpha, addsY_train); legacy v1 JS consumers will need to update.