Repository navigation
Improves flow decomposition (apparently one bug too) - #70
Merged
Vini2 merged 2 commits intoSep 7, 2026
Merged
Conversation
…nd per-rule CPU options Measured on 17,189 real flow networks extracted from Setonix run logs, with 1,293 re-solved by the original K = 1, 2, ... ladder as ground truth. Correctness - get_lowerbound_k used stDAG.get_width() over ALL edges plus ceil(log2(#distinct flows)). Both are invalid for the interval (inexact) decomposition phables solves: edges with lower bound 0 (28% of real edges) need no covering, and log2 assumes exact flows. The search started above the true minimum in 12% of real components and returned a non-minimal decomposition in 7.7% of them, while saving no time (1,084 s vs 1,098 s). Replaced by the maximum antichain over edges with lower bound > 0, which is valid for any interval model. Guards flowpaths' quirk that an empty weight_function means "all edges". Identical to the original ladder on all 1,293 ground-truth networks. Speed - 58% of the largest real components have no decomposition within --maxpaths, and the ladder proved that once per K with exponentially growing cost (0.5 s, 3 s, 30 s, 40 s, then hours): 91% of solver time ended with no result. FD_Algorithm now skips solving when the bound exceeds --maxpaths, tries K = bound, and if infeasible probes K = --maxpaths once. Feasibility is monotone in K for this model (zero-weight duplicate path; non-strict lexicographic symmetry breaking; confirmed 780/780), so an infeasible probe retires the component. Same minimum K, same model at it, so results are unchanged: 1,098 s -> 373 s on the ground-truth set, 2,237 s -> 1,197 s on the 1,131 largest components (60 s cap). - One component per pool task in resolve_short_parallel; a chunk of 9 held eight components behind one slow one. Options - --mfd-time-limit: per-solve cap; a timeout leaves the component unresolved with a WARNING (a timeout is not an infeasibility proof). Off by default. - --mfd-dump-slow: dump networks slower than N seconds as JSON for offline reproduction. - --job-cpu / --job-mem: rules took threads from config jobCPU (8) regardless of --threads, leaving 56 of 64 cores idle. None defaults so snaketool's CLI-over-config merge cannot pin them. Logging - FD_Inexact logged to a logger not attached to phables' file handler, so no per-K information ever reached phables_output.log. Now one INFO line per component with its K ladder and per-attempt times. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…; PR body for the MFD fix
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| ErrorProne | 2 high |
🟢 Metrics 29 complexity · 0 duplication
Metric Results Complexity 29 Duplication 0
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #69, one commit (
4bad76f). It fixes a correctness bug that #69introduced in the flow-decomposition search, then makes the remaining search
much cheaper, and adds four options. Rebases cleanly onto
developat8f50039; the nine upstream commits since277178dtouch only README, docs andbuild_container.yaml, none of the files changed here.Everything below was measured on 17,189 real flow networks extracted from the
logs of a Setonix run over ~10,000 metagenomes, with 1,293 of them re-solved by
the original K = 1, 2, 3, … ladder as ground truth.
1. Fix: the lower bound from #69 skipped feasible K and returned non-minimal decompositions
82313fa(merged in #69) startedFD_Algorithm's K search atmax(stDAG.get_width(), ceil(log2(#distinct flows))), both lifted fromflowpaths'
MinFlowDecomp. Both terms are invalid for the inexact (interval)decomposition phables solves:
get_width()is the minimum path cover of all edges. An edge whose lowerbound is 0 (junction coverage 0 — 28 % of real edges) need not be covered
at all, so this over-counts.
ceil(log2(#distinct flows))assumes exact flows (k paths give at most 2^kdistinct subset sums). With intervals, lows of 20, 21, 22 under an upper bound
of 30 are all met by one path of weight 22, yet the term says K ≥ 2.
On real data the search started above the true minimum in 12 % of components
(2,054 / 17,189) and returned a non-minimal decomposition in 99 of 1,293
ground-truth networks (7.7 %) — 2 paths where 1 suffices (21 cases), 3 where 2
(67), and so on. It also saved no measurable time: 1,084 s for the bounded search
vs 1,098 s for the plain ladder over the same networks. Any results produced
with
developbetween #69 and this PR carry this: roughly 1 in 13 resolvedcomponents has more paths than the minimum.
Replaced by the maximum antichain over edges that must carry flow (lower bound
2. Speed: stop proving the impossible one K at a time
Re-solving the 1,131 largest real components showed where the time goes:
solves are cheap (~0.03 s). Model construction is 0.1–0.4 s per attempt; the
solve is everything at scale.
--maxpaths.For those the ladder tried every K, and each infeasibility proof was
exponentially slower than the last — 0.5 s, 3 s, 30 s, 40 s, then hours.
91 % of all solver time ended with no result. One real sample spent over
ten hours this way.
FD_Algorithmnow:--maxpaths;--maxpathsis feasible. Feasibility ismonotone in K for this model: a K-path solution extends to K+1 by appending
a zero-weight duplicate of its last path (the lexicographic symmetry
breaking is non-strict, and the safety fixings never touch more than Kmin
paths). Confirmed on 780/780 resolved real networks. So "no" at
--maxpathsproves every K in between infeasible and the climb is skipped. "Yes" means
the climb proceeds exactly as before; if it reaches
--maxpaths, the probe'sown solution is reused rather than solved twice.
The smallest feasible K and the model solved at it are unchanged in every case,
so results are identical to the original ladder. (Not done with
allow_empty_paths=True, the textbook way to make the question monotone: on areal component that variant needed 450 s to prove K = 10 infeasible where the
standard model needed 58 s — empty paths add symmetry and loosen the relaxation.)
--maxpathsper unresolvable component, instead of a ladder of themAlso in
resolve_short_parallel: one component per pool task instead ofceil(n / (workers × 4)). Measured on a real 424,819-vertex assembly (5,307solver-bound components, max 197 nodes), the worst task's share of total work
drops 2.0 % → 0.7 % (linear cost model), 12.9 % → 6.8 % (quadratic), 24.0 % →
14.1 % (cubic) — 1.7–2.35× on the critical path, and it reaches the floor set by
the single largest component. On that assembly a chunk of nine held eight
components behind one slow one for nine hours. Order is preserved, so the merged
output is unchanged.
3. Options
--mfd-time-limit SECONDS— cap a single solve. A component that hits itis reported unresolved with a WARNING; a timeout is not an infeasibility proof,
so the search does not continue to a K it could not call minimal. Off by
default: with no limit every answer is exact.
--mfd-dump-slow SECONDS— write the flow network of any component slowerthan that to
<output>/phables/slow_mfd_instances/as JSON, so a pathologicalcomponent can be reproduced off the cluster.
--job-cpu/--job-mem— every rule takesthreadsandnthreadsfromconfig["resources"]["jobCPU"](default 8);--threadsonly sets Snakemake'stotal core budget. So
--threads 64on a 64-core node still rancoverm,foldseek,hmmsearchand the flow decomposition 8-wide with 56 cores idle —measured on a real run. These override the two config values. Their click
defaults are
Noneon purpose: snaketool merges CLI defaults over configfiles, so a non-
Nonedefault would make the config value permanentlyunsettable.
Both MFD options are plumbed through the preflight as
config.get(...), soolder config files without the keys still work.
4. Logging and small fixes
FD_Inexact.pylogged tologging.getLogger(__name__), which is not a childof the
"phables 2.0.0"logger phables attaches its file handler to — sonothing from the flow-decomposition module, including its K attempts, ever
reached
phables_output.log. It now uses the same named logger as every otherphables_utilsmodule and writes one INFO line per component with its Kladder and per-attempt times, e.g.
MFD [44 edges, lb=8]: unresolvable within --maxpaths 10; K=9..9 skipped after 2 attempt(s), 56.2s total -- K8:Infeasible/0.3s K10*:Infeasible/55.7s.Minimum unitig count to consider a componentfor--compcount, which is a maximum (len(candidate_nodes) <= compcount).Testing
1,293 ground-truth networks (paths, weights, K), including the probe-reuse
path at K =
--maxpaths.cap, the zero-solve exit, the all-zero-lower-bound edge case, time-limit
behaviour, the slow-instance dump round-trip, and logger routing.
--job-cpu 64changes every rule'sthreads8 → 64 and the runtime config carries all four values; the DAG isotherwise unchanged (7 jobs in a resume scenario, with and without the options).
4bad76fin this repo's CI (run33937580446; attempt 1 failed on an Ubuntu mirror
Hash Sum mismatchduringapt-get update, unrelated, attempt 2 passed).days with no solver-related failures. The new INFO lines are in the logs, and
one component was seen hitting a 1800 s
--mfd-time-limitat the K = 10 probeand being left unresolved, as designed.
Not verified / found but not fixed here
phables testwas not run in my local environment (thepre-built-env placeholder under
workflow/condablocks env creation there);the container build's own test passed in CI.
--prostt5-cpuis broken in this image: ProstT5 on a CPU device crashes inthe CNN head with
Input type (c10::Half) and bias type (float) should be the same(the fp16 ProstT5 weights produce half-precision embeddings while thepredictor stays float on CPU). Hit for real when a CPU node ended up running
predict_3di. Not touched in this PR — worth a separate fix.--mfd-time-limitdeliberately changes outcomes for the components it fireson (they become unresolved); the default (no limit) is unchanged behaviour.
Happy to split the correctness fix (§1) from the rest if that is easier to review
— it is the part that matters for anyone who has run
developsince #69.