Skip to content

run.log prints applied and unapplied projection parameters side by side #432

Description

@jcoludar

Summary

run.log is the reproducibility artifact — it is what someone writing a Methods section reads. For any run using an inline -m override it currently reports both the parameter that was applied and the default that was not, with nothing distinguishing them.

Reproduce

protspace prepare -q 'family:"beta-lactamase"' \
  -m 'pca2,umap2:n_neighbors=200;min_dist=0.4' \
  -a all --stats --cluster-selection both -o out/

out/run.log:

## Projection
methods: pca2, umap2:min_dist=0.4;n_neighbors=200     <- applied
similarity: False
metric: euclidean
random_state: 42
n_neighbors: 25                                        <- NOT applied
min_dist: 0.1                                          <- NOT applied

The bundle disagrees with its own log. projections_metadata.info_json records n_neighbors: 200, min_dist: 0.4 — the standalone lines are the globals that lost.

A reader reconstructing the run from n_neighbors: 25 gets a different projection.

Root cause

apps/protspace/src/protspace/cli/prepare.py (~702-706):

lines += ["", "## Projection"]
lines.append(f"methods: {', '.join(str(m) for m in pipeline_config.methods)}")
lines.append(f"similarity: {similarity}")
for key, val in rp.items():          # rp = the GLOBAL ReducerParams
    lines.append(f"{key}: {val}")

Inline overrides are parsed into MethodSpec.overrides and merged at pipeline.py as effective_params = {**global_params, **spec.overrides_dict}, then applied via _run_with_overridden_config — which restores the previous config in a finally. So the override provably cannot survive into the object _write_run_log serializes (asdict(pipeline_config.reducer_params)). Confirmed: cfg.reducer_params.n_neighbors == 25 after an override run.

Suggested fix

The resolved values already exist. <Reducer>.get_params() (e.g. UMAPReducer.get_params) feeds reduction["info"], which is serialized to info_json — the same object the bundle records. Printing those, per projection, is both correct and strictly more informative, because each reducer reports only the parameters it actually consumed (a UMAP run would stop printing perplexity/learning_rate, which the current log lists misleadingly).

## Projection
methods: pca2, umap2:min_dist=0.4;n_neighbors=200
similarity: False

### Applied (per projection)
ProtT5 — PCA 2:   n_components=2, svd_solver=arpack, random_state=42
ProtT5 — UMAP 2:  n_components=2, n_neighbors=200, min_dist=0.4, metric=euclidean, random_state=42

### Global defaults (overridden per projection where shown above)
n_neighbors: 25
min_dist: 0.1
...

Roughly: expose self.reductions on ReductionPipeline, keep the instance in prepare.py, pass it to _write_run_log. Two caveats found while checking: under --stats, route_faithfulness_to_metadata injects quality into red["info"] (filter it), and red["ids"] is also set under --stats (read only name/info). Cached projections are safe — the cache key includes effective_params, so a hit implies identical parameters.

Failing that, labelling the block defaults (may be overridden per method) would at least stop it asserting something false.

Two related ambiguities found while tracing this

1. n_neighbors — the effective value is 25. DimensionReductionConfig defaults to 15 and ReducerParams to 25, which has caused confusion. The CLI passes n_neighbors as an explicit keyword all the way to DimensionReductionConfig(n_components=dims, **filtered_config), so the dataclass default is never consulted. 25 is what reaches the reducer from any CLI run; the 15 is only live when DimensionReductionConfig is constructed directly without the kwarg. Documenting this would settle it.

2. eps has three defaults, and the Python API silently differs from the CLI. ReducerParams.eps = 1e-6, DimensionReductionConfig.eps = 1e-3, CLI eps = 1e-3. A CLI run gets 1e-3; a Python-API caller using PipelineConfig(...) with the default_factory=ReducerParams gets 1e-6 — a 1000× difference in MDS convergence tolerance, with no warning. Aligning ReducerParams.eps to 1e-3 breaks no test (none asserts either default).

Context

Found while preparing datasets for a publication, where the projection parameters go into a Methods section verbatim. Related: PRs #430 and #431 fix two other cases of the same underlying shape — an operation that did not happen reporting as though it did.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions