Skip to content

Remove the efmtool / Java compression backend - #72

Merged
VonAlphaBisZulu merged 2 commits into
mainfrom
remove-efmtool-java
Jul 25, 2026
Merged

Remove the efmtool / Java compression backend#72
VonAlphaBisZulu merged 2 commits into
mainfrom
remove-efmtool-java

Conversation

@VonAlphaBisZulu

Copy link
Copy Markdown
Contributor

Remove the efmtool / Java compression backend

The pure-Python sparse_rref compressor is the default, the tested path, and the one
every correctness gate runs. The legacy Java backend was strictly worse: it does not
normalize lumped-reaction scales, so a bound stated on a lumped reaction is read in the
lump's units — the mechanism behind the 4484× biomass trap on iML1515. It also needed a
JVM plus jpype1, and its tests could only run on the Windows CI leg because JPype's JNI
bridge segfaults non-deterministically on Linux/macOS (jpype#934).

This branch removes the backend thoroughly and surgically — code, tests, docs, packaging,
and CI — leaving the Python compressor as the single path.

Results

Gate Result
e_coli_core gene-MCS 455, set-identical to canonical reference
iML1515 gene-MCS 393, set-identical to canonical reference
Wheel size 3.08 MB → 0.70 MB
jpype on import straindesign no longer loaded
CI (CI-test) ✅ passing

Design-neutral at genome scale; the removed backend was never the tested path.

⚠️ Breaking changes

  • compression_backend parameter removed from compress_model, compress_model_coupled,
    and compute_strain_designs (was single-valued after this change).
  • compress_model_efmtool removed — the backward-compat alias named after the backend.
  • compress_model_coupled(suppressed_reactions=...) removed — documented as "Java backend
    only … ignored for the Python backend", and in fact unused.
  • straindesign.nullspace alias removed. It shadowed compression.nullspace (a different
    function taking a RationalMatrix), so the two silently disagreed. sparse_nullspace remains
    exported under its own name; compression.nullspace is untouched.
  • Pre-existing dead-but-importable helpers removed (no caller in package or tests, but public
    via star imports): modules_coeff2float, remove_redundant_bounds, lineq2mat,
    lineqlist2str, get_rids.
  • Packaging: the [java] extra and the bundled efmtool.jar are gone; jpype1 is no longer
    a dependency.

What was kept

  • The EFMTool citation in README and index.rst. The compression routine follows the
    concept of efmtool's compression, reimplemented in pure Python with exact rational
    arithmetic — concept credited, no code implied.
  • Two Java-parity tests were converted rather than deleted: test_basic_columns_rat_python
    now calls compression.basic_columns_from_numpy directly, and the backend-parity FBA test
    becomes test_fba_optimum_recovered_through_map — the part of the old test that was about
    correctness rather than about having two backends.

Suggested version bump

Given the four breaking API removals, this warrants at least a minor bump (or major, per the
project's SemVer policy).

🤖 Generated with Claude Code

VonAlphaBisZulu and others added 2 commits July 24, 2026 22:05
…oupling

The Python `sparse_rref` compressor is the default, the tested path, and the one
every correctness gate runs. The legacy Java backend was strictly worse: it does not
normalize lumped-reaction scales, so a bound stated on a lumped reaction is read in
the lump's units -- the mechanism behind the 4484x biomass trap on iML1515. It also
needed a JVM plus jpype1, and its tests could only run on the Windows CI leg because
JPype's JNI bridge segfaults non-deterministically on Linux/macOS (jpype#934).

Removed:

- `straindesign/efmtool.jar` (2.2 MB) and `straindesign/efmtool_cmp_interface.py`
- eager JVM startup from `straindesign/__init__.py`, which ran on *every* import
- the `compression_backend` parameter, now single-valued, from `compress_model`,
  `compress_model_coupled` and `compute_strain_designs`
- `compress_model_efmtool`, the backward-compat alias named after the backend
- `suppressed_reactions` from `compress_model_coupled`: it was documented as
  "Java backend only ... ignored for the Python backend" and was in fact unused
- the `[java]` extra and the jar from `package-data` in `pyproject.toml`
- Java setup and jpype1 install steps from the CI matrix
- `docs/source/legacy_methods.rst`, developer's guide section 3.11 (196 lines), and
  the JPype/JVM notes scattered through the guide, README, index and the
  compression notebook

Tests: dropped the four Java parity tests, the `jpype_available` fixture, the
`java` marker and its platform skip, `--java`, and `TestEfmtoolBackendGpr`. Two were
kept by converting rather than deleting them: `test_basic_columns_rat_python` now
calls `compression.basic_columns_from_numpy` directly (the deleted function was a
thin shim over it), and the backend-parity FBA test becomes
`test_fba_optimum_recovered_through_map`, asserting that the compressed optimum
recovers the uncompressed one through the compression map. That is the part of the
old test that was actually about correctness rather than about having two backends,
and it still exercises the map factors.

Kept deliberately: the EFMTool citation in README and index.rst. The compression
approach follows Terzer & Stelling; only the bundled implementation is gone. Note
the jar carried its own Simplified BSD licence (and a vendored JMatIO) that the
repository never reproduced, so removing it also closes that gap.

Verified: no `efmtool`/`jpype`/`java` reference remains outside those two citations;
jpype is not imported on `import straindesign`; the wheel drops from 3.08 MB to
0.70 MB; e_coli_core gene-MCS is 455, set-identical to the canonical reference.

Co-Authored-By: Claude <noreply@anthropic.com>
Follow-up to the efmtool removal. None of this was orphaned *by* that removal --
the Java module was self-contained, importing only `basic_columns_from_numpy` from
the package -- but the sweep for stale leftovers surfaced pre-existing dead code.

Removed five functions with no caller anywhere in the package or the tests:

- `networktools.modules_coeff2float` -- the float counterpart of
  `modules_coeff_to_fraction`, which is used; this one never was
- `lptools.remove_redundant_bounds`
- `parse_constr.lineq2mat`, `lineqlist2str`, `get_rids`

These modules define no `__all__`, so `straindesign/__init__.py`'s star imports made
all five importable as `straindesign.<name>`. Removing them is therefore a public API
change, even though nothing in the project used them.

Also dropped `sparse_nullspace as nullspace` from the package namespace. It shadowed
`compression.nullspace`, a different function taking a `RationalMatrix`, so
`straindesign.nullspace` and `straindesign.compression.nullspace` silently disagreed.
`sparse_nullspace` remains exported under its own name, and `compression.nullspace`
is untouched for internal callers.

Design-identical: e_coli_core gene-MCS 455, set-identical to the canonical reference.

Co-Authored-By: Claude <noreply@anthropic.com>
@VonAlphaBisZulu
VonAlphaBisZulu merged commit b7ed020 into main Jul 25, 2026
60 checks passed
@VonAlphaBisZulu
VonAlphaBisZulu deleted the remove-efmtool-java branch July 25, 2026 17:02
VonAlphaBisZulu added a commit that referenced this pull request Jul 26, 2026
The hardcoded list of names CNApy imports goes stale the moment CNApy imports
something new, and nothing in this repository can notice: a downstream consumer
is invisible to grep here.

So read CNApy instead of installing it. The test fetches its source, walks the
AST for every `from straindesign ... import`, and asserts each name is still
exported. Installing CNApy was the obvious alternative and is a bad trade: it
depends on Qt and PyQtWebEngine, on cplex, which this matrix already cannot
install on macOS or 3.13, and on jpype, which is the JVM binding removed in #72
and the reason those tests were Windows-only. That would drag all of it back into
twenty jobs to test one import list.

It is marked `downstream` and skipped unless --downstream is passed, following
the existing --medium and --large convention, and the workflow passes the flag
only on the quarterly schedule. It needs network and tracks a branch we do not
control, so it reports drift rather than gating pull requests.

The static list stays. It runs everywhere, offline and unconditionally, and it is
the layer that would have blocked the removal in the first place.

Verified against ffad74b, before the restore: the AST check fails there, as do
both static guards. A test that cannot fail is decoration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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