Skip to content

fix(pkg): land #39 in main — warning hijack, import time, recovery harness - #40

Merged
nceglia merged 4 commits into
mainfrom
refactor/pr6-9
Aug 3, 2026
Merged

fix(pkg): land #39 in main — warning hijack, import time, recovery harness#40
nceglia merged 4 commits into
mainfrom
refactor/pr6-9

Conversation

@nceglia

@nceglia nceglia commented Jul 30, 2026

Copy link
Copy Markdown
Owner

#39 never reached main. It merged into refactor/pr6-9 at 15:30:22, seventeen seconds after #38 merged into main at 15:30:05 — so its commits landed in the stack branch instead. This carries them the last hop.

main is currently missing:

  1. The warning-hijack fix. _preprocessing.py had a blanket warnings.filterwarnings('ignore') at module scope, so import tcri silenced every warning in the user's session — including the K-clamp, param-store-reuse, and batch_size >= n_obs guardrails that refactor: land the completed refactor in main (PRs #31–#37) #38 added. Those guardrails do not work in main as it stands.
  2. 1.75× import speedupimport tcri was 6.65 s, 85% of it in tcri.preprocessing, of which 2.9 s was an unused eager import umap. Now 3.81 s.
  3. Packaging cleanup — dead deps (gseapy, mpltern), the empty tcri/metrics/ dir, and 9 Removal Ledger rows verified + ticked.
  4. tcri/datasets/ + tests/test_recovery.py — the statistical-recovery harness (the only tests with an accuracy oracle).

All four were reviewed in #39; this is purely the merge that moves them to main. Full suite 164 passed, 3 skipped.

nceglia and others added 4 commits July 29, 2026 17:28
Packaging cleanup found while auditing pip-deploy readiness.

DEAD DEPENDENCIES removed (both forced an install for nothing):
  - gseapy: declared in pyproject.toml AND requirements.txt but imported nowhere
    in the package (only in pre-refactor worktree copies).
  - mpltern: imported at tcri/utils/_utils.py:11 and never used -- the ternary
    plot that needed it was deleted in PR7. `_utils` loads at `import tcri` time,
    so this was a hard import-time dependency for zero benefit; confirmed
    `mpltern` no longer enters sys.modules on import.

tcri/metrics/ removed. PR6 deleted its .py files but an empty directory holding
only a .DS_Store survived, so that Removal Ledger row was legitimately unticked.

REMOVAL LEDGER: ticked 9 rows after verifying each of the 20 symbols is genuinely
gone (grepped for a remaining `def` in tcri/). Those rows were stale bookkeeping
from PR6/PR7/PR9, not outstanding work -- except the metrics dir above. The
ledger now has zero real outstanding items.

Verified: `python -m build` produces sdist + wheel, `twine check` PASSES on both,
and the wheel's top level is exactly {tcri, dist-info} with the 7 expected
subpackages. Full suite 152 passed.

NOT included: the CI python-matrix fix (["3.10","3.11"] -> add "3.12", in both
tests.yml and release.yml). pyproject's classifiers already claim 3.12 support and
the dev venv is 3.12, so the package advertises a version CI never tests. The
push was rejected -- updating .github/workflows/ requires the GitHub `workflow`
OAuth scope, which this client does not have. Left for a maintainer push.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two release-blocking problems in tcri/preprocessing/_preprocessing.py, found
while profiling `import tcri` for pip-deploy readiness.

1. THE LIBRARY SILENCED EVERY WARNING IN THE USER'S SESSION.
   Module scope carried a blanket `warnings.filterwarnings('ignore')`, and this
   module is imported by `import tcri`. Verified: after `import tcri`, a plain
   `warnings.warn(...)` produced NOTHING. That means the guardrails added earlier
   in this branch -- the K clamp, the param-store-reuse notice, the
   batch_size >= n_obs warning -- could never reach a real user, along with any
   warning from numpy/pandas/scanpy or the user's own code. A library must not
   mutate global warning state it does not own. Removed; confirmed the K-clamp
   guardrail is now visible in a plain script.

2. `import tcri` TOOK 6.65 s, 85% OF IT IN THIS MODULE.
   -X importtime: tcri 5991 ms, of which tcri.preprocessing 5121 ms, of which
   umap 2955 ms (umap -> pynndescent -> numba/llvmlite). The module imported umap
   TWICE and never used it -- the ternary/UMAP code that needed it is gone, and
   the surviving opt-in path (to_anndata(compute_umap=True)) already imports umap
   locally.

   The whole import block was pre-refactor debris: numpy x5, pandas x4, torch x5,
   umap x2, plus scanpy/pyro/scvi/tqdm/scipy.entropy/softmax/Dirichlet/collections/
   datetime/Optional/REGISTRY_KEYS -- for a file that now contains exactly two
   functions using only numpy and _keys. Also dropped duplicate ANSI colour
   constants and unused _console imports.

   Measured: 6.65 s -> 3.81 s (1.75x), and umap no longer enters sys.modules.

Tests: two regression guards -- one asserts a warning is still VISIBLE after
`import tcri` (in a subprocess, since pytest resets filters and would mask this),
one asserts umap is not eagerly imported. Full suite 154 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the missing test tier. The existing 154 tests check STRUCTURE -- contracts,
identities, wiring -- which cannot catch an estimator that is well-formed but
numerically wrong. These check ACCURACY against a known truth.

tcri/datasets/simulate_tcri() implements the semi-synthetic generative story from
Supplementary Note 1 ("Generative Model for Semi-Synthetic Simulations"):
  pi ~ Dir, omega_c ~ Dir, z_i ~ Cat(pi), phi_i|z_i ~ Cat(omega[z_i]),
  x_i|phi_i ~ Poisson(U_i @ V) with U_i ~ Gamma(program of phi_i)
Because pi and omega are known, I(c;phi) is available in CLOSED FORM. Needs no
real dataset to fit (unlike the original sc_simulator), so it is importable,
seeded and fast.

Two oracles are reported, and the distinction drives honest assertions:
  true_*       population value implied by (pi, omega) -- the estimand
  empirical_*  value implied by the REALIZED counts -- what a perfect estimator
               returns on THIS sample
They differ by finite-sample noise plus the plug-in estimator's upward bias,
~(C-1)(P-1)/(2N ln2) bits. Both are given under BOTH normalizations, because tcri
defaults to normalize_mode="min" while the note's benchmark used the mean
denominator -- comparing across that difference silently inflates the estimate.

Fast tier (every commit, 10 tests):
  - oracle respects MI <= min(H_c, H_phi); both NMIs in [0,1]
  - omega_concentration monotonically controls the true MI (averaged over seeds)
  - fuzziness changes DIFFICULTY ONLY -- true MI identical to 1e-12
  - seed determinism; label noise lowers realized but not population MI
  - tl.mutual_information == an INDEPENDENT oracle implementation, raw and both
    normalizations (the strongest fast test: agreement is evidence, not tautology)
  - the two normalizations are provably not interchangeable
  - plug-in bias is upward at small N (documents why equality tests are invalid)
  - metamorphic: MI invariant to clone/phenotype relabeling and to uniform
    replication of counts

Slow tier (--runslow, 3 tests, 34 s total):
  - |empirical - true| falls with N, averaged over 6 seeds
  - a FITTED model's MI tracks the true MI across three difficulty levels
  - posterior HDI coverage

Measured while designing, not asserted blindly:
  - HDI coverage came out 8/8 for a nominal 94% interval (mean width 0.103), with
    posterior means close to truth (e.g. 0.2868 vs 0.2825). With 8 replicates that
    is evidence of no miscalibration, not proof of 94%; establishing the rate needs
    ~50+ reps and belongs in benchmarks/. The bar was tightened from >=2/5 (which
    would pass at 40% coverage) to >=6/8.
  - Single-seed convergence is FLAKY: the same seed gave gap +0.003 at N=5000 but
    +0.012 at N=20000. Convergence is therefore asserted on a mean over seeds.

conftest gains --runslow (canonical pytest recipe) so the per-commit suite stays
fast while the accuracy tier stays runnable nightly/pre-release.

Full suite 164 passed, 3 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(pkg): warning hijack + import time; recovery-test harness
@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@nceglia
nceglia merged commit 1e82535 into main Aug 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant