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
9 changes: 5 additions & 4 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Caveman-encoded (drop articles/filler; `→` becomes, `!` must, `?` may/uncertain, `⊥` never). Standalone surrogate-modeling library aggregating prior MetaModeling trials.

## LINKS
- engine ! PyPI wheel `itis-dakota==1.5.11` (Dakota 6.20, T16mo rung 1 ✓, parity w/ mmux/vite). In-repo `itis-dakota/` = fork checkout, **REFERENCE-ONLY**: pkg consumes pre-built wheels, ⊥ builds fork (6.24 checkout kept for T16mo ladder)
- engine ! PyPI wheel `itis-dakota==6.24.7` (Dakota 6.24, T16mo rung 2 ✓ — latest 6.24.x still shipping cp311 wheels, so Python 3.11 parity w/ mmux/vite is retained; 6.24.8+ dropped cp311). In-repo `itis-dakota/` = fork checkout, **REFERENCE-ONLY**: pkg consumes pre-built wheels, ⊥ builds fork (6.24 checkout kept for T16mo ladder). The 6.23+ `interface_cache` regression (R5) is present upstream in 6.24.7 and is shimmed in itis-sumo (config/funs_create_dakota_conf.py::add_r5_interface_cache_workaround, `truth_model_pointer='R5_WA_MODEL'`), not waiting on the upstream get-or-create fix
- feeding repos (per-branch/commit inventory + more-to-port → `PORTING.md`):
- `../mmux_vite` (GiulianaRenc/mmux_vite): v1 port source = branch `jgo/sobol-indices`@`22685f7` → `flaskapi/src/mmux_flaskapi/{dakota,data_preprocessor}/`; E1 inspiration = branch `jgo/sumo-model-export-import`@`d5150b9` → re-implemented in-repo on `feat/sumo-model-export-import` (T12ze-T14qa ✓)
- `../itis_dakota_projects` (ITISFoundation/itis-a-dakota-project) + `../itis_dakota_projects_clean` (ITISFoundation/itis_dakota_projects): recycled `validate_dakota_installation`/`get_dakota_version` + `validate` CLI
Expand Down Expand Up @@ -32,7 +32,7 @@ Consumer contract (grill 2026-08-18): itis-sumo owns ALL surrogate machinery end
- v1 config ! NIDR string composers (proven); JSON/pydantic seam later (DAKOTA-STUBS POC, Dakota 6.24 experimental)
- port ! near-verbatim from mmux_flaskapi `dakota/`+`data_preprocessor/`; re-root `mmux_flaskapi.`→`itis_sumo.`; prune known-dead (`process_json_file`, `add_interface_s4l`, `create_function_sampling_conffile`, `create_moga_iterative_optimization_conffile`); de-web `JobVariableSelection`/`required_completed_jobs` (moved from blueprints into pkg) — SUPERSEDED in part by the tabular-only boundary below: `required_completed_jobs`' Dakota rule stays, the job-shaped models go back to the consumer (T24cm)
- recycle itis_dakota_projects: `validate_dakota_installation()`, `get_dakota_version()`, `validate` CLI, config sanity-guard; ⊥ subprocess `DakotaProject`/factory/generator/parser/activity-logger
- deps ! itis-dakota==1.5.11 (Dakota 6.20, T16mo rung 1 ✓ — packaging-only bump, same engine as mmux/vite parity; stay until T16mo rung 2 resolves the 6.23+ `interface_cache` regression — R5), numpy, pandas, scipy; scikit-learn ⊥ KFold only
- deps ! itis-dakota==6.24.7 (Dakota 6.24, T16mo rung 2 ✓ — engine modernized 6.20→6.24; the 6.23+ `interface_cache` regression R5 is present upstream but shimmed in itis-sumo, see R5), numpy, pandas, scipy; scikit-learn ⊥ KFold only
- py.typed; `src/` layout; docs MkDocs + headless notebook; tests standalone ⊥ flask/osparc
- versioning single-source in `[project].version`; uv/uv_build + pip-compile pins; Conventional Commits
- release channel policy: feature branches → `.devN` (manual alpha/dev prereleases); `develop` → `aN`; `main` → `bN` until `1.0.0` graduation; version bump ! enforced by a PR-time CI check (target-branch suffix, PEP 440 order, ⊥ reuse/regression); ⊥ bot commit on `develop`/`main` themselves (both branch-protected, no bypass granted) — a bot commit on a PR's OWN head branch, pre-merge, is unaffected by that constraint and is how the `aN` counter gets auto-bumped (T43bv); merge-time job ! push `v<version>` tag only (tags fall outside 'require PR' branch protection); `develop` may later rename `staging`
Expand All @@ -41,7 +41,7 @@ Consumer contract (grill 2026-08-18): itis-sumo owns ALL surrogate machinery end
- TestPyPI publish token ! local `.env` (gitignored) `TESTPYPI_TOKEN`; Make injects `-t TESTPYPI_TOKEN`; ⊥ committed, ⊥ required as a pre-exported shell var
- feature-branch TestPyPI local path ! `make publish-testpypi-dev` computes/writes next `.devN`, builds/checks/uploads via local `.env` token; ⊥ commit/tag/CI publication
- E1: artifacts keyed server `sumo_model_id` (uuid); metadata sidecar `{id}.metadata.json`; ⊥ user-supplied path/prefix keys (traversal)
- py ! 3.11 primary (match mmux/vite); 3.10-3.13 all supported since T16mo rung 1 (1.5.11 ships cp313); rung 2 (6.24) still pending R5's upstream fix
- py ! 3.11 primary (match mmux/vite); 3.10-3.13 all supported; rung 2 took 6.24.7 (latest 6.24.x still shipping cp311 wheels) to retain 3.11 parity — 6.24.8+ dropped cp311 and would force a Python 3.12 migration (deferred; see T16mo)
- cross-repo call surface ! narrow, versioned, "deep" entrypoints — one stitched public function per feature (e.g. `analyze_dataset()`), ⊥ flaskapi orchestrating several itis-sumo internals itself; version-pinning itis-sumo is fine (same pattern as the itis-dakota engine pin, T16mo) — the coupling risk is a *wide* call surface, not the pin
- data in ! plain tabular (`pd.DataFrame` or an itis-sumo dataclass trivially convertible to one); ⊥ `FunctionJob`/oSPARC job concepts anywhere in itis_sumo. Ownership split: consumer converts jobs→table AND filters un-completed samples (warning); itis-sumo rejects insufficient/invalid data (raises; consumer catches + surfaces). Dakota sufficiency rule `max(5, n_variables + 1)` stays in itis-sumo
- preprocessing ! auto-defaulted (novice/normal users pass nothing); advanced override allowed but expressed in DOMAIN vocabulary only (`scale`, `direction`); ⊥ transform vocabulary in any public signature (`sign_switch`, `normalization="zscore"`, `mapped_name`, `_hat`/`_std_hat`). Effective config → INSPECTABLE in results, ⊥ SETTABLE in transform terms. MOGA `direction` defaults `"minimize"` (optimization-field convention)
Expand Down Expand Up @@ -135,7 +135,7 @@ T12ze|✓|E1: `core/sumo_model_store.py` (models dir single env-overridable sour
T13uv|✓|E1: capture verbatim conf block for sidecar (close branch placeholder gap)|V10
T14qa|✓|E1: empirical export→import round-trip test (real, unmocked Dakota run — `tests/test_sumo_model_store.py`); resolves R2 `?`|R2,V11
T15mn|~|wire mmux/vite flaskapi to `itis_sumo.api` (export/import + all workflows): branch `feat/consume-itis-sumo-api` (worktree, pushed to `origin`, no PR yet) already replaces flaskapi's in-tree `dakota/`+`data_preprocessor/` w/ `itis_sumo.api` across all 7 workflows + drops the direct `itis-dakota` dep from flaskapi; pinned to itis-sumo `0.1.0.dev2` (TestPyPI); flaskapi suite verified 522 passed/3 skipped 2026-08-19 — remaining: open the PR, retarget the pin at a tagged `aN`/`bN` release instead of a `.devN` build (see parity note added to `../mmux_vite/flaskapi/SPEC.md` §C)|§I,R1-R4,T40qz
T16mo|~|stepwise engine modernization: rung 1 `1.5.9→1.5.11` ✓ (packaging-only bump, same 6.20 engine, behavior-identical — forced early by B14zt: 1.5.9 vanished from PyPI; 318-test local suite green, cp313 confirmed installable); rung 2 `1.5.11→6.24.x` co-shipped w/ JSON input seam (R5 regression fixed upstream first, re-align py 3.13, re-run full smoke) still pending|R4,R5
T16mo|✓|stepwise engine modernization: rung 1 `1.5.9→1.5.11` ✓ (Dakota 6.20); rung 2 `1.5.11→6.24.7` ✓ (Dakota 6.24 engine bump). Took 6.24.7 — the latest 6.24.x still shipping cp311 wheels — to keep Python 3.11 parity with mmux/vite; 6.24.8+ dropped cp311 (would force a 3.12 migration, deferred). The 6.23+ `interface_cache` regression (R5) was NOT fixed upstream in 6.24.7, so itis-sumo shims it via `add_r5_interface_cache_workaround()` + `truth_model_pointer='R5_WA_MODEL'` in config/funs_create_dakota_conf.py: a no-op `fork` interface (`analysis_drivers='true'`) on an otherwise-unused `single` model, so the `study()` ctor's cache lookup succeeds; the interface is never executed. JSON input seam (R4) deferred per scope. 318-test suite green|R4,R5
T17bq|✓|`add_surrogate_model` (`config/funs_create_dakota_conf.py`) ! replaced filename-substring sniffing w/ explicit `has_eval_id_column` param (infer-or-override idiom); all 5 `create_sumo_*_conffile` call sites updated (landed via `feat/vv-port-tests-and-docs`, `a6d694e`, ahead of this merge)|V15,B2
T17bc|✓|E1: persist real training data on export for reference (`{id}.processed_training.dat` sidecar copy, user preference over the leaner archive-only design); `stage_model_for_import` stages it back, falling back to a synthesized header-only placeholder + loud warning log only when that stored copy is missing (legacy model / deleted out-of-band) — fallback verified safe vs Dakota source + fake-points empirical tests (header reorder, wrong/missing descriptors, 0/1/2-row placeholders)|R8,R9,V10,V11
T18ry|.|design+implement `analyze_dataset(df, input_cols, output_cols, alpha=0.05, include_detail=False) -> DatasetDiagnostics` narrow entrypoint (scale/distribution auto-selection + outlier surfacing, plain dataclasses, JSON-serializable via `dataclasses.asdict()`) as the sole flaskapi-facing dataset-diagnostics API, replacing any per-function flaskapi orchestration; BLOCKED — building blocks `select_variable_scale`/`auto_select_distributions` (+ a new raw-value outlier detector, reusing `_tukey_outlier_mask`'s IQR technique) currently exist only on confidential incubator branch `feat/nih-in-silico-example`, not `develop` — needs individual promotion first, same promotion rule as T15mn/E1|§C,V16qf,I
Expand Down Expand Up @@ -187,3 +187,4 @@ B12mn|2026-08-19|`pyproject.toml` `requires-python` had drifted to `>=3.10,<3.14
B13qf|2026-08-19|3 latent Python-3.10 incompatibilities (`datetime.UTC`, `typing.Self`, bare `import tomllib` — all stdlib/typing 3.11+ additions) shipped undetected because the pre-`ci/matrix-and-coverage` CI ran a single Python version; surfaced only once the 3.10-3.13 test matrix actually ran each version for the first time|`datetime.timezone.utc` / `sys.version_info`-gated `typing_extensions.Self` fallback / try-`tomllib`-except-`tomli` fallback + conditional deps; preventive: V41hp — `ty check` resolves these against `requires-python`'s floor in one static run, cheaper than a runtime matrix for this bug class
B14zt|2026-08-27|`itis-dakota==1.5.9` was deleted from PyPI upstream (release list jumps `1.5.1`→`1.5.11`→`6.23.0`+, no `1.5.9`; that version's PyPI JSON endpoint 404s) — invisible because `uv.lock` still installs from its cached wheel URLs/hashes, but T43bv's alpha-bump commit changes `pyproject.toml`'s `[project]` table on every qualifying PR, which invalidates the lock and forces a live re-resolve; that re-resolve then fails outright since the pinned version no longer exists in the index (reproduced locally: identical repo, only `version` differs, `uv@latest sync` succeeds unchanged / fails re-resolved)|bumped engine pin to `itis-dakota==1.5.11` (T16mo rung 1, already earmarked in a pyproject.toml comment — packaging-only, same Dakota 6.20 engine, adds cp313 wheels); widened `requires-python` to `<3.14`; regenerated `uv.lock`; 318-test local suite + `ty`/`ruff` green, cp313 install confirmed; V43zt
B15tg|2026-08-27|the `v0.1.0a2` tag (created by auto-tag.yml on the develop merge) exists, but no TestPyPI publish followed — auto-tag's trailing `gh workflow run publish.yml` did not fire. Root cause: the `tag-merged-version` step invoked `gh` from an inline Python script that read `GITHUB_TOKEN` out of the step environment, but the step's `env:` block only exported `GH_REF_NAME`, so `gh` got no usable token and the dispatch silently did nothing|add `GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` to the `tag-merged-version` step's `env:` block so the inline `gh` call inherits a valid token; one-off `gh workflow run publish.yml --ref develop --field tag=v0.1.0a2 --field registry=testpypi` published a2 manually to recover the missed alpha; V29yn
B16mo|2026-08-28|T16mo rung 2 bumped the engine `itis-dakota==1.5.11` (Dakota 6.20) → `==6.24.7` (Dakota 6.24). 6.24.7 still carries the 6.23+ `Interface::interface_cache()` regression (R5): `dakota.environment.study()`'s ctor unconditionally calls `interface_cache(problem_db)` and throws `IndexError: map::at` ("called with nonexistent study!") for itis-sumo's pure data-fit surrogate confs, which construct no Interface (only `import_build_points_file`, no `interface` block) — the cache static map is never populated. All 45 real-Dakota surrogate tests failed on it (training, cross-validation, UQ propagation, MOGA, import/export, evaluation). Upstream fix (get-or-create `operator[]`) is still pending|shim in itis-sumo, not waiting on upstream: `config/funs_create_dakota_conf.py::add_r5_interface_cache_workaround()` (appended to every conf via `start_dakota_file()`) declares an unused `single` model `R5_WA_MODEL` wired to a no-op `fork` interface (`analysis_drivers='true'`) and points each surrogate `model` at it via `truth_model_pointer='R5_WA_MODEL'`, forcing the Interface to be constructed (populating the cache) during study setup; the dummy model is never referenced by any method/iterator so the interface is never executed. 318-test suite green on 6.24.7. Took 6.24.7 (not 6.24.9) because 6.24.8+ dropped cp311 wheels, which would have forced a Python 3.12 migration of both repos; R5
17 changes: 11 additions & 6 deletions 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.0a3"
version = "0.1.0a4"
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 All @@ -23,11 +23,16 @@ classifiers = [
"Topic :: Scientific/Engineering",
]
dependencies = [
# T16mo rung 1: itis-dakota==1.5.11 (still Dakota 6.20, packaging-only
# bump -- behavior-identical to 1.5.9). Stay on 6.20 until the 6.23+
# `Interface::interface_cache()` regression is resolved (SPEC §T T16mo
# rung 2). 1.5.9 itself was removed from PyPI (SPEC §B).
"itis-dakota==1.5.11",
# T16mo rung 2: itis-dakota==6.24.7 (Dakota 6.24). The 1.5.x line wrapped
# Dakota 6.20; the package renumbered to track Dakota's version. Bumps the
# engine a full minor (6.20 -> 6.24). 6.24.7 is the latest 6.24.x that still
# ships cp311 wheels, so itis-sumo keeps its Python 3.11 primary (parity
# with mmux/vite); 6.24.8+ dropped cp311. The 6.23+ `interface_cache` regression
# (SPEC §R5) on pure data-fit surrogate confs is shimmed in itis-sumo via
# `add_r5_interface_cache_workaround()` in config/funs_create_dakota_conf.py
# (no-op fork interface so the ctor's cache lookup succeeds), rather than
# waiting on the upstream get-or-create fix.
"itis-dakota==6.24.7",
"numpy>=2.0,<3.0",
"pandas>=2.2,<3.0",
"pydantic>=2.0,<3.0",
Expand Down
29 changes: 28 additions & 1 deletion src/itis_sumo/config/funs_create_dakota_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@
_logger = logging.getLogger(__name__)


def add_r5_interface_cache_workaround() -> str:
"""T16mo rung 2 / SPEC §R5: itis-dakota 6.24's `study()` ctor unconditionally
calls `Interface::interface_cache(problem_db)` and throws `map::at`
("called with nonexistent study!") when the study constructed no Interface --
which is exactly the case for itis-sumo's pure data-fit surrogate confs
(`import_build_points_file`, no interface). The cache is only populated when an
Interface is constructed. This appends an otherwise-unused `single` model backed
by a no-op `fork` interface so the cache gets an entry and the ctor succeeds. The
dummy model is never referenced by any method/iterator, so the interface is never
executed. Upstream's own fix is a get-or-create (`operator[]`) in `interface_cache`
(pending in itis-dakota); this is the itis-sumo-side shim until then.
"""
return """
model
id_model = 'R5_WA_MODEL'
single
interface_pointer = 'R5_WA_INTERFACE'
variables_pointer = 'VARIABLES'
responses_pointer = 'RESPONSES'
interface
id_interface = 'R5_WA_INTERFACE'
fork
analysis_drivers = 'true'
"""


def start_dakota_file(
top_method_pointer: str | None = None,
results_file_name: str | None = None,
Expand All @@ -20,7 +46,7 @@ def start_dakota_file(
tabular_data
tabular_data_file = '{results_file_name}'
{f"top_method_pointer = '{top_method_pointer}'" if top_method_pointer is not None else ""}
"""
""" + add_r5_interface_cache_workaround()


def add_adaptive_sampling(
Expand Down Expand Up @@ -130,6 +156,7 @@ def add_surrogate_model(
conf = f"""
model
id_model '{id_model}'
truth_model_pointer = 'R5_WA_MODEL'
surrogate global
{surrogate_type}
"""
Expand Down
Loading