Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,4 @@ B17sc|2026-09-28|`scale="log"` on `PreprocessingSpec.overrides` reached the surr
B18mt|2026-09-29|T25dp's "correlation" port landed only table-mode `api.compute_correlations`, leaving the #470 MC→surrogate→correlate endpoint workflow (sample `distributions` ∧ predict ∧ correlate over the SHARED set) inside the flaskapi blueprint — surfaced while re-landing the mmux_vite consumption migration: either compute stays in flaskapi (⊥ §G) or the endpoint silently degrades to table correlation (the superseded branch #537 chose the latter, ignoring `distributions`/`num_samples`/`seed` unnoticed)|`api.evaluate_correlations` + `SumoSession.correlations` (log guards via existing `_uq_engine_distributions`) + engine `correlate_manual_uq_samples` w/ REQUIRED `input_scales`/`output_scale`; flip matrix extended over ∀ 11 entry points|V45ls,V22rs,T47mt
B19ps|2026-09-29|PR#47 review found shipped deliberate semantics wrong: `resolve_log_scale` + `_uq_engine_distributions` rejected log⊗normal ("source parity: log-scale sampling is uniform-only; no invented log-normal", from fc93cbe, codified in V&V I4 + 3 tests) — but the log knob must compose with a normal: μ/σ parameterizing ln-space (raw draw = lognormal) is the same "distribution describes what the model sees" contract the log-uniform branch already honors; 4 weak guards surfaced in the same review: `input_scales.get(var, "linear")` per-variable silent default behind a "REQUIRED, never defaulted" docstring; log-uniform upper bound unchecked at the boundary (missing/inverted `maximum` → `SumoEngineError` via `_run_engine`); `compute_correlations`/`generate_lhs_samples`/`generate_grid_samples` silently ignore `preprocessing` overrides for unknown columns while the session path rejects them; MOGA docstring claimed the front is reported in log space vs the exp-restored reality|new §V invariants V46rn (log⊗distribution composition) ∧ V47st (boundary input guards); tasks T48kw/T49dr/T50vb|V46rn,V47st,T48kw,T49dr,T50vb
B20qt|2026-09-30|the v1 port shipped mmux_vite's closed-form 2nd-order identity as a "standard approximation for d>3" (V9gh wording) — mmux_vite SPEC §B B26nc proved it UNDERDETERMINED for d≥4 (every pair inferred from only d first/total gaps; pair sums collapse to 0 at d=4 ∧ go negative beyond — typical 4–25-parameter studies produced scientifically invalid interactions), ∧ its raw-product form was not translation-invariant under `f→f+c` (B28pp: drift ∝ c when mean≫std, e.g. stress in Pa) ∧ differed from the scipy point estimator displayed alongside it|port mmux_vite T31rb (PR#649): exact joint-pair estimator (independent C stream, `S_ij=Var(E[Y|X_i,X_j])/V−S_i−S_j`, EXACT any d), scipy-`saltelli_2010`-pinned centered-product algebra, shared-row bootstrap, order masses w/ zero-variance null; new §V invariant V48tr ∧ task T51pq; old file's inlined-duplicate Ishigami math replaced by production-helper benchmarks|V48tr,V9gh,T51pq
B21ru|2026-10-01|`SumoSession.grid` inverse-mapped the response key but not its COMPANION series: the engine hangs the predicted-std column off the MAPPED response name (`y1_std`), which passed through `get_inverse_mapping()` unknown-key-passthrough → `GridResult.data` carried `y1_std` for an underscored QoI (mmux's V47pk route test caught it: their FE looks `gridData[qoi + "_std"]` up, a mapped-name leak camelCases on the wire ∧ the lookup misses → silent plot gaps)|grid loop renames `{mapped_response}_*` companions to `{response}_*` (suffix kept) beside the plain response rename; regression test renames the fixture QoI to `max_abs_stress` ∧ asserts no `y1*` key ∧ std companions ⊆ {`max_abs_stress_std`}|V21pf
2 changes: 1 addition & 1 deletion docs/verification-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Live results as of the last run of the ported V&V suite:

| Suite | Tests | Result |
|---|---|---|
| Full standalone suite (`uv run pytest`) | 390 | **all passing** |
| Full standalone suite (`uv run pytest`) | 391 | **all passing** |
| Analytical/integration tier (`-m analytical`, real Dakota subprocess, no mocking) | 26 | **all passing** |
| Sobol' / Ishigami acceptance gate (`test_sobol_indices.py`) | 14 | **all passing** |

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "uv_build"

[project]
name = "itis-sumo"
version = "0.1.0a9"
version = "0.1.0a10"
description = "Surrogate Modeling functionality for IT'IS Foundation / ZMT Modeling Intelligence suite: build, evaluate, cross-validate surrogates + UQ + sampling, headless or embedded"
readme = "README.md"
# T16mo rung 1 (1.5.9->1.5.11): 1.5.11 ships cp313 wheels, so the <3.13
Expand Down
13 changes: 11 additions & 2 deletions src/itis_sumo/api/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,21 @@ def grid(
original_names = self._preprocessor.get_inverse_mapping()
converted: dict[str, list[float] | list[list[float]]] = {}
for mapped_name, values in results.items():
original_name = original_names.get(mapped_name, mapped_name)
if mapped_name == self._mapped_response:
converted[original_name] = self._inverse_nested_values(
converted[self._response] = self._inverse_nested_values(
mapped_name, values
)
elif mapped_name.startswith(f"{self._mapped_response}_"):
# Companion series (the predicted-std column) hang off the
# MAPPED response name (y1_std); V21pf requires the ORIGINAL
# name with the suffix kept -- a mapped-name leak gets
# camelCased by consumers and the frontend cannot look it up.
suffix = mapped_name[len(self._mapped_response) :]
converted[f"{self._response}{suffix}"] = self._inverse_nested_values(
mapped_name, values
)
else:
original_name = original_names.get(mapped_name, mapped_name)
converted[original_name] = self._inverse_nested_values(
mapped_name, values
)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_api_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ def test_grid_preserves_original_names_and_units(self, samples):
assert len(result.data["stress"]) == 5
assert len(cast("dict[str, list[list[float]]]", result.data)["stress"][0]) == 5

def test_companion_series_keeps_the_original_response_name(self, samples):
"""V21pf regression: the engine hangs the std series off the MAPPED
response name (y1_std). It must come back under the ORIGINAL response
name with the suffix kept (max_abs_stress_std); a mapped-name leak is
camelCased consumer-side and becomes an FE lookup miss."""
renamed = samples.rename(columns={RESPONSE: "max_abs_stress"})
result = evaluate_grid(
renamed,
VARIABLES,
"max_abs_stress",
grid_variables=["width", "height"],
points_per_variable=5,
)
assert "max_abs_stress" in result.data
std_keys = {key for key in result.data if key.endswith("_std")}
assert std_keys <= {"max_abs_stress_std"}
assert not any(key.startswith("y1") for key in result.data)


class TestSobol:
"""V26dd: the sampling box is the exploration DOMAIN, not modeller
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading