Skip to content

V2 Strength Model - #26

Merged
SebastianAment merged 2 commits into
mainfrom
v2-strength-gp
Jun 9, 2026
Merged

V2 Strength Model#26
SebastianAment merged 2 commits into
mainfrom
v2-strength-gp

Conversation

@SebastianAment

@SebastianAment SebastianAment commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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 layout

Replaces 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) and boxcrete.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 gates

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

improvement_journey
  • Multi-component ARD-Matern kernel: blind_matern + source_specific_matern + additive_rbf_time (within-group lengthscale prior on Cement/FA/Slag and Fine/Coarse Aggregate)
  • 7 engineered chemistry features: W/B, SCM frac, log(HRWR/binder), log(W/C), log(coarse/fine), log(aggregate/paste), log(maturity_robust)
  • Multiplicative time gate h(t) = 1 - exp(-t/τ) with τ=0.05 (kernel + likelihood)
  • Y/y_max scaling with ZeroMean prior (preserves the f(x, t=0) = 0 invariant under un-standardisation)
  • MLL fit; reproducible via fixed seed
  • Precision: the GP fit uses dtype=torch.float64 end-to-end. An earlier float32 leak in bounds/constraint-coefs/MVN-prior caused per-VM-host CPU rounding to push L-BFGS-B into different local optima on different runners; the explicit-dtype fix made multi-threaded BLAS empirically deterministic across runs.

Tests

  • Python (pytest): public-API smoke, parity, fidelity, monotonicity, t=0 physics, lengthscale identifiability, Loo R² regression — all green
  • JS (Node): 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.mjs
  • e2e (Playwright): lengthscale-identifiability.spec.ts plus the explorer smoke suite — all green
  • CI: 7 workflows (tests.yml, js-sync.yml, e2e.yml, notebooks.yml, lighthouse.yml, strength-parity.yml, model-artifacts-coherence.yml) — all gated paths verified locally
  • Python (pytest): public-API smoke, pretrained loader fidelity, monotonicity (committed JSON + freshly-fit), lengthscale identifiability, LOO R² regression, partial-fixed-noise likelihood, models — all green

Migration / breaking changes

  • boxcrete/models.py is deleted; users importing from it should migrate to the new submodules. The public from boxcrete import fit_strength_gp API is preserved.
  • docs/model/strength.json schema_version bumped to 2 (drops L_factor / alpha, adds Y_train); legacy v1 JS consumers will need to update.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 3, 2026
@SebastianAment
SebastianAment force-pushed the v2-strength-gp branch 26 times, most recently from 6bf72a3 to faa857e Compare June 8, 2026 22:22
Comment thread .github/workflows/tests.yml Fixed
@SebastianAment
SebastianAment force-pushed the v2-strength-gp branch 2 times, most recently from 70fbd2e to 62b675d Compare June 9, 2026 15:04
@SebastianAment
SebastianAment force-pushed the v2-strength-gp branch 6 times, most recently from f87748b to 8d02f44 Compare June 9, 2026 18:19
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
SebastianAment merged commit 73cff6b into main Jun 9, 2026
19 checks passed
@SebastianAment
SebastianAment deleted the v2-strength-gp branch August 14, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants