From dd6ce13b2a665e2b61aab7033ccd3efa760b2335 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 12:49:51 +0200 Subject: [PATCH 01/30] docs: plan exact seeded base-pair probabilities --- ChangeLog | 13 + README.md | 4 + doc/Makefile.am | 1 + doc/analysis/base-pair-probabilities-plan.md | 519 +++++++++++++++++++ 4 files changed, 537 insertions(+) create mode 100644 doc/analysis/base-pair-probabilities-plan.md diff --git a/ChangeLog b/ChangeLog index de36b63d..c7f36eda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,10 @@ ## Technical changes and Optimizations +- document the implementation plan for exact seeded base-pair probabilities, + including algorithm comparison, numerical/output contracts and validation; + no prediction or probability implementation changes (issue #257) + - add an API overview, IntaRNA Doxygen styling, and automated documentation builds and GitHub Pages publishing alongside the user guide (issue #22) @@ -129,6 +133,15 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * doc/analysis/base-pair-probabilities-plan.md : + + specify the stack-seed partition and outside recurrences, sparse domain, + suffix-automaton comparison, numerical policy, output integration and + independent validation for https://github.com/BackofenLab/IntaRNA/issues/257 + + distinguish planned implementation from current supported behavior + * README.md, doc/Makefile.am : + + link the plan and include it in source distributions + 261007 Alexander Mitrofanov * doc/doxygen.cfg, doc/doxygen/* : + introduce the API main page with component links, prediction flow, diff --git a/README.md b/README.md index 8c12ed48..3e0266d5 100644 --- a/README.md +++ b/README.md @@ -2386,6 +2386,10 @@ Doxygen and Graphviz and run `bash doc/build-api.sh`, then open [API documentation build guide](doc/api-documentation.md) for validation, Autotools, and publishing instructions. +The [seeded base-pair probability implementation plan](doc/analysis/base-pair-probabilities-plan.md) +records the proposed algorithms, numerical contracts, integration, and validation +for issue #257. It is a design document; the proposed output is not implemented. + When IntaRNA is build while `pkg-config` is present, according pkg-config information is generated and installed too. diff --git a/doc/Makefile.am b/doc/Makefile.am index cb78b081..5e3ac756 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -5,6 +5,7 @@ EXTRA_DIST = \ conda.txt \ + analysis/base-pair-probabilities-plan.md \ api-documentation.md \ build-api.sh \ check-api.py \ diff --git a/doc/analysis/base-pair-probabilities-plan.md b/doc/analysis/base-pair-probabilities-plan.md new file mode 100644 index 00000000..7080047e --- /dev/null +++ b/doc/analysis/base-pair-probabilities-plan.md @@ -0,0 +1,519 @@ +# Implementation plan: seeded base-pair probabilities + +Status: design only. This document addresses [issue #257](https://github.com/BackofenLab/IntaRNA/issues/257); +it does not implement the recurrences, proposed APIs, or proposed output options. + +The code assessment uses IntaRNA revision +[`5454030a04bbcde79b2e75a0614dadf6f1115e6f`](https://github.com/BackofenLab/IntaRNA/tree/5454030a04bbcde79b2e75a0614dadf6f1115e6f). +The theory assessment uses IntaRNA-probabilities branch +`issue-10-fixed-seed-recursions` at +[`f62778504c8abbec8fc9894da16a357b60707680`](https://github.com/BackofenLab/IntaRNA-probabilities/tree/f62778504c8abbec8fc9894da16a357b60707680), +particularly [Step 5: fixed-length seeds](https://github.com/BackofenLab/IntaRNA-probabilities/blob/f62778504c8abbec8fc9894da16a357b60707680/theory/fixed-length-seeds.md) +and [Step 4: seed extension](https://github.com/BackofenLab/IntaRNA-probabilities/blob/f62778504c8abbec8fc9894da16a357b60707680/theory/seed-extension.md). +Recheck these integration points against the implementation branch before coding. + +## 1. Decision and scope + +Use the maximal-stack formulation as the leading implementation candidate for +stack-only seeds, with the optimized outside pass specified below. It has a +direct uniqueness argument, scalar seeded/seed-free state families, and simple +whole-interaction `noLP` handling. Do not port the reference implementation's +extra loop over every marked pair in every run. + +Compare it with the specialized strict-seed suffix automaton before selecting +the production default. Fewer explicit state families do not prove faster +execution: a long stack can exceed the seed length substantially. Both are +valid stack-only algorithms. A general seed-pattern automaton is a separate, +larger extension for bulged seeds; it is not needed for the initial scope. + +Initially support actual-pair probabilities for the exact seeded +`--model=P --mode=M` path with a stack-only handler. Preserve other prediction +modes; reject requests for the new output when their predictor cannot provide +the stated ensemble. The normal exact seeded path should use the selected new +backend whether or not pair probabilities are requested, so enabling output +does not change its partition function. + +Mixed-length and singleton **stacked** explicit seeds are included. Their run +predicate follows the same uniqueness proof, but extends the fixed-length +Python API and therefore needs independent tests. Computed seeds retain their +existing allowed lengths. Do not extend the computed-seed CLI to length one. + +## 2. Ensemble and output contract + +Let an interaction be its complete ordered intermolecular pair chain. Include +it if it contains at least one admitted seed, satisfies interaction constraints, +and lies wholly in one searched target/query region pair. Count the interaction +once, irrespective of the number or overlap of its seed witnesses. + +The searched ensemble is the union over the selected or automatically derived +nonoverlapping region pairs. `qRegionLenMax`, `tRegionLenMax`, and `outMinPu` +can restrict this union before prediction. Interactions spanning excluded +region boundaries are not included. Preserve model P's rejection of every +nonzero `windowWidth`; overlapping windows cannot be summed as disjoint sets. + +For the full IntaRNA Boltzmann weight $w(I)$, compute + +$$ +Z=\sum_{I\in\mathcal I}w(I),\qquad +M_k=\sum_{I\in\mathcal I:\,k\in I}w(I),\qquad P_k=M_k/Z. +$$ + +These are conditional probabilities given an allowed seeded interaction; +there is no added unbound-state weight. Merge raw region numerators and +denominators before normalizing. Ranked-output controls (`outNumber`, energy +reporting cutoffs, overlap selection, and `outPerRegion`) do not select the +probability ensemble. Structural `noLP`, endpoint `noGUend`, accessibility +limits, seed admission, loop limits, and both interaction span limits do. + +Actual pairing is different from site coverage. Preserve `spotProb`, +`qSpotProb`, and `tSpotProb`: their existing callbacks describe boundaries and +their writers accumulate coverage, not the interior pair chain. No selected-pair +"none of these pairs" probability is implied by $1-\sum_k P_k$, since pair +events can coexist. Per-nucleotide pair profiles, if added later, are row or +column sums of the actual-pair matrix. + +## 3. Establish a numerical policy before the DP + +Use `Z_type` for partition values, adjoints, raw pair masses, and normalization. +Use existing energy units and native energy functions. "Exact" refers to the +enumerated ensemble and disjoint recurrence; ordinary floating-point rounding +still occurs and must be measured against an independent higher-precision +reference. It does not mean exact real-number arithmetic. + +The new nonnegative backend must not use `Z_equal(x,0)` to prune positive DP +or boundary contributions. At the reviewed revision, that macro uses the +absolute threshold `sqrt(float::min())`, approximately $1.08\times10^{-19}$. +`PredictorMfeEns::addPartitionContribution()` currently applies that test. +Calling it unchanged while reversing all positive contributions would make +the denominator inconsistent with the pair numerators. + +Introduce a scoped exact-zero accumulation policy for the new path. Preserve +the virtual `updateZ()` dispatch from `updateCompleteZ()`, legacy tracker +callback behavior, and exception-safe restoration of scoped state. Retain the +existing policy for unrelated predictors. Observing/delegating overrides remain +supported; an override that changes boundary weights must also provide the same +objective coefficients to the reverse calculation. Arbitrary reweighting inside +an old override is not automatically a supported pair-probability backend. + +Use one boundary-admission/weight contract for forward aggregation and outside +initialization. Carry its raw denominator through predictor, region collector, +and final output exactly once per disjoint contribution. There must not be an +independent energy-to-weight reconstruction for the new probabilities. + +Check energy sentinels before exponentiation: a forbidden transition or boundary +is a structural zero. For finite admissible energies and nonnegative arithmetic, +check NaN and infinity, exponential and positive-product underflow to zero, +overflow in sums and region merges, and underflow/overflow in final $M/Z$. +Do not rely on `Z_isINF` alone; its comparison does not identify NaN. A computed +positive subnormal value may need scaling or a numerical-range failure if its +precision cannot meet the declared validation tolerance. Do not claim that +adding a small positive term to a large sum avoids ordinary rounding loss. + +Distinguish successful nonempty, successful empty, and failed computations. +Only a successfully empty ensemble produces undefined probabilities (`NA`). +Numerical failure must be reported as an error, not as emptiness or a valid +matrix of zeros. Do not clamp substantial probability or mass inconsistencies. +Define any roundoff-only endpoint correction through the tested error tolerance. + +Checked arithmetic with explicit range failure is the initial strategy. If +representative supported workloads fail, introduce a tested scaling scheme +before release, with common scales for forward/outside quantities and raw +region merging. A global replacement of all IntaRNA arithmetic is out of scope. + +Follow the exact-zero policy through serialization too. Raw +`OutputHandler::incrementZ()` currently adds small values, but `Eall`, +`EallTotal`, `Zall`, and `P_E` output in `OutputHandlerCsv`, ensemble output in +`OutputHandlerEnsemble`, and ensemble-energy reporting in `OutputHandlerText` +use approximate-zero tests. Carry the new backend's partition status/policy +through collector/hub forwarding so these outputs recognize a small positive +total. Keep unrelated legacy behavior scoped. Existing `P_E` numerators retain +their documented native energy representation; do not use that field as the +independent oracle for actual-pair marginals. + +## 4. SeedHandler capability and active occurrence mask + +Add a proposed virtual `guaranteesStackOnlySeeds() const` method. Its contract +is geometric: every exposed seed is a consecutive diagonal pair chain in +internal coordinates. It does not assert that a seed exists or that all seeds +have the same length. Default to false for an unknown handler. + +| Handler | Planned implementation | +| --- | --- | +| `SeedHandlerNoBulge` | Return true. | +| `SeedHandlerMfe` | Return true when both effective, normalized per-strand unpaired allowances are zero; otherwise return false conservatively. | +| `SeedHandlerExplicit` | Inspect retained accepted patterns after parsing and minimum-energy-per-start selection; both dot-bar strings must contain only paired positions. | +| `SeedHandlerIdxOffset` | Forward to the wrapped handler; offsets do not change geometry. | + +After `fillSeed()`, compile the admitted occurrences in the prediction range. +Check **both complete endpoints**, since explicit `fillSeed()` counts contained +seeds without pruning its stored family and iteration bounds constrain starts. +Do not infer seed acceptance from complementarity or `seedMaxUP` alone. + +Preserve existing policies: explicit patterns ignore computed-seed filters; +competing explicit seeds with the same start retain only the selected minimum- +energy pattern; NoBulge and Mfe differ on equality at their energy thresholds. +Do not replace these contracts with one generic admission test. Explicit +`getConstraint().getBasePairs()` is a minimum, not a common pattern length. + +For a run from $a$ to $q$, define $\chi(a,q)$ as a Boolean: at least one admitted +occurrence is wholly contained in the run. In a backward scan, adding start $a$ +introduces a hit exactly when its retained seed ends no later than $q$ on the +same run. OR this with the flag for the shorter run. This handles variable +lengths and singleton occurrences without counting witnesses. Do not multiply +the seed's standalone accessibility or full energy into the final interaction; +those values determine admission only. + +Move the exact predictor's constructor assertion requiring more than one seed +pair into the legacy paths that require it. Check all other singleton assumptions +in seed annotation, range bounds, and reporting before enabling explicit +singletons in the new exact path. Leave the heuristic's restrictions explicit. + +## 5. Sparse search domain and weight ownership + +Use zero-based internal coordinates, with sequence 2 reversed so both pair +coordinates increase along a path. A stacked successor is $(i_1+1,i_2+1)$. +Pair vertices must pass accessibility/complementarity checks. Transitions must +pass the native internal-loop checks, including internal GU restrictions. + +Preserve the immediate empty-seed return. For an admitted occurrence beginning +at $a$ and ending at $b$, every possible left boundary $p$ obeys + +$$ +\max(0,b_k-W_k+1)\le p_k\le a_k\quad(k=1,2), +$$ + +where $W_k$ is the effective inclusive span limit, clipped to the active region. +Discard occurrences longer than either limit. Enumerate the deduplicated union +of these rectangles intersected with valid pair vertices. Use signed or +saturating arithmetic for lower bounds; avoid unsigned underflow. This is a +necessary-condition filter, not a heuristic seed/path selection. + +For each surviving $p$, compute only states within its two inclusive span +limits. Use on-demand transitions and run scans rather than materializing all +boundary/run incidences. Preserve intermediate states that cannot currently +terminate due to endpoint GU or ED filters; later extension may make their +terminal boundary admissible. Whole-sequence seed preprocessing remains a +separate cost. + +Set + +$$ +d(p)=\operatorname{BW}(E_{\rm init}),\quad +t(r,a)=\operatorname{BW}(E_{\rm interLeft}(r,a)),\quad +T(a,q)=\prod_{b\to b^+\text{ in the stack }a\ldots q}t(b,b^+). +$$ + +The empty stack product is $T(q,q)=1$. For admissible complete boundaries use + +$$ +B(p,q)=\operatorname{BW}(\texttt{energy.getE}(p_1,q_1,p_2,q_2,0)); +$$ + +otherwise $B(p,q)=0$. This includes complete-interval ED, native accessibility- +weighted dangling energies, terminal penalties, and `energyAdd` once. Keep +IntaRNA's existing dangle convention; do not replace it with a different +microstate model. Initiation occurs in $d$ once, and each pair transition owns +one local interaction energy. + +## 6. Maximal-stack forward recurrence + +Fix left boundary $p$. Let $N_p(q)$ and $S_p(q)$ be hybrid weights of seed-free +and seeded paths through $q$. Let $J^0_p(a)$ and $J^1_p(a)$ be contributions +entering a new maximal run at $a$. For **nonstack** transitions $r\rightsquigarrow a$: + +$$ +J^0_p(a)=\mathbf1_{a=p}d(p)+\sum_{r\rightsquigarrow a}N_p(r)t(r,a), +\qquad J^1_p(a)=\sum_{r\rightsquigarrow a}S_p(r)t(r,a). +$$ + +For complete allowed runs with $p\preceq a\preceq q$: + +$$ +N_p(q)=\sum_{a:\chi(a,q)=0}J^0_p(a)T(a,q),\qquad +S_p(q)=\sum_a[J^1_p(a)+\chi(a,q)J^0_p(a)]T(a,q). +$$ + +Visit $q$ in increasing pair order, first computing its entries and then its +completed-run sums. Unreachable states are zero. Under `noLP`, omit completed +runs of length one; keep pending $J$ entries so such runs may later extend. +Without `noLP`, source singletons follow the same equations, including an +admitted singleton seed. Enumerate only runs connected by allowed stack edges. + +Every path has a unique decomposition at its nonstack edges. Each run either +contains an admitted seed or does not; the seeded-before-run and newly-seeded +cases are disjoint. This proves uniqueness even for overlapping, disjoint, +mixed-length, and singleton seed witnesses. Allowing stack edges in the entry +sum would introduce artificial cuts and duplicate paths. + +Accumulate $Z=\sum_{p,q}B(p,q)S_p(q)$. Submit each complete seeded boundary +partition once through `updateCompleteZ(..., S_p(q), true)` with the numerical +policy from section 3. Use the same terminal weight/acceptance for that update +and the outside objective. Cache or recompute coefficients deterministically +without storing every boundary globally. Keep the heuristic's legacy protected +extension methods; its own `predict()` explicitly calls them. + +## 7. Outside pass for every actual-pair numerator + +This differentiates the forward computation after attaching a marker to the +initial pair and every transition's destination pair. Seed masks and boundary +weights are fixed while differentiating. Write $H^0=N$, $H^1=S$ and +$f(c,a,q)=c\lor\chi(a,q)$. + +For each $p$, initialize $\bar H^0_p(q)=0$, $\bar H^1_p(q)=B(p,q)$ for every +terminal $q$, and all $\bar J$ to zero. Reverse $q$ in pair order. For every +forward run term, first accumulate + +$$ +\bar J^c_p(a)\mathrel{+}=\bar H^{f(c,a,q)}_p(q)T(a,q),\qquad +g(a)=\sum_{c=0}^1\bar H^{f(c,a,q)}_p(q)J^c_p(a). +$$ + +Avoid the reference's explicit marking of every pair in every run. For this +$p,q$, reconstruct $T(a,q)=t(a,a^+)T(a^+,q)$ backward from $T(q,q)=1$, with +$O(L)$ scratch. Reverse that product chain from its earliest start toward $q$: + +$$ +M_{a^+}\mathrel{+}=g(a)T(a,q),\qquad +g(a^+)\mathrel{+}=g(a)t(a,a^+). +$$ + +Use the updated, propagated $g$. Stop before $a=q$, since the empty product +has no pair marker. Set the direct $g$ contribution to zero for excluded forward +terms, but retain necessary product-chain nodes. In particular, `noLP` excludes +singleton completed paths, not the $T(q,q)=1$ node needed for longer products. + +After all runs ending at $q$, mark its entry pair and reverse nonstack entries: + +$$ +M_q\mathrel{+}=\sum_{c=0}^1\bar J^c_p(q)J^c_p(q),\qquad +\bar H^c_p(r)\mathrel{+}=\bar J^c_p(q)t(r,q) +\quad(r\rightsquigarrow q). +$$ + +Sum $M$ across left boundaries. Each entry owns a run's first pair; product +edges own the rest. One reverse pass per $p$ covers all right boundaries and +all marked pairs, without division, subtraction, or a global product tape. +Skip reverse storage and computation when pair output is not requested. + +## 8. Algorithm comparison before production integration + +Build correctness-checked numerical kernels for the comparison as an early +implementation milestone, before replacing the production default. Do not +infer production performance from exact-polynomial Python timings. + +Let $V$ be the number of allowed pair vertices, $E$ the number of nonstack +edges, $L$ the maximum run length, and $s$ a common fixed seed length. Bounds +below are conservative before span and eligible-start restrictions: + +| Candidate | Forward plus all-pair outside work | Logical DP values per left boundary | +| --- | --- | --- | +| Literal Step 5 reference | $O(VE+V^2L^2)$ | Depends on retained reference outputs | +| Maximal stacks with section 7 | $O(VE+V^2L)$ | $O(V)$ plus $O(L)$ run scratch | +| Specialized strict-seed suffix automaton | $O(VE+sV^2)$ | $O(sV)$ | + +These are graph-based bounds assuming iteration/storage indexed by allowed +vertices. A dense `Matrix` for an active span rectangle also stores invalid +pair positions: if that rectangle contains $A_p$ cells, physical DP storage is +$O(A_p+L)$ for maximal stacks or $O(sA_p)$ for the suffix candidate. Charge dense +initialization and scanning their actual cell visits: $O(\sum_p A_p)$ for +scalar tables and $O(s\sum_p A_p)$ when every suffix-state table is visited. +Do not substitute the number +of complementary vertices for rectangular cells in reported memory or timing. + +For the suffix candidate, store seed-free suffix run length and one absorbing +seeded family. A stack edge increments the capped run length; a nonstack edge +resets it to one. Accept only if an admitted occurrence ends at the new vertex +and the suffix contains its full length. For fixed length, aggregate seed-free +states before processing nonstack edges, avoiding an $s$ factor on those edges. +Initialize source-pair acceptance explicitly for singleton seeds. + +The same construction can compare mixed lengths by capping at the maximum +admitted length and using the shortest admitted length ending at each vertex; +longer occurrences ending there are redundant for existential acceptance. +With `noLP`, retain whether the current run has length one or at least two, +including after seed acceptance. Allow nonstack departure and final termination +only from runs of length at least two. This adds constant state. Reverse the +same DP transitions, initialize all accepted terminal states with $B$, and mark +each transition's destination and the source once. For mixed lengths, substitute +the maximum admitted length for $s$ in the cost estimate. + +A general prefix-pattern DFA can recognize retained bulged seed paths, but +needs state construction, failure/acceptance transitions, and a decision about +the seed family. Current handlers expose a selected MFE seed per start, not all +bulged structures satisfying constraints. A general DFA does not remove that +semantic limitation automatically. Its state count can be larger than $s$; +do not apply the strict-seed bound to it. + +Benchmark no seeds, one sparse seed amid increasing unrelated sequence, +overlapping/disjoint seeds, dense admission, long stacks with $L\gg s$, small +loop limits, mixed explicit lengths, asymmetric spans, and realistic inputs. +Measure seed preprocessing, partition-only work, added outside work, complete +runtime, and peak memory separately. Use identical inputs, models, numerical +policy, compiler settings, and thread count. Validate both candidates first. + +Keep maximal stacks if its lower state/storage cost and measured runtime meet +the representative workload requirements. Choose the suffix candidate if its +runtime advantage justifies its state/storage cost. Record the cases and +tradeoffs before selecting one production backend; shipping both or an adaptive +selector is not required. Neither the theory nor this plan claims a universal +speedup. Existing legacy results are correctness references only on independently +verified ensembles; elsewhere use them only as performance baselines. + +Report total memory separately: active span grids and run scratch; a full +target-by-query output matrix if requested; optional transition caches; and +legacy boundary-map storage when old trackers are also enabled. Do not retain +all run lists or boundary weights and still claim only $O(V)$ working memory. + +## 9. Raw result API, lifecycle, and output + +Introduce a proposed `BasePairProbabilities` result with raw `Z_type` numerator +storage, raw denominator, region identity, and explicit completion status. Pass +an optional non-owning result sink to the supported predictor; document lifetime +and keep existing constructor calls source-compatible. The pair-level owner +lives outside predictor/range scopes in `IntaRNA.cpp`. + +Do not implement this by attaching an ordinary `PredictionTracker`. Its energy +callbacks lose partition precision and cannot identify interior pairs; moreover, +a non-null tracker currently disables complete-boundary streaming. Retain those +callbacks for old outputs. Pair output alone must not populate `Z_partition`. + +Compute each region result privately and commit its raw masses only after +successful forward/outside completion. Allocate/reset region state on every +`predict()` call. Create one accumulator per target/query sequence pair, merge +disjoint completed regions once, and finalize explicitly after all requested +regions succeed. If one region fails or required work is cancelled, mark the +pair result failed and do not write a valid-looking normalized matrix. A writer +destructor must only release resources. Guard publication even on the existing +OpenMP exception path, where final output code can run before the saved exception +is rethrown. Check finite merged totals before forwarding ordinary exact-path +partition diagnostics as well. + +Store published matrix coordinates as original target/query positions. Convert +local DP pairs once through `InteractionEnergyIdxOffset::getBasePair()`, then +use each original sequence's `getInOutIndex()` only when printing labels. +Rows are target positions in original 5'-to-3' order; columns are query positions +in original 5'-to-3' order. Do not reverse these columns again with the old spot +writer, which expects internally reversed query storage. + +Add proposed `--out=bpProb:FILE`: a CSV-style matrix with the existing separator +and nucleotide/index-label conventions, but its own `bpProb` label and writer. +Use enough significant digits to preserve `Z_type`-appropriate validated +probability precision, including scientific notation for small values. Successful +empty ensembles use `NA`; successful nonempty ensembles use zero for pairs absent +from the searched ensemble. Validate the entire normalized result before writing +its first row. An output I/O failure is an error, not a successful completion. + +Register the output enum, parser lookup, help, validation, filename handling, +and writer together. Require a supported exact stack-seed predictor and +`needZall`; do not turn on `needBPs` merely to compute ensemble marginals. +Reject unsupported heuristic, kinetic, evaluation, unseeded, seed-only, or +bulged-seed requests rather than silently changing model/mode. Reuse multi-FASTA +filenames and buffer complete output blocks for synchronized shared-stream +emission. Keep computation local to each sequence pair; no new within-pair +parallelism is necessary. Check API callers' ranges for disjoint merging too. + +## 10. Validation gates + +### A. Mathematical kernel, independent of native energy rounding + +Use independent exhaustive enumeration of ordered pair chains, not the new +recurrence or the handler's own mask as its expected answer. Compare each +boundary partition, total $Z$, and every $M_k$ before normalization. Include +arbitrary positive heterogeneous weights, zero/forbidden boundaries, overlaps, +disjoint seeds, filtered windows, singleton/mixed lengths, missing edges, +asymmetric spans, `noLP`, and the eligible-start optimization. + +Use all ten fixed-length theory fixtures through a test-weight adapter where +their model matches. In particular, `random03` has two seeded interactions; +its four seed pairs have probability one and extension pair `(10,4)` has +$e/(1+2e)\simeq0.4223187983$ with **unrounded** accessibility. Map its one-based +antiparallel coordinates explicitly. Compare optimized outside marking with +literal run marking or independent forward derivatives on tiny cases. + +### B. Native IntaRNA model + +Extend `PredictorSeedOracle_test.cpp` and reuse appropriate fixtures from +`PredictorTinyOracle_test.cpp`. Existing seeded ensemble assertions cover only +a restricted unique-anchor domain; do not assume legacy multi-anchor output is +the oracle. Independently enumerate the admitted family with each handler's +specified threshold semantics, including exact energy/ED equalities, GU at +seed versus interaction ends, explicit selection, and range-crossing seeds. + +Native `AccessibilityBasePair` converts ED values to integer hundredths via +`Z_2_E`; the theory's decimal is therefore not an exact native end-to-end golden +value. Native Nussinov and nearest-neighbor oracles must use independently +enumerated structures with the production energy/rounding contract, including +initiation, loops, terminal/dangling terms, and full-span accessibility. Do not +loosen tolerances or change energy precision merely to match the theory decimal. + +Check $H_{\rm all}=N+S$ on the same allowed domain, and seeded plus seed-free +partition equality with the independently enumerated unconstrained ensemble. +Production skips starts that cannot contain a seed, so its seed-free tables do +not describe the whole unconstrained ensemble. For this identity either disable +eligible-start pruning in a test-only calculation or restrict both sides of +the comparison to the identical retained-start domain. +For nonempty ensembles verify $0\le P_{ij}\le1$, each row/column sum at most one, +and $\sum_{ij}P_{ij}$ equal to the independently computed expected pair count. +For marker tests, check $\sum_k M_k$ against the derivative that marks only +intermolecular pairs, holding accessibility and seed admission fixed. + +### C. Numerical and integration regressions + +- Tiny positive totals and positive boundary masses below the old epsilon; + both raw APIs and exact-path serialized diagnostics must remain nonempty. +- Exponential/product/normalization underflow, overflowing intermediate or + merged totals, NaN, and successful structural emptiness as distinct cases. +- Invariance of normalized probabilities under a representable constant + `energyAdd` shift with a **fixed admitted seed family**; a shift can otherwise + alter computed-seed admission. Test out-of-range shifts as explicit failures. +- Equal raw $Z$ with pair output off/on, legacy trackers off/on, and no duplicate + denominator increment through predictor, collector, hub, or final writer. +- Virtual update interception and scoped-state restoration after exceptions; + legacy heuristic paths and existing spot semantics remain covered. +- Asymmetric nonpalindromic sequences, nonzero region offsets, positive/negative + display shifts, and known interior pairs to expose double query reversal. +- Several disjoint regions, including a structure crossing a region boundary + that the region-union oracle excludes; `outPerRegion` changes reporting only. +- Injected failure after an earlier successful region: no partial probability + matrix is finalized. Repeated predictions reset all per-region state. +- Multi-FASTA file naming, shared stdout/stderr blocks, supported thread counts, + compressed streams where supported, and simultaneous spot/pair output. +- A bulge whose rectangle covers a coordinate pair that never actually pairs: + spot coverage may be positive while the actual-pair probability is zero. +- Explicit unsupported-mode/window errors and no hidden `Z_partition` retention + from pair output alone. Account separately for legacy trackers' map cost. + +Use justified absolute/relative tolerances with a higher-precision reference; +do not demand bitwise equality after changing sum order or hide model differences +inside a large tolerance. Benchmark after correctness passes, with the same +seed-locality and memory cases used in the early algorithm comparison. + +## 11. Implementation sequence and affected components + +Each milestone has an exit condition; none is implemented by this document. + +| Order | Work | Main files/components | Exit condition | +| --- | --- | --- | --- | +| 1 | Freeze ensemble, weights, numerical outcomes, and independent fixtures | This specification; `tests/PredictorSeedOracle_test.cpp`, `tests/PredictorTinyOracle_test.cpp` | Mathematical and native-model expected results distinguished | +| 2 | Add stack capability and active seed/domain compilation | `SeedHandler*`; proposed seed-domain helper | Handler-specific admission, mixed/singleton, offsets, and sparse boxes tested | +| 3 | Compare forward/outside numerical kernels | Proposed `SeededPartitionFunctionStack` or suffix helper; focused tests | Both compared kernels match oracle; measured default decision recorded | +| 4 | Integrate selected partition backend and scoped numerical policy | `PredictorMfeEns2dSeedExtension`, `PredictorMfeEns`, relevant output status/forwarding | Correct complete boundary sums with output off/on; virtual hooks preserved | +| 5 | Add raw pair result and success-only region aggregation | Proposed `BasePairProbabilities`; `src/bin/IntaRNA.cpp` | Pair numerators, disjoint merges, numerical failures and lifecycle validated | +| 6 | Add writer and CLI support, including tiny-total presentation | `CommandLineParsing.*`, proposed pair writer, `OutputHandlerCsv`, `OutputHandlerEnsemble`, `OutputHandlerText` | Correct indices, formats, statuses and compatibility checks | +| 7 | Finish performance and regression validation, documentation and registration | Library/test `Makefile.am`, README/help, Doxygen, ChangeLog | Required checks pass and intentional output changes are independently explained | + +Use the repository's `Matrix` and existing conventions rather than +introducing a new general matrix library. New helper and method names above +are proposals, not existing public API. Preserve existing predictor constructor +ownership, tracker lifetimes, and legacy subclass dependencies. + +For the implementation, register new headers/sources and tests, bootstrap after +Autotools source changes, and run `make tests -j2` on supported release and debug +builds. Run installed public-header/consumer checks for new public interfaces. +Record toolchain, options, inputs, timings, peak memory, and numerical differences. +An intentionally corrected partition can change ensemble rankings; justify those +changes against the oracle instead of regenerating expected files blindly. + +For this documentation-only plan, validate references, equations, pseudocode +contracts, Markdown structure, distribution registration, and `git diff --check`. +No C++ implementation build or performance result is claimed by this plan. From 88f4369f84a4c2d7759ba2034c47e3ce0f240162 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 13:47:16 +0200 Subject: [PATCH 02/30] test: establish independent seeded probability oracles (step 1) --- ChangeLog | 10 + tests/Makefile.am | 8 +- tests/SeededChainOracle.h | 71 + tests/SeededPartitionFunction_test.cpp | 41 + tests/SeededTheoryFixtures.h | 55 + tests/data/Makefile.am | 2 +- tests/data/seeded-theory.dat | 5681 ++++++++++++++++++++++++ tests/tools/export-seeded-theory.py | 33 + 8 files changed, 5899 insertions(+), 2 deletions(-) create mode 100644 tests/SeededChainOracle.h create mode 100644 tests/SeededPartitionFunction_test.cpp create mode 100644 tests/SeededTheoryFixtures.h create mode 100644 tests/data/seeded-theory.dat create mode 100644 tests/tools/export-seeded-theory.py diff --git a/ChangeLog b/ChangeLog index c7f36eda..606c5d2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- establish independent weighted-chain fixtures for seeded pair probabilities + (issue #257, implementation step 1) + - document the implementation plan for exact seeded base-pair probabilities, including algorithm comparison, numerical/output contracts and validation; no prediction or probability implementation changes (issue #257) @@ -133,6 +136,13 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * tests/SeededChainOracle.h, tests/SeededPartitionFunction_test.cpp, tests/Makefile.am : + + independently enumerate weighted chains, raw boundaries and pair masses; + verify overlapping witnesses and singleton/noLP semantics (PR #258, step 1) + + preserve all ten high-precision theory fixtures with an explicit coordinate + conversion and reproducible exporter; do not quantize their accessibility + 261008 Alexander Mitrofanov * doc/analysis/base-pair-probabilities-plan.md : + specify the stack-seed partition and outside recurrences, sparse domain, diff --git a/tests/Makefile.am b/tests/Makefile.am index ea9662b2..90b8432d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,6 +23,9 @@ check_PROGRAMS = runApiTests # test sources runApiTests_SOURCES = \ catch.hpp \ + SeededChainOracle.h \ + SeededTheoryFixtures.h \ + SeededPartitionFunction_test.cpp \ testEasyLoggingSetup.icc \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ @@ -73,6 +76,9 @@ LIBS= -L$(top_builddir)/src/IntaRNA -lIntaRNA \ # add source include for compilation runApiTests_CXXFLAGS = -I$(top_builddir)/src \ @AM_CXXFLAGS@ @CXXFLAGS@ \ - -DELPP_NO_LOG_TO_FILE + -DELPP_NO_LOG_TO_FILE \ + -DINTARNA_TEST_DATA_DIR=\"$(abs_srcdir)/data\" + +EXTRA_DIST = tools/export-seeded-theory.py diff --git a/tests/SeededChainOracle.h b/tests/SeededChainOracle.h new file mode 100644 index 00000000..dc6daddf --- /dev/null +++ b/tests/SeededChainOracle.h @@ -0,0 +1,71 @@ +#ifndef INTARNA_TEST_SEEDEDCHAINORACLE_H_ +#define INTARNA_TEST_SEEDEDCHAINORACLE_H_ + +#include +#include +#include +#include +#include + +// Independent exhaustive path enumeration. These test coordinates are zero-based +// with both strands increasing. Weight callbacks return long double, so expected +// values never pass through native integer energy conversion or the tested DP. +namespace seeded_oracle { +using Pair = std::array; +using Boundary = std::array; +using Chain = std::vector; +struct Model { + size_t n, m, span1, span2; + bool noLP = false; + std::vector seeds; + std::function valid; + std::function init; + std::function edge, boundary; +}; +struct Result { + long double z = 0, unseeded = 0, pairCount = 0; + std::map boundaries; + std::map mass; +}; +inline bool stack(Pair a, Pair b) { + return a[0]+1 == b[0] && a[1]+1 == b[1]; +} +inline Result enumerate(const Model & model) { + Result out; + Chain chain; + std::function visit = [&](long double hybrid) { + const Pair p = chain.front(), q = chain.back(); + bool noLP = true; + for (size_t k=0; k0 && stack(chain[k-1], chain[k])) + || (k+1 + +TEST_CASE("seeded chain oracle counts structures instead of seed witnesses", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + Model m{3,3,3,3,false, {{{0,0},{1,1}},{{1,1},{2,2}}}, + [](Pair p){return p[0]==p[1];}, [](Pair){return 2.L;}, + [](Pair a, Pair b){return stack(a,b) ? 3.L : 0.L;}, + [](Pair,Pair){return 5.L;}}; + auto r=enumerate(m); + REQUIRE(r.z == 150.L); // two two-pair chains (30 each), one three-pair (90) + REQUIRE(r.mass[Pair{1,1}] == 150.L); + REQUIRE(r.pairCount == 390.L); + m.seeds={{{1,1}}}; + r=enumerate(m); + REQUIRE(r.z == 160.L); + m.noLP=true; + REQUIRE(enumerate(m).z == 150.L); + m.span1=1; + REQUIRE(enumerate(m).z == 0.L); +} + +#include "SeededTheoryFixtures.h" +TEST_CASE("unrounded fixed-seed theory gold agrees with exhaustive chains", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + auto fixtures=theoryFixtures(); + REQUIRE(fixtures.size()==10); + for (const auto & f:fixtures) { + INFO(f.name); + const auto got=enumerate(f.model); + REQUIRE(double(got.z)==Approx(double(f.expected.z)).epsilon(2e-12)); + for (const auto & [pair, mass]:f.expected.mass) { + auto it=got.mass.find(pair); + REQUIRE(double(it==got.mass.end()?0:it->second)==Approx(double(mass)).epsilon(2e-12)); + } + if (f.name=="random03") { + REQUIRE(double(got.mass.at({9,4})/got.z)==Approx(double(std::exp(1.L)/(1+2*std::exp(1.L)))).epsilon(2e-12)); + } + } +} diff --git a/tests/SeededTheoryFixtures.h b/tests/SeededTheoryFixtures.h new file mode 100644 index 00000000..c7ae833d --- /dev/null +++ b/tests/SeededTheoryFixtures.h @@ -0,0 +1,55 @@ +#ifndef INTARNA_TEST_SEEDEDTHEORYFIXTURES_H_ +#define INTARNA_TEST_SEEDEDTHEORYFIXTURES_H_ +#include "SeededChainOracle.h" +#include +#include +#include +#include + +namespace seeded_oracle { +struct TheoryFixture { + std::string name; + Model model; + Result expected; + std::map terminal, seeded, free; + std::map valid; +}; +inline std::vector theoryFixtures() { + std::ifstream file(std::string(INTARNA_TEST_DATA_DIR)+"/seeded-theory.dat"); + if (!file) throw std::runtime_error("cannot read seeded theory fixtures"); + std::vector fixtures; + std::string tag; + while (file >> tag) { + if (tag == "CASE") { + fixtures.emplace_back(); + auto & f=fixtures.back(); + file >> f.name >> f.model.n >> f.model.m >> f.model.span1; + f.model.span2=f.model.span1; + } else { + auto & f=fixtures.back(); + if (tag == "Z") file >> f.expected.z; + else if (tag == "S") { + Pair p; size_t len; file >> p[0] >> p[1] >> len; + Chain s; for (size_t k=0;k> i; + file >> f.terminal[b] >> f.seeded[b] >> f.free[b]; + f.expected.boundaries[b]=f.terminal[b]*f.seeded[b]; + } else if (tag == "P") { + Pair p; file >> p[0] >> p[1] >> f.valid[p] >> f.expected.mass[p]; + } + } + } + for (auto & f:fixtures) { + f.model.valid=[valid=f.valid](Pair p){return valid.at(p);}; + f.model.init=[](Pair){return std::exp(1.L);}; + f.model.edge=[](Pair,Pair){return std::exp(1.L);}; + f.model.boundary=[b=f.terminal](Pair p,Pair q){ + auto it=b.find({p[0],p[1],q[0],q[1]});return it==b.end()?0.L:it->second; + }; + } + return fixtures; +} +} +#endif diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index d4543cbd..d23825de 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1,2 +1,2 @@ -EXTRA_DIST = *.parameter *.testresult +EXTRA_DIST = seeded-theory.dat *.parameter *.testresult diff --git a/tests/data/seeded-theory.dat b/tests/data/seeded-theory.dat new file mode 100644 index 00000000..95616c24 --- /dev/null +++ b/tests/data/seeded-theory.dat @@ -0,0 +1,5681 @@ +CASE random01 8 9 4 +Z 4.3717640897331911104894599604574021786328514418407E+0 +S 2 3 4 +S 3 4 4 +B 0 2 0 2 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 0 2 1 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 3 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 3 3 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 3 4 0.10923177257303592801426772675922287394788751914298 0 27.474593022118317968158957115156725710168223409107 +B 0 3 0 3 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 0 3 1 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 2 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 3 4 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 3 6 0.016970510725180570455966004230403875916087538807804 0 47.560129945305985709087486769738443607156131247662 +B 0 4 0 4 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 6 0.016970510725180570455966004230403875916087538807804 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 7 0.016970510725180570455966004230403875916087538807804 0 27.474593022118317968158957115156725710168223409107 +B 1 5 1 5 0.063101141649108249235116865494813374931987528975394 0 2.7182818284590452353602874713526624977572470937000 +B 1 5 3 6 0.016970510725180570455966004230403875916087538807804 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 3 7 0.016970510725180570455966004230403875916087538807804 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 3 8 0.016970510725180570455966004230403875916087538807804 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 2 2 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 2 2 3 3 0.70307725752434530933808924225307429131596250638101 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 4 0.70307725752434530933808924225307429131596250638101 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 4 5 0.10923177257303592801426772675922287394788751914298 0 47.560129945305985709087486769738443607156131247662 +B 2 2 5 3 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 5 4 0.40615451504869061867617848450614858263192501276200 0 27.474593022118317968158957115156725710168223409107 +B 2 3 2 3 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 2 3 3 4 0.70307725752434530933808924225307429131596250638101 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 6 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 4 5 0.10923177257303592801426772675922287394788751914298 0 27.474593022118317968158957115156725710168223409107 +B 2 3 5 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 5 6 0.063101141649108249235116865494813374931987528975394 54.598150033144239078110261202860878402790737038617 47.560129945305985709087486769738443607156131247662 +B 2 4 2 4 1.0000000000000000000000000000000000000000000000000 0 2.7182818284590452353602874713526624977572470937000 +B 2 4 3 6 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 7 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 5 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 6 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 2 4 5 7 0.063101141649108249235116865494813374931987528975394 0 47.560129945305985709087486769738443607156131247662 +B 3 0 3 0 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 3 0 5 1 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 2 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 3 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 1 0.016970510725180570455966004230403875916087538807804 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 2 0.016970510725180570455966004230403875916087538807804 0 27.474593022118317968158957115156725710168223409107 +B 3 0 6 3 0.016970510725180570455966004230403875916087538807804 0 47.560129945305985709087486769738443607156131247662 +B 3 1 3 1 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 3 1 5 2 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 3 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 4 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 6 2 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 6 3 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 3 1 6 4 0.063101141649108249235116865494813374931987528975394 0 47.560129945305985709087486769738443607156131247662 +B 3 2 3 2 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 3 2 4 5 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 3 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 3 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 4 0.10923177257303592801426772675922287394788751914298 0 27.474593022118317968158957115156725710168223409107 +B 3 3 3 3 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 3 3 4 5 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 6 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 3 3 6 4 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 6 6 0.016970510725180570455966004230403875916087538807804 0 47.560129945305985709087486769738443607156131247662 +B 3 4 3 4 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 3 4 4 5 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 6 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 3 4 5 7 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 3 4 6 6 0.016970510725180570455966004230403875916087538807804 0 27.474593022118317968158957115156725710168223409107 +B 3 4 6 7 0.016970510725180570455966004230403875916087538807804 54.598150033144239078110261202860878402790737038617 47.560129945305985709087486769738443607156131247662 +B 3 6 3 6 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 5 7 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 5 8 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 7 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 8 0.10923177257303592801426772675922287394788751914298 0 27.474593022118317968158957115156725710168223409107 +B 3 7 3 7 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 3 7 5 8 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 8 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 3 8 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 4 5 4 5 0.15536240349696360679341858802363237296378750931057 0 2.7182818284590452353602874713526624977572470937000 +B 4 5 5 6 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 7 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 8 0.10923177257303592801426772675922287394788751914298 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 6 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 7 0.063101141649108249235116865494813374931987528975394 0 27.474593022118317968158957115156725710168223409107 +B 4 5 6 8 0.063101141649108249235116865494813374931987528975394 0 47.560129945305985709087486769738443607156131247662 +B 5 0 5 0 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 6 1 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 2 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 3 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 5 1 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 2 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 3 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 4 0.23462782834889943395564767500048097878195627086869 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 5 2 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 5 2 6 3 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 5 0.016970510725180570455966004230403875916087538807804 0 47.560129945305985709087486769738443607156131247662 +B 5 3 5 3 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 5 3 6 4 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 6 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 5 0.016970510725180570455966004230403875916087538807804 0 27.474593022118317968158957115156725710168223409107 +B 5 4 5 4 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 5 4 6 6 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 7 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 7 5 0.016970510725180570455966004230403875916087538807804 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 5 6 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 6 7 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 8 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 5 7 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 5 7 6 8 0.40615451504869061867617848450614858263192501276200 0 7.3890560989306502272304274605750078131803155705521 +B 5 8 5 8 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 6 0 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 6 1 0.40615451504869061867617848450614858263192501276200 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 6 2 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 7 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 6 3 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 3 7 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 6 4 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 4 7 5 0.063101141649108249235116865494813374931987528975394 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 6 6 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 7 6 7 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 6 8 6 8 0.70307725752434530933808924225307429131596250638101 0 2.7182818284590452353602874713526624977572470937000 +B 7 5 7 5 0.10923177257303592801426772675922287394788751914298 0 2.7182818284590452353602874713526624977572470937000 +P 0 8 0 0E+0 +P 0 7 0 0E+0 +P 0 6 0 0E+0 +P 0 5 0 0E+0 +P 0 4 1 0E+0 +P 0 3 1 0E+0 +P 0 2 1 0E+0 +P 0 1 0 0E+0 +P 0 0 0 0E+0 +P 1 8 0 0E+0 +P 1 7 0 0E+0 +P 1 6 0 0E+0 +P 1 5 1 0E+0 +P 1 4 0 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 8 0 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 0 0E+0 +P 2 4 1 0E+0 +P 2 3 1 3.4452055990206988828960404957464942375988941032730E+0 +P 2 2 1 0E+0 +P 2 1 0 0E+0 +P 2 0 0 0E+0 +P 3 8 1 0E+0 +P 3 7 1 0E+0 +P 3 6 1 0E+0 +P 3 5 0 0E+0 +P 3 4 1 4.3717640897331911104894599604574021786328514418407E+0 +P 3 3 1 0E+0 +P 3 2 1 0E+0 +P 3 1 1 0E+0 +P 3 0 1 0E+0 +P 4 8 0 0E+0 +P 4 7 0 0E+0 +P 4 6 0 0E+0 +P 4 5 1 4.3717640897331911104894599604574021786328514418407E+0 +P 4 4 0 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 0 0E+0 +P 5 8 1 0E+0 +P 5 7 1 0E+0 +P 5 6 1 4.3717640897331911104894599604574021786328514418407E+0 +P 5 5 0 0E+0 +P 5 4 1 0E+0 +P 5 3 1 0E+0 +P 5 2 1 0E+0 +P 5 1 1 0E+0 +P 5 0 1 0E+0 +P 6 8 1 0E+0 +P 6 7 1 9.2655849071249222759341946471090794103395733856771E-1 +P 6 6 1 0E+0 +P 6 5 0 0E+0 +P 6 4 1 0E+0 +P 6 3 1 0E+0 +P 6 2 1 0E+0 +P 6 1 1 0E+0 +P 6 0 1 0E+0 +P 7 8 0 0E+0 +P 7 7 0 0E+0 +P 7 6 0 0E+0 +P 7 5 1 0E+0 +P 7 4 0 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +END +CASE random02 9 10 5 +Z 3.4453836971469803747340139789231149788453585142410E-1 +S 1 4 4 +B 0 4 0 4 0.64368963917561258281984974273198181802045240680428 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 5 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 6 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 6 0.010923741925317344826835743905415879904841140758756 0 27.474593022118317968158957115156725710168223409107 +B 0 4 4 7 0.0063104403630075981217993771193317378549505262491940 0 142.32935382482556026905480728176275780392268396339 +B 0 4 4 8 0.0016971388006978514167630103332475958050599117396321 0 142.32935382482556026905480728176275780392268396339 +B 0 6 0 6 0.32082507158997030168823957604942531476922839540182 0 2.7182818284590452353602874713526624977572470937000 +B 0 6 4 7 0.032690598855965146513950168964815211303554587417283 0 7.3890560989306502272304274605750078131803155705521 +B 0 6 4 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 0 6 4 9 0.0063104403630075981217993771193317378549505262491940 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 1 4 0.91553187403921065692767711750444543210662546514449 0 2.7182818284590452353602874713526624977572470937000 +B 1 4 2 5 0.26143745324123757517000007652833284147371829582510 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 2 6 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 6 0.040617551099683708085955493665962116552596190547125 0 27.474593022118317968158957115156725710168223409107 +B 1 4 4 7 0.0063104403630075981217993771193317378549505262491940 54.598150033144239078110261202860878402790737038617 67.645666868493653450016016424320161504144039086217 +B 1 4 4 8 0.0016971388006978514167630103332475958050599117396321 0 122.24381690163789252812627762718103990693477612483 +B 1 4 5 5 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 5 6 0.010923741925317344826835743905415879904841140758756 0 27.474593022118317968158957115156725710168223409107 +B 1 6 1 6 0.45631553027280385178434525045507277182765388331963 0 2.7182818284590452353602874713526624977572470937000 +B 1 6 4 7 0.032690598855965146513950168964815211303554587417283 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 4 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 4 9 0.0063104403630075981217993771193317378549505262491940 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 2 0 0.092078217204697873032189668485907684599064686994020 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 3 4 0.0063104403630075981217993771193317378549505262491940 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 1 0.015537043487627091531872110691500021954731755268318 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 2 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 3 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 5 4 0.0016971388006978514167630103332475958050599117396321 0 67.645666868493653450016016424320161504144039086217 +B 2 0 6 1 0.015537043487627091531872110691500021954731755268318 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 6 2 0.010923741925317344826835743905415879904841140758756 0 27.474593022118317968158957115156725710168223409107 +B 2 0 6 3 0.010923741925317344826835743905415879904841140758756 0 47.560129945305985709087486769738443607156131247662 +B 2 4 2 4 0.37184740431201450871202236795951820393427934846407 0 2.7182818284590452353602874713526624977572470937000 +B 2 4 3 6 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 7 0.0063104403630075981217993771193317378549505262491940 0 27.474593022118317968158957115156725710168223409107 +B 2 4 4 8 0.0016971388006978514167630103332475958050599117396321 0 27.474593022118317968158957115156725710168223409107 +B 2 4 5 5 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 6 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 6 7 0.0063104403630075981217993771193317378549505262491940 0 67.645666868493653450016016424320161504144039086217 +B 2 4 6 8 0.0016971388006978514167630103332475958050599117396321 0 142.32935382482556026905480728176275780392268396339 +B 2 5 2 5 0.29574456397791368513415619307496322017136396012302 0 2.7182818284590452353602874713526624977572470937000 +B 2 5 3 6 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 4 7 0.015537043487627091531872110691500021954731755268318 0 27.474593022118317968158957115156725710168223409107 +B 2 5 4 8 0.0063104403630075981217993771193317378549505262491940 0 27.474593022118317968158957115156725710168223409107 +B 2 5 4 9 0.0016971388006978514167630103332475958050599117396321 0 27.474593022118317968158957115156725710168223409107 +B 2 5 5 6 0.020150345049936838236908477477584164004622369777880 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 6 7 0.015537043487627091531872110691500021954731755268318 0 47.560129945305985709087486769738443607156131247662 +B 2 5 6 8 0.0063104403630075981217993771193317378549505262491940 0 122.24381690163789252812627762718103990693477612483 +B 2 6 2 6 0.18533461290713675159213390164377785771080290748405 0 2.7182818284590452353602874713526624977572470937000 +B 2 6 4 7 0.032690598855965146513950168964815211303554587417283 0 7.3890560989306502272304274605750078131803155705521 +B 2 6 4 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 2 6 4 9 0.0063104403630075981217993771193317378549505262491940 0 7.3890560989306502272304274605750078131803155705521 +B 2 6 6 7 0.032690598855965146513950168964815211303554587417283 0 7.3890560989306502272304274605750078131803155705521 +B 2 6 6 8 0.010923741925317344826835743905415879904841140758756 0 27.474593022118317968158957115156725710168223409107 +B 3 4 3 4 0.91553187403921065692767711750444543210662546514449 0 2.7182818284590452353602874713526624977572470937000 +B 3 4 4 7 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 4 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 5 0.45256354620842507899492490963015732974708535131468 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 6 7 0.023463995731345653103877435392646927203773358398159 0 47.560129945305985709087486769738443607156131247662 +B 3 4 6 8 0.0063104403630075981217993771193317378549505262491940 0 67.645666868493653450016016424320161504144039086217 +B 3 4 7 6 0.040617551099683708085955493665962116552596190547125 0 27.474593022118317968158957115156725710168223409107 +B 3 6 3 6 0.45631553027280385178434525045507277182765388331963 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 4 7 0.21041512051919336814621728461823995230866734276285 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 8 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 9 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 7 0.12155285968757925733008372679152758180611096509006 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 8 0.040617551099683708085955493665962116552596190547125 0 27.474593022118317968158957115156725710168223409107 +B 4 1 4 1 0.32082507158997030168823957604942531476922839540182 0 2.7182818284590452353602874713526624977572470937000 +B 4 1 5 4 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 5 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 2 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 3 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 7 4 0.040617551099683708085955493665962116552596190547125 0 47.560129945305985709087486769738443607156131247662 +B 4 1 8 2 0.020150345049936838236908477477584164004622369777880 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 8 3 0.020150345049936838236908477477584164004622369777880 0 27.474593022118317968158957115156725710168223409107 +B 4 2 4 2 0.51195116455715780551316440915124980304259545089142 0 2.7182818284590452353602874713526624977572470937000 +B 4 2 5 4 0.45256354620842507899492490963015732974708535131468 0 7.3890560989306502272304274605750078131803155705521 +B 4 2 5 5 0.26143745324123757517000007652833284147371829582510 0 7.3890560989306502272304274605750078131803155705521 +B 4 2 5 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 4 2 6 3 0.29574456397791368513415619307496322017136396012302 0 7.3890560989306502272304274605750078131803155705521 +B 4 2 7 4 0.26143745324123757517000007652833284147371829582510 0 27.474593022118317968158957115156725710168223409107 +B 4 2 7 6 0.040617551099683708085955493665962116552596190547125 0 67.645666868493653450016016424320161504144039086217 +B 4 2 8 3 0.079537963398669564755147976998676637300132469354617 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 4 3 0.70307725752434530933808924225307429131596250638102 0 2.7182818284590452353602874713526624977572470937000 +B 4 3 5 4 0.64368963917561258281984974273198181802045240680428 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 5 5 0.45256354620842507899492490963015732974708535131468 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 5 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 6 7 0.023463995731345653103877435392646927203773358398159 0 67.645666868493653450016016424320161504144039086217 +B 4 3 7 4 0.37184740431201450871202236795951820393427934846407 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 7 6 0.040617551099683708085955493665962116552596190547125 0 47.560129945305985709087486769738443607156131247662 +B 4 3 8 7 0.0063104403630075981217993771193317378549505262491940 0 217.01304078115746708809359813920535410370132884056 +B 4 7 4 7 0.43123502266074723523026186748061067722978944804081 0 2.7182818284590452353602874713526624977572470937000 +B 4 7 6 8 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 8 0.020150345049936838236908477477584164004622369777880 0 7.3890560989306502272304274605750078131803155705521 +B 4 8 4 8 0.40154121348638087197114211772006444058203439825245 0 2.7182818284590452353602874713526624977572470937000 +B 4 9 4 9 0.67338344834997894607896949249252805466820745659266 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 5 0 0.22670735838960044921267931693038644168816809240718 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 6 1 0.10000516944841643460419499318705458984810629012386 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 2 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 3 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 7 4 0.010923741925317344826835743905415879904841140758756 0 67.645666868493653450016016424320161504144039086217 +B 5 0 8 1 0.057771106468021763068033551939277305901419022696090 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 8 2 0.040617551099683708085955493665962116552596190547125 0 27.474593022118317968158957115156725710168223409107 +B 5 0 8 3 0.040617551099683708085955493665962116552596190547125 0 47.560129945305985709087486769738443607156131247662 +B 5 4 5 4 0.91553187403921065692767711750444543210662546514449 0 2.7182818284590452353602874713526624977572470937000 +B 5 4 6 7 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 7 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 8 7 0.023463995731345653103877435392646927203773358398159 0 27.474593022118317968158957115156725710168223409107 +B 5 4 8 8 0.0063104403630075981217993771193317378549505262491940 0 47.560129945305985709087486769738443607156131247662 +B 5 5 5 5 0.72815776513640192589217262522753638591382694165984 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 7 0.10000516944841643460419499318705458984810629012386 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 8 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 6 0.12969897862278279786331474294760082649586133991223 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 8 7 0.057771106468021763068033551939277305901419022696090 0 27.474593022118317968158957115156725710168223409107 +B 5 5 8 8 0.023463995731345653103877435392646927203773358398159 0 47.560129945305985709087486769738443607156131247662 +B 5 6 5 6 0.45631553027280385178434525045507277182765388331963 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 6 7 0.21041512051919336814621728461823995230866734276285 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 8 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 8 7 0.12155285968757925733008372679152758180611096509006 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 8 8 0.040617551099683708085955493665962116552596190547125 0 27.474593022118317968158957115156725710168223409107 +B 6 1 6 1 0.32082507158997030168823957604942531476922839540182 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 4 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 2 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 3 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 6 2 0.51195116455715780551316440915124980304259545089142 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 7 4 0.45256354620842507899492490963015732974708535131468 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 8 3 0.29574456397791368513415619307496322017136396012302 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 6 3 0.70307725752434530933808924225307429131596250638102 0 2.7182818284590452353602874713526624977572470937000 +B 6 3 7 4 0.64368963917561258281984974273198181802045240680428 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 7 6 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 7 0.023463995731345653103877435392646927203773358398159 0 47.560129945305985709087486769738443607156131247662 +B 6 7 6 7 0.43123502266074723523026186748061067722978944804081 0 2.7182818284590452353602874713526624977572470937000 +B 6 7 8 8 0.074924661836359818050111610212592495250241854845055 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 6 8 0.40154121348638087197114211772006444058203439825245 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 7 4 0.91553187403921065692767711750444543210662546514449 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 8 7 0.040617551099683708085955493665962116552596190547125 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 8 0.010923741925317344826835743905415879904841140758756 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 7 6 0.45631553027280385178434525045507277182765388331963 0 2.7182818284590452353602874713526624977572470937000 +B 7 6 8 7 0.21041512051919336814621728461823995230866734276285 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 8 8 0.070311360274050071345075243426508353200351240335493 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 8 1 0.32082507158997030168823957604942531476922839540182 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 8 2 0.51195116455715780551316440915124980304259545089142 0 2.7182818284590452353602874713526624977572470937000 +B 8 3 8 3 0.70307725752434530933808924225307429131596250638102 0 2.7182818284590452353602874713526624977572470937000 +B 8 7 8 7 0.43123502266074723523026186748061067722978944804081 0 2.7182818284590452353602874713526624977572470937000 +B 8 8 8 8 0.40154121348638087197114211772006444058203439825245 0 2.7182818284590452353602874713526624977572470937000 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 0 0E+0 +P 0 6 1 0E+0 +P 0 5 0 0E+0 +P 0 4 1 0E+0 +P 0 3 0 0E+0 +P 0 2 0 0E+0 +P 0 1 0 0E+0 +P 0 0 0 0E+0 +P 1 9 0 0E+0 +P 1 8 0 0E+0 +P 1 7 0 0E+0 +P 1 6 1 0E+0 +P 1 5 0 0E+0 +P 1 4 1 3.4453836971469803747340139789231149788453585142410E-1 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 9 0 0E+0 +P 2 8 0 0E+0 +P 2 7 0 0E+0 +P 2 6 1 0E+0 +P 2 5 1 3.4453836971469803747340139789231149788453585142410E-1 +P 2 4 1 0E+0 +P 2 3 0 0E+0 +P 2 2 0 0E+0 +P 2 1 0 0E+0 +P 2 0 1 0E+0 +P 3 9 0 0E+0 +P 3 8 0 0E+0 +P 3 7 0 0E+0 +P 3 6 1 3.4453836971469803747340139789231149788453585142410E-1 +P 3 5 0 0E+0 +P 3 4 1 0E+0 +P 3 3 0 0E+0 +P 3 2 0 0E+0 +P 3 1 0 0E+0 +P 3 0 0 0E+0 +P 4 9 1 0E+0 +P 4 8 1 0E+0 +P 4 7 1 3.4453836971469803747340139789231149788453585142410E-1 +P 4 6 0 0E+0 +P 4 5 0 0E+0 +P 4 4 0 0E+0 +P 4 3 1 0E+0 +P 4 2 1 0E+0 +P 4 1 1 0E+0 +P 4 0 0 0E+0 +P 5 9 0 0E+0 +P 5 8 0 0E+0 +P 5 7 0 0E+0 +P 5 6 1 0E+0 +P 5 5 1 0E+0 +P 5 4 1 0E+0 +P 5 3 0 0E+0 +P 5 2 0 0E+0 +P 5 1 0 0E+0 +P 5 0 1 0E+0 +P 6 9 0 0E+0 +P 6 8 1 0E+0 +P 6 7 1 0E+0 +P 6 6 0 0E+0 +P 6 5 0 0E+0 +P 6 4 0 0E+0 +P 6 3 1 0E+0 +P 6 2 1 0E+0 +P 6 1 1 0E+0 +P 6 0 0 0E+0 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 0 0E+0 +P 7 6 1 0E+0 +P 7 5 0 0E+0 +P 7 4 1 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +P 8 9 0 0E+0 +P 8 8 1 0E+0 +P 8 7 1 0E+0 +P 8 6 0 0E+0 +P 8 5 0 0E+0 +P 8 4 0 0E+0 +P 8 3 1 0E+0 +P 8 2 1 0E+0 +P 8 1 1 0E+0 +P 8 0 0 0E+0 +END +CASE random03 10 8 6 +Z 2.9540220251089651886087158590910771970452262871571E-1 +S 4 0 4 +B 0 0 0 0 0.38127651940768643182745377510846918653532856223429 0 2.7182818284590452353602874713526624977572470937000 +B 0 0 1 1 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 2 1 0.068700661285486509402040381565801256596481347716195 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 3 1 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 3 2 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 0 0 3 5 0.0076954275084611998521571809025063660433028978954380 0 47.560129945305985709087486769738443607156131247662 +B 0 0 4 1 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 5 1 0.011621611174172656427481064862902240234099017339828 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 5 2 0.011621611174172656427481064862902240234099017339828 0 87.731203791681321190944546078901879401131946924772 +B 0 0 5 5 0.0031255326277914121486356300733459981710180193504505 0 217.01304078115746708809359813920535410370132884056 +B 0 1 0 1 0.44392298941008830410090008431757976734316069222693 0 2.7182818284590452353602874713526624977572470937000 +B 0 1 1 6 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 2 6 0.068700661285486509402040381565801256596481347716195 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 3 2 0.14823073610763090710757960894460982738743325741082 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 3 5 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 3 6 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 4 6 0.020117689720553900706326499652458482297180015329205 0 47.560129945305985709087486769738443607156131247662 +B 0 1 5 2 0.060204582739105265123194946776245014533400349726817 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 5 5 0.011621611174172656427481064862902240234099017339828 0 27.474593022118317968158957115156725710168223409107 +B 0 1 5 6 0.011621611174172656427481064862902240234099017339828 0 47.560129945305985709087486769738443607156131247662 +B 0 6 0 6 0.50656945941249017637434639352669034815099282221959 0 2.7182818284590452353602874713526624977572470937000 +B 1 0 1 0 0.38127651940768643182745377510846918653532856223429 0 2.7182818284590452353602874713526624977572470937000 +B 1 0 2 1 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 3 1 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 3 2 0.037109846813316389264017369231570966423342011307960 0 27.474593022118317968158957115156725710168223409107 +B 1 0 3 5 0.0099803749487960937039179563170865499794453371679317 0 27.474593022118317968158957115156725710168223409107 +B 1 0 4 1 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 5 1 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 5 2 0.020117689720553900706326499652458482297180015329205 0 67.645666868493653450016016424320161504144039086217 +B 1 0 5 5 0.0054104800681263060003964054879261821071604586229443 0 142.32935382482556026905480728176275780392268396339 +B 1 0 6 1 0.011621611174172656427481064862902240234099017339828 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 6 2 0.011621611174172656427481064862902240234099017339828 0 87.731203791681321190944546078901879401131946924772 +B 1 0 6 5 0.0031255326277914121486356300733459981710180193504505 0 346.29487777063361298524265019950882880627071075635 +B 1 1 1 1 0.44392298941008830410090008431757976734316069222693 0 2.7182818284590452353602874713526624977572470937000 +B 1 1 2 6 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 2 0.19224381279189372809977194002879223381444971125283 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 5 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 6 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 4 6 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 1 1 5 2 0.10421765942336808611538727786042742096041680356882 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 5 5 0.020117689720553900706326499652458482297180015329205 0 27.474593022118317968158957115156725710168223409107 +B 1 1 5 6 0.020117689720553900706326499652458482297180015329205 0 47.560129945305985709087486769738443607156131247662 +B 1 1 6 2 0.060204582739105265123194946776245014533400349726817 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 6 5 0.011621611174172656427481064862902240234099017339828 0 47.560129945305985709087486769738443607156131247662 +B 1 1 6 6 0.011621611174172656427481064862902240234099017339828 0 142.32935382482556026905480728176275780392268396339 +B 1 6 1 6 0.50656945941249017637434639352669034815099282221959 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 2 0 0.38127651940768643182745377510846918653532856223429 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 3 1 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 3 2 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 3 5 0.012265322389130987555678731731666733915587776440425 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 1 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 5 1 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 5 2 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 2 0 5 5 0.0076954275084611998521571809025063660433028978954380 0 67.645666868493653450016016424320161504144039086217 +B 2 0 6 1 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 6 2 0.020117689720553900706326499652458482297180015329205 0 67.645666868493653450016016424320161504144039086217 +B 2 0 6 5 0.0054104800681263060003964054879261821071604586229443 0 217.01304078115746708809359813920535410370132884056 +B 2 0 7 3 0.00084058518745651829687485465876581423487558007795681 0 420.97856472696551980428144105695142510604935563351 +B 2 0 7 4 0.00084058518745651829687485465876581423487558007795681 0 420.97856472696551980428144105695142510604935563351 +B 2 1 2 1 0.44392298941008830410090008431757976734316069222693 0 2.7182818284590452353602874713526624977572470937000 +B 2 1 3 2 0.23625688947615654909196427111297464024146616509483 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 3 5 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 3 6 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 4 6 0.037109846813316389264017369231570966423342011307960 0 47.560129945305985709087486769738443607156131247662 +B 2 1 5 2 0.14823073610763090710757960894460982738743325741082 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 5 5 0.028613768266935144985171934442014724360261013318583 0 27.474593022118317968158957115156725710168223409107 +B 2 1 5 6 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 2 1 6 2 0.10421765942336808611538727786042742096041680356882 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 6 5 0.020117689720553900706326499652458482297180015329205 0 47.560129945305985709087486769738443607156131247662 +B 2 1 6 6 0.020117689720553900706326499652458482297180015329205 0 142.32935382482556026905480728176275780392268396339 +B 2 1 7 3 0.0054104800681263060003964054879261821071604586229443 0 67.645666868493653450016016424320161504144039086217 +B 2 1 7 4 0.0031255326277914121486356300733459981710180193504505 0 67.645666868493653450016016424320161504144039086217 +B 2 6 2 6 0.50656945941249017637434639352669034815099282221959 0 2.7182818284590452353602874713526624977572470937000 +B 2 6 7 7 0.0076954275084611998521571809025063660433028978954380 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 3 0 0.24071823208380637366988787053272051397070882736303 0 2.7182818284590452353602874713526624977572470937000 +B 3 0 4 1 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 1 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 2 0.037109846813316389264017369231570966423342011307960 0 27.474593022118317968158957115156725710168223409107 +B 3 0 5 5 0.0099803749487960937039179563170865499794453371679317 0 27.474593022118317968158957115156725710168223409107 +B 3 0 6 1 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 2 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 3 0 6 5 0.0076954275084611998521571809025063660433028978954380 0 122.24381690163789252812627762718103990693477612483 +B 3 0 7 3 0.00084058518745651829687485465876581423487558007795681 0 271.61119081430170616620385934206623250649206587918 +B 3 0 7 4 0.00084058518745651829687485465876581423487558007795681 0 271.61119081430170616620385934206623250649206587918 +B 3 0 8 1 0.0031255326277914121486356300733459981710180193504505 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 3 1 0.28026996616041937008415660219715704666848261893682 0 2.7182818284590452353602874713526624977572470937000 +B 3 1 4 6 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 2 0.19224381279189372809977194002879223381444971125283 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 5 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 6 0.037109846813316389264017369231570966423342011307960 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 6 2 0.14823073610763090710757960894460982738743325741082 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 6 5 0.028613768266935144985171934442014724360261013318583 0 27.474593022118317968158957115156725710168223409107 +B 3 1 6 6 0.028613768266935144985171934442014724360261013318583 0 47.560129945305985709087486769738443607156131247662 +B 3 1 7 3 0.0054104800681263060003964054879261821071604586229443 0 47.560129945305985709087486769738443607156131247662 +B 3 1 7 4 0.0031255326277914121486356300733459981710180193504505 0 47.560129945305985709087486769738443607156131247662 +B 3 1 8 6 0.0031255326277914121486356300733459981710180193504505 0 400.89302780377785206335291140236970720906144779496 +B 3 2 3 2 0.57439895436264836895197840231235810811534467594186 0 2.7182818284590452353602874713526624977572470937000 +B 3 2 4 6 0.078946528330264279530046876310587683057258242154251 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 5 0.064239318677836684824116782146055382867238685447990 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 6 0.064239318677836684824116782146055382867238685447990 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 5 0.049532109025409090118186687981523082677219128741729 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 6 0.049532109025409090118186687981523082677219128741729 0 27.474593022118317968158957115156725710168223409107 +B 3 2 7 3 0.016191506054842444131002615692062608106383895884815 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 7 4 0.0054104800681263060003964054879261821071604586229443 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 7 7 0.00084058518745651829687485465876581423487558007795681 0 162.41489074801322800998333693634447570091059180195 +B 3 2 8 6 0.0054104800681263060003964054879261821071604586229443 0 87.731203791681321190944546078901879401131946924772 +B 3 5 3 5 0.57439895436264836895197840231235810811534467594186 0 2.7182818284590452353602874713526624977572470937000 +B 3 5 4 6 0.26959749244672319507914834340243511481230139795174 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 5 6 0.21937328465641402365987135294327665025834638539285 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 6 6 0.16914907686610485224059436248411818570439137283397 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 7 7 0.0076954275084611998521571809025063660433028978954380 0 67.645666868493653450016016424320161504144039086217 +B 3 5 8 6 0.018476453495177337982763391106642792042526335157309 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 3 6 0.31982170023703236649842533386159357936625641051063 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 7 7 0.0076954275084611998521571809025063660433028978954380 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 4 0 0.27852017766202284423499554224192686227078672248815 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 5 1 0.054102003906078877821708238810683450549504007286716 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 2 0.054102003906078877821708238810683450549504007286716 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 5 0.014550269829465881407439507146246917851730215712919 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 6 1 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 6 2 0.045605925359697633542862804021127208486423009297338 0 27.474593022118317968158957115156725710168223409107 +B 4 0 6 5 0.012265322389130987555678731731666733915587776440425 0 47.560129945305985709087486769738443607156131247662 +B 4 0 7 3 0.0031255326277914121486356300733459981710180193504505 54.598150033144239078110261202860878402790737038617 87.731203791681321190944546078901879401131946924772 +B 4 0 7 4 0.0031255326277914121486356300733459981710180193504505 0 142.32935382482556026905480728176275780392268396339 +B 4 0 8 1 0.011621611174172656427481064862902240234099017339828 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 9 4 0.00084058518745651829687485465876581423487558007795681 148.41315910257660342111558004055227962348766759389 400.89302780377785206335291140236970720906144779496 +B 4 1 4 1 0.32428304284468219107634893328133945309549907277882 0 2.7182818284590452353602874713526624977572470937000 +B 4 1 5 2 0.28026996616041937008415660219715704666848261893682 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 5 0.054102003906078877821708238810683450549504007286716 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 6 0.054102003906078877821708238810683450549504007286716 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 2 0.23625688947615654909196427111297464024146616509483 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 5 0.045605925359697633542862804021127208486423009297338 0 27.474593022118317968158957115156725710168223409107 +B 4 1 6 6 0.045605925359697633542862804021127208486423009297338 0 47.560129945305985709087486769738443607156131247662 +B 4 1 7 3 0.020117689720553900706326499652458482297180015329205 0 47.560129945305985709087486769738443607156131247662 +B 4 1 7 4 0.011621611174172656427481064862902240234099017339828 0 47.560129945305985709087486769738443607156131247662 +B 4 1 8 6 0.011621611174172656427481064862902240234099017339828 0 400.89302780377785206335291140236970720906144779496 +B 4 1 9 4 0.0031255326277914121486356300733459981710180193504505 0 176.84196693478213160623653883004191830972551316345 +B 4 6 4 6 0.37004590802734153791770232432075204392021142306951 0 2.7182818284590452353602874713526624977572470937000 +B 4 6 7 7 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 5 0 0.38127651940768643182745377510846918653532856223429 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 6 1 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 2 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 5 0.020761400935512231834524166521222975978668774429803 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 7 3 0.0031255326277914121486356300733459981710180193504505 0 47.560129945305985709087486769738443607156131247662 +B 5 0 7 4 0.0031255326277914121486356300733459981710180193504505 0 47.560129945305985709087486769738443607156131247662 +B 5 0 8 1 0.011621611174172656427481064862902240234099017339828 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 9 4 0.00084058518745651829687485465876581423487558007795681 0 196.92750385796979934716506848462363620671342100200 +B 5 1 5 1 0.44392298941008830410090008431757976734316069222693 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 2 0.39990991272582548310870775323339736091614423838493 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 5 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 6 0.077196739831867753680885816355357498659562345705573 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 3 0.020117689720553900706326499652458482297180015329205 0 27.474593022118317968158957115156725710168223409107 +B 5 1 7 4 0.011621611174172656427481064862902240234099017339828 0 27.474593022118317968158957115156725710168223409107 +B 5 1 8 6 0.011621611174172656427481064862902240234099017339828 0 196.92750385796979934716506848462363620671342100200 +B 5 1 9 4 0.0031255326277914121486356300733459981710180193504505 0 102.15827997845022478719774797259932200994686828628 +B 5 2 5 2 0.90979745146415942728945251382646949735286686932027 0 2.7182818284590452353602874713526624977572470937000 +B 5 2 6 5 0.13363207872822327552724746618949202134045591698134 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 6 0.13363207872822327552724746618949202134045591698134 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 3 0.060204582739105265123194946776245014533400349726817 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 4 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 7 0.0031255326277914121486356300733459981710180193504505 0 47.560129945305985709087486769738443607156131247662 +B 5 2 8 6 0.020117689720553900706326499652458482297180015329205 0 67.645666868493653450016016424320161504144039086217 +B 5 2 9 4 0.0054104800681263060003964054879261821071604586229443 0 27.474593022118317968158957115156725710168223409107 +B 5 5 5 5 0.90979745146415942728945251382646949735286686932027 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 6 0.45634525162218100495506940306753188359703780966070 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 7 0.028613768266935144985171934442014724360261013318583 0 27.474593022118317968158957115156725710168223409107 +B 5 5 8 6 0.068700661285486509402040381565801256596481347716195 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 5 6 0.50656945941249017637434639352669034815099282221959 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 7 7 0.028613768266935144985171934442014724360261013318583 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 6 0 0.38127651940768643182745377510846918653532856223429 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 7 3 0.0054104800681263060003964054879261821071604586229443 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 4 0.0054104800681263060003964054879261821071604586229443 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 1 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 4 0.00084058518745651829687485465876581423487558007795681 0 47.560129945305985709087486769738443607156131247662 +B 6 1 6 1 0.44392298941008830410090008431757976734316069222693 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 3 0.034824899372981495412256593816990782487199572035467 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 7 4 0.020117689720553900706326499652458482297180015329205 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 6 0.020117689720553900706326499652458482297180015329205 0 47.560129945305985709087486769738443607156131247662 +B 6 1 9 4 0.0031255326277914121486356300733459981710180193504505 0 27.474593022118317968158957115156725710168223409107 +B 6 2 6 2 0.90979745146415942728945251382646949735286686932027 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 7 3 0.10421765942336808611538727786042742096041680356882 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 4 0.034824899372981495412256593816990782487199572035467 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 7 0.0054104800681263060003964054879261821071604586229443 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 8 6 0.034824899372981495412256593816990782487199572035467 0 47.560129945305985709087486769738443607156131247662 +B 6 2 9 4 0.0054104800681263060003964054879261821071604586229443 0 27.474593022118317968158957115156725710168223409107 +B 6 5 6 5 0.90979745146415942728945251382646949735286686932027 0 2.7182818284590452353602874713526624977572470937000 +B 6 5 7 7 0.049532109025409090118186687981523082677219128741729 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 6 0.11892486907579568082131737202495972115043636027508 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 6 6 0.50656945941249017637434639352669034815099282221959 0 2.7182818284590452353602874713526624977572470937000 +B 6 6 7 7 0.049532109025409090118186687981523082677219128741729 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 7 3 0.14823073610763090710757960894460982738743325741082 0 2.7182818284590452353602874713526624977572470937000 +B 7 3 8 6 0.049532109025409090118186687981523082677219128741729 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 9 4 0.0054104800681263060003964054879261821071604586229443 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 7 4 0.20509268167290483383094302282867985546373115523362 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 8 6 0.070450449783883035251201441521031440994177244164873 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 7 7 0.091368790542356980384216195060539799311135359588014 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 8 0 0.41907846498590290239256144681767553483540645735939 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 9 4 0.012265322389130987555678731731666733915587776440425 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 8 1 0.48793606609435112509309241540176217377017714606894 0 2.7182818284590452353602874713526624977572470937000 +B 8 1 9 4 0.045605925359697633542862804021127208486423009297338 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 8 6 0.55679366720279934779362338398584881270494783477848 0 2.7182818284590452353602874713526624977572470937000 +B 9 4 9 4 0.32688604468091552667951352133644064828400362232158 0 2.7182818284590452353602874713526624977572470937000 +P 0 7 0 0E+0 +P 0 6 1 0E+0 +P 0 5 0 0E+0 +P 0 4 0 0E+0 +P 0 3 0 0E+0 +P 0 2 0 0E+0 +P 0 1 1 0E+0 +P 0 0 1 0E+0 +P 1 7 0 0E+0 +P 1 6 1 0E+0 +P 1 5 0 0E+0 +P 1 4 0 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 1 0E+0 +P 1 0 1 0E+0 +P 2 7 0 0E+0 +P 2 6 1 0E+0 +P 2 5 0 0E+0 +P 2 4 0 0E+0 +P 2 3 0 0E+0 +P 2 2 0 0E+0 +P 2 1 1 0E+0 +P 2 0 1 0E+0 +P 3 7 0 0E+0 +P 3 6 1 0E+0 +P 3 5 1 0E+0 +P 3 4 0 0E+0 +P 3 3 0 0E+0 +P 3 2 1 0E+0 +P 3 1 1 0E+0 +P 3 0 1 0E+0 +P 4 7 0 0E+0 +P 4 6 1 0E+0 +P 4 5 0 0E+0 +P 4 4 0 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 1 0E+0 +P 4 0 1 2.9540220251089651886087158590910771970452262871571E-1 +P 5 7 0 0E+0 +P 5 6 1 0E+0 +P 5 5 1 0E+0 +P 5 4 0 0E+0 +P 5 3 0 0E+0 +P 5 2 1 0E+0 +P 5 1 1 2.9540220251089651886087158590910771970452262871571E-1 +P 5 0 1 0E+0 +P 6 7 0 0E+0 +P 6 6 1 0E+0 +P 6 5 1 0E+0 +P 6 4 0 0E+0 +P 6 3 0 0E+0 +P 6 2 1 2.9540220251089651886087158590910771970452262871571E-1 +P 6 1 1 0E+0 +P 6 0 1 0E+0 +P 7 7 1 0E+0 +P 7 6 0 0E+0 +P 7 5 0 0E+0 +P 7 4 1 0E+0 +P 7 3 1 2.9540220251089651886087158590910771970452262871571E-1 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +P 8 7 0 0E+0 +P 8 6 1 0E+0 +P 8 5 0 0E+0 +P 8 4 0 0E+0 +P 8 3 0 0E+0 +P 8 2 0 0E+0 +P 8 1 1 0E+0 +P 8 0 1 0E+0 +P 9 7 0 0E+0 +P 9 6 0 0E+0 +P 9 5 0 0E+0 +P 9 4 1 1.2475390316525342899588838346331771175813934339619E-1 +P 9 3 0 0E+0 +P 9 2 0 0E+0 +P 9 1 0 0E+0 +P 9 0 0 0E+0 +END +CASE random04 11 12 5 +Z 4.2051121195017988214604692705439839562640994090340E-1 +S 0 7 5 +B 0 1 0 1 0.48060774049313025816104798385354726236898721348287 0 2.7182818284590452353602874713526624977572470937000 +B 0 1 1 4 0.092017172313518417729031029431501717897376517568992 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 1 5 0.030725539140698490152564039530968011265493947691560 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 0 2 0.48060774049313025816104798385354726236898721348287 0 2.7182818284590452353602874713526624977572470937000 +B 0 2 1 4 0.13506258279493981269200065021219566658827158987854 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 1 5 0.048971761832097022766061408650807769206481445259439 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 4 6 0.0064231540300114684215800634069327115033256607582047 0 47.560129945305985709087486769738443607156131247662 +B 0 3 0 3 0.48060774049313025816104798385354726236898721348287 0 2.7182818284590452353602874713526624977572470937000 +B 0 3 1 4 0.20290718106638407000444252265374380602907423692976 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 1 5 0.092017172313518417729031029431501717897376517568992 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 1 7 0.073770949622119885115533660311661959956389020001113 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 4 6 0.012069005669068176965533234970090402048423646718757 0 47.560129945305985709087486769738443607156131247662 +B 0 4 0 4 0.30706105014530774912917828826972240059256810995884 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 5 0.15986177058496267504147290187304985733817916462021 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 7 0.14161554789356414242797553275321009939719166705233 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 8 0.13249243654786487612122684819329022042669791826839 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 6 0.020967527766268286670255814782678072261514711432371 0 27.474593022118317968158957115156725710168223409107 +B 0 5 0 5 0.30706105014530774912917828826972240059256810995884 0 2.7182818284590452353602874713526624977572470937000 +B 0 5 1 7 0.25250555664642979470338702597545218752888938641310 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 8 0.24338244530073052839663834141553230855839563762916 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 2 9 0.035119784368805717004796390597273011144945461061960 0 47.560129945305985709087486769738443607156131247662 +B 0 5 3 9 0.018294741516362994684917007881629317399913101168150 0 47.560129945305985709087486769738443607156131247662 +B 0 5 4 6 0.038764571960668506079700974407853412687696840859601 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 4 9 0.0076197446204681221131719450637965669418781143619490 0 47.560129945305985709087486769738443607156131247662 +B 0 7 0 7 0.53213769973877894858490242827417628628147189252259 0 2.7182818284590452353602874713526624977572470937000 +B 0 7 1 8 0.50306758079373335476888334460289954118953880543994 0 7.3890560989306502272304274605750078131803155705521 +B 0 7 2 9 0.096640385022910298575248131239402880151336392475680 0 27.474593022118317968158957115156725710168223409107 +B 0 7 2 10 0.039081213470056506477222631682993651053206781362791 0 27.474593022118317968158957115156725710168223409107 +B 0 7 3 9 0.050342304083345502451509102209278951792448241413070 0 27.474593022118317968158957115156725710168223409107 +B 0 7 3 10 0.020358345343815705504546836432245902635497119592909 0 102.15827997845022478719774797259932200994686828628 +B 0 7 4 9 0.020967527766268286670255814782678072261514711432371 0 27.474593022118317968158957115156725710168223409107 +B 0 7 4 10 0.0084792338976982158907575899994988357327662859075236 0 176.84196693478213160623653883004191830972551316345 +B 0 7 4 11 0.0028333822586415073468044184363411451876682999469722 148.41315910257660342111558004055227962348766759389 380.80749088059018432242438174778798931207353995641 +B 0 8 0 8 0.59314606528986585788891005385577420521343910476475 0 2.7182818284590452353602874713526624977572470937000 +B 0 8 2 9 0.13765411879231335295554929166748945948893034675150 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 2 10 0.080094947239459560857523792111080230390800735638607 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 3 9 0.071707345794667174295903831761045374720805001576355 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 3 10 0.041723387055137377348941565984012325563853879756189 0 27.474593022118317968158957115156725710168223409107 +B 0 8 4 9 0.029866049863468396374978394595265742474605776145985 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 4 10 0.017377755994898325595480169812086505945857350621138 0 47.560129945305985709087486769738443607156131247662 +B 0 8 4 11 0.0040299728490981610383963000932050006262207535507164 0 142.32935382482556026905480728176275780392268396339 +B 1 4 1 4 0.46674189723930631205746042832167461857450953729802 0 2.7182818284590452353602874713526624977572470937000 +B 1 4 4 6 0.056568039930640320170892875120817867143937695862467 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 1 5 0.46674189723930631205746042832167461857450953729802 0 2.7182818284590452353602874713526624977572470937000 +B 1 5 2 9 0.068769870073691161644555156028560398635010180849580 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 3 9 0.051944827221248439324675773312916704889977820955769 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 4 6 0.10458247052344540560312020282638806202194541297582 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 4 9 0.020557216986036506096722379341640220985431908027446 0 7.3890560989306502272304274605750078131803155705521 +B 1 5 5 9 0.0076197446204681221131719450637965669418781143619490 0 27.474593022118317968158957115156725710168223409107 +B 1 7 1 7 0.80886507569456835329336004612668505966089000497876 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 2 9 0.18923654690203989082272618929965073686911269460091 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 2 10 0.076526949722538108422574222184489147888626104902552 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 9 0.14293846596247509469898716026952680851022454353830 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 10 0.057804081596297307449898426933741399470916443132675 0 27.474593022118317968158957115156725710168223409107 +B 1 7 4 9 0.056568039930640320170892875120817867143937695862467 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 4 10 0.022876022726812794631998185239619095329373122393352 0 47.560129945305985709087486769738443607156131247662 +B 1 7 4 11 0.0076441477761365289996810113037553218180176214775342 0 142.32935382482556026905480728176275780392268396339 +B 1 7 5 9 0.020967527766268286670255814782678072261514711432371 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 5 10 0.0084792338976982158907575899994988357327662859075236 0 67.645666868493653450016016424320161504144039086217 +B 1 8 1 8 0.90159959956631436762742822715891628392837909468430 0 2.7182818284590452353602874713526624977572470937000 +B 1 8 2 9 0.26954766478760571027484021148037762902518103710180 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 2 10 0.15683806760810392787468824436521604004469444740344 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 3 9 0.20360089178995953161519475157393354425705569192665 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 3 10 0.11846650742378174436610601823814813521774759152102 0 27.474593022118317968158957115156725710168223409107 +B 1 8 4 9 0.080575255227042862887006538973602964582941554419145 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 4 10 0.046883238023215337348111849092404192768376980950031 0 47.560129945305985709087486769738443607156131247662 +B 1 8 4 11 0.010872415078611523273941353313226546609871193115012 0 142.32935382482556026905480728176275780392268396339 +B 1 8 5 9 0.029866049863468396374978394595265742474605776145985 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 5 10 0.017377755994898325595480169812086505945857350621138 0 67.645666868493653450016016424320161504144039086217 +B 2 9 2 9 0.31464163117694788935653505689558724093937334128523 0 2.7182818284590452353602874713526624977572470937000 +B 2 9 3 10 0.14713069382138105500055031246265098265070355266469 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 10 0.070618372210391858812522771315918707229027529725415 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 11 0.019871030222227007836422452379500943763593846305997 0 27.474593022118317968158957115156725710168223409107 +B 2 9 5 10 0.039081213470056506477222631682993651053206781362791 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 6 10 0.0075440547297211541419224920500685948773860330001643 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 6 11 0.0021227923391636021999954252342431130896907311099376 0 67.645666868493653450016016424320161504144039086217 +B 2 10 2 10 0.31464163117694788935653505689558724093937334128523 0 2.7182818284590452353602874713526624977572470937000 +B 2 10 4 11 0.054984227265010343668935128664895779774113557449177 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 6 11 0.0058738824865977803597693346585815670478887041401850 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 3 9 0.31464163117694788935653505689558724093937334128523 0 2.7182818284590452353602874713526624977572470937000 +B 3 9 4 10 0.096640385022910298575248131239402880151336392475680 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 4 11 0.027193263613563104389639587694216987613004165881765 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 5 10 0.050111505325690331622372191101875417560230552587508 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 6 10 0.018574346585354979287072051468950361384409804224883 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 6 11 0.0052265634395548147299881817500688560647732071544603 0 47.560129945305985709087486769738443607156131247662 +B 3 10 3 10 0.31464163117694788935653505689558724093937334128523 0 2.7182818284590452353602874713526624977572470937000 +B 3 10 4 11 0.075245247472524773329044193855434613198406059210649 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 6 11 0.014462186849981484348716998283818112925528553926244 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 4 0 0.40781086898459409115064573960881507135908474151129 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 7 4 0.012548796830890911283205317064784899914183526730228 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 4 6 0.56380989204633384920865668216236769341726155160635 0 2.7182818284590452353602874713526624977572470937000 +B 4 6 5 9 0.085942816247717535952146162547418746674871225843164 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 5 10 0.034755134172930284245787431672366162232103956078736 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 9 0.050342304083345502451509102209278951792448241413070 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 10 0.020358345343815705504546836432245902635497119592909 0 27.474593022118317968158957115156725710168223409107 +B 4 6 7 7 0.16129472029485928331274240706934423690540007907899 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 7 8 0.15842174795212324161188656166092202816253529186003 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 8 9 0.044116568236050684732125329297740036440958786963678 0 47.560129945305985709087486769738443607156131247662 +B 4 6 8 10 0.017840667960818616377095487624872709941621116792465 0 87.731203791681321190944546078901879401131946924772 +B 4 9 4 9 0.23020462008708659738850287321160631384346535045502 0 2.7182818284590452353602874713526624977572470937000 +B 4 9 5 10 0.076133518138208771385097551025359590482539415337778 0 7.3890560989306502272304274605750078131803155705521 +B 4 9 6 10 0.044596359397873419049797411392434534306718666975150 0 7.3890560989306502272304274605750078131803155705521 +B 4 9 6 11 0.012548796830890911283205317064784899914183526730228 0 27.474593022118317968158957115156725710168223409107 +B 4 9 8 10 0.039081213470056506477222631682993651053206781362791 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 4 10 0.23020462008708659738850287321160631384346535045502 0 2.7182818284590452353602874713526624977572470937000 +B 4 10 6 11 0.034723207057495914008826063474356946349821055687712 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 4 11 0.41667877841696977974174844682137266122122129435408 0 2.7182818284590452353602874713526624977572470937000 +B 5 9 5 9 0.23020462008708659738850287321160631384346535045502 0 2.7182818284590452353602874713526624977572470937000 +B 5 9 6 10 0.091125239095093386002673351529961996897824506863322 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 6 11 0.025641378063367498124643209436304116125462927859503 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 8 10 0.085610093167276473430098571820521113644312621250965 0 7.3890560989306502272304274605750078131803155705521 +B 5 10 5 10 0.23020462008708659738850287321160631384346535045502 0 2.7182818284590452353602874713526624977572470937000 +B 5 10 6 11 0.070951095290832921334570362042816340259586134317620 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 6 0 0.54105323335349216459044120152888834961654236503303 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 7 4 0.051389346168678737035754238204494234959725859582515 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 1 0.10690127626422169413839600872436693636588662734173 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 2 0.074118720622092874338485854954660514999234411144191 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 3 0.050111505325690331622372191101875417560230552587508 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 4 0.014100682381086517548201695322697771401724764752490 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 10 1 0.027858776307118462678819000482617127997444165227837 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 10 2 0.019315549169657305547500917271078473833341884932740 0 27.474593022118317968158957115156725710168223409107 +B 6 0 10 3 0.013059200657538066714497271759509478130897918612524 0 47.560129945305985709087486769738443607156131247662 +B 6 0 10 4 0.0036746778893592084649918034921559845772319691321991 0 67.645666868493653450016016424320161504144039086217 +B 6 6 6 6 0.74802117424652612703284434096262053576050112076509 0 2.7182818284590452353602874713526624977572470937000 +B 6 6 7 7 0.66052788391700048718449603764475223991046127960132 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 7 8 0.64876259898622035000602841878381368642932043242399 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 8 9 0.19993411567723265344582815799163487034557786123343 0 47.560129945305985709087486769738443607156131247662 +B 6 6 8 10 0.080853029019664330654009422195116636709728930186610 0 47.560129945305985709087486769738443607156131247662 +B 6 6 9 7 0.18124172789420561082201280618070110302683941737768 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 9 8 0.17801346059173061654775246417122987823498584574020 0 27.474593022118317968158957115156725710168223409107 +B 6 6 10 7 0.047232109207382695239769604605664094787307039792913 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 10 8 0.046390813576664994121582219549849255397743370607686 0 47.560129945305985709087486769738443607156131247662 +B 6 9 6 9 0.30541842678483324819202785308198063502824800402392 0 2.7182818284590452353602874713526624977572470937000 +B 6 9 8 10 0.17711413573515028423570191347185579548127373571579 0 7.3890560989306502272304274605750078131803155705521 +B 6 10 6 10 0.30541842678483324819202785308198063502824800402392 0 2.7182818284590452353602874713526624977572470937000 +B 6 11 6 11 0.55281851828427230176890882038982690309768321221036 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 7 4 0.50953962976717009265523637371305673916319080694188 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 9 5 0.10909185671749993401401567706248071085431530137447 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 7 0.096640385022910298575248131239402880151336392475680 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 8 0.090414649175615480855864358327863964799846938026293 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 10 5 0.027193263613563104389639587694216987613004165881765 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 10 7 0.024089492513171891859646831178391244637921689837242 0 27.474593022118317968158957115156725710168223409107 +B 7 4 10 8 0.022537606962976285594650452920478373150380451814981 0 47.560129945305985709087486769738443607156131247662 +B 7 5 7 5 0.50953962976717009265523637371305673916319080694188 0 2.7182818284590452353602874713526624977572470937000 +B 7 5 8 9 0.091744958882681733712568858642015035667039913831001 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 9 7 0.17231324227955231048024044769612768804115807351900 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 9 8 0.16608750643225749276085667478458877268966861906960 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 10 7 0.042952421586788968989305480606713633211070172520023 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 10 8 0.041400536036593362724309102348800761723528934497762 0 27.474593022118317968158957115156725710168223409107 +B 7 7 7 7 0.88303367158335228634779205036765146953610436766750 0 2.7182818284590452353602874713526624977572470937000 +B 7 7 8 9 0.25245793246409226728895095993329771405595546674544 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 10 0.10209357452890222075413656625323757782131459475694 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 9 8 0.34330019142382498672076344086245187354721802660439 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 10 8 0.085574238855879743993314024570464955542644478368014 0 7.3890560989306502272304274605750078131803155705521 +B 7 8 7 8 0.98427145469159855651395397968753343475288850523259 0 2.7182818284590452353602874713526624977572470937000 +B 7 8 8 9 0.35959991485169928967320569412748616631523250202172 0 7.3890560989306502272304274605750078131803155705521 +B 7 8 8 10 0.20923555691650924313839130044742603008059163003322 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 8 9 0.37400737336756029772339224728944339430702044759246 0 2.7182818284590452353602874713526624977572470937000 +B 8 10 8 10 0.37400737336756029772339224728944339430702044759246 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 9 1 0.38266574147096875641337345367076969742404110291581 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 10 2 0.11650103304476322880432311941519480581310254032768 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 3 0.095135991333441556959928389863428382884745780164388 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 4 0.028977262372023830607114372657512528864091481249791 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 5 0.0096758244881548695823494716563626911713187395112680 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 9 2 0.38266574147096875641337345367076969742404110291581 0 2.7182818284590452353602874713526624977572470937000 +B 9 2 10 3 0.11650103304476322880432311941519480581310254032768 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 10 4 0.042532755570420708230167582841817949071134648550391 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 10 5 0.015421769173626952984061162473207108657048313949189 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 9 3 0.38266574147096875641337345367076969742404110291581 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 4 0.063897797281742380074562312393584371999491408713673 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 5 0.028977262372023830607114372657512528864091481249791 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 7 0.023231317686551747205402681840668111378361906811869 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 9 4 0.24448575112449330685926968051411068816077287314354 0 2.7182818284590452353602874713526624977572470937000 +B 9 4 10 5 0.050342304083345502451509102209278951792448241413070 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 10 7 0.044596359397873419049797411392434534306718666975150 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 10 8 0.041723387055137377348941565984012325563853879756189 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 9 5 0.24448575112449330685926968051411068816077287314354 0 2.7182818284590452353602874713526624977572470937000 +B 9 5 10 7 0.079516894307591968517245351128506377442118594439033 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 10 8 0.076643921964855926816389505720084168699253807220073 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 9 7 0.42369452315990373749457921801495719877392184374251 0 2.7182818284590452353602874713526624977572470937000 +B 9 7 10 8 0.15842174795212324161188656166092202816253529186003 0 7.3890560989306502272304274605750078131803155705521 +B 9 8 9 8 0.47227012748867397173102822242119295273061558821529 0 2.7182818284590452353602874713526624977572470937000 +B 10 1 10 1 0.26670831851506389288133846547555590877032518360643 0 2.7182818284590452353602874713526624977572470937000 +B 10 2 10 2 0.26670831851506389288133846547555590877032518360643 0 2.7182818284590452353602874713526624977572470937000 +B 10 3 10 3 0.26670831851506389288133846547555590877032518360643 0 2.7182818284590452353602874713526624977572470937000 +B 10 4 10 4 0.17040036908622230512653943124492271135619398509205 0 2.7182818284590452353602874713526624977572470937000 +B 10 5 10 5 0.17040036908622230512653943124492271135619398509205 0 2.7182818284590452353602874713526624977572470937000 +B 10 7 10 7 0.29530433898168219889498560864438124514493245666375 0 2.7182818284590452353602874713526624977572470937000 +B 10 8 10 8 0.32916030346279383976556155417528517566469441939229 0 2.7182818284590452353602874713526624977572470937000 +P 0 11 0 0E+0 +P 0 10 0 0E+0 +P 0 9 0 0E+0 +P 0 8 1 0E+0 +P 0 7 1 4.2051121195017988214604692705439839562640994090340E-1 +P 0 6 0 0E+0 +P 0 5 1 0E+0 +P 0 4 1 0E+0 +P 0 3 1 0E+0 +P 0 2 1 0E+0 +P 0 1 1 0E+0 +P 0 0 0 0E+0 +P 1 11 0 0E+0 +P 1 10 0 0E+0 +P 1 9 0 0E+0 +P 1 8 1 4.2051121195017988214604692705439839562640994090340E-1 +P 1 7 1 0E+0 +P 1 6 0 0E+0 +P 1 5 1 0E+0 +P 1 4 1 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 11 0 0E+0 +P 2 10 1 0E+0 +P 2 9 1 4.2051121195017988214604692705439839562640994090340E-1 +P 2 8 0 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 0 0E+0 +P 2 4 0 0E+0 +P 2 3 0 0E+0 +P 2 2 0 0E+0 +P 2 1 0 0E+0 +P 2 0 0 0E+0 +P 3 11 0 0E+0 +P 3 10 1 4.2051121195017988214604692705439839562640994090340E-1 +P 3 9 1 0E+0 +P 3 8 0 0E+0 +P 3 7 0 0E+0 +P 3 6 0 0E+0 +P 3 5 0 0E+0 +P 3 4 0 0E+0 +P 3 3 0 0E+0 +P 3 2 0 0E+0 +P 3 1 0 0E+0 +P 3 0 0 0E+0 +P 4 11 1 4.2051121195017988214604692705439839562640994090340E-1 +P 4 10 1 0E+0 +P 4 9 1 0E+0 +P 4 8 0 0E+0 +P 4 7 0 0E+0 +P 4 6 1 0E+0 +P 4 5 0 0E+0 +P 4 4 0 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 1 0E+0 +P 5 11 0 0E+0 +P 5 10 1 0E+0 +P 5 9 1 0E+0 +P 5 8 0 0E+0 +P 5 7 0 0E+0 +P 5 6 0 0E+0 +P 5 5 0 0E+0 +P 5 4 0 0E+0 +P 5 3 0 0E+0 +P 5 2 0 0E+0 +P 5 1 0 0E+0 +P 5 0 0 0E+0 +P 6 11 1 0E+0 +P 6 10 1 0E+0 +P 6 9 1 0E+0 +P 6 8 0 0E+0 +P 6 7 0 0E+0 +P 6 6 1 0E+0 +P 6 5 0 0E+0 +P 6 4 0 0E+0 +P 6 3 0 0E+0 +P 6 2 0 0E+0 +P 6 1 0 0E+0 +P 6 0 1 0E+0 +P 7 11 0 0E+0 +P 7 10 0 0E+0 +P 7 9 0 0E+0 +P 7 8 1 0E+0 +P 7 7 1 0E+0 +P 7 6 0 0E+0 +P 7 5 1 0E+0 +P 7 4 1 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +P 8 11 0 0E+0 +P 8 10 1 0E+0 +P 8 9 1 0E+0 +P 8 8 0 0E+0 +P 8 7 0 0E+0 +P 8 6 0 0E+0 +P 8 5 0 0E+0 +P 8 4 0 0E+0 +P 8 3 0 0E+0 +P 8 2 0 0E+0 +P 8 1 0 0E+0 +P 8 0 0 0E+0 +P 9 11 0 0E+0 +P 9 10 0 0E+0 +P 9 9 0 0E+0 +P 9 8 1 0E+0 +P 9 7 1 0E+0 +P 9 6 0 0E+0 +P 9 5 1 0E+0 +P 9 4 1 0E+0 +P 9 3 1 0E+0 +P 9 2 1 0E+0 +P 9 1 1 0E+0 +P 9 0 0 0E+0 +P 10 11 0 0E+0 +P 10 10 0 0E+0 +P 10 9 0 0E+0 +P 10 8 1 0E+0 +P 10 7 1 0E+0 +P 10 6 0 0E+0 +P 10 5 1 0E+0 +P 10 4 1 0E+0 +P 10 3 1 0E+0 +P 10 2 1 0E+0 +P 10 1 1 0E+0 +P 10 0 0 0E+0 +END +CASE random05 12 10 6 +Z 1.3554498903412247165130335441381304398879900228361E+1 +S 4 4 5 +S 5 5 5 +S 5 2 5 +S 6 3 5 +S 7 4 5 +B 0 4 0 4 0.38521465009432587930693741340357162008529055176591 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 7 0.020887907892673245671178530399523389494097771969937 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 7 0.0097710522738542818372395632581124933737795929144156 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 7 0.0050688394171512309551667391368252496922671519365775 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 7 0.0013632208775449096771870834230216176521610922514038 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 5 5 0.0033564095117383688853734202379888409349738548367327 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 5 6 0.0033564095117383688853734202379888409349738548367327 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 5 8 0.00036662656044818007309391501553800601075471095873930 0 87.731203791681321190944546078901879401131946924772 +B 0 4 5 9 0.00036662656044818007309391501553800601075471095873930 0 87.731203791681321190944546078901879401131946924772 +B 0 7 0 7 0.15645666939871197139807804494347790419547472188796 0 2.7182818284590452353602874713526624977572470937000 +B 0 7 5 8 0.0013632208775449096771870834230216176521610922514038 0 7.3890560989306502272304274605750078131803155705521 +B 0 7 5 9 0.00036662656044818007309391501553800601075471095873930 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 1 4 0.45306338041001536949408190386615944798240780667743 0 2.7182818284590452353602874713526624977572470937000 +B 1 4 2 7 0.022881096526866704879364867214490612776910534555266 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 7 0.012760835225144470649519068480563328297998736792409 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 4 7 0.0090552166855381493715394127667596962578926771072353 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 5 5 0.039851077450240387388383941543763532284995520717500 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 5 6 0.039851077450240387388383941543763532284995520717500 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 5 8 0.0043530038288350984894665886454724525763802361293972 0 67.645666868493653450016016424320161504144039086217 +B 1 4 5 9 0.0043530038288350984894665886454724525763802361293972 0 67.645666868493653450016016424320161504144039086217 +B 1 4 6 5 0.030727410465614882762631311217319859447490104247309 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 6 6 0.030727410465614882762631311217319859447490104247309 0 27.474593022118317968158957115156725710168223409107 +B 1 4 6 8 0.0033564095117383688853734202379888409349738548367327 0 107.81674071486898893187307573348359729811985476333 +B 1 4 6 9 0.0033564095117383688853734202379888409349738548367327 0 291.69672773748937390713238899664795040347997371773 +B 1 7 1 7 0.18401373755675022983109908271607868954798855310536 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 5 8 0.016185695035970194789105706278236145812585330992098 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 5 9 0.0043530038288350984894665886454724525763802361293972 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 6 8 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 6 9 0.0033564095117383688853734202379888409349738548367327 0 27.474593022118317968158957115156725710168223409107 +B 2 4 2 4 0.41044879140587531000824036301351580831767738430577 0 2.7182818284590452353602874713526624977572470937000 +B 2 4 3 7 0.032004763511492209505117497540934285614415951025458 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 7 0.028299144971885888227137841827130653574309891340284 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 5 0.19122556927021310360473990704734575635277782505588 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 6 0.19122556927021310360473990704734575635277782505588 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 8 0.020887907892673245671178530399523389494097771969937 0 47.560129945305985709087486769738443607156131247662 +B 2 4 5 9 0.020887907892673245671178530399523389494097771969937 0 47.560129945305985709087486769738443607156131247662 +B 2 4 6 5 0.098576140781304372949775801679907687344607359158817 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 6 6 0.098576140781304372949775801679907687344607359158817 0 27.474593022118317968158957115156725710168223409107 +B 2 4 6 8 0.010767646590951011441332731665596105015185974207080 0 87.731203791681321190944546078901879401131946924772 +B 2 4 6 9 0.010767646590951011441332731665596105015185974207080 0 217.01304078115746708809359813920535410370132884056 +B 2 4 7 7 0.010767646590951011441332731665596105015185974207080 0 142.32935382482556026905480728176275780392268396339 +B 2 7 2 7 0.16670562982577446027531331005862632976724407955580 0 2.7182818284590452353602874713526624977572470937000 +B 2 7 5 8 0.077667128351853198315698989282130845113657789665252 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 5 9 0.020887907892673245671178530399523389494097771969937 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 6 8 0.040037144654402131944147088337033299124993102524327 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 6 9 0.010767646590951011441332731665596105015185974207080 0 27.474593022118317968158957115156725710168223409107 +B 3 4 3 4 0.46917385473693929556963222314965996337728922274709 0 2.7182818284590452353602874713526624977572470937000 +B 3 4 4 7 0.047543073258233627082736270887501610890727105573334 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 5 0.34260006109018581982109587255092798042056012939426 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 6 0.34260006109018581982109587255092798042056012939426 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 8 0.037422811956511392852890472153574326411815307810476 0 27.474593022118317968158957115156725710168223409107 +B 3 4 5 9 0.037422811956511392852890472153574326411815307810476 0 27.474593022118317968158957115156725710168223409107 +B 3 4 6 5 0.24995063260127708916613176718348991141238966349720 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 6 6 0.24995063260127708916613176718348991141238966349720 0 27.474593022118317968158957115156725710168223409107 +B 3 4 6 8 0.027302550654789158623044673419647041932903510047620 0 67.645666868493653450016016424320161504144039086217 +B 3 4 6 9 0.027302550654789158623044673419647041932903510047620 0 142.32935382482556026905480728176275780392268396339 +B 3 4 7 7 0.027302550654789158623044673419647041932903510047620 0 142.32935382482556026905480728176275780392268396339 +B 3 4 8 5 0.098576140781304372949775801679907687344607359158817 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 8 6 0.098576140781304372949775801679907687344607359158817 0 47.560129945305985709087486769738443607156131247662 +B 3 4 8 8 0.010767646590951011441332731665596105015185974207080 0 549.30618690635445548446849144292198683254911538885 +B 3 4 8 9 0.010767646590951011441332731665596105015185974207080 0 807.86986088530674727876659556352893623768787922043 +B 3 7 3 7 0.19055707944420639743035469211742348307965185108803 0 2.7182818284590452353602874713526624977572470937000 +B 3 7 5 8 0.13914856166773620184229227228602554441473024833840 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 5 9 0.037422811956511392852890472153574326411815307810476 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 8 0.10151857797028513547074037134092799842606556119748 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 9 0.027302550654789158623044673419647041932903510047620 0 27.474593022118317968158957115156725710168223409107 +B 3 7 8 8 0.040037144654402131944147088337033299124993102524327 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 8 9 0.010767646590951011441332731665596105015185974207080 0 47.560129945305985709087486769738443607156131247662 +B 4 4 4 4 0.73713158694237960244180523951534260332162796085950 0 2.7182818284590452353602874713526624977572470937000 +B 4 4 5 5 0.61968146028025163131902151924305429320240428397687 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 6 0.61968146028025163131902151924305429320240428397687 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 8 0.067688904337059242959797997232495132163028817420385 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 9 0.067688904337059242959797997232495132163028817420385 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 5 0.52703203179134290066405741387561622419423381807982 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 6 0.52703203179134290066405741387561622419423381807982 0 27.474593022118317968158957115156725710168223409107 +B 4 4 6 8 0.057568643035337008729952198498567847684117019657527 0 47.560129945305985709087486769738443607156131247662 +B 4 4 6 9 0.057568643035337008729952198498567847684117019657527 0 67.645666868493653450016016424320161504144039086217 +B 4 4 7 7 0.057568643035337008729952198498567847684117019657527 0 142.32935382482556026905480728176275780392268396339 +B 4 4 8 5 0.10769980776592987757552843200635136018211277562901 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 8 6 0.10769980776592987757552843200635136018211277562901 0 47.560129945305985709087486769738443607156131247662 +B 4 4 8 8 0.011764240908047741045425900073079716656592355499745 148.41315910257660342111558004055227962348766759389 380.80749088059018432242438174778798931207353995641 +B 4 4 8 9 0.011764240908047741045425900073079716656592355499745 0 678.58802389583060138161754350322546153511849730464 +B 4 4 9 5 0.039851077450240387388383941543763532284995520717500 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 9 6 0.039851077450240387388383941543763532284995520717500 0 67.645666868493653450016016424320161504144039086217 +B 4 4 9 8 0.0043530038288350984894665886454724525763802361293972 0 678.58802389583060138161754350322546153511849730464 +B 4 4 9 9 0.0043530038288350984894665886454724525763802361293972 403.42879349273512260838718054338827960589989735717 1863.0974804102862952567528610897581621630557180601 +B 4 7 4 7 0.29938932222165391338962193535360915913430284933701 0 2.7182818284590452353602874713526624977572470937000 +B 4 7 5 8 0.25168642298479003907953917123601485250948730627255 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 5 9 0.067688904337059242959797997232495132163028817420385 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 8 0.21405643928733897270798727029091730652082261913163 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 9 0.057568643035337008729952198498567847684117019657527 0 27.474593022118317968158957115156725710168223409107 +B 4 7 8 8 0.043742763194008453222126744050836931165099162209500 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 9 0.011764240908047741045425900073079716656592355499745 0 47.560129945305985709087486769738443607156131247662 +B 4 7 9 8 0.016185695035970194789105706278236145812585330992098 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 9 9 0.0043530038288350984894665886454724525763802361293972 0 67.645666868493653450016016424320161504144039086217 +B 5 0 5 0 0.62880512726487713594477414956949796603990970044708 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 6 1 0.37695887830145684963788848510339041779633395668342 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 2 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 3 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 5 0.058565237352433738334045366906051459325523400950192 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 7 4 0.058565237352433738334045366906051459325523400950192 0 67.645666868493653450016016424320161504144039086217 +B 5 0 8 1 0.082135928242085078350693731048717798112411706996935 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 8 2 0.047448381733614774500106399764640563205205221894673 0 27.474593022118317968158957115156725710168223409107 +B 5 0 8 3 0.047448381733614774500106399764640563205205221894673 0 47.560129945305985709087486769738443607156131247662 +B 5 0 8 5 0.012760835225144470649519068480563328297998736792409 0 251.52565389111403842527532968748451460950415804062 +B 5 0 9 3 0.019891313575576516067085361992039777852691390677272 0 142.32935382482556026905480728176275780392268396339 +B 5 0 9 5 0.0053495981459318280935597570529560642177866174220617 0 475.57671476010975888239170225981230350884009267213 +B 5 0 10 4 0.0053495981459318280935597570529560642177866174220617 0 678.58802389583060138161754350322546153511849730464 +B 5 1 5 1 0.62880512726487713594477414956949796603990970044708 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 2 0.37695887830145684963788848510339041779633395668342 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 3 0.37695887830145684963788848510339041779633395668342 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 5 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 6 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 4 0.21776205782694529398596692600472093856092867881680 0 47.560129945305985709087486769738443607156131247662 +B 5 1 8 2 0.082135928242085078350693731048717798112411706996935 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 3 0.082135928242085078350693731048717798112411706996935 0 27.474593022118317968158957115156725710168223409107 +B 5 1 8 5 0.047448381733614774500106399764640563205205221894673 0 176.84196693478213160623653883004191830972551316345 +B 5 1 8 6 0.047448381733614774500106399764640563205205221894673 0 196.92750385796979934716506848462363620671342100200 +B 5 1 9 3 0.034433029005221204040610966931123491487596163932483 0 47.560129945305985709087486769738443607156131247662 +B 5 1 9 5 0.019891313575576516067085361992039777852691390677272 0 271.61119081430170616620385934206623250649206587918 +B 5 1 9 6 0.019891313575576516067085361992039777852691390677272 0 772.40303296526296572462286234091686275581542785991 +B 5 1 10 4 0.019891313575576516067085361992039777852691390677272 0 271.61119081430170616620385934206623250649206587918 +B 5 2 5 2 0.62880512726487713594477414956949796603990970044708 0 2.7182818284590452353602874713526624977572470937000 +B 5 2 6 3 0.53615569877596840528981004420205989703173923455000 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 5 0.37695887830145684963788848510339041779633395668342 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 6 0.37695887830145684963788848510339041779633395668342 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 4 0.37695887830145684963788848510339041779633395668342 0 27.474593022118317968158957115156725710168223409107 +B 5 2 7 7 0.058565237352433738334045366906051459325523400950192 0 67.645666868493653450016016424320161504144039086217 +B 5 2 8 3 0.11682347475055538220128106233279503301961819209920 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 8 5 0.082135928242085078350693731048717798112411706996935 0 102.15827997845022478719774797259932200994686828628 +B 5 2 8 6 0.082135928242085078350693731048717798112411706996935 0 122.24381690163789252812627762718103990693477612483 +B 5 2 9 3 0.048974744434865892014136571870207205122500937187693 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 9 5 0.034433029005221204040610966931123491487596163932483 0 122.24381690163789252812627762718103990693477612483 +B 5 2 9 6 0.034433029005221204040610966931123491487596163932483 148.41315910257660342111558004055227962348766759389 271.61119081430170616620385934206623250649206587918 +B 5 2 10 4 0.034433029005221204040610966931123491487596163932483 0 67.645666868493653450016016424320161504144039086217 +B 5 2 10 7 0.0053495981459318280935597570529560642177866174220617 403.42879349273512260838718054338827960589989735717 1863.0974804102862952567528610897581621630557180601 +B 5 3 5 3 0.81551167013511752373840181087933770678506786769174 0 2.7182818284590452353602874713526624977572470937000 +B 5 3 6 5 0.53615569877596840528981004420205989703173923455000 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 6 0.53615569877596840528981004420205989703173923455000 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 8 0.058565237352433738334045366906051459325523400950192 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 4 0.53615569877596840528981004420205989703173923455000 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 7 0.058565237352433738334045366906051459325523400950192 0 47.560129945305985709087486769738443607156131247662 +B 5 3 8 5 0.11682347475055538220128106233279503301961819209920 0 27.474593022118317968158957115156725710168223409107 +B 5 3 8 6 0.11682347475055538220128106233279503301961819209920 0 47.560129945305985709087486769738443607156131247662 +B 5 3 8 8 0.012760835225144470649519068480563328297998736792409 0 196.92750385796979934716506848462363620671342100200 +B 5 3 9 5 0.048974744434865892014136571870207205122500937187693 0 27.474593022118317968158957115156725710168223409107 +B 5 3 9 6 0.048974744434865892014136571870207205122500937187693 0 122.24381690163789252812627762718103990693477612483 +B 5 3 9 8 0.0053495981459318280935597570529560642177866174220617 0 400.89302780377785206335291140236970720906144779496 +B 5 3 10 4 0.048974744434865892014136571870207205122500937187693 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 10 7 0.0053495981459318280935597570529560642177866174220617 0 678.58802389583060138161754350322546153511849730464 +B 5 5 5 5 0.81551167013511752373840181087933770678506786769174 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 6 0.69535251925047996094173160330072937626714451241661 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 8 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 9 0.058565237352433738334045366906051459325523400950192 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 7 0.21776205782694529398596692600472093856092867881680 0 27.474593022118317968158957115156725710168223409107 +B 5 5 8 6 0.15151102125902568605186839361687226792682467720146 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 8 8 0.047448381733614774500106399764640563205205221894673 0 102.15827997845022478719774797259932200994686828628 +B 5 5 8 9 0.012760835225144470649519068480563328297998736792409 0 122.24381690163789252812627762718103990693477612483 +B 5 5 9 6 0.063516459864510579987662176809290918757405710442904 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 9 8 0.019891313575576516067085361992039777852691390677272 0 122.24381690163789252812627762718103990693477612483 +B 5 5 9 9 0.0053495981459318280935597570529560642177866174220617 148.41315910257660342111558004055227962348766759389 271.61119081430170616620385934206623250649206587918 +B 5 5 10 7 0.019891313575576516067085361992039777852691390677272 0 67.645666868493653450016016424320161504144039086217 +B 5 6 5 6 0.81551167013511752373840181087933770678506786769174 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 6 8 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 9 0.058565237352433738334045366906051459325523400950192 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 7 7 0.21776205782694529398596692600472093856092867881680 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 8 8 0.047448381733614774500106399764640563205205221894673 0 27.474593022118317968158957115156725710168223409107 +B 5 6 8 9 0.012760835225144470649519068480563328297998736792409 0 47.560129945305985709087486769738443607156131247662 +B 5 6 9 8 0.019891313575576516067085361992039777852691390677272 0 27.474593022118317968158957115156725710168223409107 +B 5 6 9 9 0.0053495981459318280935597570529560642177866174220617 0 122.24381690163789252812627762718103990693477612483 +B 5 6 10 7 0.019891313575576516067085361992039777852691390677272 0 7.3890560989306502272304274605750078131803155705521 +B 5 8 5 8 0.81551167013511752373840181087933770678506786769174 0 2.7182818284590452353602874713526624977572470937000 +B 5 8 6 9 0.53615569877596840528981004420205989703173923455000 0 7.3890560989306502272304274605750078131803155705521 +B 5 8 8 9 0.11682347475055538220128106233279503301961819209920 0 7.3890560989306502272304274605750078131803155705521 +B 5 8 9 9 0.048974744434865892014136571870207205122500937187693 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 5 9 0.62880512726487713594477414956949796603990970044708 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 6 0 0.67840652361131561688041337937919844826201612241818 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 7 4 0.074103547099175155911664140252618784601834555498070 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 1 0.18214924804789702527030221228233260735803335042502 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 2 0.10522419650989145674872002705473163046617162088265 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 3 0.10522419650989145674872002705473163046617162088265 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 5 0.028299144971885888227137841827130653574309891340284 0 27.474593022118317968158957115156725710168223409107 +B 6 0 9 3 0.077667128351853198315698989282130845113657789665252 0 47.560129945305985709087486769738443607156131247662 +B 6 0 9 5 0.020887907892673245671178530399523389494097771969937 0 87.731203791681321190944546078901879401131946924772 +B 6 0 10 4 0.0063461924630285576976529254604396758591929987147261 0 196.92750385796979934716506848462363620671342100200 +B 6 0 11 1 0.0087744579567575522331463948506288817323732116217511 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 11 2 0.0050688394171512309551667391368252496922671519365775 0 27.474593022118317968158957115156725710168223409107 +B 6 0 11 3 0.0050688394171512309551667391368252496922671519365775 0 47.560129945305985709087486769738443607156131247662 +B 6 0 11 5 0.0013632208775449096771870834230216176521610922514038 0 752.31749604207529798369433268633514485882752002136 +B 6 1 6 1 0.67840652361131561688041337937919844826201612241818 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 4 0.27553787260322197623458055329481200582189507780477 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 2 0.18214924804789702527030221228233260735803335042502 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 3 0.18214924804789702527030221228233260735803335042502 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 5 0.10522419650989145674872002705473163046617162088265 0 27.474593022118317968158957115156725710168223409107 +B 6 1 8 6 0.10522419650989145674872002705473163046617162088265 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 3 0.13444634881103315096021944816473830073321780736057 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 5 0.077667128351853198315698989282130845113657789665252 0 67.645666868493653450016016424320161504144039086217 +B 6 1 9 6 0.077667128351853198315698989282130845113657789665252 0 142.32935382482556026905480728176275780392268396339 +B 6 1 10 4 0.023596932115182837345065017705843409892797450362446 0 122.24381690163789252812627762718103990693477612483 +B 6 1 11 2 0.0087744579567575522331463948506288817323732116217511 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 11 3 0.0087744579567575522331463948506288817323732116217511 0 27.474593022118317968158957115156725710168223409107 +B 6 1 11 5 0.0050688394171512309551667391368252496922671519365775 0 474.62249995002254866542970058547939053277047051169 +B 6 1 11 6 0.0050688394171512309551667391368252496922671519365775 0 733.18617392897484045972780470608633993790923434326 +B 6 2 6 2 0.67840652361131561688041337937919844826201612241818 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 7 4 0.47697219810726879655749696633700522704195560011149 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 7 0.074103547099175155911664140252618784601834555498070 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 8 3 0.25907429958590259379188439750993358424989507996739 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 8 5 0.18214924804789702527030221228233260735803335042502 0 27.474593022118317968158957115156725710168223409107 +B 6 2 8 6 0.18214924804789702527030221228233260735803335042502 0 27.474593022118317968158957115156725710168223409107 +B 6 2 9 3 0.19122556927021310360473990704734575635277782505588 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 9 5 0.13444634881103315096021944816473830073321780736057 0 47.560129945305985709087486769738443607156131247662 +B 6 2 9 6 0.13444634881103315096021944816473830073321780736057 0 122.24381690163789252812627762718103990693477612483 +B 6 2 10 4 0.040847671767337116992477109951247143926401902010164 0 47.560129945305985709087486769738443607156131247662 +B 6 2 10 7 0.0063461924630285576976529254604396758591929987147261 0 678.58802389583060138161754350322546153511849730464 +B 6 2 11 3 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 11 5 0.0087744579567575522331463948506288817323732116217511 0 196.92750385796979934716506848462363620671342100200 +B 6 2 11 6 0.0087744579567575522331463948506288817323732116217511 0 400.89302780377785206335291140236970720906144779496 +B 6 3 6 3 0.87984084911536243720332979242139166948207664472488 0 2.7182818284590452353602874713526624977572470937000 +B 6 3 7 4 0.67840652361131561688041337937919844826201612241818 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 7 7 0.074103547099175155911664140252618784601834555498070 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 5 0.25907429958590259379188439750993358424989507996739 0 27.474593022118317968158957115156725710168223409107 +B 6 3 8 6 0.25907429958590259379188439750993358424989507996739 0 27.474593022118317968158957115156725710168223409107 +B 6 3 8 8 0.028299144971885888227137841827130653574309891340284 0 47.560129945305985709087486769738443607156131247662 +B 6 3 9 5 0.19122556927021310360473990704734575635277782505588 0 27.474593022118317968158957115156725710168223409107 +B 6 3 9 6 0.19122556927021310360473990704734575635277782505588 0 102.15827997845022478719774797259932200994686828628 +B 6 3 9 8 0.020887907892673245671178530399523389494097771969937 0 196.92750385796979934716506848462363620671342100200 +B 6 3 10 4 0.058098411419491396639889202196650877960006353657887 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 10 7 0.0063461924630285576976529254604396758591929987147261 148.41315910257660342111558004055227962348766759389 380.80749088059018432242438174778798931207353995641 +B 6 3 11 5 0.012480076496363873511126050564432513772479271306925 0 47.560129945305985709087486769738443607156131247662 +B 6 3 11 6 0.012480076496363873511126050564432513772479271306925 0 196.92750385796979934716506848462363620671342100200 +B 6 3 11 8 0.0013632208775449096771870834230216176521610922514038 403.42879349273512260838718054338827960589989735717 1604.5338064313340034624547569691512127579169542285 +B 6 5 6 5 0.87984084911536243720332979242139166948207664472488 0 2.7182818284590452353602874713526624977572470937000 +B 6 5 7 7 0.27553787260322197623458055329481200582189507780477 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 6 0.33599935112390816231346658273753456114175680950975 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 8 0.10522419650989145674872002705473163046617162088265 0 27.474593022118317968158957115156725710168223409107 +B 6 5 8 9 0.028299144971885888227137841827130653574309891340284 0 27.474593022118317968158957115156725710168223409107 +B 6 5 9 6 0.24800478972939305624926036592995321197233784275120 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 9 8 0.077667128351853198315698989282130845113657789665252 0 47.560129945305985709087486769738443607156131247662 +B 6 5 9 9 0.020887907892673245671178530399523389494097771969937 0 122.24381690163789252812627762718103990693477612483 +B 6 5 10 7 0.023596932115182837345065017705843409892797450362446 0 47.560129945305985709087486769738443607156131247662 +B 6 5 11 6 0.016185695035970194789105706278236145812585330992098 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 11 8 0.0050688394171512309551667391368252496922671519365775 0 196.92750385796979934716506848462363620671342100200 +B 6 5 11 9 0.0013632208775449096771870834230216176521610922514038 0 400.89302780377785206335291140236970720906144779496 +B 6 6 6 6 0.87984084911536243720332979242139166948207664472488 0 2.7182818284590452353602874713526624977572470937000 +B 6 6 7 7 0.27553787260322197623458055329481200582189507780477 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 8 8 0.10522419650989145674872002705473163046617162088265 0 27.474593022118317968158957115156725710168223409107 +B 6 6 8 9 0.028299144971885888227137841827130653574309891340284 0 27.474593022118317968158957115156725710168223409107 +B 6 6 9 8 0.077667128351853198315698989282130845113657789665252 0 27.474593022118317968158957115156725710168223409107 +B 6 6 9 9 0.020887907892673245671178530399523389494097771969937 0 102.15827997845022478719774797259932200994686828628 +B 6 6 10 7 0.023596932115182837345065017705843409892797450362446 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 11 8 0.0050688394171512309551667391368252496922671519365775 0 47.560129945305985709087486769738443607156131247662 +B 6 6 11 9 0.0013632208775449096771870834230216176521610922514038 0 196.92750385796979934716506848462363620671342100200 +B 6 8 6 8 0.87984084911536243720332979242139166948207664472488 0 2.7182818284590452353602874713526624977572470937000 +B 6 8 8 9 0.25907429958590259379188439750993358424989507996739 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 9 9 0.19122556927021310360473990704734575635277782505588 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 11 9 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 6 9 6 9 0.67840652361131561688041337937919844826201612241818 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 7 4 0.77105595210022434753537748474663651727018658831526 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 8 5 0.32692302990159208397902888797252141214701233487889 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 6 0.32692302990159208397902888797252141214701233487889 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 8 0.035710382051098530783097153254737917654522010710632 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 9 0.035710382051098530783097153254737917654522010710632 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 5 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 6 0.23427360141268335332406478260508334313884186898183 0 27.474593022118317968158957115156725710168223409107 +B 7 4 9 8 0.025590120749376296553251354520810633175610212947775 0 47.560129945305985709087486769738443607156131247662 +B 7 4 9 9 0.025590120749376296553251354520810633175610212947775 0 67.645666868493653450016016424320161504144039086217 +B 7 4 10 7 0.011048405319731608579725749581726919540705439692564 0 142.32935382482556026905480728176275780392268396339 +B 7 4 11 5 0.030727410465614882762631311217319859447490104247309 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 11 6 0.030727410465614882762631311217319859447490104247309 0 47.560129945305985709087486769738443607156131247662 +B 7 4 11 8 0.0033564095117383688853734202379888409349738548367327 148.41315910257660342111558004055227962348766759389 380.80749088059018432242438174778798931207353995641 +B 7 4 11 9 0.0033564095117383688853734202379888409349738548367327 0 678.58802389583060138161754350322546153511849730464 +B 7 7 7 7 0.31316785630067304260613245423990955181055976494570 0 2.7182818284590452353602874713526624977572470937000 +B 7 7 8 8 0.13278126466792971518174106482733241581868545210005 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 9 0.035710382051098530783097153254737917654522010710632 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 9 8 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 9 9 0.025590120749376296553251354520810633175610212947775 0 27.474593022118317968158957115156725710168223409107 +B 7 7 11 8 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 11 9 0.0033564095117383688853734202379888409349738548367327 0 47.560129945305985709087486769738443607156131247662 +B 8 0 8 0 0.32692302990159208397902888797252141214701233487889 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 9 3 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 5 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 10 4 0.011048405319731608579725749581726919540705439692564 0 27.474593022118317968158957115156725710168223409107 +B 8 0 11 1 0.021603743480989378136878680890876186609984687777117 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 11 2 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 11 3 0.012480076496363873511126050564432513772479271306925 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 11 5 0.0033564095117383688853734202379888409349738548367327 0 102.15827997845022478719774797259932200994686828628 +B 8 1 8 1 0.32692302990159208397902888797252141214701233487889 0 2.7182818284590452353602874713526624977572470937000 +B 8 1 9 3 0.16471244119158100106712697324365910648443131697048 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 9 5 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 9 6 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 10 4 0.041081084733808287839555192305947434609160425656318 0 27.474593022118317968158957115156725710168223409107 +B 8 1 11 2 0.021603743480989378136878680890876186609984687777117 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 11 3 0.021603743480989378136878680890876186609984687777117 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 11 5 0.012480076496363873511126050564432513772479271306925 0 102.15827997845022478719774797259932200994686828628 +B 8 1 11 6 0.012480076496363873511126050564432513772479271306925 0 122.24381690163789252812627762718103990693477612483 +B 8 2 8 2 0.32692302990159208397902888797252141214701233487889 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 3 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 5 0.16471244119158100106712697324365910648443131697048 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 6 0.16471244119158100106712697324365910648443131697048 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 4 0.071113764147884967099384635030167949677615411620077 0 27.474593022118317968158957115156725710168223409107 +B 8 2 10 7 0.011048405319731608579725749581726919540705439692564 0 67.645666868493653450016016424320161504144039086217 +B 8 2 11 3 0.030727410465614882762631311217319859447490104247309 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 11 5 0.021603743480989378136878680890876186609984687777117 0 102.15827997845022478719774797259932200994686828628 +B 8 2 11 6 0.021603743480989378136878680890876186609984687777117 0 122.24381690163789252812627762718103990693477612483 +B 8 3 8 3 0.42399391251842326837767279954511591031117577626830 0 2.7182818284590452353602874713526624977572470937000 +B 8 3 9 5 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 6 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 8 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 10 4 0.10114644356196164635921407775438846474607039758383 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 10 7 0.011048405319731608579725749581726919540705439692564 0 47.560129945305985709087486769738443607156131247662 +B 8 3 11 5 0.030727410465614882762631311217319859447490104247309 0 27.474593022118317968158957115156725710168223409107 +B 8 3 11 6 0.030727410465614882762631311217319859447490104247309 0 47.560129945305985709087486769738443607156131247662 +B 8 3 11 8 0.0033564095117383688853734202379888409349738548367327 0 196.92750385796979934716506848462363620671342100200 +B 8 5 8 5 0.42399391251842326837767279954511591031117577626830 0 2.7182818284590452353602874713526624977572470937000 +B 8 5 9 6 0.30383476163378570558100259196650757979325242099318 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 8 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 9 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 10 7 0.041081084733808287839555192305947434609160425656318 0 27.474593022118317968158957115156725710168223409107 +B 8 5 11 6 0.039851077450240387388383941543763532284995520717500 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 11 8 0.012480076496363873511126050564432513772479271306925 0 102.15827997845022478719774797259932200994686828628 +B 8 5 11 9 0.0033564095117383688853734202379888409349738548367327 0 122.24381690163789252812627762718103990693477612483 +B 8 6 8 6 0.42399391251842326837767279954511591031117577626830 0 2.7182818284590452353602874713526624977572470937000 +B 8 6 9 8 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 9 9 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 10 7 0.041081084733808287839555192305947434609160425656318 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 11 8 0.012480076496363873511126050564432513772479271306925 0 27.474593022118317968158957115156725710168223409107 +B 8 6 11 9 0.0033564095117383688853734202379888409349738548367327 0 47.560129945305985709087486769738443607156131247662 +B 8 8 8 8 0.42399391251842326837767279954511591031117577626830 0 2.7182818284590452353602874713526624977572470937000 +B 8 8 9 9 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 11 9 0.030727410465614882762631311217319859447490104247309 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 8 9 0.32692302990159208397902888797252141214701233487889 0 2.7182818284590452353602874713526624977572470937000 +B 9 0 9 0 0.44437315656372005510181260824480972226623601176154 0 2.7182818284590452353602874713526624977572470937000 +B 9 0 10 4 0.033997952145685668713303834355901508897228713610787 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 1 0.064699121385769054147518492010044297238809673542392 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 2 0.037375466194201966561575536592143802569054365971145 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 3 0.037375466194201966561575536592143802569054365971145 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 5 0.010051811002634878975632581174243307899299058399900 0 27.474593022118317968158957115156725710168223409107 +B 9 3 9 3 0.57631765289318328790680699789475122217760352006005 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 4 0.31124599871299834813696190339411484387346454036352 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 7 0.033997952145685668713303834355901508897228713610787 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 5 0.092022776577336141733461447427944791908564981113642 0 27.474593022118317968158957115156725710168223409107 +B 9 3 11 6 0.092022776577336141733461447427944791908564981113642 0 27.474593022118317968158957115156725710168223409107 +B 9 3 11 8 0.010051811002634878975632581174243307899299058399900 0 47.560129945305985709087486769738443607156131247662 +B 9 5 9 5 0.57631765289318328790680699789475122217760352006005 0 2.7182818284590452353602874713526624977572470937000 +B 9 5 10 7 0.12641396766812322852118985736863928722264065586170 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 11 6 0.11934643176890322931940440284584528657832028868488 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 11 8 0.037375466194201966561575536592143802569054365971145 0 27.474593022118317968158957115156725710168223409107 +B 9 5 11 9 0.010051811002634878975632581174243307899299058399900 0 27.474593022118317968158957115156725710168223409107 +B 9 6 9 6 0.57631765289318328790680699789475122217760352006005 0 2.7182818284590452353602874713526624977572470937000 +B 9 6 10 7 0.12641396766812322852118985736863928722264065586170 0 7.3890560989306502272304274605750078131803155705521 +B 9 6 11 8 0.037375466194201966561575536592143802569054365971145 0 27.474593022118317968158957115156725710168223409107 +B 9 6 11 9 0.010051811002634878975632581174243307899299058399900 0 27.474593022118317968158957115156725710168223409107 +B 9 8 9 8 0.57631765289318328790680699789475122217760352006005 0 2.7182818284590452353602874713526624977572470937000 +B 9 8 11 9 0.092022776577336141733461447427944791908564981113642 0 7.3890560989306502272304274605750078131803155705521 +B 9 9 9 9 0.44437315656372005510181260824480972226623601176154 0 2.7182818284590452353602874713526624977572470937000 +B 10 4 10 4 0.63792879424950264057052677989594163887741511691725 0 2.7182818284590452353602874713526624977572470937000 +B 10 4 11 5 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 6 0.23427360141268335332406478260508334313884186898183 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 8 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 9 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 10 7 10 7 0.25909766006400268163549848266362211658969942564290 0 2.7182818284590452353602874713526624977572470937000 +B 10 7 11 8 0.095151280970478648810189163882234869830020764959125 0 7.3890560989306502272304274605750078131803155705521 +B 10 7 11 9 0.025590120749376296553251354520810633175610212947775 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 11 0 0.24339726839730885794981741293152701597634728545202 0 2.7182818284590452353602874713526624977572470937000 +B 11 1 11 1 0.24339726839730885794981741293152701597634728545202 0 2.7182818284590452353602874713526624977572470937000 +B 11 2 11 2 0.24339726839730885794981741293152701597634728545202 0 2.7182818284590452353602874713526624977572470937000 +B 11 3 11 3 0.31566745284092080188064170959927127302945751585588 0 2.7182818284590452353602874713526624977572470937000 +B 11 5 11 5 0.31566745284092080188064170959927127302945751585588 0 2.7182818284590452353602874713526624977572470937000 +B 11 6 11 6 0.31566745284092080188064170959927127302945751585588 0 2.7182818284590452353602874713526624977572470937000 +B 11 8 11 8 0.31566745284092080188064170959927127302945751585588 0 2.7182818284590452353602874713526624977572470937000 +B 11 9 11 9 0.24339726839730885794981741293152701597634728545202 0 2.7182818284590452353602874713526624977572470937000 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 1 0E+0 +P 0 6 0 0E+0 +P 0 5 0 0E+0 +P 0 4 1 0E+0 +P 0 3 0 0E+0 +P 0 2 0 0E+0 +P 0 1 0 0E+0 +P 0 0 0 0E+0 +P 1 9 0 0E+0 +P 1 8 0 0E+0 +P 1 7 1 0E+0 +P 1 6 0 0E+0 +P 1 5 0 0E+0 +P 1 4 1 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 9 0 0E+0 +P 2 8 0 0E+0 +P 2 7 1 0E+0 +P 2 6 0 0E+0 +P 2 5 0 0E+0 +P 2 4 1 0E+0 +P 2 3 0 0E+0 +P 2 2 0 0E+0 +P 2 1 0 0E+0 +P 2 0 0 0E+0 +P 3 9 0 0E+0 +P 3 8 0 0E+0 +P 3 7 1 0E+0 +P 3 6 0 0E+0 +P 3 5 0 0E+0 +P 3 4 1 0E+0 +P 3 3 0 0E+0 +P 3 2 0 0E+0 +P 3 1 0 0E+0 +P 3 0 0 0E+0 +P 4 9 0 0E+0 +P 4 8 0 0E+0 +P 4 7 1 0E+0 +P 4 6 0 0E+0 +P 4 5 0 0E+0 +P 4 4 1 3.5020952403433299004983682717362182959262569839776E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 0 0E+0 +P 5 9 1 0E+0 +P 5 8 1 0E+0 +P 5 7 0 0E+0 +P 5 6 1 0E+0 +P 5 5 1 4.2960460011103591139861036928632289586591870736370E+0 +P 5 4 0 0E+0 +P 5 3 1 0E+0 +P 5 2 1 7.2684965378197797127166071620893066858645768976523E+0 +P 5 1 1 0E+0 +P 5 0 1 0E+0 +P 6 9 1 0E+0 +P 6 8 1 0E+0 +P 6 7 0 0E+0 +P 6 6 1 4.2960460011103591139861036928632289586591870736370E+0 +P 6 5 1 0E+0 +P 6 4 0 0E+0 +P 6 3 1 8.7603175634228600559542067129517878373394534928670E+0 +P 6 2 1 0E+0 +P 6 1 1 0E+0 +P 6 0 1 0E+0 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 1 4.2960460011103591139861036928632289586591870736370E+0 +P 7 6 0 0E+0 +P 7 5 0 0E+0 +P 7 4 1 9.2584529023018880511442317485180754402207131547235E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +P 8 9 1 0E+0 +P 8 8 1 4.2960460011103591139861036928632289586591870736370E+0 +P 8 7 0 0E+0 +P 8 6 1 0E+0 +P 8 5 1 9.2584529023018880511442317485180754402207131547235E+0 +P 8 4 0 0E+0 +P 8 3 1 0E+0 +P 8 2 1 0E+0 +P 8 1 1 0E+0 +P 8 0 1 0E+0 +P 9 9 1 2.5500778435032294684452055906063619082198696145711E+0 +P 9 8 1 0E+0 +P 9 7 0 0E+0 +P 9 6 1 9.2584529023018880511442317485180754402207131547235E+0 +P 9 5 1 0E+0 +P 9 4 0 0E+0 +P 9 3 1 0E+0 +P 9 2 0 0E+0 +P 9 1 0 0E+0 +P 9 0 1 0E+0 +P 10 9 0 0E+0 +P 10 8 0 0E+0 +P 10 7 1 4.1481382901663585048372485000828674287333518597356E+0 +P 10 6 0 0E+0 +P 10 5 0 0E+0 +P 10 4 1 0E+0 +P 10 3 0 0E+0 +P 10 2 0 0E+0 +P 10 1 0 0E+0 +P 10 0 0 0E+0 +P 11 9 1 0E+0 +P 11 8 1 1.0480978927710785157910339725213965616973709535105E+0 +P 11 7 0 0E+0 +P 11 6 1 0E+0 +P 11 5 1 0E+0 +P 11 4 0 0E+0 +P 11 3 1 0E+0 +P 11 2 1 0E+0 +P 11 1 1 0E+0 +P 11 0 1 0E+0 +END +CASE random06 13 11 6 +Z 2.5150136397711549006246480727112924889328760111822E-1 +S 0 0 6 +S 1 1 6 +B 0 0 0 0 0.046729393361575581163417810823464253799132512611304 0 2.7182818284590452353602874713526624977572470937000 +B 0 0 1 1 0.030882999876246950515976910702814984295798243422244 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 1 3 0.015853005880646567252489310842023896357395341113773 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 2 2 0.0034373297264915744931036693550953455804132477925726 0 27.474593022118317968158957115156725710168223409107 +B 0 0 2 4 0.00066352605966593570520956714377171482603815969863381 0 47.560129945305985709087486769738443607156131247662 +B 0 0 2 5 0.00017844964160258893841979848919338350054363859425489 0 47.560129945305985709087486769738443607156131247662 +B 0 0 3 1 0.0046608323101952050932054722382797769584187852125004 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 3 3 0.0023925202317881613336630295471741533922529162845302 0 102.15827997845022478719774797259932200994686828628 +B 0 0 4 4 0.00046184091966346339518652299049314719424411944910338 0 420.02434991687830958731943938261851212997973347307 +B 0 0 5 2 0.00052433979410189638506118410071219765925963324920310 0 47.560129945305985709087486769738443607156131247662 +B 0 0 5 4 0.00010121610237886270475870577805320138413821301891487 0 420.02434991687830958731943938261851212997973347307 +B 0 0 5 5 0.000027221202439302280203104128826486698148282049992599 403.42879349273512260838718054338827960589989735717 1287.6219513029931288792950672334649356139537112022 +B 0 2 0 2 0.11852666887923602260431736696687762770955667326735 0 2.7182818284590452353602874713526624977572470937000 +B 0 2 1 3 0.084007604289244019748180443372117754697109738921813 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 1 6 0.0097717248919536502390946798101695994997806685343951 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 1 7 0.0075345466402267836765714376029354017295836111021214 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 4 0.011485431043859607231096956556773523446354435009720 0 27.474593022118317968158957115156725710168223409107 +B 0 2 2 5 0.0026038317319193227723686417620850401280162441161494 0 27.474593022118317968158957115156725710168223409107 +B 0 2 3 3 0.012678345948981180537566929734386244443033573859959 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 3 6 0.0014747392185105008583843313937669992987153357267336 0 176.84196693478213160623653883004191830972551316345 +B 0 2 3 7 0.0011371064522281550373183952593423819305582636341902 0 196.92750385796979934716506848462363620671342100200 +B 0 2 4 4 0.0079943235970235011732543287174837759644120353511435 0 47.560129945305985709087486769738443607156131247662 +B 0 2 5 4 0.0017520194534423396919063559232866028029875627735769 0 47.560129945305985709087486769738443607156131247662 +B 0 2 5 5 0.00039719570213710440298111237496006012809793689460398 0 251.52565389111403842527532968748451460950415804062 +B 0 4 0 4 0.17316373647984517714469883132854205154579578086291 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 6 0.052970945579291752007032192622997973956059876307482 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 7 0.030252746109759267841801815112966687842821743704801 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 8 0.013615827628919700689966068634789698587198283681499 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 9 0.0030601901367730505515249531884670061891894962375745 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 5 0.017729881530528694714775678856284733208909576341686 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 6 0.0079943235970235011732543287174837759644120353511435 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 7 0.0045657150246258281052863619884130789474851494926666 0 47.560129945305985709087486769738443607156131247662 +B 0 4 3 8 0.0020548874655058155125970934127495360240958441919867 0 67.645666868493653450016016424320161504144039086217 +B 0 4 3 9 0.00046184091966346339518652299049314719424411944910338 0 87.731203791681321190944546078901879401131946924772 +B 0 4 5 5 0.0027045652209388701675602527216554410435739340055070 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 0 5 0.18248311308899896218691145311130337999988721737004 0 2.7182818284590452353602874713526624977572470937000 +B 0 5 1 6 0.10053823989456143260074968328202260594804198498153 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 7 0.077820040425028948435519305771991319834803852378853 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 8 0.024171465121066350828407184081112390985207071125423 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 9 0.0052973683884999171140481953957012039593865536698482 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 6 0.015173133399878574727120104933943882042988941507204 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 7 0.011744524827480901659152138204873185026062055648728 0 27.474593022118317968158957115156725710168223409107 +B 0 5 3 8 0.0036479340113481676300076638350059248539475689348702 0 47.560129945305985709087486769738443607156131247662 +B 0 5 3 9 0.00079947368594580921625245912491776456240119154164673 0 67.645666868493653450016016424320161504144039086217 +B 1 1 1 1 0.41839453959996411141767694978925033137359882285075 0 2.7182818284590452353602874713526624977572470937000 +B 1 1 2 2 0.051364021094052568118684046005612072638839318592649 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 2 4 0.020327362384100300377535795256492291897789455978320 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 2 5 0.012008903143680516801617922017403797269977725966669 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 3 0.027624685449931052803662528291985896491957735112422 0 27.474593022118317968158957115156725710168223409107 +B 1 1 3 6 0.0052554457515931807841496721078224214046723316946508 0 67.645666868493653450016016424320161504144039086217 +B 1 1 4 4 0.010932496715148176824510285433437971373959197140896 0 102.15827997845022478719774797259932200994686828628 +B 1 1 5 2 0.010419544620163786608788925033674840544298047109725 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 5 4 0.0041235451364592736665291113846525291230593962204728 0 102.15827997845022478719774797259932200994686828628 +B 1 1 5 5 0.0024360885203221128630010484475297497445030424470775 0 399.93881299369064184639090972803679423299182563451 +B 1 1 6 3 0.0031337999557909394711074310160945975081236080438004 0 47.560129945305985709087486769738443607156131247662 +B 1 1 6 6 0.00059618835095353080728603624462487574411889546372571 403.42879349273512260838718054338827960589989735717 1401.5224972956131609632289157257380547316385495960 +B 1 3 1 3 0.51341428536807012211359485220471830802316602815455 0 2.7182818284590452353602874713526624977572470937000 +B 1 3 2 4 0.12273246847312838839107147177047773361299483183035 0 7.3890560989306502272304274605750078131803155705521 +B 1 3 2 5 0.081770426037517153185657201164883556926912681489541 0 7.3890560989306502272304274605750078131803155705521 +B 1 3 3 6 0.028488924317936362298190973262470026653925461507902 0 47.560129945305985709087486769738443607156131247662 +B 1 3 3 7 0.016270583522821060722941301640754796553384004219931 0 47.560129945305985709087486769738443607156131247662 +B 1 3 3 8 0.0073228876434859119151361591670894065184698428528222 0 47.560129945305985709087486769738443607156131247662 +B 1 3 4 4 0.066008185571287576518468433098099847117517559767297 0 7.3890560989306502272304274605750078131803155705521 +B 1 3 5 4 0.024897124570075383000925674446806158871537060778113 0 7.3890560989306502272304274605750078131803155705521 +B 1 3 5 5 0.016587692796628939267167049221944706972145994955057 0 47.560129945305985709087486769738443607156131247662 +B 1 3 6 6 0.0032318409536244409605809216920512464079082162527492 0 217.01304078115746708809359813920535410370132884056 +B 1 3 6 7 0.0018457677650999891767183475886151785005108334737265 0 346.29487777063361298524265019950882880627071075635 +B 1 3 6 8 0.00083072312316502571118978968002728979490894844169664 0 475.57671476010975888239170225981230350884009267213 +B 1 6 1 6 0.41188250605738609685555606021484825420196564330405 0 2.7182818284590452353602874713526624977572470937000 +B 1 6 3 7 0.060612937526186697488184875820144181061962796142796 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 3 8 0.028488924317936362298190973262470026653925461507902 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 3 9 0.0073228876434859119151361591670894065184698428528222 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 6 7 0.0068760537123291274719159996466609033628597674516444 0 7.3890560989306502272304274605750078131803155705521 +B 1 6 6 8 0.0032318409536244409605809216920512464079082162527492 0 27.474593022118317968158957115156725710168223409107 +B 1 6 6 9 0.00083072312316502571118978968002728979490894844169664 0 47.560129945305985709087486769738443607156131247662 +B 1 7 1 7 0.41188250605738609685555606021484825420196564330405 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 3 8 0.040707265113051663873440644884185256754466918795875 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 9 0.016270583522821060722941301640754796553384004219931 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 6 8 0.0046179141421488927444434957954873143153055990317719 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 6 9 0.0018457677650999891767183475886151785005108334737265 0 27.474593022118317968158957115156725710168223409107 +B 1 8 1 8 0.30414806401526423797605240924304370891672290746881 0 2.7182818284590452353602874713526624977572470937000 +B 1 8 3 9 0.028488924317936362298190973262470026653925461507902 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 6 9 0.0032318409536244409605809216920512464079082162527492 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 1 9 0.26223294558247502496041271453221527195391611761927 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 2 0 0.074785171074994790505935291363176664568858418112029 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 3 1 0.047912569980766533352401435869317358881755670510503 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 3 3 0.024594704423328423806627792475009486696867436760065 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 4 0.0047476467529102113550530161255897855677458500109699 0 47.560129945305985709087486769738443607156131247662 +B 2 0 5 2 0.0066684302345235879577919608702774487312684855059644 0 27.474593022118317968158957115156725710168223409107 +B 2 0 5 4 0.0012872425951952018887603480631098479036687943264343 0 47.560129945305985709087486769738443607156131247662 +B 2 0 5 5 0.00034619285319979884778739180374867388405684026332678 0 176.84196693478213160623653883004191830972551316345 +B 2 0 6 1 0.0086100594240512934275620371876664037356092865842054 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 6 3 0.0044197559572955939605877781240355867400970109048686 0 102.15827997845022478719774797259932200994686828628 +B 2 0 7 4 0.00085316902606916067716429997974290282221536985333265 0 420.02434991687830958731943938261851212997973347307 +B 2 2 2 2 0.18968868567362153065467411881254712183555169772146 0 2.7182818284590452353602874713526624977572470937000 +B 2 2 3 3 0.13033125782967449689262457167713520468786213047378 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 6 0.015160070413969902637649617260254298735045791014989 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 7 0.011689262526950005543450750215366127679279144013649 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 4 4 0.082180298131180720934115269683449939673275606489464 0 27.474593022118317968158957115156725710168223409107 +B 2 2 5 4 0.022281771527220681811274549793781364633923492646325 0 27.474593022118317968158957115156725710168223409107 +B 2 2 5 5 0.0050514415631768140526521731005545439821166105788646 0 102.15827997845022478719774797259932200994686828628 +B 2 2 6 3 0.023420991092218903656832682116568947370788453184880 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 6 6 0.0027243186326569592278166427377573020551072737367343 0 454.53696302683488092450117093089767263578256267313 +B 2 2 6 7 0.0021006020971276930442658618184191689774766391089337 0 474.62249995002254866542970058547939053277047051169 +B 2 2 7 4 0.014768092187076566175095797813441409397937600628941 0 47.560129945305985709087486769738443607156131247662 +B 2 4 2 4 0.27712920551797846431053042668004958697704065804896 0 2.7182818284590452353602874713526624977572470937000 +B 2 4 3 6 0.082180298131180720934115269683449939673275606489464 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 7 0.046934780329065363238783009949408033676277375251556 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 8 0.021123896536308526712428925430121315641100789758725 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 9 0.0047476467529102113550530161255897855677458500109699 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 5 0.034396024664580195677671438428840027748202716733372 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 6 6 0.014768092187076566175095797813441409397937600628941 0 27.474593022118317968158957115156725710168223409107 +B 2 4 6 7 0.0084343471421021296096808298159302891877071198689376 0 47.560129945305985709087486769738443607156131247662 +B 2 4 6 8 0.0037960394217663277770369972046974536624663762770680 0 67.645666868493653450016016424320161504144039086217 +B 2 4 6 9 0.00085316902606916067716429997974290282221536985333265 0 87.731203791681321190944546078901879401131946924772 +B 2 5 2 5 0.29204382614305502063953220013456158695599018269226 0 2.7182818284590452353602874713526624977572470937000 +B 2 5 3 6 0.15597725201547547071257541767163990426397169563479 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 7 0.12073173421336011301724315793759799826697346439688 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 8 0.037500146319706842069804834734652845714455729506481 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 9 0.0082184546399301084492518831704779566235124970123097 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 6 6 0.028029667563075323543381453717532034084078489788417 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 6 7 0.021695922518100886977966485720020913873848009028414 0 27.474593022118317968158957115156725710168223409107 +B 2 5 6 8 0.0067389098174634948769096944296520045027173827008036 0 47.560129945305985709087486769738443607156131247662 +B 2 5 6 9 0.0014768855615984268607150808990810358998460044811332 0 67.645666868493653450016016424320161504144039086217 +B 3 1 3 1 0.46124777565031448500943818890915423891244863250889 0 2.7182818284590452353602874713526624977572470937000 +B 3 1 4 4 0.065229694795963474640526789667015628485253032714740 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 2 0.090290055905611646329918903406270201447827770751349 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 4 0.035732379338317794844262160094463219798648550218463 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 5 0.021109806302402956649024379868133353772557046435141 0 27.474593022118317968158957115156725710168223409107 +B 3 1 6 3 0.034876345111472217592173215331967725466333535917903 0 27.474593022118317968158957115156725710168223409107 +B 3 1 6 6 0.0066350344542164454605238808098538868976300598515149 0 142.32935382482556026905480728176275780392268396339 +B 3 1 7 4 0.013802348231570391579708716487306328755232548838754 0 102.15827997845022478719774797259932200994686828628 +B 3 1 8 2 0.027590600447099370454491721314387482430159096851979 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 8 4 0.010919007541410123867660183061018384880098435367044 0 102.15827997845022478719774797259932200994686828628 +B 3 1 8 5 0.0064506797051286485120941001394244521921131514111467 0 420.02434991687830958731943938261851212997973347307 +B 3 3 3 3 0.56599973159195230420368813736405779764105512715290 0 2.7182818284590452353602874713526624977572470937000 +B 3 3 4 4 0.39384359410640353905760326134666186740652739819268 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 4 0.21574481911338535481988508755428638563815316096423 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 5 0.14373984320335833082963591657035711930235131666592 0 27.474593022118317968158957115156725710168223409107 +B 3 3 6 6 0.035967452305214996939938175917301470748488849809298 0 122.24381690163789252812627762718103990693477612483 +B 3 3 6 7 0.020541717556764325353803543977062841751128557943426 0 122.24381690163789252812627762718103990693477612483 +B 3 3 6 8 0.0092451932938620165011438101682173063236471675168708 0 122.24381690163789252812627762718103990693477612483 +B 3 3 7 4 0.083335763744309791046107032323351103022725039298310 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 8 4 0.065926740691265705846823492554861043181072951318959 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 8 5 0.043923647431323473055158168757323979860683144938196 0 67.645666868493653450016016424320161504144039086217 +B 3 6 3 6 0.45406875990755118159227487575168645754472119173597 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 6 7 0.076524227984962213852100408988840403495412727970386 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 8 0.035967452305214996939938175917301470748488849809298 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 9 0.0092451932938620165011438101682173063236471675168708 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 3 7 0.45406875990755118159227487575168645754472119173597 0 2.7182818284590452353602874713526624977572470937000 +B 3 7 6 8 0.051393187053665668526072807857540099745849141675168 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 9 0.020541717556764325353803543977062841751128557943426 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 3 8 0.33529982998707882959135128016820040098999996587001 0 2.7182818284590452353602874713526624977572470937000 +B 3 8 6 9 0.035967452305214996939938175917301470748488849809298 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 3 9 0.28909163816476571986335331289175830867792090354865 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 4 0 0.18777629057366756975990551077022844521583120113516 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 5 2 0.055280259174418474074173268845403982462648447568410 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 4 0.010671042776205278612574158054218120184628103516691 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 5 0.0028698854117326663782647195029580033418309277525133 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 6 1 0.074372726995430485134181052438303163916374156608107 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 6 3 0.038177355928596490014498642879212708041882447841595 0 27.474593022118317968158957115156725710168223409107 +B 4 0 7 4 0.0073695782957723975266862630508995606988629140128138 0 122.24381690163789252812627762718103990693477612483 +B 4 0 8 2 0.012780960660435721466123922457241270821390914985392 0 27.474593022118317968158957115156725710168223409107 +B 4 0 8 4 0.0024671768903648809595241320459386829294242055838148 0 122.24381690163789252812627762718103990693477612483 +B 4 0 8 5 0.00066352605966593570520956714377171482603815969863381 0 474.62249995002254866542970058547939053277047051169 +B 4 0 9 1 0.014142789099577822604410902976582797575701648016060 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 9 3 0.0072598426209493590218741272785706406388119161267136 0 122.24381690163789252812627762718103990693477612483 +B 4 4 4 4 0.69583706868316724147040700680472549993913095890443 0 2.7182818284590452353602874713526624977572470937000 +B 4 4 5 5 0.28513774473993976407375761783835417242329367722198 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 6 0.12756512288459812087112018525145074237931401773273 0 27.474593022118317968158957115156725710168223409107 +B 4 4 6 7 0.072854944024195161830773253881952460070560640810504 0 27.474593022118317968158957115156725710168223409107 +B 4 4 6 8 0.032789762494586587382628613148435399510410272903864 0 27.474593022118317968158957115156725710168223409107 +B 4 4 6 9 0.0073695782957723975266862630508995606988629140128138 0 27.474593022118317968158957115156725710168223409107 +B 4 4 8 5 0.065924696315696490430601608311291263574598290125607 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 9 6 0.024257906121009205606011062538797957369522735109740 0 47.560129945305985709087486769738443607156131247662 +B 4 4 9 7 0.013854166033995856816369525579957644178699327949763 0 122.24381690163789252812627762718103990693477612483 +B 4 4 9 8 0.0062353326860621548260772482650510248768273372759076 0 196.92750385796979934716506848462363620671342100200 +B 4 4 9 9 0.0014014060772080996351342305940780994639067630881736 0 271.61119081430170616620385934206623250649206587918 +B 5 0 5 0 0.11549179030157689400439125673911007842626314314849 0 2.7182818284590452353602874713526624977572470937000 +B 5 0 6 1 0.074372726995430485134181052438303163916374156608107 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 6 3 0.038177355928596490014498642879212708041882447841595 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 7 4 0.0073695782957723975266862630508995606988629140128138 0 47.560129945305985709087486769738443607156131247662 +B 5 0 8 2 0.012780960660435721466123922457241270821390914985392 0 27.474593022118317968158957115156725710168223409107 +B 5 0 8 4 0.0024671768903648809595241320459386829294242055838148 0 47.560129945305985709087486769738443607156131247662 +B 5 0 8 5 0.00066352605966593570520956714377171482603815969863381 0 176.84196693478213160623653883004191830972551316345 +B 5 0 9 1 0.014142789099577822604410902976582797575701648016060 0 7.3890560989306502272304274605750078131803155705521 +B 5 0 9 3 0.0072598426209493590218741272785706406388119161267136 0 102.15827997845022478719774797259932200994686828628 +B 5 0 10 2 0.0015691492888053095445018239086333898474199647572455 0 47.560129945305985709087486769738443607156131247662 +B 5 0 10 4 0.00030290124238133501478174993133176901593225326844532 0 420.02434991687830958731943938261851212997973347307 +B 5 0 10 5 0.000081462690660773644502315761951340372604873287687543 0 678.58802389583060138161754350322546153511849730464 +B 5 2 5 2 0.29293890210441237630806705638961428926609085298616 0 2.7182818284590452353602874713526624977572470937000 +B 5 2 6 3 0.20230789250980536713553943698770755496814257860828 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 6 0.023532358597802105422296352243231486293279438826007 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 7 0.018144765163792202790426322512454177761807263888276 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 4 0.12756512288459812087112018525145074237931401773273 0 27.474593022118317968158957115156725710168223409107 +B 5 2 8 4 0.042706069542402580947348095687750593040591911371105 0 27.474593022118317968158957115156725710168223409107 +B 5 2 8 5 0.0096817802131606619767823916546065553429683891245388 0 102.15827997845022478719774797259932200994686828628 +B 5 2 9 3 0.038471062881989405390798738155091580211282137606645 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 9 6 0.0044749358818697122225248676346369467498604996405918 0 454.53696302683488092450117093089767263578256267313 +B 5 2 9 7 0.0034504259469825080267279886211173309878759207897857 0 474.62249995002254866542970058547939053277047051169 +B 5 2 10 4 0.0052431269002784457497489837625763502849299624321531 0 47.560129945305985709087486769738443607156131247662 +B 5 2 10 5 0.0011886554492635804958994866088534835892417731914764 0 196.92750385796979934716506848462363620671342100200 +B 5 4 5 4 0.42797452529765714109440126576530747660666092930547 0 2.7182818284590452353602874713526624977572470937000 +B 5 4 6 6 0.12756512288459812087112018525145074237931401773273 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 7 0.072854944024195161830773253881952460070560640810504 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 8 0.032789762494586587382628613148435399510410272903864 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 9 0.0073695782957723975266862630508995606988629140128138 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 8 5 0.065924696315696490430601608311291263574598290125607 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 9 6 0.024257906121009205606011062538797957369522735109740 0 27.474593022118317968158957115156725710168223409107 +B 5 4 9 7 0.013854166033995856816369525579957644178699327949763 0 47.560129945305985709087486769738443607156131247662 +B 5 4 9 8 0.0062353326860621548260772482650510248768273372759076 0 67.645666868493653450016016424320161504144039086217 +B 5 4 9 9 0.0014014060772080996351342305940780994639067630881736 0 87.731203791681321190944546078901879401131946924772 +B 5 4 10 5 0.0080937335687687301658349379779785471776070085797762 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 5 5 0.45100738345521314866918943498008592278077398896434 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 6 0.24211712263193496382353140419155615325319291573815 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 7 0.18740694377153200478318447282205787094443953881592 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 8 0.058209946693400777238570963245971238321957631794917 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 9 0.012757171729782300158556292781676869230335088950545 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 9 6 0.046041224264782744899625460026150564466394589310766 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 9 7 0.035637484177769396109983923067310251275571182150790 0 27.474593022118317968158957115156725710168223409107 +B 5 5 9 8 0.011069259294916210017020265936023950289747911463641 0 47.560129945305985709087486769738443607156131247662 +B 5 5 9 9 0.0024259160120953038309311096075977152258913419389796 0 67.645666868493653450016016424320161504144039086217 +B 6 1 6 1 0.56384780438140809934121964163769815335761348137836 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 4 0.079739398502936260842900832661924497097557660351279 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 2 0.11725377025128962965307652078591212983890444714250 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 4 0.046403296082206151208835068293702717134744427419974 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 5 0.027413920097898011268344286855374725170836820206813 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 3 0.033475500419930068604618477140232721977824363522447 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 6 0.0063685313913616457391517206923669590100588249356294 0 142.32935382482556026905480728176275780392268396339 +B 6 1 10 2 0.015037552780683379477788996850506099643363038044358 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 10 4 0.0059511264545131206878434352423113320037011739413271 0 102.15827997845022478719774797259932200994686828628 +B 6 1 10 5 0.0035157783797834353207350169716507255162511100336067 0 142.32935382482556026905480728176275780392268396339 +B 6 1 11 3 0.0050840716124572780714260806321768726700791508471420 0 47.560129945305985709087486769738443607156131247662 +B 6 1 11 6 0.00096721689754301912562005243947305494591439321071866 0 901.68486995473911162177191440122033745838480977571 +B 6 3 6 3 0.69190080209847190885166124305093136582420050053340 0 2.7182818284590452353602874713526624977572470937000 +B 6 3 7 4 0.48145022595173290267647825590091925264107566460519 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 4 0.28017363816533743713898005725759704507865444431613 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 5 0.18666550133208492276692206167203931390109043755767 0 27.474593022118317968158957115156725710168223409107 +B 6 3 9 6 0.034522782157898376665695159474045338284990700046115 0 122.24381690163789252812627762718103990693477612483 +B 6 3 9 7 0.019716638096672448450420661923912725933126967291923 0 122.24381690163789252812627762718103990693477612483 +B 6 3 9 8 0.0088738504852450793042339593447664207460207518571955 0 122.24381690163789252812627762718103990693477612483 +B 6 3 10 4 0.035931687847973574409588594557743846214068217278629 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 10 5 0.023939463290589392920890530831570840529921399943708 0 47.560129945305985709087486769738443607156131247662 +B 6 3 11 6 0.0052431269002784457497489837625763502849299624321531 0 271.61119081430170616620385934206623250649206587918 +B 6 3 11 7 0.0029944526230504517525448010163344882937584878310570 0 603.90433693949869456257875264578286523533985242747 +B 6 3 11 8 0.0013477107370847481742223897392529612040925847764877 0 936.19748306469568295895364594949949796418763897576 +B 6 6 6 6 0.55507188723260549269009333213805059428573500798393 0 2.7182818284590452353602874713526624977572470937000 +B 6 6 9 7 0.073450552741628484864639040785843560132331375135003 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 9 8 0.034522782157898376665695159474045338284990700046115 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 9 9 0.0088738504852450793042339593447664207460207518571955 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 11 7 0.011155258784143056597400721301894029719649293782379 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 11 8 0.0052431269002784457497489837625763502849299624321531 0 27.474593022118317968158957115156725710168223409107 +B 6 6 11 9 0.0013477107370847481742223897392529612040925847764877 0 47.560129945305985709087486769738443607156131247662 +B 6 7 6 7 0.55507188723260549269009333213805059428573500798393 0 2.7182818284590452353602874713526624977572470937000 +B 6 7 9 8 0.049328926219124304880969657024177950636854432800309 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 9 9 0.019716638096672448450420661923912725933126967291923 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 11 8 0.0074918011775064397469531665088182122761014370332492 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 11 9 0.0029944526230504517525448010163344882937584878310570 0 27.474593022118317968158957115156725710168223409107 +B 6 8 6 8 0.40988397761077618096088908075276786060425053714882 0 2.7182818284590452353602874713526624977572470937000 +B 6 8 9 9 0.034522782157898376665695159474045338284990700046115 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 11 9 0.0052431269002784457497489837625763502849299624321531 0 7.3890560989306502272304274605750078131803155705521 +B 6 9 6 9 0.35339722823466909316603665448768604017448864545016 0 2.7182818284590452353602874713526624977572470937000 +B 7 0 7 0 0.18777629057366756975990551077022844521583120113516 0 2.7182818284590452353602874713526624977572470937000 +B 7 0 8 2 0.055530294064121447275470925423063600249778942561848 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 8 4 0.010719308342311721590528917463574071967582074313221 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 8 5 0.0028828660216845606139709086977418893127546553453732 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 9 1 0.056632798641208863020446742680727857420933620988199 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 9 3 0.029071012968111373091436947845758924691286104110701 0 27.474593022118317968158957115156725710168223409107 +B 7 0 10 2 0.0068898687862441493280713950396578773745958654867223 0 27.474593022118317968158957115156725710168223409107 +B 7 0 10 4 0.0013299880579155458183786191572270373571509683953068 0 122.24381690163789252812627762718103990693477612483 +B 7 0 10 5 0.00035768887870092628246833234275633810503125743214932 0 142.32935382482556026905480728176275780392268396339 +B 7 0 11 1 0.0062648287334405647419096124212441145946025141981971 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 11 3 0.0032158911747710131228242351857149169370858678118148 0 122.24381690163789252812627762718103990693477612483 +B 7 0 12 1 0.0026254548090098501386234042555622462565547134341105 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 12 3 0.0013477107370847481742223897392529612040925847764877 0 142.32935382482556026905480728176275780392268396339 +B 7 4 7 4 0.69583706868316724147040700680472549993913095890443 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 8 5 0.28642743450661143695567280421016133638334753950685 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 6 0.097137354105737990217609674993112827862402383092255 0 27.474593022118317968158957115156725710168223409107 +B 7 4 9 7 0.055477048396951892500507494622024124071766482698546 0 27.474593022118317968158957115156725710168223409107 +B 7 4 9 8 0.024968507837060735884444227432377115251455439086822 0 27.474593022118317968158957115156725710168223409107 +B 7 4 9 9 0.0056117324260645203694198734241718014014692522570807 0 27.474593022118317968158957115156725710168223409107 +B 7 4 10 5 0.035538213398479951125280068063180053984966298120001 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 11 6 0.010745520293768556596759673672391201643894108270133 0 47.560129945305985709087486769738443607156131247662 +B 7 4 11 7 0.0061369774262012044580279229604916696012118111542763 0 122.24381690163789252812627762718103990693477612483 +B 7 4 11 8 0.0027620641939268828509717970862461108440991036074858 0 196.92750385796979934716506848462363620671342100200 +B 7 4 11 9 0.00062078059694559177559129604965452537255598562976137 0 271.61119081430170616620385934206623250649206587918 +B 7 4 12 6 0.0045032161501873951154117008781940284824696356925669 0 47.560129945305985709087486769738443607156131247662 +B 7 4 12 7 0.0025718750794255379756296934719752074332794805267779 0 251.52565389111403842527532968748451460950415804062 +B 7 4 12 8 0.0011575216225834032988802861249820577932729616957798 0 658.50248697264293364068901384864374363813058946609 +B 7 4 12 9 0.00026015577966099108516347883721457956245007919957291 0 1268.4906291898926713553285392532161306930354255241 +B 8 0 8 0 0.11601416442718549491352895091421749384163064874617 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 9 1 0.056632798641208863020446742680727857420933620988199 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 3 0.029071012968111373091436947845758924691286104110701 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 10 2 0.0068898687862441493280713950396578773745958654867223 0 27.474593022118317968158957115156725710168223409107 +B 8 0 10 4 0.0013299880579155458183786191572270373571509683953068 0 47.560129945305985709087486769738443607156131247662 +B 8 0 10 5 0.00035768887870092628246833234275633810503125743214932 0 47.560129945305985709087486769738443607156131247662 +B 8 0 11 1 0.0062648287334405647419096124212441145946025141981971 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 11 3 0.0032158911747710131228242351857149169370858678118148 0 102.15827997845022478719774797259932200994686828628 +B 8 0 12 1 0.0026254548090098501386234042555622462565547134341105 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 12 3 0.0013477107370847481742223897392529612040925847764877 0 122.24381690163789252812627762718103990693477612483 +B 8 2 8 2 0.29426387682723859793223081297599351903817495847470 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 3 0.15405193009446966624274348895902503606319380529182 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 6 0.017919247819216431990398034664317229720961247328719 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 7 0.013816742688165794783405314250935420281130582304839 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 4 0.023021682277311732445611832678163686436383819385911 0 27.474593022118317968158957115156725710168223409107 +B 8 2 10 5 0.0052191847747740239620197664151098343656298122479367 0 27.474593022118317968158957115156725710168223409107 +B 8 2 11 3 0.017041519777473069539019487321413513065132759159386 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 11 6 0.0019822615394779825911486103480609436515162782427485 0 176.84196693478213160623653883004191830972551316345 +B 8 2 11 7 0.0015284345586338523192961722485921375585295140384195 0 196.92750385796979934716506848462363620671342100200 +B 8 2 12 3 0.0071417339493703195935684119579094243516630502738551 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 12 6 0.00083072312316502571118978968002728979490894844169664 0 196.92750385796979934716506848462363620671342100200 +B 8 2 12 7 0.00064053400866368083584768606575638638408932536098874 0 697.71934600893105890558407148347426645603678298274 +B 8 4 8 4 0.42991027170743521544039671171378358359929004551304 0 2.7182818284590452353602874713526624977572470937000 +B 8 4 9 6 0.097137354105737990217609674993112827862402383092255 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 9 7 0.055477048396951892500507494622024124071766482698546 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 9 8 0.024968507837060735884444227432377115251455439086822 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 9 9 0.0056117324260645203694198734241718014014692522570807 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 10 5 0.035538213398479951125280068063180053984966298120001 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 11 6 0.010745520293768556596759673672391201643894108270133 0 27.474593022118317968158957115156725710168223409107 +B 8 4 11 7 0.0061369774262012044580279229604916696012118111542763 0 47.560129945305985709087486769738443607156131247662 +B 8 4 11 8 0.0027620641939268828509717970862461108440991036074858 0 67.645666868493653450016016424320161504144039086217 +B 8 4 11 9 0.00062078059694559177559129604965452537255598562976137 0 87.731203791681321190944546078901879401131946924772 +B 8 4 12 6 0.0045032161501873951154117008781940284824696356925669 0 27.474593022118317968158957115156725710168223409107 +B 8 4 12 7 0.0025718750794255379756296934719752074332794805267779 0 122.24381690163789252812627762718103990693477612483 +B 8 4 12 8 0.0011575216225834032988802861249820577932729616957798 0 271.61119081430170616620385934206623250649206587918 +B 8 4 12 9 0.00026015577966099108516347883721457956245007919957291 0 475.57671476010975888239170225981230350884009267213 +B 8 5 8 5 0.45304730843135427273105215252662343931831720437654 0 2.7182818284590452353602874713526624977572470937000 +B 8 5 9 6 0.18436557065395366357628500807565933511386513721616 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 7 0.14270526494516756585918282770457063132322923682245 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 8 0.044325283248056951399468581440582429101441625916563 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 9 0.0097142375571151575764125938375536108412999172809602 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 11 6 0.020394872798138673694603031504314404169339732325084 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 11 7 0.015786329930571321555871280792414872126657435209227 0 27.474593022118317968158957115156725710168223409107 +B 8 5 11 8 0.0049033477909081739263522981228376963156422215852101 0 47.560129945305985709087486769738443607156131247662 +B 8 5 11 9 0.0010746075777897220474437341491233314655427498340904 0 67.645666868493653450016016424320161504144039086217 +B 8 5 12 6 0.0085470519858267017165434628998116527476567386031124 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 12 7 0.0066157109150648445767614554935928316984665834373233 0 47.560129945305985709087486769738443607156131247662 +B 8 5 12 8 0.0020548874655058155125970934127495360240958441919867 0 142.32935382482556026905480728176275780392268396339 +B 8 5 12 9 0.00045034489416233596050558245148548297326970228028084 0 291.69672773748937390713238899664795040347997371773 +B 9 1 9 1 0.41865381945102072095198187501083658703890757658169 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 10 2 0.065183960572779664376513454478507435874235653136106 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 4 0.025796617164523788484628664457049114045854027367030 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 5 0.015240003651712530266306213532585900420832963680061 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 11 3 0.036826616768138556192491864948050000628289078721245 0 27.474593022118317968158957115156725710168223409107 +B 9 1 11 6 0.0070060630008059337788578970047020660994255575985079 0 67.645666868493653450016016424320161504144039086217 +B 9 1 12 3 0.0077518081163105323401075028329258566071885989784338 0 27.474593022118317968158957115156725710168223409107 +B 9 1 12 6 0.0014747392185105008583843313937669992987153357267336 0 142.32935382482556026905480728176275780392268396339 +B 9 3 9 3 0.51373244912701364409895109265903112964681656505992 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 4 0.15575471342680109194541529156005199170414369288356 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 5 0.10377147492189017056110064071885084064082782667695 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 6 0.037978738251869001729106237987826574625510595988702 0 47.560129945305985709087486769738443607156131247662 +B 9 3 11 7 0.021690402414714787929629997404782151544376212300756 0 47.560129945305985709087486769738443607156131247662 +B 9 3 11 8 0.0097621809077817389641764102274429777328308038516618 0 47.560129945305985709087486769738443607156131247662 +B 9 3 12 6 0.0079943235970235011732543287174837759644120353511435 0 47.560129945305985709087486769738443607156131247662 +B 9 3 12 7 0.0045657150246258281052863619884130789474851494926666 0 176.84196693478213160623653883004191830972551316345 +B 9 3 12 8 0.0020548874655058155125970934127495360240958441919867 0 306.12380392425827750338559089034539301229489507924 +B 9 6 9 6 0.41213775038950737414254974577697196928775191073136 0 2.7182818284590452353602874713526624977572470937000 +B 9 6 11 7 0.080803433056776142977585130644073529852202254301119 0 7.3890560989306502272304274605750078131803155705521 +B 9 6 11 8 0.037978738251869001729106237987826574625510595988702 0 7.3890560989306502272304274605750078131803155705521 +B 9 6 11 9 0.0097621809077817389641764102274429777328308038516618 0 7.3890560989306502272304274605750078131803155705521 +B 9 6 12 7 0.017008695426433895677677501240758190230064102622798 0 7.3890560989306502272304274605750078131803155705521 +B 9 6 12 8 0.0079943235970235011732543287174837759644120353511435 0 27.474593022118317968158957115156725710168223409107 +B 9 6 12 9 0.0020548874655058155125970934127495360240958441919867 0 47.560129945305985709087486769738443607156131247662 +B 9 7 9 7 0.41213775038950737414254974577697196928775191073136 0 2.7182818284590452353602874713526624977572470937000 +B 9 7 11 8 0.054267074089023215528582478570870997706644979676645 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 11 9 0.021690402414714787929629997404782151544376212300756 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 12 8 0.011422932169421174241222295446554472981338921209620 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 12 9 0.0045657150246258281052863619884130789474851494926666 0 27.474593022118317968158957115156725710168223409107 +B 9 8 9 8 0.30433654511927776802722549064339434302634973909677 0 2.7182818284590452353602874713526624977572470937000 +B 9 8 11 9 0.037978738251869001729106237987826574625510595988702 0 7.3890560989306502272304274605750078131803155705521 +B 9 8 12 9 0.0079943235970235011732543287174837759644120353511435 0 7.3890560989306502272304274605750078131803155705521 +B 9 9 9 9 0.26239545181197259068277852604379775533249274419528 0 2.7182818284590452353602874713526624977572470937000 +B 10 0 10 0 0.070926116176272661720612924710718478195105834923484 0 2.7182818284590452353602874713526624977572470937000 +B 10 0 11 1 0.039192341320324203684356129057906860342577720032800 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 11 3 0.020118395878545474521818637104260048702448769409429 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 12 1 0.015005551255457813216166758576089183707062333601445 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 12 3 0.0077027197243904817624329956173314979254666760882290 0 27.474593022118317968158957115156725710168223409107 +B 10 2 10 2 0.17990039420943700148272755131041771054080274924108 0 2.7182818284590452353602874713526624977572470937000 +B 10 2 11 3 0.10661058556487932754250565439524537331253045565575 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 11 6 0.012400893008723373284901199856191367749130334701261 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 11 7 0.0095617823657342163034961861797968350774277057224601 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 12 3 0.040817939224227349275890658533990683149824814180003 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 12 6 0.0047479234306256990513853931535284770518713891786358 0 27.474593022118317968158957115156725710168223409107 +B 10 2 12 7 0.0036609146213000618657138530297898108248712965277300 0 47.560129945305985709087486769738443607156131247662 +B 10 4 10 4 0.26282881945533682190000074924725539219597730553084 0 2.7182818284590452353602874713526624977572470937000 +B 10 4 11 6 0.067223242156623451650620864373787051484148829886673 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 7 0.038392512261178833977058525276791943280788267804567 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 8 0.017279285235556317540413623427865516030746140430629 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 9 0.0038835610797559023406861588270077697340224477648591 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 12 6 0.025737727621191306874685628307562600974443388588912 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 12 7 0.014699321121245684370199740668676205899657342558321 0 27.474593022118317968158957115156725710168223409107 +B 10 4 12 8 0.0066157109150648445767614554935928316984665834373233 0 47.560129945305985709087486769738443607156131247662 +B 10 4 12 9 0.0014868970026487874943707727823124783708711112259184 0 67.645666868493653450016016424320161504144039086217 +B 10 5 10 5 0.27697381772134882363147131343543532187065470506453 0 2.7182818284590452353602874713526624977572470937000 +B 10 5 11 6 0.12758893337699727784886582726762047341407825405343 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 7 0.098758203481552660175303488170625365210717691971320 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 8 0.030675009391356732740141088028723262327469833096399 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 9 0.0067226717227450593220911725034023024057250767436595 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 6 0.048849908296366292985812890953673978804357413483192 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 7 0.037811501796420670481327003314787583729571367452602 0 27.474593022118317968158957115156725710168223409107 +B 10 5 12 8 0.011744524827480901659152138204873185026062055648728 0 47.560129945305985709087486769738443607156131247662 +B 10 5 12 9 0.0025739058119744246800423129060511445978712038768242 0 67.645666868493653450016016424320161504144039086217 +B 11 1 11 1 0.41293688481743127002245369280696459174100319568668 0 2.7182818284590452353602874713526624977572470937000 +B 11 1 12 3 0.044112361432511403330173358965630243664463517787169 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 12 6 0.0083921361893303855627204711081381340068229403775310 0 7.3890560989306502272304274605750078131803155705521 +B 11 3 11 3 0.50671716658483070225978364744093712639969809678109 0 2.7182818284590452353602874713526624977572470937000 +B 11 3 12 6 0.045492417592013117365284989968166529861496488006085 0 7.3890560989306502272304274605750078131803155705521 +B 11 3 12 7 0.025981612075816003210939572776658642645077189981306 0 7.3890560989306502272304274605750078131803155705521 +B 11 3 12 8 0.011693521978543596103958417633661798782020959017451 0 7.3890560989306502272304274605750078131803155705521 +B 11 6 11 6 0.40650979605219582341618197464249717806169195135171 0 2.7182818284590452353602874713526624977572470937000 +B 11 6 12 7 0.096789511413171750263623412177034977229821398312691 0 7.3890560989306502272304274605750078131803155705521 +B 11 6 12 8 0.045492417592013117365284989968166529861496488006085 0 7.3890560989306502272304274605750078131803155705521 +B 11 6 12 9 0.011693521978543596103958417633661798782020959017451 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 11 7 0.40650979605219582341618197464249717806169195135171 0 2.7182818284590452353602874713526624977572470937000 +B 11 7 12 8 0.065003223108210231519630407159674417077915786030865 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 12 9 0.025981612075816003210939572776658642645077189981306 0 7.3890560989306502272304274605750078131803155705521 +B 11 8 11 8 0.30018067204653034665965337461259486117244290384573 0 2.7182818284590452353602874713526624977572470937000 +B 11 8 12 9 0.045492417592013117365284989968166529861496488006085 0 7.3890560989306502272304274605750078131803155705521 +B 11 9 11 9 0.25881230608042930580095836186032539910137819680698 0 2.7182818284590452353602874713526624977572470937000 +B 12 1 12 1 0.37972071765696628957177865409888515462882326636653 0 2.7182818284590452353602874713526624977572470937000 +B 12 3 12 3 0.46595742162816408873120075396412087589836608947983 0 2.7182818284590452353602874713526624977572470937000 +B 12 6 12 6 0.37381061650565056542853953599364341235869565836635 0 2.7182818284590452353602874713526624977572470937000 +B 12 7 12 7 0.37381061650565056542853953599364341235869565836635 0 2.7182818284590452353602874713526624977572470937000 +B 12 8 12 8 0.27603448470497908709227700810211260311268533280197 0 2.7182818284590452353602874713526624977572470937000 +B 12 9 12 9 0.23799374242571053294256469729287326924279163875654 0 2.7182818284590452353602874713526624977572470937000 +P 0 10 0 0E+0 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 0 0E+0 +P 0 6 0 0E+0 +P 0 5 1 0E+0 +P 0 4 1 0E+0 +P 0 3 0 0E+0 +P 0 2 1 0E+0 +P 0 1 0 0E+0 +P 0 0 1 1.0981816857509217185963430779588650543172142442905E-2 +P 1 10 0 0E+0 +P 1 9 1 0E+0 +P 1 8 1 0E+0 +P 1 7 1 0E+0 +P 1 6 1 0E+0 +P 1 5 0 0E+0 +P 1 4 0 0E+0 +P 1 3 1 0E+0 +P 1 2 0 0E+0 +P 1 1 1 2.5150136397711549006246480727112924889328760111822E-1 +P 1 0 0 0E+0 +P 2 10 0 0E+0 +P 2 9 0 0E+0 +P 2 8 0 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 1 0E+0 +P 2 4 1 0E+0 +P 2 3 0 0E+0 +P 2 2 1 2.5150136397711549006246480727112924889328760111822E-1 +P 2 1 0 0E+0 +P 2 0 1 0E+0 +P 3 10 0 0E+0 +P 3 9 1 0E+0 +P 3 8 1 0E+0 +P 3 7 1 0E+0 +P 3 6 1 0E+0 +P 3 5 0 0E+0 +P 3 4 0 0E+0 +P 3 3 1 2.5150136397711549006246480727112924889328760111822E-1 +P 3 2 0 0E+0 +P 3 1 1 0E+0 +P 3 0 0 0E+0 +P 4 10 0 0E+0 +P 4 9 0 0E+0 +P 4 8 0 0E+0 +P 4 7 0 0E+0 +P 4 6 0 0E+0 +P 4 5 0 0E+0 +P 4 4 1 2.5150136397711549006246480727112924889328760111822E-1 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 1 0E+0 +P 5 10 0 0E+0 +P 5 9 0 0E+0 +P 5 8 0 0E+0 +P 5 7 0 0E+0 +P 5 6 0 0E+0 +P 5 5 1 2.5150136397711549006246480727112924889328760111822E-1 +P 5 4 1 0E+0 +P 5 3 0 0E+0 +P 5 2 1 0E+0 +P 5 1 0 0E+0 +P 5 0 1 0E+0 +P 6 10 0 0E+0 +P 6 9 1 0E+0 +P 6 8 1 0E+0 +P 6 7 1 0E+0 +P 6 6 1 2.4051954711960627287650137649154059835011545867532E-1 +P 6 5 0 0E+0 +P 6 4 0 0E+0 +P 6 3 1 0E+0 +P 6 2 0 0E+0 +P 6 1 1 0E+0 +P 6 0 0 0E+0 +P 7 10 0 0E+0 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 0 0E+0 +P 7 6 0 0E+0 +P 7 5 0 0E+0 +P 7 4 1 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 1 0E+0 +P 8 10 0 0E+0 +P 8 9 0 0E+0 +P 8 8 0 0E+0 +P 8 7 0 0E+0 +P 8 6 0 0E+0 +P 8 5 1 0E+0 +P 8 4 1 0E+0 +P 8 3 0 0E+0 +P 8 2 1 0E+0 +P 8 1 0 0E+0 +P 8 0 1 0E+0 +P 9 10 0 0E+0 +P 9 9 1 0E+0 +P 9 8 1 0E+0 +P 9 7 1 0E+0 +P 9 6 1 0E+0 +P 9 5 0 0E+0 +P 9 4 0 0E+0 +P 9 3 1 0E+0 +P 9 2 0 0E+0 +P 9 1 1 0E+0 +P 9 0 0 0E+0 +P 10 10 0 0E+0 +P 10 9 0 0E+0 +P 10 8 0 0E+0 +P 10 7 0 0E+0 +P 10 6 0 0E+0 +P 10 5 1 0E+0 +P 10 4 1 0E+0 +P 10 3 0 0E+0 +P 10 2 1 0E+0 +P 10 1 0 0E+0 +P 10 0 1 0E+0 +P 11 10 0 0E+0 +P 11 9 1 0E+0 +P 11 8 1 0E+0 +P 11 7 1 0E+0 +P 11 6 1 0E+0 +P 11 5 0 0E+0 +P 11 4 0 0E+0 +P 11 3 1 0E+0 +P 11 2 0 0E+0 +P 11 1 1 0E+0 +P 11 0 0 0E+0 +P 12 10 0 0E+0 +P 12 9 1 0E+0 +P 12 8 1 0E+0 +P 12 7 1 0E+0 +P 12 6 1 0E+0 +P 12 5 0 0E+0 +P 12 4 0 0E+0 +P 12 3 1 0E+0 +P 12 2 0 0E+0 +P 12 1 1 0E+0 +P 12 0 0 0E+0 +END +CASE random07 14 12 7 +Z 1.1005735584612756575783984806581962556404642459653E-1 +S 8 0 6 +B 0 1 0 1 0.28787207124492078972027134949890465179142467604531 0 2.7182818284590452353602874713526624977572470937000 +B 0 1 1 4 0.010287353917632803474997973338684448901847326234077 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 1 7 0.0026214932884024457366620019893298472816561111576930 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 2 2 0.029534581420289705891794767982719417043698612070614 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 2 5 0.0040170315884195951750837270070215176099629934351512 0 27.474593022118317968158957115156725710168223409107 +B 0 1 3 2 0.016983576103595267733735356778379507902851971139091 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 3 5 0.0023099552596198998304264358190189811819490389360955 0 47.560129945305985709087486769738443607156131247662 +B 0 1 3 6 0.0017811047520151427806254120018830462204546725418322 0 122.24381690163789252812627762718103990693477612483 +B 0 1 4 4 0.0016746444785290838109931241646192086417092650356364 0 47.560129945305985709087486769738443607156131247662 +B 0 1 4 7 0.00042674426252600380928982714781560755887036889053714 0 603.90433693949869456257875264578286523533985242747 +B 0 1 5 4 0.00087837101606835309988102348313458593089921752917721 0 47.560129945305985709087486769738443607156131247662 +B 0 1 5 7 0.00022383245893811728508678024261944341721731871497476 0 733.18617392897484045972780470608633993790923434326 +B 0 1 6 3 0.0013253935264534320619722720610317978051918826703252 0 47.560129945305985709087486769738443607156131247662 +B 0 1 6 4 0.00025054900983013057028847221879289676140447419979848 0 47.560129945305985709087486769738443607156131247662 +B 0 1 6 7 0.000063846597768914237508574036897839039581500210285449 0 862.46801091845098635687685676638981464047861625905 +B 0 3 0 3 0.41113017732442481860982064223758401612400696922743 0 2.7182818284590452353602874713526624977572470937000 +B 0 3 1 4 0.068580529989743774680728280570708206155975477675479 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 1 7 0.011663916015001750571540502878170921444825857376544 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 2 5 0.021071403037996587244308891173405531352105192452063 0 27.474593022118317968158957115156725710168223409107 +B 0 3 2 8 0.0021776806436314998312305625199586284518701545564168 0 47.560129945305985709087486769738443607156131247662 +B 0 3 3 5 0.012116907025453730879232087818450988190599575492067 0 27.474593022118317968158957115156725710168223409107 +B 0 3 3 6 0.011588056517848973829431064001315053229105209097803 0 102.15827997845022478719774797259932200994686828628 +B 0 3 3 8 0.0012522542444103857308243881847471112589603061475688 0 122.24381690163789252812627762718103990693477612483 +B 0 3 3 9 0.0012522542444103857308243881847471112589603061475688 0 251.52565389111403842527532968748451460950415804062 +B 0 3 4 4 0.011163998711570529501722107295443517323227784851601 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 4 7 0.0018987304907503662623968678466908383195266452106253 0 454.53696302683488092450117093089767263578256267313 +B 0 3 5 4 0.0058556505678633127495375444498334391441615225091760 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 5 7 0.00099590680397146585516395111951777917419401758574162 0 474.62249995002254866542970058547939053277047051169 +B 0 3 6 4 0.0016702821755849305457807080193925488125077209821747 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 6 7 0.00028407524730839171231336622218020266077362070232964 0 494.70803687321021640635823024006110842975837835024 +B 0 4 0 4 0.27017387191398526623675453454172319148767967464155 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 7 0.023999365996277591937545526143837118556021938506957 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 10 0.00070502813109485630207800915199119687660830738859689 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 5 0.026990952759767091087523360295564283921015887391246 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 8 0.0021776806436314998312305625199586284518701545564168 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 5 0.015520886982645194330158806149723152390078125345683 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 6 0.014992036475040437280357782332587217428583758951420 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 8 0.0012522542444103857308243881847471112589603061475688 0 47.560129945305985709087486769738443607156131247662 +B 0 4 3 9 0.0012522542444103857308243881847471112589603061475688 0 122.24381690163789252812627762718103990693477612483 +B 0 4 4 7 0.0039067777851967811673334764818385990287100257419770 0 122.24381690163789252812627762718103990693477612483 +B 0 4 4 10 0.00011476920852523380886400289361470728816064485426232 0 678.58802389583060138161754350322546153511849730464 +B 0 4 5 7 0.0020491515761904861342226104429280938028859912166235 0 122.24381690163789252812627762718103990693477612483 +B 0 4 5 10 0.000060197819655558331388219432490657788796844011424152 0 1010.8811700210275897779924368069420942639662838529 +B 0 4 6 7 0.00058450573734143441233802695632363661179063119428323 0 122.24381690163789252812627762718103990693477612483 +B 0 4 6 10 0.000017170994753610154313599491424074609125756712907190 0 1343.1743161462245781743673301106587269928140704012 +B 0 7 0 7 0.29886730264461325030958744208802836560853266212940 0 2.7182818284590452353602874713526624977572470937000 +B 0 7 1 10 0.0064544236030176246058299876640071480917517186958852 0 7.3890560989306502272304274605750078131803155705521 +B 0 7 2 8 0.027695230475501610547941603495656527885605773191880 0 7.3890560989306502272304274605750078131803155705521 +B 0 7 3 8 0.015925875088385753634133309144107637979863238350564 0 7.3890560989306502272304274605750078131803155705521 +B 0 7 3 9 0.012521895131194290183206590812835473780384688496948 0 27.474593022118317968158957115156725710168223409107 +B 0 7 3 11 0.0012522542444103857308243881847471112589603061475688 0 47.560129945305985709087486769738443607156131247662 +B 0 7 4 10 0.0010506943705275438101414756562174081002898169630868 0 122.24381690163789252812627762718103990693477612483 +B 0 7 5 10 0.00055110173750323519248390186287701467405826812207598 0 122.24381690163789252812627762718103990693477612483 +B 0 7 6 10 0.00015719780379952240389852312784536790049298720504197 0 122.24381690163789252812627762718103990693477612483 +B 0 10 0 10 0.13592053443478877675788914921392011333798939537533 0 2.7182818284590452353602874713526624977572470937000 +B 0 10 3 11 0.0073004867396256344307562256016989502659106879725027 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 1 4 0.34835680034766501355734813390623980763178210194426 0 2.7182818284590452353602874713526624977572470937000 +B 1 4 2 5 0.12276508442622560213436889346748117649982960243600 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 2 8 0.0099049170456584256150755267461028130332982227480385 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 5 0.028573475737949122413453587233789115852926152995131 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 6 0.027599878213210800482440545398148028738958288864820 0 27.474593022118317968158957115156725710168223409107 +B 1 4 3 8 0.0023053615628032772252259953635375406463448485205334 0 27.474593022118317968158957115156725710168223409107 +B 1 4 3 9 0.0023053615628032772252259953635375406463448485205334 0 47.560129945305985709087486769738443607156131247662 +B 1 4 4 7 0.0068914243185609776969461888312384737712235562284028 0 122.24381690163789252812627762718103990693477612483 +B 1 4 4 10 0.00020244901505524302550314946612552640487963070450215 0 346.29487777063361298524265019950882880627071075635 +B 1 4 5 7 0.0046408177393974393526090402400219205106708472000359 0 122.24381690163789252812627762718103990693477612483 +B 1 4 5 10 0.00013633306221784024762620248494401058582311631734276 0 678.58802389583060138161754350322546153511849730464 +B 1 4 6 7 0.0029796817154697659751113154772644393022111499261890 0 122.24381690163789252812627762718103990693477612483 +B 1 4 6 10 0.000087533955332028420351000783848694246305672246665189 0 1010.8811700210275897779924368069420942639662838529 +B 1 4 7 5 0.0024602501182057279512389655481052560943535590198154 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 7 6 0.0023764208547556040416197320631856650597269406237248 0 47.560129945305985709087486769738443607156131247662 +B 1 4 7 8 0.00019849758949209301803345713694227490713340762270913 0 1119.1232552772288577172509575383309380934781357697 +B 1 4 7 9 0.00019849758949209301803345713694227490713340762270913 0 1213.8924791567484322772182780503552522902446884854 +B 1 7 1 7 0.38535353748404211297178706570270347661564044486368 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 2 8 0.12596840644309551269338783323163118288340451393679 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 8 0.029319046389064298932714786757045236848260188039077 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 9 0.023052423944868204471349109826488856354765285224596 0 27.474593022118317968158957115156725710168223409107 +B 1 7 3 11 0.0023053615628032772252259953635375406463448485205334 0 27.474593022118317968158957115156725710168223409107 +B 1 7 4 10 0.0018533894514975393898434819667451709353885773936017 0 122.24381690163789252812627762718103990693477612483 +B 1 7 5 10 0.0012481081191526349431137131150960308564907606286142 0 122.24381690163789252812627762718103990693477612483 +B 1 7 6 10 0.00080135983578862423999223685091001075659234574959176 0 122.24381690163789252812627762718103990693477612483 +B 1 7 7 8 0.0025244456784294591304434056910207608084039610237214 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 7 9 0.0019848732879201795571505352949609348503996318715136 0 47.560129945305985709087486769738443607156131247662 +B 1 7 7 11 0.00019849758949209301803345713694227490713340762270913 0 1119.1232552772288577172509575383309380934781357697 +B 1 10 1 10 0.17525322542041371387671569767171919209989707742101 0 2.7182818284590452353602874713526624977572470937000 +B 1 10 3 11 0.013439971630690981341656881472299356709679071986566 0 7.3890560989306502272304274605750078131803155705521 +B 1 10 7 11 0.0011572162972519921394224949576000560382708287553703 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 2 2 0.31721481351967889514050820887518694225007701167454 0 2.7182818284590452353602874713526624977572470937000 +B 2 2 3 5 0.013400485231136175438769461377613526720273138503508 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 6 0.011743273015809120012245964175262087221238708239022 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 8 0.0022668665190040605185811093493655790213234057126708 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 4 4 0.020405911485139318206172792279031209665001765882510 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 4 7 0.0049518740225321634904023129109951873361083678626186 0 47.560129945305985709087486769738443607156131247662 +B 2 2 5 4 0.016310204873818347387953068589822828584816594014751 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 5 7 0.0039579746229741689976586776522666590516834206480795 0 67.645666868493653450016016424320161504144039086217 +B 2 2 6 3 0.027705140265838097159824708722030997624379929860296 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 6 4 0.013201095313148235635509726351030647993922653923479 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 6 7 0.0032034913509134796371530243691657145486400423559201 0 87.731203791681321190944546078901879401131946924772 +B 2 2 7 5 0.0014055953669469972871921406098928681350167726702346 0 87.731203791681321190944546078901879401131946924772 +B 2 2 7 6 0.0012317680934017556366125506563730771698861454134430 0 107.81674071486898893187307573348359729811985476333 +B 2 2 7 8 0.00023777475379742058853794323200965342036598309472089 0 679.54223870591781159857954517755837451118811946510 +B 2 2 8 5 0.00020387340681482648709349767331913233094873070242022 0 87.731203791681321190944546078901879401131946924772 +B 2 2 8 6 0.00017866077500886414221094347758264778250160304047850 0 346.29487777063361298524265019950882880627071075635 +B 2 2 8 8 0.000034487840705201104915344771510274088664827029389006 0 1211.0966628505912987834289553124697159302805684898 +B 2 5 2 5 0.34827281410972790226471604753697180764402930269888 0 2.7182818284590452353602874713526624977572470937000 +B 2 5 3 6 0.076131921207398299813260315718970085924532422325098 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 8 0.017905255081960068652863824596455716423085150236401 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 9 0.0072385031649852267981516009564198975184266965061280 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 11 0.0022668665190040605185811093493655790213234057126708 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 4 7 0.026672533929335412667538469209587590158496668696992 0 27.474593022118317968158957115156725710168223409107 +B 2 5 4 10 0.0023053615628032772252259953635375406463448485205334 0 67.645666868493653450016016424320161504144039086217 +B 2 5 5 7 0.021319042435724921653174870124935914893595424286450 0 27.474593022118317968158957115156725710168223409107 +B 2 5 5 10 0.0018426483631927201750229081959317820498899243161095 0 142.32935382482556026905480728176275780392268396339 +B 2 5 6 7 0.017255130352827415274853578574406288222210077294708 0 27.474593022118317968158957115156725710168223409107 +B 2 5 6 10 0.0014913961448866226040925978466635032659476248078230 0 217.01304078115746708809359813920535410370132884056 +B 2 5 7 6 0.0079855821546858923464146475997464769484105491180581 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 7 8 0.0018781068859156073835279781736969189891450532185822 0 251.52565389111403842527532968748451460950415804062 +B 2 5 7 9 0.00075925657443314554027671309256902631575786486509551 0 271.61119081430170616620385934206623250649206587918 +B 2 5 7 11 0.00023777475379742058853794323200965342036598309472089 0 1452.3706162125130563305878525163804837983955444785 +B 2 5 8 6 0.0011582620984385235389523600502663387387442185997151 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 8 8 0.00027240844570060248974143379218205237944412372768453 0 271.61119081430170616620385934206623250649206587918 +B 2 5 8 9 0.00011012573612308813956300735871972773400621001521419 0 975.41434210098380822384870358433002078209383249242 +B 2 5 8 11 0.000034487840705201104915344771510274088664827029389006 0 2894.4895318958338317830592725491872208554019069050 +B 2 8 2 8 0.29249034760996381347681676171278741446381277417650 0 2.7182818284590452353602874713526624977572470937000 +B 2 8 3 9 0.062229474965186491498236216358950431540753395421982 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 3 11 0.011743273015809120012245964175262087221238708239022 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 4 10 0.014139289040943223744807115348474829171506863068031 0 27.474593022118317968158957115156725710168223409107 +B 2 8 5 10 0.011301367311911773122731267054709742276037763743052 0 27.474593022118317968158957115156725710168223409107 +B 2 8 6 10 0.0091470602734690559961658741276550077656352305522511 0 27.474593022118317968158957115156725710168223409107 +B 2 8 7 9 0.0065273354053906964071126284517282806478244338893205 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 7 11 0.0012317680934017556366125506563730771698861454134430 0 251.52565389111403842527532968748451460950415804062 +B 2 8 8 9 0.00094675191581662223533769372176783550964290623950756 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 8 11 0.00017866077500886414221094347758264778250160304047850 0 271.61119081430170616620385934206623250649206587918 +B 3 0 3 0 0.17258650495914689625426572123874791215589906247585 0 2.7182818284590452353602874713526624977572470937000 +B 3 0 4 4 0.011997918809232958832350254865320720371226791050124 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 4 0.010175739230318596539936789668063532149778015410555 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 1 0.022017602759543309011086856550066898917842620524090 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 3 0.011320278413092031990802422054884608004838724428130 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 4 0.0032297443105943604564945670825419377957755445681689 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 7 2 0.0051470412261804911712394536620239055373644669761487 0 27.474593022118317968158957115156725710168223409107 +B 3 0 7 5 0.00095182375437734303079972745060367730794329565561531 0 107.81674071486898893187307573348359729811985476333 +B 3 0 7 6 0.00054985069028145528941354672780257710672956275863961 0 107.81674071486898893187307573348359729811985476333 +B 3 0 8 2 0.0019509400394401405106855927659814272528674585946029 0 27.474593022118317968158957115156725710168223409107 +B 3 0 8 5 0.00036078029906960736821188357040459955257132133517946 0 182.50042767120089575091186659092619359789849964050 +B 3 0 8 6 0.00020841599673370745594471396662208846944930991382445 0 475.57671476010975888239170225981230350884009267213 +B 3 0 9 1 0.0021523798254394210395899988610296091068954018318933 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 9 3 0.0011066390442590611128583831623565791286191065996852 0 176.84196693478213160623653883004191830972551316345 +B 3 0 9 4 0.00031573085278041614750681571530848787949238909100709 0 196.92750385796979934716506848462363620671342100200 +B 3 2 3 2 0.24863500742224039581367051387457547438719243613021 0 2.7182818284590452353602874713526624977572470937000 +B 3 2 4 4 0.074666521505962468923286140649694188187536059391991 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 4 7 0.018119220426271016307016868597116004639020384696962 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 4 0.063326570562801095997664874085466996275044226106916 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 7 0.015367370378645146866132635755265363304085609960794 0 27.474593022118317968158957115156725710168223409107 +B 3 2 6 3 0.042183103543612187787826043827719863046215465768048 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 4 0.020099633683149668636183725932291279717054788932111 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 7 0.0048775500163511457550195812964937488993223450081770 0 47.560129945305985709087486769738443607156131247662 +B 3 2 7 5 0.0032504190223268609717126809177143415232607477643697 0 87.731203791681321190944546078901879401131946924772 +B 3 2 7 6 0.0028484459582309732303265001949132413220470148673941 0 87.731203791681321190944546078901879401131946924772 +B 3 2 7 8 0.00054985069028145528941354672780257710672956275863961 0 311.78226466067704164806091865122966830046788155628 +B 3 2 8 5 0.0012320423204228238955823229700842689442803956755686 0 87.731203791681321190944546078901879401131946924772 +B 3 2 8 6 0.0010796780180869239833151533663017578611583842542137 0 326.20934084744594524431412054492711090928280291780 +B 3 2 8 8 0.00020841599673370745594471396662208846944930991382445 0 788.73853877220628975480006758328013131676959354234 +B 3 2 9 3 0.0041237032947349024643113172516029894048274872710155 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 9 4 0.0019648844840559443445290182318307999130103604574812 0 27.474593022118317968158957115156725710168223409107 +B 3 2 9 7 0.00047681577179039276135637365233402201169944612733108 0 1689.8945384581904701669954253080728023304767783111 +B 3 5 3 5 0.27297846768358701504131828668295713944235550343696 0 2.7182818284590452353602874713526624977572470937000 +B 3 5 4 7 0.097596489610551658111820307155083772215570346721562 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 4 10 0.0084354638523952674192832751847210485404110910381601 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 5 7 0.082774058056404112592306184253668761368235494522448 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 5 10 0.0071543308313352912258535836701308312215859889722030 0 27.474593022118317968158957115156725710168223409107 +B 3 5 6 7 0.026272198709253699795588450286858330725330137200094 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 6 10 0.0022707597723975402690430017736541281953816287721798 0 47.560129945305985709087486769738443607156131247662 +B 3 5 7 6 0.018466543608722440366473821694297468964090575686445 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 7 8 0.0043430950979887156884670922164217051349370011821973 0 67.645666868493653450016016424320161504144039086217 +B 3 5 7 9 0.0017557698825691185135720888962058777103707614495665 0 67.645666868493653450016016424320161504144039086217 +B 3 5 7 11 0.00054985069028145528941354672780257710672956275863961 0 291.69672773748937390713238899664795040347997371773 +B 3 5 8 6 0.0069995785409822692479518212566682337328936592489788 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 8 8 0.0016462114347683406861512535584164050866234357518929 0 87.731203791681321190944546078901879401131946924772 +B 3 5 8 9 0.00066550890374140719274622277796962171881534417788948 0 271.61119081430170616620385934206623250649206587918 +B 3 5 8 11 0.00020841599673370745594471396662208846944930991382445 0 679.54223870591781159857954517755837451118811946510 +B 3 5 9 7 0.0025682973341511126148196050496800819682520365917474 0 47.560129945305985709087486769738443607156131247662 +B 3 5 9 10 0.00022198318208867779997632540070908328254986840380107 0 1452.3706162125130563305878525163804837983955444785 +B 3 6 3 6 0.32069231647327487890372796356512669800804268205624 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 4 7 0.13377266924585428760115504675346798417302962772190 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 10 0.011997918809232958832350254865320720371226791050124 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 5 7 0.11345599349630029014130975250493775947211841036492 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 5 10 0.010175739230318596539936789668063532149778015410555 0 27.474593022118317968158957115156725710168223409107 +B 3 6 6 7 0.036010538517508156628421319473152812037940117500064 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 10 0.0032297443105943604564945670825419377957755445681689 0 47.560129945305985709087486769738443607156131247662 +B 3 6 7 8 0.0084250694531660553215026875581459963723932272296310 0 67.645666868493653450016016424320161504144039086217 +B 3 6 7 9 0.0058377442377464581466076842379301689478269874970004 0 67.645666868493653450016016424320161504144039086217 +B 3 6 7 11 0.00095182375437734303079972745060367730794329565561531 0 291.69672773748937390713238899664795040347997371773 +B 3 6 8 8 0.0031934473824766908823923845309778356798965788235755 0 67.645666868493653450016016424320161504144039086217 +B 3 6 8 9 0.0022127448514497573889873537505310523120884872495722 0 251.52565389111403842527532968748451460950415804062 +B 3 6 8 11 0.00036078029906960736821188357040459955257132133517946 0 659.45670178273014385765101552297665661420021162654 +B 3 6 9 7 0.0035202904446397341940207304337537073495858111367495 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 9 10 0.00031573085278041614750681571530848787949238909100709 0 1303.0032422998492426925102708014952911988382547241 +B 3 8 3 8 0.22925581230595816764825595274606391948711145254599 0 2.7182818284590452353602874713526624977572470937000 +B 3 8 4 10 0.051736553401373279734751974144304604159501772062423 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 5 10 0.043879083069198079403023563917265231181852957691384 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 6 10 0.013927068657045637476779001577724228708779440664128 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 7 9 0.015094369023762992129433286220086115034415693009696 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 7 11 0.0028484459582309732303265001949132413220470148673941 0 67.645666868493653450016016424320161504144039086217 +B 3 8 8 9 0.0057213858612119745720385462839860496409778134692616 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 8 11 0.0010796780180869239833151533663017578611583842542137 0 87.731203791681321190944546078901879401131946924772 +B 3 8 9 10 0.0013614716339607760742384314139815178577686843232152 0 47.560129945305985709087486769738443607156131247662 +B 3 9 3 9 0.33485964330997769675222552144195569984084577957377 0 2.7182818284590452353602874713526624977572470937000 +B 3 9 4 10 0.072107674845762102861686506598498575750558165757118 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 5 10 0.061156347813457850985552233996197866048928657995030 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 6 10 0.019410812515589703525110277027227278213901904288092 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 7 11 0.0051470412261804911712394536620239055373644669761487 0 67.645666868493653450016016424320161504144039086217 +B 3 9 8 11 0.0019509400394401405106855927659814272528674585946029 0 67.645666868493653450016016424320161504144039086217 +B 3 9 9 10 0.0018975472357377060782099506094046703777458241083634 0 7.3890560989306502272304274605750078131803155705521 +B 3 11 3 11 0.30131312135699265073831340243661514310796169847203 0 2.7182818284590452353602874713526624977572470937000 +B 4 4 4 4 0.39245482389313331185439465295841062700329170772577 0 2.7182818284590452353602874713526624977572470937000 +B 4 4 5 7 0.089933761114754616424631654351049852959758998788484 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 10 0.0026419794394110758308738395177038813707302718918188 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 7 0.021513844220185642375126215488947745272393176195361 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 10 0.00063201108669187189310089418564719429201351429616443 0 27.474593022118317968158957115156725710168223409107 +B 4 4 7 5 0.028935870261180911795282233685571600073332685817482 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 7 6 0.027949924696811954478287438580628969205478047676021 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 7 8 0.0023346002319834289660407181954435842926189757542381 0 47.560129945305985709087486769738443607156131247662 +B 4 4 7 9 0.0023346002319834289660407181954435842926189757542381 0 47.560129945305985709087486769738443607156131247662 +B 4 4 8 5 0.017725264528651818715155003986902881499676692275801 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 8 6 0.017121303224514513926924872541631215895646515617283 0 27.474593022118317968158957115156725710168223409107 +B 4 4 8 8 0.0014301075553298167773361467744364613219179329761234 0 87.731203791681321190944546078901879401131946924772 +B 4 4 8 9 0.0014301075553298167773361467744364613219179329761234 0 217.01304078115746708809359813920535410370132884056 +B 4 4 9 7 0.0034415127155571412468891239646704262790474463871942 0 142.32935382482556026905480728176275780392268396339 +B 4 4 9 10 0.00010110114068699876438261221793568905940623073664181 0 1269.4448439999798815722905409275490436691050476845 +B 4 4 10 5 0.0078961460632038518874034378272820965929147243149443 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 10 8 0.00063707585998066406790435217176681816372951489414601 0 569.39172382954212322539702109750370472953702322740 +B 4 7 4 7 0.43413492872526722044131030827932170022329449564019 0 2.7182818284590452353602874713526624977572470937000 +B 4 7 5 10 0.024186913543351703410601410456324446092234036535550 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 10 0.0057859638437093809341835960379824474991851416657967 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 7 8 0.029690896909988649473951621440392305910598060859690 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 7 9 0.023344795522671823005471293181626440940030608654057 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 7 11 0.0023346002319834289660407181954435842926189757542381 0 47.560129945305985709087486769738443607156131247662 +B 4 7 8 8 0.018187771685184544560713494082670910458714233364569 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 9 0.014300336304789515008984222978248130976530246596716 0 27.474593022118317968158957115156725710168223409107 +B 4 7 8 11 0.0014301075553298167773361467744364613219179329761234 0 87.731203791681321190944546078901879401131946924772 +B 4 7 9 10 0.00092556532138484928638730816220091005902277814932887 0 142.32935382482556026905480728176275780392268396339 +B 4 7 10 8 0.0081021810172862699364828794163616675692152475144901 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 4 10 0.19743829789006464000837338728139373675689751865692 0 2.7182818284590452353602874713526624977572470937000 +B 4 10 7 11 0.013610429441145042019495021978922603602459618667174 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 8 11 0.0083373494564113702702160751052175688242528030365627 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 5 4 0.50561165838015400492810092053499836996424575641445 0 2.7182818284590452353602874713526624977572470937000 +B 5 4 6 7 0.080811935140666440236566155845209972421127577545818 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 6 10 0.0023740080305129656516232180259328100726260902553717 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 7 5 0.081172543411961831859005527057926893419518085420935 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 7 6 0.078406713029008483810414927822727596307007445445324 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 7 8 0.0065491529015625700883681075163534272188864450973905 0 27.474593022118317968158957115156725710168223409107 +B 5 4 7 9 0.0065491529015625700883681075163534272188864450973905 0 27.474593022118317968158957115156725710168223409107 +B 5 4 8 5 0.018355167205611950069846034403409711510241532278453 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 8 6 0.017729742930265303543303482608400766024202739784500 0 27.474593022118317968158957115156725710168223409107 +B 5 4 8 8 0.0014809292836028716482417963895613061548432219924564 0 67.645666868493653450016016424320161504144039086217 +B 5 4 8 9 0.0014809292836028716482417963895613061548432219924564 0 142.32935382482556026905480728176275780392268396339 +B 5 4 9 7 0.0037102876001181595117203352056855750113456884904703 0 142.32935382482556026905480728176275780392268396339 +B 5 4 9 10 0.00010899692654137015866342113358597412831428808210172 0 882.55354784163865409780538642097153253746652409761 +B 5 4 10 5 0.0085260487401639832420944682437889266034795643175974 0 7.3890560989306502272304274605750078131803155705521 +B 5 4 10 8 0.00068789758825371893881000178689166299665480391047897 0 549.30618690635445548446849144292198683254911538885 +B 5 4 11 7 0.0014427511771383176440086620257978573961818850520794 0 162.41489074801322800998333693634447570091059180195 +B 5 4 11 10 0.000042383626559572499196153687160559157572884374848920 0 2782.6994070508387912367742482000274276031635209199 +B 5 7 5 7 0.55930942342883231672274139141717600777451960983724 0 2.7182818284590452353602874713526624977572470937000 +B 5 7 6 10 0.021733676700390689024552045329400598418531347896607 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 7 8 0.083290586963938724230732027699866316091215315403150 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 7 9 0.065488143639821359719436347271587742429388417814519 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 7 11 0.0065491529015625700883681075163534272188864450973905 0 27.474593022118317968158957115156725710168223409107 +B 5 7 8 8 0.018834110477698396307628921599994752277973141169708 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 8 9 0.014808527316847838406053416194890863490210057622372 0 27.474593022118317968158957115156725710168223409107 +B 5 7 8 11 0.0014809292836028716482417963895613061548432219924564 0 67.645666868493653450016016424320161504144039086217 +B 5 7 9 10 0.00099785002086724589560537812706303477395668300109839 0 142.32935382482556026905480728176275780392268396339 +B 5 7 10 8 0.0087485198098001216833983069336855093884741553196296 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 11 10 0.00038801555226281275672488568017061259442629394277659 0 162.41489074801322800998333693634447570091059180195 +B 5 10 5 10 0.25436585091162920727703149605910911940553161133544 0 2.7182818284590452353602874713526624977572470937000 +B 5 10 7 11 0.038180748140446674842616784120628486443266542564084 0 7.3890560989306502272304274605750078131803155705521 +B 5 10 8 11 0.0086336338211866621825300607697099223728002680095612 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 6 1 0.38539354268831773478837074109149968779780706941187 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 2 0.14785532322492902754447732139949853859932010303048 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 7 5 0.020109968563918903732448351819800135588636775783598 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 7 6 0.015505910957758071646495041478872257519684893501812 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 2 0.020774223763324224584254293406811044833326865633628 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 5 0.0028255255049879030488573902941786017238633276675700 0 27.474593022118317968158957115156725710168223409107 +B 6 1 8 6 0.0021786382584319147840024181494323763558159193381797 0 47.560129945305985709087486769738443607156131247662 +B 6 1 9 3 0.0090227064023955481911524996385767986620206838791813 0 47.560129945305985709087486769738443607156131247662 +B 6 1 9 4 0.0017056293923189098060762601203844263500027494697387 0 47.560129945305985709087486769738443607156131247662 +B 6 1 9 7 0.00043464004838037520357063606346589262777842623599705 0 291.69672773748937390713238899664795040347997371773 +B 6 1 10 2 0.010018808710315513431165382959411151749152511197318 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 10 5 0.0013626694245283138105672999104183083709995409993616 0 306.12380392425827750338559089034539301229489507924 +B 6 1 11 3 0.0038809637230430513212719771474579005441591236160580 0 67.645666868493653450016016424320161504144039086217 +B 6 1 11 4 0.00073364747796605301425361767318066603127970351070427 0 196.92750385796979934716506848462363620671342100200 +B 6 1 11 7 0.00018695302552436571763229361688480858744069407838792 0 1402.4767121057003711801909174000709677077081717564 +B 6 1 12 3 0.0017709440192499050287648798287810273918594408732012 0 67.645666868493653450016016424320161504144039086217 +B 6 1 12 4 0.00033477473794138466104824692736436353206212628833871 0 380.80749088059018432242438174778798931207353995641 +B 6 1 12 7 0.000085309568978255975820994386635118921590116045721982 0 2121.6611543892385870510509652103651115681944818916 +B 6 3 6 3 0.55040739054651169531242963233175912370812230385804 0 2.7182818284590452353602874713526624977572470937000 +B 6 3 7 5 0.10548715970104907017724712236763256345805974420939 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 7 6 0.10088310209488823809129381202670468538910786192760 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 7 8 0.010901853351597239560541731137944379450733011220026 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 7 9 0.010901853351597239560541731137944379450733011220026 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 5 0.014821338941265995173115325114131543427464191954287 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 8 6 0.014174451694710006908260352969385318059416783624897 0 27.474593022118317968158957115156725710168223409107 +B 6 3 8 8 0.0015317510118759265191474460046861509877685110087893 0 47.560129945305985709087486769738443607156131247662 +B 6 3 8 9 0.0015317510118759265191474460046861509877685110087893 0 67.645666868493653450016016424320161504144039086217 +B 6 3 9 4 0.011370559293271771932045508805046862715665491226025 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 9 7 0.0019338615297979430451906788721071364302896751636823 0 142.32935382482556026905480728176275780392268396339 +B 6 3 10 5 0.0071479041226776996918488900251479959048610237888983 0 27.474593022118317968158957115156725710168223409107 +B 6 3 10 8 0.00073871931652677380971565140201650782958009292681195 0 549.30618690635445548446849144292198683254911538885 +B 6 3 11 4 0.0048908527175595017774968270765222092159142857695839 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 11 7 0.00083181765069311093885681784760150546208025746212282 0 237.09857770434513482902212779378707200068923667911 +B 6 3 12 4 0.0022317720512993478199945712269771427362139513898871 0 7.3890560989306502272304274605750078131803155705521 +B 6 3 12 7 0.00037957131236630484450453213709794397672973607306546 0 257.18411462753280256995065744836878989767714451766 +B 6 4 6 4 0.36169978278359199411403004611018355478661440662463 0 2.7182818284590452353602874713526624977572470937000 +B 6 4 7 5 0.13512146956322118508727770959209322517625463488023 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 6 0.13051741195706035300132439925116534710730275259844 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 8 0.010901853351597239560541731137944379450733011220026 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 9 0.010901853351597239560541731137944379450733011220026 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 8 5 0.018985069882572081424537064819916541520806372281107 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 8 6 0.018338182636016093159682092675170316152758963951716 0 27.474593022118317968158957115156725710168223409107 +B 6 4 8 8 0.0015317510118759265191474460046861509877685110087893 0 47.560129945305985709087486769738443607156131247662 +B 6 4 8 9 0.0015317510118759265191474460046861509877685110087893 0 67.645666868493653450016016424320161504144039086217 +B 6 4 9 7 0.0039790624846791777765515464467007237436439305937465 0 142.32935382482556026905480728176275780392268396339 +B 6 4 9 10 0.00011689271239574155294423004923625919722234542756163 0 495.66225168329742662332023191439402140582800051069 +B 6 4 10 5 0.0091559514171241145967854986602957566140444043202498 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 10 8 0.00073871931652677380971565140201650782958009292681195 0 529.22064998316678774353996178834026893556120755030 +B 6 4 11 7 0.0017115260616993359088398732668130061284801271553554 0 162.41489074801322800998333693634447570091059180195 +B 6 4 11 10 0.000050279412413943893476962602810844226480941720308835 0 2341.2099608593533246841788324905890380687342602944 +B 6 4 12 7 0.00078099592242005606795116823247666062915496844578987 0 162.41489074801322800998333693634447570091059180195 +B 6 4 12 10 0.000022943276737473804514181251452807768972113485067795 0 2782.6994070508387912367742482000274276031635209199 +B 6 7 6 7 0.40011359233912273310488145628968609456472467938206 0 2.7182818284590452353602874713526624977572470937000 +B 6 7 7 8 0.13864720801260736337257070071764278246141633846691 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 7 9 0.10901289815043524846254011349318212074322144779609 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 7 11 0.010901853351597239560541731137944379450733011220026 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 8 8 0.019480449270212248054544349117318594097232048974847 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 8 9 0.015316718328906161803122609411533596003889868648028 0 27.474593022118317968158957115156725710168223409107 +B 6 7 8 11 0.0015317510118759265191474460046861509877685110087893 0 47.560129945305985709087486769738443607156131247662 +B 6 7 9 10 0.0010701347203496425048234480919251594888905878528679 0 142.32935382482556026905480728176275780392268396339 +B 6 7 10 8 0.0093948586023139734303137344510093512077330631247683 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 11 10 0.00046030025174520936594295564503273730936019879454610 0 162.41489074801322800998333693634447570091059180195 +B 6 7 12 10 0.00021004215345982031843462065699974122682612116703035 0 162.41489074801322800998333693634447570091059180195 +B 6 10 6 10 0.18196588527459303527346043967173771210938156220829 0 2.7182818284590452353602874713526624977572470937000 +B 6 10 7 11 0.063556451244573514900338870067044431513687313299784 0 7.3890560989306502272304274605750078131803155705521 +B 6 10 8 11 0.0089299181859619540948440464342022759213477329825605 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 7 0 0.23694583858617247096911274000267642277945249855587 0 2.7182818284590452353602874713526624977572470937000 +B 7 0 8 2 0.025597240442751239471104316506347200499584556343643 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 8 5 0.0047336052751998441385731614688678645431231858429478 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 8 6 0.0027345147839803988899335100767158728829919003289940 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 9 1 0.034322376988042304026463227792501912941287607262700 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 9 3 0.017646737819144899325568161103164698899427261721479 0 27.474593022118317968158957115156725710168223409107 +B 7 0 9 4 0.0050347216731011521411944189750535672842361281170699 0 27.474593022118317968158957115156725710168223409107 +B 7 0 10 2 0.0078072990084825749536939203151856806925333424830207 0 27.474593022118317968158957115156725710168223409107 +B 7 0 10 5 0.0014437756231680518218175374501154795506723470937439 0 196.92750385796979934716506848462363620671342100200 +B 7 0 11 1 0.0076554998455001896419005090744353383866441046429336 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 11 3 0.0039360501953321071516188777647847631354228788691141 0 122.24381690163789252812627762718103990693477612483 +B 7 0 11 4 0.0011229790700099404193595456210795328152237218148563 0 196.92750385796979934716506848462363620671342100200 +B 7 0 12 1 0.0036307034750011617433713796829657418046308085054100 0 7.3890560989306502272304274605750078131803155705521 +B 7 0 12 3 0.0018667143113288864199258180574099604822098065060627 0 142.32935382482556026905480728176275780392268396339 +B 7 0 12 4 0.00053258495122760597516102560989486202429410364631560 0 549.30618690635445548446849144292198683254911538885 +B 7 0 13 2 0.00024892285310401770779856552841524400402766294659259 0 67.645666868493653450016016424320161504144039086217 +B 7 0 13 5 0.000046032404672928405316508291567740408357540573710217 0 3059.4401883328043303380768786265502689531509482692 +B 7 0 13 6 0.000026592054850829710634535855859989019756769683929092 0 3614.8301805169098289746061428282617776052650472886 +B 7 2 7 2 0.34135363219440200404812251467252588642736741774850 0 2.7182818284590452353602874713526624977572470937000 +B 7 2 8 5 0.016164968104585264429158564683683528351419513850272 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 8 6 0.014165877613365819180518913291531536691288228336318 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 8 8 0.0027345147839803988899335100767158728829919003289940 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 9 3 0.065757584881575528076333228391523801102540563974237 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 9 4 0.031332530254484682592242499122062601766719134108843 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 9 7 0.0076034213291759989406053792010599941236211034769525 0 47.560129945305985709087486769738443607156131247662 +B 7 2 10 5 0.0049304041088858976069710854853007795035411115037212 0 47.560129945305985709087486769738443607156131247662 +B 7 2 10 8 0.00083404203704688338338682424481508078679581366306608 0 217.01304078115746708809359813920535410370132884056 +B 7 2 11 3 0.014667025569841823425533092443929113209235265423289 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 11 4 0.0069886237950799661644240198665589780812074248995109 0 27.474593022118317968158957115156725710168223409107 +B 7 2 11 7 0.0016959195696457444386436001830866975935274506627068 0 217.01304078115746708809359813920535410370132884056 +B 7 2 12 3 0.0069559952686376530190722666568730103421046388696420 0 7.3890560989306502272304274605750078131803155705521 +B 7 2 12 4 0.0033144303063616346184358769553432533186940721183787 0 47.560129945305985709087486769738443607156131247662 +B 7 2 12 7 0.00080430817047883729137404342412430064583103459240012 0 311.78226466067704164806091865122966830046788155628 +B 7 2 13 5 0.00015719780379952240389852312784536790049298720504197 0 291.69672773748937390713238899664795040347997371773 +B 7 2 13 6 0.00013775745397742370921655069213761651189221631526084 0 441.06410165015318754520997071153314300303726347207 +B 7 2 13 8 0.000026592054850829710634535855859989019756769683929092 0 2027.8461453198062227080456463726737103474975513364 +B 7 5 7 5 0.37477502633573018979982205384131263657617328893638 0 2.7182818284590452353602874713526624977572470937000 +B 7 5 8 6 0.091837725039905008213540020278419445814733565271210 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 8 0.021599059460312348973825013722043217179321985315736 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 9 0.0087317862576387346358524642531718478633857568708557 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 11 0.0027345147839803988899335100767158728829919003289940 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 9 7 0.040954699666970808342395512579734422207341794559393 0 27.474593022118317968158957115156725710168223409107 +B 7 5 9 10 0.0035397983063223414240840546398339116969518915401412 0 67.645666868493653450016016424320161504144039086217 +B 7 5 10 8 0.0065878318362402380768324939045848831647802756216650 0 102.15827997845022478719774797259932200994686828628 +B 7 5 11 7 0.0091348188699819094192068628023878480959699022103462 0 27.474593022118317968158957115156725710168223409107 +B 7 5 11 10 0.00078954104479982868062130101714135266250538194314495 0 420.02434991687830958731943938261851212997973347307 +B 7 5 12 7 0.0043322864978233879382651666752358632906730385910946 0 27.474593022118317968158957115156725710168223409107 +B 7 5 12 10 0.00037444836690784241269327424608361778132573545279998 0 494.70803687321021640635823024006110842975837835024 +B 7 5 13 6 0.00089308488509311954389227925147988686012702149114237 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 13 8 0.00021004215345982031843462065699974122682612116703035 0 251.52565389111403842527532968748451460950415804062 +B 7 5 13 9 0.000084913104317125794680453162983243185559082353272462 0 549.30618690635445548446849144292198683254911538885 +B 7 5 13 11 0.000026592054850829710634535855859989019756769683929092 0 3239.7721357666309427063096882719436726357845331552 +B 7 6 7 6 0.44028187414125576872768599292105984758875117696195 0 2.7182818284590452353602874713526624977572470937000 +B 7 6 8 8 0.041899514509932493105103663621426266983291970740031 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 8 9 0.029032241307258878767131114152554897667355742295152 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 8 11 0.0047336052751998441385731614688678645431231858429478 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 9 7 0.056135415469089402326180214933851980661917903523685 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 9 10 0.0050347216731011521411944189750535672842361281170699 0 47.560129945305985709087486769738443607156131247662 +B 7 6 10 8 0.012779582190545596363278145573037991676250834836852 0 27.474593022118317968158957115156725710168223409107 +B 7 6 11 7 0.012520830494939880170750249508100243194472788112454 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 11 10 0.0011229790700099404193595456210795328152237218148563 0 142.32935382482556026905480728176275780392268396339 +B 7 6 12 7 0.0059381390771758996992429769369804003701256723969262 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 12 10 0.00053258495122760597516102560989486202429410364631560 0 162.41489074801322800998333693634447570091059180195 +B 7 6 13 8 0.00040745590208491145140685811587836398302706483255771 0 67.645666868493653450016016424320161504144039086217 +B 7 6 13 9 0.00028232685294221692765269062186186594176002601879984 0 162.41489074801322800998333693634447570091059180195 +B 7 6 13 11 0.000046032404672928405316508291567740408357540573710217 0 1140.1630070105037356751414888672455689665356657687 +B 7 8 7 8 0.31474773019158037927140441118288092805012656088998 0 2.7182818284590452353602874713526624977572470937000 +B 7 8 8 9 0.075067242762226251574354862989680686103198184609205 0 7.3890560989306502272304274605750078131803155705521 +B 7 8 8 11 0.014165877613365819180518913291531536691288228336318 0 7.3890560989306502272304274605750078131803155705521 +B 7 8 9 10 0.021710360841998556842089485664390781326096473658290 0 27.474593022118317968158957115156725710168223409107 +B 7 8 11 10 0.0048424287201780229096411769307301080664449475886758 0 27.474593022118317968158957115156725710168223409107 +B 7 8 12 10 0.0022965741148998812986065872354506433467151056456628 0 27.474593022118317968158957115156725710168223409107 +B 7 8 13 9 0.00072999869985269710813326306877348478049504731184297 0 7.3890560989306502272304274605750078131803155705521 +B 7 8 13 11 0.00013775745397742370921655069213761651189221631526084 0 251.52565389111403842527532968748451460950415804062 +B 7 9 7 9 0.45973234704260774580325891071611097390641969254549 0 2.7182818284590452353602874713526624977572470937000 +B 7 9 8 11 0.025597240442751239471104316506347200499584556343643 0 7.3890560989306502272304274605750078131803155705521 +B 7 9 9 10 0.030258753965188646509941903231275830514618395325888 0 7.3890560989306502272304274605750078131803155705521 +B 7 9 11 10 0.0067491213206542738838782099084899934556220359233716 0 7.3890560989306502272304274605750078131803155705521 +B 7 9 12 10 0.0032008436714301668646906105049250589401255093658098 0 7.3890560989306502272304274605750078131803155705521 +B 7 9 13 11 0.00024892285310401770779856552841524400402766294659259 0 67.645666868493653450016016424320161504144039086217 +B 7 11 7 11 0.41367597213843414395096788943141488921151032010344 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 8 0 0.18064917999119894528175659760368629565509148375776 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 9 1 0.088496560770353507791517217924432783588185706746473 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 3 0.045500217142726736998350562522596905213866185340009 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 4 0.012981488801333331153900140811566936785854766707214 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 10 2 0.036526270221190266034038441850727985895829221152911 0 27.474593022118317968158957115156725710168223409107 +B 8 0 10 5 0.0067546713009590928696018369106802365166186243400251 0 67.645666868493653450016016424320161504144039086217 +B 8 0 11 1 0.027400346464051814947842117173955603235705534896692 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 11 3 0.014087798475417771608421835793623714224173442678604 0 102.15827997845022478719774797259932200994686828628 +B 8 0 11 4 0.0040193346236219062937387595996717172270288594535091 0 122.24381690163789252812627762718103990693477612483 +B 8 0 12 1 0.0088687685193565882727925780075257031673990976382018 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 12 3 0.0045598483139526408156715228694160558640848601098619 0 122.24381690163789252812627762718103990693477612483 +B 8 0 12 4 0.0013009524688129328576498106442504041828238945906025 0 420.02434991687830958731943938261851212997973347307 +B 8 0 13 2 0.0014752081085167496343904387917110573921004764244536 0 67.645666868493653450016016424320161504144039086217 +B 8 0 13 5 0.00027280491036173267088197501583392811547515742013404 403.42879349273512260838718054338827960589989735717 1863.0974804102862952567528610897581621630557180601 +B 8 0 13 6 0.00015759426846065258503906435149724363652402089749148 0 2450.4062609256418028403993548963107948743157343716 +B 8 2 8 2 0.26025041887582941854941717937570141672787011897961 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 3 0.16954886628660484639720948997968625223593923488575 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 4 0.080787562257735723826838581704820684564588898022817 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 7 0.019604604831257804120277056392566797220364465119049 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 5 0.023066783094277210997883065895175592838634032882232 0 47.560129945305985709087486769738443607156131247662 +B 8 2 10 8 0.0039020466345540297774759838817372732517984040684978 0 67.645666868493653450016016424320161504144039086217 +B 8 2 11 3 0.052495799140665400631141664026076322648664474673709 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 11 4 0.025013482745305475437703006626494127923459158070617 0 27.474593022118317968158957115156725710168223409107 +B 8 2 11 7 0.0060699869010869145634064095123208715852931800334636 0 176.84196693478213160623653883004191830972551316345 +B 8 2 12 3 0.016991503790947084073036662664318779479874949369162 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 12 4 0.0080962037696157689662438937413405962092961499708021 0 47.560129945305985709087486769738443607156131247662 +B 8 2 12 7 0.0019646944542067627034748114241018463258256927659829 0 271.61119081430170616620385934206623250649206587918 +B 8 2 13 5 0.00093161183038978119555766223594083499326338518361512 0 291.69672773748937390713238899664795040347997371773 +B 8 2 13 6 0.00081640118848870110971475157160415051431224866097255 0 420.97856472696551980428144105695142510604935563351 +B 8 2 13 8 0.00015759426846065258503906435149724363652402089749148 0 1660.0861712745654527575270198463450041367773134276 +B 8 5 8 5 0.28573112569819406573190697029718370575367150326291 0 2.7182818284590452353602874713526624977572470937000 +B 8 5 9 7 0.10559729208651134570661036719623855396900350793196 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 9 10 0.0091269895450244391715608227017608872740563623453887 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 10 8 0.030821020888380139849786735792842059366188780609857 0 27.474593022118317968158957115156725710168223409107 +B 8 5 11 7 0.032695082878355001242246972272984649608357364469638 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 11 10 0.0028259027642487365386692043259409795709056710404717 0 102.15827997845022478719774797259932200994686828628 +B 8 5 12 7 0.010582534865014657617716921700321140932454167822661 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 12 10 0.00091467009394252320437546851115785070377242075690283 0 122.24381690163789252812627762718103990693477612483 +B 8 5 13 6 0.0052927485269204270460862493692981991804770766193931 0 7.3890560989306502272304274605750078131803155705521 +B 8 5 13 8 0.0012447868247145894627046174630376884341982033791685 0 67.645666868493653450016016424320161504144039086217 +B 8 5 13 9 0.00050322619416389284256779634450729707337743046541915 0 142.32935382482556026905480728176275780392268396339 +B 8 5 13 11 0.00015759426846065258503906435149724363652402089749148 0 772.40303296526296572462286234091686275581542785991 +B 8 6 8 6 0.33567400889245942649795480291621132256082276269534 0 2.7182818284590452353602874713526624977572470937000 +B 8 6 9 7 0.14473913645782922451743770448826838283152462497659 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 9 10 0.012981488801333331153900140811566936785854766707214 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 10 8 0.059788983603499052589749451543727385478493464335787 0 27.474593022118317968158957115156725710168223409107 +B 8 6 11 7 0.044814199007615874826597698379585800963766268274688 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 11 10 0.0040193346236219062937387595996717172270288594535091 0 102.15827997845022478719774797259932200994686828628 +B 8 6 12 7 0.014505172695548195421563634705338234756716931517301 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 12 10 0.0013009524688129328576498106442504041828238945906025 0 122.24381690163789252812627762718103990693477612483 +B 8 6 13 8 0.0024147330914911744358313044730016177149049310111137 0 67.645666868493653450016016424320161504144039086217 +B 8 6 13 9 0.0016731724609404778156944833544712263540841580973644 0 142.32935382482556026905480728176275780392268396339 +B 8 6 13 11 0.00027280491036173267088197501583392811547515742013404 0 772.40303296526296572462286234091686275581542785991 +B 8 8 8 8 0.23996589137192917028498209590966130418634623533661 0 2.7182818284590452353602874713526624977572470937000 +B 8 8 9 10 0.055977832428960101947066796213402815160174288113670 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 11 10 0.017331882612255949633159040980003606238560951671596 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 12 10 0.0056098726742168803147708657823600514861381321189421 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 13 9 0.0043262399888184560290948126014959383564324315568083 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 13 11 0.00081640118848870110971475157160415051431224866097255 0 67.645666868493653450016016424320161504144039086217 +B 8 9 8 9 0.35050318673764198274876117749270507469363645059004 0 2.7182818284590452353602874713526624977572470937000 +B 8 9 9 10 0.078018945484120142842800984233626873641877603972805 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 11 10 0.024156262327213636923104911987575711221318025903699 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 12 10 0.0078187441590923487736932350945817075453458256291214 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 13 11 0.0014752081085167496343904387917110573921004764244536 0 67.645666868493653450016016424320161504144039086217 +B 8 11 8 11 0.31538948138855917823351971945017121001929887905233 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 9 1 0.45864551887399724426219143266873814012488375979667 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 10 2 0.28960869226246130524416723696068783130239318180897 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 5 0.039390003485879270796907432056185853723190515037699 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 11 3 0.089781791464158297465236921125893124672468484002009 0 27.474593022118317968158957115156725710168223409107 +B 9 1 11 4 0.016972120734822743126837717390219876988142723075541 0 27.474593022118317968158957115156725710168223409107 +B 9 1 11 7 0.0043249509011285008217692540624630550146765253336753 0 47.560129945305985709087486769738443607156131247662 +B 9 1 12 3 0.025574500946424421817291165687570501751016697617350 0 27.474593022118317968158957115156725710168223409107 +B 9 1 12 4 0.0048345383926631925642507424732207035556285579645936 0 102.15827997845022478719774797259932200994686828628 +B 9 1 12 7 0.0012319698583682850375121091395571240011938566687308 0 196.92750385796979934716506848462363620671342100200 +B 9 1 13 2 0.011715859240087181199824680662651841929334248023129 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 13 5 0.0015934871729916041633936624627435530649328477370962 0 529.22064998316678774353996178834026893556120755030 +B 9 1 13 6 0.0012286677693305362484317406793882794678899897388330 0 549.30618690635445548446849144292198683254911538885 +B 9 3 9 3 0.65502364535839396692047218695068043500478156882186 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 5 0.20662088929342905134595595219794251521686260258080 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 8 0.021353789796192323403420074557504559224221256143955 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 4 0.11314445331262584207806318596026684838512671328838 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 7 0.019243178803067237979065020070329526817246704659551 0 27.474593022118317968158957115156725710168223409107 +B 9 3 12 4 0.032229396196461281771253400782545817339993203728505 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 12 7 0.0054814532711537847617816815036298021556902699390757 0 47.560129945305985709087486769738443607156131247662 +B 9 3 13 5 0.0083586622905283031514335111112017736516263403044089 0 47.560129945305985709087486769738443607156131247662 +B 9 3 13 6 0.0079938428868672352364715893278465000545834823061459 0 67.645666868493653450016016424320161504144039086217 +B 9 3 13 8 0.00086384836566946833346981889603300587084713174056983 0 271.61119081430170616620385934206623250649206587918 +B 9 3 13 9 0.00086384836566946833346981889603300587084713174056983 0 291.69672773748937390713238899664795040347997371773 +B 9 4 9 4 0.43044827215892337346995982993387168699614972392585 0 2.7182818284590452353602874713526624977572470937000 +B 9 4 10 5 0.26466650806515282310939906404939768598692095455018 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 10 8 0.021353789796192323403420074557504559224221256143955 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 11 7 0.039594257231674030564855204590244853942287415004903 0 27.474593022118317968158957115156725710168223409107 +B 9 4 11 10 0.0011631584427049402455021382305238495213099758982087 0 47.560129945305985709087486769738443607156131247662 +B 9 4 12 7 0.011278493696003603565266851231527473798857070507868 0 27.474593022118317968158957115156725710168223409107 +B 9 4 12 10 0.00033132772479455815582745080614122911258518134476522 0 122.24381690163789252812627762718103990693477612483 +B 9 4 13 5 0.010706845605471663453060304769501209069205261376786 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 13 6 0.010342026201810595538098382986145935472162403378522 0 27.474593022118317968158957115156725710168223409107 +B 9 4 13 8 0.00086384836566946833346981889603300587084713174056983 0 176.84196693478213160623653883004191830972551316345 +B 9 4 13 9 0.00086384836566946833346981889603300587084713174056983 0 196.92750385796979934716506848462363620671342100200 +B 9 7 9 7 0.47616341697589792546332481973311156465209040199147 0 2.7182818284590452353602874713526624977572470937000 +B 9 7 10 8 0.27157247857277435785067987946200653680342392291523 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 11 10 0.010648535817975621974303485726341466001409624204608 0 27.474593022118317968158957115156725710168223409107 +B 9 7 12 10 0.0030332541255157388008814258063889137784112073166622 0 27.474593022118317968158957115156725710168223409107 +B 9 7 13 8 0.010986220432765045369900837095941294735248532026603 0 7.3890560989306502272304274605750078131803155705521 +B 9 7 13 9 0.0086380371178216850682740434376418593176696109542266 0 27.474593022118317968158957115156725710168223409107 +B 9 7 13 11 0.00086384836566946833346981889603300587084713174056983 0 176.84196693478213160623653883004191830972551316345 +B 9 10 9 10 0.21655224757262596961054568858755596758904527270584 0 2.7182818284590452353602874713526624977572470937000 +B 9 10 13 11 0.0050361286989181682099062214711088092736403428042625 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 10 2 0.60300885188457322999987349067880702005615598535459 0 2.7182818284590452353602874713526624977572470937000 +B 10 2 11 3 0.25773571039378780858112985230263368932260713696953 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 11 4 0.12280730744776970308628656949707275295192986570530 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 11 7 0.029801477673300786226516751271714741729754484674866 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 12 3 0.11760391316702858923534424414438346237327243282251 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 12 4 0.056036549608502374179378228084593018269658116007404 0 27.474593022118317968158957115156725710168223409107 +B 10 2 12 7 0.013598311181578826226744882637554828721243622591192 0 47.560129945305985709087486769738443607156131247662 +B 10 2 13 5 0.0039314818428865115969818709348622797551196587481863 0 142.32935382482556026905480728176275780392268396339 +B 10 2 13 6 0.0034452830506793697648359155034897127832482737626705 0 142.32935382482556026905480728176275780392268396339 +B 10 2 13 8 0.00066506133218253537525645739213926158568069928128216 0 291.69672773748937390713238899664795040347997371773 +B 10 5 10 5 0.66204849467374585727175787986835068115159091735074 0 2.7182818284590452353602874713526624977572470937000 +B 10 5 11 7 0.16052123312680337235519656310802052174150293131345 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 10 0.013874177903184214645720059633484602942139493506475 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 7 0.073245283446619939567965741290472201168251347979744 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 10 0.0063307393876347797283608458738285412304850115169428 0 27.474593022118317968158957115156725710168223409107 +B 10 5 13 6 0.022335852823858939878547630214542877527206236597551 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 13 8 0.0052531071847619204901234627520950300370730782730271 0 47.560129945305985709087486769738443607156131247662 +B 10 5 13 9 0.0021236577088039608716943236862569625012948542378297 0 47.560129945305985709087486769738443607156131247662 +B 10 5 13 11 0.00066506133218253537525645739213926158568069928128216 0 142.32935382482556026905480728176275780392268396339 +B 10 8 10 8 0.55600892891044751110322935419632449580282248849947 0 2.7182818284590452353602874713526624977572470937000 +B 10 8 11 10 0.085093381768736033817376575886124984162356800097146 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 12 10 0.038827815770384808790790714878713835371064884035062 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 13 9 0.018257103880800900795985974446819867152057359399332 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 13 11 0.0034452830506793697648359155034897127832482737626705 0 47.560129945305985709087486769738443607156131247662 +B 11 1 11 1 0.30994660269094874800905949373720638030142612409410 0 2.7182818284590452353602874713526624977572470937000 +B 11 1 12 3 0.099440243643822040705605745931037703936859783037202 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 12 4 0.018797929886450164634042809999994748571387464594424 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 12 7 0.0047902159707681407422136065796312277643391046884581 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 2 0.037460200724813900325650236784022019918742598174163 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 5 0.0050950039710649130927241388595033562509932561935448 0 47.560129945305985709087486769738443607156131247662 +B 11 1 13 6 0.0039285331378638804671978133818274398623307901259791 0 47.560129945305985709087486769738443607156131247662 +B 11 3 11 3 0.44265635486749619908348237011683427207407487571736 0 2.7182818284590452353602874713526624977572470937000 +B 11 3 12 4 0.12531618962900854035698338699316810650781637458520 0 7.3890560989306502272304274605750078131803155705521 +B 11 3 12 7 0.021313301477421988713804455791338621676209564432415 0 7.3890560989306502272304274605750078131803155705521 +B 11 3 13 5 0.026725924302910365101734622481318593537770325722877 0 27.474593022118317968158957115156725710168223409107 +B 11 3 13 6 0.025559453469709332476208297003642677149107859655312 0 27.474593022118317968158957115156725710168223409107 +B 11 3 13 8 0.0027620623046628478416714879041515234736683240584135 0 47.560129945305985709087486769738443607156131247662 +B 11 3 13 9 0.0027620623046628478416714879041515234736683240584135 0 47.560129945305985709087486769738443607156131247662 +B 11 4 11 4 0.29089127463273068746599867706768353844031487350698 0 2.7182818284590452353602874713526624977572470937000 +B 11 4 12 7 0.043853687053968166738114251649480768894664213990854 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 12 10 0.0012882874918476347692563057245403475625770147119667 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 13 5 0.034233988074747095597202984223610301260376428184248 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 13 6 0.033067517241546062971676658745934384871713962116683 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 13 8 0.0027620623046628478416714879041515234736683240584135 0 27.474593022118317968158957115156725710168223409107 +B 11 4 13 9 0.0027620623046628478416714879041515234736683240584135 0 27.474593022118317968158957115156725710168223409107 +B 11 7 11 7 0.32178496757086814004684513241048172953218382843049 0 2.7182818284590452353602874713526624977572470937000 +B 11 7 12 10 0.011794072928609152688128208289812988167863284641441 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 8 0.035127259058411835074597585828670187141417666039031 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 9 0.027619195286575104579129224086378479418811563577659 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 11 0.0027620623046628478416714879041515234736683240584135 0 27.474593022118317968158957115156725710168223409107 +B 11 10 11 10 0.14634315757626384435379016201478029743690841754733 0 2.7182818284590452353602874713526624977572470937000 +B 11 10 13 11 0.016102480242504741464771477034822813139586756857614 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 12 1 0.30994660269094874800905949373720638030142612409410 0 2.7182818284590452353602874713526624977572470937000 +B 12 1 13 2 0.058733436378199013100464558527733921246250742517231 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 5 0.0079884006436460181924220389629409248114277119074589 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 6 0.0061595038640445255507279061545807441453130452790289 0 7.3890560989306502272304274605750078131803155705521 +B 12 3 12 3 0.44265635486749619908348237011683427207407487571736 0 2.7182818284590452353602874713526624977572470937000 +B 12 3 13 5 0.041903282532433545181737870621552948367482622391830 0 7.3890560989306502272304274605750078131803155705521 +B 12 3 13 6 0.040074385752832052540043737813192767701367955763400 0 7.3890560989306502272304274605750078131803155705521 +B 12 3 13 8 0.0043306070844430329090337733462205634791983786505986 0 7.3890560989306502272304274605750078131803155705521 +B 12 3 13 9 0.0043306070844430329090337733462205634791983786505986 0 7.3890560989306502272304274605750078131803155705521 +B 12 4 12 4 0.29089127463273068746599867706768353844031487350698 0 2.7182818284590452353602874713526624977572470937000 +B 12 4 13 5 0.053675093076271047587901434108571107143131046730058 0 7.3890560989306502272304274605750078131803155705521 +B 12 4 13 6 0.051846196296669554946207301300210926477016380101627 0 7.3890560989306502272304274605750078131803155705521 +B 12 4 13 8 0.0043306070844430329090337733462205634791983786505986 0 7.3890560989306502272304274605750078131803155705521 +B 12 4 13 9 0.0043306070844430329090337733462205634791983786505986 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 12 7 0.32178496757086814004684513241048172953218382843049 0 2.7182818284590452353602874713526624977572470937000 +B 12 7 13 8 0.055075642818996027817076292911013559914021409260370 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 13 9 0.043303832275158525410912729423995401138372984922141 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 13 11 0.0043306070844430329090337733462205634791983786505986 0 7.3890560989306502272304274605750078131803155705521 +B 12 10 12 10 0.14634315757626384435379016201478029743690841754733 0 2.7182818284590452353602874713526624977572470937000 +B 12 10 13 11 0.025246901526287998523668000875039625585420136130977 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 13 0 0.10115161711882714405573291827774201598858485465872 0 2.7182818284590452353602874713526624977572470937000 +B 13 2 13 2 0.14572305684656193022178480569403127958960418281678 0 2.7182818284590452353602874713526624977572470937000 +B 13 5 13 5 0.15999057082331225157707155011831295869410769484598 0 2.7182818284590452353602874713526624977572470937000 +B 13 6 13 6 0.18795528895224455870899817213636362611967657856689 0 2.7182818284590452353602874713526624977572470937000 +B 13 8 13 8 0.13436506031643210212398344907421920833948574353304 0 2.7182818284590452353602874713526624977572470937000 +B 13 9 13 9 0.19625864975164579822606080483548292420740180078546 0 2.7182818284590452353602874713526624977572470937000 +B 13 11 13 11 0.17659729242211473061119681551655155486955813928314 0 2.7182818284590452353602874713526624977572470937000 +P 0 11 0 0E+0 +P 0 10 1 0E+0 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 1 0E+0 +P 0 6 0 0E+0 +P 0 5 0 0E+0 +P 0 4 1 0E+0 +P 0 3 1 0E+0 +P 0 2 0 0E+0 +P 0 1 1 0E+0 +P 0 0 0 0E+0 +P 1 11 0 0E+0 +P 1 10 1 0E+0 +P 1 9 0 0E+0 +P 1 8 0 0E+0 +P 1 7 1 0E+0 +P 1 6 0 0E+0 +P 1 5 0 0E+0 +P 1 4 1 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 11 0 0E+0 +P 2 10 0 0E+0 +P 2 9 0 0E+0 +P 2 8 1 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 1 0E+0 +P 2 4 0 0E+0 +P 2 3 0 0E+0 +P 2 2 1 0E+0 +P 2 1 0 0E+0 +P 2 0 0 0E+0 +P 3 11 1 0E+0 +P 3 10 0 0E+0 +P 3 9 1 0E+0 +P 3 8 1 0E+0 +P 3 7 0 0E+0 +P 3 6 1 0E+0 +P 3 5 1 0E+0 +P 3 4 0 0E+0 +P 3 3 0 0E+0 +P 3 2 1 0E+0 +P 3 1 0 0E+0 +P 3 0 1 0E+0 +P 4 11 0 0E+0 +P 4 10 1 0E+0 +P 4 9 0 0E+0 +P 4 8 0 0E+0 +P 4 7 1 0E+0 +P 4 6 0 0E+0 +P 4 5 0 0E+0 +P 4 4 1 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 0 0E+0 +P 5 11 0 0E+0 +P 5 10 1 0E+0 +P 5 9 0 0E+0 +P 5 8 0 0E+0 +P 5 7 1 0E+0 +P 5 6 0 0E+0 +P 5 5 0 0E+0 +P 5 4 1 0E+0 +P 5 3 0 0E+0 +P 5 2 0 0E+0 +P 5 1 0 0E+0 +P 5 0 0 0E+0 +P 6 11 0 0E+0 +P 6 10 1 0E+0 +P 6 9 0 0E+0 +P 6 8 0 0E+0 +P 6 7 1 0E+0 +P 6 6 0 0E+0 +P 6 5 0 0E+0 +P 6 4 1 0E+0 +P 6 3 1 0E+0 +P 6 2 0 0E+0 +P 6 1 1 0E+0 +P 6 0 0 0E+0 +P 7 11 1 0E+0 +P 7 10 0 0E+0 +P 7 9 1 0E+0 +P 7 8 1 0E+0 +P 7 7 0 0E+0 +P 7 6 1 0E+0 +P 7 5 1 0E+0 +P 7 4 0 0E+0 +P 7 3 0 0E+0 +P 7 2 1 0E+0 +P 7 1 0 0E+0 +P 7 0 1 0E+0 +P 8 11 1 0E+0 +P 8 10 0 0E+0 +P 8 9 1 0E+0 +P 8 8 1 0E+0 +P 8 7 0 0E+0 +P 8 6 1 0E+0 +P 8 5 1 0E+0 +P 8 4 0 0E+0 +P 8 3 0 0E+0 +P 8 2 1 0E+0 +P 8 1 0 0E+0 +P 8 0 1 1.1005735584612756575783984806581962556404642459653E-1 +P 9 11 0 0E+0 +P 9 10 1 0E+0 +P 9 9 0 0E+0 +P 9 8 0 0E+0 +P 9 7 1 0E+0 +P 9 6 0 0E+0 +P 9 5 0 0E+0 +P 9 4 1 0E+0 +P 9 3 1 0E+0 +P 9 2 0 0E+0 +P 9 1 1 1.1005735584612756575783984806581962556404642459653E-1 +P 9 0 0 0E+0 +P 10 11 0 0E+0 +P 10 10 0 0E+0 +P 10 9 0 0E+0 +P 10 8 1 0E+0 +P 10 7 0 0E+0 +P 10 6 0 0E+0 +P 10 5 1 0E+0 +P 10 4 0 0E+0 +P 10 3 0 0E+0 +P 10 2 1 1.1005735584612756575783984806581962556404642459653E-1 +P 10 1 0 0E+0 +P 10 0 0 0E+0 +P 11 11 0 0E+0 +P 11 10 1 0E+0 +P 11 9 0 0E+0 +P 11 8 0 0E+0 +P 11 7 1 0E+0 +P 11 6 0 0E+0 +P 11 5 0 0E+0 +P 11 4 1 0E+0 +P 11 3 1 1.1005735584612756575783984806581962556404642459653E-1 +P 11 2 0 0E+0 +P 11 1 1 0E+0 +P 11 0 0 0E+0 +P 12 11 0 0E+0 +P 12 10 1 0E+0 +P 12 9 0 0E+0 +P 12 8 0 0E+0 +P 12 7 1 0E+0 +P 12 6 0 0E+0 +P 12 5 0 0E+0 +P 12 4 1 1.1005735584612756575783984806581962556404642459653E-1 +P 12 3 1 0E+0 +P 12 2 0 0E+0 +P 12 1 1 0E+0 +P 12 0 0 0E+0 +P 13 11 1 0E+0 +P 13 10 0 0E+0 +P 13 9 1 0E+0 +P 13 8 1 0E+0 +P 13 7 0 0E+0 +P 13 6 1 0E+0 +P 13 5 1 1.1005735584612756575783984806581962556404642459653E-1 +P 13 4 0 0E+0 +P 13 3 0 0E+0 +P 13 2 1 0E+0 +P 13 1 0 0E+0 +P 13 0 1 0E+0 +END +CASE random08 10 14 7 +Z 2.6189396722694609712858318187062440939281069588821E+0 +S 3 7 4 +S 5 3 4 +B 0 4 0 4 0.33048882165305291504344256279255451441825454156388 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 7 0.016991409939943731326339904309512221901110099213043 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 9 0.0045696939403287305353706025612845839731925716873829 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 7 0.014890114977677886489757365674819381277454305356583 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 9 0.0040045686864593439619620997921710435612203524565393 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 6 0.015888007152973324950997356389453225866949813297540 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 7 0.0049756131409864156517276328886368033609317078513118 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 3 9 0.0013381484703241125519710583883646625255923393692360 0 47.560129945305985709087486769738443607156131247662 +B 0 4 4 5 0.0091782030655181053248927101580224846082432955642313 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 8 0.0028743181787205708151450942539439627372759139948521 0 87.731203791681321190944546078901879401131946924772 +B 0 4 4 10 0.00077302321645472597856255561925112211362012013839238 0 311.78226466067704164806091865122966830046788155628 +B 0 4 5 6 0.0024683989780628856987880639265917433495367778309232 0 27.474593022118317968158957115156725710168223409107 +B 0 4 5 7 0.00077302321645472597856255561925112211362012013839238 0 47.560129945305985709087486769738443607156131247662 +B 0 4 5 9 0.00020789796258533940515405285013758170164790090754877 0 346.29487777063361298524265019950882880627071075635 +B 0 4 6 5 0.0024683989780628856987880639265917433495367778309232 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 6 8 0.00077302321645472597856255561925112211362012013839238 0 311.78226466067704164806091865122966830046788155628 +B 0 4 6 10 0.00020789796258533940515405285013758170164790090754877 148.41315910257660342111558004055227962348766759389 1567.2253770152202986314837026829865158921500050328 +B 0 5 0 5 0.16476506760985839961885916240174236090806541568812 0 2.7182818284590452353602874713526624977572470937000 +B 0 5 1 7 0.041834841939173732908278507805967497756945154264362 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 9 0.016991409939943731326339904309512221901110099213043 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 2 7 0.036661207560114971545347897440116056709922211156669 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 2 9 0.014890114977677886489757365674819381277454305356583 0 27.474593022118317968158957115156725710168223409107 +B 0 5 3 6 0.042938244726144139283621055726026493791105440179865 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 7 0.012250542482311021851240781889181085031610444815464 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 9 0.0049756131409864156517276328886368033609317078513118 0 47.560129945305985709087486769738443607156131247662 +B 0 5 4 8 0.0070769081032522604883101715233296439845875017077715 0 87.731203791681321190944546078901879401131946924772 +B 0 5 4 10 0.0028743181787205708151450942539439627372759139948521 0 311.78226466067704164806091865122966830046788155628 +B 0 5 4 11 0.0028743181787205708151450942539439627372759139948521 0 311.78226466067704164806091865122966830046788155628 +B 0 5 5 6 0.0066709889025945753719531411959774245968483655438427 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 5 7 0.0019032737241934991253795611574782029375645586000796 0 27.474593022118317968158957115156725710168223409107 +B 0 5 5 9 0.00077302321645472597856255561925112211362012013839238 0 326.20934084744594524431412054492711090928280291780 +B 0 5 6 8 0.0019032737241934991253795611574782029375645586000796 0 182.50042767120089575091186659092619359789849964050 +B 0 5 6 10 0.00077302321645472597856255561925112211362012013839238 148.41315910257660342111558004055227962348766759389 1383.3453899925999136562243894198221627867898860783 +B 0 5 6 11 0.00077302321645472597856255561925112211362012013839238 0 2379.2706135581036686283870676566391479972636235627 +B 1 7 1 7 0.28148331799486344659849767489800235296208560547856 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 2 9 0.061071872637918921239455012991051028388233977497875 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 9 0.022597811240428544577102002620883967125656331030847 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 13 0.0091782030655181053248927101580224846082432955642313 0 27.474593022118317968158957115156725710168223409107 +B 1 7 4 8 0.061071872637918921239455012991051028388233977497875 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 4 10 0.017424176861369783214171392255032526078633387923156 0 47.560129945305985709087486769738443607156131247662 +B 1 7 4 11 0.017424176861369783214171392255032526078633387923156 0 47.560129945305985709087486769738443607156131247662 +B 1 7 4 12 0.017424176861369783214171392255032526078633387923156 0 47.560129945305985709087486769738443607156131247662 +B 1 7 5 9 0.0070769081032522604883101715233296439845875017077715 0 27.474593022118317968158957115156725710168223409107 +B 1 7 5 13 0.0028743181787205708151450942539439627372759139948521 0 455.49117783692209114146317260523058561185218483358 +B 1 7 6 8 0.024804616814369357327787098461001959193976902861854 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 6 10 0.0070769081032522604883101715233296439845875017077715 0 142.32935382482556026905480728176275780392268396339 +B 1 7 6 11 0.0070769081032522604883101715233296439845875017077715 0 271.61119081430170616620385934206623250649206587918 +B 1 7 6 12 0.0070769081032522604883101715233296439845875017077715 0 400.89302780377785206335291140236970720906144779496 +B 1 9 1 9 0.60557780596085256576292347083901110332094601401879 0 2.7182818284590452353602874713526624977572470937000 +B 1 9 3 13 0.034127145676423074820933870859902911908743128590097 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 4 10 0.13138885423635105608068448708721147630956337812699 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 4 11 0.084025030898233013813016192263663766509742794641828 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 4 12 0.036661207560114971545347897440116056709922211156669 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 5 13 0.010687525053146196816592288405433700030142717643663 0 67.645666868493653450016016424320161504144039086217 +B 1 9 6 10 0.053364176375168263152110376044986442540031951823610 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 6 11 0.034127145676423074820933870859902911908743128590097 0 27.474593022118317968158957115156725710168223409107 +B 1 9 6 12 0.014890114977677886489757365674819381277454305356583 0 47.560129945305985709087486769738443607156131247662 +B 2 7 2 7 0.28148331799486344659849767489800235296208560547856 0 2.7182818284590452353602874713526624977572470937000 +B 2 7 3 9 0.041834841939173732908278507805967497756945154264362 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 3 13 0.016991409939943731326339904309512221901110099213043 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 4 8 0.12849781178630883570719124787169329171380834105210 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 4 10 0.036661207560114971545347897440116056709922211156669 0 27.474593022118317968158957115156725710168223409107 +B 2 7 4 11 0.036661207560114971545347897440116056709922211156669 0 27.474593022118317968158957115156725710168223409107 +B 2 7 4 12 0.036661207560114971545347897440116056709922211156669 0 27.474593022118317968158957115156725710168223409107 +B 2 7 5 9 0.026313938801997448819486676708413174615876324941286 0 27.474593022118317968158957115156725710168223409107 +B 2 7 5 13 0.010687525053146196816592288405433700030142717643663 0 271.61119081430170616620385934206623250649206587918 +B 2 7 6 8 0.024804616814369357327787098461001959193976902861854 0 7.3890560989306502272304274605750078131803155705521 +B 2 7 6 10 0.0070769081032522604883101715233296439845875017077715 0 122.24381690163789252812627762718103990693477612483 +B 2 7 6 11 0.0070769081032522604883101715233296439845875017077715 0 196.92750385796979934716506848462363620671342100200 +B 2 7 6 12 0.0070769081032522604883101715233296439845875017077715 0 271.61119081430170616620385934206623250649206587918 +B 2 7 8 9 0.0019032737241934991253795611574782029375645586000796 0 47.560129945305985709087486769738443607156131247662 +B 2 7 8 13 0.00077302321645472597856255561925112211362012013839238 0 1972.2937804765747734129733834954799189686371921373 +B 2 9 2 9 0.60557780596085256576292347083901110332094601401879 0 2.7182818284590452353602874713526624977572470937000 +B 2 9 3 13 0.063178850819591173308228602100773567214147197503087 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 10 0.27644772516765442715333512983077384635176901904036 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 11 0.17679223084762852975785353477267717585275865934736 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 12 0.077136736527602632362371939714580505353748299654365 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 5 13 0.039739230196314295303887019646304355335546786556655 0 67.645666868493653450016016424320161504144039086217 +B 2 9 6 10 0.053364176375168263152110376044986442540031951823610 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 6 11 0.034127145676423074820933870859902911908743128590097 0 27.474593022118317968158957115156725710168223409107 +B 2 9 6 12 0.014890114977677886489757365674819381277454305356583 0 47.560129945305985709087486769738443607156131247662 +B 2 9 8 13 0.0028743181787205708151450942539439627372759139948521 0 291.69672773748937390713238899664795040347997371773 +B 3 1 3 1 0.39820156837986849357813618739426234501610579526901 0 2.7182818284590452353602874713526624977572470937000 +B 3 1 4 4 0.19592375093469875017492748275233555503938270460633 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 4 5 0.072495696462238483569184324024644821726655997082579 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 2 0.29700198656029549201637452354475457183687181906066 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 3 0.29700198656029549201637452354475457183687181906066 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 6 0.059076088287328044316975031561783339209242961615963 0 47.560129945305985709087486769738443607156131247662 +B 3 1 5 7 0.018500731927571822818039482556923437323009521292474 0 47.560129945305985709087486769738443607156131247662 +B 3 1 6 4 0.024804616814369357327787098461001959193976902861854 0 47.560129945305985709087486769738443607156131247662 +B 3 1 6 5 0.0091782030655181053248927101580224846082432955642313 0 67.645666868493653450016016424320161504144039086217 +B 3 1 7 4 0.024804616814369357327787098461001959193976902861854 0 47.560129945305985709087486769738443607156131247662 +B 3 1 7 5 0.0091782030655181053248927101580224846082432955642313 0 196.92750385796979934716506848462363620671342100200 +B 3 1 8 2 0.012409748535522723308292254330942406055843527882378 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 8 3 0.012409748535522723308292254330942406055843527882378 0 27.474593022118317968158957115156725710168223409107 +B 3 1 8 6 0.0024683989780628856987880639265917433495367778309232 148.41315910257660342111558004055227962348766759389 917.06616095159522543498711796925069304326935329767 +B 3 1 8 7 0.00077302321645472597856255561925112211362012013839238 0 1194.7611570436479747532517500701064473693264028074 +B 3 1 9 4 0.0066709889025945753719531411959774245968483655438427 0 142.32935382482556026905480728176275780392268396339 +B 3 1 9 5 0.0024683989780628856987880639265917433495367778309232 0 420.97856472696551980428144105695142510604935563351 +B 3 2 3 2 0.61350444123810380558157925512175779452701763753189 0 2.7182818284590452353602874713526624977572470937000 +B 3 2 4 4 0.39298748526948431230843403600938131850850123157665 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 4 5 0.072495696462238483569184324024644821726655997082579 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 4 8 0.022703321852103512491204559826309118570321109005395 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 3 0.45758744384818155441000804786090096322240371544586 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 6 0.059076088287328044316975031561783339209242961615963 0 47.560129945305985709087486769738443607156131247662 +B 3 2 5 7 0.018500731927571822818039482556923437323009521292474 0 47.560129945305985709087486769738443607156131247662 +B 3 2 6 4 0.049753559425274326823828259162882386494476735887720 0 27.474593022118317968158957115156725710168223409107 +B 3 2 6 5 0.0091782030655181053248927101580224846082432955642313 0 47.560129945305985709087486769738443607156131247662 +B 3 2 6 8 0.0028743181787205708151450942539439627372759139948521 0 326.20934084744594524431412054492711090928280291780 +B 3 2 7 4 0.049753559425274326823828259162882386494476735887720 0 27.474593022118317968158957115156725710168223409107 +B 3 2 7 5 0.0091782030655181053248927101580224846082432955642313 0 122.24381690163789252812627762718103990693477612483 +B 3 2 8 3 0.019119552622977942934396900562373147314550045615686 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 8 6 0.0024683989780628856987880639265917433495367778309232 148.41315910257660342111558004055227962348766759389 530.17486479325399796050196346267318191163082971075 +B 3 2 8 7 0.00077302321645472597856255561925112211362012013839238 0 807.86986088530674727876659556352893623768787922043 +B 3 2 9 4 0.013380792990049794998057787427408165855554883277151 0 47.560129945305985709087486769738443607156131247662 +B 3 2 9 5 0.0024683989780628856987880639265917433495367778309232 0 217.01304078115746708809359813920535410370132884056 +B 3 3 3 3 0.61350444123810380558157925512175779452701763753189 0 2.7182818284590452353602874713526624977572470937000 +B 3 3 4 4 0.39298748526948431230843403600938131850850123157665 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 5 0.072495696462238483569184324024644821726655997082579 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 8 0.022703321852103512491204559826309118570321109005395 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 6 0.059076088287328044316975031561783339209242961615963 0 47.560129945305985709087486769738443607156131247662 +B 3 3 5 7 0.018500731927571822818039482556923437323009521292474 0 47.560129945305985709087486769738443607156131247662 +B 3 3 5 9 0.0049756131409864156517276328886368033609317078513118 0 67.645666868493653450016016424320161504144039086217 +B 3 3 6 4 0.049753559425274326823828259162882386494476735887720 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 6 5 0.0091782030655181053248927101580224846082432955642313 0 27.474593022118317968158957115156725710168223409107 +B 3 3 6 8 0.0028743181787205708151450942539439627372759139948521 0 306.12380392425827750338559089034539301229489507924 +B 3 3 7 4 0.049753559425274326823828259162882386494476735887720 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 7 5 0.0091782030655181053248927101580224846082432955642313 0 47.560129945305985709087486769738443607156131247662 +B 3 3 8 6 0.0024683989780628856987880639265917433495367778309232 0 291.69672773748937390713238899664795040347997371773 +B 3 3 8 7 0.00077302321645472597856255561925112211362012013839238 0 420.97856472696551980428144105695142510604935563351 +B 3 3 8 9 0.00020789796258533940515405285013758170164790090754877 0 1402.4767121057003711801909174000709677077081717564 +B 3 3 9 4 0.013380792990049794998057787427408165855554883277151 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 9 5 0.0024683989780628856987880639265917433495367778309232 0 67.645666868493653450016016424320161504144039086217 +B 3 6 3 6 0.61350444123810380558157925512175779452701763753189 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 4 8 0.19592375093469875017492748275233555503938270460633 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 10 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 11 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 12 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 5 7 0.15965649511114918626325956822228648584512562997031 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 5 9 0.045550969500742637150663181893496705247165148174799 0 27.474593022118317968158957115156725710168223409107 +B 3 6 6 8 0.024804616814369357327787098461001959193976902861854 0 27.474593022118317968158957115156725710168223409107 +B 3 6 6 10 0.0070769081032522604883101715233296439845875017077715 0 122.24381690163789252812627762718103990693477612483 +B 3 6 6 11 0.0070769081032522604883101715233296439845875017077715 0 142.32935382482556026905480728176275780392268396339 +B 3 6 6 12 0.0070769081032522604883101715233296439845875017077715 0 162.41489074801322800998333693634447570091059180195 +B 3 6 8 7 0.0066709889025945753719531411959774245968483655438427 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 8 9 0.0019032737241934991253795611574782029375645586000796 0 122.24381690163789252812627762718103990693477612483 +B 3 7 3 7 0.21405737884647353213076144001736008963651124192434 0 2.7182818284590452353602874713526624977572470937000 +B 3 7 4 8 0.19592375093469875017492748275233555503938270460633 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 4 10 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 4 11 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 4 12 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 5 9 0.045550969500742637150663181893496705247165148174799 0 27.474593022118317968158957115156725710168223409107 +B 3 7 5 13 0.018500731927571822818039482556923437323009521292474 0 87.731203791681321190944546078901879401131946924772 +B 3 7 6 8 0.024804616814369357327787098461001959193976902861854 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 10 0.0070769081032522604883101715233296439845875017077715 54.598150033144239078110261202860878402790737038617 47.560129945305985709087486769738443607156131247662 +B 3 7 6 11 0.0070769081032522604883101715233296439845875017077715 0 122.24381690163789252812627762718103990693477612483 +B 3 7 6 12 0.0070769081032522604883101715233296439845875017077715 0 142.32935382482556026905480728176275780392268396339 +B 3 7 8 9 0.0019032737241934991253795611574782029375645586000796 0 47.560129945305985709087486769738443607156131247662 +B 3 7 8 13 0.00077302321645472597856255561925112211362012013839238 148.41315910257660342111558004055227962348766759389 1030.9667069442152575189209664615238121609541916915 +B 3 9 3 9 0.46051893502954919469027282809544873327874037310540 0 2.7182818284590452353602874713526624977572470937000 +B 3 9 4 10 0.42150659609895779822598577257433621639397465995371 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 4 11 0.26955943079702404570269087728169058519577452405289 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 4 12 0.11761226549509029317939598198904495399757438815206 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 5 13 0.068790935339482393791181750887175010640950855469644 0 67.645666868493653450016016424320161504144039086217 +B 3 9 6 10 0.053364176375168263152110376044986442540031951823610 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 6 11 0.034127145676423074820933870859902911908743128590097 0 27.474593022118317968158957115156725710168223409107 +B 3 9 6 12 0.014890114977677886489757365674819381277454305356583 0 47.560129945305985709087486769738443607156131247662 +B 3 9 8 13 0.0028743181787205708151450942539439627372759139948521 0 291.69672773748937390713238899664795040347997371773 +B 3 13 3 13 0.33077562923147857911039995251362008169053143171477 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 4 0 0.62942334578478712888362208362083253458728556112915 0 2.7182818284590452353602874713526624977572470937000 +B 4 0 5 1 0.41461830172832741677534152002525295523798016496626 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 2 0.41461830172832741677534152002525295523798016496626 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 3 0.41461830172832741677534152002525295523798016496626 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 5 6 0.049188434171404940250419756393768846082504516656876 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 6 4 0.091665430708889885222114830572530682107579047185235 0 67.645666868493653450016016424320161504144039086217 +B 4 0 6 5 0.022138196598234125917796057057195578158348889774551 0 67.645666868493653450016016424320161504144039086217 +B 4 0 7 4 0.035663315384819533084107906725482212120426703215713 0 67.645666868493653450016016424320161504144039086217 +B 4 0 7 5 0.0086130778116487187514842073889089441962710763333876 0 251.52565389111403842527532968748451460950415804062 +B 4 0 8 1 0.041940352550848700822381065011392649201609932238908 0 7.3890560989306502272304274605750078131803155705521 +B 4 0 8 2 0.041940352550848700822381065011392649201609932238908 0 27.474593022118317968158957115156725710168223409107 +B 4 0 8 3 0.041940352550848700822381065011392649201609932238908 0 47.560129945305985709087486769738443607156131247662 +B 4 0 8 6 0.0049756131409864156517276328886368033609317078513118 148.41315910257660342111558004055227962348766759389 1154.5900831972726392713946907609430115753505871302 +B 4 0 9 4 0.0055407383948558022251361356577503437729039270821554 0 291.69672773748937390713238899664795040347997371773 +B 4 0 9 5 0.0013381484703241125519710583883646625255923393692360 0 659.45670178273014385765101552297665661420021162654 +B 4 4 4 4 0.56460444819158930505073692443263078451551347942110 0 2.7182818284590452353602874713526624977572470937000 +B 4 4 5 6 0.090734834985688233439120838495094507768449312375136 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 7 0.028415233764263293656069215343106015239532118797745 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 5 9 0.0076420333571216470617186742924431843965597209386152 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 5 0.040836949763878294447038517091333653167449646323406 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 6 8 0.012788820015412041653174827040126540653798511500123 0 47.560129945305985709087486769738443607156131247662 +B 4 4 6 10 0.0034394434325899573885535970230575031492481332256957 0 67.645666868493653450016016424320161504144039086217 +B 4 4 7 5 0.015888007152973324950997356389453225866949813297540 0 7.3890560989306502272304274605750078131803155705521 +B 4 4 8 6 0.0091782030655181053248927101580224846082432955642313 0 47.560129945305985709087486769738443607156131247662 +B 4 4 8 7 0.0028743181787205708151450942539439627372759139948521 0 67.645666868493653450016016424320161504144039086217 +B 4 4 8 9 0.00077302321645472597856255561925112211362012013839238 0 217.01304078115746708809359813920535410370132884056 +B 4 4 9 5 0.0024683989780628856987880639265917433495367778309232 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 4 5 0.28148331799486344659849767489800235296208560547856 0 2.7182818284590452353602874713526624977572470937000 +B 4 5 5 6 0.24521606217131388268682976036795328376782853084255 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 7 0.069961634578546586844770297444431676925476914516007 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 9 0.028415233764263293656069215343106015239532118797745 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 8 0.031487573181056210182417287074264615662899268048979 0 47.560129945305985709087486769738443607156131247662 +B 4 5 6 10 0.012788820015412041653174827040126540653798511500123 0 67.645666868493653450016016424320161504144039086217 +B 4 5 6 11 0.012788820015412041653174827040126540653798511500123 0 67.645666868493653450016016424320161504144039086217 +B 4 5 8 6 0.024804616814369357327787098461001959193976902861854 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 8 7 0.0070769081032522604883101715233296439845875017077715 0 27.474593022118317968158957115156725710168223409107 +B 4 5 8 9 0.0028743181787205708151450942539439627372759139948521 0 176.84196693478213160623653883004191830972551316345 +B 4 8 4 8 0.80675222061905190279078962756087889726350881876597 0 2.7182818284590452353602874713526624977572470937000 +B 4 8 5 9 0.52755312809966977283434935979678606955141458771542 0 7.3890560989306502272304274605750078131803155705521 +B 4 8 5 13 0.10565584735707611827992367627953540323922172803145 0 7.3890560989306502272304274605750078131803155705521 +B 4 8 6 10 0.23743538623706303068904807430966132946700330588868 0 27.474593022118317968158957115156725710168223409107 +B 4 8 6 11 0.15184328823672356026181237407079674855225882632150 0 27.474593022118317968158957115156725710168223409107 +B 4 8 6 12 0.066251190236384089834576673831932167637514346754320 0 27.474593022118317968158957115156725710168223409107 +B 4 8 8 9 0.053364176375168263152110376044986442540031951823610 0 7.3890560989306502272304274605750078131803155705521 +B 4 8 8 13 0.010687525053146196816592288405433700030142717643663 0 251.52565389111403842527532968748451460950415804062 +B 4 10 4 10 0.80675222061905190279078962756087889726350881876597 0 2.7182818284590452353602874713526624977572470937000 +B 4 10 5 13 0.22444286176417223609247921931729045292485613298340 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 6 11 0.23071989893020140383075237760320089000603936200662 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 6 12 0.14512780092986193340351667736433630909129488243943 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 8 13 0.022703321852103512491204559826309118570321109005395 0 47.560129945305985709087486769738443607156131247662 +B 4 11 4 11 0.58844998801552386817143553721445221578185302653146 0 2.7182818284590452353602874713526624977572470937000 +B 4 11 5 13 0.22444286176417223609247921931729045292485613298340 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 6 12 0.14512780092986193340351667736433630909129488243943 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 8 13 0.022703321852103512491204559826309118570321109005395 0 27.474593022118317968158957115156725710168223409107 +B 4 12 4 12 0.58844998801552386817143553721445221578185302653146 0 2.7182818284590452353602874713526624977572470937000 +B 4 12 5 13 0.30168347535698506071633368025371984092454574221711 0 7.3890560989306502272304274605750078131803155705521 +B 4 12 8 13 0.030516528726529138492651753977798855863187912654204 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 5 1 0.58159422458506826072445679275856673761790011023893 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 4 0.17779012302292396821909352548731102044225416728832 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 5 0.065785892374783263943079677793214080467949479349271 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 4 0.11036418387453405375135729060666875711667980373409 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 5 0.040836949763878294447038517091333653167449646323406 0 27.474593022118317968158957115156725710168223409107 +B 5 1 8 2 0.046142942475380390495546142280778330448921519951829 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 3 0.046142942475380390495546142280778330448921519951829 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 6 0.0091782030655181053248927101580224846082432955642313 0 142.32935382482556026905480728176275780392268396339 +B 5 1 8 7 0.0028743181787205708151450942539439627372759139948521 0 142.32935382482556026905480728176275780392268396339 +B 5 1 9 4 0.0066709889025945753719531411959774245968483655438427 0 47.560129945305985709087486769738443607156131247662 +B 5 1 9 5 0.0024683989780628856987880639265917433495367778309232 0 87.731203791681321190944546078901879401131946924772 +B 5 2 5 2 0.89605533507338516588561919515942877913025738527603 0 2.7182818284590452353602874713526624977572470937000 +B 5 2 6 4 0.35661471883425978048266356427390709786957937896608 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 5 0.065785892374783263943079677793214080467949479349271 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 6 8 0.020602026889837667654622021191616277946665315148934 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 4 0.22137052234737931956613114758613185250148898373217 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 7 5 0.040836949763878294447038517091333653167449646323406 0 27.474593022118317968158957115156725710168223409107 +B 5 2 8 3 0.071091885086285359991587302982658757749421352977695 0 7.3890560989306502272304274605750078131803155705521 +B 5 2 8 6 0.0091782030655181053248927101580224846082432955642313 0 142.32935382482556026905480728176275780392268396339 +B 5 2 8 7 0.0028743181787205708151450942539439627372759139948521 0 142.32935382482556026905480728176275780392268396339 +B 5 2 9 4 0.013380792990049794998057787427408165855554883277151 0 27.474593022118317968158957115156725710168223409107 +B 5 2 9 5 0.0024683989780628856987880639265917433495367778309232 0 67.645666868493653450016016424320161504144039086217 +B 5 3 5 3 0.89605533507338516588561919515942877913025738527603 0 2.7182818284590452353602874713526624977572470937000 +B 5 3 6 4 0.35661471883425978048266356427390709786957937896608 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 5 0.065785892374783263943079677793214080467949479349271 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 8 0.020602026889837667654622021191616277946665315148934 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 4 0.22137052234737931956613114758613185250148898373217 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 5 0.040836949763878294447038517091333653167449646323406 0 27.474593022118317968158957115156725710168223409107 +B 5 3 8 6 0.0091782030655181053248927101580224846082432955642313 54.598150033144239078110261202860878402790737038617 87.731203791681321190944546078901879401131946924772 +B 5 3 8 7 0.0028743181787205708151450942539439627372759139948521 0 142.32935382482556026905480728176275780392268396339 +B 5 3 8 9 0.00077302321645472597856255561925112211362012013839238 0 162.41489074801322800998333693634447570091059180195 +B 5 3 9 4 0.013380792990049794998057787427408165855554883277151 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 9 5 0.0024683989780628856987880639265917433495367778309232 0 47.560129945305985709087486769738443607156131247662 +B 5 6 5 6 0.89605533507338516588561919515942877913025738527603 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 6 8 0.17779012302292396821909352548731102044225416728832 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 10 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 11 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 12 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 8 7 0.024804616814369357327787098461001959193976902861854 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 8 9 0.0070769081032522604883101715233296439845875017077715 0 27.474593022118317968158957115156725710168223409107 +B 5 7 5 7 0.31264200131970379715456599524859554709340289439677 0 2.7182818284590452353602874713526624977572470937000 +B 5 7 6 8 0.17779012302292396821909352548731102044225416728832 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 6 10 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 6 11 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 6 12 0.050724603879801398513593792259348146294188091282492 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 8 9 0.0070769081032522604883101715233296439845875017077715 0 27.474593022118317968158957115156725710168223409107 +B 5 7 8 13 0.0028743181787205708151450942539439627372759139948521 0 87.731203791681321190944546078901879401131946924772 +B 5 9 5 9 0.67261199903097314390700000254034843959362022862879 0 2.7182818284590452353602874713526624977572470937000 +B 5 9 6 10 0.38249425716836640176169871705322369950920894680203 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 6 11 0.24461048818611907620664971657981015789527469102702 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 6 12 0.10672671920387175065160071610639661628134043525201 0 7.3890560989306502272304274605750078131803155705521 +B 5 9 8 13 0.010687525053146196816592288405433700030142717643663 0 67.645666868493653450016016424320161504144039086217 +B 5 13 5 13 0.48311511272351296361475479473275643160567323574107 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 6 0 0.47865256294132397664610678536047326787586891042343 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 7 4 0.12178800769885361608108660164026255045510182331879 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 5 0.029413125939558732117309206057739859829027626738701 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 1 0.10326206159697820214419269744874729714103161787534 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 2 0.10326206159697820214419269744874729714103161787534 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 3 0.10326206159697820214419269744874729714103161787534 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 6 0.012250542482311021851240781889181085031610444815464 0 47.560129945305985709087486769738443607156131247662 +B 6 0 9 4 0.020602026889837667654622021191616277946665315148934 0 67.645666868493653450016016424320161504144039086217 +B 6 0 9 5 0.0049756131409864156517276328886368033609317078513118 0 87.731203791681321190944546078901879401131946924772 +B 6 4 6 4 0.42936025170470884413420450774485553914742308418722 0 2.7182818284590452353602874713526624977572470937000 +B 6 4 7 5 0.054256557938788733699247809554195135684862681790020 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 8 6 0.022597811240428544577102002620883967125656331030847 0 27.474593022118317968158957115156725710168223409107 +B 6 4 8 7 0.0070769081032522604883101715233296439845875017077715 0 27.474593022118317968158957115156725710168223409107 +B 6 4 8 9 0.0019032737241934991253795611574782029375645586000796 0 27.474593022118317968158957115156725710168223409107 +B 6 4 9 5 0.0091782030655181053248927101580224846082432955642313 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 6 5 0.21405737884647353213076144001736008963651124192434 0 2.7182818284590452353602874713526624977572470937000 +B 6 5 8 6 0.061071872637918921239455012991051028388233977497875 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 7 0.017424176861369783214171392255032526078633387923156 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 9 0.0070769081032522604883101715233296439845875017077715 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 6 8 0.61350444123810380558157925512175779452701763753189 0 2.7182818284590452353602874713526624977572470937000 +B 6 8 8 9 0.13138885423635105608068448708721147630956337812699 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 8 13 0.026313938801997448819486676708413174615876324941286 0 7.3890560989306502272304274605750078131803155705521 +B 6 10 6 10 0.61350444123810380558157925512175779452701763753189 0 2.7182818284590452353602874713526624977572470937000 +B 6 10 8 13 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 6 11 6 11 0.44749387961648362609003846500988007374455162150522 0 2.7182818284590452353602874713526624977572470937000 +B 6 11 8 13 0.055898238258860159876524402625199587341211034390185 0 7.3890560989306502272304274605750078131803155705521 +B 6 12 6 12 0.44749387961648362609003846500988007374455162150522 0 2.7182818284590452353602874713526624977572470937000 +B 6 12 8 13 0.075135268957605348207700907810283117972499857623698 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 7 4 0.56460444819158930505073692443263078451551347942110 0 2.7182818284590452353602874713526624977572470937000 +B 7 4 8 6 0.036017419415338983829311295083745449643069366497463 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 7 0.011279498027783950161475248792715325231899089420691 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 8 9 0.0030335242319322722721965666957052837615089970617668 0 7.3890560989306502272304274605750078131803155705521 +B 7 4 9 5 0.022597811240428544577102002620883967125656331030847 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 7 5 0.28148331799486344659849767489800235296208560547856 0 2.7182818284590452353602874713526624977572470937000 +B 7 5 8 6 0.097339128461468485151122927521100097582491052133898 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 7 0.027771445619487305940032612986735408172679274138539 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 9 0.011279498027783950161475248792715325231899089420691 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 8 1 0.21480891217466872643181558202995795241431148029907 0 2.7182818284590452353602874713526624977572470937000 +B 8 1 9 4 0.079205500549693703195288970256075562985362514815886 0 7.3890560989306502272304274605750078131803155705521 +B 8 1 9 5 0.029307615327883764203206648852314708384362848764155 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 8 2 0.33095354740282244527753931508408680992377788978774 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 4 0.15887185873094792230113967436930504841124229371942 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 5 0.029307615327883764203206648852314708384362848764155 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 8 3 0.33095354740282244527753931508408680992377788978774 0 2.7182818284590452353602874713526624977572470937000 +B 8 3 9 4 0.15887185873094792230113967436930504841124229371942 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 5 0.029307615327883764203206648852314708384362848764155 0 7.3890560989306502272304274605750078131803155705521 +B 8 6 8 6 0.33095354740282244527753931508408680992377788978774 0 2.7182818284590452353602874713526624977572470937000 +B 8 7 8 7 0.11547275637324326710695688478612463217961958945191 0 2.7182818284590452353602874713526624977572470937000 +B 8 9 8 9 0.24842587102812524547354565365054902696386051758204 0 2.7182818284590452353602874713526624977572470937000 +B 8 13 8 13 0.17843614573944419460604511029448373177538962768851 0 2.7182818284590452353602874713526624977572470937000 +B 9 4 9 4 0.52823168175636477322496645269715656387659162681053 0 2.7182818284590452353602874713526624977572470937000 +B 9 5 9 5 0.26334969008308866464266371763297781836495706816056 0 2.7182818284590452353602874713526624977572470937000 +P 0 13 0 0E+0 +P 0 12 0 0E+0 +P 0 11 0 0E+0 +P 0 10 0 0E+0 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 0 0E+0 +P 0 6 0 0E+0 +P 0 5 1 1.1472681761368075886742677292520335343101852290935E-1 +P 0 4 1 3.0854793398279495066268150027989757421959708991976E-2 +P 0 3 0 0E+0 +P 0 2 0 0E+0 +P 0 1 0 0E+0 +P 0 0 0 0E+0 +P 1 13 0 0E+0 +P 1 12 0 0E+0 +P 1 11 0 0E+0 +P 1 10 0 0E+0 +P 1 9 1 0E+0 +P 1 8 0 0E+0 +P 1 7 1 0E+0 +P 1 6 0 0E+0 +P 1 5 0 0E+0 +P 1 4 0 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 0 0E+0 +P 1 0 0 0E+0 +P 2 13 0 0E+0 +P 2 12 0 0E+0 +P 2 11 0 0E+0 +P 2 10 0 0E+0 +P 2 9 1 0E+0 +P 2 8 0 0E+0 +P 2 7 1 0E+0 +P 2 6 0 0E+0 +P 2 5 0 0E+0 +P 2 4 0 0E+0 +P 2 3 0 0E+0 +P 2 2 0 0E+0 +P 2 1 0 0E+0 +P 2 0 0 0E+0 +P 3 13 1 0E+0 +P 3 12 0 0E+0 +P 3 11 0 0E+0 +P 3 10 0 0E+0 +P 3 9 1 0E+0 +P 3 8 0 0E+0 +P 3 7 1 6.4669451901778215288896242660336270688481085283579E-1 +P 3 6 1 0E+0 +P 3 5 0 0E+0 +P 3 4 0 0E+0 +P 3 3 1 0E+0 +P 3 2 1 3.6634289025988455027090264161684414145819496466147E-1 +P 3 1 1 3.6634289025988455027090264161684414145819496466147E-1 +P 3 0 0 0E+0 +P 4 13 0 0E+0 +P 4 12 1 0E+0 +P 4 11 1 0E+0 +P 4 10 1 0E+0 +P 4 9 0 0E+0 +P 4 8 1 6.4669451901778215288896242660336270688481085283579E-1 +P 4 7 0 0E+0 +P 4 6 0 0E+0 +P 4 5 1 0E+0 +P 4 4 1 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 0 0E+0 +P 4 0 1 7.3844646472608781889979660521902350809507355578890E-1 +P 5 13 1 0E+0 +P 5 12 0 0E+0 +P 5 11 0 0E+0 +P 5 10 0 0E+0 +P 5 9 1 6.4669451901778215288896242660336270688481085283579E-1 +P 5 8 0 0E+0 +P 5 7 1 0E+0 +P 5 6 1 0E+0 +P 5 5 0 0E+0 +P 5 4 0 0E+0 +P 5 3 1 1.9722451532516788183968693921028813870432961060463E+0 +P 5 2 1 0E+0 +P 5 1 1 0E+0 +P 5 0 0 0E+0 +P 6 13 0 0E+0 +P 6 12 1 0E+0 +P 6 11 1 0E+0 +P 6 10 1 6.4669451901778215288896242660336270688481085283579E-1 +P 6 9 0 0E+0 +P 6 8 1 0E+0 +P 6 7 0 0E+0 +P 6 6 0 0E+0 +P 6 5 1 0E+0 +P 6 4 1 1.9722451532516788183968693921028813870432961060463E+0 +P 6 3 0 0E+0 +P 6 2 0 0E+0 +P 6 1 0 0E+0 +P 6 0 1 0E+0 +P 7 13 0 0E+0 +P 7 12 0 0E+0 +P 7 11 0 0E+0 +P 7 10 0 0E+0 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 0 0E+0 +P 7 6 0 0E+0 +P 7 5 1 1.9722451532516788183968693921028813870432961060463E+0 +P 7 4 1 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 0 0E+0 +P 7 0 0 0E+0 +P 8 13 1 1.1472681761368075886742677292520335343101852290935E-1 +P 8 12 0 0E+0 +P 8 11 0 0E+0 +P 8 10 0 0E+0 +P 8 9 1 0E+0 +P 8 8 0 0E+0 +P 8 7 1 0E+0 +P 8 6 1 1.9722451532516788183968693921028813870432961060463E+0 +P 8 5 0 0E+0 +P 8 4 0 0E+0 +P 8 3 1 0E+0 +P 8 2 1 0E+0 +P 8 1 1 0E+0 +P 8 0 0 0E+0 +P 9 13 0 0E+0 +P 9 12 0 0E+0 +P 9 11 0 0E+0 +P 9 10 0 0E+0 +P 9 9 0 0E+0 +P 9 8 0 0E+0 +P 9 7 0 0E+0 +P 9 6 0 0E+0 +P 9 5 1 0E+0 +P 9 4 1 0E+0 +P 9 3 0 0E+0 +P 9 2 0 0E+0 +P 9 1 0 0E+0 +P 9 0 0 0E+0 +END +CASE random09 15 13 8 +Z 3.6482022900263229041784731414666952674058608368036E+0 +S 0 3 5 +S 5 1 5 +B 0 1 0 1 0.26303194965333685193754815100891447200911099316975 0 2.7182818284590452353602874713526624977572470937000 +B 0 1 1 2 0.017795003478060391654466047877489316650040178579053 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 1 4 0.0052738901346756608512443098440777250952052020160477 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 1 8 0.00080449380401872740606934972862833154024160527482372 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 2 3 0.0062745315303705722091862046597250441606997161318407 0 27.474593022118317968158957115156725710168223409107 +B 0 1 2 5 0.0019759219050722008056791647292801125900536820514734 0 47.560129945305985709087486769738443607156131247662 +B 0 1 3 3 0.0052362313802415305135958115095096608029383318752137 0 27.474593022118317968158957115156725710168223409107 +B 0 1 3 5 0.0016489492855627788481810075048971379530021141142556 0 122.24381690163789252812627762718103990693477612483 +B 0 1 3 6 0.00068678600552261761483244084027971864043997968375863 0 251.52565389111403842527532968748451460950415804062 +B 0 1 3 7 0.00048286362126894186461110262053395635496090395183745 0 251.52565389111403842527532968748451460950415804062 +B 0 1 4 3 0.0047170813051770096658006149344019691240576397469002 0 27.474593022118317968158957115156725710168223409107 +B 0 1 4 5 0.0014854629758080678694319288927056506344763301456467 0 196.92750385796979934716506848462363620671342100200 +B 0 1 4 6 0.00061869409352924747277594158520403717415715240507798 0 658.50248697264293364068901384864374363813058946609 +B 0 1 4 7 0.00043498974652505410829073316923541237215472255481294 0 1342.2201013361373679574053284363258140167444482408 +B 0 1 5 3 0.0041979312301124888180054183592942774451769476185867 0 27.474593022118317968158957115156725710168223409107 +B 0 1 5 5 0.0013219766660533568906828502805141633159505461770378 0 271.61119081430170616620385934206623250649206587918 +B 0 1 6 2 0.0025074572626607261706327763073264810710965741447187 0 7.3890560989306502272304274605750078131803155705521 +B 0 1 6 4 0.00074313298881741089696464122633435486307285542161379 0 326.20934084744594524431412054492711090928280291780 +B 0 1 6 8 0.00011335956377526856683248606114103718687402123182666 0 8887.4531648571570383219333334698031028139792844189 +B 0 1 7 3 0.0012292847896278899581356880376320405908531454203273 0 47.560129945305985709087486769738443607156131247662 +B 0 1 7 5 0.00038711587178116635197036371793686838934854115778840 0 1253.1093381930365575421133356851857751081508820021 +B 0 1 7 6 0.00016123343851915632315285551243958116968020262885119 0 2988.3045409652661961260645913868786220760988374603 +B 0 1 7 7 0.00011335956377526856683248606114103718687402123182666 0 5462.0174996615850792085672436735619177387585968197 +B 0 3 0 3 0.31064420004183214319394715133935057236560782329739 0 2.7182818284590452353602874713526624977572470937000 +B 0 3 1 4 0.035304740997488137692718829706964666157514270077992 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 1 8 0.0092916782267734413132461449864638426149519565654467 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 1 9 0.0071048373382014421860305850064678808225519818366749 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 2 5 0.022910599872165301212860451613445751512085567606515 0 27.474593022118317968158957115156725710168223409107 +B 0 3 3 5 0.019119387863479974243270157162120831322586769813170 0 27.474593022118317968158957115156725710168223409107 +B 0 3 3 6 0.017602906071907003277015700272377517268420652416019 0 102.15827997845022478719774797259932200994686828628 +B 0 3 3 7 0.0068894990603450911403897831971627789762272338348387 0 102.15827997845022478719774797259932200994686828628 +B 0 3 3 10 0.0012411045170554273477383310654056133820439626504133 0 142.32935382482556026905480728176275780392268396339 +B 0 3 4 5 0.017223781859137310758475009936458371227837370916498 0 27.474593022118317968158957115156725710168223409107 +B 0 3 4 6 0.015857652788588056694133452153392393911203875135891 0 176.84196693478213160623653883004191830972551316345 +B 0 3 4 7 0.0062064345250704641082190043056297903179104859445352 148.41315910257660342111558004055227962348766759389 306.12380392425827750338559089034539301229489507924 +B 0 3 4 10 0.0011180542817996811404615120607684010304714704451166 0 772.40303296526296572462286234091686275581542785991 +B 0 3 5 5 0.015328175854794647273679862710795911133087972019826 0 27.474593022118317968158957115156725710168223409107 +B 0 3 6 4 0.0049747182870546157402138127651140863616277848361281 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 6 8 0.0013092712277777580194731744792338223958676556183544 403.42879349273512260838718054338827960589989735717 2194.4364117253960734361657527191418819158338824479 +B 0 3 6 9 0.0010011279854854012307992664221348799049495227515535 403.42879349273512260838718054338827960589989735717 2214.5219486485837411770942823737235998128217902865 +B 0 3 7 5 0.0044885664862441366790427659538164006338474948374145 0 47.560129945305985709087486769738443607156131247662 +B 0 3 7 6 0.0041325493692078921340484884454189141601231805735893 0 346.29487777063361298524265019950882880627071075635 +B 0 3 7 7 0.0016174144700701148081470825363327648867857884851552 0 1104.6961790904599541209977556446334954846632144082 +B 0 3 7 10 0.00029136812229339083932962481533978042373617836373937 2596.6951103496523211358276571196311444903381670246 14239.484415932135018992565653757188023452825810906 +B 0 5 0 5 0.65886868730819787335632356284003753555788276274060 0 2.7182818284590452353602874713526624977572470937000 +B 0 5 1 8 0.042696509872740983075359487579986059294945177525294 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 9 0.021196718753578671722353153540121292009844190595842 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 1 11 0.0011442466605327087644555269130441746449169294599854 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 6 0.052812447153875609290596984382300737320845858234201 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 7 0.042099040142313697153971067307085999028652439653019 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 3 10 0.0027575863086283983139927879551489274362100800475649 0 47.560129945305985709087486769738443607156131247662 +B 0 5 3 12 0.000075018852761590364168426181042431784002752487995140 0 67.645666868493653450016016424320161504144039086217 +B 0 5 4 6 0.047576317595557489683950341720223590444913784528985 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 4 7 0.037925099332039897098035893872460986851620395337627 0 27.474593022118317968158957115156725710168223409107 +B 0 5 4 10 0.0024841833523489352048030698438343783471049662257240 0 87.731203791681321190944546078901879401131946924772 +B 0 5 4 12 0.000067581052516667379320316337298162768149862854282859 0 237.09857770434513482902212779378707200068923667911 +B 0 5 6 8 0.0060162772040289028816143872441957686070532323116258 0 142.32935382482556026905480728176275780392268396339 +B 0 5 6 9 0.0029867859508297691362470828886245464001710117239228 0 162.41489074801322800998333693634447570091059180195 +B 0 5 6 11 0.00016123343851915632315285551243958116968020262885119 0 550.26040171644166570143049311725489980861873754931 +B 0 5 7 6 0.012398523532451580162010595008548565356306880970629 0 7.3890560989306502272304274605750078131803155705521 +B 0 5 7 7 0.0098833886333138028361091890994624160829694888821946 0 47.560129945305985709087486769738443607156131247662 +B 0 5 7 10 0.00064738523932963538432390232373726689746049262756474 0 1010.8811700210275897779924368069420942639662838529 +B 0 5 7 12 0.000017611814287493054191747158543949221261658437777614 0 2894.4895318958338317830592725491872208554019069050 +B 1 0 1 0 0.054074036095181836797037867708640490838115103823296 0 2.7182818284590452353602874713526624977572470937000 +B 1 0 2 1 0.022393917148027496362659157711021751142983412324178 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 2 3 0.0046069856512570654056492617527347034513250487733121 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 2 5 0.00064274950602286832018031129841123167242843822157473 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 3 1 0.017847607538406590532578408959278874145047290767947 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 3 3 0.0036716967065294796613102714859235988638127719606816 0 27.474593022118317968158957115156725710168223409107 +B 1 0 3 5 0.00051226147052219912805538245002975030500305638256579 0 47.560129945305985709087486769738443607156131247662 +B 1 0 3 6 0.00013776832799532409018501531982951379851471315244423 0 67.645666868493653450016016424320161504144039086217 +B 1 0 3 7 0.00013776832799532409018501531982951379851471315244423 0 67.645666868493653450016016424320161504144039086217 +B 1 0 4 1 0.016884055598334012082317961658168349495141945526211 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 4 3 0.0034734701107618750030770029279477181434739689471128 0 47.560129945305985709087486769738443607156131247662 +B 1 0 4 5 0.00048460563302779375747114280250834380567352086719736 0 142.32935382482556026905480728176275780392268396339 +B 1 0 4 6 0.00013033052775040110533690547608524478266182351873195 0 291.69672773748937390713238899664795040347997371773 +B 1 0 4 7 0.00013033052775040110533690547608524478266182351873195 0 475.57671476010975888239170225981230350884009267213 +B 1 0 5 1 0.015920503658261433632057514357057824845236600284475 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 5 3 0.0032752435149942703448437343699718374231351659335440 0 67.645666868493653450016016424320161504144039086217 +B 1 0 5 5 0.00045694979553338838688690315498693730634398535182899 0 291.69672773748937390713238899664795040347997371773 +B 1 0 6 2 0.0013708110503742666582885966640720029751896665919202 0 87.731203791681321190944546078901879401131946924772 +B 1 0 6 4 0.00020910731326304407947322496373812515248638402587572 0 495.66225168329742662332023191439402140582800051069 +B 1 0 7 1 0.0042086787095438730423361627040502431512389260992538 0 7.3890560989306502272304274605750078131803155705521 +B 1 0 7 3 0.00086582987234672242829330068549028510127594152278967 0 326.20934084744594524431412054492711090928280291780 +B 1 0 7 5 0.00012079736402019155168059590488530620272691086553894 0 2081.4900805428632515691939059012016757742186662145 +B 1 0 7 6 0.000032487414777339023887966846032487252967437705202176 0 3410.6626650436512051546934791160682066896098124077 +B 1 0 7 7 0.000032487414777339023887966846032487252967437705202176 0 4387.4565664960545026028896293726363869791652642141 +B 1 0 8 2 0.00042929395803898301630266350746553080701444983646059 0 107.81674071486898893187307573348359729811985476333 +B 1 0 8 4 0.000065485689031380810512116609842493204067839834802139 0 1640.9548491614649952335604918660961992158590277495 +B 1 2 1 2 0.072435462537959366942098020926719933154760003823015 0 2.7182818284590452353602874713526624977572470937000 +B 1 2 2 3 0.024610978990788702842675106181841508211797367560072 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 2 5 0.011595682861149027827461269686749562291655670036687 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 3 3 0.019614571727673668318141037042325916677696320172066 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 3 5 0.0092415808934902906520793152973108229684555166566699 0 27.474593022118317968158957115156725710168223409107 +B 1 2 3 6 0.0074746167042232128665166872142637804358064872525513 0 47.560129945305985709087486769738443607156131247662 +B 1 2 3 7 0.0071001235616963378286463200840635439293181440224299 0 47.560129945305985709087486769738443607156131247662 +B 1 2 4 3 0.018555625389839749031188010384099586175548018240401 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 4 5 0.0087426488556752521900202485259802687730514704100126 0 47.560129945305985709087486769738443607156131247662 +B 1 2 4 6 0.0070710790641695152411501998000490320926453976958224 0 142.32935382482556026905480728176275780392268396339 +B 1 2 4 7 0.0067168039588921225890159624736259330696337003473570 0 271.61119081430170616620385934206623250649206587918 +B 1 2 5 3 0.017496679052005829744234983725873255673399716308737 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 5 5 0.0082437168178602137279611817546497145776474241633554 0 67.645666868493653450016016424320161504144039086217 +B 1 2 6 4 0.0028359975495977574747535278507467649476446140135316 0 87.731203791681321190944546078901879401131946924772 +B 1 2 6 8 0.0011058812026457090749107094875135461259112471109099 0 1879.4329862172296192869300663321214307240098837425 +B 1 2 6 9 0.00053746859280488325388326282461420512688116277433270 0 1879.4329862172296192869300663321214307240098837425 +B 1 2 7 3 0.0046253499383285647445388296237791420753931780080476 0 7.3890560989306502272304274605750078131803155705521 +B 1 2 7 5 0.0021792749905140791259334521289946049988550565166176 0 326.20934084744594524431412054492711090928280291780 +B 1 2 7 6 0.0017626037617293874237307852092657060747008046078238 0 734.14038873906205067668980638041925291397885650372 +B 1 2 7 7 0.0016742938124865348959381561504128871249413314474870 0 1250.3135218868794240483240129473002387481867620064 +B 1 2 8 4 0.00088814327308149138283763021672309214883461042392651 0 107.81674071486898893187307573348359729811985476333 +B 1 2 8 8 0.00034632644555576690454994456490131130902584329035538 0 8419.0305883643841164986248712479521622004107222291 +B 1 2 8 9 0.00016831788703764463205280581070256807216368615844268 0 13527.859122605198608611621278280910852200752748244 +B 1 4 1 4 0.14980923130854855290248981052400747617756435534403 0 2.7182818284590452353602874713526624977572470937000 +B 1 4 2 5 0.10872227272446246175324446218430521262111424577591 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 5 0.086649979163682441114652805984967722383294492142116 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 3 6 0.083865037070202035619268284079274110330973119794121 0 27.474593022118317968158957115156725710168223409107 +B 1 4 3 7 0.064190407070102090716173629263967136893367853956219 0 27.474593022118317968158957115156725710168223409107 +B 1 4 3 10 0.0046896746107428073711321653085701683834851149045570 0 27.474593022118317968158957115156725710168223409107 +B 1 4 4 5 0.081971942886229158310329369345267760461240796086189 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 4 6 0.079337353513772469716857746546251485146452045354738 0 47.560129945305985709087486769738443607156131247662 +B 1 4 4 7 0.060724912261716844364474560958396646407746808906662 0 122.24381690163789252812627762718103990693477612483 +B 1 4 4 10 0.0044364896917128266476785770010327567778566469643721 0 196.92750385796979934716506848462363620671342100200 +B 1 4 5 5 0.077293906608775875506005932705567798539187100030267 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 6 8 0.0080034443947067647860581926306107114851426632870400 0 698.67356081901826912254607315780717943210640514320 +B 1 4 6 9 0.0024151309307785430514611936564994012043891999291855 0 698.67356081901826912254607315780717943210640514320 +B 1 4 6 11 0.00020910731326304407947322496373812515248638402587572 0 1308.6617030362680068371855985623795664870112412012 +B 1 4 7 5 0.020433098481342379198877837386955251669835231240734 0 7.3890560989306502272304274605750078131803155705521 +B 1 4 7 6 0.019776375922258700850057761665203091721045673743820 0 87.731203791681321190944546078901879401131946924772 +B 1 4 7 7 0.015136863527032284422733417301212673049908222875737 0 291.69672773748937390713238899664795040347997371773 +B 1 4 7 10 0.0011058812026457090749107094875135461259112471109099 0 4497.0568496172441229665597933672531436113611544670 +B 1 4 8 8 0.0025064215241119552542316219369022111615534826655664 0 1750.1511492277534733897810142718179560214405018267 +B 1 4 8 9 0.00075634137627736550347916489060103299160688346293889 0 3649.3427936268664003117878743765409381110678764886 +B 1 4 8 11 0.000065485689031380810512116609842493204067839834802139 0 18382.790496105065039809371927228796299493648450117 +B 1 8 1 8 0.11396144880131430127441044753858061858048921602142 0 2.7182818284590452353602874713526624977572470937000 +B 1 8 3 10 0.019240078585146793280270669912125680171207976941944 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 3 12 0.0016357408981028242416664838406304598244680860729305 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 4 10 0.018201350284562356379053773057676487152536320891936 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 4 12 0.0015474309488599717138738547817776408747086129125937 0 27.474593022118317968158957115156725710168223409107 +B 1 8 6 9 0.0090924378251037988373675831445823876661128447310091 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 6 11 0.00089070838977629336505377906002548092591905541567976 0 47.560129945305985709087486769738443607156131247662 +B 1 8 7 10 0.0045370399890857122167462005649263231256337048477108 0 27.474593022118317968158957115156725710168223409107 +B 1 8 7 12 0.00038572721174874913505848308144376305200533034654924 0 196.92750385796979934716506848462363620671342100200 +B 1 8 8 9 0.0028474592622349106049347435134319299345289090029058 0 7.3890560989306502272304274605750078131803155705521 +B 1 8 8 11 0.00027894123701526611438976440078819406948182821991629 0 142.32935382482556026905480728176275780392268396339 +B 1 9 1 9 0.11396144880131430127441044753858061858048921602142 0 2.7182818284590452353602874713526624977572470937000 +B 1 9 3 10 0.030613256068859004416418653862428461913053123984878 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 3 12 0.0034027050873699020272291119236775023571171154770494 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 4 10 0.028960515654570332636372854303781257378780486341103 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 4 12 0.0032190007403657086627439035077088775551146856267842 0 27.474593022118317968158957115156725710168223409107 +B 1 9 6 11 0.0026800607785071006348390808330578580536676194101958 0 47.560129945305985709087486769738443607156131247662 +B 1 9 7 10 0.0072189708771646617608793195872156564077059697517086 0 7.3890560989306502272304274605750078131803155705521 +B 1 9 7 12 0.00080239844053344083726115000117266197615958225534299 0 176.84196693478213160623653883004191830972551316345 +B 1 9 8 11 0.00083930888876058161523188383316525248959549000904411 0 67.645666868493653450016016424320161504144039086217 +B 1 11 1 11 0.079295214616343071727661091549779105571604002529179 0 2.7182818284590452353602874713526624977572470937000 +B 1 11 3 12 0.022738424454311960371924662843502002262370036917505 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 4 12 0.021510828377358885819893361139339788892078345523356 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 7 12 0.0053619916665860501789721998553471913357398843141937 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 2 1 0.23734293543041795099691912174282437594321621670250 0 2.7182818284590452353602874713526624977572470937000 +B 2 1 3 3 0.035310113931060871840467111419583798631135625669772 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 3 5 0.011119559643500214501297228082671517298177925873343 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 3 6 0.0046312873401219393584885370802719943452069570989597 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 3 7 0.0032561528019001529626762751344244387732240997530450 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 4 3 0.030557375210123242043219493055458621499209756101504 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 4 5 0.0096228677387213307151523972924715310665781825644664 0 27.474593022118317968158957115156725710168223409107 +B 2 1 4 6 0.0040079164070187374722407706678111224608872078357665 0 47.560129945305985709087486769738443607156131247662 +B 2 1 4 7 0.0028178748758335616578255977744252970282499911513562 0 67.645666868493653450016016424320161504144039086217 +B 2 1 5 3 0.025804636489185612245971874691333444367283886533233 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 5 5 0.0081261758339424469290075665022715448349784392555895 0 47.560129945305985709087486769738443607156131247662 +B 2 1 6 2 0.031128630077462452120653100334577638796914094270774 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 6 4 0.0092255657760282305413870049950187853581635372510319 0 67.645666868493653450016016424320161504144039086217 +B 2 1 6 8 0.0014072933481460122306328267918307838277153025522400 0 645.02962559596124026139781362927921400538529026504 +B 2 1 7 3 0.0090969239609526448421544858524033677960631677584782 0 27.474593022118317968158957115156725710168223409107 +B 2 1 7 5 0.0028647256350883160106096661812857785321029882783186 0 271.61119081430170616620385934206623250649206587918 +B 2 1 7 6 0.0011931558435825790617396174553545418516969155641283 0 495.66225168329742662332023191439402140582800051069 +B 2 1 7 7 0.00083888073830518640960538012893144282868521821566282 0 645.02962559596124026139781362927921400538529026504 +B 2 1 8 2 0.012500271788890795219700914586357664139298668920731 0 7.3890560989306502272304274605750078131803155705521 +B 2 1 8 4 0.0037046949807835214479344212497336048149131740902815 0 162.41489074801322800998333693634447570091059180195 +B 2 1 8 8 0.00056512443029928862446750247213561162621069828970109 0 4762.7166027737839599749109047234019918619529864351 +B 2 1 9 3 0.0041979312301124888180054183592942774451769476185867 0 47.560129945305985709087486769738443607156131247662 +B 2 1 9 5 0.0013219766660533568906828502805141633159505461770378 0 807.86986088530674727876659556352893623768787922043 +B 2 3 2 3 0.28030513559107121563044832323076619395467836757252 0 2.7182818284590452353602874713526624977572470937000 +B 2 3 3 5 0.12893008630196930813095627363294131604519240874631 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 6 0.11870381077165633063696341551983738128321618588937 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 7 0.046458794328049875514925304877930951262516671014545 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 10 0.0083692905670566417096727041909764061542122111815137 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 4 5 0.11157610623105908422540616116730246530546616837246 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 4 6 0.10272628663002424936841325370475329895935865228388 0 27.474593022118317968158957115156725710168223409107 +B 2 3 4 7 0.040205444051069724968588107376902750210626805429746 0 47.560129945305985709087486769738443607156131247662 +B 2 3 4 10 0.0072427846763509790863220515056998802013037491956456 0 67.645666868493653450016016424320161504144039086217 +B 2 3 5 5 0.094222126160148860319856048701663614565739927998603 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 6 4 0.061758247130796165953664769136909914284295875346586 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 6 8 0.016253844214003425421819345921668342162534563280913 0 311.78226466067704164806091865122966830046788155628 +B 2 3 6 9 0.012428424278426831578403734752584514586745413667084 0 311.78226466067704164806091865122966830046788155628 +B 2 3 7 5 0.033216182583210341493363761773430011942070128591303 0 27.474593022118317968158957115156725710168223409107 +B 2 3 7 6 0.030581593210753652899892138974413736627281377859852 0 87.731203791681321190944546078901879401131946924772 +B 2 3 7 7 0.011969151958698027547508953386558897888576141411777 0 182.50042767120089575091186659092619359789849964050 +B 2 3 7 10 0.0021561754245335307063411915284395804860795935813878 0 2026.8919305097190124910836446983407973714279291759 +B 2 3 8 4 0.024800155754344355433076999297458027569655842878829 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 8 8 0.0065270289692714787332760970432998955936480957883692 0 1141.1172218205909458921034905415784819426052879292 +B 2 3 8 9 0.0049908614995706521869443835778049300715279997340379 0 1988.6292862835180974431505887378431875295913578198 +B 2 3 9 5 0.015328175854794647273679862710795911133087972019826 0 47.560129945305985709087486769738443607156131247662 +B 2 5 2 5 0.59452027981776402060269981496248550803586628473468 0 2.7182818284590452353602874713526624977572470937000 +B 2 5 3 6 0.35613657811574044546677393748159093573204947885602 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 7 0.28389156167213399034473582683968450571134996398118 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 10 0.018595566097369619203665562304080340916188434038452 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 3 12 0.00050588372545658017105175124272932762925838506121587 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 4 6 0.30820062106792203257406776662941447610148157893834 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 4 7 0.24567977848896750817424262030156392735274973208419 0 27.474593022118317968158957115156725710168223409107 +B 2 5 4 10 0.016092604277385813943314958968249046547411265284224 0 47.560129945305985709087486769738443607156131247662 +B 2 5 4 12 0.00043779181346321002899525198765364616297555778253521 0 67.645666868493653450016016424320161504144039086217 +B 2 5 6 8 0.074688598013814163510865630785963940398401309610792 0 142.32935382482556026905480728176275780392268396339 +B 2 5 6 9 0.037079218205810692012210634303211345285740509376240 0 142.32935382482556026905480728176275780392268396339 +B 2 5 6 11 0.0020016197837223915293844729202771438753623054888257 0 291.69672773748937390713238899664795040347997371773 +B 2 5 7 6 0.091751257930178710694596621833187906242467223119617 0 7.3890560989306502272304274605750078131803155705521 +B 2 5 7 7 0.073138816678123085342213436245333067503761986671541 0 47.560129945305985709087486769738443607156131247662 +B 2 5 7 10 0.0047907647969902192998128143274558558008683443128377 0 916.11194614150801521802511629491778006719973113721 +B 2 5 7 12 0.00013033052775040110533690547608524478266182351873195 0 1858.3932344839547413290395350032067998509523537434 +B 2 5 8 8 0.029992575078973532001134677445199283631908213493662 0 291.69672773748937390713238899664795040347997371773 +B 2 5 8 9 0.014889839486633940621202579951684736531485333489587 0 678.58802389583060138161754350322546153511849730464 +B 2 5 8 11 0.00080378710056585805417303063766576731350279306658214 0 3705.0971499975484207105849580481822294032354437755 +B 3 1 3 1 0.23734293543041795099691912174282437594321621670250 0 2.7182818284590452353602874713526624977572470937000 +B 3 1 4 3 0.051968499354703215885516585389745418362347817380291 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 4 5 0.016365476171672696320989238157245334203739039041368 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 4 6 0.0068162072095398944631333431957493647847410537920160 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 4 7 0.0047923202716009795090079885999194042953441958073766 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 3 0.047215760633765586088268967025620241230421947812020 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 5 5 0.014868784266893812534844407367045347972139295732491 0 27.474593022118317968158957115156725710168223409107 +B 3 1 6 2 0.074802381832179050387071374972073906569467651974957 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 6 4 0.022169118656332535097692019128575440008419132168779 0 47.560129945305985709087486769738443607156131247662 +B 3 1 6 8 0.0033817387439134300818152176173248910948095072082604 0 182.50042767120089575091186659092619359789849964050 +B 3 1 7 3 0.030508048105532618684451578186690164659201229037264 0 27.474593022118317968158957115156725710168223409107 +B 3 1 7 5 0.0096073340680396816164465070460595816692638447552203 0 196.92750385796979934716506848462363620671342100200 +B 3 1 7 6 0.0040014466461037360526321899832927841755507615203776 0 291.69672773748937390713238899664795040347997371773 +B 3 1 7 7 0.0028133261340726042607877709544255500957794228716831 0 311.78226466067704164806091865122966830046788155628 +B 3 1 8 2 0.022194761889066302358537988713772786230860301426427 0 7.3890560989306502272304274605750078131803155705521 +B 3 1 8 4 0.0065778428148405478710792026904883594623816241675175 0 142.32935382482556026905480728176275780392268396339 +B 3 1 8 8 0.0010034023563658799293181798321347533711848068913901 0 2582.2819226938245111276129089000523060235420281953 +B 3 1 9 3 0.0089506699510501186152530367234194545771028171868563 0 47.560129945305985709087486769738443607156131247662 +B 3 1 9 5 0.0028186685708322406768276810707141495475502894859145 0 678.58802389583060138161754350322546153511849730464 +B 3 1 10 3 0.0070203236606945614509736995519726010805403992452239 0 47.560129945305985709087486769738443607156131247662 +B 3 1 10 5 0.0022107803960694720956133517325198292579998524038830 0 807.86986088530674727876659556352893623768787922043 +B 3 3 3 3 0.28030513559107121563044832323076619395467836757252 0 2.7182818284590452353602874713526624977572470937000 +B 3 3 4 5 0.18975591865456252898239810198721578712288656563452 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 6 0.17470515460617475517493702125588376926368431110510 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 7 0.068376834687403975858835948842441889200443517031811 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 10 0.012317702295794866412738528965585413224945323072089 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 5 0.17240193858365230507684798952157693638316032526067 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 6 4 0.14840563081857527999664878815755337831459873896969 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 6 8 0.039058135809738476501309195820868764696016177742363 0 87.731203791681321190944546078901879401131946924772 +B 3 3 6 9 0.029865616833562682847135593085446220663891930987916 0 87.731203791681321190944546078901879401131946924772 +B 3 3 7 5 0.11139599500671378625035570259334333375949052585337 0 27.474593022118317968158957115156725710168223409107 +B 3 3 7 6 0.10256046118690415870641590652554420693160703668107 0 67.645666868493653450016016424320161504144039086217 +B 3 3 7 7 0.040140542595032278437756794852098036878392853013842 0 87.731203791681321190944546078901879401131946924772 +B 3 3 7 10 0.0072310930439774180327576689883251135097211674578310 0 584.77301482639823703861222466553406031442156674937 +B 3 3 8 4 0.044033726712135182712729948686467300793839502743718 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 8 8 0.011589016324010097224010432946235058369014895266313 0 604.85855174958590477954075432011577821140947458793 +B 3 3 8 9 0.0088614859320677386220758578826470545703717331051274 0 843.33668880535052883291032878614100971956033058095 +B 3 3 9 5 0.032682155925704871179229975176434761872814212393681 0 47.560129945305985709087486769738443607156131247662 +B 3 3 10 5 0.025633758565839687554183172870780171563072533004613 0 47.560129945305985709087486769738443607156131247662 +B 3 5 3 5 0.59452027981776402060269981496248550803586628473468 0 2.7182818284590452353602874713526624977572470937000 +B 3 5 4 6 0.52415247274842225857882454068577055962143444151647 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 4 7 0.41782415282965147926272346827232867955819364744318 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 4 10 0.027368466344182640220199609696917431084147577601512 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 4 12 0.00074454639572314960075727940825948331655047983809691 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 6 8 0.17947738185833102173377062262340825964451041020331 0 47.560129945305985709087486769738443607156131247662 +B 3 5 6 9 0.089101699347761217688758090147669165353122933129555 0 47.560129945305985709087486769738443607156131247662 +B 3 5 6 11 0.0048099105862435485202770454482153861992161514450752 0 67.645666868493653450016016424320161504144039086217 +B 3 5 7 6 0.30770310961067893669935339588954398976242008569777 0 7.3890560989306502272304274605750078131803155705521 +B 3 5 7 7 0.24528319101880705643069428421609781970920590203055 0 27.474593022118317968158957115156725710168223409107 +B 3 5 7 10 0.016066626863787045576697465056124240337604656630127 0 306.12380392425827750338559089034539301229489507924 +B 3 5 7 12 0.00043708511001034067709893289669108193623674557429363 0 510.08932787006633021957343380809146401464292187220 +B 3 5 8 8 0.053253087097623024606190302611917780506153721235081 0 142.32935382482556026905480728176275780392268396339 +B 3 5 8 9 0.026437540523395466747485251957838466250097959031828 0 271.61119081430170616620385934206623250649206587918 +B 3 5 8 11 0.0014271580336690599404207970501266391978225423297753 0 1253.1093381930365575421133356851857751081508820021 +B 3 6 3 6 0.59452027981776402060269981496248550803586628473468 0 2.7182818284590452353602874713526624977572470937000 +B 3 6 4 7 0.43837641196429422501978973477349674585760017125269 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 10 0.047920725478825385977265876198085497383554101411012 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 4 12 0.0027684333336620645548826340040894438059473378227366 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 6 8 0.19398024624375977143426194064198732324490010245339 0 27.474593022118317968158957115156725710168223409107 +B 3 6 6 9 0.10360456373318996738924940816624822895351262537963 0 27.474593022118317968158957115156725710168223409107 +B 3 6 6 11 0.0062380824285736669587388732791058813036227956818897 0 47.560129945305985709087486769738443607156131247662 +B 3 6 7 7 0.25734837123649036595475955928892927587125979714030 0 7.3890560989306502272304274605750078131803155705521 +B 3 6 7 10 0.028131807081470355100762740128955696499658551739875 0 176.84196693478213160623653883004191830972551316345 +B 3 6 7 12 0.0016252056220414724689433519255583160160080842229883 0 326.20934084744594524431412054492711090928280291780 +B 3 6 8 8 0.057556260524188152514643473816295729117437332196052 0 47.560129945305985709087486769738443607156131247662 +B 3 6 8 9 0.030740713949960594655938423162216414861381569992800 0 122.24381690163789252812627762718103990693477612483 +B 3 6 8 11 0.0018509137109722399515234142681185250244602777681606 0 697.71934600893105890558407148347426645603678298274 +B 3 7 3 7 0.50690426609972209238226295370217793799331474794400 0 2.7182818284590452353602874713526624977572470937000 +B 3 7 4 10 0.073974479699723103683937328469089612123164894500579 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 4 12 0.0047923202716009795090079885999194042953441958073766 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 8 0.21236528419611135874260914611283241130914859596227 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 9 0.12198960168554155469759661363709331701776111888852 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 6 11 0.0076662542709037853972007011099963764080294399187051 0 27.474593022118317968158957115156725710168223409107 +B 3 7 7 10 0.043426633697027346604953494206819481995882852787078 0 47.560129945305985709087486769738443607156131247662 +B 3 7 7 12 0.0028133261340726042607877709544255500957794228716831 0 142.32935382482556026905480728176275780392268396339 +B 3 7 8 8 0.063011321308072869718512623943471736714723656518424 0 7.3890560989306502272304274605750078131803155705521 +B 3 7 8 9 0.036195774733845311859807573289392422458667894315172 0 27.474593022118317968158957115156725710168223409107 +B 3 7 8 11 0.0022746693882754199626260314861104108510980132065458 0 196.92750385796979934716506848462363620671342100200 +B 3 10 3 10 0.26998748890009174921041063742988672295671963861768 0 2.7182818284590452353602874713526624977572470937000 +B 3 10 4 12 0.033440127157999385082575673484407312552338151555432 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 6 11 0.086715546579784025985632231473954904265915579806050 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 7 12 0.019630988399880440952230722142725942576918672576211 0 27.474593022118317968158957115156725710168223409107 +B 3 10 8 11 0.025729540440791045303007192906469715954748298894003 0 7.3890560989306502272304274605750078131803155705521 +B 3 12 3 12 0.21763743190211025398341644733044533439721003564361 0 2.7182818284590452353602874713526624977572470937000 +B 4 1 4 1 0.36110699553122032001883406374639079973686122170682 0 2.7182818284590452353602874713526624977572470937000 +B 4 1 5 3 0.081546168078884004194574350681774635630603451957561 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 5 0.025679823107407373582373639617369981721655921101135 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 2 0.14482848986237883880940778380284895287185109638195 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 4 0.042922697084430518919456939131335893668341050657344 0 27.474593022118317968158957115156725710168223409107 +B 4 1 6 8 0.0065475470619223799886004700409120366384267779711348 0 47.560129945305985709087486769738443607156131247662 +B 4 1 7 3 0.064838455550651036790756961842844559059382733182807 0 27.474593022118317968158957115156725710168223409107 +B 4 1 7 5 0.020418372908553242663975739296384215418780470123865 0 122.24381690163789252812627762718103990693477612483 +B 4 1 7 6 0.0085042353284688858362305103821397188445163863061572 0 142.32935382482556026905480728176275780392268396339 +B 4 1 7 7 0.0059791344520815541675730233780126956393966936345578 0 142.32935382482556026905480728176275780392268396339 +B 4 1 8 2 0.031889251989241809497375062841187908322421933932122 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 8 4 0.0094509906488975742942239841312431141098500742447529 0 122.24381690163789252812627762718103990693477612483 +B 4 1 8 8 0.0014416802824324712341688571921338951161589154930791 0 1323.0887792230369104334388004560770090958261625627 +B 4 1 9 3 0.013703408671987748412500655087544631709028686755126 0 47.560129945305985709087486769738443607156131247662 +B 4 1 9 5 0.0043153604756111244629725118609141357791500327947912 0 549.30618690635445548446849144292198683254911538885 +B 4 1 10 3 0.011773062381632191248221317916097778212466268813494 0 47.560129945305985709087486769738443607156131247662 +B 4 1 10 5 0.0037074723008483558817581825227198154895995957127598 0 678.58802389583060138161754350322546153511849730464 +B 4 1 11 2 0.0035664036004946454575858029655528115732448760763832 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 11 4 0.0010569720195958387776266189452031789191312610067670 0 400.89302780377785206335291140236970720906144779496 +B 4 1 11 8 0.00016123343851915632315285551243958116968020262885119 0 5271.7936567440137881324921040547410788507078238012 +B 4 3 4 3 0.42647212212869029522610826022843533923795146782307 0 2.7182818284590452353602874713526624977572470937000 +B 4 3 5 5 0.29775475968535142476179671139447123295505961557087 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 6 4 0.28733528093729483396613797368384161562675761493688 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 6 8 0.075622335647748985090633765493378230437329450252536 0 27.474593022118317968158957115156725710168223409107 +B 4 3 6 9 0.057824257448344968539037542980779655620148264401076 0 27.474593022118317968158957115156725710168223409107 +B 4 3 7 5 0.23674881610841290593530442446623763033138981616358 0 27.474593022118317968158957115156725710168223409107 +B 4 3 7 6 0.21797074270101885561712894064600268359310103387136 0 47.560129945305985709087486769738443607156131247662 +B 4 3 7 7 0.085310301656271009924503984301783533404763064621034 0 47.560129945305985709087486769738443607156131247662 +B 4 3 7 10 0.015368171155778579326660765288130169008541084780666 0 196.92750385796979934716506848462363620671342100200 +B 4 3 8 4 0.063267297669926009992382898075476574018023162608607 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 8 8 0.016651003678748715714744768849170221144381694744258 0 380.80749088059018432242438174778798931207353995641 +B 4 3 8 9 0.012732110364564825057207332187489179069215466476217 0 455.49117783692209114146317260523058561185218483358 +B 4 3 9 5 0.050036135996615095084780087642073612612540452767535 0 47.560129945305985709087486769738443607156131247662 +B 4 3 10 5 0.042987738636749911459733285336419022302798773378468 0 47.560129945305985709087486769738443607156131247662 +B 4 3 11 4 0.0070756353356833721921421788470154140791994702759607 0 7.3890560989306502272304274605750078131803155705521 +B 4 3 11 8 0.0018622010792781505354671840678666118001284277737699 0 659.45670178273014385765101552297665661420021162654 +B 4 3 11 9 0.0014239231532115592306165067078674700551543191720809 0 1769.2824713408539309137475422520667609423587875048 +B 4 5 4 5 0.90453685355346375612046857720291086502220387709400 0 2.7182818284590452353602874713526624977572470937000 +B 4 5 6 8 0.34749479284379602002062045305380890177180217103609 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 9 0.17251408637841827694421739690380837130564345274497 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 11 0.0093126992686086983038753658470623208681817762308545 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 7 6 0.65395840226405725419939070225212228812352341180883 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 7 7 0.52129796121930940850676574590790313793518544255851 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 7 10 0.034146244563172629644836249108365115746829867072884 0 47.560129945305985709087486769738443607156131247662 +B 4 5 7 12 0.00092893269930689083025804936975864922915730829135953 0 67.645666868493653450016016424320161504144039086217 +B 4 5 8 8 0.076513599116272517211245927778636277380399228976501 0 47.560129945305985709087486769738443607156131247662 +B 4 5 8 9 0.037985241560156992873767923963992195968710584574068 0 67.645666868493653450016016424320161504144039086217 +B 4 5 8 11 0.0020505289667722618266685634625875110821422915929686 0 217.01304078115746708809359813920535410370132884056 +B 4 5 11 8 0.0085570641627823930964593492711531237396606251179525 0 47.560129945305985709087486769738443607156131247662 +B 4 5 11 9 0.0042481618042187153564771976063118574958427831451736 0 196.92750385796979934716506848462363620671342100200 +B 4 5 11 11 0.00022932535051252646520935476751526263596302990753184 0 530.17486479325399796050196346267318191163082971075 +B 4 6 4 6 0.90453685355346375612046857720291086502220387709400 0 2.7182818284590452353602874713526624977572470937000 +B 4 6 6 8 0.37557448624624520400565223472578190611245144894314 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 9 0.20059377978086746092924917857578137564629273065202 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 11 0.012077851475295016619150261653212605097936774490574 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 7 7 0.54693997045401315231537148463412853483749892332523 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 7 10 0.059788253797876373453441987834590512649143347839608 0 47.560129945305985709087486769738443607156131247662 +B 4 6 7 12 0.0034540335756942224989155363738856724342770009629586 0 67.645666868493653450016016424320161504144039086217 +B 4 6 8 8 0.082696363429718248493801935906384648476140259428507 0 27.474593022118317968158957115156725710168223409107 +B 4 6 8 9 0.044168005873602724156323932091740567064451615026076 0 47.560129945305985709087486769738443607156131247662 +B 4 6 8 11 0.0026593776511120524191682697330411270481256676928580 0 196.92750385796979934716506848462363620671342100200 +B 4 6 11 8 0.0092485270078789651247636149386896770902632016598264 0 27.474593022118317968158957115156725710168223409107 +B 4 6 11 9 0.0049396246493152873847814632738484108464453596870476 0 122.24381690163789252812627762718103990693477612483 +B 4 6 11 11 0.00029741726250589660726585402259094410224585718621247 0 400.89302780377785206335291140236970720906144779496 +B 4 7 4 7 0.77123288384917117488191835194405218113356396872123 0 2.7182818284590452353602874713526624977572470937000 +B 4 7 6 8 0.41117064264505323710884467975097905574681382064607 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 9 0.23618993617967549403244162360097852528065510235495 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 11 0.014843003681981334934425157459362889327691772750293 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 7 10 0.092294198859889810752477981466806359715481527080846 0 47.560129945305985709087486769738443607156131247662 +B 4 7 7 12 0.0059791344520815541675730233780126956393966936345578 0 67.645666868493653450016016424320161504144039086217 +B 4 7 8 8 0.090534150058086029808876809229746732626472715964591 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 9 0.052005792501970505471398805415102651214784071562155 0 27.474593022118317968158957115156725710168223409107 +B 4 7 8 11 0.0032682263354518430116679760034947430141090437927474 0 176.84196693478213160623653883004191830972551316345 +B 4 7 11 8 0.010125082860012147734464969658687960580211418863205 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 11 9 0.0058161805014484699944828179938466943363935768904256 0 47.560129945305985709087486769738443607156131247662 +B 4 7 11 11 0.00036550917449926674932235327766662556852868446489313 0 271.61119081430170616620385934206623250649206587918 +B 4 10 4 10 0.41077426960666095913029016918161660558429376032906 0 2.7182818284590452353602874713526624977572470937000 +B 4 10 6 11 0.16789414121754899475807235221373903887730433105683 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 7 12 0.041721547192334624650808710120746185362188945087680 0 27.474593022118317968158957115156725710168223409107 +B 4 10 8 11 0.036967992843751582735803147373813460029919994526568 0 7.3890560989306502272304274605750078131803155705521 +B 4 10 11 11 0.0041343955896329756269408981511849779654481929200446 0 7.3890560989306502272304274605750078131803155705521 +B 4 12 4 12 0.33112592547479473881510234643923868704022612011515 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 5 1 0.36110699553122032001883406374639079973686122170682 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 2 0.15452297996255434594824485793026407496341272888765 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 4 0.045795844918487545342601720572090648315809500734580 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 8 0.0069858249879889712934511474009111783834008865728241 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 3 0.069591194271588666588004580206969736191308602751075 0 27.474593022118317968158957115156725710168223409107 +B 5 1 7 5 0.021915064813332126450120570086584201650380213432741 0 47.560129945305985709087486769738443607156131247662 +B 5 1 7 6 0.0091276062615720877224782767946005907288361355693504 0 47.560129945305985709087486769738443607156131247662 +B 5 1 7 7 0.0064174123781481454724237007380118373843708022362468 0 47.560129945305985709087486769738443607156131247662 +B 5 1 8 2 0.041583742089417316636212136968603030413983566437820 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 4 0.012324138482954600717368765571997868757318524321989 0 102.15827997845022478719774797259932200994686828628 +B 5 1 8 8 0.0018799582084990625390195345521330368611330240947681 0 510.08932787006633021957343380809146401464292187220 +B 5 1 9 3 0.018456147392925378209748273451669808840954556323396 0 47.560129945305985709087486769738443607156131247662 +B 5 1 9 5 0.0058120523803900082491173426511141220107497761036684 148.41315910257660342111558004055227962348766759389 271.61119081430170616620385934206623250649206587918 +B 5 1 10 3 0.016525801102569821045468936280222955344392138381764 0 47.560129945305985709087486769738443607156131247662 +B 5 1 10 5 0.0052041642056272396679030133129198017211993390216365 0 549.30618690635445548446849144292198683254911538885 +B 5 1 11 2 0.0035664036004946454575858029655528115732448760763832 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 11 4 0.0010569720195958387776266189452031789191312610067670 0 380.80749088059018432242438174778798931207353995641 +B 5 1 11 8 0.00016123343851915632315285551243958116968020262885119 403.42879349273512260838718054338827960589989735717 3297.9183253885689543914464530925755004613018042622 +B 5 1 12 3 0.0012292847896278899581356880376320405908531454203273 0 67.645666868493653450016016424320161504144039086217 +B 5 1 12 5 0.00038711587178116635197036371793686838934854115778840 0 1733.8156434208101493596038090294546874604863361443 +B 5 1 12 6 0.00016123343851915632315285551243958116968020262885119 403.42879349273512260838718054338827960589989735717 4094.5822709345962105751346144981977769815308792703 +B 5 1 12 7 0.00011335956377526856683248606114103718687402123182666 403.42879349273512260838718054338827960589989735717 4223.8641079240723564722836665585012516841002611862 +B 5 3 5 3 0.42647212212869029522610826022843533923795146782307 0 2.7182818284590452353602874713526624977572470937000 +B 5 3 6 4 0.30656885189508566124579092307285088885094127480176 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 8 0.080684323002487603581368101396313393212696249730483 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 6 9 0.061694881880842054974169017285621780118991997772166 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 7 5 0.25410279617932312984085453693187648107111605653743 0 27.474593022118317968158957115156725710168223409107 +B 5 3 7 6 0.23394826684265093688567910246108676591695856747686 0 27.474593022118317968158957115156725710168223409107 +B 5 3 7 7 0.091563651933251160470841181802811734456652930205833 0 27.474593022118317968158957115156725710168223409107 +B 5 3 7 10 0.016494677046484241950011417973406694961449546766535 0 67.645666868493653450016016424320161504144039086217 +B 5 3 8 4 0.082500868627716837272035847464485847242206822473496 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 8 8 0.021712991033487334205479104752105383919748494222202 0 251.52565389111403842527532968748451460950415804062 +B 5 3 8 9 0.016602734797061911492338806492331303568059199847307 0 271.61119081430170616620385934206623250649206587918 +B 5 3 9 5 0.067390116067525318990330200107712463352266693141388 0 47.560129945305985709087486769738443607156131247662 +B 5 3 10 5 0.060341718707660135365283397802057873042525013752322 0 47.560129945305985709087486769738443607156131247662 +B 5 3 11 4 0.0070756353356833721921421788470154140791994702759607 0 7.3890560989306502272304274605750078131803155705521 +B 5 3 11 8 0.0018622010792781505354671840678666118001284277737699 0 530.17486479325399796050196346267318191163082971075 +B 5 3 11 9 0.0014239231532115592306165067078674700551543191720809 0 1233.9780160799361000181468077049369701872325963240 +B 5 3 12 5 0.0044885664862441366790427659538164006338474948374145 0 67.645666868493653450016016424320161504144039086217 +B 5 3 12 6 0.0041325493692078921340484884454189141601231805735893 0 400.89302780377785206335291140236970720906144779496 +B 5 3 12 7 0.0016174144700701148081470825363327648867857884851552 0 475.57671476010975888239170225981230350884009267213 +B 5 3 12 10 0.00029136812229339083932962481533978042373617836373937 0 6807.9295730242174990316936637196867170417183248709 +B 5 5 5 5 0.90453685355346375612046857720291086502220387709400 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 8 0.37075530486244551262567607822052739864604767877750 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 9 0.18406178741517980307050006890996210102425607828721 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 11 0.0099360702017119001901231322595231927525015254940480 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 6 0.70189435931187566709209687310429874775409131172654 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 7 0.55950974440247589067725895244602371629378567445551 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 10 0.036649206383156434905186852444196410115607035827112 0 47.560129945305985709087486769738443607156131247662 +B 5 5 7 12 0.00099702461130026097231454862483433069544013557004016 0 67.645666868493653450016016424320161504144039086217 +B 5 5 8 8 0.099774111134922009816301552945354774254644736717926 0 47.560129945305985709087486769738443607156131247662 +B 5 5 8 9 0.049532942596918519000050595970145925687323210116309 0 67.645666868493653450016016424320161504144039086217 +B 5 5 8 11 0.0026738998998754637129163298750483829664620408561618 0 217.01304078115746708809359813920535410370132884056 +B 5 5 11 8 0.0085570641627823930964593492711531237396606251179525 0 47.560129945305985709087486769738443607156131247662 +B 5 5 11 9 0.0042481618042187153564771976063118574958427831451736 0 196.92750385796979934716506848462363620671342100200 +B 5 5 11 11 0.00022932535051252646520935476751526263596302990753184 0 530.17486479325399796050196346267318191163082971075 +B 5 5 12 6 0.012398523532451580162010595008548565356306880970629 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 12 7 0.0098833886333138028361091890994624160829694888821946 0 27.474593022118317968158957115156725710168223409107 +B 5 5 12 10 0.00064738523932963538432390232373726689746049262756474 0 1065.4793200541718288561026980098029726667570208916 +B 5 5 12 12 0.000017611814287493054191747158543949221261658437777614 0 3245.9140001341312777034006938331526585051679791314 +B 6 0 6 0 0.27334631866906308391110033206027309692255495465841 0 2.7182818284590452353602874713526624977572470937000 +B 6 0 7 1 0.13798368585167142937007748443747841916478421345389 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 3 0.028386675570165072369449763219355462699371106666871 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 5 0.0039604034137541839458227129889604956455339368433284 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 6 0.0010651165232936311143710478799100121617229628487208 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 7 7 0.0010651165232936311143710478799100121617229628487208 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 8 2 0.015197286317011627140513547012752623404786974399224 0 27.474593022118317968158957115156725710168223409107 +B 6 0 8 4 0.0023182361345656538438702119121321786061071326964572 0 47.560129945305985709087486769738443607156131247662 +B 6 0 9 1 0.043076112893091501377916503153386626476396256253801 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 9 3 0.0088618276426856392818015344365411496118206768020428 0 102.15827997845022478719774797259932200994686828628 +B 6 0 9 5 0.0012363692381463782670356383358950274933366289499188 0 251.52565389111403842527532968748451460950415804062 +B 6 0 10 1 0.039493355223543173998096201702754274128365479939305 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 10 3 0.0081247652937256581956958127277059257446472030029810 0 122.24381690163789252812627762718103990693477612483 +B 6 0 10 5 0.0011335370401401144454949491350349526252407826262783 0 549.30618690635445548446849144292198683254911538885 +B 6 0 11 2 0.0010569720195958387776266189452031789191312610067670 0 67.645666868493653450016016424320161504144039086217 +B 6 0 11 4 0.00016123343851915632315285551243958116968020262885119 0 772.40303296526296572462286234091686275581542785991 +B 6 0 12 1 0.0022815748293987161418152681018291938514282356157815 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 12 3 0.00046937668081151311182676356953852366059833549565204 0 326.20934084744594524431412054492711090928280291780 +B 6 0 12 5 0.000065485689031380810512116609842493204067839834802139 0 3185.1739857334867036281994270690827942329937961166 +B 6 0 12 6 0.000017611814287493054191747158543949221261658437777614 0 5382.1461631478400476093994489292432485456661281268 +B 6 0 12 7 0.000017611814287493054191747158543949221261658437777614 0 5402.2317000710277153503279785838249664426540359653 +B 6 0 13 1 0.0013180228893261376915548208007186692015228903740454 0 7.3890560989306502272304274605750078131803155705521 +B 6 0 13 3 0.00027115008504390845359349501156264294025953248208323 0 346.29487777063361298524265019950882880627071075635 +B 6 0 13 5 0.000037829851536975439927876962321086704738304319433736 0 4091.9884461558896481850701125547597405348739673624 +B 6 2 6 2 0.36616403094065064985104776387942946481557957079699 0 2.7182818284590452353602874713526624977572470937000 +B 6 2 7 3 0.15164446537510714999761684210720114443360828536297 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 5 0.071448646102065743553800533462044429839423498324737 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 6 0.057787866578630022926261175792321704570599426415665 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 7 7 0.054892579688169470094809510683271221086788452421057 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 8 4 0.031440851562885519940018388845108958097655204315049 0 27.474593022118317968158957115156725710168223409107 +B 6 2 8 8 0.012260182221772591874631893886621394670763002130531 0 87.731203791681321190944546078901879401131946924772 +B 6 2 8 9 0.0059585630631056371058887340287542760425506416487809 0 87.731203791681321190944546078901879401131946924772 +B 6 2 9 3 0.047340771264311708537468168614728891473592453487175 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 9 5 0.022305027775961854702658456993756578096972079277040 0 102.15827997845022478719774797259932200994686828628 +B 6 2 10 3 0.043403310339030593299887126133439630441639708030833 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 10 5 0.020449857846118155989335569375648392969175972143680 0 122.24381690163789252812627762718103990693477612483 +B 6 2 11 4 0.0021867127907590021107815619727388740147079461503299 0 67.645666868493653450016016424320161504144039086217 +B 6 2 11 8 0.00085269628361572835145712117997613452028277917072505 0 812.57410681163830120647992165008029854979124353703 +B 6 2 11 9 0.00041441835754913704660644381997699277530867056903603 0 1051.0522438674029252598494961161055300579420995300 +B 6 2 12 3 0.0025074572626607261706327763073264810710965741447187 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 12 5 0.0011814109148840022018153185863334966080469640233028 0 326.20934084744594524431412054492711090928280291780 +B 6 2 12 6 0.00095552848162199217299781038083620938837862549436560 0 956.28301998788335069988217560408121586117554681432 +B 6 2 12 7 0.00090765460687810441667744092953766540557244409734106 0 976.36855691107101844081070525866293375816345465288 +B 6 2 13 3 0.0014485109248268068836797496491001505689482722130543 0 7.3890560989306502272304274605750078131803155705521 +B 6 2 13 5 0.00068247887706896373975625181500294241264291777664542 0 346.29487777063361298524265019950882880627071075635 +B 6 4 6 4 0.75729138858348782772229056239372307503353778021541 0 2.7182818284590452353602874713526624977572470937000 +B 6 4 7 5 0.66990959310633033406495053855555899822707052118256 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 6 0.64837860784037999847277515343421451465704432535363 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 7 0.49626981906632829133572426846188360192477102006192 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 7 10 0.036256881312679687334085790670977221000573230586732 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 8 8 0.088728957908117409991237350790814162418069702500940 0 67.645666868493653450016016424320161504144039086217 +B 6 4 8 9 0.026774978388225952696213440655040546695115293700146 0 67.645666868493653450016016424320161504144039086217 +B 6 4 8 11 0.0023182361345656538438702119121321786061071326964572 0 87.731203791681321190944546078901879401131946924772 +B 6 4 9 5 0.20913415294496370860977719854464342439836441130564 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 10 5 0.19173989566163697077550897898500589608259835372777 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 11 8 0.0061711034378418282754493357327078780439462697465487 0 107.81674071486898893187307573348359729811985476333 +B 6 4 11 9 0.0018622010792781505354671840678666118001284277737699 0 291.69672773748937390713238899664795040347997371773 +B 6 4 11 11 0.00016123343851915632315285551243958116968020262885119 0 495.66225168329742662332023191439402140582800051069 +B 6 4 12 5 0.011077025926435813590230964107555327825727839128886 0 7.3890560989306502272304274605750078131803155705521 +B 6 4 12 6 0.010721008809399569045236686599157841352003524865061 0 67.645666868493653450016016424320161504144039086217 +B 6 4 12 7 0.0082058739102617917193352806900716920786661327766265 0 87.731203791681321190944546078901879401131946924772 +B 6 4 12 10 0.00059951136458574762800353287243872291465431123054020 0 1561.5669162788015344868083749221022406039770185557 +B 6 4 13 5 0.0063989896489825307859075274678553659036741430729618 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 6 8 0.57607947823979466441425335394039632232511759415940 0 2.7182818284590452353602874713526624977572470937000 +B 6 8 7 10 0.14874917848464659716616517699815066094979731136836 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 7 12 0.012646264085135842440177708316111946096966858827151 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 8 9 0.10080191643480122502288745417618728194313756915516 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 8 11 0.0098747018567576841270642715302625810212492713403719 0 27.474593022118317968158957115156725710168223409107 +B 6 8 11 9 0.0070107783041475027598899699341989017555848661549748 0 7.3890560989306502272304274605750078131803155705521 +B 6 8 11 11 0.00068678600552261761483244084027971864043997968375863 0 47.560129945305985709087486769738443607156131247662 +B 6 8 12 10 0.0024595833879168384143124068560279370882903927476942 0 47.560129945305985709087486769738443607156131247662 +B 6 8 12 12 0.00020910731326304407947322496373812515248638402587572 0 271.61119081430170616620385934206623250649206587918 +B 6 9 6 9 0.57607947823979466441425335394039632232511759415940 0 2.7182818284590452353602874713526624977572470937000 +B 6 9 7 10 0.23667765549036378831263561147280888112688617403816 0 7.3890560989306502272304274605750078131803155705521 +B 6 9 7 12 0.026307043608571563067717065985834671365790930736226 0 7.3890560989306502272304274605750078131803155705521 +B 6 9 8 11 0.029712082483464987962095093781171775429139265968841 0 27.474593022118317968158957115156725710168223409107 +B 6 9 11 11 0.0020664768152582609616366818330936561989769096098119 0 27.474593022118317968158957115156725710168223409107 +B 6 9 12 10 0.0039134900485873182007877204699212473391606944641618 0 7.3890560989306502272304274605750078131803155705521 +B 6 9 12 12 0.00043498974652505410829073316923541237215472255481294 0 176.84196693478213160623653883004191830972551316345 +B 6 11 6 11 0.40084033981295463697999652886903224821914345096682 0 2.7182818284590452353602874713526624977572470937000 +B 6 11 7 12 0.17579564151184037789728008909335560605084935860993 0 7.3890560989306502272304274605750078131803155705521 +B 6 11 12 12 0.0029067995126799010749095964470227645951565015258954 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 7 1 0.35520202124700576579148899596145203581012439279854 0 2.7182818284590452353602874713526624977572470937000 +B 7 1 8 2 0.064149561303270088774745365198112266103551737244206 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 8 4 0.019011951243403937205644261610113850220430641231919 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 8 8 0.0029001356848639084176819976260269522994997854892197 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 9 3 0.029519075059994200347629099905243238141448371844874 0 27.474593022118317968158957115156725710168223409107 +B 7 1 9 5 0.0092958951192119087576542400680885564295266146272487 0 47.560129945305985709087486769738443607156131247662 +B 7 1 10 3 0.027588728769638643183349762733796384644885953903243 0 27.474593022118317968158957115156725710168223409107 +B 7 1 10 5 0.0086880069444491401764399107298942361399761775452173 0 122.24381690163789252812627762718103990693477612483 +B 7 1 11 2 0.016437732714171910457281957067646925171251414377073 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 11 4 0.0048716369459881488427573335425644057347749278394622 0 176.84196693478213160623653883004191830972551316345 +B 7 1 11 8 0.00074313298881741089696464122633435486307285542161379 0 658.50248697264293364068901384864374363813058946609 +B 7 1 12 3 0.0041979312301124888180054183592942774451769476185867 0 47.560129945305985709087486769738443607156131247662 +B 7 1 12 5 0.0013219766660533568906828502805141633159505461770378 0 697.71934600893105890558407148347426645603678298274 +B 7 1 12 6 0.00055060218153587733071944233012835570787432512639732 0 1159.2943291236041931991080168474943738874539514468 +B 7 1 12 7 0.00038711587178116635197036371793686838934854115778840 0 1159.2943291236041931991080168474943738874539514468 +B 7 1 13 3 0.0017484348646924108059308846127397322697338375486408 0 47.560129945305985709087486769738443607156131247662 +B 7 1 13 5 0.00055060218153587733071944233012835570787432512639732 0 827.00118299840720480273312354377774115860616489853 +B 7 1 14 3 0.00071013471456336911034049146252434891197245329201382 0 47.560129945305985709087486769738443607156131247662 +B 7 1 14 5 0.00022362956202645537322128510574538107082275718917949 0 956.28301998788335069988217560408121586117554681432 +B 7 3 7 3 0.41949826965485584960523736582332182730334743949532 0 2.7182818284590452353602874713526624977572470937000 +B 7 3 8 4 0.12727076168918476118712684448820837684328919852277 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 8 8 0.033495755297465748734929805323874077683264409644338 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 8 9 0.025612369165234558362053475959213323016361055851068 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 9 5 0.10778489422237375725581586671597754511596737057911 0 27.474593022118317968158957115156725710168223409107 +B 7 3 10 5 0.10073649686250857363076906441032295480622569119005 0 27.474593022118317968158957115156725710168223409107 +B 7 3 11 4 0.032611957439360468827580226481728670456098186460348 0 7.3890560989306502272304274605750078131803155705521 +B 7 3 11 8 0.0085829779885179465741835487367001427882775437179611 0 176.84196693478213160623653883004191830972551316345 +B 7 3 11 9 0.0065629330888871196651997018699073650046124418047529 0 196.92750385796979934716506848462363620671342100200 +B 7 3 12 5 0.015328175854794647273679862710795911133087972019826 0 47.560129945305985709087486769738443607156131247662 +B 7 3 12 6 0.014112399505269110111251204034407270553987097855762 0 196.92750385796979934716506848462363620671342100200 +B 7 3 12 7 0.0055233699897958370760482254140968016595937380542314 0 196.92750385796979934716506848462363620671342100200 +B 7 3 12 10 0.00099500404654393493318469305613118867889897823981997 0 1253.1093381930365575421133356851857751081508820021 +B 7 3 13 5 0.0063841724905868001638379131794788607285968937340870 0 47.560129945305985709087486769738443607156131247662 +B 7 3 14 5 0.0025929604819014731942476187281539405390980959407423 0 47.560129945305985709087486769738443607156131247662 +B 7 5 7 5 0.88974548444276548822279219511716724248344689070726 0 2.7182818284590452353602874713526624977572470937000 +B 7 5 8 8 0.15391749604848146567094768935966383340095793061975 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 9 0.076412472230608409913306284135515318411563775464545 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 8 11 0.0041249175020619779115813604651971711339500212185902 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 11 8 0.039439937057692356346049860518743686011728311278354 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 11 9 0.019579990401147080143450213765527520501470722951168 0 27.474593022118317968158957115156725710168223409107 +B 7 5 11 11 0.0010569720195958387776266189452031789191312610067670 0 47.560129945305985709087486769738443607156131247662 +B 7 5 12 6 0.042340188037239370077303699058146443568981710823769 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 12 7 0.033751158521766097042100720437835974674588351022236 0 7.3890560989306502272304274605750078131803155705521 +B 7 5 12 10 0.0022107803960694720956133517325198292579998524038830 0 142.32935382482556026905480728176275780392268396339 +B 7 5 12 12 0.000060143252271744394472206493553893752296973220570579 0 291.69672773748937390713238899664795040347997371773 +B 7 6 7 6 0.88974548444276548822279219511716724248344689070726 0 2.7182818284590452353602874713526624977572470937000 +B 7 6 8 8 0.16635496615542728597721186482624007481853807917433 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 8 9 0.088849942337554230219570459602091559829143924019133 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 8 11 0.0053496993192600474054807233043673899684002569479603 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 11 8 0.042626923922527810649856619678367264715255166940739 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 11 9 0.022766977265982534447256972925151099204997578613557 0 27.474593022118317968158957115156725710168223409107 +B 7 6 11 11 0.0013708110503742666582885966640720029751896665919202 0 47.560129945305985709087486769738443607156131247662 +B 7 6 12 7 0.035411336736299691806994629840227448224713878299722 0 7.3890560989306502272304274605750078131803155705521 +B 7 6 12 10 0.0038709586106030668605072611349113028081253796813687 0 142.32935382482556026905480728176275780392268396339 +B 7 6 12 12 0.00022362956202645537322128510574538107082275718917949 0 291.69672773748937390713238899664795040347997371773 +B 7 7 7 7 0.75862135761836391234845840669038124810418480163011 0 2.7182818284590452353602874713526624977572470937000 +B 7 7 8 8 0.18212173841988966672296452355556158415234478676087 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 9 0.10461671460201661096532311833141306916295063160567 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 11 0.0065744811364581168993800861435376088028504926773308 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 11 8 0.046667013721789464467824313411952820282585370767157 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 11 9 0.026807067065244188265224666658736654772327782439973 0 27.474593022118317968158957115156725710168223409107 +B 7 7 11 11 0.0016846500811526945389505743829408270312480721770734 0 47.560129945305985709087486769738443607156131247662 +B 7 7 12 10 0.0059755386901447192278664212633056093292755600722770 0 142.32935382482556026905480728176275780392268396339 +B 7 7 12 12 0.00038711587178116635197036371793686838934854115778840 0 291.69672773748937390713238899664795040347997371773 +B 7 10 7 10 0.40405711505506868071350953553361598227824567674202 0 2.7182818284590452353602874713526624977572470937000 +B 7 10 8 11 0.074366138283492418690284915888331332366310155606715 0 7.3890560989306502272304274605750078131803155705521 +B 7 10 11 11 0.019055636223452736715830757731015362151494962735058 0 7.3890560989306502272304274605750078131803155705521 +B 7 10 12 12 0.0027012393253336050318605875690942912135772043097098 0 47.560129945305985709087486769738443607156131247662 +B 7 12 7 12 0.32571121432557149183680462876927723979043175945964 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 8 0 0.11333347705456518598364298664576205580820476717089 0 2.7182818284590452353602874713526624977572470937000 +B 8 0 9 1 0.058370695511357389347458156257026560518424706753514 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 3 0.012008303634293168284457689829857925800853375011859 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 9 5 0.0016753538676658389237826347868567334650495497598494 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 10 1 0.054787937841809061967637854806394208170393930439021 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 10 3 0.011271241285333187198351968121022701933679901212797 0 27.474593022118317968158957115156725710168223409107 +B 8 0 10 5 0.0015725216696595751022419455859966585969537034362089 0 47.560129945305985709087486769738443607156131247662 +B 8 0 11 2 0.0051854759767665767234193112614332297908333334246153 0 47.560129945305985709087486769738443607156131247662 +B 8 0 11 4 0.00079100686356129865328501067763289884587903681863831 0 142.32935382482556026905480728176275780392268396339 +B 8 0 12 1 0.0087549883191647788724169114557931201491750476554847 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 12 3 0.0018011188170743081726322909523013896887882183354203 0 176.84196693478213160623653883004191830972551316345 +B 8 0 12 5 0.00025128539952086074380552475326678757015229270454790 0 658.50248697264293364068901384864374363813058946609 +B 8 0 12 6 0.000067581052516667379320316337298162768149862854282859 0 807.86986088530674727876659556352893623768787922043 +B 8 0 12 7 0.000067581052516667379320316337298162768149862854282859 0 807.86986088530674727876659556352893623768787922043 +B 8 0 13 1 0.0042086787095438730423361627040502431512389260992538 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 13 3 0.00086582987234672242829330068549028510127594152278967 0 196.92750385796979934716506848462363620671342100200 +B 8 0 13 5 0.00012079736402019155168059590488530620272691086553894 0 1159.2943291236041931991080168474943738874539514468 +B 8 0 14 1 0.0022815748293987161418152681018291938514282356157815 0 7.3890560989306502272304274605750078131803155705521 +B 8 0 14 3 0.00046937668081151311182676356953852366059833549565204 0 217.01304078115746708809359813920535410370132884056 +B 8 0 14 5 0.000065485689031380810512116609842493204067839834802139 0 1714.6843213077096918356372810492058825395680504662 +B 8 2 8 2 0.15181709049852325605268699720439615358662826140858 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 3 0.064149561303270088774745365198112266103551737244206 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 5 0.030224639533151688018009074237519872803560554057138 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 3 0.060212100377988973537164322716823005071598991787861 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 5 0.028369469603307989304686186619411687675764446923778 0 27.474593022118317968158957115156725710168223409107 +B 8 2 11 4 0.010727953469293185419519905446988653154903553278261 0 47.560129945305985709087486769738443607156131247662 +B 8 2 11 8 0.0041833047726828459242249886934953451722281790241877 0 142.32935382482556026905480728176275780392268396339 +B 8 2 11 9 0.0020331251892777844990643725238023681345071013760911 0 142.32935382482556026905480728176275780392268396339 +B 8 2 12 3 0.0096217572014435992690728987632947336094942253960536 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 12 5 0.0045333769581728163013154065184333443220552098966351 0 176.84196693478213160623653883004191830972551316345 +B 8 2 12 6 0.0036666080758939959046594192109317308617360321560665 0 271.61119081430170616620385934206623250649206587918 +B 8 2 12 7 0.0034829037288898025401742107949631060597336023058013 0 271.61119081430170616620385934206623250649206587918 +B 8 2 13 3 0.0046253499383285647445388296237791420753931780080476 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 13 5 0.0021792749905140791259334521289946049988550565166176 0 196.92750385796979934716506848462363620671342100200 +B 8 2 14 3 0.0025074572626607261706327763073264810710965741447187 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 14 5 0.0011814109148840022018153185863334966080469640233028 0 217.01304078115746708809359813920535410370132884056 +B 8 4 8 4 0.31398435007114743066116233332772332189352920799795 0 2.7182818284590452353602874713526624977572470937000 +B 8 4 9 5 0.28338921835572394275117351342289349958348233767305 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 10 5 0.26599496107239720491690529386325597126771628009517 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 11 8 0.030275265601928159287392052387819141739731525585639 0 47.560129945305985709087486769738443607156131247662 +B 8 4 11 9 0.0091359078400183390901775583253329321925383158733763 0 47.560129945305985709087486769738443607156131247662 +B 8 4 11 11 0.00079100686356129865328501067763289884587903681863831 0 47.560129945305985709087486769738443607156131247662 +B 8 4 12 5 0.042505392042122399837469493586292741907654984874526 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 12 6 0.041139262971573145773127935803226764591021489093919 0 47.560129945305985709087486769738443607156131247662 +B 8 4 12 7 0.031488044708055553187213487955464160997728099902565 0 47.560129945305985709087486769738443607156131247662 +B 8 4 12 10 0.0023004790053447418403178614278657535451025363754590 0 306.12380392425827750338559089034539301229489507924 +B 8 4 13 5 0.020433098481342379198877837386955251669835231240734 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 14 5 0.011077025926435813590230964107555327825727839128886 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 8 8 0.23885117841202875442235705924240144113561086780796 0 2.7182818284590452353602874713526624977572470937000 +B 8 8 11 9 0.034394687655491765230275362793824326768336941813993 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 11 11 0.0033693534613894255646568026143862142926473072433285 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 12 10 0.0094380528544394059045876903473261088074917955457884 0 27.474593022118317968158957115156725710168223409107 +B 8 8 12 12 0.00080239844053344083726115000117266197615958225534299 0 47.560129945305985709087486769738443607156131247662 +B 8 9 8 9 0.23885117841202875442235705924240144113561086780796 0 2.7182818284590452353602874713526624977572470937000 +B 8 9 11 11 0.010138079044102069889371880893270816811143470273804 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 12 10 0.015017065941062775979392431012286591576155021162765 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 12 12 0.0016691673228122612339171373086742754364787599959117 0 27.474593022118317968158957115156725710168223409107 +B 8 11 8 11 0.16619440743131233097160134989194018895196820034562 0 2.7182818284590452353602874713526624977572470937000 +B 8 11 12 12 0.011154135929161353576623020851398794977949935181964 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 9 1 0.32900422694262355938002127667798825156389178127436 0 2.7182818284590452353602874713526624977572470937000 +B 9 1 10 3 0.066202051965220793597875483047736721338121162552980 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 10 5 0.020847785050668523130070785404639318199881100063376 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 11 2 0.060212100377988973537164322716823005071598991787861 0 7.3890560989306502272304274605750078131803155705521 +B 9 1 11 4 0.017845009278199309810821349317283049300568887482739 0 27.474593022118317968158957115156725710168223409107 +B 9 1 11 8 0.0027221271263457861451848588718282090626376283573071 0 47.560129945305985709087486769738443607156131247662 +B 9 1 12 3 0.022835990048701013386102144369671207512960084334974 0 27.474593022118317968158957115156725710168223409107 +B 9 1 12 5 0.0071913150396702563902950799396942499083764342363401 0 122.24381690163789252812627762718103990693477612483 +B 9 1 12 6 0.0029951767308988145784264174376614760383733360750272 0 142.32935382482556026905480728176275780392268396339 +B 9 1 12 7 0.0021058406417610725678370427576303240808013626237054 0 142.32935382482556026905480728176275780392268396339 +B 9 1 13 3 0.011400166316470196627327570469973999752545927256802 0 27.474593022118317968158957115156725710168223409107 +B 9 1 13 5 0.0035900430553497202367910890210999571556265105365551 0 196.92750385796979934716506848462363620671342100200 +B 9 1 14 3 0.0041979312301124888180054183592942774451769476185867 0 27.474593022118317968158957115156725710168223409107 +B 9 1 14 5 0.0013219766660533568906828502805141633159505461770378 0 271.61119081430170616620385934206623250649206587918 +B 9 3 9 3 0.38855832922073362970233468840835803381036270081448 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 5 0.24172780325512055171152321009715277356907746549653 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 4 0.11945896000416864779262361730393773395684685494125 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 8 0.031439806278219280894022353630473830488634961714744 0 27.474593022118317968158957115156725710168223409107 +B 9 3 11 9 0.024040297575918090883798157667110888083276613780353 0 27.474593022118317968158957115156725710168223409107 +B 9 3 12 5 0.083382516791598349725218951944684104066499450816193 0 27.474593022118317968158957115156725710168223409107 +B 9 3 12 6 0.076768912352330950122660376300680860241637201396211 0 47.560129945305985709087486769738443607156131247662 +B 9 3 12 7 0.030046138254363852154349766998110512385928990275870 0 47.560129945305985709087486769738443607156131247662 +B 9 3 12 10 0.0054126428613947723691163305796319459932324873336965 0 196.92750385796979934716506848462363620671342100200 +B 9 3 13 5 0.041626159289912718289071924707751812277305290679419 0 27.474593022118317968158957115156725710168223409107 +B 9 3 14 5 0.015328175854794647273679862710795911133087972019826 0 27.474593022118317968158957115156725710168223409107 +B 9 5 9 5 0.82412263381018069705636688675414927113569014613263 0 2.7182818284590452353602874713526624977572470937000 +B 9 5 11 8 0.14447013406976264132603250628611943102113783283506 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 11 9 0.071722321316095270391058650321672534441578386002963 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 11 11 0.0038717325830319971881277721576597595283215532784053 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 12 6 0.23032299951531582976606849172764013825823884916749 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 12 7 0.18360022541734873179775788242506979040253063804715 0 7.3890560989306502272304274605750078131803155705521 +B 9 5 12 10 0.012026247300662171971674906223635189818094736753678 0 47.560129945305985709087486769738443607156131247662 +B 9 5 12 12 0.00032716846348558854665829339756802016565741572106159 0 67.645666868493653450016016424320161504144039086217 +B 10 1 10 1 0.32900422694262355938002127667798825156389178127436 0 2.7182818284590452353602874713526624977572470937000 +B 10 1 11 2 0.067028075890717284725373381021175196633356180768881 0 7.3890560989306502272304274605750078131803155705521 +B 10 1 11 4 0.019865054177830136719805196184075827084233989395947 0 7.3890560989306502272304274605750078131803155705521 +B 10 1 11 8 0.0030302703686381429338587669289271515535557612241080 0 7.3890560989306502272304274605750078131803155705521 +B 10 1 12 3 0.026177532554347606866865622137457222827204228089925 0 27.474593022118317968158957115156725710168223409107 +B 10 1 12 5 0.0082436050794410825739746600038914031689515244317942 0 47.560129945305985709087486769738443607156131247662 +B 10 1 12 6 0.0034334546569654058832770947976606177833474446767162 0 47.560129945305985709087486769738443607156131247662 +B 10 1 12 7 0.0024139838840534293565109508147292665717194954905062 0 47.560129945305985709087486769738443607156131247662 +B 10 1 13 3 0.014741708822116790108091048237760015066790071011753 0 27.474593022118317968158957115156725710168223409107 +B 10 1 13 5 0.0046423330951205464204706690852971104162016007320092 0 122.24381690163789252812627762718103990693477612483 +B 10 1 14 3 0.0075394737357590822987688961270802927594210913735377 0 27.474593022118317968158957115156725710168223409107 +B 10 1 14 5 0.0023742667058241830743624303447113165765256363724920 0 196.92750385796979934716506848462363620671342100200 +B 10 3 10 3 0.38855832922073362970233468840835803381036270081448 0 2.7182818284590452353602874713526624977572470937000 +B 10 3 11 4 0.13298164632557211812970170559057769201215985666446 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 11 8 0.034998774465211824059843247428641535473633085418515 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 11 9 0.026761645586824867840491553965583167799240701501256 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 12 5 0.095583705506986053490517409330330824591233410697652 0 27.474593022118317968158957115156725710168223409107 +B 10 3 12 6 0.088002346207858653162654566883731956243281149559919 0 27.474593022118317968158957115156725710168223409107 +B 10 3 12 7 0.034442726617442161529298158295973642639961149962503 0 27.474593022118317968158957115156725710168223409107 +B 10 3 12 10 0.0062046635336171295204423720380287007456956260593744 0 67.645666868493653450016016424320161504144039086217 +B 10 3 13 5 0.053827348005300422054370382093398532802039250560878 0 27.474593022118317968158957115156725710168223409107 +B 10 3 14 5 0.027529364570182351038978320096442631657821931901286 0 27.474593022118317968158957115156725710168223409107 +B 10 5 10 5 0.82412263381018069705636688675414927113569014613263 0 2.7182818284590452353602874713526624977572470937000 +B 10 5 11 8 0.16082407106844679980101791040625088064817063559811 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 9 0.079841247291732603215323803231670791285877393504874 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 11 11 0.0043100105090985884929784495176589012732956618800943 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 6 0.26402568073330081156342148651645016030392814034770 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 7 0.21046606114288431993006507792869184670060814075028 0 7.3890560989306502272304274605750078131803155705521 +B 10 5 12 10 0.013786022832744529848305214484627569093647887197110 0 47.560129945305985709087486769738443607156131247662 +B 10 5 12 12 0.00037504233822947630297866284886656414846359711808613 0 67.645666868493653450016016424320161504144039086217 +B 11 0 11 0 0.12291627437099350686299609995898727225241104981265 0 2.7182818284590452353602874713526624977572470937000 +B 11 0 12 1 0.054787937841809061967637854806394208170393930439021 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 12 3 0.011271241285333187198351968121022701933679901212797 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 12 5 0.0015725216696595751022419455859966585969537034362089 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 12 6 0.00042291621297336405929903230016510813126977851511065 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 12 7 0.00042291621297336405929903230016510813126977851511065 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 13 1 0.033562841915261084109937533856409840333799877204499 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 13 3 0.0069047112256837382285449903910796577448207966736398 0 27.474593022118317968158957115156725710168223409107 +B 11 0 13 5 0.00096331963359334983379407977006176051760092123219867 0 47.560129945305985709087486769738443607156131247662 +B 11 0 14 1 0.020195363177140353040916775354990955591582117188256 0 7.3890560989306502272304274605750078131803155705521 +B 11 0 14 3 0.0041546884256114185423553721137146429964657044909613 0 47.560129945305985709087486769738443607156131247662 +B 11 0 14 5 0.00057964667906269991821556261414286754454707145300487 0 142.32935382482556026905480728176275780392268396339 +B 11 2 11 2 0.16465383075592103332373817580272523595432569818791 0 2.7182818284590452353602874713526624977572470937000 +B 11 2 12 3 0.060212100377988973537164322716823005071598991787861 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 12 5 0.028369469603307989304686186619411687675764446923778 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 12 6 0.022945307067128077735159718708982890774559385574935 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 12 7 0.021795701610441866692216805423151340308875460653836 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 13 3 0.036885659252356844021909131980703499856522981320129 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 13 5 0.017379008245662252159662240377976608562807881976887 0 27.474593022118317968158957115156725710168223409107 +B 11 2 14 3 0.022194761889066302358537988713772786230860301426427 0 7.3890560989306502272304274605750078131803155705521 +B 11 2 14 5 0.010457260564102495768429756676874422247027499690103 0 47.560129945305985709087486769738443607156131247662 +B 11 4 11 4 0.34053297864462401432569831121235282458871068634045 0 2.7182818284590452353602874713526624977572470937000 +B 11 4 12 5 0.26599496107239720491690529386325597126771628009517 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 12 6 0.25744584691340980386373818461406147839667400723968 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 12 7 0.19704938183054391856533597674111519904574221916520 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 12 10 0.014396192908140676681992609459788397903517112719444 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 13 5 0.16294730519892317129432150350459312074556866127379 0 7.3890560989306502272304274605750078131803155705521 +B 11 4 14 5 0.098048312343069502761572061905742969404558127018234 0 7.3890560989306502272304274605750078131803155705521 +B 11 8 11 8 0.25904699778506839562725352375669638773563903809154 0 2.7182818284590452353602874713526624977572470937000 +B 11 8 12 10 0.059062494921302762494221409430991454605915066866763 0 7.3890560989306502272304274605750078131803155705521 +B 11 8 12 12 0.0050213380397182082310706854434913099940054781995033 0 7.3890560989306502272304274605750078131803155705521 +B 11 9 11 9 0.25904699778506839562725352375669638773563903809154 0 2.7182818284590452353602874713526624977572470937000 +B 11 9 12 10 0.093975462370895038316035712539464914814620126521820 0 7.3890560989306502272304274605750078131803155705521 +B 11 9 12 12 0.010445500575898119800597153353920106895210539548347 0 7.3890560989306502272304274605750078131803155705521 +B 11 11 11 11 0.18024680715404741470982797636843384618550945143191 0 2.7182818284590452353602874713526624977572470937000 +B 11 11 12 12 0.069801590097870189844665396596441942094391796133096 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 12 1 0.31335842117604058072923483144950700014262875538385 0 2.7182818284590452353602874713526624977572470937000 +B 12 1 13 3 0.053971108998075364238545499387023499817857795297188 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 5 0.016996120904669066587926717236393359381870668201934 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 14 3 0.037288060022137708514112067982477651666641340054551 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 14 5 0.011742437541155760218881762038613567777652386129287 0 27.474593022118317968158957115156725710168223409107 +B 12 3 12 3 0.37008042635465343791973725568114782867906395824517 0 2.7182818284590452353602874713526624977572470937000 +B 12 3 13 5 0.19706817583541523595900300302892693018292069744257 0 7.3890560989306502272304274605750078131803155705521 +B 12 3 14 5 0.13615228787064936612003594538767289333223871929482 0 7.3890560989306502272304274605750078131803155705521 +B 12 5 12 5 0.78493145752572667452625596003149594088640771943101 0 2.7182818284590452353602874713526624977572470937000 +B 12 6 12 6 0.78493145752572667452625596003149594088640771943101 0 2.7182818284590452353602874713526624977572470937000 +B 12 7 12 7 0.66925404889068852361719392466513055095337609717505 0 2.7182818284590452353602874713526624977572470937000 +B 12 10 12 10 0.35645827462945333275412594634900855475999546166319 0 2.7182818284590452353602874713526624977572470937000 +B 12 12 12 12 0.28734169789370933166067490019358875711362296917740 0 2.7182818284590452353602874713526624977572470937000 +B 13 1 13 1 0.28756620539937377598638706047176323856161783818539 0 2.7182818284590452353602874713526624977572470937000 +B 13 1 14 3 0.050629566492428770757782021619237484503613651542236 0 7.3890560989306502272304274605750078131803155705521 +B 13 1 14 5 0.015943830864898240404247137172196206121295578006480 0 7.3890560989306502272304274605750078131803155705521 +B 13 3 13 3 0.33961947950842935899612393144901248276857486049927 0 2.7182818284590452353602874713526624977572470937000 +B 13 3 14 5 0.18486698712002753219370454564328020965818673756111 0 7.3890560989306502272304274605750078131803155705521 +B 13 5 13 5 0.72032454048032939036177057017020543623262541363551 0 2.7182818284590452353602874713526624977572470937000 +B 14 1 14 1 0.28756620539937377598638706047176323856161783818539 0 2.7182818284590452353602874713526624977572470937000 +B 14 3 14 3 0.33961947950842935899612393144901248276857486049927 0 2.7182818284590452353602874713526624977572470937000 +B 14 5 14 5 0.72032454048032939036177057017020543623262541363551 0 2.7182818284590452353602874713526624977572470937000 +P 0 12 0 0E+0 +P 0 11 0 0E+0 +P 0 10 0 0E+0 +P 0 9 0 0E+0 +P 0 8 0 0E+0 +P 0 7 0 0E+0 +P 0 6 0 0E+0 +P 0 5 1 0E+0 +P 0 4 0 0E+0 +P 0 3 1 2.6097923001933355342308171557443294970222716928515E+0 +P 0 2 0 0E+0 +P 0 1 1 0E+0 +P 0 0 0 0E+0 +P 1 12 0 0E+0 +P 1 11 1 0E+0 +P 1 10 0 0E+0 +P 1 9 1 0E+0 +P 1 8 1 0E+0 +P 1 7 0 0E+0 +P 1 6 0 0E+0 +P 1 5 0 0E+0 +P 1 4 1 2.6097923001933355342308171557443294970222716928515E+0 +P 1 3 0 0E+0 +P 1 2 1 0E+0 +P 1 1 0 0E+0 +P 1 0 1 0E+0 +P 2 12 0 0E+0 +P 2 11 0 0E+0 +P 2 10 0 0E+0 +P 2 9 0 0E+0 +P 2 8 0 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 1 2.6097923001933355342308171557443294970222716928515E+0 +P 2 4 0 0E+0 +P 2 3 1 0E+0 +P 2 2 0 0E+0 +P 2 1 1 0E+0 +P 2 0 0 0E+0 +P 3 12 1 0E+0 +P 3 11 0 0E+0 +P 3 10 1 0E+0 +P 3 9 0 0E+0 +P 3 8 0 0E+0 +P 3 7 1 0E+0 +P 3 6 1 2.6097923001933355342308171557443294970222716928515E+0 +P 3 5 1 0E+0 +P 3 4 0 0E+0 +P 3 3 1 0E+0 +P 3 2 0 0E+0 +P 3 1 1 0E+0 +P 3 0 0 0E+0 +P 4 12 1 0E+0 +P 4 11 0 0E+0 +P 4 10 1 0E+0 +P 4 9 0 0E+0 +P 4 8 0 0E+0 +P 4 7 1 2.6097923001933355342308171557443294970222716928515E+0 +P 4 6 1 0E+0 +P 4 5 1 0E+0 +P 4 4 0 0E+0 +P 4 3 1 0E+0 +P 4 2 0 0E+0 +P 4 1 1 0E+0 +P 4 0 0 0E+0 +P 5 12 0 0E+0 +P 5 11 0 0E+0 +P 5 10 0 0E+0 +P 5 9 0 0E+0 +P 5 8 0 0E+0 +P 5 7 0 0E+0 +P 5 6 0 0E+0 +P 5 5 1 0E+0 +P 5 4 0 0E+0 +P 5 3 1 0E+0 +P 5 2 0 0E+0 +P 5 1 1 1.0384099898329873699476559857223657703835891439521E+0 +P 5 0 0 0E+0 +P 6 12 0 0E+0 +P 6 11 1 0E+0 +P 6 10 0 0E+0 +P 6 9 1 7.2340779953215843454714295591728611637030091838721E-1 +P 6 8 1 8.4772165599310348519908413430219898030584970855325E-1 +P 6 7 0 0E+0 +P 6 6 0 0E+0 +P 6 5 0 0E+0 +P 6 4 1 0E+0 +P 6 3 0 0E+0 +P 6 2 1 1.0384099898329873699476559857223657703835891439521E+0 +P 6 1 0 0E+0 +P 6 0 1 0E+0 +P 7 12 1 0E+0 +P 7 11 0 0E+0 +P 7 10 1 7.5659417847100751807090423546965966176157846611893E-1 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 1 0E+0 +P 7 6 1 0E+0 +P 7 5 1 0E+0 +P 7 4 0 0E+0 +P 7 3 1 1.0384099898329873699476559857223657703835891439521E+0 +P 7 2 0 0E+0 +P 7 1 1 0E+0 +P 7 0 0 0E+0 +P 8 12 0 0E+0 +P 8 11 1 0E+0 +P 8 10 0 0E+0 +P 8 9 1 0E+0 +P 8 8 1 0E+0 +P 8 7 0 0E+0 +P 8 6 0 0E+0 +P 8 5 0 0E+0 +P 8 4 1 1.0384099898329873699476559857223657703835891439521E+0 +P 8 3 0 0E+0 +P 8 2 1 0E+0 +P 8 1 0 0E+0 +P 8 0 1 0E+0 +P 9 12 0 0E+0 +P 9 11 0 0E+0 +P 9 10 0 0E+0 +P 9 9 0 0E+0 +P 9 8 0 0E+0 +P 9 7 0 0E+0 +P 9 6 0 0E+0 +P 9 5 1 1.0384099898329873699476559857223657703835891439521E+0 +P 9 4 0 0E+0 +P 9 3 1 0E+0 +P 9 2 0 0E+0 +P 9 1 1 0E+0 +P 9 0 0 0E+0 +P 10 12 0 0E+0 +P 10 11 0 0E+0 +P 10 10 0 0E+0 +P 10 9 0 0E+0 +P 10 8 0 0E+0 +P 10 7 0 0E+0 +P 10 6 0 0E+0 +P 10 5 1 0E+0 +P 10 4 0 0E+0 +P 10 3 1 0E+0 +P 10 2 0 0E+0 +P 10 1 1 0E+0 +P 10 0 0 0E+0 +P 11 12 0 0E+0 +P 11 11 1 0E+0 +P 11 10 0 0E+0 +P 11 9 1 0E+0 +P 11 8 1 6.5046211572468320925182801182675654473904348804001E-2 +P 11 7 0 0E+0 +P 11 6 0 0E+0 +P 11 5 0 0E+0 +P 11 4 1 0E+0 +P 11 3 0 0E+0 +P 11 2 1 0E+0 +P 11 1 0 0E+0 +P 11 0 1 0E+0 +P 12 12 1 0E+0 +P 12 11 0 0E+0 +P 12 10 1 0E+0 +P 12 9 0 0E+0 +P 12 8 0 0E+0 +P 12 7 1 4.5732512044719359723709928089287063422906450569799E-2 +P 12 6 1 6.5046211572468320925182801182675654473904348804001E-2 +P 12 5 1 0E+0 +P 12 4 0 0E+0 +P 12 3 1 0E+0 +P 12 2 0 0E+0 +P 12 1 1 0E+0 +P 12 0 0 0E+0 +P 13 12 0 0E+0 +P 13 11 0 0E+0 +P 13 10 0 0E+0 +P 13 9 0 0E+0 +P 13 8 0 0E+0 +P 13 7 0 0E+0 +P 13 6 0 0E+0 +P 13 5 1 0E+0 +P 13 4 0 0E+0 +P 13 3 1 0E+0 +P 13 2 0 0E+0 +P 13 1 1 0E+0 +P 13 0 0 0E+0 +P 14 12 0 0E+0 +P 14 11 0 0E+0 +P 14 10 0 0E+0 +P 14 9 0 0E+0 +P 14 8 0 0E+0 +P 14 7 0 0E+0 +P 14 6 0 0E+0 +P 14 5 1 0E+0 +P 14 4 0 0E+0 +P 14 3 1 0E+0 +P 14 2 0 0E+0 +P 14 1 1 0E+0 +P 14 0 0 0E+0 +END +CASE random10 16 14 8 +Z 2.4872742576227010784009272144678128689224276522040E+0 +S 5 5 6 +S 6 6 6 +B 0 0 0 0 0.18695701887732585566966717258355074202859428818460 0 2.7182818284590452353602874713526624977572470937000 +B 0 0 1 1 0.10810263967833063034038466277261899794436994794191 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 1 7 0.00084366938800814223885214277849601985149128113407602 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 2 2 0.010198122445049959442260801152984413697992452148758 0 27.474593022118317968158957115156725710168223409107 +B 0 0 2 3 0.0024759985669741631588701797818898496927801491478335 0 27.474593022118317968158957115156725710168223409107 +B 0 0 2 4 0.00043136961404410694838946967667317105767607666697290 0 27.474593022118317968158957115156725710168223409107 +B 0 0 3 2 0.0020661684347113953097237306593792175409352464274304 0 27.474593022118317968158957115156725710168223409107 +B 0 0 3 3 0.00050164430864975780450907731307716655372359273381672 0 102.15827997845022478719774797259932200994686828628 +B 0 0 3 4 0.000087396703171002659021794763021193252372662835440460 0 176.84196693478213160623653883004191830972551316345 +B 0 0 4 1 0.0021661055965175932334617288453756591511740012328712 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 4 5 0.000021924501266791685029830987460271843261098374391826 0 1084.6106421672722863800692259900517775876753065696 +B 0 0 4 6 0.000016905017198588637520863234194963832875166442250409 0 1084.6106421672722863800692259900517775876753065696 +B 0 0 4 7 0.000016905017198588637520863234194963832875166442250409 0 1084.6106421672722863800692259900517775876753065696 +B 0 0 5 1 0.0010797081370714666242825323031740306795857120984367 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 5 5 0.000010928397238364456836990703517659397807969407178369 0 1104.6961790904599541209977556446334954846632144082 +B 0 0 5 6 0.0000084264057375565686722312745762433461235108247281393 0 4052.9735786470520940238998757143767176302749819336 +B 0 0 5 7 0.0000084264057375565686722312745762433461235108247281393 0 7001.2509782036442339268019957841199397758867494590 +B 0 0 6 1 0.00088492328579400467601082016503342355633546403352934 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 6 5 0.0000089568586737394248482983871351924621415900851422989 0 1124.7817160136476218619262852992152133816511222468 +B 0 0 6 6 0.0000069062391925073049428792888441443644243167059377131 0 7075.9346651599761407458407866415625360756653943361 +B 0 0 6 7 0.0000069062391925073049428792888441443644243167059377131 0 21041.336494777477023922319949225495508316640557337 +B 0 0 7 1 0.00078753086015527370187496409596311999471034000107564 0 7.3890560989306502272304274605750078131803155705521 +B 0 0 7 7 0.0000061461559199826730782032959780948735747196465424997 0 43353.280350991347259787738666354730762833424397797 +B 0 2 0 2 0.089621224404813964947468875754355239943792910939322 0 2.7182818284590452353602874713526624977572470937000 +B 0 2 1 7 0.0045694566193547638442059426302208150062118804022927 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 3 0.0053407965788647467284937033720438431871976241584945 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 4 0.0032961676259346905180129932668271645520935516776338 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 2 8 0.00066416754183434352152902086702199396418316176500262 0 27.474593022118317968158957115156725710168223409107 +B 0 2 2 9 0.00034454412086717754000933277489237541033685452582345 0 27.474593022118317968158957115156725710168223409107 +B 0 2 3 3 0.0010820604838708513608109306748952719657975186673401 0 7.3890560989306502272304274605750078131803155705521 +B 0 2 3 4 0.00066781287839209621532364812483929866444658876896386 0 27.474593022118317968158957115156725710168223409107 +B 0 2 3 8 0.00013456222139831917480933123974354131434306620430250 0 67.645666868493653450016016424320161504144039086217 +B 0 2 3 9 0.000069805612821082632327193732202928900704269199702389 0 142.32935382482556026905480728176275780392268396339 +B 0 2 4 5 0.00015617690025449513780125528925059226039438529401481 0 142.32935382482556026905480728176275780392268396339 +B 0 2 4 6 0.00013751304385545606338896434405231170435799995450891 0 142.32935382482556026905480728176275780392268396339 +B 0 2 4 7 0.000091560442794744935170027014988086057020707800274028 0 142.32935382482556026905480728176275780392268396339 +B 0 2 5 5 0.000077847298995244353618224820918548997942462371413522 0 142.32935382482556026905480728176275780392268396339 +B 0 2 5 6 0.000068544189462831408458785770390406778543978782465291 0 529.22064998316678774353996178834026893556120755030 +B 0 2 5 7 0.000045638843867208349309987476688812223717445180521062 0 916.11194614150801521802511629491778006719973113721 +B 0 2 6 5 0.000063803249463239109905109100093095477769780794094719 0 142.32935382482556026905480728176275780392268396339 +B 0 2 6 6 0.000056178468309089604308475599114214564172279438273949 0 916.11194614150801521802511629491778006719973113721 +B 0 2 6 7 0.000037405363809105327329413292759668018814322129220802 0 2741.5741183943762455379551872165310788331180830824 +B 0 2 7 7 0.000033288623780053816339126200795095916362760603570673 0 5618.7158705834302512288450200466026541016777397989 +B 0 3 0 3 0.11823281764338185076434882439006190423855855985949 0 2.7182818284590452353602874713526624977572470937000 +B 0 3 1 7 0.0060019126840516888728642598418880403679876171290261 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 2 4 0.013744388311960099785610887436624181249497810501482 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 2 8 0.00086739199890639121647893336397720106477592645516690 0 27.474593022118317968158957115156725710168223409107 +B 0 3 2 9 0.00054776857793922523495924527184758251092961921598776 0 27.474593022118317968158957115156725710168223409107 +B 0 3 2 10 0.00029588664266274181842286134536969965728090020686336 0 27.474593022118317968158957115156725710168223409107 +B 0 3 3 4 0.0027846519236854560602083131220997499335387734174479 0 7.3890560989306502272304274605750078131803155705521 +B 0 3 3 8 0.00017573607086189745939770050892297969699666152447159 0 47.560129945305985709087486769738443607156131247662 +B 0 3 3 9 0.00011097946228466091691556300138236728335786451987148 0 122.24381690163789252812627762718103990693477612483 +B 0 3 3 10 0.000059947470195283802629548583568234330603599288661066 0 196.92750385796979934716506848462363620671342100200 +B 0 3 4 5 0.00084849913792544544746595490150682888395979992348451 0 47.560129945305985709087486769738443607156131247662 +B 0 3 4 6 0.00041510295624552030899931914683303982175158775247678 0 47.560129945305985709087486769738443607156131247662 +B 0 3 4 7 0.00012026326733019010460025346671698263382895700406276 0 47.560129945305985709087486769738443607156131247662 +B 0 3 5 5 0.00042293941024346882356073938699186331139028569144102 0 47.560129945305985709087486769738443607156131247662 +B 0 3 5 6 0.00020691052195297212479155304474749373370639690400088 0 176.84196693478213160623653883004191830972551316345 +B 0 3 5 7 0.000059945936401237070798945385099786546484845934369755 0 306.12380392425827750338559089034539301229489507924 +B 0 3 6 5 0.00034663898488305625545981407918428310184416025248523 0 47.560129945305985709087486769738443607156131247662 +B 0 3 6 6 0.00016958280915489397596697115435093262163362622498907 0 306.12380392425827750338559089034539301229489507924 +B 0 3 6 7 0.000049131383925719072736884990320645127846370243450747 0 916.11194614150801521802511629491778006719973113721 +B 0 3 7 7 0.000043724107687960073705854792931074418527132397991245 0 1877.5245565970551988530060629834556047718706394216 +B 0 4 0 4 0.14325534343380362119321309043088424525182362686084 0 2.7182818284590452353602874713526624977572470937000 +B 0 4 1 7 0.0094771952869681756645581260692448222361604229424031 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 1 11 0.0017751161958447976401905927414272186401714309511302 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 2 8 0.0016230378047358414660880851434108496257220834825401 0 27.474593022118317968158957115156725710168223409107 +B 0 4 2 9 0.0013034143837686754845683970512812310718757762433609 0 27.474593022118317968158957115156725710168223409107 +B 0 4 2 10 0.0010515324484921920680320131248033482182270572342366 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 8 0.00032883204713002880225574376236537855525945721810282 0 27.474593022118317968158957115156725710168223409107 +B 0 4 3 9 0.00026407543855279225977360625482476614162066021350272 0 102.15827997845022478719774797259932200994686828628 +B 0 4 3 10 0.00021304344646341514548759183701063318886639498229231 0 176.84196693478213160623653883004191830972551316345 +B 0 4 4 5 0.0013972453987635977755541166859400256228537011982191 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 6 0.00081164835198824167611658766477553214070945419725318 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 7 0.00018989920885814335474044949424479684758856642994496 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 4 11 0.000035568873597627711933154179393244388911551781756313 0 1084.6106421672722863800692259900517775876753065696 +B 0 4 5 5 0.00069646522725212376056191584152709918269131261120923 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 5 6 0.00040457091819126036002147119509934104683106099946213 0 27.474593022118317968158957115156725710168223409107 +B 0 4 5 7 0.000094656383030080963271942158270939372394321707712446 0 47.560129945305985709087486769738443607156131247662 +B 0 4 5 11 0.000017729515269969513831670325104385565521994413676370 0 1144.8672529368352896028548149537969312786390300853 +B 0 4 6 5 0.00057081935032265390484634319918135964177798828571331 0 7.3890560989306502272304274605750078131803155705521 +B 0 4 6 6 0.00033158426242258686339952911085711335926394823564314 0 47.560129945305985709087486769738443607156131247662 +B 0 4 6 7 0.000077579889061084975217999895945120684519102287529251 0 142.32935382482556026905480728176275780392268396339 +B 0 4 6 11 0.000014531020346656810539512789823025278021818061758485 0 1368.9183138058310100599711875261247201779749647168 +B 0 4 7 7 0.000069041642076586981191028764782211340581492577437657 0 291.69672773748937390713238899664795040347997371773 +B 0 4 7 11 0.000012931772885000458893434022182345134271729885799543 0 1905.1769838768360511725339237475874239091707780581 +B 0 8 0 8 0.19479532583719339018116743755310861189517210127073 0 2.7182818284590452353602874713526624977572470937000 +B 0 8 1 11 0.0066828548634582094605427761804512258701199734912405 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 2 9 0.022275396200071186948300195397806843545248802558374 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 2 10 0.0044200943309357424707283045679131074199419702631465 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 3 9 0.0045130582366773865041649038341681254217683292506606 0 7.3890560989306502272304274605750078131803155705521 +B 0 8 3 10 0.00089552360586326293036147403445700450644885137823657 0 27.474593022118317968158957115156725710168223409107 +B 0 8 4 11 0.00013390763966102613150357665864995517947941041142725 0 142.32935382482556026905480728176275780392268396339 +B 0 8 4 12 0.000064271698133072881363380631122140965719800985545052 0 162.41489074801322800998333693634447570091059180195 +B 0 8 4 13 0.000035568873597627711933154179393244388911551781756313 0 162.41489074801322800998333693634447570091059180195 +B 0 8 5 11 0.000066747054432842127793625006686512714198870940867752 0 142.32935382482556026905480728176275780392268396339 +B 0 8 5 12 0.000032036607803998235320628233515359888289395167525061 0 549.30618690635445548446849144292198683254911538885 +B 0 8 5 13 0.000017729515269969513831670325104385565521994413676370 0 990.79563309783992203706390715236037636697837601438 +B 0 8 6 11 0.000054705545598636458428099393008477943726598220066934 0 142.32935382482556026905480728176275780392268396339 +B 0 8 6 12 0.000026257040463270555946984487384002387053866175988430 0 936.19748306469568295895364594949949796418763897576 +B 0 8 6 13 0.000014531020346656810539512789823025278021818061758485 0 2870.8559553838523914351042392768345535356874649981 +B 0 8 7 11 0.000048684791181533623745336586169460558490461859666526 0 142.32935382482556026905480728176275780392268396339 +B 0 9 0 9 0.16018957515550531402873683448537832579596402703660 0 2.7182818284590452353602874713526624977572470937000 +B 0 9 1 11 0.022520537517976034132642220819768978071381573013531 0 7.3890560989306502272304274605750078131803155705521 +B 0 9 2 10 0.010388699359754971204713165834106699655104268383082 0 7.3890560989306502272304274605750078131803155705521 +B 0 9 3 10 0.0021047798563402177162928130502803634496536427489987 0 7.3890560989306502272304274605750078131803155705521 +B 0 9 4 11 0.00045125505260026253093792829619928163272822851881906 0 47.560129945305985709087486769738443607156131247662 +B 0 9 4 12 0.00020691052195297212479155304474749373370639690400088 0 67.645666868493653450016016424320161504144039086217 +B 0 9 4 13 0.000081521474658338840152091508457470036248843935991194 0 67.645666868493653450016016424320161504144039086217 +B 0 9 5 11 0.00022493074805328812100063743514813727228288458400344 0 47.560129945305985709087486769738443607156131247662 +B 0 9 5 12 0.00010313577258536658754197758897967851733458331284919 0 196.92750385796979934716506848462363620671342100200 +B 0 9 5 13 0.000040634860865592572980468618805980120348528015620604 0 380.80749088059018432242438174778798931207353995641 +B 0 9 6 11 0.00018435209461632882270168628093867901386443212303277 0 47.560129945305985709087486769738443607156131247662 +B 0 9 6 12 0.000084529553520540697223099487923394078537616134952728 0 326.20934084744594524431412054492711090928280291780 +B 0 9 6 13 0.000033304124846641087518575096177571823379775370811631 0 1045.3937831309841611151741683552212547697691130530 +B 0 9 7 11 0.00016406276789784917355221070383394988465520589254743 0 47.560129945305985709087486769738443607156131247662 +B 0 10 0 10 0.13305617930024038417011650022664961913314310836226 0 2.7182818284590452353602874713526624977572470937000 +B 0 10 1 11 0.045762191182542034809568885494192217354996812651511 0 7.3890560989306502272304274605750078131803155705521 +B 0 10 4 11 0.00091695946301006277072017818800432379662327943669721 0 7.3890560989306502272304274605750078131803155705521 +B 0 10 4 12 0.00052041406726734140360290967006183147766541299192081 0 27.474593022118317968158957115156725710168223409107 +B 0 10 4 13 0.00013249355978725301587999659078700369397206802236749 0 27.474593022118317968158957115156725710168223409107 +B 0 10 5 11 0.00045706386390780282192394244950737523707928196688978 0 7.3890560989306502272304274605750078131803155705521 +B 0 10 5 12 0.00025940346766951458669402429915552792395461787142854 0 47.560129945305985709087486769738443607156131247662 +B 0 10 5 13 0.000066042197962023520294026341448990726859520200015063 0 122.24381690163789252812627762718103990693477612483 +B 0 10 6 11 0.00037460721317155799348237710943874322355269548296587 0 7.3890560989306502272304274605750078131803155705521 +B 0 10 6 12 0.00021260575990386510604981915293256248592237347231182 0 67.645666868493653450016016424320161504144039086217 +B 0 10 6 13 0.000054127848827857484403056500823166466455006059069362 0 271.61119081430170616620385934206623250649206587918 +B 0 10 7 11 0.00033337888780343557926159443940442721678940224100393 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 1 1 0.37468295951964957947169640048431411849078975162643 0 2.7182818284590452353602874713526624977572470937000 +B 1 1 2 2 0.046487639293086189215242169816935870491274833815265 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 2 3 0.014864428834327943201796394681987820556505449836253 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 2 4 0.0066863906346234282621769448823095067420485292324084 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 2 8 0.00064157721587190052866194196337412805374283502155311 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 2 0.016086850117435717065449398639669779168863279927769 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 3 3 0.0051437724602781970375009814598235920229977285002393 0 27.474593022118317968158957115156725710168223409107 +B 1 1 3 4 0.0023137970781366420318243827541763381578869218299322 0 47.560129945305985709087486769738443607156131247662 +B 1 1 3 8 0.00022201507040233668321383372697906347516280042150742 0 67.645666868493653450016016424320161504144039086217 +B 1 1 4 5 0.00070936489281991235245917349703257163166101229801365 0 291.69672773748937390713238899664795040347997371773 +B 1 1 4 6 0.00055637970882229074114601268961620698086855259564066 0 291.69672773748937390713238899664795040347997371773 +B 1 1 4 7 0.00017971226249516708399223981239766420112814639497165 0 291.69672773748937390713238899664795040347997371773 +B 1 1 5 5 0.00035667920089716993615391611113815659966261201251755 0 291.69672773748937390713238899664795040347997371773 +B 1 1 5 6 0.00027975597882952370420407328469673997400506527271010 0 1084.6106421672722863800692259900517775876753065696 +B 1 1 5 7 0.000090361993985050113520934785597058798128965236453677 0 1877.5245565970551988530060629834556047718706394216 +B 1 1 6 5 0.00018933104519766835000491193475703721978183847263424 0 291.69672773748937390713238899664795040347997371773 +B 1 1 6 6 0.00014849896416404908475315218638767325656596610854523 0 1877.5245565970551988530060629834556047718706394216 +B 1 1 6 7 0.000047965596884544058442362141434388417317280660429573 0 5618.7158705834302512288450200466026541016777397989 +B 1 1 7 7 0.000019952962702166653041138671077804907594719052355756 0 11515.270669696614531034649260186089098392901274850 +B 1 1 8 2 0.00063978559525069764384928924114225295634934239728048 0 7.3890560989306502272304274605750078131803155705521 +B 1 1 8 3 0.00020457152900096834333939297134438633581034925137709 0 47.560129945305985709087486769738443607156131247662 +B 1 1 8 4 0.000092021373365101341964951198636820780210446642442513 0 142.32935382482556026905480728176275780392268396339 +B 1 1 8 8 0.0000088296989736997913737426140829819520738315052871819 0 63986.829730314909457536377517736762165008857647291 +B 1 7 1 7 0.36565210678136423112531683212347416874259007145303 0 2.7182818284590452353602874713526624977572470937000 +B 1 7 2 8 0.11697626338399314502473687087642353299590346284394 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 2 9 0.078387249526134608872353375767675519823630024787805 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 2 10 0.012447381756102137976229726007842202717684066616282 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 8 0.040479139078069868854213195645941582314614394431335 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 3 9 0.027125574742456377663928694755771422670766775068022 0 27.474593022118317968158957115156725710168223409107 +B 1 7 3 10 0.0043073635854574180803620021571475928866566752784145 0 47.560129945305985709087486769738443607156131247662 +B 1 7 4 11 0.00063866781448803191793172223464675815350552550209064 0 291.69672773748937390713238899664795040347997371773 +B 1 7 4 12 0.00017971226249516708399223981239766420112814639497165 0 291.69672773748937390713238899664795040347997371773 +B 1 7 4 13 0.000056280103996555318813675494851837442172687035296697 0 291.69672773748937390713238899664795040347997371773 +B 1 7 5 11 0.00032113166018798880937046326492130867510160545587604 0 291.69672773748937390713238899664795040347997371773 +B 1 7 5 12 0.000090361993985050113520934785597058798128965236453677 0 1084.6106421672722863800692259900517775876753065696 +B 1 7 5 13 0.000028298471947352455771349815260205746484154961704793 0 1877.5245565970551988530060629834556047718706394216 +B 1 7 6 11 0.00017046183998540185419764138654248030696489775269661 0 291.69672773748937390713238899664795040347997371773 +B 1 7 6 12 0.000047965596884544058442362141434388417317280660429573 0 1877.5245565970551988530060629834556047718706394216 +B 1 7 6 13 0.000015021283152514904275628341202177841718883194202508 0 5618.7158705834302512288450200466026541016777397989 +B 1 7 7 11 0.000070909546764493557589594858024862658300928371491096 0 291.69672773748937390713238899664795040347997371773 +B 1 7 8 8 0.0016098844647175034753079298238051801324399977102466 0 7.3890560989306502272304274605750078131803155705521 +B 1 7 8 9 0.0010788036101803440379520344735574070794427501632730 0 47.560129945305985709087486769738443607156131247662 +B 1 7 8 10 0.00017130694669034020662290920370122009765984999757845 0 142.32935382482556026905480728176275780392268396339 +B 1 11 1 11 0.21221908494237961908020309000633765844638044132569 0 2.7182818284590452353602874713526624977572470937000 +B 1 11 4 12 0.0055076093511965108345586398683177478573176327298475 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 4 13 0.0016568290223046518664727348314012974282527708549496 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 5 12 0.0027693077598319630642055010159664556120951121631986 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 5 13 0.00083307823333299590205323092589121992762062891642082 0 27.474593022118317968158957115156725710168223409107 +B 1 11 6 12 0.0014699930114348286938894237233747670235310633512379 0 7.3890560989306502272304274605750078131803155705521 +B 1 11 6 13 0.00044221129870097405260049637311037438669454755503026 0 47.560129945305985709087486769738443607156131247662 +B 2 0 2 0 0.27908238530859269933904006387055946400397923613911 0 2.7182818284590452353602874713526624977572470937000 +B 2 0 3 2 0.026329848661673625676418660231028676883236454177893 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 3 3 0.0063926147098375139595778271784152481427130625595134 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 3 4 0.0011137242876053991852025775882178671192025092951287 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 1 0.030199320903274085646568347775321624306331266985473 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 4 5 0.00030566609978042390858048419018543539952333417986752 0 67.645666868493653450016016424320161504144039086217 +B 2 0 4 6 0.00023568566559095693320826278317780445023600654389240 0 67.645666868493653450016016424320161504144039086217 +B 2 0 4 7 0.00023568566559095693320826278317780445023600654389240 0 67.645666868493653450016016424320161504144039086217 +B 2 0 5 1 0.014820332580613513066197076123056175398881617568933 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 5 5 0.00015000579886793896784876189710934347858758778652942 0 87.731203791681321190944546078901879401131946924772 +B 2 0 5 6 0.00011566286406667343645058674618079405998312633980244 0 271.61119081430170616620385934206623250649206587918 +B 2 0 5 7 0.00011566286406667343645058674618079405998312633980244 0 455.49117783692209114146317260523058561185218483358 +B 2 0 6 1 0.0089276202227677293703654132902041978797721507300529 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 6 5 0.000090361993985050113520934785597058798128965236453677 0 107.81674071486898893187307573348359729811985476333 +B 2 0 6 6 0.000069674153305817560937739795484774447580695144870716 0 530.17486479325399796050196346267318191163082971075 +B 2 0 6 7 0.000069674153305817560937739795484774447580695144870716 0 1452.3706162125130563305878525163804837983955444785 +B 2 0 7 1 0.0061600168489315738871553333124137082235879448294632 0 7.3890560989306502272304274605750078131803155705521 +B 2 0 7 7 0.000048074844985489261009133502590654406638852629976812 0 3206.6971410978431524385056361983221356872617856148 +B 2 0 8 2 0.0020226209891923883410021039989483668101797272483782 0 87.731203791681321190944546078901879401131946924772 +B 2 0 8 3 0.00049107143964554391337573445286057800023116354922206 0 107.81674071486898893187307573348359729811985476333 +B 2 0 8 4 0.000085554693049300420047114629676651597656147069654008 0 127.90227763805665667280160538806531519510776260188 +B 2 0 9 2 0.00093079696408657784741569930806313524607248793154359 0 87.731203791681321190944546078901879401131946924772 +B 2 0 9 3 0.00022598786802573815664808080627135269849924492183075 0 346.29487777063361298524265019950882880627071075635 +B 2 0 9 4 0.000039371710755085621406904493531675696086788719688418 0 659.45670178273014385765101552297665661420021162654 +B 2 2 2 2 0.13378318306189873451022511561079915874738766392247 0 2.7182818284590452353602874713526624977572470937000 +B 2 2 3 3 0.013789044641501569780160409765022856928082862903121 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 4 0.0085101542192694550057851601748254759045723096387366 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 8 0.0017147696506607675495913926967316508788776155761293 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 3 9 0.00088955536752802037837440651609896002400890354500248 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 4 5 0.0021773806115669761697900069258171911150819873108205 0 47.560129945305985709087486769738443607156131247662 +B 2 2 4 6 0.0019171736347726070213742335562353600446544078059607 0 47.560129945305985709087486769738443607156131247662 +B 2 2 4 7 0.0012765135727684335268713562615051287319463245633316 0 47.560129945305985709087486769738443607156131247662 +B 2 2 5 5 0.0010685506777241373927695007591655762739769135338471 0 47.560129945305985709087486769738443607156131247662 +B 2 2 5 6 0.00094085396731663801538009271981034114257657477290975 0 176.84196693478213160623653883004191830972551316345 +B 2 2 5 7 0.00062644970569667094600821890360173458558448138355174 0 306.12380392425827750338559089034539301229489507924 +B 2 2 6 5 0.00064368424848852231137009242679153875174452888016440 0 47.560129945305985709087486769738443607156131247662 +B 2 2 6 6 0.00056676102642087607942024960035012212608698214035697 0 306.12380392425827750338559089034539301229489507924 +B 2 2 6 7 0.00037736704157640248873711110125044095021088210410052 0 916.11194614150801521802511629491778006719973113721 +B 2 2 7 7 0.00026038152120469966719940739747494205734716255836436 0 1877.5245565970551988530060629834556047718706394216 +B 2 2 8 3 0.0010592545164685770502928046960055714042401485022425 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 8 4 0.00065373776987233355696418487282164500166513202267442 0 27.474593022118317968158957115156725710168223409107 +B 2 2 8 8 0.00013172613073561462765698349583794228311679467278540 0 10432.828507766716528037259091141570110720490883034 +B 2 2 8 9 0.000068334360008302341864523949131726253785291225701752 0 10452.914044689904195778187620796151828617478790873 +B 2 2 9 3 0.00048746200765850229627912289252740258664244594496790 0 7.3890560989306502272304274605750078131803155705521 +B 2 2 9 4 0.00030084585038784976103794657978772558422998974282558 0 47.560129945305985709087486769738443607156131247662 +B 2 2 9 8 0.000060619504709351783059736533420561217886070812668135 0 10527.597731646236102597226411653594424917257435750 +B 2 2 9 9 0.000031447026001605996007855256561830135428880749891259 0 38907.051420661186389550377951596591154505140050490 +B 2 3 2 3 0.17649349014985128003173425048092769271967744116851 0 2.7182818284590452353602874713526624977572470937000 +B 2 3 3 4 0.035485714762803322075764950268558668627718612586566 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 8 0.0022394612522658305386139713231074498539087744667558 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 9 0.0014142469691330833673969851424747589990400624356290 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 3 10 0.00076392988653535719252085850396733446918173670137771 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 4 5 0.011829569986595907770440839298752920059877637932850 0 27.474593022118317968158957115156725710168223409107 +B 2 3 4 6 0.0057872651285836761021068941754004927733599976403368 0 27.474593022118317968158957115156725710168223409107 +B 2 3 4 7 0.0016766814179417366260315724451022217009485593401416 0 27.474593022118317968158957115156725710168223409107 +B 2 3 5 5 0.0058053676785820644062797963506800008871255143903869 0 27.474593022118317968158957115156725710168223409107 +B 2 3 5 6 0.0028401033987654459207884727788524423291813025836490 0 102.15827997845022478719774797259932200994686828628 +B 2 3 5 7 0.00082283228570670138619397724481406855419374303794303 0 176.84196693478213160623653883004191830972551316345 +B 2 3 6 5 0.0034970954670550228630463460159791352818072638940593 0 27.474593022118317968158957115156725710168223409107 +B 2 3 6 6 0.0017108499016234713177022012508405582928797768705628 0 176.84196693478213160623653883004191830972551316345 +B 2 3 6 7 0.00049566594500251382585334390791642627696496902707388 0 529.22064998316678774353996178834026893556120755030 +B 2 3 7 7 0.00034200721989386916296813149649565573355934203726731 0 1084.6106421672722863800692259900517775876753065696 +B 2 3 8 4 0.0027259614142870639077186964418968461290621388070525 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 8 8 0.00017203218261976980964450860946351296971797431830334 0 6026.8489100024969277649455266765422960196810014726 +B 2 3 8 9 0.00010864041189245752385204906275729694038647087121970 0 6046.9344469256845955058740563311240139166689093112 +B 2 3 8 10 0.000058683991793196965388764553926271139922027305975378 0 6067.0199838488722632468025859857057318136568171498 +B 2 3 9 4 0.0012544696323203278346708291107757376583056084083398 0 7.3890560989306502272304274605750078131803155705521 +B 2 3 9 8 0.000079168086439964489285201790334849539443619828955153 0 6046.9344469256845955058740563311240139166689093112 +B 2 3 9 9 0.000049995607732218702233320513476118456986429766178276 0 22449.693858776863372989070873362427145009509111382 +B 2 3 9 10 0.000027005989601343817256594322255483481715089375497072 0 38907.051420661186389550377951596591154505140050490 +B 2 4 2 4 0.21384617273952658395968066170324944420872169756194 0 2.7182818284590452353602874713526624977572470937000 +B 2 4 3 8 0.0041904125000590090632423512386297234434837516695100 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 9 0.0033651982169262618920253650579970325886150396383830 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 3 10 0.0027148811343285357171492384194896080587567139041317 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 5 0.019480057780065080203460826871626861841279758926586 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 6 0.011315805232077654169824706392646078219515664772871 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 7 0.0026475288909297462443224445164219150333715497236057 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 4 11 0.00049589264238532608162403615275963552066358604875219 0 67.645666868493653450016016424320161504144039086217 +B 2 4 5 5 0.0095598485778808519374950887534766912987130015391682 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 5 6 0.0055532373557001256551157042554332622450162601571874 0 27.474593022118317968158957115156725710168223409107 +B 2 4 5 7 0.0012992761925354333643534342513064596611906366349654 0 47.560129945305985709087486769738443607156131247662 +B 2 4 5 11 0.00024335957447417281383999478553602919138346510073976 0 127.90227763805665667280160538806531519510776260188 +B 2 4 6 5 0.0057587572361316968086441171928400024883090496363636 0 7.3890560989306502272304274605750078131803155705521 +B 2 4 6 6 0.0033452146805011375304380371691578482512596067846352 0 47.560129945305985709087486769738443607156131247662 +B 2 4 6 7 0.00078267099259386620106952022356980842904688589472074 0 142.32935382482556026905480728176275780392268396339 +B 2 4 6 11 0.00014659737537346379288758262192619107323824188467817 0 351.95333850705237712991797796039310409444369723340 +B 2 4 7 7 0.00054003938129589612204782757873012250250010096725182 0 291.69672773748937390713238899664795040347997371773 +B 2 4 7 11 0.00010115151404029186255670197631172631931593011207370 0 888.21200857805741824248071418185580782563951057465 +B 2 4 8 8 0.00032190144291755148503436213595659037111130501403634 0 1620.8693122382773274926319622115144813188711199110 +B 2 4 8 9 0.00025850967219023919924190258925037434177980156695269 0 1640.9548491614649952335604918660961992158590277495 +B 2 4 8 10 0.00020855325209097864077861808041934854131535800170836 0 1661.0403860846526629744890215206779171128469355881 +B 2 4 9 8 0.00014813694083258914421538036399675446525764100099876 0 1620.8693122382773274926319622115144813188711199110 +B 2 4 9 9 0.00011896446212484335716349908713802338280045093822189 0 6046.9344469256845955058740563311240139166689093112 +B 2 4 9 10 0.000095974843993968472186772895917388407529110547540684 0 10527.597731646236102597226411653594424917257435750 +B 2 8 2 8 0.29078311425836351286750726492347014903804357205180 0 2.7182818284590452353602874713526624977572470937000 +B 2 8 3 9 0.057511352112797746814357773644687617910890375579641 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 3 10 0.011411945231188043887484074283112065903843165716242 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 4 11 0.0018669079605466387990751244076764218220888112090263 0 47.560129945305985709087486769738443607156131247662 +B 2 8 4 12 0.00089606048755862918078425233635672848966582082556218 0 47.560129945305985709087486769738443607156131247662 +B 2 8 4 13 0.00049589264238532608162403615275963552066358604875219 0 47.560129945305985709087486769738443607156131247662 +B 2 8 5 11 0.00091618606131293523218521013324075426698962035215339 0 47.560129945305985709087486769738443607156131247662 +B 2 8 5 12 0.00043974215448420325402575312674836315999272675513106 0 176.84196693478213160623653883004191830972551316345 +B 2 8 5 13 0.00024335957447417281383999478553602919138346510073976 0 306.12380392425827750338559089034539301229489507924 +B 2 8 6 11 0.00055190132639092750521999174424555855207424567529839 0 47.560129945305985709087486769738443607156131247662 +B 2 8 6 12 0.00026489627879957513000381542859217639999232880765154 0 306.12380392425827750338559089034539301229489507924 +B 2 8 6 13 0.00014659737537346379288758262192619107323824188467817 0 916.11194614150801521802511629491778006719973113721 +B 2 8 7 11 0.00038080937413148831740512215756690676446886852096115 0 47.560129945305985709087486769738443607156131247662 +B 2 8 8 9 0.0044179391000261357189942632323856252852720031403615 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 8 10 0.00087664916911253289462225734122639354625323008521748 0 27.474593022118317968158957115156725710168223409107 +B 2 8 9 9 0.0020331067084721966555556780281107690728365997046666 0 7.3890560989306502272304274605750078131803155705521 +B 2 8 9 10 0.00040342821988848238177126736461470824621499235204026 0 47.560129945305985709087486769738443607156131247662 +B 2 9 2 9 0.23912495505345510227533714091429221748692386814484 0 2.7182818284590452353602874713526624977572470937000 +B 2 9 3 10 0.026821886421528847804477740249885459393536626456083 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 4 11 0.0062912889217441579939730355726438723377400185358859 0 27.474593022118317968158957115156725710168223409107 +B 2 9 4 12 0.0028846965082253742915573987110266432268308052645548 0 27.474593022118317968158957115156725710168223409107 +B 2 9 4 13 0.0011365527043894995761269134474898668333716692913814 0 27.474593022118317968158957115156725710168223409107 +B 2 9 5 11 0.0030874533397494119291713582849358158593101847238907 0 27.474593022118317968158957115156725710168223409107 +B 2 9 5 12 0.0014156663251788399917321877345708664392277567255946 0 102.15827997845022478719774797259932200994686828628 +B 2 9 5 13 0.00055776383609413988321186860174463574837555849009778 0 176.84196693478213160623653883004191830972551316345 +B 2 9 6 11 0.0018598510339001833688646381727464438780430549087487 0 27.474593022118317968158957115156725710168223409107 +B 2 9 6 12 0.00085278324522152488899073751297273116778518082290841 0 176.84196693478213160623653883004191830972551316345 +B 2 9 6 13 0.00033599136021793738357072112102587224911434192093460 0 529.22064998316678774353996178834026893556120755030 +B 2 9 7 11 0.0012832886502172626774005988873679623347957496346742 0 27.474593022118317968158957115156725710168223409107 +B 2 9 8 10 0.0020604186200616846903973697587186036394299551400230 0 7.3890560989306502272304274605750078131803155705521 +B 2 9 9 10 0.00094819118685534973554820618521293040869623069641801 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 2 10 0.19862124525811237035918377185933719907923541407546 0 2.7182818284590452353602874713526624977572470937000 +B 2 10 4 11 0.012784027299155660983766306028152330815825490202143 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 4 12 0.0072554871956616829160484938109067453696698230696097 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 4 13 0.0018471932005831400460020121492277290953670801699857 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 5 11 0.0062737681055797636380433227845452952614506126057762 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 5 12 0.0035606341486450839037160913079644753456156550322378 0 27.474593022118317968158957115156725710168223409107 +B 2 10 5 13 0.00090651103251537248398191756888179172397211332618277 0 47.560129945305985709087486769738443607156131247662 +B 2 10 6 11 0.0037792552027877948255252736006555697677373653202771 0 7.3890560989306502272304274605750078131803155705521 +B 2 10 6 12 0.0021448904239101286127894376823382798093575354062048 0 47.560129945305985709087486769738443607156131247662 +B 2 10 6 13 0.00054607318574164352683705461023783777553871204877399 0 142.32935382482556026905480728176275780392268396339 +B 2 10 7 11 0.0026076686893797781794572409996411225615104405441075 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 3 0 0.30644695820795267822020262493342462380356203651641 0 2.7182818284590452353602874713526624977572470937000 +B 3 0 4 1 0.12917682643862982325455947832153979177951523271345 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 4 5 0.0013074789610659068385515587146454264203581307817331 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 4 6 0.0010081394351756849579317461401961007082991061884588 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 4 7 0.0010081394351756849579317461401961007082991061884588 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 1 0.079152063123575977428170754467322615699077881368595 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 5 5 0.00080114723447090367401348642098004174282305314006555 0 27.474593022118317968158957115156725710168223409107 +B 3 0 5 6 0.00061772934364742430752138483569849766591395382732732 0 47.560129945305985709087486769738443607156131247662 +B 3 0 5 7 0.00061772934364742430752138483569849766591395382732732 0 67.645666868493653450016016424320161504144039086217 +B 3 0 6 1 0.033101663063015518802410198192493893768578364840065 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 6 5 0.00033504250897313112121430237646204557082218569339105 0 47.560129945305985709087486769738443607156131247662 +B 3 0 6 6 0.00025833652075032557594716923780512357753411659065156 0 142.32935382482556026905480728176275780392268396339 +B 3 0 6 7 0.00025833652075032557594716923780512357753411659065156 0 291.69672773748937390713238899664795040347997371773 +B 3 0 7 1 0.012340279607384981031746335049013718477117742425260 0 7.3890560989306502272304274605750078131803155705521 +B 3 0 7 7 0.000096307695863774284822098810253028526524935962397334 0 827.95539780849441501969512521811065413467578705899 +B 3 0 8 2 0.0040862807006073910864142883834347081753501353619075 0 87.731203791681321190944546078901879401131946924772 +B 3 0 8 3 0.00099210665624720440185767635006689663965904953991309 0 87.731203791681321190944546078901879401131946924772 +B 3 0 8 4 0.00017284527992233358369701517904273511486094034750512 0 87.731203791681321190944546078901879401131946924772 +B 3 0 9 2 0.0029021007797528212056824594817914398016700830952519 0 87.731203791681321190944546078901879401131946924772 +B 3 0 9 3 0.00070460002913285136362327324644777944373900882295158 0 326.20934084744594524431412054492711090928280291780 +B 3 0 9 4 0.00012275574254226788701673922087589518080659830627127 0 564.68747790321056929768369501095234241743365891082 +B 3 0 10 2 0.0022460378993094401159096260638515532889869562996708 0 87.731203791681321190944546078901879401131946924772 +B 3 0 10 3 0.00054531475279149679510270726633356774284986282990670 0 564.68747790321056929768369501095234241743365891082 +B 3 0 10 4 0.000095004988121497646015284822219906720495571565870640 0 1689.8945384581904701669954253080728023304767783111 +B 3 2 3 2 0.14690088542623004796335753970662207409121655494170 0 2.7182818284590452353602874713526624977572470937000 +B 3 2 4 5 0.0093136901406525045943845069160253020903536113452416 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 4 6 0.0082006614209953466696729586754343204895820420523251 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 4 7 0.0054602543138043166567779391025600271113853833601245 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 5 0.0057068888457062927187202618849514543336707394447269 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 5 6 0.0050248894352430643256973768574744487309738664143673 0 27.474593022118317968158957115156725710168223409107 +B 3 2 5 7 0.0033457269855003378796129249456065200767014459487656 0 47.560129945305985709087486769738443607156131247662 +B 3 2 6 5 0.0023866404014475256259064530848552264973282476271530 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 6 6 0.0021014259193046742333447593193097331340319639719054 0 47.560129945305985709087486769738443607156131247662 +B 3 2 6 7 0.0013991944493217311461939442999870970307192512116418 0 142.32935382482556026905480728176275780392268396339 +B 3 2 7 7 0.00052161882914647327635039702442048990528718572949391 0 291.69672773748937390713238899664795040347997371773 +B 3 2 8 3 0.0021400011721449851094025091529391263582471398335793 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 8 4 0.0013207397958201142912418479819149648334490306411713 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 8 8 0.00026612496788414806280772951077847054298694148024517 0 1620.8693122382773274926319622115144813188711199110 +B 3 2 8 9 0.00013805521547651811034304399590779874026976317648105 0 1620.8693122382773274926319622115144813188711199110 +B 3 2 9 3 0.0015198415198032710913926285661609692409298923359894 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 9 4 0.00093799723321268761478609454058908497799748181930912 0 27.474593022118317968158957115156725710168223409107 +B 3 2 9 8 0.00018900352995658901617158478390618123031454238804562 0 1661.0403860846526629744890215206779171128469355881 +B 3 2 9 9 0.000098047632514279721402091338447856509700718783818009 0 6067.0199838488722632468025859857057318136568171498 +B 3 2 10 3 0.0011762588254130003265296922947187727966549363650382 0 7.3890560989306502272304274605750078131803155705521 +B 3 2 10 4 0.00072594906074300117744226985060511177430064510100213 0 47.560129945305985709087486769738443607156131247662 +B 3 2 10 8 0.00014627648162581124204476406590635128666353092285230 0 1755.8096099641722375344563420327022313096134883038 +B 3 2 10 9 0.000075882512454785712736172238136643267076206364562765 0 10676.965105558899916235303993368479617516814725504 +B 3 3 3 3 0.19379902153309396327203314081582211359763023828427 0 2.7182818284590452353602874713526624977572470937000 +B 3 3 4 5 0.050600684495407084732138560384211820294490573932433 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 6 0.024754879272412335598308644462091571450537949050785 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 4 7 0.0071719620852419183427291124920496601362750018395894 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 5 0.031005163106243410974880661066017465144398255006665 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 5 6 0.015168353495023879429226598319053234130808569174815 0 27.474593022118317968158957115156725710168223409107 +B 3 3 5 7 0.0043945621776105250056200131436466138332165176046016 0 47.560129945305985709087486769738443607156131247662 +B 3 3 6 5 0.012966465078166887816070720624826322787500729312678 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 6 6 0.0063434572239651589619450314111564030830957966844457 0 47.560129945305985709087486769738443607156131247662 +B 3 3 6 7 0.0018378209079101936292899043428464343805916730723683 0 142.32935382482556026905480728176275780392268396339 +B 3 3 7 7 0.00068513850282191427703829457907904596671531259254000 0 291.69672773748937390713238899664795040347997371773 +B 3 3 8 4 0.0055072322384281017153334079053910588733666529467978 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 8 8 0.00034755487631087077571705785978127463164111921763926 0 1620.8693122382773274926319622115144813188711199110 +B 3 3 8 9 0.00021948512390324082325237234491060282892394091387515 0 1620.8693122382773274926319622115144813188711199110 +B 3 3 8 10 0.00011855867430451844175746294637419905575815518924240 0 1620.8693122382773274926319622115144813188711199110 +B 3 3 9 4 0.0039112689862559855514689770534122665055364417883218 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 9 8 0.00024683553369173774636365544784936292617439395138605 0 1640.9548491614649952335604918660961992158590277495 +B 3 3 9 9 0.00015587963624942845159416200239103820556057034715844 0 6046.9344469256845955058740563311240139166689093112 +B 3 3 9 10 0.000084201073385502066888692111580440716900030597377642 0 10452.914044689904195778187620796151828617478790873 +B 3 3 10 4 0.0030270686803208758240899607774707223469316207021245 0 7.3890560989306502272304274605750078131803155705521 +B 3 3 10 8 0.00019103470404467997333861627183283702316687580265102 0 1661.0403860846526629744890215206779171128469355881 +B 3 3 10 9 0.00012064073487365444403002444406312900357955124436148 0 10527.597731646236102597226411653594424917257435750 +B 3 3 10 10 0.000065166173175585158486050018268916229493341646004836 1096.6331584284585992637202382881214324422191348337 30274.216195943132452426198286410083098774491213748 +B 3 4 3 4 0.23481420759672231623457748694722637886507154362267 0 2.7182818284590452353602874713526624977572470937000 +B 3 4 4 5 0.083325451288447111111022417325870675256878834255897 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 4 6 0.048403068835861094054850381785530432994523987004153 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 4 7 0.011324737437980328160955492879964260827302254417981 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 4 11 0.0021211681548328428826432943807870823090706754813752 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 5 0.051057001182150872742501481090244321162219036994231 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 5 6 0.029658591757485415048269157329527017906843943183122 0 27.474593022118317968158957115156725710168223409107 +B 3 4 5 7 0.0069391419286399592112194516682730921670949104133011 0 47.560129945305985709087486769738443607156131247662 +B 3 4 5 11 0.0012997287541106527005442698631755032686108268576868 0 67.645666868493653450016016424320161504144039086217 +B 3 4 6 5 0.021352212228516680939497292880780546082484332879309 0 7.3890560989306502272304274605750078131803155705521 +B 3 4 6 6 0.012403324342248142716606480894862104006170376682414 0 47.560129945305985709087486769738443607156131247662 +B 3 4 6 7 0.0029019728482587936542695462663714858404887385906190 0 142.32935382482556026905480728176275780392268396339 +B 3 4 6 11 0.00054355100289317696850886300335061694083040024589911 0 291.69672773748937390713238899664795040347997371773 +B 3 4 7 7 0.0010818536909272301421636812926881619871087801490006 0 291.69672773748937390713238899664795040347997371773 +B 3 4 7 11 0.00020263547918444903270417336379489387121549840417147 0 827.95539780849441501969512521811065413467578705899 +B 3 4 8 8 0.00065033422510703792020178605539048595113847639153751 0 1620.8693122382773274926319622115144813188711199110 +B 3 4 8 9 0.00052226447269940796773710054051981414842129808777341 0 1620.8693122382773274926319622115144813188711199110 +B 3 4 8 10 0.00042133802310068558624219114198341037525551236314065 0 1620.8693122382773274926319622115144813188711199110 +B 3 4 9 8 0.00046187122228351690048006512028115539215601320072847 0 1620.8693122382773274926319622115144813188711199110 +B 3 4 9 9 0.00037091532484120760571057167482283067154218959650083 0 6026.8489100024969277649455266765422960196810014726 +B 3 4 9 10 0.00029923676197728122100510178401223318288164984672004 0 10432.828507766716528037259091141570110720490883034 +B 3 4 10 8 0.00035745838913888782997053954921547534542550459772092 0 1620.8693122382773274926319622115144813188711199110 +B 3 4 10 9 0.00028706441996786230066194772144576732583818003943139 0 10432.828507766716528037259091141570110720490883034 +B 3 4 10 10 0.00023158985826979301511797329565155455175197044107475 1096.6331584284585992637202382881214324422191348337 30124.848822030468638788120704695197906174933923994 +B 3 8 3 8 0.31929496648159630847269423805983622021490218537856 0 2.7182818284590452353602874713526624977572470937000 +B 3 8 4 11 0.0079856512790088543868208481581913160249875465392319 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 4 12 0.0038328759262704445685944677702767153339602939608404 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 4 13 0.0021211681548328428826432943807870823090706754813752 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 5 11 0.0048931436972502740321507965858420753590042913222225 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 5 12 0.0023485639462208398265513580612155970251258985135229 0 27.474593022118317968158957115156725710168223409107 +B 3 8 5 13 0.0012997287541106527005442698631755032686108268576868 0 47.560129945305985709087486769738443607156131247662 +B 3 8 6 11 0.0020463294018302394765844649697350057505998876248764 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 6 12 0.00098217746148163945160482304620995429070282210662561 0 47.560129945305985709087486769738443607156131247662 +B 3 8 6 13 0.00054355100289317696850886300335061694083040024589911 0 142.32935382482556026905480728176275780392268396339 +B 3 8 7 11 0.00076287034096520589851745763206256595303709282367819 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 8 9 0.0089255176216202210022529139182471004477221066538376 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 8 10 0.0017710854381959110567768427419284476401751741060723 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 9 9 0.0063389555312613165471412283419858713430242508011131 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 9 10 0.0012578353783755537181853968853466179573936128593195 0 27.474593022118317968158957115156725710168223409107 +B 3 8 10 9 0.0049059407118393620644698203797409792178804524034400 0 7.3890560989306502272304274605750078131803155705521 +B 3 8 10 10 0.00097348305428743511763290717562518515372946317291950 0 47.560129945305985709087486769738443607156131247662 +B 3 9 3 9 0.26257162388345302456289951548450307450551163981008 0 2.7182818284590452353602874713526624977572470937000 +B 3 9 4 11 0.026910828217707084436031497827317501355404834385214 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 4 12 0.012339215883849593901831880122098633229821018801940 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 4 13 0.0048615752620238728955383139536613756872673341735758 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 5 11 0.016489393899203877572043980219589856615182508133529 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 5 12 0.0075607554501882681015796822228691450809325421044796 0 27.474593022118317968158957115156725710168223409107 +B 3 9 5 13 0.0029788912038533791466287217750434319228832473232889 0 47.560129945305985709087486769738443607156131247662 +B 3 9 6 11 0.0068959208316859698013911540537285791724634785793461 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 6 12 0.0031619337454697954154948273422714006212979221500405 0 47.560129945305985709087486769738443607156131247662 +B 3 9 6 13 0.0012457824728761200556596780226732530441431130061628 0 142.32935382482556026905480728176275780392268396339 +B 3 9 7 11 0.0025707950398563258925552903186202158116479627633265 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 8 10 0.0041626428714615355490800870258276662294820658109519 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 9 10 0.0029563336462190163063591296838307383857301992054493 0 7.3890560989306502272304274605750078131803155705521 +B 3 9 10 10 0.0022880106227658725938029392778434498901899739059094 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 3 10 0.21809644624300207825448233935808044985500538039557 0 2.7182818284590452353602874713526624977572470937000 +B 3 10 4 11 0.054683351354125927538664697656170387688841573259421 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 4 12 0.031035161790677169082122960332731526144855535306052 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 4 13 0.0079013218951051247890531461009849947775230174590510 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 5 11 0.033506784440526386537251711436226554757256840713362 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 5 12 0.019016546178064850918209152425752770981221466705053 0 27.474593022118317968158957115156725710168223409107 +B 3 10 5 13 0.0048414715444195849592052752721929046540647671016292 0 47.560129945305985709087486769738443607156131247662 +B 3 10 6 11 0.014012651661950595895373097659832563610172909415334 0 7.3890560989306502272304274605750078131803155705521 +B 3 10 6 12 0.0079527845436676121407116481761268626870983294173657 0 47.560129945305985709087486769738443607156131247662 +B 3 10 6 13 0.0020247199310818686880776261806528111407438948691660 0 142.32935382482556026905480728176275780392268396339 +B 3 10 7 11 0.0052239079112179631054208769293289776369249661320498 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 4 1 0.34476291934747108076490012391292607585550537944089 0 2.7182818284590452353602874713526624977572470937000 +B 4 1 5 5 0.0068433375399628741422211696405919068523474129308165 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 6 0.0053674691070788729158042258244315114822960244000990 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 5 7 0.0017337081166167303106186624249417206022407541408100 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 5 0.0033517616700353187586618974919739109238358799192483 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 6 6 0.0026289039687355922190664484996830602151822924524501 0 27.474593022118317968158957115156725710168223409107 +B 4 1 6 7 0.00084914362010800425017377072921197641824445992705573 0 47.560129945305985709087486769738443607156131247662 +B 4 1 7 7 0.00027955444791593490187905988059629444836196463607138 0 142.32935382482556026905480728176275780392268396339 +B 4 1 8 2 0.010626561204608650728488348972739870708887545240757 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 8 3 0.0033978443556506334153846332881134674171187616143746 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 8 4 0.0015284350936554278738211842937347819704078030671477 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 8 8 0.00014665746971924921184927283660758369875156685642355 0 678.58802389583060138161754350322546153511849730464 +B 4 1 9 2 0.0091717198195462197027522768311299506864879947964223 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 9 3 0.0029326586296739691613194972249610777581662951155000 0 27.474593022118317968158957115156725710168223409107 +B 4 1 9 4 0.0013191829578217473447768590110426974350241736423027 0 47.560129945305985709087486769738443607156131247662 +B 4 1 9 8 0.00012657916289280660335887174096282962627227008242326 0 738.84463466539360460440313246697061522608222082031 +B 4 1 10 2 0.0083187624682520597783390764271178194588720814363035 0 7.3890560989306502272304274605750078131803155705521 +B 4 1 10 3 0.0026599254033835447843104769055128877692464589604263 0 47.560129945305985709087486769738443607156131247662 +B 4 1 10 4 0.0011965007538606036787536339080607585483855951381751 0 142.32935382482556026905480728176275780392268396339 +B 4 1 10 8 0.00011480747452526747212190656877169178295252928857364 0 962.89569553438932506151950503929840412541815545183 +B 4 1 11 7 0.000081474643166997140946524917942383548777966947514815 0 962.89569553438932506151950503929840412541815545183 +B 4 5 4 5 0.27155836587049741866090736613985466050706796675445 0 2.7182818284590452353602874713526624977572470937000 +B 4 5 5 6 0.075858407099605611668429696407855557817090684648437 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 7 0.026692794489896865633607864497080181443769014406247 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 11 0.0057643912611738034528368598489272090973454720665303 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 5 12 0.0013367859625217997735860284004460229871913064743786 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 6 0.037154283239954370381007872047551398533576471659214 0 7.3890560989306502272304274605750078131803155705521 +B 4 5 6 7 0.013073720960701237886841762619371072504510362307319 0 27.474593022118317968158957115156725710168223409107 +B 4 5 6 11 0.0028233103463713880440132831028837943796937573899502 0 47.560129945305985709087486769738443607156131247662 +B 4 5 6 12 0.00065473724247220842522693612601124225373299498955569 0 67.645666868493653450016016424320161504144039086217 +B 4 5 7 7 0.0043041209506007443818264142926557003894737383824905 0 47.560129945305985709087486769738443607156131247662 +B 4 5 7 11 0.00092948818843485116393746711263471805205472908004333 0 142.32935382482556026905480728176275780392268396339 +B 4 5 8 8 0.0031375665503321605159144387175872637428172399727572 0 271.61119081430170616620385934206623250649206587918 +B 4 5 8 9 0.0028453340733096889920063006031851147442960432037347 0 271.61119081430170616620385934206623250649206587918 +B 4 5 8 10 0.0026150378026536592150109820269972483656866705019473 0 271.61119081430170616620385934206623250649206587918 +B 4 5 9 8 0.0027080144517820539658141997856974485023192701296308 0 271.61119081430170616620385934206623250649206587918 +B 4 5 9 9 0.0024557903926706572585558675402725087211304693753745 0 1009.9269552109403795610304351326091812878966616925 +B 4 5 9 10 0.0022570230935158338016528696619258640863384854487279 0 1748.2427196075790529558570109231521300693012575058 +B 4 5 10 8 0.0024561728256198836440385824231781575358678920504008 0 271.61119081430170616620385934206623250649206587918 +B 4 5 10 9 0.0022274052577255248611957655950066965909346316573124 0 1748.2427196075790529558570109231521300693012575058 +B 4 5 10 10 0.0020471230445029688267571443717918521300291206823787 0 5231.8245744250890237543598655400251429700392162120 +B 4 5 11 7 0.0012544129460721173160430494184157469358429057183738 0 47.560129945305985709087486769738443607156131247662 +B 4 5 11 11 0.00027089434292757472730585467146219050590016917083293 1096.6331584284585992637202382881214324422191348337 29337.829448025312797789782328112773352117917755962 +B 4 6 4 6 0.30754725187697977753927236561919577021288605416436 0 2.7182818284590452353602874713526624977572470937000 +B 4 6 5 7 0.12181402102286795517061308510044164954767330809730 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 5 11 0.0057643912611738034528368598489272090973454720665303 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 5 12 0.0013367859625217997735860284004460229871913064743786 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 5 13 0.00014601950023700816248812632695893014204296347508442 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 7 0.059662637441604312888334914830137508205052605654699 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 6 11 0.0028233103463713880440132831028837943796937573899502 0 27.474593022118317968158957115156725710168223409107 +B 4 6 6 12 0.00065473724247220842522693612601124225373299498955569 0 47.560129945305985709087486769738443607156131247662 +B 4 6 6 13 0.000071518109564820950386432316409039760198600177055515 0 67.645666868493653450016016424320161504144039086217 +B 4 6 7 7 0.019642090308674581123981504369793524158714311501501 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 7 11 0.00092948818843485116393746711263471805205472908004333 0 47.560129945305985709087486769738443607156131247662 +B 4 6 8 8 0.017980396197392157634042189778065131663473597604599 0 67.645666868493653450016016424320161504144039086217 +B 4 6 8 9 0.0099537438129966016574705958429806818855941610040431 0 67.645666868493653450016016424320161504144039086217 +B 4 6 8 10 0.0026150378026536592150109820269972483656866705019473 0 67.645666868493653450016016424320161504144039086217 +B 4 6 9 8 0.015518769712199516209839678705714622748017149183373 0 67.645666868493653450016016424320161504144039086217 +B 4 6 9 9 0.0085910152541871686090335995531861571531142369256798 0 251.52565389111403842527532968748451460950415804062 +B 4 6 9 10 0.0022570230935158338016528696619258640863384854487279 0 435.40564091373442340053464295064886771486427699503 +B 4 6 10 8 0.014075545434801491790364369007564389568249147158721 0 67.645666868493653450016016424320161504144039086217 +B 4 6 10 9 0.0077920626302177023947955248785491102772090074143041 0 435.40564091373442340053464295064886771486427699503 +B 4 6 10 10 0.0020471230445029688267571443717918521300291206823787 0 1303.0032422998492426925102708014952911988382547241 +B 4 6 11 7 0.0057245817796268144265717643860625404565826169937969 0 7.3890560989306502272304274605750078131803155705521 +B 4 6 11 11 0.00027089434292757472730585467146219050590016917083293 0 7579.8037357207255335467399693841759133528561277238 +B 4 7 4 7 0.33645321890568977313963690909545922969473126611154 0 2.7182818284590452353602874713526624977572470937000 +B 4 7 5 11 0.0061613134152687339898694938734229067123949197329617 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 5 12 0.0017337081166167303106186624249417206022407541408100 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 5 13 0.00054294165433193869952076035145462775709241114151578 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 11 0.0030177167240071838689601177060845285442052223274503 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 6 12 0.00084914362010800425017377072921197641824445992705573 0 27.474593022118317968158957115156725710168223409107 +B 4 7 6 13 0.00026592448720061677533326691960977392471006511455557 0 47.560129945305985709087486769738443607156131247662 +B 4 7 7 11 0.00099349051534911230193515464452252467670084129372408 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 8 0.026739482607397031680982623793795616218198880803696 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 9 0.017918459991025715887129370239850849043561773537363 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 8 10 0.0028453340733096889920063006031851147442960432037347 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 9 8 0.023078683486837980309908101896623020066648755665626 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 9 9 0.015465312952242942959484344338636327601620332355763 0 27.474593022118317968158957115156725710168223409107 +B 4 7 9 10 0.0024557903926706572585558675402725087211304693753745 0 47.560129945305985709087486769738443607156131247662 +B 4 7 10 8 0.020932397607461484802689711167182685011971802636098 0 7.3890560989306502272304274605750078131803155705521 +B 4 7 10 9 0.014027060080129689041960173402607773084221397740566 0 47.560129945305985709087486769738443607156131247662 +B 4 7 10 10 0.0022274052577255248611957655950066965909346316573124 0 142.32935382482556026905480728176275780392268396339 +B 4 7 11 11 0.00028954747753541684980648864777717853953081656255929 0 827.95539780849441501969512521811065413467578705899 +B 4 11 4 11 0.19527248145947965626921523603870786498135978055203 0 2.7182818284590452353602874713526624977572470937000 +B 4 11 5 12 0.053132640492912276141750116002109953922362076598565 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 5 13 0.015983650107866091116041874280227581597558789646543 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 6 12 0.026023551635723641278688115168665655581406063425962 0 7.3890560989306502272304274605750078131803155705521 +B 4 11 6 13 0.0078285464462260170609895366284076633908765973743352 0 27.474593022118317968158957115156725710168223409107 +B 4 12 4 12 0.24135778055313884611157718700715417353164493803672 0 2.7182818284590452353602874713526624977572470937000 +B 4 12 5 13 0.067400118656396534846960667535388883211831801130568 0 7.3890560989306502272304274605750078131803155705521 +B 4 12 6 13 0.033011543409791806968382012697007028446166411795924 0 7.3890560989306502272304274605750078131803155705521 +B 4 13 4 13 0.20983821396791219115451520572454598434533135025416 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 5 1 0.42606222272522797970205431876880290478682508555577 0 2.7182818284590452353602874713526624977572470937000 +B 5 1 6 5 0.011281779943155940712027279537179476627073918071259 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 6 0.0088486948019343307329618053321236626532727154989293 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 6 7 0.0028581541306582109843576801917732179059348812855298 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 7 7 0.0022858853597389784093263687400345652554011993331590 0 47.560129945305985709087486769738443607156131247662 +B 5 1 8 2 0.060445088426338610404825635206707172202151573117493 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 3 0.019327325047275423807824012984660943416305425275548 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 4 0.0086939126036232931853529906151552820688929250400583 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 8 8 0.00083420436346976848939046564814369154645744398695726 0 196.92750385796979934716506848462363620671342100200 +B 5 1 9 2 0.033562299784534658242464337367581250823188353658546 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 9 3 0.010731549810871798648682612219954366221607228681873 0 27.474593022118317968158957115156725710168223409107 +B 5 1 9 4 0.0048273186242243023666672369229153635558852763832578 0 47.560129945305985709087486769738443607156131247662 +B 5 1 9 8 0.00046319424219982429562910262365440445044882983862759 0 257.18411462753280256995065744836878989767714451766 +B 5 1 10 2 0.0098860893840899840798633657861424840486381484376369 0 7.3890560989306502272304274605750078131803155705521 +B 5 1 10 3 0.0031610783927560209299870237901690291829416237200748 0 47.560129945305985709087486769738443607156131247662 +B 5 1 10 4 0.0014219318613724559149125118248187841030879109149240 0 142.32935382482556026905480728176275780392268396339 +B 5 1 10 8 0.00013643819732202473305281984154203951171103725005649 0 481.23517549652852302706703002069657879701307914918 +B 5 1 11 7 0.00012137548767743089212380164940263999684509320686737 0 868.12647165486975050155218452727408992865160273609 +B 5 1 12 7 0.000060561867375138598966323689278565100646652651058690 0 868.12647165486975050155218452727408992865160273609 +B 5 5 5 5 0.33559514225427825871505088428593604135291783748908 0 2.7182818284590452353602874713526624977572470937000 +B 5 5 6 6 0.12505854792904628708581068528013979807384120642516 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 7 0.044005170246875707655752178242225821882497940867975 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 11 0.0095030521781286858272579540695321612403056022507248 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 6 12 0.0022037967544638558900615314543647193189019945337339 0 7.3890560989306502272304274605750078131803155705521 +B 5 5 7 7 0.035194314169820219653692908816641412032884196399691 0 27.474593022118317968158957115156725710168223409107 +B 5 5 7 11 0.0076003206453450985861886265152633145299958573935059 0 47.560129945305985709087486769738443607156131247662 +B 5 5 8 8 0.017846835295702226252562280371108399884293268993245 0 122.24381690163789252812627762718103990693477612483 +B 5 5 8 9 0.016184583738066580155025690127723564865318788600631 0 122.24381690163789252812627762718103990693477612483 +B 5 5 8 10 0.014874632364707659372365269299888299778246208425114 0 122.24381690163789252812627762718103990693477612483 +B 5 5 9 8 0.0099095038487621733828049749244771614757592884025114 0 122.24381690163789252812627762718103990693477612483 +B 5 5 9 9 0.0089865341493685745848155456076866307900905294152011 0 454.53696302683488092450117093089767263578256267313 +B 5 5 9 10 0.0082591800856978282396425232244764709391844549910963 0 786.83010915203186932087606423461430536463034922143 +B 5 5 10 8 0.0029189370641993049794766758911727119467903586770475 0 122.24381690163789252812627762718103990693477612483 +B 5 5 10 9 0.0026470677046622587889726832263250170045454878324798 0 786.83010915203186932087606423461430536463034922143 +B 5 5 10 10 0.0024328187606538535919960678957788929059826755550019 403.42879349273512260838718054338827960589989735717 1951.2540287432998954550828521665652880955796621383 +B 5 5 11 7 0.0018687407168671079739247499643767908157432605598724 0 27.474593022118317968158957115156725710168223409107 +B 5 5 11 11 0.00040356031893871772097393199557133150728489018888365 1096.6331584284585992637202382881214324422191348337 12600.979459111675306890375075842501824820134949322 +B 5 5 12 7 0.00093243231907089086608617741016331208163088404592104 0 27.474593022118317968158957115156725710168223409107 +B 5 5 12 11 0.00020136163389422005058378768915895108683547838978529 1096.6331584284585992637202382881214324422191348337 12675.663146068007213709413866699944421119913594199 +B 5 6 5 6 0.38007064673819514917140414086770509204113833020800 0 2.7182818284590452353602874713526624977572470937000 +B 5 6 6 7 0.20081999041339460519401643282574039950695814607627 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 11 0.0095030521781286858272579540695321612403056022507248 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 12 0.0022037967544638558900615314543647193189019945337339 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 6 13 0.00024072462588079060717308524213922355780333427834645 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 7 7 0.16061116897260704654542793890935291505982472012157 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 7 11 0.0076003206453450985861886265152633145299958573935059 0 27.474593022118317968158957115156725710168223409107 +B 5 6 8 8 0.10227453803405600711239246500233291246187364850517 0 47.560129945305985709087486769738443607156131247662 +B 5 6 8 9 0.056618026600060208274095063928804577386504916839581 0 47.560129945305985709087486769738443607156131247662 +B 5 6 8 10 0.014874632364707659372365269299888299778246208425114 0 47.560129945305985709087486769738443607156131247662 +B 5 6 9 8 0.056788215472740659503849534277500011241706824850027 0 47.560129945305985709087486769738443607156131247662 +B 5 6 9 9 0.031437313294291801054023204516627661832320782759144 0 176.84196693478213160623653883004191830972551316345 +B 5 6 9 10 0.0082591800856978282396425232244764709391844549910963 0 306.12380392425827750338559089034539301229489507924 +B 5 6 10 8 0.016727500133503143218344418708797946065303507335354 0 47.560129945305985709087486769738443607156131247662 +B 5 6 10 9 0.0092601547336818842668413919302037037609174921190720 0 306.12380392425827750338559089034539301229489507924 +B 5 6 10 10 0.0024328187606538535919960678957788929059826755550019 0 916.11194614150801521802511629491778006719973113721 +B 5 6 11 7 0.0085281000105440351769954211652625864226148653852819 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 11 11 0.00040356031893871772097393199557133150728489018888365 0 5329.1876230833151607043916879954874936134626808356 +B 5 6 12 7 0.0042552056571182135101205302517118854940287493751186 0 7.3890560989306502272304274605750078131803155705521 +B 5 6 12 11 0.00020136163389422005058378768915895108683547838978529 0 5349.2731600065028284453202176500692115104505886741 +B 5 7 5 7 0.41579299351952592258065100458657110749066060526718 0 2.7182818284590452353602874713526624977572470937000 +B 5 7 6 11 0.010157409554323040921554102806940659827338489002521 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 6 12 0.0028581541306582109843576801917732179059348812855298 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 6 13 0.00089508200207514570146923397954772214483622103014229 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 7 11 0.0081236604926385751578838955719094389012464929065417 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 8 8 0.15209721748722380072528938510165986029571928294610 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 8 9 0.10192223784977928179751629558678334252997174828807 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 8 10 0.016184583738066580155025690127723564865318788600631 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 9 8 0.084452393777546592575845314089562566513960601998230 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 9 9 0.056592599837017807051033127343919640406944140286819 0 27.474593022118317968158957115156725710168223409107 +B 5 7 9 10 0.0089865341493685745848155456076866307900905294152011 0 47.560129945305985709087486769738443607156131247662 +B 5 7 10 8 0.024876242657540112662899631559203031386657357780057 0 7.3890560989306502272304274605750078131803155705521 +B 5 7 10 9 0.016669879717974502224817041374496375221621448768264 0 47.560129945305985709087486769738443607156131247662 +B 5 7 10 10 0.0026470677046622587889726832263250170045454878324798 0 142.32935382482556026905480728176275780392268396339 +B 5 7 11 11 0.00043134851440339808859774770123127390056984672954488 0 827.95539780849441501969512521811065413467578705899 +B 5 7 12 11 0.00021522691295945364234898911991792422485824757931118 0 827.95539780849441501969512521811065413467578705899 +B 5 11 5 11 0.24132011541486284730235311741411130130701604758934 0 2.7182818284590452353602874713526624977572470937000 +B 5 11 6 12 0.087593335026859550482352608928654037685722937333041 0 7.3890560989306502272304274605750078131803155705521 +B 5 11 6 13 0.026350303803124145282597152760344678682583675822184 0 7.3890560989306502272304274605750078131803155705521 +B 5 12 5 12 0.29827289039415789908836064295340069377720752640734 0 2.7182818284590452353602874713526624977572470937000 +B 5 12 6 13 0.11111439445791098230571398878721341281806687774297 0 7.3890560989306502272304274605750078131803155705521 +B 5 13 5 13 0.25932062538823739010092672917574477935479293573688 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 6 1 0.65567764014864360191063668487796711359862794589652 0 2.7182818284590452353602874713526624977572470937000 +B 6 1 7 7 0.0053558515781021271367467740737252783594146386326100 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 2 0.18092977832526779976140643326526661653096226181700 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 3 0.057852320634544538007458476091519083312242732831934 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 4 0.026023415981428089707546706591139109385750833945372 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 8 8 0.0024970169535691899763965767724464363825033174644711 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 2 0.062318572122316998229223729297797417447666876367572 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 9 3 0.019926371707734392936876871495348875736067079313241 0 27.474593022118317968158957115156725710168223409107 +B 6 1 9 4 0.0089633787247126601877851882004209403527167379583174 0 47.560129945305985709087486769738443607156131247662 +B 6 1 9 8 0.00086006036459017762328527537588766151910307268249040 0 87.731203791681321190944546078901879401131946924772 +B 6 1 10 2 0.011558672398144820853394584639485860283577535416108 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 10 3 0.0036958870335041309543806768347497685430301733112625 0 47.560129945305985709087486769738443607156131247662 +B 6 1 10 4 0.0016625021198512456463218122664933797424804731249106 0 142.32935382482556026905480728176275780392268396339 +B 6 1 10 8 0.00015952156248725754941686849853646456928693484304397 0 311.78226466067704164806091865122966830046788155628 +B 6 1 11 7 0.00016395593091502787003767898398586712556340612760638 0 827.95539780849441501969512521811065413467578705899 +B 6 1 12 7 0.00010314231061273557688020102386179222936496557179770 0 827.95539780849441501969512521811065413467578705899 +B 6 1 13 2 0.00063978559525069764384928924114225295634934239728048 0 7.3890560989306502272304274605750078131803155705521 +B 6 1 13 3 0.00020457152900096834333939297134438633581034925137709 0 67.645666868493653450016016424320161504144039086217 +B 6 1 13 4 0.000092021373365101341964951198636820780210446642442513 0 291.69672773748937390713238899664795040347997371773 +B 6 1 13 8 0.0000088296989736997913737426140829819520738315052871819 0 5349.2731600065028284453202176500692115104505886741 +B 6 5 6 5 0.51645562357341658612499291772659397815660390890455 0 2.7182818284590452353602874713526624977572470937000 +B 6 5 7 7 0.082460619594754165641545504406104497156296507064754 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 7 11 0.017807625018912539958620898432558869782776654712507 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 8 8 0.053420783026796595894392311091566542793101475418904 0 27.474593022118317968158957115156725710168223409107 +B 6 5 8 9 0.048445179323108438617485606569769028567000132874717 0 27.474593022118317968158957115156725710168223409107 +B 6 5 8 10 0.044524112818465288662293918266653583066458880127899 0 27.474593022118317968158957115156725710168223409107 +B 6 5 9 8 0.018399994465815050621320607013903238479023403912067 0 27.474593022118317968158957115156725710168223409107 +B 6 5 9 9 0.016686221746197150452226597055629473529706016732079 0 102.15827997845022478719774797259932200994686828628 +B 6 5 9 10 0.015335668686176736691476611584873927057437072519441 0 176.84196693478213160623653883004191830972551316345 +B 6 5 10 8 0.0034127789022603568667477497263581201475630288755895 0 27.474593022118317968158957115156725710168223409107 +B 6 5 10 9 0.0030949131881348691096279674252693290727775062643127 0 176.84196693478213160623653883004191830972551316345 +B 6 5 10 10 0.0028444164285742044268104767972765098772452498840015 0 529.22064998316678774353996178834026893556120755030 +B 6 5 11 7 0.0025243245546170931288503716092068724302207359448286 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 11 11 0.00054513566979691107563552088370024289134032775424642 0 3078.5715104459047878620434066067990738740692339473 +B 6 5 12 7 0.0015880161568208760210117990549933936961083594308772 0 7.3890560989306502272304274605750078131803155705521 +B 6 5 12 11 0.00034293698475241340524537657728786247089091595514806 0 3098.6570473690924556029719362613807917710571417859 +B 6 5 13 8 0.00018890117361506935785633528230050263920087479943638 0 67.645666868493653450016016424320161504144039086217 +B 6 5 13 9 0.00017130694669034020662290920370122009765984999757845 0 291.69672773748937390713238899664795040347997371773 +B 6 5 13 10 0.00015744166762510661485770777294224695963708080805257 0 1124.7817160136476218619262852992152133816511222468 +B 6 6 6 6 0.58490007198733208889115201003143702395719372039041 0 2.7182818284590452353602874713526624977572470937000 +B 6 6 7 7 0.37631352733323022083990210252219506567317771756769 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 7 11 0.017807625018912539958620898432558869782776654712507 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 8 8 0.30613752045993677267365290138899565819440453045383 0 27.474593022118317968158957115156725710168223409107 +B 6 6 8 9 0.16947426612580307748493398418384179492783889004773 0 27.474593022118317968158957115156725710168223409107 +B 6 6 8 10 0.044524112818465288662293918266653583066458880127899 0 27.474593022118317968158957115156725710168223409107 +B 6 6 9 8 0.10544451734104354717598227225023293943612701865914 0 27.474593022118317968158957115156725710168223409107 +B 6 6 9 9 0.058372891263099774238461703452323377128166601037692 0 102.15827997845022478719774797259932200994686828628 +B 6 6 9 10 0.015335668686176736691476611584873927057437072519441 0 176.84196693478213160623653883004191830972551316345 +B 6 6 10 8 0.019557550672589257585348611134396819018426082341924 0 27.474593022118317968158957115156725710168223409107 +B 6 6 10 9 0.010826838678498504761501556491889932636358900998246 0 176.84196693478213160623653883004191830972551316345 +B 6 6 10 10 0.0028444164285742044268104767972765098772452498840015 0 529.22064998316678774353996178834026893556120755030 +B 6 6 11 7 0.011519892549319082811654161400506132789442245578267 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 11 11 0.00054513566979691107563552088370024289134032775424642 403.42879349273512260838718054338827960589989735717 2675.1427169531696652536562260634107942681693365901 +B 6 6 12 7 0.0072469981958932611447792704869554318608561295681036 0 7.3890560989306502272304274605750078131803155705521 +B 6 6 12 11 0.00034293698475241340524537657728786247089091595514806 0 3098.6570473690924556029719362613807917710571417859 +B 6 6 13 8 0.0010825325580398395974202591213977164829610057756051 0 67.645666868493653450016016424320161504144039086217 +B 6 6 13 9 0.00059927777083795628172369426685608706784936759238365 0 291.69672773748937390713238899664795040347997371773 +B 6 6 13 10 0.00015744166762510661485770777294224695963708080805257 0 1124.7817160136476218619262852992152133816511222468 +B 6 7 6 7 0.63987407059330512425093616864073997267072372059134 0 2.7182818284590452353602874713526624977572470937000 +B 6 7 7 11 0.019033815359153642372806746901786939623602378320759 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 8 8 0.45527133072837450221849730952918441219001678470087 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 8 9 0.30508298326089176535193788517031358946884444065647 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 8 10 0.048445179323108438617485606569769028567000132874717 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 9 8 0.15681144100123601003975622998680350502244194069438 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 9 9 0.10508129768144606076763824776271472095907857569179 0 27.474593022118317968158957115156725710168223409107 +B 6 7 9 10 0.016686221746197150452226597055629473529706016732079 0 47.560129945305985709087486769738443607156131247662 +B 6 7 10 8 0.029084942306712658358262256333502072581219251602452 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 10 9 0.019490181718024434511046319461300847139172816110959 0 47.560129945305985709087486769738443607156131247662 +B 6 7 10 10 0.0030949131881348691096279674252693290727775062643127 0 142.32935382482556026905480728176275780392268396339 +B 6 7 11 11 0.00058267240429777352403425092815458632019649195878341 0 827.95539780849441501969512521811065413467578705899 +B 6 7 12 11 0.00036655080285382907778549234684123664448489280854972 0 827.95539780849441501969512521811065413467578705899 +B 6 7 13 8 0.0016098844647175034753079298238051801324399977102466 0 7.3890560989306502272304274605750078131803155705521 +B 6 7 13 9 0.0010788036101803440379520344735574070794427501632730 0 67.645666868493653450016016424320161504144039086217 +B 6 7 13 10 0.00017130694669034020662290920370122009765984999757845 0 291.69672773748937390713238899664795040347997371773 +B 6 11 6 11 0.37137346461636102614365835250711440871782955871827 0 2.7182818284590452353602874713526624977572470937000 +B 6 12 6 12 0.45901949166725863636036873407465948655001442444769 0 2.7182818284590452353602874713526624977572470937000 +B 6 13 6 13 0.39907489241561916637651890932693931860791996869201 0 2.7182818284590452353602874713526624977572470937000 +B 7 1 7 1 0.70657224345076044718162010405539423312191085787967 0 2.7182818284590452353602874713526624977572470937000 +B 7 1 8 2 0.22110050464673322082796490111987900267398089117962 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 8 3 0.070696915707744827930267919685491818502290942710932 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 8 4 0.031801235039274160596110737539082588451542689970311 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 8 8 0.0030514142760572530618104156840191602107466240789245 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 9 2 0.098610233086160733128290785928055091165887097803064 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 9 3 0.031530635117961858058207261508643304640901446566811 0 27.474593022118317968158957115156725710168223409107 +B 7 1 9 4 0.014183265681193642854716176358764121091703973958574 0 47.560129945305985709087486769738443607156131247662 +B 7 1 9 8 0.0013609225971022222651597170652471589182992168089152 0 67.645666868493653450016016424320161504144039086217 +B 7 1 10 2 0.014473745034328124036359778282618530678551754403062 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 10 3 0.0046279818958450498082163204333975753645726977014847 0 47.560129945305985709087486769738443607156131247662 +B 7 1 10 4 0.0020817816244726361701967355437769161106320883334867 0 142.32935382482556026905480728176275780392268396339 +B 7 1 10 8 0.00019975256183303406802279482997898759904794885410517 0 291.69672773748937390713238899664795040347997371773 +B 7 1 11 7 0.00023548794612597307014155935660182474436659332896086 0 827.95539780849441501969512521811065413467578705899 +B 7 1 12 7 0.00017199472709651755024748079335477916751696611176573 0 827.95539780849441501969512521811065413467578705899 +B 7 1 13 2 0.00092774090086837100143961085896710938763109803165767 0 7.3890560989306502272304274605750078131803155705521 +B 7 1 13 3 0.00029664527619289425143312342451091847137454713732079 0 67.645666868493653450016016424320161504144039086217 +B 7 1 13 4 0.00013343844009403046531264120084725981212884182322726 0 291.69672773748937390713238899664795040347997371773 +B 7 1 13 8 0.000012803778236124397710025037612867637440619683841237 0 5329.1876230833151607043916879954874936134626808356 +B 7 1 14 5 0.000039245798347136834284611121108008046565017674043525 0 1824.8348361840853802088198051292605523212191467039 +B 7 1 14 6 0.000030781853003855608906574845354496480067025201659022 0 1824.8348361840853802088198051292605523212191467039 +B 7 1 14 7 0.0000099426279560519408426445453264259299747797461603342 0 1824.8348361840853802088198051292605523212191467039 +B 7 7 7 7 0.68954197901667938713955770521886724298036327344736 0 2.7182818284590452353602874713526624977572470937000 +B 7 7 8 8 0.55635242527225039956455903995077735128241671254829 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 9 0.37281868237769086670711384620322931682531711293120 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 8 10 0.059201164646233794165204537882088972842153111052640 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 9 8 0.24813169206377035113709644883157463102090425719423 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 9 9 0.16627613413582775726761375018839454352405587019869 0 27.474593022118317968158957115156725710168223409107 +B 7 7 9 10 0.026403560923864889812528317733887478459078791408881 0 47.560129945305985709087486769738443607156131247662 +B 7 7 10 8 0.036420103000156672668386963461095459741375017068754 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 10 9 0.024405564163639211548823074189016408257922436723199 0 47.560129945305985709087486769738443607156131247662 +B 7 7 10 10 0.0038754437227266106515344258501351857762315642238764 0 142.32935382482556026905480728176275780392268396339 +B 7 7 11 11 0.00083688541784729475686398620334480423025855135802886 0 827.95539780849441501969512521811065413467578705899 +B 7 7 12 11 0.00061124096337695611396998344856223749595933714554222 0 827.95539780849441501969512521811065413467578705899 +B 7 7 13 8 0.0023344627867180529197129786240222173158877299207657 0 7.3890560989306502272304274605750078131803155705521 +B 7 7 13 9 0.0015643525590421957073083881244674573439718630667472 0 67.645666868493653450016016424320161504144039086217 +B 7 7 13 10 0.00024840893922475809052066632020911710546730912517882 0 291.69672773748937390713238899664795040347997371773 +B 7 7 14 11 0.000035334463985895616976753372586960629468757163313837 0 1824.8348361840853802088198051292605523212191467039 +B 7 7 14 12 0.0000099426279560519408426445453264259299747797461603342 0 6326.0670614589061258935163679066373918000060404804 +B 7 7 14 13 0.0000031137114829919287373767544777297058721818036781102 0 6326.0670614589061258935163679066373918000060404804 +B 7 11 7 11 0.40019998545714727499176271912299202968187524483182 0 2.7182818284590452353602874713526624977572470937000 +B 7 11 14 12 0.00030470992878236207190665963826553634701235852992334 0 7.3890560989306502272304274605750078131803155705521 +B 7 11 14 13 0.000091664499277045399825597260533892787948367038252813 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 8 2 0.33352968663649548420330067796563813145861911334906 0 2.7182818284590452353602874713526624977572470937000 +B 8 2 9 3 0.052354349161368488992002767955751240553736894571833 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 4 0.032311418013090749630245193978395410825351665934634 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 8 0.0065106504008123579839687852950163879578642823892360 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 9 9 0.0033774705587478570942425194897115961589178052857349 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 3 0.011595942054565049969683513284615571051053685392189 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 10 4 0.0071566419405916634683854492141921041818665621479545 0 27.474593022118317968158957115156725710168223409107 +B 8 2 10 8 0.0014420411292412560116223087826020559580867015878037 0 47.560129945305985709087486769738443607156131247662 +B 8 2 10 9 0.00074807448698338210031721521304437640864405104118133 0 67.645666868493653450016016424320161504144039086217 +B 8 2 11 7 0.0018069133167142912325911068055718139468870483049731 0 142.32935382482556026905480728176275780392268396339 +B 8 2 12 7 0.0012515531293972508202733028995893974971196520600904 0 142.32935382482556026905480728176275780392268396339 +B 8 2 13 3 0.00094973964134061604556137072720045093571997123400313 0 7.3890560989306502272304274605750078131803155705521 +B 8 2 13 4 0.00058614871632485768482012862810057073801484404078586 0 47.560129945305985709087486769738443607156131247662 +B 8 2 13 8 0.00011810714631372639613591295665347118012574235833821 0 916.11194614150801521802511629491778006719973113721 +B 8 2 13 9 0.000061269364025837379385148928442212296713195420570672 0 1065.4793200541718288561026980098029726667570208916 +B 8 2 14 5 0.00016824271780890682214914972453581982770567802422977 0 291.69672773748937390713238899664795040347997371773 +B 8 2 14 6 0.00014813694083258914421538036399675446525764100099876 0 291.69672773748937390713238899664795040347997371773 +B 8 2 14 7 0.000098634162379154108476132677158699796552005622224754 0 291.69672773748937390713238899664795040347997371773 +B 8 2 15 7 0.000041522103838156838319700384724240121265883654870932 0 1877.5245565970551988530060629834556047718706394216 +B 8 3 8 3 0.44000910365412140895297639118019878161627844461126 0 2.7182818284590452353602874713526624977572470937000 +B 8 3 9 4 0.13473243065302190276415298400610411088344094528869 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 8 0.0085028034488771702549402707201385211804077310476743 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 9 0.0053696236068126693652140049148337293814612539441729 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 9 10 0.0029004947807697725658115675845696486566959263934845 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 10 4 0.029841827541540346555804142962158640662229655440214 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 10 8 0.0018832822425245535313434037752761727743054160975665 0 27.474593022118317968158957115156725710168223409107 +B 8 3 10 9 0.0011893156002666796200383102057184932248627655509441 0 47.560129945305985709087486769738443607156131247662 +B 8 3 10 10 0.00064242932910323821530691496705218594749301984090933 0 67.645666868493653450016016424320161504144039086217 +B 8 3 11 7 0.0023733535205550949223380969457368015726029184529491 0 47.560129945305985709087486769738443607156131247662 +B 8 3 12 7 0.0016438962502186191321215233935202399469074838884532 0 47.560129945305985709087486769738443607156131247662 +B 8 3 13 4 0.0024441279934728097569220959798540882886162542128424 0 7.3890560989306502272304274605750078131803155705521 +B 8 3 13 8 0.00015424601064251436203586557718512564537639097912493 0 306.12380392425827750338559089034539301229489507924 +B 8 3 13 9 0.000097408228354625345285101548973866761963844041357395 0 400.89302780377785206335291140236970720906144779496 +B 8 3 13 10 0.000052616734176332317167655060939826033634846643852094 0 550.26040171644166570143049311725489980861873754931 +B 8 3 14 5 0.00091405195512569175400010143187090386527017922854326 0 67.645666868493653450016016424320161504144039086217 +B 8 3 14 6 0.00044717272154495822157732595713166073902721402295694 0 67.645666868493653450016016424320161504144039086217 +B 8 3 14 7 0.00012955449183098978447196243247690657763851833637476 0 67.645666868493653450016016424320161504144039086217 +B 8 3 15 7 0.000054538660163478071767915187710215837165608088910249 0 435.40564091373442340053464295064886771486427699503 +B 8 4 8 4 0.53313163396051065050581442520799591984641838654885 0 2.7182818284590452353602874713526624977572470937000 +B 8 4 9 8 0.015910189927005860653147582710930763354703713699740 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 9 9 0.012777010084941359763421316905625971555757236596239 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 9 10 0.010307881258898462964018879575361890830991909045550 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 10 8 0.0035239410560148777455375894912750946064146532276705 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 10 9 0.0028299744137570038342324959217174150569720026810484 0 27.474593022118317968158957115156725710168223409107 +B 8 4 10 10 0.0022830881425935624295011006830511077796022569710136 0 47.560129945305985709087486769738443607156131247662 +B 8 4 11 7 0.0037475944725223803154252840808537295704076910336608 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 11 11 0.00070193928078916640728398533543886607311190986850672 0 291.69672773748937390713238899664795040347997371773 +B 8 4 12 7 0.0025957601542979031676112800865202633778261080630372 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 12 11 0.00048619615307060535933072493961243439324462533424682 0 311.78226466067704164806091865122966830046788155628 +B 8 4 13 8 0.00028862049317739344638820504128724783036338317164083 0 47.560129945305985709087486769738443607156131247662 +B 8 4 13 9 0.00023178271088950442963744101307598894695083623387329 0 87.731203791681321190944546078901879401131946924772 +B 8 4 13 10 0.00018699121671121140151999452504194821862183883636799 0 182.50042767120089575091186659092619359789849964050 +B 8 4 14 5 0.0015051929123379525915026109917319282286611730683931 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 14 6 0.00087435417415189528485968440375870148443513916603119 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 14 7 0.00020457032349850149717600967724359731811142858383928 0 7.3890560989306502272304274605750078131803155705521 +B 8 4 14 11 0.000038316831450201074674824595541503709207894552531743 0 1195.7153718537351849702137517444393603453960249678 +B 8 4 15 7 0.000086118136045582969244971027108030028456711997620851 0 47.560129945305985709087486769738443607156131247662 +B 8 4 15 11 0.000016130267808313162185591557463705421771906237717428 0 1255.9719826232981881929993407081845140363597484835 +B 8 8 8 8 0.72494015135596950525531956328511473980731110149162 0 2.7182818284590452353602874713526624977572470937000 +B 8 8 9 9 0.21835953741084839296566285081984747314906645021461 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 9 10 0.043328960111262025446609723679634392300816004582454 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 10 9 0.048364359092180549471279788391992855037220254293054 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 10 10 0.0095969125542200054788950287457408960245694810249529 0 27.474593022118317968158957115156725710168223409107 +B 8 8 11 11 0.0026426204365972554901181626107207816966325525971943 0 142.32935382482556026905480728176275780392268396339 +B 8 8 12 11 0.0018304031779712577066687021265433002739510813371936 0 142.32935382482556026905480728176275780392268396339 +B 8 8 13 9 0.0039611744213393476323968326033109431228640351529375 0 7.3890560989306502272304274605750078131803155705521 +B 8 8 13 10 0.00078601361099713084814072598791633806949273194778788 0 47.560129945305985709087486769738443607156131247662 +B 8 8 14 11 0.00014425299256954846337470159562640123076731751414627 0 291.69672773748937390713238899664795040347997371773 +B 8 8 14 12 0.000069237160902036750670654350859710490294407266681752 0 1065.4793200541718288561026980098029726667570208916 +B 8 8 14 13 0.000038316831450201074674824595541503709207894552531743 0 1065.4793200541718288561026980098029726667570208916 +B 8 8 15 11 0.000060726300015739293110862199847495328962734580467344 0 291.69672773748937390713238899664795040347997371773 +B 8 9 8 9 0.59615318981492479144448693329764436490789899246774 0 2.7182818284590452353602874713526624977572470937000 +B 8 9 9 10 0.10183754156925940014757311989960938123989253137799 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 10 10 0.022555952846948235434949457027295201549828561694501 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 11 11 0.0089053606436339839053180878811278583065651654916248 0 47.560129945305985709087486769738443607156131247662 +B 8 9 12 11 0.0061682715373520917860001063028463517851922145187170 0 47.560129945305985709087486769738443607156131247662 +B 8 9 13 10 0.0018473947581104904836656069832713470119964435397671 0 7.3890560989306502272304274605750078131803155705521 +B 8 9 14 11 0.00048611783401229470261807409567353816112489070278692 0 67.645666868493653450016016424320161504144039086217 +B 8 9 14 12 0.00022289588601068141355586676228048103370003425648831 0 326.20934084744594524431412054492711090928280291780 +B 8 9 14 13 0.000087819609903636110414072282379558377913529931305753 0 326.20934084744594524431412054492711090928280291780 +B 8 9 15 11 0.00020464142133480847185116185804340814307365835351811 0 67.645666868493653450016016424320161504144039086217 +B 8 10 8 10 0.49517495528296729506776689882708446722628195872735 0 2.7182818284590452353602874713526624977572470937000 +B 8 10 11 11 0.018095874310201162112934121798802064745606443547744 0 7.3890560989306502272304274605750078131803155705521 +B 8 10 12 11 0.012534053467099811596491324016579303042948727059620 0 7.3890560989306502272304274605750078131803155705521 +B 8 10 14 11 0.00098780134530790459187735809410134130294469210937574 0 7.3890560989306502272304274605750078131803155705521 +B 8 10 14 12 0.00056061989270096752859499964747430055753676696630149 0 47.560129945305985709087486769738443607156131247662 +B 8 10 14 13 0.00014272966459483014518435016660718375593840315553926 0 47.560129945305985709087486769738443607156131247662 +B 8 10 15 11 0.00041583553853968040770315977947305923031598872492784 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 9 0 0.40099401689788665071116395948135930071360900942062 0 2.7182818284590452353602874713526624977572470937000 +B 9 0 10 2 0.044196273743080833351018922297101558642600235835478 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 10 3 0.010730397780876016217580287270657520582434977553333 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 10 4 0.0018694548579375367655125261196274534650251811849998 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 1 0.070971303999522001942063292717605425100198243448609 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 11 7 0.00055388394575362863177955497610409037405072060005037 0 67.645666868493653450016016424320161504144039086217 +B 9 0 12 1 0.041500562135029119871649163076637424817681124503301 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 12 7 0.00032388435622513543724042367894231734849468941051032 0 87.731203791681321190944546078901879401131946924772 +B 9 0 13 2 0.0053675458005133117659414524214259043331825206978165 0 47.560129945305985709087486769738443607156131247662 +B 9 0 13 3 0.0013031845598882727471522797308174915720895296294478 0 67.645666868493653450016016424320161504144039086217 +B 9 0 13 4 0.00022704141598685732405170901888936471746934832942367 0 87.731203791681321190944546078901879401131946924772 +B 9 0 14 1 0.0032250658398908381491572332920299495334578906156357 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 14 5 0.000032642896175444591976233399952154565865845019937629 0 659.45670178273014385765101552297665661420021162654 +B 9 0 14 6 0.000025169499389866201860335208889059851812319028032582 0 659.45670178273014385765101552297665661420021162654 +B 9 0 14 7 0.000025169499389866201860335208889059851812319028032582 0 659.45670178273014385765101552297665661420021162654 +B 9 0 15 1 0.0010797081370714666242825323031740306795857120984367 0 7.3890560989306502272304274605750078131803155705521 +B 9 0 15 7 0.0000084264057375565686722312745762433461235108247281393 0 4264.7205769289798339102812244624368979725937444500 +B 9 2 9 2 0.19222372601573275865940698807110492269156049553016 0 2.7182818284590452353602874713526624977572470937000 +B 9 2 10 3 0.023145761278850736503905392680436300922228973534959 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 10 4 0.014284818355912257051837631529406233804819177166626 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 10 8 0.0028783465435274970299978342578558201431005299737084 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 10 9 0.0014931735095812834832225908518443520978244588659409 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 11 7 0.0029999294726737520910103495415707424998321949276358 0 47.560129945305985709087486769738443607156131247662 +B 9 2 12 7 0.0017542126530779355701163563128172389565371946329042 0 47.560129945305985709087486769738443607156131247662 +B 9 2 13 3 0.0028110047121660047460244973645830743529404014772963 0 7.3890560989306502272304274605750078131803155705521 +B 9 2 13 4 0.0017348615682645893229239266526549474983202201772721 0 27.474593022118317968158957115156725710168223409107 +B 9 2 13 8 0.00034956921915960733449494566617413379209741489952951 0 306.12380392425827750338559089034539301229489507924 +B 9 2 13 9 0.00018134282648761724368462254049229968213418581843927 0 326.20934084744594524431412054492711090928280291780 +B 9 2 14 5 0.00023252826953615236261906447204240956998491936951225 0 142.32935382482556026905480728176275780392268396339 +B 9 2 14 6 0.00020474007407147199499524876638246717669996282885104 0 142.32935382482556026905480728176275780392268396339 +B 9 2 14 7 0.00013632228124858767235559803152882942495214519067746 0 142.32935382482556026905480728176275780392268396339 +B 9 2 15 7 0.000045638843867208349309987476688812223717445180521062 0 916.11194614150801521802511629491778006719973113721 +B 9 3 9 3 0.25359118775360926547965359737730080599404472639990 0 2.7182818284590452353602874713526624977572470937000 +B 9 3 10 4 0.059564959289289394665532986006343361291334417720385 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 8 0.0037590737346787304445304235947531048493389266871215 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 9 0.0023739007007325168977551801887416368040628555793541 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 10 10 0.0012823033971700478224907998950292636608662500427243 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 11 7 0.0039403623347765912891735173599502870985665908274725 0 27.474593022118317968158957115156725710168223409107 +B 9 3 12 7 0.0023041319898817937965699283077924559973911161463581 0 27.474593022118317968158957115156725710168223409107 +B 9 3 13 4 0.0072340407915277066439259945218208966366050863980428 0 7.3890560989306502272304274605750078131803155705521 +B 9 3 13 8 0.00045653171024522242008365046821358336474661069556682 0 176.84196693478213160623653883004191830972551316345 +B 9 3 13 9 0.00028830531757323232927332734253174925478338161447658 0 196.92750385796979934716506848462363620671342100200 +B 9 3 13 10 0.00015573308859644726699257248419639833570321779873213 0 217.01304078115746708809359813920535410370132884056 +B 9 3 14 5 0.0012633112574472558098043916385782892282935680216310 0 47.560129945305985709087486769738443607156131247662 +B 9 3 14 6 0.00061803744303942825696051016882064571499963419242107 0 47.560129945305985709087486769738443607156131247662 +B 9 3 14 7 0.00017905727028442482021121011931496124634415371514877 0 47.560129945305985709087486769738443607156131247662 +B 9 3 15 7 0.000059945936401237070798945385099786546484845934369755 0 306.12380392425827750338559089034539301229489507924 +B 9 4 9 4 0.30726065247809787959335783340760058454367026114298 0 2.7182818284590452353602874713526624977572470937000 +B 9 4 10 8 0.0070338656453661376703235644758902242789287432970114 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 10 9 0.0056486926114199241235483210698787562336526721892435 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 10 10 0.0045570953078574550482839407761663830904560666526135 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 11 7 0.0062219471215103105817265773369104984407035514960878 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 11 11 0.0011653953274836594965872536174707534054960891819467 0 67.645666868493653450016016424320161504144039086217 +B 9 4 12 7 0.0036382916554392648328285665234141342860119737648243 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 12 11 0.00068146643043833547045940683741104775050531571610881 0 87.731203791681321190944546078901879401131946924772 +B 9 4 13 8 0.00085424839717557760692591504321963612198710214280019 0 47.560129945305985709087486769738443607156131247662 +B 9 4 13 9 0.00068602200450358751611559191753780201202387306170997 0 67.645666868493653450016016424320161504144039086217 +B 9 4 13 10 0.00055344977552680245383483705920245109294370924596549 0 87.731203791681321190944546078901879401131946924772 +B 9 4 14 5 0.0020803272069199567507792391937991151279102373299070 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 14 6 0.0012084449521802031043328587891926648907464196000032 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 14 7 0.00028273665535756790059057475089163610543045388588861 0 7.3890560989306502272304274605750078131803155705521 +B 9 4 14 11 0.000052957694854546569484150914549002245097275568693800 0 659.45670178273014385765101552297665661420021162654 +B 9 4 15 7 0.000094656383030080963271942158270939372394321707712446 0 47.560129945305985709087486769738443607156131247662 +B 9 4 15 11 0.000017729515269969513831670325104385565521994413676370 0 719.71331255229314708043660448672181030516393514220 +B 9 8 9 8 0.41780597834437472110657941299643301910040619775072 0 2.7182818284590452353602874713526624977572470937000 +B 9 8 10 9 0.096536349068037284898161172505497816806512984225219 0 7.3890560989306502272304274605750078131803155705521 +B 9 8 10 10 0.019155653412957113485137806773369909716643273707499 0 7.3890560989306502272304274605750078131803155705521 +B 9 8 11 11 0.0043874129763202179873034814128105093463674457503987 0 47.560129945305985709087486769738443607156131247662 +B 9 8 12 11 0.0025655454327996647331716170480079430799800948480289 0 47.560129945305985709087486769738443607156131247662 +B 9 8 13 9 0.011724139416123332366956121026435092335576264720779 0 7.3890560989306502272304274605750078131803155705521 +B 9 8 13 10 0.0023264143857581045772685644096387357119549087253028 0 27.474593022118317968158957115156725710168223409107 +B 9 8 14 11 0.00019937206896352679771912763391180892557558426390495 0 142.32935382482556026905480728176275780392268396339 +B 9 8 14 12 0.000095692683890383717339763002335134066489284093165114 0 400.89302780377785206335291140236970720906144779496 +B 9 8 14 13 0.000052957694854546569484150914549002245097275568693800 0 400.89302780377785206335291140236970720906144779496 +B 9 8 15 11 0.000066747054432842127793625006686512714198870940867752 0 142.32935382482556026905480728176275780392268396339 +B 9 9 9 9 0.34358197190189797531666388517931740765268802473003 0 2.7182818284590452353602874713526624977572470937000 +B 9 9 10 10 0.045022189448329379680865758947889839102287540936441 0 7.3890560989306502272304274605750078131803155705521 +B 9 9 11 11 0.014785133084417200630279235396733510460112646687830 0 27.474593022118317968158957115156725710168223409107 +B 9 9 12 11 0.0086456257623315385119800058100598030387871091447065 0 27.474593022118317968158957115156725710168223409107 +B 9 9 13 10 0.0054678515502933790788930108534449321051653515777248 0 7.3890560989306502272304274605750078131803155705521 +B 9 9 14 11 0.00067186348512230981425641380251522426315178217663185 0 47.560129945305985709087486769738443607156131247662 +B 9 9 14 12 0.00030806441631346106048656411699201181127488066974354 0 196.92750385796979934716506848462363620671342100200 +B 9 9 14 13 0.00012137548767743089212380164940263999684509320686737 0 196.92750385796979934716506848462363620671342100200 +B 9 9 15 11 0.00022493074805328812100063743514813727228288458400344 0 47.560129945305985709087486769738443607156131247662 +B 9 10 9 10 0.28538501593084446450277179350152608152782771971219 0 2.7182818284590452353602874713526624977572470937000 +B 9 10 11 11 0.030043691733750178432085727351803934631990769011335 0 7.3890560989306502272304274605750078131803155705521 +B 9 10 12 11 0.017568087738257670126165545431145410789989282990055 0 7.3890560989306502272304274605750078131803155705521 +B 9 10 14 11 0.0013652402936739197122320284925296578489621030952592 0 7.3890560989306502272304274605750078131803155705521 +B 9 10 14 12 0.00077483278453314486485967987215763867321531768767707 0 47.560129945305985709087486769738443607156131247662 +B 9 10 14 13 0.00019726667728589360487935057531937246264643683694599 0 47.560129945305985709087486769738443607156131247662 +B 9 10 15 11 0.00045706386390780282192394244950737523707928196688978 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 10 0 0.31188260614570564732085256587660786038230182759465 0 2.7182818284590452353602874713526624977572470937000 +B 10 0 11 1 0.14773693726660891055490873661127128891783893368924 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 11 7 0.0011529887875152578913739065883218445584926576494450 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 12 1 0.082458461431047148668876117556095777700320924279953 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 12 7 0.00064353358899125490858108974892246288581424504521207 0 27.474593022118317968158957115156725710168223409107 +B 10 0 13 2 0.014821084138343820901324011602090655464765020249171 0 47.560129945305985709087486769738443607156131247662 +B 10 0 13 3 0.0035984058129597232896614158000617697975690125955604 0 47.560129945305985709087486769738443607156131247662 +B 10 0 13 4 0.00062691592289871638669099684916917071553298014059017 0 47.560129945305985709087486769738443607156131247662 +B 10 0 14 1 0.012507627242589561286534355091606987511063590738497 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 14 5 0.00012659747048600812196726475574311109771171517340175 0 435.40564091373442340053464295064886771486427699503 +B 10 0 14 6 0.000097613733139069044042290811060503040409627049447665 0 435.40564091373442340053464295064886771486427699503 +B 10 0 14 7 0.000097613733139069044042290811060503040409627049447665 0 435.40564091373442340053464295064886771486427699503 +B 10 0 15 1 0.0026955857182042156913094810687028043423159459242534 0 7.3890560989306502272304274605750078131803155705521 +B 10 0 15 7 0.000021037258294227419690599221542011842343742735141496 0 2822.6016612456590584578098044296301609155873820236 +B 10 2 10 2 0.14950656146096919927673197257763425614855850565328 0 2.7182818284590452353602874713526624977572470937000 +B 10 2 11 7 0.0062447829944288223423075097138146324723674290306505 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 12 7 0.0034854871585844928820352241217835537237120370437011 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 13 3 0.0077618596842357743526649602877476436182033573219771 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 13 4 0.0047903697941747674496945413368550445361673248670068 0 7.3890560989306502272304274605750078131803155705521 +B 10 2 13 8 0.00096524463915039527549262405758917770723233158062118 0 47.560129945305985709087486769738443607156131247662 +B 10 2 13 9 0.00050073113283933757133128362827319787518128464795605 0 47.560129945305985709087486769738443607156131247662 +B 10 2 14 5 0.00090180388962881769942076667275648813462316380553340 0 47.560129945305985709087486769738443607156131247662 +B 10 2 14 6 0.00079403418573086434801721370387383798352094968003416 0 47.560129945305985709087486769738443607156131247662 +B 10 2 14 7 0.00052869254873088244965650268659110364481848355144463 0 47.560129945305985709087486769738443607156131247662 +B 10 2 15 7 0.00011394136202166630376281252325845774098642649547574 0 306.12380392425827750338559089034539301229489507924 +B 10 3 10 3 0.19723656014628541839014486561157562599216621900887 0 2.7182818284590452353602874713526624977572470937000 +B 10 3 11 7 0.0082024287318226339850531105673194893394595484588754 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 12 7 0.0045781350672197707587568398537489584227329661488041 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 13 4 0.019974925453116787171909785875415139266442848720485 0 7.3890560989306502272304274605750078131803155705521 +B 10 3 13 8 0.0012605937873356135352531508826951540434337027932709 0 47.560129945305985709087486769738443607156131247662 +B 10 3 13 9 0.00079608028102455583109181045337917421138265586060567 0 47.560129945305985709087486769738443607156131247662 +B 10 3 13 10 0.00043001649077523754685064563243185315806539933215710 0 47.560129945305985709087486769738443607156131247662 +B 10 3 14 5 0.0048994430141780311135019975687294999889118527551382 0 27.474593022118317968158957115156725710168223409107 +B 10 3 14 6 0.0023969067123795518760415088444806694306124390247486 0 27.474593022118317968158957115156725710168223409107 +B 10 3 14 7 0.00069442972732271395691000354483896991052487392485200 0 27.474593022118317968158957115156725710168223409107 +B 10 3 15 7 0.00014966026880730746240573179301597194008655535960577 0 176.84196693478213160623653883004191830972551316345 +B 10 4 10 4 0.23897925909777878165508431466072031957756621327436 0 2.7182818284590452353602874713526624977572470937000 +B 10 4 11 7 0.012951874345903488170980613656746447309022606548619 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 11 11 0.0024259373392436490041073073696950415369613504947464 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 12 7 0.0072290088786945245405319331572349832658575175945520 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 12 11 0.0013540219813895644019446233421377355952886930448343 0 27.474593022118317968158957115156725710168223409107 +B 10 4 13 8 0.0023587851580835683879766453455371172033854723786167 0 47.560129945305985709087486769738443607156131247662 +B 10 4 13 9 0.0018942716517725106838153049162211373713344254459515 0 47.560129945305985709087486769738443607156131247662 +B 10 4 13 10 0.0015282078615231923995741400952738163180171689175029 0 47.560129945305985709087486769738443607156131247662 +B 10 4 14 5 0.0080680390846386641313292710954127727476866049502963 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 14 6 0.0046866574995475293020612199223567690573752165878684 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 14 7 0.0010965248055675882845992414756869624576316423028949 0 7.3890560989306502272304274605750078131803155705521 +B 10 4 14 11 0.00020538343703702239544584377994315319151184117494690 0 435.40564091373442340053464295064886771486427699503 +B 10 4 15 7 0.00023631793210785562766551212256821647650951015791679 0 47.560129945305985709087486769738443607156131247662 +B 10 4 15 11 0.000044263284226087140708652546971123317004413675225058 0 495.66225168329742662332023191439402140582800051069 +B 10 8 10 8 0.32495850785345369780230687544447892466865477815188 0 2.7182818284590452353602874713526624977572470937000 +B 10 8 11 11 0.0091330286907183148327804113126268563736165280127148 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 12 11 0.0050975437014995960604413323775891651374341735956857 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 13 9 0.032373167029039524812271070834444374491132034418215 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 13 10 0.0064237893132978800310600142248543322452860267008167 0 7.3890560989306502272304274605750078131803155705521 +B 10 8 14 11 0.00077321569387372823038858256903901200432499992639719 0 47.560129945305985709087486769738443607156131247662 +B 10 8 14 12 0.00037112061562885390269934463819101945721823154835430 0 87.731203791681321190944546078901879401131946924772 +B 10 8 14 13 0.00020538343703702239544584377994315319151184117494690 0 87.731203791681321190944546078901879401131946924772 +B 10 8 15 11 0.00016663985431227646461135214628088205252749733766610 0 47.560129945305985709087486769738443607156131247662 +B 10 9 10 9 0.26722902663341284079148538634742550790132238215227 0 2.7182818284590452353602874713526624977572470937000 +B 10 9 11 11 0.030777372767248568515647816985291284428914029148030 0 7.3890560989306502272304274605750078131803155705521 +B 10 9 12 11 0.017178201012095339989125516024789694823726894402294 0 7.3890560989306502272304274605750078131803155705521 +B 10 9 13 10 0.015098052423720699990866089883504012139522981809635 0 7.3890560989306502272304274605750078131803155705521 +B 10 9 14 11 0.0026056578212683580642354361180944723400446249472943 0 27.474593022118317968158957115156725710168223409107 +B 10 9 14 12 0.0011947523173930362300232874890255458452938220162128 0 67.645666868493653450016016424320161504144039086217 +B 10 9 14 13 0.00047072507403700429380655479722588753021430730353644 0 67.645666868493653450016016424320161504144039086217 +B 10 9 15 11 0.00056155926886128617259717104935725294794000534772547 0 27.474593022118317968158957115156725710168223409107 +B 10 10 10 10 0.22196496399623608785690697081324594486002884090247 0 2.7182818284590452353602874713526624977572470937000 +B 10 10 11 11 0.062540248675102276891404416164840778809764393626240 0 7.3890560989306502272304274605750078131803155705521 +B 10 10 12 11 0.034906454531121400091228586479073474771561075064961 0 7.3890560989306502272304274605750078131803155705521 +B 10 10 14 11 0.0052947497935158049265780568354259951432463788047284 0 7.3890560989306502272304274605750078131803155705521 +B 10 10 14 12 0.0030049990063478275005583457575498955164836012416087 0 47.560129945305985709087486769738443607156131247662 +B 10 10 14 13 0.00076505044838392527009223975919122992621886155608010 0 47.560129945305985709087486769738443607156131247662 +B 10 10 15 11 0.0011410998783419912414761033402669908227926912659782 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 11 1 0.40561902779981853591404808214248760641272811491075 0 2.7182818284590452353602874713526624977572470937000 +B 11 1 12 7 0.0020165571788247983687472550565692032523274985207841 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 2 0.046692958719483115385859921097319801636382145337951 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 3 0.014930079748170669588759613993208623015497338785062 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 4 0.0067159220522355683581007622594130771361785399355674 0 7.3890560989306502272304274605750078131803155705521 +B 11 1 13 8 0.00064441083504369070817134582763555070746812133665726 0 27.474593022118317968158957115156725710168223409107 +B 11 1 14 5 0.0012783187524225116414875527638746255231810774983151 0 67.645666868493653450016016424320161504144039086217 +B 11 1 14 6 0.0010026301307745607529981619420933149889826696426240 0 67.645666868493653450016016424320161504144039086217 +B 11 1 14 7 0.00032385244535377752461851822521119667953258482646080 0 67.645666868493653450016016424320161504144039086217 +B 11 1 15 7 0.000058022763122000469846422857700853882408096955101372 0 435.40564091373442340053464295064886771486427699503 +B 11 7 11 7 0.39584253379378534169860849238246846642287220108128 0 2.7182818284590452353602874713526624977572470937000 +B 11 7 12 11 0.0071665124477792409045437818153892773071425615019806 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 8 0.11749290608010073593539880739815311292955441530774 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 9 0.078733458224925908909556020228708663647257793701758 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 13 10 0.012502357429661163933180628660098496377290743957909 0 7.3890560989306502272304274605750078131803155705521 +B 11 7 14 11 0.0011509183102976301900866906905551282821278083935342 0 67.645666868493653450016016424320161504144039086217 +B 11 7 14 12 0.00032385244535377752461851822521119667953258482646080 0 87.731203791681321190944546078901879401131946924772 +B 11 7 14 13 0.00010142017606917336898572510251847673981487670009537 0 87.731203791681321190944546078901879401131946924772 +B 11 7 15 11 0.00020620335417946996936385183474621992056028718278167 0 67.645666868493653450016016424320161504144039086217 +B 11 11 11 11 0.22974116310293746552213979649478445418086079675164 0 2.7182818284590452353602874713526624977572470937000 +B 11 11 14 12 0.0099250475825837929863720990349484436614148803909880 0 7.3890560989306502272304274605750078131803155705521 +B 11 11 14 13 0.0029857068346735637052804953936510793228522243617877 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 12 1 0.40561902779981853591404808214248760641272811491075 0 2.7182818284590452353602874713526624977572470937000 +B 12 1 13 2 0.076291868584756324572722708315779059283537896630374 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 3 0.024394335106292822673880985601792706287452698181951 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 4 0.010973180040116653575691281418536675121998528880588 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 13 8 0.0010529062216233567514162441800775364497944265915341 0 7.3890560989306502272304274605750078131803155705521 +B 12 1 14 5 0.0027300710665574422017669663826059080284596006791229 0 67.645666868493653450016016424320161504144039086217 +B 12 1 14 6 0.0021412902731020977466554678096063081149628399891404 0 67.645666868493653450016016424320161504144039086217 +B 12 1 14 7 0.00069164297967053209846350325409578661792286517219645 0 67.645666868493653450016016424320161504144039086217 +B 12 1 15 7 0.00021998119927978268084012592469903414500852934375401 0 435.40564091373442340053464295064886771486427699503 +B 12 7 12 7 0.39584253379378534169860849238246846642287220108128 0 2.7182818284590452353602874713526624977572470937000 +B 12 7 13 8 0.19197227154003296814331062390580009225894206579271 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 13 9 0.12864300770071104622887243239280763776512947124004 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 13 10 0.020427666958388027575676416063623217630347703398067 0 7.3890560989306502272304274605750078131803155705521 +B 12 7 14 11 0.0024579853600365654637979989730945863584131472421439 0 67.645666868493653450016016424320161504144039086217 +B 12 7 14 12 0.00069164297967053209846350325409578661792286517219645 0 67.645666868493653450016016424320161504144039086217 +B 12 7 14 13 0.00021660034926883052274970650886336925274740429269121 0 67.645666868493653450016016424320161504144039086217 +B 12 7 15 11 0.00078177699073959011725034398518982161752443072119443 0 67.645666868493653450016016424320161504144039086217 +B 12 11 12 11 0.22974116310293746552213979649478445418086079675164 0 2.7182818284590452353602874713526624977572470937000 +B 12 11 14 12 0.021196657866489675354840617862119781852143896349397 0 7.3890560989306502272304274605750078131803155705521 +B 12 11 14 13 0.0063764939903431518118336596483706900577614646294952 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 13 0 0.36333881723544573891822679728824402769985389053699 0 2.7182818284590452353602874713526624977572470937000 +B 13 0 14 1 0.14278037453292134968285488853574870789925192015487 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 14 5 0.0014451689277534259768641679574926871563503425313696 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 14 6 0.0011143061035345782007150192002710906365746028527197 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 14 7 0.0011143061035345782007150192002710906365746028527197 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 15 1 0.058070905519148303127348058822842619311746629767135 0 7.3890560989306502272304274605750078131803155705521 +B 13 0 15 7 0.00045320489366587745531414534669112124182862304754624 0 67.645666868493653450016016424320161504144039086217 +B 13 2 13 2 0.17417302581083675408094641181321041824613885128500 0 2.7182818284590452353602874713526624977572470937000 +B 13 2 14 5 0.010294510271141530521769538012066846562988787268805 0 7.3890560989306502272304274605750078131803155705521 +B 13 2 14 6 0.0090642690441359495376004359712869779422430792971315 0 7.3890560989306502272304274605750078131803155705521 +B 13 2 14 7 0.0060352710115569021373914273633905651195574347394120 0 7.3890560989306502272304274605750078131803155705521 +B 13 2 15 7 0.0024546346361751949703495337655519953890432362212988 0 47.560129945305985709087486769738443607156131247662 +B 13 3 13 3 0.22977779801435696515859797638391136249480767661303 0 2.7182818284590452353602874713526624977572470937000 +B 13 3 14 5 0.055929417706424365258624154858698773792117809494034 0 7.3890560989306502272304274605750078131803155705521 +B 13 3 14 6 0.027361803440119993544076570240609277087949757543020 0 7.3890560989306502272304274605750078131803155705521 +B 13 3 14 7 0.0079272378870001786738588269186136267798546220683850 0 7.3890560989306502272304274605750078131803155705521 +B 13 3 15 7 0.0032241257516638439478503120840522196089872627986236 0 47.560129945305985709087486769738443607156131247662 +B 13 4 13 4 0.27840745085932923188495968687039395549698852328802 0 2.7182818284590452353602874713526624977572470937000 +B 13 4 14 5 0.092100413604303795374999781652015258105398175564230 0 7.3890560989306502272304274605750078131803155705521 +B 13 4 14 6 0.053500372222028133608102402454273642904187238843824 0 7.3890560989306502272304274605750078131803155705521 +B 13 4 14 7 0.012517339970803654834386086324511504743061714276427 0 7.3890560989306502272304274605750078131803155705521 +B 13 4 14 11 0.0023445473305401591848841212410509592573203108243928 0 7.3890560989306502272304274605750078131803155705521 +B 13 4 15 7 0.0050909886542425016635147348960205909146316893304328 0 47.560129945305985709087486769738443607156131247662 +B 13 4 15 11 0.00095356232929320683407299245140633977863227634098441 0 67.645666868493653450016016424320161504144039086217 +B 13 8 13 8 0.37857205745840499111874998510496395433005431458402 0 2.7182818284590452353602874713526624977572470937000 +B 13 8 14 11 0.0088266162897869118818787802021718988808245903614079 0 7.3890560989306502272304274605750078131803155705521 +B 13 8 14 12 0.0042365142059834357213515207962740209176174981533655 0 7.3890560989306502272304274605750078131803155705521 +B 13 8 14 13 0.0023445473305401591848841212410509592573203108243928 0 7.3890560989306502272304274605750078131803155705521 +B 13 8 15 11 0.0035899163473605135272381935818749353042207294501185 0 7.3890560989306502272304274605750078131803155705521 +B 13 9 13 9 0.31131802978009890949201659224631200150633502695395 0 2.7182818284590452353602874713526624977572470937000 +B 13 9 14 11 0.029744794309068810550895507172629800047864345390010 0 7.3890560989306502272304274605750078131803155705521 +B 13 9 14 12 0.013638652643131960539219469538617552728669748858604 0 7.3890560989306502272304274605750078131803155705521 +B 13 9 14 13 0.0053735453631192065850931298489473720800059553821123 0 7.3890560989306502272304274605750078131803155705521 +B 13 9 15 11 0.012097650994816266080375348622583987710095296989981 0 7.3890560989306502272304274605750078131803155705521 +B 13 10 13 10 0.25858603813392296556047804987951506793024587815134 0 2.7182818284590452353602874713526624977572470937000 +B 13 10 14 11 0.060442028205165496457632147115057437473748073605714 0 7.3890560989306502272304274605750078131803155705521 +B 13 10 14 12 0.034303459423257356393606314901393071657510592304914 0 7.3890560989306502272304274605750078131803155705521 +B 13 10 14 13 0.0087334062199171017614512872140653814224673396184813 0 7.3890560989306502272304274605750078131803155705521 +B 13 10 15 11 0.024582673359485884024971059997311072313163830520651 0 7.3890560989306502272304274605750078131803155705521 +B 14 1 14 1 0.38037225616899763871656351439795089497826574933344 0 2.7182818284590452353602874713526624977572470937000 +B 14 1 15 7 0.0011200624131107872249118326211091770195499726383276 0 7.3890560989306502272304274605750078131803155705521 +B 14 5 14 5 0.29960666449637113690625069425374499410432324687905 0 2.7182818284590452353602874713526624977572470937000 +B 14 5 15 7 0.017244884258469241222658331248636546673504287909130 0 7.3890560989306502272304274605750078131803155705521 +B 14 5 15 11 0.0037240859197825360776159502981817551003842038212243 0 7.3890560989306502272304274605750078131803155705521 +B 14 6 14 6 0.33931271465166019241380311727206097477858031336633 0 2.7182818284590452353602874713526624977572470937000 +B 14 6 15 7 0.078697968262303632765470506399414962214934781245356 0 7.3890560989306502272304274605750078131803155705521 +B 14 6 15 11 0.0037240859197825360776159502981817551003842038212243 0 7.3890560989306502272304274605750078131803155705521 +B 14 7 14 7 0.37120427629717370709861336808831902383752710538402 0 2.7182818284590452353602874713526624977572470937000 +B 14 7 15 11 0.0039805175425405104528010226157937084031816657914871 0 7.3890560989306502272304274605750078131803155705521 +B 14 11 14 11 0.21544148216705797488627929130064909863894582927089 0 2.7182818284590452353602874713526624977572470937000 +B 14 12 14 12 0.26628676803961128630522014456217781655100899821660 0 2.7182818284590452353602874713526624977572470937000 +B 14 13 14 13 0.23151165742683605894591096585462673444471851421142 0 2.7182818284590452353602874713526624977572470937000 +B 15 1 15 1 0.38336020234057910393636554995973418131956513636132 0 2.7182818284590452353602874713526624977572470937000 +B 15 7 15 7 0.37412020504394335461855748901301133339428570942499 0 2.7182818284590452353602874713526624977572470937000 +B 15 11 15 11 0.21713384416612788656112751926238441256696153803466 0 2.7182818284590452353602874713526624977572470937000 +P 0 13 0 0E+0 +P 0 12 0 0E+0 +P 0 11 0 0E+0 +P 0 10 1 0E+0 +P 0 9 1 0E+0 +P 0 8 1 0E+0 +P 0 7 0 0E+0 +P 0 6 0 0E+0 +P 0 5 0 0E+0 +P 0 4 1 0E+0 +P 0 3 1 0E+0 +P 0 2 1 0E+0 +P 0 1 0 0E+0 +P 0 0 1 0E+0 +P 1 13 0 0E+0 +P 1 12 0 0E+0 +P 1 11 1 0E+0 +P 1 10 0 0E+0 +P 1 9 0 0E+0 +P 1 8 0 0E+0 +P 1 7 1 0E+0 +P 1 6 0 0E+0 +P 1 5 0 0E+0 +P 1 4 0 0E+0 +P 1 3 0 0E+0 +P 1 2 0 0E+0 +P 1 1 1 0E+0 +P 1 0 0 0E+0 +P 2 13 0 0E+0 +P 2 12 0 0E+0 +P 2 11 0 0E+0 +P 2 10 1 0E+0 +P 2 9 1 0E+0 +P 2 8 1 0E+0 +P 2 7 0 0E+0 +P 2 6 0 0E+0 +P 2 5 0 0E+0 +P 2 4 1 0E+0 +P 2 3 1 0E+0 +P 2 2 1 0E+0 +P 2 1 0 0E+0 +P 2 0 1 0E+0 +P 3 13 0 0E+0 +P 3 12 0 0E+0 +P 3 11 0 0E+0 +P 3 10 1 0E+0 +P 3 9 1 0E+0 +P 3 8 1 0E+0 +P 3 7 0 0E+0 +P 3 6 0 0E+0 +P 3 5 0 0E+0 +P 3 4 1 2.5396911773440219645312380965548667753274842116074E-1 +P 3 3 1 7.1463386312237848126675767208515554805496384689134E-2 +P 3 2 1 0E+0 +P 3 1 0 0E+0 +P 3 0 1 0E+0 +P 4 13 1 0E+0 +P 4 12 1 0E+0 +P 4 11 1 0E+0 +P 4 10 0 0E+0 +P 4 9 0 0E+0 +P 4 8 0 0E+0 +P 4 7 1 0E+0 +P 4 6 1 0E+0 +P 4 5 1 2.9707171888506824920562278705167207908788723360437E-1 +P 4 4 0 0E+0 +P 4 3 0 0E+0 +P 4 2 0 0E+0 +P 4 1 1 0E+0 +P 4 0 0 0E+0 +P 5 13 1 0E+0 +P 5 12 1 0E+0 +P 5 11 1 0E+0 +P 5 10 0 0E+0 +P 5 9 0 0E+0 +P 5 8 0 0E+0 +P 5 7 1 0E+0 +P 5 6 1 0E+0 +P 5 5 1 1.9702791131816109477277482821408645804312801544386E+0 +P 5 4 0 0E+0 +P 5 3 0 0E+0 +P 5 2 0 0E+0 +P 5 1 1 0E+0 +P 5 0 0 0E+0 +P 6 13 1 0E+0 +P 6 12 1 0E+0 +P 6 11 1 0E+0 +P 6 10 0 0E+0 +P 6 9 0 0E+0 +P 6 8 0 0E+0 +P 6 7 1 0E+0 +P 6 6 1 2.4872742576227010784009272144678128689224276522040E+0 +P 6 5 1 0E+0 +P 6 4 0 0E+0 +P 6 3 0 0E+0 +P 6 2 0 0E+0 +P 6 1 1 0E+0 +P 6 0 0 0E+0 +P 7 13 0 0E+0 +P 7 12 0 0E+0 +P 7 11 1 0E+0 +P 7 10 0 0E+0 +P 7 9 0 0E+0 +P 7 8 0 0E+0 +P 7 7 1 2.4872742576227010784009272144678128689224276522040E+0 +P 7 6 0 0E+0 +P 7 5 0 0E+0 +P 7 4 0 0E+0 +P 7 3 0 0E+0 +P 7 2 0 0E+0 +P 7 1 1 0E+0 +P 7 0 0 0E+0 +P 8 13 0 0E+0 +P 8 12 0 0E+0 +P 8 11 0 0E+0 +P 8 10 1 0E+0 +P 8 9 1 0E+0 +P 8 8 1 2.4872742576227010784009272144678128689224276522040E+0 +P 8 7 0 0E+0 +P 8 6 0 0E+0 +P 8 5 0 0E+0 +P 8 4 1 0E+0 +P 8 3 1 0E+0 +P 8 2 1 0E+0 +P 8 1 0 0E+0 +P 8 0 0 0E+0 +P 9 13 0 0E+0 +P 9 12 0 0E+0 +P 9 11 0 0E+0 +P 9 10 1 0E+0 +P 9 9 1 2.4872742576227010784009272144678128689224276522040E+0 +P 9 8 1 0E+0 +P 9 7 0 0E+0 +P 9 6 0 0E+0 +P 9 5 0 0E+0 +P 9 4 1 0E+0 +P 9 3 1 0E+0 +P 9 2 1 0E+0 +P 9 1 0 0E+0 +P 9 0 1 0E+0 +P 10 13 0 0E+0 +P 10 12 0 0E+0 +P 10 11 0 0E+0 +P 10 10 1 2.4872742576227010784009272144678128689224276522040E+0 +P 10 9 1 0E+0 +P 10 8 1 0E+0 +P 10 7 0 0E+0 +P 10 6 0 0E+0 +P 10 5 0 0E+0 +P 10 4 1 0E+0 +P 10 3 1 0E+0 +P 10 2 1 0E+0 +P 10 1 0 0E+0 +P 10 0 1 0E+0 +P 11 13 0 0E+0 +P 11 12 0 0E+0 +P 11 11 1 9.5955277161525224246628799736669231389316863623286E-1 +P 11 10 0 0E+0 +P 11 9 0 0E+0 +P 11 8 0 0E+0 +P 11 7 1 0E+0 +P 11 6 0 0E+0 +P 11 5 0 0E+0 +P 11 4 0 0E+0 +P 11 3 0 0E+0 +P 11 2 0 0E+0 +P 11 1 1 0E+0 +P 11 0 0 0E+0 +P 12 13 0 0E+0 +P 12 12 0 0E+0 +P 12 11 1 2.2081984456373349562239041230741088069525147735921E-1 +P 12 10 0 0E+0 +P 12 9 0 0E+0 +P 12 8 0 0E+0 +P 12 7 1 0E+0 +P 12 6 0 0E+0 +P 12 5 0 0E+0 +P 12 4 0 0E+0 +P 12 3 0 0E+0 +P 12 2 0 0E+0 +P 12 1 1 0E+0 +P 12 0 0 0E+0 +P 13 13 0 0E+0 +P 13 12 0 0E+0 +P 13 11 0 0E+0 +P 13 10 1 0E+0 +P 13 9 1 0E+0 +P 13 8 1 0E+0 +P 13 7 0 0E+0 +P 13 6 0 0E+0 +P 13 5 0 0E+0 +P 13 4 1 0E+0 +P 13 3 1 0E+0 +P 13 2 1 0E+0 +P 13 1 0 0E+0 +P 13 0 1 0E+0 +P 14 13 1 0E+0 +P 14 12 1 0E+0 +P 14 11 1 0E+0 +P 14 10 0 0E+0 +P 14 9 0 0E+0 +P 14 8 0 0E+0 +P 14 7 1 0E+0 +P 14 6 1 0E+0 +P 14 5 1 0E+0 +P 14 4 0 0E+0 +P 14 3 0 0E+0 +P 14 2 0 0E+0 +P 14 1 1 0E+0 +P 14 0 0 0E+0 +P 15 13 0 0E+0 +P 15 12 0 0E+0 +P 15 11 1 0E+0 +P 15 10 0 0E+0 +P 15 9 0 0E+0 +P 15 8 0 0E+0 +P 15 7 1 0E+0 +P 15 6 0 0E+0 +P 15 5 0 0E+0 +P 15 4 0 0E+0 +P 15 3 0 0E+0 +P 15 2 0 0E+0 +P 15 1 1 0E+0 +P 15 0 0 0E+0 +END diff --git a/tests/tools/export-seeded-theory.py b/tests/tools/export-seeded-theory.py new file mode 100644 index 00000000..960d8507 --- /dev/null +++ b/tests/tools/export-seeded-theory.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Extract numerical gold data from IntaRNA-probabilities tests/examples/*.json. + +Source revision: f62778504c8abbec8fc9894da16a357b60707680. Run with the path +of that checkout as argument; redirect stdout to tests/data/seeded-theory.dat. +Values preserve unrounded accessibility; they are NOT native ED gold values. +""" +import json +from decimal import Decimal, getcontext +from pathlib import Path +import sys +getcontext().prec = 50 +e = Decimal(1).exp() +def poly(cs): + return sum(Decimal(c) * e**i for i, c in enumerate(cs)) +for path in sorted((Path(sys.argv[1]) / 'tests/examples').glob('*.json')): + d = json.loads(path.read_text()) + p = d['input'] + n, m = len(p['rna1']), len(p['rna2']) + def pair(v): + return f'{v[0]-1} {m-v[1]}' # one-based antiparallel -> internal + print('CASE', p['id'], n, m, p['max_interaction_length']) + print('Z', d['ensembles']['seed']['partition_value_at_e']) + for s in d['seeds']['occurrences']: + print('S', pair(s['left_pair']), len(s['pairs'])) + denominator = Decimal(d['common_denominator']['value_at_e']) + for b in d['helpers']['boundaries']: + print('B', pair(b['left_pair']), pair(b['right_pair']), + poly(b['accessibility_product_coefficients'])/denominator, + poly(b['hybrid']['seed']), poly(b['hybrid']['noseed'])) + for a in d['actual_pairs']: + print('P', pair(a['pair']), int(a['allowed']), a['partition_value_at_e']) + print('END') From 62afc24b5a2644f8a406f605ca2f0de05c09b0f6 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 13:52:37 +0200 Subject: [PATCH 03/30] feat: compile active stack-seed domains (step 2) --- ChangeLog | 8 ++++ src/IntaRNA/Makefile.am | 6 ++- src/IntaRNA/SeedHandler.h | 13 +++++++ src/IntaRNA/SeedHandlerExplicit.cpp | 10 +++++ src/IntaRNA/SeedHandlerExplicit.h | 3 ++ src/IntaRNA/SeedHandlerIdxOffset.h | 13 +++++++ src/IntaRNA/SeedHandlerMfe.h | 13 +++++++ src/IntaRNA/SeedHandlerNoBulge.h | 13 +++++++ src/IntaRNA/StackSeedDomain.cpp | 46 +++++++++++++++++++++++ src/IntaRNA/StackSeedDomain.h | 58 +++++++++++++++++++++++++++++ tests/Makefile.am | 1 + tests/StackSeedDomain_test.cpp | 50 +++++++++++++++++++++++++ 12 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 src/IntaRNA/StackSeedDomain.cpp create mode 100644 src/IntaRNA/StackSeedDomain.h create mode 100644 tests/StackSeedDomain_test.cpp diff --git a/ChangeLog b/ChangeLog index 606c5d2b..555e85fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- expose geometric stack-seed capability and compile contained occurrences plus + deduplicated eligible-start boxes (issue #257, implementation step 2) + - establish independent weighted-chain fixtures for seeded pair probabilities (issue #257, implementation step 1) @@ -136,6 +139,11 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * SeedHandler*, StackSeedDomain, tests/StackSeedDomain_test.cpp, Makefile.am : + + preserve each handler's admission rules, normalized allowances and retained + explicit patterns; filter complete endpoints and span limits (PR #258, step 2) + 261008 Alexander Mitrofanov * tests/SeededChainOracle.h, tests/SeededPartitionFunction_test.cpp, tests/Makefile.am : + independently enumerate weighted chains, raw boundaries and pair masses; diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index 5f27f746..d6e0f473 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -27,6 +27,7 @@ libIntaRNA_adir = $(includedir)/IntaRNA libIntaRNA_a_HEADERS = \ intarna_config.h \ Matrix.h \ + StackSeedDomain.h \ Accessibility.h \ AccessibilityArchive.h \ AccessibilityConstraint.h \ @@ -97,7 +98,8 @@ libIntaRNA_a_HEADERS = \ # the sources to add to the library and to add to the source distribution -libIntaRNA_a_SOURCES = \ +libIntaRNA_a_SOURCES = \ + StackSeedDomain.cpp \ $(libmylib_a_HEADERS) \ Accessibility.cpp \ AccessibilityConstraint.cpp \ @@ -158,7 +160,7 @@ libIntaRNA_a_SOURCES = \ # generated intaRNA sources -nodist_libIntaRNA_a_SOURCES = \ +nodist_libIntaRNA_a_SOURCES = \ ../config.h \ intarna_config.h diff --git a/src/IntaRNA/SeedHandler.h b/src/IntaRNA/SeedHandler.h index 55253d5c..27952062 100644 --- a/src/IntaRNA/SeedHandler.h +++ b/src/IntaRNA/SeedHandler.h @@ -18,6 +18,13 @@ class SeedHandler public: + /** Whether every exposed seed is a consecutive diagonal pair chain. + * This geometric guarantee does not imply existence or a common length. + * @return false for unknown handlers; offsets preserve this capability + */ + virtual bool guaranteesStackOnlySeeds() const; + + /** * Construction * @param energy the energy function to be used for seed prediction @@ -251,6 +258,12 @@ getInteractionEnergy() const ////////////////////////////////////////////////////////////////////////// +inline bool +SeedHandler::guaranteesStackOnlySeeds() const +{ + return false; +} + } // namespace #endif /* SEEDHANDLER_H_ */ diff --git a/src/IntaRNA/SeedHandlerExplicit.cpp b/src/IntaRNA/SeedHandlerExplicit.cpp index 11d0a439..5c38fde3 100644 --- a/src/IntaRNA/SeedHandlerExplicit.cpp +++ b/src/IntaRNA/SeedHandlerExplicit.cpp @@ -447,4 +447,14 @@ updateToNextSeed( size_t & i1_out, size_t & i2_out ////////////////////////////////////////////////////////////////////////// +bool +SeedHandlerExplicit::guaranteesStackOnlySeeds() const +{ + for (const auto & entry : seedForLeftEnd) { + if (entry.second.dotBar1.find('.') != std::string::npos + || entry.second.dotBar2.find('.') != std::string::npos) return false; + } + return true; +} + } /* namespace IntaRNA */ diff --git a/src/IntaRNA/SeedHandlerExplicit.h b/src/IntaRNA/SeedHandlerExplicit.h index ff1bb4f6..33d6087a 100644 --- a/src/IntaRNA/SeedHandlerExplicit.h +++ b/src/IntaRNA/SeedHandlerExplicit.h @@ -20,6 +20,9 @@ class SeedHandlerExplicit : public SeedHandler { public: + /** @return true if all retained accepted patterns contain only paired positions. */ + bool guaranteesStackOnlySeeds() const override; + /** * Construction * @param energy the energy function to be used for seed prediction diff --git a/src/IntaRNA/SeedHandlerIdxOffset.h b/src/IntaRNA/SeedHandlerIdxOffset.h index e798a5ff..11b30f96 100644 --- a/src/IntaRNA/SeedHandlerIdxOffset.h +++ b/src/IntaRNA/SeedHandlerIdxOffset.h @@ -24,6 +24,13 @@ class SeedHandlerIdxOffset : public SeedHandler public: + /** Whether every exposed seed is a consecutive diagonal pair chain. + * This geometric guarantee does not imply existence or a common length. + * @return false for unknown handlers; offsets preserve this capability + */ + virtual bool guaranteesStackOnlySeeds() const override; + + /** * Construction * @param seedHandler the seed handler to be wrapped @@ -516,6 +523,12 @@ isFeasibleSeedBasePair( const size_t i1 ////////////////////////////////////////////////////////////////////////// +inline bool +SeedHandlerIdxOffset::guaranteesStackOnlySeeds() const +{ + return seedHandlerOriginal->guaranteesStackOnlySeeds(); +} + } // namespace #endif /* SEEDHANDLERIDXOFFSET_H_ */ diff --git a/src/IntaRNA/SeedHandlerMfe.h b/src/IntaRNA/SeedHandlerMfe.h index 30fdeef8..94979fdf 100644 --- a/src/IntaRNA/SeedHandlerMfe.h +++ b/src/IntaRNA/SeedHandlerMfe.h @@ -21,6 +21,13 @@ class SeedHandlerMfe : public SeedHandler { public: + /** Whether every exposed seed is a consecutive diagonal pair chain. + * This geometric guarantee does not imply existence or a common length. + * @return false for unknown handlers; offsets preserve this capability + */ + virtual bool guaranteesStackOnlySeeds() const override; + + //! 5D matrix type to hold the mfe energies for seed interactions //! of the ranges i1..(i1+bp+u1-1) with i2..(i2+bp+u2-1), with //! i1,i2 = the start index of the seed in seq1/2 @@ -403,6 +410,12 @@ decodeSeedLength2( const size_t code ) const ////////////////////////////////////////////////////////////////////////// +inline bool +SeedHandlerMfe::guaranteesStackOnlySeeds() const +{ + return getConstraint().getMaxUnpaired1() == 0 && getConstraint().getMaxUnpaired2() == 0; +} + } // namespace #endif /* SEEDHANDLERMFE_H_ */ diff --git a/src/IntaRNA/SeedHandlerNoBulge.h b/src/IntaRNA/SeedHandlerNoBulge.h index 25ac247a..927643c9 100644 --- a/src/IntaRNA/SeedHandlerNoBulge.h +++ b/src/IntaRNA/SeedHandlerNoBulge.h @@ -34,6 +34,13 @@ class SeedHandlerNoBulge : public SeedHandler public: + /** Whether every exposed seed is a consecutive diagonal pair chain. + * This geometric guarantee does not imply existence or a common length. + * @return false for unknown handlers; offsets preserve this capability + */ + virtual bool guaranteesStackOnlySeeds() const override; + + /** * Construction * @param energy the energy function to be used for seed prediction @@ -314,6 +321,12 @@ storeSeed( const size_t j1, const size_t j2, const StackingEnergyList & bpE ) ////////////////////////////////////////////////////////////////////////// +inline bool +SeedHandlerNoBulge::guaranteesStackOnlySeeds() const +{ + return true; +} + } // namespace #endif /* SEEDHANDLERNOBULGE_H_ */ diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp new file mode 100644 index 00000000..1e35fc19 --- /dev/null +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -0,0 +1,46 @@ +#include "IntaRNA/StackSeedDomain.h" +#include +#include + +namespace IntaRNA { +StackSeedDomain::StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t w1, size_t w2) { + if (!handler.guaranteesStackOnlySeeds()) throw std::invalid_argument("stack seed domain requires a stack-only handler"); + if (n==0 || m==0) return; + // Use the range end as an invalid initial cursor, avoiding lastPos+offset wrap. + size_t i=n, j=m; + while (handler.updateToNextSeed(i,j,0,n-1,0,m-1)) { + const size_t len=handler.getSeedLength1(i,j); + if (len != handler.getSeedLength2(i,j)) throw std::logic_error("stack seed has unequal strand lengths"); + add({i,j,len},n,m,w1,w2); + } +} +StackSeedDomain::StackSeedDomain(const std::vector & seeds, size_t n, size_t m, size_t w1, size_t w2) { + for (auto s:seeds) add(s,n,m,w1,w2); +} +void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2) { + w1=std::min(w1,n); w2=std::min(w2,m); + if (s.length==0 || s.i>=n || s.j>=m || s.length>n-s.i || s.length>m-s.j + || s.length>w1 || s.length>w2) return; + auto [p,inserted]=lengths.emplace(std::make_pair(s.i,s.j),s.length); + if (!inserted && p->second!=s.length) throw std::invalid_argument("multiple retained seed lengths at one start"); + maximum=std::max(maximum,s.length); + const size_t end1=s.i+s.length, end2=s.j+s.length; + boxes.push_back({end1>w1?end1-w1:0,s.i,end2>w2?end2-w2:0,s.j}); +} +void StackSeedDomain::forEachStart(const std::function & visit) const { + if (boxes.empty()) return; + size_t first=boxes.front().i0, last=0; + for (auto b:boxes) { first=std::min(first,b.i0); last=std::max(last,b.i1); } + std::vector> intervals; + for (size_t i=first;i<=last;++i) { + intervals.clear(); + for (auto b:boxes) if (b.i0<=i && i<=b.i1) intervals.emplace_back(b.j0,b.j1); + std::sort(intervals.begin(),intervals.end()); + size_t next=0; + for (auto [lo,hi]:intervals) { + for (size_t j=std::max(lo,next);j<=hi;++j) visit(i,j); + next=std::max(next,hi+1); + } + } +} +} diff --git a/src/IntaRNA/StackSeedDomain.h b/src/IntaRNA/StackSeedDomain.h new file mode 100644 index 00000000..ebd88fc8 --- /dev/null +++ b/src/IntaRNA/StackSeedDomain.h @@ -0,0 +1,58 @@ +#ifndef INTARNA_STACKSEEDDOMAIN_H_ +#define INTARNA_STACKSEEDDOMAIN_H_ + +#include "IntaRNA/SeedHandler.h" +#include +#include +#include +#include + +namespace IntaRNA { +/** Admitted stack occurrences and the deduplicated union of eligible-start boxes. + * Coordinates are local, zero-based and increasing on both strands. Seed energies + * have already been filtered by the handler and are never reused as path weights. + */ +class StackSeedDomain { +public: + /** A consecutive diagonal run, including singleton explicit seeds. */ + struct Occurrence { size_t i, j, length; }; + /** Compile a filled handler, requiring both endpoints inside the active range. + * @param handler filled stack-only handler, including any index offset + * @param n first range length + * @param m second range length + * @param span1 inclusive first-strand interaction span limit + * @param span2 inclusive second-strand interaction span limit + */ + StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t span1, size_t span2); + /** Compile already admitted occurrences (also useful for independent weight models). + * @param occurrences retained occurrences; out-of-range/overlong ones are excluded + * @param n first range length + * @param m second range length + * @param span1 inclusive first span limit + * @param span2 inclusive second span limit + */ + StackSeedDomain(const std::vector & occurrences, size_t n, size_t m, size_t span1, size_t span2); + /** @return retained length at this start, or zero if no occurrence starts here */ + size_t seedLength(size_t i, size_t j) const; + /** @return maximum retained occurrence length, zero for an empty family */ + size_t maxSeedLength() const; + /** Enumerate each eligible left boundary once, in row-major order. + * The caller intersects these boxes with accessible complementary vertices. + * @param visit callback receiving local first/second coordinates + */ + void forEachStart(const std::function & visit) const; +private: + //! admitted occurrences keyed by local start + std::map, size_t> lengths; + //! eligible inclusive rectangles [i0,i1] x [j0,j1] + struct Box { size_t i0, i1, j0, j1; }; + std::vector boxes; + size_t maximum = 0; + void add(Occurrence seed, size_t n, size_t m, size_t span1, size_t span2); +}; +inline size_t StackSeedDomain::seedLength(size_t i, size_t j) const { + auto p=lengths.find({i,j}); return p==lengths.end()?0:p->second; +} +inline size_t StackSeedDomain::maxSeedLength() const { return maximum; } +} +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 90b8432d..20e8044e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,6 +26,7 @@ runApiTests_SOURCES = \ SeededChainOracle.h \ SeededTheoryFixtures.h \ SeededPartitionFunction_test.cpp \ + StackSeedDomain_test.cpp \ testEasyLoggingSetup.icc \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp new file mode 100644 index 00000000..068c638d --- /dev/null +++ b/tests/StackSeedDomain_test.cpp @@ -0,0 +1,50 @@ +#include "catch.hpp" +#include "IntaRNA/StackSeedDomain.h" +#include "IntaRNA/SeedHandlerExplicit.h" +#include "IntaRNA/SeedHandlerNoBulge.h" +#include "IntaRNA/SeedHandlerMfe.h" +#include "IntaRNA/SeedHandlerIdxOffset.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include +using namespace IntaRNA; + +TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGGGGG"), q("q","CCCCCC"); + AccessibilityDisabled at(t,0,nullptr), aq(q,0,nullptr); + ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar); + SeedConstraint computed(2,0,4,3,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + SeedHandlerMfe mfe(energy,computed); + SeedHandlerNoBulge stack(energy,computed); + REQUIRE(mfe.guaranteesStackOnlySeeds()); + REQUIRE(stack.guaranteesStackOnlySeeds()); + SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + SeedHandlerMfe bulge(energy,bulged); + REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); + SeedConstraint explicitC(1,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1|&6|,2|||&3|||,5||&1||",false,false,false); + SeedHandlerIdxOffset explicitH(new SeedHandlerExplicit(energy,explicitC)); + REQUIRE(explicitH.guaranteesStackOnlySeeds()); + explicitH.setOffset1(1); explicitH.setOffset2(1); + explicitH.fillSeed(0,3,0,3); + StackSeedDomain domain(explicitH,4,4,4,4); + REQUIRE(domain.seedLength(0,0)==3); + REQUIRE(domain.seedLength(3,3)==0); // end outside the active range + REQUIRE(domain.maxSeedLength()==3); + StackSeedDomain mixed({{0,0,1},{2,2,2},{4,4,3}},6,6,4,3); + REQUIRE(mixed.maxSeedLength()==2); + std::set> starts; + size_t calls=0; + mixed.forEachStart([&](size_t i,size_t j){ ++calls; starts.emplace(i,j); }); + REQUIRE(calls==starts.size()); + for (size_t i=0;i<6;++i) for(size_t j=0;j<6;++j) { + const bool expected=(i==0 && j==0) || (i<=2 && j>=1 && j<=2); + REQUIRE((starts.count({i,j})!=0)==expected); + } + SeedConstraint explicitB(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1|.|&4|.|",false,false,false); + SeedHandlerExplicit eb(energy,explicitB); + REQUIRE_FALSE(eb.guaranteesStackOnlySeeds()); +} From 5c8907cb4b0bde9ae185f031810f6c52a02d7780 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 14:02:16 +0200 Subject: [PATCH 04/30] feat: validate and select seeded inside-outside kernel (step 3) --- ChangeLog | 9 ++ doc/Makefile.am | 1 + doc/analysis/seeded-kernel-comparison.md | 63 ++++++++++++ src/IntaRNA/Makefile.am | 3 + src/IntaRNA/PartitionArithmetic.h | 53 +++++++++++ src/IntaRNA/SeededPartitionFunction.cpp | 97 +++++++++++++++++++ src/IntaRNA/SeededPartitionFunction.h | 44 +++++++++ src/IntaRNA/StackSeedDomain.cpp | 2 + src/IntaRNA/StackSeedDomain.h | 7 ++ tests/Makefile.am | 1 + tests/SeededPartitionFunction_test.cpp | 107 ++++++++++++++++++++- tests/SeededStackKernel.h | 116 +++++++++++++++++++++++ 12 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 doc/analysis/seeded-kernel-comparison.md create mode 100644 src/IntaRNA/PartitionArithmetic.h create mode 100644 src/IntaRNA/SeededPartitionFunction.cpp create mode 100644 src/IntaRNA/SeededPartitionFunction.h create mode 100644 tests/SeededStackKernel.h diff --git a/ChangeLog b/ChangeLog index 555e85fa..6bee2563 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- select the checked suffix inside/outside kernel after independent correctness + and maximal-stack timing comparisons (issue #257, implementation step 3) + - expose geometric stack-seed capability and compile contained occurrences plus deduplicated eligible-start boxes (issue #257, implementation step 2) @@ -139,6 +142,12 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * SeededPartitionFunction, PartitionArithmetic, StackSeedDomain, tests, doc/analysis : + + add checked nonnegative kernels and raw pair differentiation; compare both + against exhaustive and high-precision gold values and record time/storage + tradeoffs before selecting the suffix backend (PR #258, step 3) + 261008 Alexander Mitrofanov * SeedHandler*, StackSeedDomain, tests/StackSeedDomain_test.cpp, Makefile.am : + preserve each handler's admission rules, normalized allowances and retained diff --git a/doc/Makefile.am b/doc/Makefile.am index 5e3ac756..4092731b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -6,6 +6,7 @@ EXTRA_DIST = \ conda.txt \ analysis/base-pair-probabilities-plan.md \ + analysis/seeded-kernel-comparison.md \ api-documentation.md \ build-api.sh \ check-api.py \ diff --git a/doc/analysis/seeded-kernel-comparison.md b/doc/analysis/seeded-kernel-comparison.md new file mode 100644 index 00000000..05cc407f --- /dev/null +++ b/doc/analysis/seeded-kernel-comparison.md @@ -0,0 +1,63 @@ +# Seeded partition kernel comparison (PR #258, step 3) + +Production uses the specialized stack-seed suffix automaton. Both candidates +passed independent exhaustive comparisons of raw Z, complete boundary partitions +and every pair numerator: all ten unrounded theory fixtures and 100 deterministic +heterogeneous-weight cases with missing edges, forbidden boundaries, mixed and +singleton occurrences, asymmetric spans and noLP. The tolerance is 2e-12 relative; +the oracle accumulates in long double. Mathematical fixture accessibility remains +unrounded, independently of native integer ED tests. + +## Reproduce + +GCC 14.4.0, Linux x86-64, release Autotools configuration, Z_type=double, +`-O3 -DNDEBUG`, Kokkos mdspan. The hidden Catch case contains every input and +uses deterministic weights and identical callback/checked-arithmetic contracts. + +```sh +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 /usr/bin/time -v \ + ./tests/runApiTests '[SeededBenchmark]' +``` + +Single-run milliseconds on the development host (these are kernel timings, +not native full-program speedups; rerun to account for shared-host noise): + +| Case | Stack Z | Suffix Z | Stack Z+outside | Suffix Z+outside | +| --- | ---: | ---: | ---: | ---: | +| none | 0.000 | 0.000 | 0.009 | 0.000 | +| sparse | 59.462 | 43.934 | 156.972 | 140.072 | +| overlap | 109.494 | 77.083 | 304.623 | 249.862 | +| dense | 207.217 | 96.059 | 554.614 | 302.839 | +| long-stack | 1.003 | 0.879 | 3.057 | 1.466 | +| mixed | 196.146 | 99.959 | 522.997 | 310.235 | +| asymmetric | 65.849 | 37.285 | 182.743 | 121.412 | + +The outside increment is the difference between each combined and partition-only +time. All corresponding Z values agree within 2e-12; pair masses were checked +before timing. The numerical kernels do not include native seed preprocessing. +Ordinary cases use 45 x 45 positions, spans 30 x 30, seed length four and loop +limits two. The long-stack case uses a 90-position diagonal (L up to 30); mixed +lengths are one through four; asymmetric spans are 12 x 24. No-seed input exits +before DP allocation. Sparse input has one seed. These are synthetic stress cases; +end-to-end native measurements belong to the final integration gate. + +## Memory and decision + +For a per-start dense rectangle of A cells and c=max(2, maximum seed length), +the suffix implementation stores (c+4)A partition values, or 2(c+4)A with the +outside pass, plus A bytes of validity and A size_t ending lengths. For c=4 and +A=900 this is 65,700 bytes partition-only and 123,300 with outside (eight-byte +Z_type/size_t). Maximal stacks use 4A or 8A partition values, A validity bytes +and up to 3L scratch partition values for the outside pass. The output matrix +adds n*m*sizeof(Z_type) for either kernel. Occurrences/eligible-start boxes are +separate O(number of retained seeds) metadata; neither candidate retains run +lists or boundary coefficients globally. These are physical rectangular-cell +counts, not just complementary vertices. Allocator overhead is not included. + +The suffix candidate generally improves throughput, especially partition-only +and long-stack work. The extra bounded-span memory is acceptable for the intended +workload, so only suffix states ship in the library. Maximal stacks remain in a +test-only header for independent comparisons. This is not a claim of universal +speedup or lower memory use. Final integration also accounts for the global pair +matrix, private regional result, and legacy boundary-map storage when trackers +are enabled. diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index d6e0f473..4e37bc30 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -28,6 +28,8 @@ libIntaRNA_a_HEADERS = \ intarna_config.h \ Matrix.h \ StackSeedDomain.h \ + PartitionArithmetic.h \ + SeededPartitionFunction.h \ Accessibility.h \ AccessibilityArchive.h \ AccessibilityConstraint.h \ @@ -100,6 +102,7 @@ libIntaRNA_a_HEADERS = \ # the sources to add to the library and to add to the source distribution libIntaRNA_a_SOURCES = \ StackSeedDomain.cpp \ + SeededPartitionFunction.cpp \ $(libmylib_a_HEADERS) \ Accessibility.cpp \ AccessibilityConstraint.cpp \ diff --git a/src/IntaRNA/PartitionArithmetic.h b/src/IntaRNA/PartitionArithmetic.h new file mode 100644 index 00000000..37f28a89 --- /dev/null +++ b/src/IntaRNA/PartitionArithmetic.h @@ -0,0 +1,53 @@ +#ifndef INTARNA_PARTITIONARITHMETIC_H_ +#define INTARNA_PARTITIONARITHMETIC_H_ +#include "IntaRNA/general.h" +#include + +namespace IntaRNA { +/** Checked nonnegative arithmetic for exact seeded partitions only. + * Structural zero is exactly zero. Positive subnormals are rejected because + * their relative precision is not guaranteed. Ordinary sum rounding remains. + */ +class PartitionArithmetic { +public: + /** @return x; throws on negative, NaN, infinite or positive subnormal values */ + static Z_type check(const Z_type x); + /** @return checked sum of two nonnegative values */ + static Z_type add(const Z_type a, const Z_type b); + /** @return checked product; positive underflow is an error */ + static Z_type multiply(const Z_type a, const Z_type b); + /** @return checked quotient; denominator must be positive */ + static Z_type divide(const Z_type a, const Z_type b); + /** @return checked exponential; a finite exponent must yield a positive normal value */ + static Z_type exp(const Z_type exponent); +}; +inline Z_type PartitionArithmetic::check(const Z_type x) { + if (!(x>=0 && x<=std::numeric_limits::max()) + || (x>0 && x::min())) + throw std::range_error("exact seeded partition: numerical range exceeded"); + return x; +} +inline Z_type PartitionArithmetic::add(const Z_type a,const Z_type b) { + return check(check(a)+check(b)); +} +inline Z_type PartitionArithmetic::multiply(const Z_type a,const Z_type b) { + check(a); check(b); + if (a==0 || b==0) return 0; + const Z_type r=check(a*b); + if (r==0) throw std::range_error("exact seeded partition: product underflow"); + return r; +} +inline Z_type PartitionArithmetic::divide(const Z_type a,const Z_type b) { + check(a); check(b); + if (b==0) throw std::range_error("exact seeded partition: zero denominator"); + const Z_type r=check(a/b); + if (a>0 && r==0) throw std::range_error("exact seeded partition: quotient underflow"); + return r; +} +inline Z_type PartitionArithmetic::exp(const Z_type exponent) { + const Z_type r=check(Z_exp(exponent)); + if (r==0) throw std::range_error("exact seeded partition: exponential underflow"); + return r; +} +} +#endif diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp new file mode 100644 index 00000000..2caa278c --- /dev/null +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -0,0 +1,97 @@ +#include "IntaRNA/SeededPartitionFunction.h" +#include "IntaRNA/PartitionArithmetic.h" + +// Seed-free suffix lengths plus two absorbing seeded states (singleton/run). +// Nonstack edges operate on aggregate totals, independently of seed length. +namespace IntaRNA { +SeededPartitionFunction::Result SeededPartitionFunction::compute( + const IntaRNA::SeededPartitionFunction::Domain & d, + const IntaRNA::StackSeedDomain & seeds, + const IntaRNA::SeededPartitionFunction::Weights & w, bool pairs) +{ + using namespace IntaRNA; + using A=PartitionArithmetic; + using Pair=SeededPartitionFunction::Pair; + SeededPartitionFunction::Result out; + if (pairs) out.mass=Matrix(d.n,d.m,0); + if (!seeds.maxSeedLength() || !d.span1 || !d.span2) return out; + // At least two length states are necessary for noLP even with singleton seeds. + const size_t cap=std::max(size_t(2),seeds.maxSeedLength()), s1=cap, s2=cap+1, states=cap+2; + seeds.forEachStart([&](size_t p1,size_t p2) { + const Pair p{p1,p2}; if (!w.valid(p)) return; + const size_t n=std::min(d.span1,d.n-p1),m=std::min(d.span2,d.m-p2); + Matrix h(n*m,states),dh; + Matrix> total(n,m),dt; + Matrix valid(n,m); + Matrix ending(n,m); + if (pairs) { dh.resize(n*m,states); dt.resize(n,m); } + for (size_t i=0;i=cap) return stacked || !d.noLP?s2:s1; + const size_t len=stacked?std::min(cap,c+2):1; + return accepts(i,j,len)?(len>=2 || !d.noLP?s2:s1):len-1; + }; + auto nonstack=[&](size_t i,size_t j,const auto & visit) { + for(size_t u=1;u<=i && u-1<=d.loop1;++u) for(size_t v=1;v<=j && v-1<=d.loop2;++v) + if((u!=1 || v!=1) && valid(i-u,j-v)) visit(i-u,j-v); + }; + const size_t initial=accepts(0,0,1)?(d.noLP?s1:s2):0; + const Z_type init=A::check(w.initiation(p)); h(0,initial)=init; + for(size_t i=0;i0) { + const Z_type b=A::check(w.boundary(p,{p1+i,p2+j})); + out.z=A::add(out.z,A::multiply(b,total(i,j)[1])); + if(pairs) { dh(q,s2)=b;if(!d.noLP) dh(q,s1)=b; } + if(w.complete) w.complete(p,{p1+i,p2+j},total(i,j)[1],b); + } + } + if(!pairs) return; + for(size_t ii=n;ii>0;--ii) for(size_t jj=m;jj>0;--jj) { + const size_t i=ii-1,j=jj-1,q=i*m+j; if(!valid(i,j)) continue; + for(size_t c=d.noLP?1:0;c +#include + +namespace IntaRNA { +/** Nonnegative stack-seed suffix inside/outside computation. A path is counted once + * if it contains any admitted seed. Native energy ownership is supplied by the + * adapter: initiation once, one transition per successive pair, one boundary + * factor. All callbacks must be deterministic; zero denotes forbidden weight. + */ +class SeededPartitionFunction { +public: + //! Local zero-based pair, second strand in reversed coordinates. + using Pair = std::array; + /** Active rectangle, inclusive spans, loop limits and structural restriction. */ + struct Domain { size_t n,m,span1,span2,loop1,loop2; bool noLP; }; + /** Energy adapter. Complete boundary callbacks are made once per positive + * seeded hybrid partition and receive the SAME coefficient used outside. + * No callback may alter the forward weights after observing them. + */ + struct Weights { + std::function valid; + std::function initiation; + std::function transition, boundary; + std::function complete; + }; + /** Raw unnormalized result; pair masses remain empty in partition-only mode. */ + struct Result { Z_type z=0; Matrix mass; }; + /** Compute the union of seeded paths, optionally differentiating every pair. + * @param domain active dimensions and structural constraints + * @param seeds active admitted seed occurrences + * @param weights weight/admission contract (no integer energy reconstruction) + * @param pairs request the outside pass and raw local pair masses + * @return successful raw result, including exact zero for an empty ensemble + * @throws std::range_error on numerical failure; no partial result is returned + */ + static Result compute(const Domain & domain, const StackSeedDomain & seeds, + const Weights & weights, bool pairs); +}; +} +#endif diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp index 1e35fc19..baf99c5a 100644 --- a/src/IntaRNA/StackSeedDomain.cpp +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -23,6 +23,8 @@ void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2 || s.length>w1 || s.length>w2) return; auto [p,inserted]=lengths.emplace(std::make_pair(s.i,s.j),s.length); if (!inserted && p->second!=s.length) throw std::invalid_argument("multiple retained seed lengths at one start"); + auto [end, fresh]=endingLengths.emplace(std::make_pair(s.i+s.length-1,s.j+s.length-1),s.length); + if (!fresh) end->second=std::min(end->second,s.length); maximum=std::max(maximum,s.length); const size_t end1=s.i+s.length, end2=s.j+s.length; boxes.push_back({end1>w1?end1-w1:0,s.i,end2>w2?end2-w2:0,s.j}); diff --git a/src/IntaRNA/StackSeedDomain.h b/src/IntaRNA/StackSeedDomain.h index ebd88fc8..a6c82f64 100644 --- a/src/IntaRNA/StackSeedDomain.h +++ b/src/IntaRNA/StackSeedDomain.h @@ -34,6 +34,8 @@ class StackSeedDomain { StackSeedDomain(const std::vector & occurrences, size_t n, size_t m, size_t span1, size_t span2); /** @return retained length at this start, or zero if no occurrence starts here */ size_t seedLength(size_t i, size_t j) const; + /** @return shortest admitted occurrence ending here, or zero */ + size_t seedEndingLength(size_t i, size_t j) const; /** @return maximum retained occurrence length, zero for an empty family */ size_t maxSeedLength() const; /** Enumerate each eligible left boundary once, in row-major order. @@ -44,6 +46,8 @@ class StackSeedDomain { private: //! admitted occurrences keyed by local start std::map, size_t> lengths; + //! shortest retained length per ending vertex for suffix acceptance + std::map, size_t> endingLengths; //! eligible inclusive rectangles [i0,i1] x [j0,j1] struct Box { size_t i0, i1, j0, j1; }; std::vector boxes; @@ -53,6 +57,9 @@ class StackSeedDomain { inline size_t StackSeedDomain::seedLength(size_t i, size_t j) const { auto p=lengths.find({i,j}); return p==lengths.end()?0:p->second; } +inline size_t StackSeedDomain::seedEndingLength(size_t i, size_t j) const { + auto p=endingLengths.find({i,j}); return p==endingLengths.end()?0:p->second; +} inline size_t StackSeedDomain::maxSeedLength() const { return maximum; } } #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 20e8044e..ebbc88ca 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,6 +25,7 @@ runApiTests_SOURCES = \ catch.hpp \ SeededChainOracle.h \ SeededTheoryFixtures.h \ + SeededStackKernel.h \ SeededPartitionFunction_test.cpp \ StackSeedDomain_test.cpp \ testEasyLoggingSetup.icc \ diff --git a/tests/SeededPartitionFunction_test.cpp b/tests/SeededPartitionFunction_test.cpp index 12338b6c..1eca2608 100644 --- a/tests/SeededPartitionFunction_test.cpp +++ b/tests/SeededPartitionFunction_test.cpp @@ -27,7 +27,7 @@ TEST_CASE("unrounded fixed-seed theory gold agrees with exhaustive chains", "[Se auto fixtures=theoryFixtures(); REQUIRE(fixtures.size()==10); for (const auto & f:fixtures) { - INFO(f.name); + CAPTURE(f.name); const auto got=enumerate(f.model); REQUIRE(double(got.z)==Approx(double(f.expected.z)).epsilon(2e-12)); for (const auto & [pair, mass]:f.expected.mass) { @@ -39,3 +39,108 @@ TEST_CASE("unrounded fixed-seed theory gold agrees with exhaustive chains", "[Se } } } + +#include "IntaRNA/SeededPartitionFunction.h" +#include "IntaRNA/PartitionArithmetic.h" +#include "SeededStackKernel.h" +#include +#include +#include + +namespace { +using Kernel=IntaRNA::SeededPartitionFunction; +using IntaRNA::Z_type; +IntaRNA::StackSeedDomain domainFor(const seeded_oracle::Model & m) { + std::vector seeds; + for(const auto & s:m.seeds) seeds.push_back({s[0][0],s[0][1],s.size()}); + return {seeds,m.n,m.m,m.span1,m.span2}; +} +Kernel::Weights weightsFor(const seeded_oracle::Model & m) { + return {m.valid,[&](auto p){return Z_type(m.init(p));}, + [&](auto p,auto q){return Z_type(m.edge(p,q));}, + [&](auto p,auto q){return Z_type(m.boundary(p,q));},{}}; +} +void compareKernels(const seeded_oracle::Model & m) { + const auto oracle=seeded_oracle::enumerate(m); + const auto seeds=domainFor(m); + Kernel::Domain d{m.n,m.m,m.span1,m.span2,m.n,m.m,m.noLP}; + for(bool suffix:{false,true}) { + CAPTURE(suffix); + auto w=weightsFor(m); + std::map boundaries; + w.complete=[&](auto p,auto q,auto h,auto b){boundaries[{p[0],p[1],q[0],q[1]}]+=h*b;}; + auto got=suffix?Kernel::compute(d,seeds,w,true):seededStack(d,seeds,w,true); + REQUIRE(double(got.z)==Approx(double(oracle.z)).epsilon(2e-12)); + for(auto [b,z]:oracle.boundaries) REQUIRE(double(boundaries[b])==Approx(double(z)).epsilon(2e-12)); + Z_type expectedCount=0; + for(size_t i=0;isecond)).epsilon(2e-12)); + expectedCount+=got.mass(i,j); + } + REQUIRE(double(expectedCount)==Approx(double(oracle.pairCount)).epsilon(2e-12)); + auto only=suffix?Kernel::compute(d,seeds,w,false):seededStack(d,seeds,w,false); + REQUIRE(only.z==got.z); + REQUIRE(only.mass.storageSize()==0); + } +} +} +TEST_CASE("both numerical kernels match all theory fixtures", "[SeededPartitionFunction]") { + for(const auto & f:seeded_oracle::theoryFixtures()) { CAPTURE(f.name);compareKernels(f.model); } +} +TEST_CASE("heterogeneous weighted kernels match independent enumeration", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + std::mt19937 rng(257); + for(size_t trial=0;trial<100;++trial) { + CAPTURE(trial); + Model m{6,5,2+rng()%5,2+rng()%4,bool(trial%2),{}, + [trial](Pair p){return (p[0]+3*p[1]+trial)%7!=0;}, + [](Pair p){return .7L+.13L*p[0];}, + [trial](Pair p,Pair q){return (p[0]+q[1]+trial)%11==0?0.L:.1L+.17L*(q[0]-p[0])+.21L*(q[1]-p[1]);}, + [trial](Pair p,Pair q){return (q[0]+p[1]+trial)%5==0?0.L:.2L+.07L*(q[0]+p[1]);}}; + for(size_t i=0;i::quiet_NaN()),std::range_error); + REQUIRE_THROWS_AS(A::add(std::numeric_limits::max(),std::numeric_limits::max()),std::range_error); +} +TEST_CASE("numerical seeded kernel comparison timing", "[.][SeededBenchmark]") { + using namespace seeded_oracle; + for (std::string kind:{"none","sparse","overlap","dense","long-stack","mixed","asymmetric"}) { + const size_t n=kind=="long-stack"?90:45; + Model m{n,n,kind=="asymmetric"?12u:30u,kind=="asymmetric"?24u:30u,false,{}, + [kind](Pair p){return kind=="long-stack"?p[0]==p[1]:(7*p[0]+11*p[1])%17!=0;}, + [](Pair){return .8L;},[](Pair p,Pair q){return q[0]-p[0]<=3 && q[1]-p[1]<=3?.7L:0.L;}, + [](Pair,Pair){return .6L;}}; + if(kind!="none") for(size_t i=0;i+4(std::chrono::steady_clock::now()-start).count(); + if(!suffix && !outside) reference=r.z; else REQUIRE(double(r.z)==Approx(double(reference)).epsilon(2e-12)); + std::cout< +#include + +inline IntaRNA::SeededPartitionFunction::Result seededStack(const IntaRNA::SeededPartitionFunction::Domain & d, const IntaRNA::StackSeedDomain & seeds, + const IntaRNA::SeededPartitionFunction::Weights & w, bool pairs) +{ + using namespace IntaRNA; + using Pair=SeededPartitionFunction::Pair; + using Result=SeededPartitionFunction::Result; + using A=PartitionArithmetic; + using State=std::array; + Result result; + if (pairs) result.mass=Matrix(d.n,d.m,0); + if (!seeds.maxSeedLength() || !d.span1 || !d.span2) return result; + seeds.forEachStart([&](size_t p1,size_t p2) { + const Pair p{p1,p2}; + if (!w.valid(p)) return; + const size_t n=std::min(d.span1,d.n-p1), m=std::min(d.span2,d.m-p2); + Matrix h(n,m), entry(n,m), dh, de; + Matrix valid(n,m); + if (pairs) { dh.resize(n,m); de.resize(n,m); } + for (size_t i=0;i0 && length<=k+1; + if (d.noLP && k==0) continue; + for (size_t c=0;c<2;++c) { + const size_t accepted=c || hit; + h(i,j)[accepted]=A::add(h(i,j)[accepted],A::multiply(entry(a,b)[c],product)); + } + } + if (h(i,j)[1]>0) { + const Z_type b=A::check(w.boundary(p,q)); + result.z=A::add(result.z,A::multiply(b,h(i,j)[1])); + if (pairs) dh(i,j)[1]=b; + if (w.complete) w.complete(p,q,h(i,j)[1],b); + } + } + if (!pairs) return; + // Recompute each diagonal product chain with O(run length) scratch, then + // reverse it once. Each entry marks the first pair, product edges the rest. + std::vector product, edge, g; + for (size_t ii=n;ii>0;--ii) for (size_t jj=m;jj>0;--jj) { + const size_t i=ii-1,j=jj-1; + if (!valid(i,j)) continue; + product.clear(); edge.clear(); g.clear(); + bool hit=false; + for (size_t k=0;k<=std::min(i,j);++k) { + const size_t a=i-k,b=j-k; + if (!valid(a,b)) break; + Z_type t=1; + if (k) { t=A::check(w.transition({p1+a,p2+b},{p1+a+1,p2+b+1})); if (t==0) break; } + edge.push_back(t); + product.push_back(k?A::multiply(product.back(),t):Z_type(1)); + g.push_back(0); + const size_t length=seeds.seedLength(p1+a,p2+b); + hit |= length>0 && length<=k+1; + if (d.noLP && k==0) continue; // retain the empty product node + for (size_t c=0;c<2;++c) { + const Z_type adj=dh(i,j)[c || hit]; + de(a,b)[c]=A::add(de(a,b)[c],A::multiply(adj,product.back())); + g.back()=A::add(g.back(),A::multiply(adj,entry(a,b)[c])); + } + } + for (size_t k=product.size();k>1;) { + --k; + auto & mass=result.mass(p1+i-k+1,p2+j-k+1); + mass=A::add(mass,A::multiply(g[k],product[k])); + g[k-1]=A::add(g[k-1],A::multiply(g[k],edge[k])); + } + for (size_t c=0;c<2;++c) { + auto & mass=result.mass(p1+i,p2+j); + mass=A::add(mass,A::multiply(de(i,j)[c],entry(i,j)[c])); + } + incoming(i,j,[&](size_t u,size_t v) { + if (de(i,j)[0]==0 && de(i,j)[1]==0) return; + const Z_type t=A::check(w.transition({p1+u,p2+v},{p1+i,p2+j})); + for (size_t c=0;c<2;++c) dh(u,v)[c]=A::add(dh(u,v)[c],A::multiply(de(i,j)[c],t)); + }); + } + }); + return result; +} +#endif From 8bd85345d44d4b0c3e9174ce402f8f641aac8c7e Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 14:06:42 +0200 Subject: [PATCH 05/30] fix: integrate unique exact seeded partition accounting (step 4) --- ChangeLog | 11 +++ src/IntaRNA/OutputHandler.h | 18 ++++- src/IntaRNA/OutputHandlerCsv.cpp | 8 +- src/IntaRNA/OutputHandlerEnsemble.cpp | 4 +- src/IntaRNA/OutputHandlerHub.h | 9 +++ src/IntaRNA/OutputHandlerText.cpp | 2 +- src/IntaRNA/PredictorMfeEns.cpp | 26 ++++++ src/IntaRNA/PredictorMfeEns.h | 10 +++ .../PredictorMfeEns2dSeedExtension.cpp | 50 +++++++++++- src/IntaRNA/PredictorMfeEns2dSeedExtension.h | 7 ++ src/bin/IntaRNA.cpp | 1 + tests/PredictorMfeEnsRegression_test.cpp | 4 +- tests/PredictorSeedOracle_test.cpp | 81 +++++++++++++++++++ 13 files changed, 221 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6bee2563..72fef34c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- count each exact stack-seeded interaction once; preserve tiny positive totals + through complete updates and output forwarding (issue #257, step 4) + - select the checked suffix inside/outside kernel after independent correctness and maximal-stack timing comparisons (issue #257, implementation step 3) @@ -142,6 +145,14 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * PredictorMfeEns*, OutputHandler*, IntaRNA.cpp, predictor oracle tests : + + use the suffix kernel on the exact stack-only path, preserving virtual + update hooks and the legacy heuristic/bulged paths; share the boundary + objective with the outside pass and restore update scopes after exceptions + + retain exact-zero status through collectors, hubs and ensemble diagnostics; + test native Nussinov/nearest-neighbor boundary sums (PR #258, step 4) + 261008 Alexander Mitrofanov * SeededPartitionFunction, PartitionArithmetic, StackSeedDomain, tests, doc/analysis : + add checked nonnegative kernels and raw pair differentiation; compare both diff --git a/src/IntaRNA/OutputHandler.h b/src/IntaRNA/OutputHandler.h index 9528d121..9f925ade 100644 --- a/src/IntaRNA/OutputHandler.h +++ b/src/IntaRNA/OutputHandler.h @@ -3,6 +3,7 @@ #define INTARNA_OUTPUTHANDLER_H_ #include "IntaRNA/general.h" +#include "IntaRNA/PartitionArithmetic.h" #include "IntaRNA/Interaction.h" #include "IntaRNA/InteractionRange.h" #include "IntaRNA/OutputConstraint.h" @@ -97,7 +98,18 @@ class OutputHandler { getOutputConstraint() const; + /** Select exact-zero, checked partition accumulation for the seeded backend. + * @param exact whether this sequence pair uses the checked exact backend + */ + virtual void setExactPartition(bool exact); + /** @return whether exact-zero presentation/merging is enabled */ + bool usesExactPartition() const; + /** @return whether the accumulated partition is nonempty under its policy */ + bool hasNonzeroZ() const; + protected: + //! scoped to a supported sequence-pair ensemble, legacy outputs default false + bool exactPartition = false; //! the output constraints to be applied const OutputConstraint outConstraint; @@ -185,11 +197,15 @@ incrementZ( const Z_type subZ ) #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_outputHandlerIncrementZ) #endif - {Z += subZ;} + {Z = exactPartition ? PartitionArithmetic::add(Z,subZ) : Z+subZ;} } //////////////////////////////////////////////////////////////////////////// +inline void OutputHandler::setExactPartition(bool exact) { exactPartition=exact; } +inline bool OutputHandler::usesExactPartition() const { return exactPartition; } +inline bool OutputHandler::hasNonzeroZ() const { return exactPartition ? getZ()!=0 : !Z_equal(getZ(),Z_type(0)); } + } // namespace #endif /* OUTPUTHANDLER_H_ */ diff --git a/src/IntaRNA/OutputHandlerCsv.cpp b/src/IntaRNA/OutputHandlerCsv.cpp index 79a7d242..34d04a7e 100644 --- a/src/IntaRNA/OutputHandlerCsv.cpp +++ b/src/IntaRNA/OutputHandlerCsv.cpp @@ -424,7 +424,7 @@ add( const Interaction & i ) break; case Eall: - if ( Z_equal(Z,Z_type(0)) ) outTmp << notAvailable; else outTmp <setExactPartition(usesExactPartition()); outList.push_back(handler); } } @@ -304,6 +308,11 @@ operator= ( const OutputHandlerHub & toCopy) ///////////////////////////////////////////////////////////////////////// +inline void OutputHandlerHub::setExactPartition(bool exact) { + OutputHandler::setExactPartition(exact); + for (auto handler:outList) handler->setExactPartition(exact); +} + } // namespace #endif /* OUTPUTHANDLERHUB_H_ */ diff --git a/src/IntaRNA/OutputHandlerText.cpp b/src/IntaRNA/OutputHandlerText.cpp index d66ca12b..fe1154f5 100644 --- a/src/IntaRNA/OutputHandlerText.cpp +++ b/src/IntaRNA/OutputHandlerText.cpp @@ -417,7 +417,7 @@ add( const Interaction & i ) } // detailed // ensemble output if available - if (!Z_equal(Z,Z_type(0)) && Z_isNotINF(Z)) { + if (hasNonzeroZ() && Z_isNotINF(Z)) { outTmp <<"\n" <<"ensemble energy = "< #include @@ -45,6 +46,17 @@ addPartitionContribution( const size_t i1, const size_t j1 , const bool isHybridZ , Z_type & partZ_noED ) { + if (exactContribution) { + // The coefficient was admitted and computed once by the forward adapter. + // Reconstructing it here could disagree with the reverse objective. + if (!isHybridZ || partZ != exactContribution->first) + throw std::logic_error("exact seeded updateZ override changed a boundary weight; override exactBoundaryWeight instead"); + partZ_noED=PartitionArithmetic::check(partZ); + const Z_type weighted=PartitionArithmetic::multiply(partZ,exactContribution->second); + if (weighted==0) return false; + Zall=PartitionArithmetic::add(Zall,weighted); + return true; + } // check if something to be done if (Z_equal(partZ,0) || Z_isINF(Zall)) return false; @@ -150,6 +162,20 @@ updateCompleteZ( const size_t i1, const size_t j1 //////////////////////////////////////////////////////////////////////////// +void +PredictorMfeEns::updateExactCompleteZ(size_t i1,size_t j1,size_t i2,size_t j2, + Z_type hybrid,Z_type coefficient) +{ + const std::pair contribution(hybrid,coefficient); + struct Restore { + const std::pair * & slot; + const std::pair * previous; + ~Restore() { slot=previous; } + } restore{exactContribution,exactContribution}; + exactContribution=&contribution; + updateCompleteZ(i1,j1,i2,j2,hybrid,true); +} + void PredictorMfeEns:: updateOptimaUsingZ() diff --git a/src/IntaRNA/PredictorMfeEns.h b/src/IntaRNA/PredictorMfeEns.h index 2037d52f..c51fd654 100644 --- a/src/IntaRNA/PredictorMfeEns.h +++ b/src/IntaRNA/PredictorMfeEns.h @@ -61,6 +61,16 @@ class PredictorMfeEns : public PredictorMfe { //! map storing the partition of Zall for all considered interaction sites std::unordered_map Z_partition; + /** Checked exact-path update with the forward/outside boundary coefficient. + * Preserves virtual updateZ dispatch. Observing/delegating overrides work; + * reweighting must override the objective coefficient instead of partFunct. + */ + void updateExactCompleteZ(size_t i1, size_t j1, size_t i2, size_t j2, + Z_type hybrid, Z_type coefficient); + + //! scoped coefficient and hybrid expected by the checked complete update + const std::pair * exactContribution = nullptr; + //! whether updateZ() currently receives one complete boundary partition bool updateZisComplete; diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index d5adb5c4..00d67e1c 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -1,5 +1,7 @@ #include "IntaRNA/PredictorMfeEns2dSeedExtension.h" +#include "IntaRNA/SeededPartitionFunction.h" +#include "IntaRNA/PartitionArithmetic.h" namespace IntaRNA { @@ -17,7 +19,7 @@ PredictorMfeEns2dSeedExtension( , hybridZ_left( 0,0 ) , hybridZ_right( 0,0 ) { - assert( seedHandler.getConstraint().getBasePairs() > 1 ); + } ////////////////////////////////////////////////////////////////////////// @@ -33,6 +35,9 @@ void PredictorMfeEns2dSeedExtension:: predict( const IndexRange & r1, const IndexRange & r2 ) { + if (seedHandler.guaranteesStackOnlySeeds()) { predictStackSeeds(r1,r2); return; } + if (seedHandler.getConstraint().getBasePairs()<2) + throw std::invalid_argument("legacy bulged seed extension requires at least two seed pairs"); #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_logOutput) #endif @@ -130,6 +135,49 @@ predict( const IndexRange & r1, const IndexRange & r2 ) ////////////////////////////////////////////////////////////////////////// +Z_type +PredictorMfeEns2dSeedExtension::exactBoundaryWeight(size_t i1,size_t j1,size_t i2,size_t j2) const +{ + if (!isValidOutputSite(i1,j1,i2,j2)) return 0; + const E_type e=energy.getE(i1,j1,i2,j2,E_type(0)); + return E_isINF(e)?Z_type(0):PartitionArithmetic::exp(-E_2_Z(e)/energy.getRT()); +} + +void +PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const IndexRange & r2) +{ + const size_t size1=energy.getAccessibility1().getSequence().size(); + const size_t size2=energy.getAccessibility2().getSequence().size(); + if (!r1.isAscending() || !r2.isAscending() || r1.from>=size1 || r2.from>=size2) + throw std::invalid_argument("exact seeded prediction: invalid region"); + energy.setOffset1(r1.from); energy.setOffset2(r2.from); + seedHandler.setOffset1(r1.from); seedHandler.setOffset2(r2.from); + const size_t n=r1.to==RnaSequence::lastPos?energy.size1():std::min(energy.size1(),r1.to-r1.from+1); + const size_t m=r2.to==RnaSequence::lastPos?energy.size2():std::min(energy.size2(),r2.to-r2.from+1); + initOptima(); initZ(); Zall=0; + if (seedHandler.fillSeed(0,n-1,0,m-1)==0) { + output.setExactPartition(true); + reportOptima(); return; + } + const size_t span1=std::min(n,energy.getAccessibility1().getMaxLength()); + const size_t span2=std::min(m,energy.getAccessibility2().getMaxLength()); + StackSeedDomain seeds(seedHandler,n,m,span1,span2); + using K=SeededPartitionFunction; + const K::Domain domain{n,m,span1,span2,energy.getMaxInternalLoopSize1(),energy.getMaxInternalLoopSize2(),output.getOutputConstraint().noLP}; + auto boltzmann=[&](E_type e) { return E_isINF(e)?Z_type(0):PartitionArithmetic::exp(-E_2_Z(e)/energy.getRT()); }; + K::Weights weights{ + [&](K::Pair p){return energy.isAccessible1(p[0]) && energy.isAccessible2(p[1]) && energy.areComplementary(p[0],p[1]);}, + [&](K::Pair){return boltzmann(energy.getE_init());}, + [&](K::Pair p,K::Pair q){return boltzmann(energy.getE_interLeft(p[0],q[0],p[1],q[1]));}, + [&](K::Pair p,K::Pair q){return exactBoundaryWeight(p[0],q[0],p[1],q[1]);}, + [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p[0],q[0],p[1],q[1],h,b);} + }; + const auto result=K::compute(domain,seeds,weights,false); + if (result.z!=Zall) throw std::logic_error("exact seeded updateZ override did not preserve the partition objective"); + output.setExactPartition(true); + reportOptima(); +} + E_type PredictorMfeEns2dSeedExtension:: getNonOverlappingEnergy( const size_t si1, const size_t si2, const size_t si1p, const size_t si2p ) { diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h index 92107dac..d2591a01 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h @@ -73,6 +73,13 @@ class PredictorMfeEns2dSeedExtension: public PredictorMfeEns { protected: + /** Shared forward/outside objective coefficient. Override here for supported + * reweighting; return zero for forbidden boundaries. Coordinates are local. + * @return complete boundary Boltzmann factor (ED, dangles, ends, energyAdd) + */ + virtual Z_type exactBoundaryWeight(size_t i1,size_t j1,size_t i2,size_t j2) const; + /** Run the selected disjoint stack-seed partition backend. */ + void predictStackSeeds(const IndexRange & r1,const IndexRange & r2); //! access to the interaction energy handler of the super class using PredictorMfeEns::energy; diff --git a/src/bin/IntaRNA.cpp b/src/bin/IntaRNA.cpp index 555e0d02..ca60cf3d 100644 --- a/src/bin/IntaRNA.cpp +++ b/src/bin/IntaRNA.cpp @@ -335,6 +335,7 @@ int main(int argc, char **argv){ #endif {// update final output handler // copy partition function information if available + output->setExactPartition(bestInteractions.usesExactPartition()); output->incrementZ( bestInteractions.getZ() ); // Apply the energy window to the sequence pair's best candidate. // Independent per-region output retains each region's local window. diff --git a/tests/PredictorMfeEnsRegression_test.cpp b/tests/PredictorMfeEnsRegression_test.cpp index faf71ec2..e4f55379 100644 --- a/tests/PredictorMfeEnsRegression_test.cpp +++ b/tests/PredictorMfeEnsRegression_test.cpp @@ -231,7 +231,9 @@ TEST_CASE("ensemble predictor regressions", "[PredictorMfeEns]") { InspectableEnsemblePredictor exact( energy, exactOut, new SeedHandlerNoBulge(energy, seedConstraint)); exact.predict(); - REQUIRE(exact.getPartitionCount() > 0); + // The new exact stack backend streams each completed boundary once. + REQUIRE(exact.getPartitionCount() == 0); + REQUIRE(exact.getZall() > 0); exact.predict(IndexRange(0, 0), IndexRange(0, 0)); REQUIRE(exact.getPartitionCount() == 0); REQUIRE(exact.getZall() == 0.0); diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 61e9e3ad..ae0e1df9 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -17,6 +17,7 @@ #include "IntaRNA/SeedHandlerMfe.h" #include +#include #include #include #include @@ -208,6 +209,8 @@ bool passesOutputConstraint(const InteractionEnergy & energy, } const SeedOraclePair & left = chain.front(); const SeedOraclePair & right = chain.back(); + if (right.i1-left.i1+1>energy.getAccessibility1().getMaxLength() + || right.i2-left.i2+1>energy.getAccessibility2().getMaxLength()) return false; if (constraint.noGUend && (energy.isGU(left.i1, left.i2) || energy.isGU(right.i1, right.i2))) @@ -665,3 +668,81 @@ TEST_CASE("tiny exhaustive seed oracle for exact predictor families", == Approx(seedOnlyExpected.partition).epsilon(1e-12)); } } + +#include "IntaRNA/AccessibilityBasePair.h" +#include "IntaRNA/InteractionEnergyVrna.h" +#include "IntaRNA/SeedHandlerNoBulge.h" +#include "IntaRNA/OutputHandlerHub.h" + +namespace { +class ExactSeedProbe : public PredictorMfeEns2dSeedExtension { +public: + using PredictorMfeEns2dSeedExtension::PredictorMfeEns2dSeedExtension; + std::map,Z_type> boundaries; + bool throwUpdate=false; + size_t calls=0; + bool restored() const { return exactContribution==nullptr && !updateZisComplete; } + size_t retained() const { return Z_partition.size(); } +protected: + void updateZ(size_t i,size_t j,size_t k,size_t l,Z_type z,bool hybrid) override { + ++calls; + if (throwUpdate) throw std::runtime_error("injected update failure"); + if (exactContribution) boundaries[{i,k,j,l}]=z*exactContribution->second; + PredictorMfeEns2dSeedExtension::updateZ(i,j,k,l,z,hybrid); + } +}; +void checkNativeStackPartition(const InteractionEnergy & energy,bool noLP,bool noGU) { + const SeedConstraint sc=makeSeedConstraint(2,0,0,0,false); + OutputConstraint oc(3,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,noGU,true,false); + IndexRange r1(0,energy.size1()-1), r2(0,energy.size2()-1); + auto oracle=enumerateSeededInteractions(energy,sc,oc,r1,r2); + OutputHandlerInteractionList output(oc,3); + ExactSeedProbe p(energy,output,nullptr,new SeedHandlerMfe(energy,sc)); + p.predict(); + REQUIRE(p.getZall()==Approx(oracle.unionPartition).epsilon(2e-12)); + REQUIRE(output.getZ()==p.getZall()); + REQUIRE(output.usesExactPartition()); + REQUIRE(p.retained()==0); + std::map,Z_type> expected; + for(const auto & i:oracle.interactions) expected[{i.chain.front().i1,i.chain.front().i2,i.chain.back().i1,i.chain.back().i2}]+=energy.getBoltzmannWeight(i.energy); + for(const auto & [b,z]:p.boundaries) REQUIRE(z==Approx(expected[b]).epsilon(2e-12)); + for(const auto & [b,z]:expected) REQUIRE(p.boundaries[b]==Approx(z).epsilon(2e-12)); +} +} + +TEST_CASE("native exact stacked partitions agree beyond the unique-anchor domain", "[PredictorSeedOracle][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GUGGCGC"),q("q","GCGCCAC"); + AccessibilityBasePair at(t,6,nullptr), aq(q,5,nullptr); + ReverseAccessibility ar(aq); + InteractionEnergyBasePair simple(at,ar,2,1); + VrnaHandler vrna; + InteractionEnergyVrna nearest(at,ar,vrna,2,1); + for(bool noLP:{false,true}) for(bool noGU:{false,true}) { + checkNativeStackPartition(simple,noLP,noGU); + checkNativeStackPartition(nearest,noLP,noGU); + } +} +TEST_CASE("exact seeded updates retain tiny weights and restore scopes on exceptions", "[PredictorSeedOracle][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGGG"),q("q","CCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,2,false,1,-100,3,10000); + const auto sc=makeSeedConstraint(2,0,0,0,false); + const auto oc=makeOutputConstraint(false); + OutputHandlerInteractionList output(oc,1); + ExactSeedProbe p(energy,output,nullptr,new SeedHandlerMfe(energy,sc)); + p.throwUpdate=true;REQUIRE_THROWS(p.predict());REQUIRE(p.restored()); + p.throwUpdate=false;p.predict();REQUIRE(p.restored()); + REQUIRE(p.calls>0); + REQUIRE(p.getZall()>0); + REQUIRE(p.getZall()<1e-19); + REQUIRE(output.hasNonzeroZ()); + auto oracle=enumerateSeededInteractions(energy,sc,oc,IndexRange(0,3),IndexRange(0,3)); + REQUIRE(double(p.getZall()/oracle.unionPartition)==Approx(1).epsilon(2e-12)); + OutputHandlerHub hub(oc,false);hub.addOutputHandler(&output);hub.setExactPartition(true); + REQUIRE(output.usesExactPartition()); + p.predict(IndexRange(1,3),IndexRange(1,3)); + auto sub=enumerateSeededInteractions(energy,sc,oc,IndexRange(1,3),IndexRange(1,3)); + REQUIRE(double(p.getZall()/sub.unionPartition)==Approx(1).epsilon(2e-12)); +} From 99919de070e3c4db8f92f172872b6e2d6409ec21 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 14:08:58 +0200 Subject: [PATCH 06/30] feat: accumulate raw pair probabilities across regions (step 5) --- ChangeLog | 10 +++ src/IntaRNA/BasePairProbabilities.cpp | 63 +++++++++++++++++++ src/IntaRNA/BasePairProbabilities.h | 62 ++++++++++++++++++ src/IntaRNA/Makefile.am | 2 + .../PredictorMfeEns2dSeedExtension.cpp | 33 +++++++--- src/IntaRNA/PredictorMfeEns2dSeedExtension.h | 9 ++- tests/BasePairProbabilities_test.cpp | 33 ++++++++++ tests/Makefile.am | 1 + tests/PredictorSeedOracle_test.cpp | 52 +++++++++++++++ 9 files changed, 255 insertions(+), 10 deletions(-) create mode 100644 src/IntaRNA/BasePairProbabilities.cpp create mode 100644 src/IntaRNA/BasePairProbabilities.h create mode 100644 tests/BasePairProbabilities_test.cpp diff --git a/ChangeLog b/ChangeLog index 72fef34c..7a3e8406 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,9 @@ ## Technical changes and Optimizations +- collect raw actual-pair masses and disjoint regional denominators with explicit + successful-empty/nonempty/failed lifecycle (issue #257, step 5) + - count each exact stack-seeded interaction once; preserve tiny positive totals through complete updates and output forwarding (issue #257, step 4) @@ -145,6 +148,13 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * BasePairProbabilities, PredictorMfeEns2dSeedExtension, API tests, Makefile.am : + + add optional non-owning pair sink, original-coordinate raw regional commits + and explicit success-only finalization; reject overlap and range failures + + verify tracker parity, offsets, region crossings and injected failure after + a successful region; pair-only computation retains no boundary map (PR #258, step 5) + 261008 Alexander Mitrofanov * PredictorMfeEns*, OutputHandler*, IntaRNA.cpp, predictor oracle tests : + use the suffix kernel on the exact stack-only path, preserving virtual diff --git a/src/IntaRNA/BasePairProbabilities.cpp b/src/IntaRNA/BasePairProbabilities.cpp new file mode 100644 index 00000000..49d4e851 --- /dev/null +++ b/src/IntaRNA/BasePairProbabilities.cpp @@ -0,0 +1,63 @@ +#include "IntaRNA/BasePairProbabilities.h" +#include "IntaRNA/PartitionArithmetic.h" +#include +#include + +namespace IntaRNA { +BasePairProbabilities::BasePairProbabilities(size_t n,size_t m):mass(n,m,0) {} +void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, + Z_type denominator,const Matrix & values) +{ + using A=PartitionArithmetic; + try { + if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); + if (!t.isAscending() || !q.isAscending() || t.to>=mass.size1() || q.to>=mass.size2() + || values.size1()!=t.to-t.from+1 || values.size2()!=q.to-q.from+1) + throw std::invalid_argument("pair probabilities: invalid region dimensions"); + for (const auto & r:regions) + if (t.from<=r.first.to && r.first.from<=t.to && q.from<=r.second.to && r.second.from<=q.to) + throw std::invalid_argument("pair probabilities: searched regions overlap"); + const Z_type merged=A::add(z,denominator); + for (size_t i=0;i BasePairProbabilities::probabilities() const { + using A=PartitionArithmetic; + if (state!=Status::nonempty) throw std::logic_error("pair probabilities: no successful nonempty result"); + Matrix p(mass.size1(),mass.size2(),0); + // Only roundoff-sized endpoint correction is allowed. Bound scales with + // matrix dimensions, at 512 ulps per row/column element (tested with oracles). + const Z_type tolerance=512*std::numeric_limits::epsilon() + *Z_type(std::max(size_t(1),mass.size1()+mass.size2())); + std::vector columns(mass.size2(),0); + for(size_t i=0;i1+tolerance) throw std::logic_error("pair probabilities: pair mass exceeds denominator"); + value=std::min(Z_type(1),value); + p(i,j)=value;row=A::add(row,value);columns[j]=A::add(columns[j],value); + } + if(row>1+tolerance) throw std::logic_error("pair probabilities: target exclusivity violated"); + } + for(auto c:columns) if(c>1+tolerance) throw std::logic_error("pair probabilities: query exclusivity violated"); + return p; +} +void BasePairProbabilities::finalize() { + try { + if(state!=Status::pending) throw std::logic_error("pair probabilities: completion requires a pending result"); + PartitionArithmetic::check(z); + state=z==0?Status::empty:Status::nonempty; + if(z>0) probabilities(); // validate all entries before a writer can observe success + } catch (...) { fail();throw; } +} +} diff --git a/src/IntaRNA/BasePairProbabilities.h b/src/IntaRNA/BasePairProbabilities.h new file mode 100644 index 00000000..73d30825 --- /dev/null +++ b/src/IntaRNA/BasePairProbabilities.h @@ -0,0 +1,62 @@ +#ifndef INTARNA_BASEPAIRPROBABILITIES_H_ +#define INTARNA_BASEPAIRPROBABILITIES_H_ +#include "IntaRNA/general.h" +#include "IntaRNA/IndexRange.h" +#include "IntaRNA/Matrix.h" +#include +#include + +namespace IntaRNA { +/** Success-only accumulator for one target/query pair. Raw numerators and Z are + * merged over disjoint searched rectangles, then normalized explicitly. All + * stored coordinates follow the original sequences' 5'-to-3' order. This is + * conditional on an allowed interaction, without an unbound-state contribution. + * A destructor never publishes data. Not thread safe: use one owner per pair. + */ +class BasePairProbabilities { +public: + /** Pending accumulation, successful nonempty/empty completion, or failure. */ + enum class Status { pending, nonempty, empty, failed }; + /** Allocate raw masses for the full original target/query sequences. + * @param targetLength number of target positions + * @param queryLength number of query positions + */ + BasePairProbabilities(size_t targetLength,size_t queryLength); + /** Commit one completed region, after validating ALL sums and disjointness. + * @param target inclusive original target region + * @param query inclusive original query region + * @param denominator raw region Z + * @param masses raw region numerators in original orientation (local indices) + * @throws on invalid/overlapping regions or numerical failure, marking failed + */ + void addRegion(const IndexRange & target,const IndexRange & query, + Z_type denominator,const Matrix & masses); + /** Finalize only after all requested regions succeeded; validate normalized + * masses, row/column exclusivity and numerical range before publication. + * @throws on failed computation or inconsistent masses + */ + void finalize(); + /** Mark this pair failed after any error or cancellation. */ + void fail() noexcept; + /** @return explicit lifecycle status */ + Status status() const; + /** @return the raw merged denominator (not usable as a result after failure) */ + Z_type getZ() const; + /** @return raw merged pair numerators (original coordinates) */ + const Matrix & rawMasses() const; + /** @return validated matrix; only available for finalized nonempty results. + * Successful empty output is represented by status()==empty, not zeroes. + */ + Matrix probabilities() const; +private: + Status state=Status::pending; + Z_type z=0; + Matrix mass; + std::vector> regions; +}; +inline BasePairProbabilities::Status BasePairProbabilities::status() const { return state; } +inline Z_type BasePairProbabilities::getZ() const { return z; } +inline const Matrix & BasePairProbabilities::rawMasses() const { return mass; } +inline void BasePairProbabilities::fail() noexcept { state=Status::failed; } +} +#endif diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index 4e37bc30..253db1d5 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -28,6 +28,7 @@ libIntaRNA_a_HEADERS = \ intarna_config.h \ Matrix.h \ StackSeedDomain.h \ + BasePairProbabilities.h \ PartitionArithmetic.h \ SeededPartitionFunction.h \ Accessibility.h \ @@ -102,6 +103,7 @@ libIntaRNA_a_HEADERS = \ # the sources to add to the library and to add to the source distribution libIntaRNA_a_SOURCES = \ StackSeedDomain.cpp \ + BasePairProbabilities.cpp \ SeededPartitionFunction.cpp \ $(libmylib_a_HEADERS) \ Accessibility.cpp \ diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index 00d67e1c..4f50a61e 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -12,14 +12,17 @@ PredictorMfeEns2dSeedExtension( const InteractionEnergy & energy , OutputHandler & output , PredictionTracker * predTracker - , SeedHandler * seedHandlerInstance ) + , SeedHandler * seedHandlerInstance + , BasePairProbabilities * pairProbabilities ) : PredictorMfeEns(energy,output,predTracker) + , pairProbabilities(pairProbabilities) , seedHandler(seedHandlerInstance) , hybridZ_left( 0,0 ) , hybridZ_right( 0,0 ) { - + if (pairProbabilities && (!seedHandler.guaranteesStackOnlySeeds() || !output.getOutputConstraint().needZall)) + throw std::invalid_argument("pair probabilities require a stack-only exact predictor and needZall"); } ////////////////////////////////////////////////////////////////////////// @@ -35,7 +38,11 @@ void PredictorMfeEns2dSeedExtension:: predict( const IndexRange & r1, const IndexRange & r2 ) { - if (seedHandler.guaranteesStackOnlySeeds()) { predictStackSeeds(r1,r2); return; } + if (seedHandler.guaranteesStackOnlySeeds()) { + try { predictStackSeeds(r1,r2); } + catch (...) { if (pairProbabilities) pairProbabilities->fail(); throw; } + return; + } if (seedHandler.getConstraint().getBasePairs()<2) throw std::invalid_argument("legacy bulged seed extension requires at least two seed pairs"); #if INTARNA_MULITHREADING @@ -155,13 +162,11 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In const size_t n=r1.to==RnaSequence::lastPos?energy.size1():std::min(energy.size1(),r1.to-r1.from+1); const size_t m=r2.to==RnaSequence::lastPos?energy.size2():std::min(energy.size2(),r2.to-r2.from+1); initOptima(); initZ(); Zall=0; - if (seedHandler.fillSeed(0,n-1,0,m-1)==0) { - output.setExactPartition(true); - reportOptima(); return; - } + const size_t seedCount=seedHandler.fillSeed(0,n-1,0,m-1); const size_t span1=std::min(n,energy.getAccessibility1().getMaxLength()); const size_t span2=std::min(m,energy.getAccessibility2().getMaxLength()); - StackSeedDomain seeds(seedHandler,n,m,span1,span2); + StackSeedDomain seeds=seedCount ? StackSeedDomain(seedHandler,n,m,span1,span2) + : StackSeedDomain(std::vector(),n,m,span1,span2); using K=SeededPartitionFunction; const K::Domain domain{n,m,span1,span2,energy.getMaxInternalLoopSize1(),energy.getMaxInternalLoopSize2(),output.getOutputConstraint().noLP}; auto boltzmann=[&](E_type e) { return E_isINF(e)?Z_type(0):PartitionArithmetic::exp(-E_2_Z(e)/energy.getRT()); }; @@ -172,10 +177,20 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In [&](K::Pair p,K::Pair q){return exactBoundaryWeight(p[0],q[0],p[1],q[1]);}, [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p[0],q[0],p[1],q[1],h,b);} }; - const auto result=K::compute(domain,seeds,weights,false); + const auto result=K::compute(domain,seeds,weights,pairProbabilities!=nullptr); if (result.z!=Zall) throw std::logic_error("exact seeded updateZ override did not preserve the partition objective"); output.setExactPartition(true); reportOptima(); + if (pairProbabilities) { + const auto first=energy.getBasePair(0,0), last=energy.getBasePair(n-1,m-1); + const IndexRange target(first.first,last.first), query(last.second,first.second); + Matrix original(n,m,0); + for(size_t i=0;iaddRegion(target,query,result.z,original); + } } E_type diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h index d2591a01..e046de5b 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h @@ -3,6 +3,7 @@ #define INTARNA_PREDICTORMFEENS2DSEEDEXTENSION_H_ #include "IntaRNA/PredictorMfeEns.h" +#include "IntaRNA/BasePairProbabilities.h" #include "IntaRNA/Matrix.h" #include "IntaRNA/SeedHandlerIdxOffset.h" @@ -38,13 +39,16 @@ class PredictorMfeEns2dSeedExtension: public PredictorMfeEns { * @param predTracker the prediction tracker to be used or NULL if no * tracking is to be done; if non-NULL, the tracker gets deleted * on this->destruction. + * @param pairProbabilities optional non-owning sink; must outlive predictions + * and remain pending until all disjoint regions have succeeded * @param seedHandler the seed handler to be used */ PredictorMfeEns2dSeedExtension( const InteractionEnergy & energy , OutputHandler & output , PredictionTracker * predTracker - , SeedHandler * seedHandler ); + , SeedHandler * seedHandler + , BasePairProbabilities * pairProbabilities = nullptr ); /** @@ -81,6 +85,9 @@ class PredictorMfeEns2dSeedExtension: public PredictorMfeEns { /** Run the selected disjoint stack-seed partition backend. */ void predictStackSeeds(const IndexRange & r1,const IndexRange & r2); + //! optional sequence-pair owner, never deleted or finalized by the predictor + BasePairProbabilities * pairProbabilities; + //! access to the interaction energy handler of the super class using PredictorMfeEns::energy; diff --git a/tests/BasePairProbabilities_test.cpp b/tests/BasePairProbabilities_test.cpp new file mode 100644 index 00000000..c53671ba --- /dev/null +++ b/tests/BasePairProbabilities_test.cpp @@ -0,0 +1,33 @@ +#include "catch.hpp" +#include "IntaRNA/BasePairProbabilities.h" +using namespace IntaRNA; +TEST_CASE("raw pair regions commit once and finalize only on success", "[BasePairProbabilities]") { + BasePairProbabilities r(4,5); + Matrix m(2,2,0);m(0,1)=2;m(1,0)=2; + r.addRegion({0,1},{1,2},2,m); + REQUIRE_THROWS(r.probabilities()); + r.addRegion({2,3},{3,4},2,m); + r.finalize(); + REQUIRE(r.getZ()==4); + REQUIRE(r.status()==BasePairProbabilities::Status::nonempty); + auto p=r.probabilities(); + REQUIRE(p(0,2)==.5);REQUIRE(p(3,3)==.5);REQUIRE(p(0,4)==0); + BasePairProbabilities empty(1,1);empty.finalize(); + REQUIRE(empty.status()==BasePairProbabilities::Status::empty); + BasePairProbabilities overlap(4,5);overlap.addRegion({0,1},{1,2},2,m); + REQUIRE_THROWS(overlap.addRegion({1,2},{2,3},2,m)); + REQUIRE(overlap.status()==BasePairProbabilities::Status::failed); + REQUIRE(overlap.getZ()==2);REQUIRE_THROWS(overlap.finalize()); + BasePairProbabilities cancelled(4,5);cancelled.addRegion({0,1},{1,2},2,m);cancelled.fail(); + REQUIRE_THROWS(cancelled.finalize());REQUIRE_THROWS(cancelled.probabilities()); +} +TEST_CASE("pair result rejects inconsistent and out-of-range numerical results", "[BasePairProbabilities]") { + Matrix m(1,1,1e-100);BasePairProbabilities tiny(1,1); + tiny.addRegion({0,0},{0,0},1e-100,m);tiny.finalize();REQUIRE(tiny.probabilities()(0,0)==1); + m(0,0)=2;BasePairProbabilities excess(1,1);excess.addRegion({0,0},{0,0},1,m); + REQUIRE_THROWS(excess.finalize());REQUIRE(excess.status()==BasePairProbabilities::Status::failed); + m(0,0)=std::numeric_limits::max();BasePairProbabilities overflow(2,1); + overflow.addRegion({0,0},{0,0},m(0,0),m); + REQUIRE_THROWS(overflow.addRegion({1,1},{0,0},m(0,0),m)); + REQUIRE(overflow.status()==BasePairProbabilities::Status::failed); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index ebbc88ca..023ad135 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,6 +28,7 @@ runApiTests_SOURCES = \ SeededStackKernel.h \ SeededPartitionFunction_test.cpp \ StackSeedDomain_test.cpp \ + BasePairProbabilities_test.cpp \ testEasyLoggingSetup.icc \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index ae0e1df9..530ba836 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -746,3 +746,55 @@ TEST_CASE("exact seeded updates retain tiny weights and restore scopes on except auto sub=enumerateSeededInteractions(energy,sc,oc,IndexRange(1,3),IndexRange(1,3)); REQUIRE(double(p.getZall()/sub.unionPartition)==Approx(1).epsilon(2e-12)); } + +namespace { +class PairTestTracker : public PredictionTracker { +public: + void updateOptimumCalled(size_t,size_t,size_t,size_t,E_type) override {} +}; +} +TEST_CASE("raw native pair masses share the exact forward denominator", "[BasePairProbabilities][PredictorSeedOracle]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("target","GGAGGG"),q("query","CCUCCC"); + AccessibilityBasePair at(t,5,nullptr),aq(q,4,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,1); + const auto sc=makeSeedConstraint(2,0,0,0,false); + for(bool noLP:{false,true}) for(bool tracker:{false,true}) { + auto oc=makeOutputConstraint(noLP); + BasePairProbabilities result(t.size(),q.size()); + OutputHandlerInteractionList output(oc,1); + ExactSeedProbe p(energy,output,tracker?new PairTestTracker():nullptr,new SeedHandlerMfe(energy,sc),&result); + const IndexRange r1(1,5),r2(1,4); + p.predict(r1,r2);result.finalize(); + auto oracle=enumerateSeededInteractions(energy,sc,oc,r1,r2); + REQUIRE(result.getZ()==p.getZall()); + REQUIRE(result.getZ()==Approx(oracle.unionPartition).epsilon(2e-12)); + REQUIRE((p.retained()>0)==tracker); + Matrix expected(t.size(),q.size(),0); + for(const auto & chain:oracle.interactions) for(const auto & pair:chain.chain) { + const auto bp=energy.getBasePair(pair.i1,pair.i2); + expected(bp.first,bp.second)+=energy.getBoltzmannWeight(chain.energy); + } + for(size_t i=0;i Date: Thu, 8 Oct 2026 14:19:49 +0200 Subject: [PATCH 07/30] feat: expose exact actual-pair probability output (step 6) --- ChangeLog | 12 +++ src/IntaRNA/BasePairProbabilityWriter.cpp | 59 +++++++++++ src/IntaRNA/BasePairProbabilityWriter.h | 39 ++++++++ src/IntaRNA/Makefile.am | 2 + src/IntaRNA/OutputHandler.h | 10 +- src/IntaRNA/PredictorMfe.cpp | 2 + src/IntaRNA/SeedHandlerExplicit.cpp | 9 +- src/bin/CommandLineParsing.cpp | 31 +++++- src/bin/CommandLineParsing.h | 11 +- src/bin/IntaRNA.cpp | 19 +++- tests/BasePairProbabilities_test.cpp | 17 ++++ tests/Makefile.am | 4 +- tests/PredictorSeedOracle_test.cpp | 22 ++++ tests/StackSeedDomain_test.cpp | 22 ++++ tests/runBasePairProbabilities.sh | 4 + tests/testBasePairProbabilities.py | 117 ++++++++++++++++++++++ 16 files changed, 371 insertions(+), 9 deletions(-) create mode 100644 src/IntaRNA/BasePairProbabilityWriter.cpp create mode 100644 src/IntaRNA/BasePairProbabilityWriter.h create mode 100644 tests/runBasePairProbabilities.sh create mode 100644 tests/testBasePairProbabilities.py diff --git a/ChangeLog b/ChangeLog index 7a3e8406..f876d0e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,10 @@ ## Interface and handling +- --out=bpProb:FILE writes actual-pair conditional probabilities for exact + model P/mode M stack-seed ensembles, with disjoint-region merging, original + coordinates and success-only publication (issue #257, implementation step 6) + - deterministic kinetic seed extension with --model=X --mode=K and configurable local scoring; complete-energy downhill steps, atomic noLP loop/stack moves and retained greedy traceback; equilibrium probability output is unsupported @@ -148,6 +152,14 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * BasePairProbabilityWriter, CommandLineParsing, IntaRNA.cpp, CLI/API tests : + + add bpProb matrix output, capability/mode validation, multi-FASTA naming, + gzip and synchronized standard-stream blocks; preserve spot coverage + + guard OpenMP cancellation before publication, propagate write errors, keep + tiny totals, support ranked count zero and mixed explicit seeds in debug + validation (PR #258, step 6) + 261008 Alexander Mitrofanov * BasePairProbabilities, PredictorMfeEns2dSeedExtension, API tests, Makefile.am : + add optional non-owning pair sink, original-coordinate raw regional commits diff --git a/src/IntaRNA/BasePairProbabilityWriter.cpp b/src/IntaRNA/BasePairProbabilityWriter.cpp new file mode 100644 index 00000000..d0aea7af --- /dev/null +++ b/src/IntaRNA/BasePairProbabilityWriter.cpp @@ -0,0 +1,59 @@ +#include "IntaRNA/BasePairProbabilityWriter.h" +#include +#include +#include +#include +#include + +namespace IntaRNA { +std::string BasePairProbabilityWriter::block(const BasePairProbabilities & result, + const RnaSequence & target,const RnaSequence & query,const std::string & sep) +{ + const bool empty=result.status()==BasePairProbabilities::Status::empty; + if (!empty && result.status()!=BasePairProbabilities::Status::nonempty) + throw std::logic_error("bpProb output requires successful explicit finalization"); + if (result.rawMasses().size1()!=target.size() || result.rawMasses().size2()!=query.size()) + throw std::invalid_argument("bpProb output sequence dimensions do not match result"); + // Validate every normalized value before opening a file or writing a header. + const auto p=empty?Matrix():result.probabilities(); + std::ostringstream data; + data<::max_digits10)<<"bpProb"; + for(size_t j=0;j file(out==&std::cout || out==&std::cerr?nullptr:out); + emit(*out,data); + if(auto filtered=dynamic_cast(out)) filtered->reset(); +} +} diff --git a/src/IntaRNA/BasePairProbabilityWriter.h b/src/IntaRNA/BasePairProbabilityWriter.h new file mode 100644 index 00000000..48578daf --- /dev/null +++ b/src/IntaRNA/BasePairProbabilityWriter.h @@ -0,0 +1,39 @@ +#ifndef INTARNA_BASEPAIRPROBABILITYWRITER_H_ +#define INTARNA_BASEPAIRPROBABILITYWRITER_H_ +#include "IntaRNA/BasePairProbabilities.h" +#include "IntaRNA/RnaSequence.h" +#include +#include + +namespace IntaRNA { +/** Explicit writer for a completed actual-pair matrix, independent of trackers. + * Whole blocks are synchronized for shared streams. No destructor emits data. + */ +class BasePairProbabilityWriter { +public: + /** Validate and write one matrix with original 5'-to-3' nucleotide/index labels. + * @param out destination stream; an I/O failure throws + * @param result explicitly finalized raw result + * @param target original target sequence + * @param query original query sequence + * @param separator CSV-style separator, shared with ordinary output + */ + static void write(std::ostream & out,const BasePairProbabilities & result, + const RnaSequence & target,const RnaSequence & query,const std::string & separator=";"); + /** Write to a filename or STDOUT/STDERR, using existing gzip stream support. + * Checks stream flush and explicit close; does not publish pending/failed data. + * @param filename destination (already expanded for multi-FASTA) + * @param result finalized raw result + * @param target original target sequence + * @param query original query sequence + * @param separator CSV-style separator + */ + static void writeFile(const std::string & filename,const BasePairProbabilities & result, + const RnaSequence & target,const RnaSequence & query,const std::string & separator=";"); +private: + static std::string block(const BasePairProbabilities & result,const RnaSequence & target, + const RnaSequence & query,const std::string & separator); + static void emit(std::ostream & out,const std::string & data); +}; +} +#endif diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index 253db1d5..4fc5db3e 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -29,6 +29,7 @@ libIntaRNA_a_HEADERS = \ Matrix.h \ StackSeedDomain.h \ BasePairProbabilities.h \ + BasePairProbabilityWriter.h \ PartitionArithmetic.h \ SeededPartitionFunction.h \ Accessibility.h \ @@ -104,6 +105,7 @@ libIntaRNA_a_HEADERS = \ libIntaRNA_a_SOURCES = \ StackSeedDomain.cpp \ BasePairProbabilities.cpp \ + BasePairProbabilityWriter.cpp \ SeededPartitionFunction.cpp \ $(libmylib_a_HEADERS) \ Accessibility.cpp \ diff --git a/src/IntaRNA/OutputHandler.h b/src/IntaRNA/OutputHandler.h index 9f925ade..2e6aefe6 100644 --- a/src/IntaRNA/OutputHandler.h +++ b/src/IntaRNA/OutputHandler.h @@ -8,6 +8,7 @@ #include "IntaRNA/InteractionRange.h" #include "IntaRNA/OutputConstraint.h" #include +#include namespace IntaRNA { @@ -193,11 +194,16 @@ incrementZ( const Z_type subZ ) LOG(WARNING) <<"OutputHandler::incrementZ() : partition function overflow! Recompile with larger partition function data type!"; } #endif - // increment partition function + // Capture range failures within the OpenMP structured block. + std::exception_ptr failure; #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_outputHandlerIncrementZ) #endif - {Z = exactPartition ? PartitionArithmetic::add(Z,subZ) : Z+subZ;} + { + try { Z = exactPartition ? PartitionArithmetic::add(Z,subZ) : Z+subZ; } + catch (...) { failure=std::current_exception(); } + } + if (failure) std::rethrow_exception(failure); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/IntaRNA/PredictorMfe.cpp b/src/IntaRNA/PredictorMfe.cpp index 91792bea..ac6ec7ca 100644 --- a/src/IntaRNA/PredictorMfe.cpp +++ b/src/IntaRNA/PredictorMfe.cpp @@ -215,6 +215,8 @@ reportOptima() output.incrementZ( getZall() ); } + // A zero ranked-output count still publishes ensemble statistics. + if (mfeInteractions.empty()) return; // temporary access const OutputConstraint & outConstraint = output.getOutputConstraint(); // number of reported interactions diff --git a/src/IntaRNA/SeedHandlerExplicit.cpp b/src/IntaRNA/SeedHandlerExplicit.cpp index 5c38fde3..7deb8b2c 100644 --- a/src/IntaRNA/SeedHandlerExplicit.cpp +++ b/src/IntaRNA/SeedHandlerExplicit.cpp @@ -86,7 +86,14 @@ getSeedMinBP( const std::string & seedEncoding ) size_t minBP = 99999; if (!seedEncoding.empty()) { #if INTARNA_IN_DEBUG_MODE - if (!checkSeedEncoding(seedEncoding).empty()) throw std::runtime_error("SeedHandlerExplicit::getSeedMinBP() : no valid seed encoding : "+checkSeedEncoding(seedEncoding)); + size_t begin=0; + while(begin::getUnescaped( outSep ); + if (hasBasePairProbabilityOutput()) { + if (model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) + throw error("bpProb requires --model=P --mode=M with stack-only seeds and no windows"); + outNeedsZall=true; + } // K needs a seed even before the usual --noSeed model normalization. if (mode.val == 'K') { if (model.val != 'X') throw error("--mode=K is available only with --model=X"); @@ -1573,6 +1580,9 @@ parse(int argc, char** argv) if (c1->second.empty()) { continue; } for (auto c2=c1; noDuplicate && (++c2)!=outPrefix2streamName.end();) { if ( ! c2->second.empty() && boost::iequals( c1->second, c2->second ) ) { + // Pair matrices emit synchronized complete blocks on shared standard streams. + if ((c1->first==OP_bpProb || c2->first==OP_bpProb) + && (boost::iequals(c1->second,"STDOUT") || boost::iequals(c1->second,"STDERR"))) continue; noDuplicate = false; throw error("--out argument shows multiple times '"+toString(c1->second)+"' as target file/stream."); } @@ -2478,10 +2488,27 @@ getTemperature() const { //////////////////////////////////////////////////////////////////////////// +bool CommandLineParsing::hasBasePairProbabilityOutput() const { + return !outPrefix2streamName.at(OutPrefixCode::OP_bpProb).empty(); +} +void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const { + const auto & t=energy.getAccessibility1().getSequence(); + const auto & q=energy.getAccessibility2().getAccessibilityOrigin().getSequence(); + BasePairProbabilityWriter::writeFile(getFullFilename(outPrefix2streamName.at(OutPrefixCode::OP_bpProb),&t,&q),result,t,q,outSep); +} + Predictor* CommandLineParsing:: -getPredictor( const InteractionEnergy & energy, OutputHandler & output ) const +getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePairProbabilities * pairProbabilities ) const { + std::unique_ptr pairSeeds; + if (pairProbabilities) { + if(model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) + throw std::invalid_argument("bpProb requires --model=P --mode=M with stack-only seeds and no windows"); + pairSeeds.reset(getSeedHandler(energy)); + if(!pairSeeds->guaranteesStackOnlySeeds()) + throw std::invalid_argument("bpProb requires a stack-only seed handler; bulged seeds are unsupported"); + } // set up hub for prediction tracking (if needed) PredictionTrackerHub * predTracker = new PredictionTrackerHub(); @@ -2627,7 +2654,7 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output ) const case 'P' : { switch ( mode.val ) { case 'H' : return new PredictorMfeEns2dHeuristicSeedExtension( energy, output, predTracker, getSeedHandler( energy ) ); - case 'M' : return new PredictorMfeEns2dSeedExtension( energy, output, predTracker, getSeedHandler( energy ) ); + case 'M' : return new PredictorMfeEns2dSeedExtension( energy, output, predTracker, pairSeeds ? pairSeeds.release() : getSeedHandler( energy ), pairProbabilities ); case 'S' : return new PredictorMfeEnsSeedOnly( energy, output, predTracker, getSeedHandler(energy) ); default : INTARNA_NOT_IMPLEMENTED("mode "+toString(mode.val)+" not available for model "+toString(model.val)); } diff --git a/src/bin/CommandLineParsing.h b/src/bin/CommandLineParsing.h index f6658728..efcc3c11 100644 --- a/src/bin/CommandLineParsing.h +++ b/src/bin/CommandLineParsing.h @@ -26,6 +26,7 @@ #include "IntaRNA/Predictor.h" #include "IntaRNA/SeedConstraint.h" #include "IntaRNA/SeedHandler.h" +#include "IntaRNA/BasePairProbabilities.h" #include "IntaRNA/SeedHandlerExplicit.h" #include "IntaRNA/PredictionTrackerSpotProb.h" #include "IntaRNA/VrnaHandler.h" @@ -281,11 +282,17 @@ class CommandLineParsing { * parameters * @param energy the interaction energy handler to be used * @param output the output handler to be used + * @param pairProbabilities optional non-owning raw result sink for exact stack seeds * @return the newly allocated Predictor object to be deleted by the calling * function */ Predictor* getPredictor( const InteractionEnergy & energy - , OutputHandler & output ) const; + , OutputHandler & output, BasePairProbabilities * pairProbabilities = nullptr ) const; + + /** @return whether actual base-pair matrix output was requested */ + bool hasBasePairProbabilityOutput() const; + /** Publish a successful sequence-pair result using ordinary filename rules. */ + void writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const; /** * Provides the seed constraint according to the user settings @@ -404,6 +411,7 @@ class CommandLineParsing { OP_tPu, OP_spotProb, OP_spotProbAll, + OP_bpProb, OP_UNKNOWN }; @@ -430,6 +438,7 @@ class CommandLineParsing { if (prefLC == "tacc") { return OutPrefixCode::OP_tAcc; } else if (prefLC == "qpu") { return OutPrefixCode::OP_qPu; } else if (prefLC == "tpu") { return OutPrefixCode::OP_tPu; } else + if (prefLC == "bpprob") { return OutPrefixCode::OP_bpProb; } else if (prefLC == "spotprob") { return OutPrefixCode::OP_spotProb; } else // not known return OutPrefixCode::OP_UNKNOWN; diff --git a/src/bin/IntaRNA.cpp b/src/bin/IntaRNA.cpp index ca60cf3d..15d02b8c 100644 --- a/src/bin/IntaRNA.cpp +++ b/src/bin/IntaRNA.cpp @@ -243,6 +243,11 @@ int main(int argc, char **argv){ (parameters.reportBestPerRegion() ? std::numeric_limits::max() : 1 ) * parameters.getOutputConstraint(*energy).reportMax ); + // One owner spans all disjoint searched regions for this sequence pair. + std::unique_ptr pairProbabilities; + if (parameters.hasBasePairProbabilityOutput()) + pairProbabilities=std::make_unique(energy->size1(),energy->size2()); + // run prediction for all range combinations for(const IndexRange & tRange : parameters.getTargetRanges(*energy, targetNumber, *targetAcc)) { for(const IndexRange & qRange : parameters.getQueryRanges(*energy, queryNumber, *queryAcc.at(queryNumber).original)) { @@ -289,7 +294,7 @@ int main(int argc, char **argv){ // The prediction collector merges equal-energy structures with // identical boundaries and pair counts, even if inner pairs differ. std::unique_ptr predictor(parameters.getPredictor( *energy, - parameters.isEvaluation() ? *output : bestInteractions )); + parameters.isEvaluation() ? *output : bestInteractions, pairProbabilities.get() )); INTARNA_CHECK_NOT_NULL(predictor.get(),"predictor initialization failed"); // run prediction for this window combination @@ -330,6 +335,15 @@ int main(int argc, char **argv){ }} // window combinations } // target ranges } // query ranges + bool publish=true; +#if INTARNA_MULITHREADING + #pragma omp flush(threadAborted) + publish=!threadAborted; +#endif + if (pairProbabilities) { + if (publish) pairProbabilities->finalize(); else pairProbabilities->fail(); + } + if (publish) { #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_outputHandlerUpdate) #endif @@ -349,6 +363,9 @@ int main(int argc, char **argv){ } } + if (pairProbabilities) parameters.writeBasePairProbabilities(*pairProbabilities,*energy); + } // successful sequence-pair computation + #if INTARNA_MULITHREADING #pragma omp atomic update #endif diff --git a/tests/BasePairProbabilities_test.cpp b/tests/BasePairProbabilities_test.cpp index c53671ba..500c306c 100644 --- a/tests/BasePairProbabilities_test.cpp +++ b/tests/BasePairProbabilities_test.cpp @@ -31,3 +31,20 @@ TEST_CASE("pair result rejects inconsistent and out-of-range numerical results", REQUIRE_THROWS(overflow.addRegion({1,1},{0,0},m(0,0),m)); REQUIRE(overflow.status()==BasePairProbabilities::Status::failed); } + +#include "IntaRNA/BasePairProbabilityWriter.h" +#include +TEST_CASE("pair writer validates before emitting and checks stream failure", "[BasePairProbabilities]") { + RnaSequence t("target","GA"),q("query","UC"); + BasePairProbabilities r(2,2);std::ostringstream out; + REQUIRE_THROWS(BasePairProbabilityWriter::write(out,r,t,q));REQUIRE(out.str().empty()); + Matrix m(2,2,0);m(0,1)=1e-100; + r.addRegion({0,1},{0,1},1,m);r.finalize(); + BasePairProbabilityWriter::write(out,r,t,q); + REQUIRE(out.str().find("bpProb;U_1;C_2\nG_1;0;1e-100\nA_2;0;0\n")==0); + std::ostringstream broken;broken.setstate(std::ios::badbit); + REQUIRE_THROWS(BasePairProbabilityWriter::write(broken,r,t,q)); + BasePairProbabilities empty(2,2);empty.finalize();std::ostringstream na; + BasePairProbabilityWriter::write(na,empty,t,q); + REQUIRE(na.str()=="bpProb;U_1;C_2\nG_1;NA;NA\nA_2;NA;NA\n"); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 023ad135..0a0f3e62 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,7 @@ TEST_EXTENSIONS = $(EXEEXT) .sh SH_LOG_COMPILER = $(SHELL) # the script needed for tests -dist_check_SCRIPTS = runIntaRNA.sh runAccessibilityBinary.sh runIntaRNAeval.sh runKineticSeedExtension.sh runOutputOverlap.sh +dist_check_SCRIPTS = runIntaRNA.sh runAccessibilityBinary.sh runIntaRNAeval.sh runKineticSeedExtension.sh runOutputOverlap.sh runBasePairProbabilities.sh # the program to build check_PROGRAMS = runApiTests @@ -84,4 +84,4 @@ runApiTests_CXXFLAGS = -I$(top_builddir)/src \ -EXTRA_DIST = tools/export-seeded-theory.py +EXTRA_DIST = tools/export-seeded-theory.py testBasePairProbabilities.py diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 530ba836..7801b8a4 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -798,3 +798,25 @@ TEST_CASE("raw pair owner excludes region crossings and invalidates partial resu REQUIRE(failed.status()==BasePairProbabilities::Status::failed); REQUIRE_THROWS(failed.finalize());REQUIRE(p2.restored()); } + +#include "SeededChainOracle.h" +#include "IntaRNA/SeedHandlerExplicit.h" +TEST_CASE("native singleton and mixed explicit seeds count overlapping witnesses once", "[BasePairProbabilities][PredictorSeedOracle]") { + #include "testEasyLoggingSetup.icc" + using namespace seeded_oracle; + RnaSequence t("t","GGGG"),q("q","CCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,2); + SeedConstraint sc(1,0,0,0,-10000,0,-10000,IndexRangeList(),IndexRangeList(),"1|&4|,2||&2||",true,true,true); + for(bool noLP:{false,true}) { + BasePairProbabilities result(4,4);auto oc=makeOutputConstraint(noLP);OutputHandlerInteractionList output(oc,1); + ExactSeedProbe p(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),&result); + p.predict();result.finalize(); + Model model{4,4,4,4,noLP,{{{0,0}},{{1,1},{2,2}}},[](Pair){return true;}, + [](Pair){return std::exp(1.L);},[](Pair,Pair){return std::exp(1.L);},[](Pair,Pair){return 1.L;}}; + auto ref=enumerate(model); + REQUIRE(double(result.getZ())==Approx(double(ref.z)).epsilon(2e-12)); + for(size_t i=0;i<4;++i) for(size_t j=0;j<4;++j) + REQUIRE(double(result.rawMasses()(i,3-j))==Approx(double(ref.mass[{i,j}])).epsilon(2e-12)); + } +} diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index 068c638d..2e1a57c2 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -48,3 +48,25 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") SeedHandlerExplicit eb(energy,explicitB); REQUIRE_FALSE(eb.guaranteesStackOnlySeeds()); } + +TEST_CASE("stack-domain admission preserves handler-specific threshold equality", "[StackSeedDomain]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGG"),q("q","CCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair e(at,ar); + for(bool hybrid:{false,true}) { + SeedConstraint sc(2,0,0,0,hybrid?E_INF:-200,Accessibility::ED_UPPER_BOUND,hybrid?-200:E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + SeedHandlerMfe mfe(e,sc);SeedHandlerNoBulge nb(e,sc); + REQUIRE(mfe.fillSeed(0,2,0,2)==4); + REQUIRE(nb.fillSeed(0,2,0,2)==0); + REQUIRE(StackSeedDomain(mfe,3,3,3,3).maxSeedLength()==2); + REQUIRE(StackSeedDomain(nb,3,3,3,3).maxSeedLength()==0); + } + SeedConstraint exactED(2,0,0,0,E_INF,0,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + SeedHandlerMfe mfe(e,exactED);SeedHandlerNoBulge nb(e,exactED); + REQUIRE(mfe.fillSeed(0,2,0,2)==0);REQUIRE(nb.fillSeed(0,2,0,2)==0); + // Explicit seeds ignore these computed-seed admission thresholds. + SeedConstraint explicitC(1,0,0,0,-10000,0,-10000,IndexRangeList(),IndexRangeList(),"1|&3|,2||&1||",true,true,true); + SeedHandlerExplicit ex(e,explicitC);REQUIRE(ex.fillSeed(0,2,0,2)==2); + REQUIRE(StackSeedDomain(ex,3,3,3,3).maxSeedLength()==2); +} diff --git a/tests/runBasePairProbabilities.sh b/tests/runBasePairProbabilities.sh new file mode 100644 index 00000000..be6d002c --- /dev/null +++ b/tests/runBasePairProbabilities.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +python3 "$script_dir/testBasePairProbabilities.py" "$INTARNABINPATH/src/bin/IntaRNA" diff --git a/tests/testBasePairProbabilities.py b/tests/testBasePairProbabilities.py new file mode 100644 index 00000000..6ae33bba --- /dev/null +++ b/tests/testBasePairProbabilities.py @@ -0,0 +1,117 @@ +"""Native CLI checks with an independent exhaustive base-pair-energy oracle.""" +import csv +import gzip +import math +from pathlib import Path +import subprocess +import sys +import tempfile + +BIN = sys.argv[1] +COMMON = ['--energy=B', '--acc=N', '--model=P', '--mode=M', '--seedBP=2', + '--threads=1', '--outOverlap=B', '--outNoLP=false', '--outNoGUend=false', + '--outMode=C', '--outCsvCols=Zall,Eall', '--default-log-file=/dev/null'] + +def run(t='GGAGGG', q='CCCC', options=(), success=True): + keys = {o.split('=')[0] for o in options} + if any(o.startswith('--outMode=') and o!='--outMode=C' for o in options): + keys.add('--outCsvCols') + common = [o for o in COMMON if o.split('=')[0] not in keys] + p = subprocess.run([BIN, f'--target={t}', f'--query={q}', *common, *options], + text=True, capture_output=True) + assert (p.returncode == 0) == success, (p.args, p.returncode, p.stdout, p.stderr) + return p + +def matrix(path): + with (gzip.open(path, 'rt') if str(path).endswith('.gz') else open(path)) as f: + rows = list(csv.reader(f, delimiter=';')) + assert rows[0][0] == 'bpProb', rows + return rows[0][1:], [r[0] for r in rows[1:]], [[None if v=='NA' else float(v) for v in r[1:]] for r in rows[1:]] + +def oracle(t, q, regions=None, no_lp=False, seeds=None): + # Both internal coordinates increase; only printing reverses query labels. + n, m = len(t), len(q) + qr = q[::-1] + valid = {(i,j) for i in range(n) for j in range(m) if t[i]+qr[j] in ('GC','CG','AU','UA','GU','UG')} + z = 0.0 + mass = [[0.0]*m for _ in t] + if regions is None: regions = [(0,n-1,0,m-1)] + def stack(a,b): return b==(a[0]+1,a[1]+1) + def visit(path, end1, end2): + nonlocal z + admitted = any(stack(a,b) for a,b in zip(path,path[1:])) if seeds is None else any( + any(path[k:k+len(s)]==s for k in range(len(path))) for s in seeds) + lp_ok = all((k and stack(path[k-1],p)) or (k+10 for v in covered[3][1:]);assert all(v==0 for v in with_spot[2]) + compressed=d/'pairs.csv.gz';run(options=[f'--out=bpProb:{compressed}']);same(matrix(compressed)[2],values) + # Unsupported requests fail explicitly and never create a probability file. + for opts in (['--mode=H'],['--mode=S'],['--model=X','--mode=K'],['--noSeed'],['--windowWidth=3','--windowOverlap=2'],['--seedMaxUP=1'],['--seedTQ=1|.|&2||']): + fail=d/'unsupported.csv';run(options=[f'--out=bpProb:{fail}',*opts],success=False);assert not fail.exists() + fail=d/'range.csv';run('GGGG','CCCC',[f'--out=bpProb:{fail}',*seedopt,'--energyAdd=900'],success=False);assert not fail.exists() + run(options=['--out=bpProb:/dev/full'],success=False) + # Independent pairs and complete shared-stream blocks at both thread counts. + fasta=d/'targets.fa';fasta.write_text('>a\nGGGG\n>b\nGAGG\n') + for threads in (1,2): + prefix=d/f'multi{threads}.csv' + run(str(fasta),'CCCC',[f'--threads={threads}',f'--out=bpProb:{prefix}','--outNumber=0']) + files=sorted(d.glob(f'multi{threads}*.csv'));assert len(files)==2,files + for file,t in zip(files,('GGGG','GAGG')):same(matrix(file)[2],oracle(t,'CCCC')[1]) + shared=run(str(fasta),'CCCC',[f'--threads={threads}','--out=bpProb:STDOUT','--outNumber=0']) + lines=shared.stdout.splitlines();starts=[i for i,l in enumerate(lines) if l.startswith('bpProb;')] + assert len(starts)==2 and starts[1]-starts[0]==5,lines +print('base-pair probability CLI regressions passed') From 48efc541e23571ce1bab88e2754e1db7445cbc89 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 8 Oct 2026 14:47:21 +0200 Subject: [PATCH 08/30] test: finish seeded probability validation and documentation (step 7) --- .github/workflows/build.yml | 5 + ChangeLog | 19 +++ IntaRNA.pc.in | 2 +- README.md | 67 +++++++- doc/Makefile.am | 4 +- doc/analysis/base-pair-probabilities-plan.md | 16 +- .../seeded-probabilities-validation.md | 159 ++++++++++++++++++ doc/seeded-probabilities-example.cpp | 29 ++++ src/IntaRNA/OutputHandlerHub.h | 2 + src/IntaRNA/SeededPartitionFunction.cpp | 2 +- src/bin/CommandLineParsing.cpp | 10 ++ src/bin/CommandLineParsing.h | 4 + src/bin/IntaRNA.cpp | 21 +-- tests/Makefile.am | 1 + tests/OutputHandlerHub_test.cpp | 11 ++ tests/PredictorSeedOracle_test.cpp | 5 +- tests/SeededNativeBenchmark_test.cpp | 68 ++++++++ tests/SeededPartitionFunction_test.cpp | 12 +- tests/StackSeedDomain_test.cpp | 15 ++ tests/testBasePairProbabilities.py | 8 + 20 files changed, 433 insertions(+), 27 deletions(-) create mode 100644 doc/analysis/seeded-probabilities-validation.md create mode 100644 doc/seeded-probabilities-example.cpp create mode 100644 tests/SeededNativeBenchmark_test.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31f22a56..9459ab05 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -128,3 +128,8 @@ jobs: ${LDFLAGS:-} $(pkg-config --cflags --libs IntaRNA) \ -o "$RUNNER_TEMP/intarna-consumer" "$RUNNER_TEMP/intarna-consumer" + "$CXX" -std=c++23 ${CPPFLAGS:-} ${CXXFLAGS:-} doc/seeded-probabilities-example.cpp \ + ${LDFLAGS:-} $(pkg-config --cflags --libs IntaRNA) \ + -o "$RUNNER_TEMP/intarna-probabilities-consumer" + "$RUNNER_TEMP/intarna-probabilities-consumer" > "$RUNNER_TEMP/intarna-pair-probabilities.csv" + grep -q '^bpProb;' "$RUNNER_TEMP/intarna-pair-probabilities.csv" diff --git a/ChangeLog b/ChangeLog index f876d0e8..bd016930 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,10 @@ ## Technical changes and Optimizations +- validate exact pair probabilities on independent mathematical/native oracles, + release/debug suites and installed consumers; document measured runtime and + storage, and supply the required logging link dependency (issue #257, step 7) + - collect raw actual-pair masses and disjoint regional denominators with explicit successful-empty/nonempty/failed lifecycle (issue #257, step 5) @@ -152,6 +156,21 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261008 Alexander Mitrofanov + * tests, README.md, doc/analysis, doc/seeded-probabilities-example.cpp : + + finish probability regression and native performance gates, document the + selected algorithm, supported ensemble, public API and numerical limits + * CommandLineParsing, IntaRNA.cpp, OutputHandlerHub, SeededPartitionFunction : + + retain copied output policy, check dense-grid dimension products, serialize + seed-constraint initialization and emit empty matrices for too-short pairs + + preserve dynamic OpenMP exceptions and report fatal failures as errors even + with redirected warning logs; reject unsupported empty searches (PR #258) + * IntaRNA.pc.in, .github/workflows/build.yml, Makefile.am : + + link the logging dependency required by installed predictor consumers; + compile/run a real probability consumer in CI and distribute new artifacts + + GCC 14 release/debug: seven suites pass, 91,361 API assertions; all 74 + installed headers, installed consumer and source distribution checks pass + 261008 Alexander Mitrofanov * BasePairProbabilityWriter, CommandLineParsing, IntaRNA.cpp, CLI/API tests : + add bpProb matrix output, capability/mode validation, multi-FASTA naming, diff --git a/IntaRNA.pc.in b/IntaRNA.pc.in index db398781..73479143 100644 --- a/IntaRNA.pc.in +++ b/IntaRNA.pc.in @@ -6,5 +6,5 @@ includedir=${prefix}/include Name: IntaRNA Description: RNA-RNA interaction prediction C++ library Version: @VERSION@ -Libs: -L${libdir} -lIntaRNA @LIBS@ +Libs: -L${libdir} -lIntaRNA -leasylogging @LIBS@ Cflags: -I${includedir} @BOOST_CPPFLAGS@ @VRNA_CFLAGS@ diff --git a/README.md b/README.md index 3e0266d5..32f77132 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ The following topics are covered by this documentation: - [Minimal energy profiles](#profileMinE) - [Minimal energy for all intermolecular index pairs](#pairMinE) - [Spot probability profiles](#profileSpotProb) using partition functions + - [Actual base-pair probabilities](#bpProb) - [Interaction probabilities for interaction spots of interest](#spotProb) - [Accessibility and unpaired probabilities](#accessibility) - [Local versus global unpaired probabilities](#accLocalGlobal) @@ -1875,13 +1876,14 @@ targeted file/stream name: - `qSpotProb:`/`tSpotProb:` [query/target's spot probability profile](#profileSpotProb) (CSV format), respectively - `spotProb:` [all spot probabilities](#spotProb) (CSV format) +- `bpProb:` [actual base-pair probabilities](#bpProb) for exact stack-seeded ensembles (CSV matrix) - `qMinE:`/`tMinE:` [the query/target's minimal interaction energy profile](profileMinE) (CSV format), respectively - `pMinE:` [minimal interaction energy for all query-target index pairs](pairMinE) (CSV format) - `qAcc:`/`tAcc:` the [query/target's ED accessibility values](#accessibility) (RNAplfold-like format), respectively - `qPu:`/`tPu:` the [query/target's unpaired probabilities](#accessibility) (RNAplfold format; rounded!!), respectively Note, for *multiple sequences* in FASTA input, the provided file names are suffixed by -- `-t#q#` : for `*spotProb` and `*minE` output, and +- `-t#q#` : for `bpProb`, `*spotProb` and `*minE` output, and - `-s#` : for `*Acc` and `*Pu` output, where `#` denotes the according target/query sequence number within the input where numbering starts with 1. @@ -2005,6 +2007,63 @@ or `STDERR` instead of a file name. [![up](doc/figures/icon-up.28.png) back to overview](#overview)
+ + +## Actual base-pair probabilities + +`--out=bpProb:FILE` writes the probability that each target/query nucleotide pair +actually pairs, conditional on an allowed seeded interaction. It requires +`--model=P --mode=M` and a stack-only seed handler. For example: + +```sh +IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ + --model=P --mode=M --out=bpProb:pairs.csv +``` + +Every complete ordered interaction is counted once, even when it contains +several overlapping or disjoint seeds. The exact stack-seeded partition backend +is also used without `bpProb`, so requesting the matrix does not change `Zall`. +Computed seeds preserve their handler-specific admission thresholds. Explicit +stacked seeds may have different lengths, including singletons; competing +explicit patterns at the same start still retain only the selected minimum-energy +pattern. Other predictors retain their existing behavior. Heuristic, kinetic, +evaluation, seed-only, unseeded and bulged-seed `bpProb` requests are rejected. + +Rows follow the original target and columns the original query, both 5' to 3'. +The header starts with `bpProb`; labels contain each nucleotide and its display +index (including `tIdxPos0`/`qIdxPos0` shifts). Entries use sufficient significant +digits for the partition type, with scientific notation for small probabilities. +A successful empty ensemble contains `NA`; pairs absent from a nonempty ensemble +have probability zero. The output uses `outSep`, the usual multi-FASTA filename +suffixes and gzip support. `STDOUT`/`STDERR` receive synchronized complete blocks. + +The ensemble is the union of the searched, disjoint target/query region pairs. +Interactions crossing excluded region boundaries are absent. Region selection, +`outMinPu`, accessibility, seed admission, loop/span limits, `outNoLP` and +`outNoGUend` can restrict it. Ranked reporting options (`outNumber`, `outMaxE`, +`outDeltaE`, overlap selection and `outPerRegion`) do not select the probability +ensemble. Raw numerators and denominators are merged before normalization. +Model P continues to reject nonzero `windowWidth`. + +Unlike `spotProb` coverage, this output uses interior pair chains: an unpaired +bulge can have positive site coverage and zero actual-pair probability. There +is no added unbound-state weight. Individual pair events can coexist, so +`1 - sum(pair probabilities)` is not a probability of no interaction. + +The backend checks nonnegative arithmetic and reports numerical range failures +as errors, including underflow, overflow and NaN. Positive subnormals are rejected; +there is currently no scaling fallback. Failed or cancelled regional work never +produces a successful-looking probability matrix. Ordinary floating-point +rounding remains; "exact" describes the enumerated ensemble, not real arithmetic. + +For library use, keep a `BasePairProbabilities` owner alive across predictions, +pass its optional non-owning pointer to `PredictorMfeEns2dSeedExtension`, and call +`finalize()` only after all requested disjoint regions succeed. A writer is called +explicitly after finalization; its destructor publishes nothing. The output +constraint must enable `needZall`. The [standalone API example](doc/seeded-probabilities-example.cpp) +and [implementation validation report](doc/analysis/seeded-probabilities-validation.md) +show the public API and measured limits. + ### Interaction probabilities for interaction spots of interest @@ -2387,8 +2446,10 @@ Doxygen and Graphviz and run `bash doc/build-api.sh`, then open Autotools, and publishing instructions. The [seeded base-pair probability implementation plan](doc/analysis/base-pair-probabilities-plan.md) -records the proposed algorithms, numerical contracts, integration, and validation -for issue #257. It is a design document; the proposed output is not implemented. +records the reviewed algorithms, numerical contracts, integration, and validation +for issue #257. The implementation selects the suffix backend after the +[measured kernel comparison](doc/analysis/seeded-kernel-comparison.md); +[actual base-pair output](#bpProb) is available for exact stack-seeded ensembles. When IntaRNA is build while `pkg-config` is present, according pkg-config information is generated and installed too. diff --git a/doc/Makefile.am b/doc/Makefile.am index 4092731b..9d47b6ab 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -6,7 +6,9 @@ EXTRA_DIST = \ conda.txt \ analysis/base-pair-probabilities-plan.md \ - analysis/seeded-kernel-comparison.md \ + analysis/seeded-kernel-comparison.md \ + analysis/seeded-probabilities-validation.md \ + seeded-probabilities-example.cpp \ api-documentation.md \ build-api.sh \ check-api.py \ diff --git a/doc/analysis/base-pair-probabilities-plan.md b/doc/analysis/base-pair-probabilities-plan.md index 7080047e..c682f943 100644 --- a/doc/analysis/base-pair-probabilities-plan.md +++ b/doc/analysis/base-pair-probabilities-plan.md @@ -1,7 +1,11 @@ # Implementation plan: seeded base-pair probabilities -Status: design only. This document addresses [issue #257](https://github.com/BackofenLab/IntaRNA/issues/257); -it does not implement the recurrences, proposed APIs, or proposed output options. +Status: implemented in the seven milestone commits of [PR #258](https://github.com/BackofenLab/IntaRNA/pull/258). +This document preserves the reviewed design for [issue #257](https://github.com/BackofenLab/IntaRNA/issues/257). +The [kernel comparison](seeded-kernel-comparison.md) selected the suffix automaton +for production; sections 6–7 below describe the retained test-only maximal-stack +candidate. See [implementation validation](seeded-probabilities-validation.md) +for the delivered API, tests, measurements and numerical limits. The code assessment uses IntaRNA revision [`5454030a04bbcde79b2e75a0614dadf6f1115e6f`](https://github.com/BackofenLab/IntaRNA/tree/5454030a04bbcde79b2e75a0614dadf6f1115e6f). @@ -490,7 +494,7 @@ seed-locality and memory cases used in the early algorithm comparison. ## 11. Implementation sequence and affected components -Each milestone has an exit condition; none is implemented by this document. +Each milestone has an exit condition and a separate implementation commit in PR #258. | Order | Work | Main files/components | Exit condition | | --- | --- | --- | --- | @@ -514,6 +518,6 @@ Record toolchain, options, inputs, timings, peak memory, and numerical differenc An intentionally corrected partition can change ensemble rankings; justify those changes against the oracle instead of regenerating expected files blindly. -For this documentation-only plan, validate references, equations, pseudocode -contracts, Markdown structure, distribution registration, and `git diff --check`. -No C++ implementation build or performance result is claimed by this plan. +The original plan was validated as documentation only. Implementation builds, +independent numerical checks and performance measurements are recorded in the +linked validation report. diff --git a/doc/analysis/seeded-probabilities-validation.md b/doc/analysis/seeded-probabilities-validation.md new file mode 100644 index 00000000..a08848d0 --- /dev/null +++ b/doc/analysis/seeded-probabilities-validation.md @@ -0,0 +1,159 @@ +# Exact seeded probabilities: implementation and validation + +PR #258 implements the seven steps of the reviewed plan in separate commits. +The library ships the specialized suffix automaton; maximal stacks remain a +correctness/performance comparison in the tests. See the [kernel decision](seeded-kernel-comparison.md) +for measured tradeoffs and exact cell-storage counts. + +## Delivered contract + +- Stack-only capability is geometric and forwarded through index offsets. Active + occurrences retain each handler's admission and per-start selection policies; + both endpoints and inclusive span limits are checked. +- Seed-free suffix lengths and two seeded run-length states count ordered chains + once. Whole-interaction noLP forbids departure/termination of singleton runs. + End GU and full-span ED filters affect termination, not intermediate states. +- Initiation, successive-pair loop energies and the complete boundary coefficient + own their contributions once. The complete coefficient is shared with outside + initialization and virtual `updateZ` dispatch. Overrides can observe/delegate; + reweighting belongs in `exactBoundaryWeight`, which serves both passes. Changing + the hybrid mass through an old update override is rejected. +- The pair sink is non-owning. Each prediction creates private region state and + converts internal pairs once through `getBasePair`. The accumulator rejects + intersecting rectangles, validates every merge before mutation, and requires + explicit finalization. CLI ownership spans all regions of one sequence pair. + Cancellation/failure guards apply before output, including the OpenMP path. +- `bpProb` is independent of coverage trackers and ranked reporting. Pair-only + output does not retain `Z_partition`. Simultaneous legacy trackers retain their + existing callbacks/map and native energy rounding semantics. + +## Numerical limits + +Arithmetic uses `Z_type`, exact zero and checked nonnegative sums/products, +exponentials, division and region merges. Structural forbidden energies become +zero before exponentiation. NaN, infinity, positive underflow and positive +subnormals are errors. No scaling is implemented. Ordinary rounding loss in a +sum with widely different terms remains possible. Final row/column exclusivity +and pair mass bounds are checked; endpoint correction is limited to +`512 * epsilon(Z_type) * max(1, targetLength + queryLength)` above one. + +Independent tests use a 2e-12 relative tolerance for raw double-precision totals, +boundaries and masses against long-double enumeration and 50-digit theory gold +values. The CLI base-pair-energy oracle also uses a 2e-14 absolute tolerance for +printed zeros/small values. Native integer ED is preserved and is not compared +against unrounded theoretical ED as though they were identical models. + +## Memory + +Beyond the active suffix grids documented in the kernel comparison, pair output +holds one full target-by-query raw matrix, the private regional outside matrix, +and briefly a regional matrix in original coordinates. Explicit finalization and +writing allocate a normalized full matrix; writing also buffers the complete CSV +block. These phases are sequential, not all simultaneous. Native accessibility +and retained seed preprocessing are additional costs. Legacy trackers add their +own coverage matrices and complete-boundary map. No global run tape or global +boundary-weight table is retained. Without pair output, no pair matrix or reverse +state is allocated. + +## Reproducible checks + +Use GCC 14 with C++23, Boost, ViennaRNA and the bundled Kokkos mdspan fallback: + +```sh +bash autotools-init.sh +./configure --disable-debug # dependency prefixes as in AGENTS.md when needed +make -j2 +make tests -j2 +# Repeat in a clean source/build directory with --enable-debug. +``` + +The focused tags are `[SeededPartitionFunction]`, `[StackSeedDomain]`, +`[BasePairProbabilities]`, `[PredictorSeedOracle]` and `[PredictorMfeEns]`. +`tests/runBasePairProbabilities.sh` checks CLI behavior with a separate Python +chain enumerator. `tests/tools/export-seeded-theory.py` reproduces all ten +fixture extractions from the cited theory revision. No legacy CLI golden file +is regenerated for this change. + +Final local verification used GCC 14.4.0, ViennaRNA 2.7.2, Boost 1.85, +Z_type=double and Kokkos mdspan on Linux x86-64. Release and debug each passed all +seven Autotools test suites; the API suite passed 91,361 assertions in 85 cases. +The initial incremental run exposed stale objects after the virtual interface +change; clean release/debug rebuilds passed. No failing golden files were replaced. + +All 74 installed public headers compiled independently. The standalone probability +consumer compiled, linked through installed pkg-config metadata, and produced the +expected matrix. This check exposed a missing `-leasylogging` dependency in the +metadata; it is fixed, and CI now builds/runs this predictor consumer on Linux +and macOS. `make dist` includes every new source, fixture, test, example and report. +Apple Clang execution was not available locally; its existing CI job remains the +platform gate. + +The final integration checks also cover too-short sequences as successful empty +matrices, dynamic OpenMP exception propagation and visible fatal diagnostics even +when a log file is selected. Seed-constraint lazy initialization is synchronized +for concurrent sequence pairs. Pure header/Matrix consumers did not previously +exercise the logging dependency; the new consumer calls a real predictor. + +## Native measurements + +The hidden `[SeededNativeBenchmark]` case uses the production predictor, native +ViennaRNA transition energies, inclusive spans of 30, loop limits three and a +four-pair computed stack seed. OxyS/fhlA accessibility is ViennaRNA-derived; +synthetic cases disable accessibility. Inputs and the timing adapter are committed +in `tests/SeededNativeBenchmark_test.cpp`. Set `INTARNA_BENCH_CASE` and +`INTARNA_BENCH_OUTSIDE=0|1` to measure an individual case in a fresh process. + +```sh +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 /usr/bin/time -v \ + ./tests/runApiTests '[SeededNativeBenchmark]' +``` + +Milliseconds from one controlled single-thread run; "predictor" excludes measured +seed filling but includes active-domain construction, merging and finalization. +The added outside cost is the difference between the two predictor columns; it +includes pair-result allocation/normalization, not just differentiation. + +| Input | Accessibility | Seed filling (Z / pairs) | Predictor Z | Predictor Z+pairs | Z | +| --- | ---: | ---: | ---: | ---: | ---: | + +| none | 0.005 | 0.024 / 0.019 | 0.010 | 0.198 | 0 | +| sparse100 | 0.000 | 0.074 / 0.028 | 0.751 | 1.488 | 1.702e+15 | +| sparse400 | 0.000 | 0.308 / 0.288 | 0.712 | 5.621 | 1.702e+15 | +| dense | 0.000 | 0.296 / 0.304 | 413.301 | 972.852 | 2.00398e+68 | +| OxyS-fhlA | 27.627 | 0.180 / 0.178 | 53.376 | 134.278 | 37643.8 | + +Raw Z was bitwise identical with pair output off/on for every measured case. No +representative measured case exceeded the checked numerical range. Increasing +unrelated sparse-sequence length from 100 to 400 leaves partition DP work nearly +constant; seed preprocessing and full-matrix pair output still grow. These costs +are reported separately rather than attributed to the recurrence. + +Fresh-process peak RSS (KiB, includes the executable, native libraries, +accessibility and allocator overhead; small differences include measurement noise): + +| Input | Z only | Z+pairs | +| --- | ---: | ---: | +| sparse100 | 15,436 | 15,376 | +| sparse400 | 15,436 | 18,688 | +| dense | 15,560 | 16,000 | +| OxyS-fhlA | 17,784 | 17,836 | + +Full CLI measurement (including startup, accessibility, computation, normalization +and file output) used the repository's `doc/handson/fhlA.fasta` and `OxyS.fasta`: + +```sh +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 /usr/bin/time -v src/bin/IntaRNA \ + --target=doc/handson/fhlA.fasta --query=doc/handson/OxyS.fasta \ + --model=P --mode=M --seedBP=4 --intLenMax=30 \ + --qIntLoopMax=3 --tIntLoopMax=3 --threads=1 \ + --outNoLP=false --outNoGUend=false --outNumber=0 --outMode=E \ + --default-log-file=/dev/null --out=ensemble.txt +# Repeat with --out=bpProb:pairs.csv. +``` + +- Without pair matrix: median 0.09 s over three runs, maximum observed RSS 16,916 KiB. +- With pair matrix: median 0.16 s over three runs, maximum observed RSS 16,788 KiB. + +Both runs report Eall=-6.52 kcal/mol with the native presentation convention. +These measurements quantify added output work; they are not a speedup claim over +the legacy seeded ensemble, whose multi-anchor partition is a different result. diff --git a/doc/seeded-probabilities-example.cpp b/doc/seeded-probabilities-example.cpp new file mode 100644 index 00000000..eb63e832 --- /dev/null +++ b/doc/seeded-probabilities-example.cpp @@ -0,0 +1,29 @@ +// Standalone installed-library example; see README.md#bpProb. +#include +#include +#include +#include +#include +#include +#include + +INITIALIZE_EASYLOGGINGPP + +int main() { + using namespace IntaRNA; + RnaSequence target("target", "GGAGGG"), query("query", "CCCC"); + AccessibilityDisabled targetAcc(target, 0, nullptr), queryAcc(query, 0, nullptr); + ReverseAccessibility reversed(queryAcc); + InteractionEnergyBasePair energy(targetAcc, reversed); + SeedConstraint seeds(2, 0, 0, 0, E_INF, Accessibility::ED_UPPER_BOUND, + E_INF, IndexRangeList(), IndexRangeList(), "", false, false, false); + OutputConstraint constraints(1, OutputConstraint::OVERLAP_BOTH, E_INF, E_INF, + false, false, false, true, false); // needZall=true; needBPs=false + OutputHandlerInteractionList output(constraints, 1); + BasePairProbabilities probabilities(target.size(), query.size()); + PredictorMfeEns2dSeedExtension predictor(energy, output, nullptr, + new SeedHandlerNoBulge(energy, seeds), &probabilities); + predictor.predict(); + probabilities.finalize(); + BasePairProbabilityWriter::write(std::cout, probabilities, target, query); +} diff --git a/src/IntaRNA/OutputHandlerHub.h b/src/IntaRNA/OutputHandlerHub.h index e711627e..ac5e4cb2 100644 --- a/src/IntaRNA/OutputHandlerHub.h +++ b/src/IntaRNA/OutputHandlerHub.h @@ -160,6 +160,7 @@ ::OutputHandlerHub( const OutputHandlerHub & toCopy ) // dont delete on destruction (should be done by original) to avoid double cleanup , deleteOutListOnDestruction(false) { + exactPartition=toCopy.exactPartition; } ///////////////////////////////////////////////////////////////////////// @@ -298,6 +299,7 @@ operator= ( const OutputHandlerHub & toCopy) outList.clear(); // copy data + exactPartition=toCopy.exactPartition; outList = toCopy.outList; // ensure this copy doesnt delete the list content deleteOutListOnDestruction = false; diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index 2caa278c..63e2007f 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -20,7 +20,7 @@ SeededPartitionFunction::Result SeededPartitionFunction::compute( seeds.forEachStart([&](size_t p1,size_t p2) { const Pair p{p1,p2}; if (!w.valid(p)) return; const size_t n=std::min(d.span1,d.n-p1),m=std::min(d.span2,d.m-p2); - Matrix h(n*m,states),dh; + Matrix h(matrix_detail::product(n,m),states),dh; Matrix> total(n,m),dt; Matrix valid(n,m); Matrix ending(n,m); diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index 8a1518e7..dc864b8c 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -69,6 +69,7 @@ extern "C" { #include "IntaRNA/SeedHandlerMfe.h" #include "IntaRNA/BasePairProbabilityWriter.h" +#include #include "IntaRNA/SeedHandlerNoBulge.h" #include "IntaRNA/OutputStreamHandlerSortedCsv.h" @@ -2491,6 +2492,13 @@ getTemperature() const { bool CommandLineParsing::hasBasePairProbabilityOutput() const { return !outPrefix2streamName.at(OutPrefixCode::OP_bpProb).empty(); } +BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const InteractionEnergy & energy) const { + if (!hasBasePairProbabilityOutput()) return nullptr; + std::unique_ptr handler(getSeedHandler(energy)); + if (!handler->guaranteesStackOnlySeeds()) + throw std::invalid_argument("bpProb requires a stack-only seed handler; bulged seeds are unsupported"); + return new BasePairProbabilities(energy.size1(),energy.size2()); +} void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const { const auto & t=energy.getAccessibility1().getSequence(); const auto & q=energy.getAccessibility2().getAccessibilityOrigin().getSequence(); @@ -2787,6 +2795,8 @@ const SeedConstraint & CommandLineParsing:: getSeedConstraint( const InteractionEnergy & energy ) const { + static std::mutex seedInitialization; + const std::lock_guard lock(seedInitialization); if (seedConstraint == NULL) { // setup according to user data seedConstraint = new SeedConstraint( diff --git a/src/bin/CommandLineParsing.h b/src/bin/CommandLineParsing.h index efcc3c11..fff52480 100644 --- a/src/bin/CommandLineParsing.h +++ b/src/bin/CommandLineParsing.h @@ -291,6 +291,10 @@ class CommandLineParsing { /** @return whether actual base-pair matrix output was requested */ bool hasBasePairProbabilityOutput() const; + /** Allocate a pair result after validating the actual seed capability. + * @return owned result or nullptr when pair output is not requested + */ + BasePairProbabilities * getBasePairProbabilityResult(const InteractionEnergy & energy) const; /** Publish a successful sequence-pair result using ordinary filename rules. */ void writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const; diff --git a/src/bin/IntaRNA.cpp b/src/bin/IntaRNA.cpp index 15d02b8c..bb971e29 100644 --- a/src/bin/IntaRNA.cpp +++ b/src/bin/IntaRNA.cpp @@ -137,7 +137,7 @@ int main(int argc, char **argv){ { if (!threadAborted) { // store exception information - exceptionPtrDuringOmp = std::make_exception_ptr(e); + exceptionPtrDuringOmp = std::current_exception(); exceptionInfoDuringOmp <<" #thread "< pairProbabilities; - if (parameters.hasBasePairProbabilityOutput()) - pairProbabilities=std::make_unique(energy->size1(),energy->size2()); + std::unique_ptr pairProbabilities(parameters.getBasePairProbabilityResult(*energy)); // run prediction for all range combinations for(const IndexRange & tRange : parameters.getTargetRanges(*energy, targetNumber, *targetAcc)) { @@ -309,7 +307,7 @@ int main(int argc, char **argv){ { if (!threadAborted) { // store exception information - exceptionPtrDuringOmp = std::make_exception_ptr(e); + exceptionPtrDuringOmp = std::current_exception(); exceptionInfoDuringOmp <<" #thread "< Please report (including input) to the IntaRNA development team! Thanks!\n"; + LOG(ERROR) < Please report (including input) to the IntaRNA development team! Thanks!\n"; el::Loggers::flushAll(); return -1; diff --git a/tests/Makefile.am b/tests/Makefile.am index 0a0f3e62..5a919fb1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,6 +27,7 @@ runApiTests_SOURCES = \ SeededTheoryFixtures.h \ SeededStackKernel.h \ SeededPartitionFunction_test.cpp \ + SeededNativeBenchmark_test.cpp \ StackSeedDomain_test.cpp \ BasePairProbabilities_test.cpp \ testEasyLoggingSetup.icc \ diff --git a/tests/OutputHandlerHub_test.cpp b/tests/OutputHandlerHub_test.cpp index 508eadfb..c83a7496 100644 --- a/tests/OutputHandlerHub_test.cpp +++ b/tests/OutputHandlerHub_test.cpp @@ -49,3 +49,14 @@ TEST_CASE("OutputHandlerHub reports the largest child count", "[OutputHandlerHub REQUIRE(second.reported() == 2); REQUIRE(hub.reported() == 2); } + +TEST_CASE("exact-zero partition status follows copied hubs and new children", "[OutputHandlerHub]") { + OutputConstraint constraint; + CountingOutputHandler output(constraint),other(constraint); + OutputHandlerHub hub(constraint,false);hub.addOutputHandler(&output);hub.setExactPartition(true); + hub.incrementZ(1e-100); + REQUIRE(hub.hasNonzeroZ()); + OutputHandlerHub copy(hub);REQUIRE(copy.usesExactPartition());REQUIRE(copy.hasNonzeroZ()); + copy.addOutputHandler(&other);REQUIRE(other.usesExactPartition()); + OutputHandlerHub assigned(constraint,false);assigned=hub;REQUIRE(assigned.usesExactPartition()); +} diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 7801b8a4..27779cc9 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -670,6 +670,7 @@ TEST_CASE("tiny exhaustive seed oracle for exact predictor families", } #include "IntaRNA/AccessibilityBasePair.h" +#include "IntaRNA/AccessibilityVrna.h" #include "IntaRNA/InteractionEnergyVrna.h" #include "IntaRNA/SeedHandlerNoBulge.h" #include "IntaRNA/OutputHandlerHub.h" @@ -717,7 +718,9 @@ TEST_CASE("native exact stacked partitions agree beyond the unique-anchor domain ReverseAccessibility ar(aq); InteractionEnergyBasePair simple(at,ar,2,1); VrnaHandler vrna; - InteractionEnergyVrna nearest(at,ar,vrna,2,1); + AccessibilityVrna nativeTarget(t,6,nullptr,vrna),nativeQuery(q,5,nullptr,vrna); + ReverseAccessibility nativeReverse(nativeQuery); + InteractionEnergyVrna nearest(nativeTarget,nativeReverse,vrna,2,1); for(bool noLP:{false,true}) for(bool noGU:{false,true}) { checkNativeStackPartition(simple,noLP,noGU); checkNativeStackPartition(nearest,noLP,noGU); diff --git a/tests/SeededNativeBenchmark_test.cpp b/tests/SeededNativeBenchmark_test.cpp new file mode 100644 index 00000000..14439244 --- /dev/null +++ b/tests/SeededNativeBenchmark_test.cpp @@ -0,0 +1,68 @@ +#include "catch.hpp" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/AccessibilityVrna.h" +#include "IntaRNA/InteractionEnergyVrna.h" +#include "IntaRNA/SeedHandlerNoBulge.h" +#include "IntaRNA/PredictorMfeEns2dSeedExtension.h" +#include "IntaRNA/OutputHandlerInteractionList.h" +#include +#include +#include +#include + +using namespace IntaRNA; +namespace { +using Clock=std::chrono::steady_clock; +double elapsed(Clock::time_point start) { return std::chrono::duration(Clock::now()-start).count(); } +class TimedStackSeeds:public SeedHandlerNoBulge { + double & ms; +public: + TimedStackSeeds(const InteractionEnergy & e,const SeedConstraint & c,double & ms):SeedHandlerNoBulge(e,c),ms(ms) {} + size_t fillSeed(size_t i,size_t j,size_t k,size_t l) override { + auto start=Clock::now();auto count=SeedHandlerNoBulge::fillSeed(i,j,k,l);ms=elapsed(start);return count; + } +}; +} +TEST_CASE("native seeded preprocessing partition and outside benchmark", "[.][SeededNativeBenchmark]") { + #include "testEasyLoggingSetup.icc" + for(std::string name:{"none","sparse100","sparse400","dense","OxyS-fhlA"}) { + const char * requested=std::getenv("INTARNA_BENCH_CASE"); + if(requested && name!=requested) continue; + const size_t n=name=="sparse400"?400:100; + std::string target(n,'A'),query(n,'A'); + if(name=="sparse100" || name=="sparse400") { target.replace(n/2,8,8,'G');query.replace(n/2,8,8,'C'); } + if(name=="dense") { target=std::string(40,'G');query=std::string(40,'C'); } + if(name=="OxyS-fhlA") { target="AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGAC";query="GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCG"; } + RnaSequence t("target",target),q("query",query); + VrnaHandler vrna; + auto start=Clock::now(); + std::unique_ptr at,aq; + if(name=="OxyS-fhlA") { + at=std::make_unique(t,30,nullptr,vrna,0); + aq=std::make_unique(q,30,nullptr,vrna,0); + } else { + at=std::make_unique(t,30,nullptr); + aq=std::make_unique(q,30,nullptr); + } + const double accMs=elapsed(start); + ReverseAccessibility ar(*aq); + InteractionEnergyVrna energy(*at,ar,vrna,3,3); + SeedConstraint sc(4,0,0,0,0,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + Z_type reference=0; + for(bool pairs:{false,true}) { + const char * mode=std::getenv("INTARNA_BENCH_OUTSIDE"); + if(mode && pairs!=(std::string(mode)=="1")) continue; + OutputConstraint oc(0,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,false,false,true,false); + OutputHandlerInteractionList output(oc,0); + std::unique_ptr result; + start=Clock::now(); + if(pairs) result=std::make_unique(t.size(),q.size()); + double seedMs=0; + PredictorMfeEns2dSeedExtension p(energy,output,nullptr,new TimedStackSeeds(energy,sc,seedMs),result.get()); + p.predict();if(result) result->finalize(); + const double ms=elapsed(start); + if(!pairs) reference=p.getZall();else if(!mode) REQUIRE(p.getZall()==reference); + std::cout<::min(),Z_type(.1)),std::range_error); + REQUIRE_THROWS_AS(A::divide(std::numeric_limits::min(),std::numeric_limits::max()),std::range_error); REQUIRE_THROWS_AS(A::check(std::numeric_limits::quiet_NaN()),std::range_error); REQUIRE_THROWS_AS(A::add(std::numeric_limits::max(),std::numeric_limits::max()),std::range_error); } diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index 2e1a57c2..454e7210 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -70,3 +70,18 @@ TEST_CASE("stack-domain admission preserves handler-specific threshold equality" SeedHandlerExplicit ex(e,explicitC);REQUIRE(ex.fillSeed(0,2,0,2)==2); REQUIRE(StackSeedDomain(ex,3,3,3,3).maxSeedLength()==2); } + +TEST_CASE("explicit stack capability inspects the retained minimum-energy pattern", "[StackSeedDomain]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGGG"),q("q","CCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair e(at,ar); + SeedConstraint sc(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1|.|&2|.|,1|||&2|||",false,false,false); + SeedHandlerExplicit handler(e,sc); + REQUIRE(handler.guaranteesStackOnlySeeds()); + REQUIRE(handler.fillSeed(0,3,0,3)==1); + StackSeedDomain domain(handler,4,4,4,4); + REQUIRE(domain.seedLength(0,0)==3); + REQUIRE(domain.maxSeedLength()==3); // constraint's minimum BP is only two +} diff --git a/tests/testBasePairProbabilities.py b/tests/testBasePairProbabilities.py index 6ae33bba..1c5f8c8d 100644 --- a/tests/testBasePairProbabilities.py +++ b/tests/testBasePairProbabilities.py @@ -20,6 +20,8 @@ def run(t='GGAGGG', q='CCCC', options=(), success=True): p = subprocess.run([BIN, f'--target={t}', f'--query={q}', *common, *options], text=True, capture_output=True) assert (p.returncode == 0) == success, (p.args, p.returncode, p.stdout, p.stderr) + if not success: + assert p.returncode>0 and 'ERROR' in p.stdout+p.stderr, (p.returncode,p.stdout,p.stderr) return p def matrix(path): @@ -86,13 +88,19 @@ def predict(t='GGAGGG',q='CCCC',extra=()): tiny,tiny_values=predict('GGGG','CCCC',seedopt+['--energyAdd=100','--outMaxE=999']) same(tiny_values,single) ztext,etext=tiny.stdout.splitlines()[1].split(';');assert 00 ensemble=run('GGGG','CCCC',seedopt+['--energyAdd=100','--outMode=E']) assert 'Eall 0.00\n' not in ensemble.stdout + # Explicit GU seed admission is separate from complete-interaction GU ends. + _,gu=predict('GGG','CUC',['--seedTQ=2|&2|','--seedNoGU','--seedNoGUend','--outNoGUend']) + same(gu,[[0,0,1],[0,1,0],[1,0,0]]) # Labels use original sequence order and signed display shifts that skip zero. _,normal=predict('GAGGG','CCUCC') _,shifted=predict('GAGGG','CCUCC',['--tIdxPos0=-2','--qIdxPos0=5']) cols,rows,_=matrix(path);assert cols==['C_5','C_6','U_7','C_8','C_9'];assert rows==['G_-2','A_-1','G_1','G_2','G_3'];same(normal,shifted) _,empty=predict('AAAA','AAAA');assert all(v is None for row in empty for v in row) + _,short=predict('G','C');assert short==[[None]] # Coverage and actual pairing differ at the unpaired A bulge. spot=d/'spot.csv';_,with_spot=predict(extra=[f'--out=spotProb:{spot}']) same(with_spot,values) From 20af34770ceb0c76bcaadd31184ee34f02bc43fe Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 11:59:50 +0200 Subject: [PATCH 09/30] feat: add SVG base-pair probability plots (step 8) --- ChangeLog | 19 ++ README.md | 60 ++++- src/IntaRNA/BasePairProbabilities.cpp | 10 +- src/IntaRNA/BasePairProbabilities.h | 15 +- src/IntaRNA/BasePairProbabilitySvg.cpp | 210 ++++++++++++++++++ src/IntaRNA/BasePairProbabilityWriter.cpp | 8 +- src/IntaRNA/BasePairProbabilityWriter.h | 20 ++ src/IntaRNA/Makefile.am | 1 + .../PredictorMfeEns2dSeedExtension.cpp | 9 +- src/bin/CommandLineParsing.cpp | 21 +- src/bin/CommandLineParsing.h | 4 +- tests/BasePairProbabilities_test.cpp | 41 ++++ tests/Makefile.am | 2 +- tests/runBasePairProbabilities.sh | 1 + tests/testBasePairProbabilitySvg.py | 157 +++++++++++++ 15 files changed, 559 insertions(+), 19 deletions(-) create mode 100644 src/IntaRNA/BasePairProbabilitySvg.cpp create mode 100644 tests/testBasePairProbabilitySvg.py diff --git a/ChangeLog b/ChangeLog index bd016930..d04228f5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,10 @@ ## Interface and handling +- --out=bpsvg:FILE renders actual-pair probabilities with nucleotide/accessibility + frames, a shared CSS palette, signed-coordinate guides, hover values and admitted + seed outlines (PR #258, step 8) + - --out=bpProb:FILE writes actual-pair conditional probabilities for exact model P/mode M stack-seed ensembles, with disjoint-region merging, original coordinates and success-only publication (issue #257, implementation step 6) @@ -156,6 +160,21 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * BasePairProbabilityWriter, BasePairProbabilitySvg, CommandLineParsing : + + add success-only SVG dot plots on files, gzip and synchronized standard + streams; share CSV normalization and use original sequence coordinates + + show sequence labels, 1-nt accessibility, seven CSS probability classes, + legends, signed 10/50-nt guides, missing-zero boundary and hover values + * BasePairProbabilities, PredictorMfeEns2dSeedExtension : + + optionally retain admitted seed-cell masks with completed regional masses; + annotate seed cells without changing probability or ranked-output semantics + * tests, README.md, src/IntaRNA/Makefile.am, tests/Makefile.am : + + document the SVG contract and register the writer and regression coverage + for coordinates, seed admission, accessibility, empty/failed results and I/O + + investigate the referenced dev-frank profile tracker files: their current + versions contain profile output but no SVG implementation (PR #258 step 8) + 261008 Alexander Mitrofanov * tests, README.md, doc/analysis, doc/seeded-probabilities-example.cpp : + finish probability regression and native performance gates, document the diff --git a/README.md b/README.md index 32f77132..fcb80537 100644 --- a/README.md +++ b/README.md @@ -1876,6 +1876,7 @@ targeted file/stream name: - `qSpotProb:`/`tSpotProb:` [query/target's spot probability profile](#profileSpotProb) (CSV format), respectively - `spotProb:` [all spot probabilities](#spotProb) (CSV format) +- `bpsvg:` [SVG base-pair probability dot plot](#bpsvg), with accessibility frames and seed outlines - `bpProb:` [actual base-pair probabilities](#bpProb) for exact stack-seeded ensembles (CSV matrix) - `qMinE:`/`tMinE:` [the query/target's minimal interaction energy profile](profileMinE) (CSV format), respectively - `pMinE:` [minimal interaction energy for all query-target index pairs](pairMinE) (CSV format) @@ -1883,7 +1884,7 @@ targeted file/stream name: - `qPu:`/`tPu:` the [query/target's unpaired probabilities](#accessibility) (RNAplfold format; rounded!!), respectively Note, for *multiple sequences* in FASTA input, the provided file names are suffixed by -- `-t#q#` : for `bpProb`, `*spotProb` and `*minE` output, and +- `-t#q#` : for `bpProb`, `bpsvg`, `*spotProb` and `*minE` output, and - `-s#` : for `*Acc` and `*Pu` output, where `#` denotes the according target/query sequence number within the input where numbering starts with 1. @@ -2064,6 +2065,63 @@ constraint must enable `needZall`. The [standalone API example](doc/seeded-proba and [implementation validation report](doc/analysis/seeded-probabilities-validation.md) show the public API and measured limits. + + +### SVG base-pair probability dot plots + +`--out=bpsvg:FILE` renders the same actual-pair probabilities as `bpProb` in a +standalone SVG. Both outputs can be requested together. The same exact +`--model=P --mode=M` stack-seed restrictions, regional ensemble, normalization +and success-only publication rules apply. + +```sh +IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ + --model=P --mode=M --out=bpsvg:pairs.svg --out=bpProb:pairs.csv +``` + +The query is the horizontal axis and the target the vertical axis, both in +original 5' to 3' order. Sequence names label the axes; nucleotides appear on all +four sides. Their colored backgrounds show **single-nucleotide unpaired +probabilities**, computed from the prediction accessibility model's opening +energies. These are distinct from the interaction-conditional probabilities in +the matrix. Disabled accessibility gives probability one at unconstrained bases. +Each probability rectangle has a hover title with its index (pair), type and +full-precision value. An empty interaction ensemble uses gray `NA` cells while +its accessibility frame remains defined. + +A shared seven-step, white-to-dark-blue palette encodes both probability types: +`[0,0.01)`, `[0.01,0.1)`, `[0.1,0.25)`, `[0.25,0.5)`, `[0.5,0.75)`, +`[0.75,0.9)`, and `[0.9,1]`. The legend appears below the plot. Edit the `.p0` +through `.p6` fill rules in the SVG's ` +)SVG"; + out<<"\n"; + auto text=[&](double tx,double ty,const std::string & value,const std::string & cls,const char * anchor="middle") { + out<<""<\n"; + }; + text(width/2,30,"Base-pair probabilities","heading"); + text(x+w/2,y-65,q.getId()+" (5' to 3')","axis"); + out<<""<\n"; + auto rect=[&](double rx,double ry,Z_type value,bool missing,const std::string & attributes,const std::string & title) { + out<<""<<xml(title)<<": "; + if (missing) out<<"NA (empty interaction ensemble)"; else out<<value; + out<<"\n"; + }; + out<<"\n"; + for(size_t i=0;i\n\n"; + for(bool target:{false,true}) { + const auto & seq=target?t:q; + const auto & acc=target?at:aq; + for(size_t i=0;i=.75?"nt nt-light":"nt"); + } + } + } + out<<"\n\n"; + for(bool target:{false,true}) { + const auto & seq=target?t:q; + for(size_t i=0;i0 && seq.getInOutIndex(i-1)==-1; + if (idx%10!=0 && idx!=1) continue; + // Negative multiples: before the labelled nt (-11|-10); positive: + // after it (10|11). At the missing zero: -1|+1. + const double offset=cell*(i+(idx>0 && !origin?1:0)); + if (idx%10==0 || origin) out<<"\n"; + const std::string label=idx==1?"+1":std::to_string(idx); + if(target) { + text(x-26,y+cell*(i+.5)+4,label,"index","end"); + text(x+w+26,y+cell*(i+.5)+4,label,"index","start"); + } else { + text(x+cell*(i+.5),y-28,label,"index"); + text(x+cell*(i+.5),y+h+38,label,"index"); + } + } + } + out<<"\n\n"; + bool annotated=false; + if(result.collectsSeedPairs()) { + out<<"\n"; + for(size_t i=0;i\n"; + } + out<<"\n"; + } + const double legendX=(width-7*86)/2, legendY=y+h+85; + text(width/2,legendY-14,"Probability ranges (base pairs and single-nucleotide accessibility)","legend"); + out<<"\n"; + for(size_t k=0;k\n"; + std::ostringstream range; + range.imbue(std::locale::classic()); + range<<'['<\n"; + if (annotated) { + out<<"\n"; + text(legendX+28,legendY+61,"Orange outline: pair in an admitted seed within a searched region","legend","start"); + } + if(empty) text(width/2,legendY+90,"Gray / NA: empty interaction ensemble; accessibility remains defined","legend"); + out<<"\n"; + return out.str(); +} + +void BasePairProbabilityWriter::writeSvg(std::ostream & out,const BasePairProbabilities & result,const InteractionEnergy & energy) +{ + emit(out,svgBlock(result,energy)); +} + +void BasePairProbabilityWriter::writeSvgFile(const std::string & filename,const BasePairProbabilities & result,const InteractionEnergy & energy) +{ + emitFile(filename,svgBlock(result,energy)); +} + +} // namespace IntaRNA diff --git a/src/IntaRNA/BasePairProbabilityWriter.cpp b/src/IntaRNA/BasePairProbabilityWriter.cpp index d0aea7af..6d679fdd 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.cpp +++ b/src/IntaRNA/BasePairProbabilityWriter.cpp @@ -36,7 +36,7 @@ void BasePairProbabilityWriter::emit(std::ostream & out,const std::string & data { try { out< file(out==&std::cout || out==&std::cerr?nullptr:out); diff --git a/src/IntaRNA/BasePairProbabilityWriter.h b/src/IntaRNA/BasePairProbabilityWriter.h index 48578daf..41d7c1a2 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.h +++ b/src/IntaRNA/BasePairProbabilityWriter.h @@ -6,6 +6,7 @@ #include namespace IntaRNA { +class InteractionEnergy; /** Explicit writer for a completed actual-pair matrix, independent of trackers. * Whole blocks are synchronized for shared streams. No destructor emits data. */ @@ -30,7 +31,26 @@ class BasePairProbabilityWriter { */ static void writeFile(const std::string & filename,const BasePairProbabilities & result, const RnaSequence & target,const RnaSequence & query,const std::string & separator=";"); + /** Write a complete SVG dot plot with original target rows/query columns. + * Includes 1-nt accessibility, signed coordinate guides and a CSS color legend. + * Seed outlines are included if the result collected admitted seed pairs. + * @param out destination stream; an I/O failure throws + * @param result explicitly finalized raw result + * @param energy original, unshifted energy/accessibility model used in prediction + */ + static void writeSvg(std::ostream & out,const BasePairProbabilities & result, + const InteractionEnergy & energy); + /** Write a complete SVG to a file or STDOUT/STDERR, including gzip support. + * Validates the whole document before opening the destination. + * @param filename destination (already expanded for multi-FASTA) + * @param result explicitly finalized raw result + * @param energy original, unshifted energy/accessibility model + */ + static void writeSvgFile(const std::string & filename,const BasePairProbabilities & result, + const InteractionEnergy & energy); private: + static std::string svgBlock(const BasePairProbabilities & result,const InteractionEnergy & energy); + static void emitFile(const std::string & name,const std::string & data); static std::string block(const BasePairProbabilities & result,const RnaSequence & target, const RnaSequence & query,const std::string & separator); static void emit(std::ostream & out,const std::string & data); diff --git a/src/IntaRNA/Makefile.am b/src/IntaRNA/Makefile.am index 4fc5db3e..178691d6 100644 --- a/src/IntaRNA/Makefile.am +++ b/src/IntaRNA/Makefile.am @@ -106,6 +106,7 @@ libIntaRNA_a_SOURCES = \ StackSeedDomain.cpp \ BasePairProbabilities.cpp \ BasePairProbabilityWriter.cpp \ + BasePairProbabilitySvg.cpp \ SeededPartitionFunction.cpp \ $(libmylib_a_HEADERS) \ Accessibility.cpp \ diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index 4f50a61e..fb7c8929 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -185,11 +185,18 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In const auto first=energy.getBasePair(0,0), last=energy.getBasePair(n-1,m-1); const IndexRange target(first.first,last.first), query(last.second,first.second); Matrix original(n,m,0); + const bool annotate=pairProbabilities->collectsSeedPairs(); + Matrix seedPairs(annotate?n:0,annotate?m:0,0); for(size_t i=0;iaddRegion(target,query,result.z,original); + pairProbabilities->addRegion(target,query,result.z,original,annotate?&seedPairs:nullptr); } } diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index dc864b8c..77455d1e 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -957,6 +957,7 @@ CommandLineParsing::CommandLineParsing( const Personality personality ) "\n 'tAcc:' (target) ED accessibility values ('tPu'-like format OR '.agz' file ending for IntaRNA's binary format)." "\n 'tPu:' (target) unpaired probability values (RNAplfold format OR '.agz' file ending for IntaRNA's binary format)." "\n 'pMinE:' (target+query) for each index pair the minimal energy of any interaction covering the pair (CSV format)" + "\n 'bpsvg:' (target+query) SVG actual-pair probability plot with accessibility frame and seed annotations; same requirements as bpProb" "\n 'bpProb:' (target+query) actual-pair conditional probabilities for exact --model=P --mode=M stack-seed ensembles (CSV matrix, original strand order; NA for an empty ensemble)" "\n 'spotProb:' (target+query) tracks for a given set of interaction spots their probability to be covered by an interaction. If no spots are provided, probabilities for all index combinations are computed. Spots are encoded by comma-separated 'idxT&idxQ' pairs (target-query). For each spot a probability is provided in concert with the probability that none of the spots (encoded by '0&0') is covered (CSV format). The spot encoding is followed colon-separated by the output stream/file name, eg. '--out=\"spotProb:3&76,59&2:STDERR\"'. NOTE: value has to be quoted due to '&' symbol!" "\nFor each, provide a file name or STDOUT/STDERR to write to the respective output stream." @@ -1246,7 +1247,7 @@ parse(int argc, char** argv) if (hasBasePairProbabilityOutput()) { if (model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) - throw error("bpProb requires --model=P --mode=M with stack-only seeds and no windows"); + throw error("bpProb/bpsvg requires --model=P --mode=M with stack-only seeds and no windows"); outNeedsZall=true; } // K needs a seed even before the usual --noSeed model normalization. @@ -1582,7 +1583,7 @@ parse(int argc, char** argv) for (auto c2=c1; noDuplicate && (++c2)!=outPrefix2streamName.end();) { if ( ! c2->second.empty() && boost::iequals( c1->second, c2->second ) ) { // Pair matrices emit synchronized complete blocks on shared standard streams. - if ((c1->first==OP_bpProb || c2->first==OP_bpProb) + if ((c1->first==OP_bpProb || c2->first==OP_bpProb || c1->first==OP_bpsvg || c2->first==OP_bpsvg) && (boost::iequals(c1->second,"STDOUT") || boost::iequals(c1->second,"STDERR"))) continue; noDuplicate = false; throw error("--out argument shows multiple times '"+toString(c1->second)+"' as target file/stream."); @@ -2490,19 +2491,23 @@ getTemperature() const { //////////////////////////////////////////////////////////////////////////// bool CommandLineParsing::hasBasePairProbabilityOutput() const { - return !outPrefix2streamName.at(OutPrefixCode::OP_bpProb).empty(); + return !outPrefix2streamName.at(OutPrefixCode::OP_bpProb).empty() + || !outPrefix2streamName.at(OutPrefixCode::OP_bpsvg).empty(); } BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const InteractionEnergy & energy) const { if (!hasBasePairProbabilityOutput()) return nullptr; std::unique_ptr handler(getSeedHandler(energy)); if (!handler->guaranteesStackOnlySeeds()) - throw std::invalid_argument("bpProb requires a stack-only seed handler; bulged seeds are unsupported"); - return new BasePairProbabilities(energy.size1(),energy.size2()); + throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); + return new BasePairProbabilities(energy.size1(),energy.size2(),!outPrefix2streamName.at(OP_bpsvg).empty()); } void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const { const auto & t=energy.getAccessibility1().getSequence(); const auto & q=energy.getAccessibility2().getAccessibilityOrigin().getSequence(); - BasePairProbabilityWriter::writeFile(getFullFilename(outPrefix2streamName.at(OutPrefixCode::OP_bpProb),&t,&q),result,t,q,outSep); + if (!outPrefix2streamName.at(OP_bpProb).empty()) + BasePairProbabilityWriter::writeFile(getFullFilename(outPrefix2streamName.at(OP_bpProb),&t,&q),result,t,q,outSep); + if (!outPrefix2streamName.at(OP_bpsvg).empty()) + BasePairProbabilityWriter::writeSvgFile(getFullFilename(outPrefix2streamName.at(OP_bpsvg),&t,&q),result,energy); } Predictor* @@ -2512,10 +2517,10 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair std::unique_ptr pairSeeds; if (pairProbabilities) { if(model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) - throw std::invalid_argument("bpProb requires --model=P --mode=M with stack-only seeds and no windows"); + throw std::invalid_argument("bpProb/bpsvg requires --model=P --mode=M with stack-only seeds and no windows"); pairSeeds.reset(getSeedHandler(energy)); if(!pairSeeds->guaranteesStackOnlySeeds()) - throw std::invalid_argument("bpProb requires a stack-only seed handler; bulged seeds are unsupported"); + throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); } // set up hub for prediction tracking (if needed) PredictionTrackerHub * predTracker = new PredictionTrackerHub(); diff --git a/src/bin/CommandLineParsing.h b/src/bin/CommandLineParsing.h index fff52480..f0216b37 100644 --- a/src/bin/CommandLineParsing.h +++ b/src/bin/CommandLineParsing.h @@ -289,7 +289,7 @@ class CommandLineParsing { Predictor* getPredictor( const InteractionEnergy & energy , OutputHandler & output, BasePairProbabilities * pairProbabilities = nullptr ) const; - /** @return whether actual base-pair matrix output was requested */ + /** @return whether actual base-pair CSV or SVG output was requested */ bool hasBasePairProbabilityOutput() const; /** Allocate a pair result after validating the actual seed capability. * @return owned result or nullptr when pair output is not requested @@ -416,6 +416,7 @@ class CommandLineParsing { OP_spotProb, OP_spotProbAll, OP_bpProb, + OP_bpsvg, OP_UNKNOWN }; @@ -442,6 +443,7 @@ class CommandLineParsing { if (prefLC == "tacc") { return OutPrefixCode::OP_tAcc; } else if (prefLC == "qpu") { return OutPrefixCode::OP_qPu; } else if (prefLC == "tpu") { return OutPrefixCode::OP_tPu; } else + if (prefLC == "bpsvg") { return OutPrefixCode::OP_bpsvg; } else if (prefLC == "bpprob") { return OutPrefixCode::OP_bpProb; } else if (prefLC == "spotprob") { return OutPrefixCode::OP_spotProb; } else // not known diff --git a/tests/BasePairProbabilities_test.cpp b/tests/BasePairProbabilities_test.cpp index 500c306c..669071cb 100644 --- a/tests/BasePairProbabilities_test.cpp +++ b/tests/BasePairProbabilities_test.cpp @@ -48,3 +48,44 @@ TEST_CASE("pair writer validates before emitting and checks stream failure", "[B BasePairProbabilityWriter::write(na,empty,t,q); REQUIRE(na.str()=="bpProb;U_1;C_2\nG_1;NA;NA\nA_2;NA;NA\n"); } + +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +TEST_CASE("seed annotations commit with regional masses and SVG requires success", "[BasePairProbabilities]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("target<&", "GGGG"),q("query", "CCCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr); + ReverseAccessibility ar(aq); InteractionEnergyBasePair energy(at,ar); + BasePairProbabilities r(4,5,true); + Matrix mass(2,2,0);mass(0,1)=2;mass(1,0)=2; + Matrix seeds(2,2,0);seeds(0,1)=1; + r.addRegion({0,1},{1,2},2,mass,&seeds); + r.addRegion({2,3},{3,4},2,mass,&seeds); + REQUIRE(r.collectsSeedPairs());REQUIRE(r.seedPairs()(0,2)==1); + REQUIRE(r.seedPairs()(2,4)==1);REQUIRE(r.seedPairs()(1,1)==0); + std::ostringstream pending; + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(pending,r,energy)); + REQUIRE(pending.str().empty()); + r.finalize();std::ostringstream svg; + BasePairProbabilityWriter::writeSvg(svg,r,energy); + REQUIRE(svg.str().find("target<&")!=std::string::npos); + REQUIRE(svg.str().find("Orange outline:")!=std::string::npos); + std::ostringstream broken;broken.setstate(std::ios::badbit); + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(broken,r,energy)); + r.fail();std::ostringstream failed; + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(failed,r,energy)); + REQUIRE(failed.str().empty()); + BasePairProbabilities missing(4,5,true); + REQUIRE_THROWS(missing.addRegion({0,1},{1,2},2,mass)); + REQUIRE(missing.status()==BasePairProbabilities::Status::failed); + REQUIRE(missing.getZ()==0);REQUIRE(missing.seedPairs()(0,2)==0); + BasePairProbabilities plain(4,5); + REQUIRE_FALSE(plain.collectsSeedPairs());REQUIRE(plain.seedPairs().size1()==0); + plain.finalize();std::ostringstream empty; + BasePairProbabilityWriter::writeSvg(empty,plain,energy); + REQUIRE(empty.str().find("data-probability=\"NA\"")!=std::string::npos); + REQUIRE(empty.str().find("Orange outline:")==std::string::npos); + BasePairProbabilities wrongSize(1,1);wrongSize.finalize();std::ostringstream wrong; + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(wrong,wrongSize,energy)); + REQUIRE(wrong.str().empty()); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 5a919fb1..4d184d6b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -85,4 +85,4 @@ runApiTests_CXXFLAGS = -I$(top_builddir)/src \ -EXTRA_DIST = tools/export-seeded-theory.py testBasePairProbabilities.py +EXTRA_DIST = tools/export-seeded-theory.py testBasePairProbabilities.py testBasePairProbabilitySvg.py diff --git a/tests/runBasePairProbabilities.sh b/tests/runBasePairProbabilities.sh index be6d002c..b68221c6 100644 --- a/tests/runBasePairProbabilities.sh +++ b/tests/runBasePairProbabilities.sh @@ -2,3 +2,4 @@ set -euo pipefail script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) python3 "$script_dir/testBasePairProbabilities.py" "$INTARNABINPATH/src/bin/IntaRNA" +python3 "$script_dir/testBasePairProbabilitySvg.py" "$INTARNABINPATH/src/bin/IntaRNA" diff --git a/tests/testBasePairProbabilitySvg.py b/tests/testBasePairProbabilitySvg.py new file mode 100644 index 00000000..29047ef3 --- /dev/null +++ b/tests/testBasePairProbabilitySvg.py @@ -0,0 +1,157 @@ +"""SVG semantics, original coordinates, seed provenance and output lifecycle.""" +import csv +import gzip +import math +from pathlib import Path +import re +import subprocess +import sys +import tempfile +import xml.etree.ElementTree as ET + +BIN = sys.argv[1] +COMMON = ['--energy=B', '--acc=N', '--model=P', '--mode=M', '--seedBP=2', + '--threads=1', '--outNoLP=false', '--outNoGUend=false', '--outNumber=0', + '--default-log-file=/dev/null'] +NS = {'s': 'http://www.w3.org/2000/svg'} + + +def run(t='GGAGGG', q='CCCC', options=(), success=True): + keys = {o.split('=')[0] for o in options} + p = subprocess.run([BIN, f'--target={t}', f'--query={q}', + *[o for o in COMMON if o.split('=')[0] not in keys], *options], + text=True, capture_output=True) + assert (p.returncode == 0) == success, (options, p.stdout, p.stderr) + return p + + +def load(path): + return ET.fromstring(gzip.decompress(path.read_bytes()) if str(path).endswith('.gz') else path.read_bytes()) + + +def pairs(root): + return {(int(r.attrib['data-target']), int(r.attrib['data-query'])): r + for r in root.findall('.//s:rect[@data-type="base-pair"]', NS)} + + +def seeds(root): + return {(int(r.attrib['data-target']), int(r.attrib['data-query'])) + for r in root.findall('.//s:rect[@class="seed"][@data-target]', NS)} + + +with tempfile.TemporaryDirectory(prefix='intarna-bpsvg-') as tmp: + d = Path(tmp) + svg, table = d/'pairs.svg', d/'pairs.csv' + + def plot(t='GGAGGG', q='CCCC', opts=()): + run(t, q, [f'--out=bpsvg:{svg}', *opts]) + return load(svg) + + root = plot(opts=[f'--out=bpProb:{table}']) + cells = pairs(root) + rows = list(csv.reader(table.open(), delimiter=';')) + assert len(cells) == 24 + assert '+1' in [e.text for e in root.findall('.//s:text[@class="index"]', NS)] + assert not root.findall('.//s:line[@data-index="1"]', NS) + for i, row in enumerate(rows[1:], 1): + for j, value in enumerate(row[1:], 1): + r = cells[i, j] + assert float(r.attrib['data-probability']) == float(value) + bounds = [0.01, 0.1, 0.25, 0.5, 0.75, 0.9] + expected_color = sum(float(value) >= boundary for boundary in bounds) + assert f'p{expected_color}' in r.attrib['class'].split() + assert 'Base-pair probability' in r.find('s:title', NS).text + assert all(float(cells[3, j].attrib['data-probability']) == 0 for j in range(1, 5)) + frame = root.findall('.//s:rect[@data-type="unpaired"]', NS) + assert len(frame) == 2*(6+4) + assert all(float(r.attrib['data-probability']) == 1 for r in frame) + assert all('Unpaired probability' in r.find('s:title', NS).text for r in frame) + css = root.find('s:style', NS).text + assert all(f'.p{i} {{ fill:' in css for i in range(7)) + assert len(root.findall('.//s:g[@class="probability-legend"]/s:rect', NS)) == 7 + # All admitted computed two-pair stacks, in original query orientation. + expected = {(i+k+1, 4-j-k) for i in (0, 3, 4) for j in range(3) for k in range(2)} + assert seeds(root) == expected, seeds(root) + # Requesting SVG must not affect CSV or ensemble/ranked output. + baseline = run(options=['--outNumber=1', '--outMode=C', '--outCsvCols=Zall,Eall']) + plotted = run(options=['--outNumber=1', '--outMode=C', '--outCsvCols=Zall,Eall', f'--out=bpsvg:{svg}']) + assert baseline.stdout == plotted.stdout + # Explicit singleton/mixed seed masks exclude extension-only cells. + opts = ['--seedTQ=1|&4|,2||&2||'] + mixed = plot('GGGG', 'CCCC', opts) + assert seeds(mixed) == {(1,4), (2,3), (3,2)} + restricted = plot('GGGG', 'CCCC', [*opts, '--tRegion=2-4', '--qRegion=1-3']) + assert seeds(restricted) == {(2,3), (3,2)} + # A seed crossing a searched region boundary is not annotated. + cut = plot('GGGG', 'CCCC', ['--seedTQ=2||&2||', '--tRegion=1-2,3-4']) + assert not seeds(cut) + assert all(r.attrib['data-probability'] == 'NA' for r in pairs(cut).values()) + # Four disjoint rectangles merge both probabilities and masks. + regional = plot('GGGG', 'CCCC', ['--tRegion=1-2,3-4', '--qRegion=1-2,3-4']) + assert seeds(regional) == {(1,2),(2,1),(1,4),(2,3),(3,2),(4,1),(3,4),(4,3)} + # Signed guides follow displayed coordinates; there is no zero nucleotide. + signed = plot('A'*72, 'A'*18, ['--tIdxPos0=-15', '--qIdxPos0=-15']) + signed_cells = pairs(signed) + for strand, length in (('target',72), ('query',18)): + guides = signed.findall(f'.//s:line[@data-strand="{strand}"]', NS) + expected_indices = {-10, 1} | ({10,20,30,40,50} if length == 72 else set()) + assert {int(g.attrib['data-index']) for g in guides} == expected_indices + assert not any(0 in k for k in signed_cells) + for g in guides: + idx = int(g.attrib['data-index']) + r = signed_cells[idx,-15] if strand == 'target' else signed_cells[-15,idx] + axis = 'y' if strand == 'target' else 'x' + expected_pos = float(r.attrib[axis]) + (20 if idx > 1 else 0) + assert float(g.attrib[axis+'1']) == expected_pos + assert ('major' in g.attrib['class']) == (idx % 50 == 0) + assert ('origin' in g.attrib['class']) == (idx == 1) + negative = plot('A'*13, 'A'*13, ['--tIdxPos0=-55', '--qIdxPos0=45']) + assert 'major' in negative.find('.//s:line[@data-strand="target"][@data-index="-50"]', NS).attrib['class'] + # User sequence identifiers must round-trip through XML escaping. + name = '' + named = plot(opts=[f'--tId={name}']) + assert name in named.find('s:title', NS).text + assert name+" (5' to 3')" in [e.text for e in named.findall('.//s:text', NS)] + # Native accessibility values must use original (not reversed) query order. + pu_t, pu_q = d/'target.pu', d/'query.pu' + native = plot('GGGGAAAACCCC', 'GCGCGAAAACGCGC', + ['--energy=V', '--acc=C', f'--out=tPu:{pu_t}', f'--out=qPu:{pu_q}']) + for strand, path in (('target', pu_t), ('query', pu_q)): + expected_pu = [float(line.split()[1]) for line in path.read_text().splitlines() + if line.strip() and not line.lstrip().startswith('#')] + assert any(0 < v < .9 for v in expected_pu) + for r in native.findall(f'.//s:rect[@data-type="unpaired"][@data-strand="{strand}"]', NS): + assert math.isclose(float(r.attrib['data-probability']), expected_pu[int(r.attrib['data-index'])-1], + rel_tol=2e-5, abs_tol=1e-6) + for t,q in (('AAAA','AAAA'), ('G','C')): + empty = plot(t,q) + assert all(r.attrib['data-probability'] == 'NA' for r in pairs(empty).values()) + assert not seeds(empty) + assert 'empty interaction ensemble' in ''.join(empty.itertext()) + compressed = d/'pairs.svg.gz' + run(options=[f'--out=bpsvg:{compressed}']) + assert len(pairs(load(compressed))) == 24 + # Validation/numerical failures must not publish a partial document. + for opts in (['--mode=H'], ['--noSeed'], ['--seedMaxUP=1'], ['--model=X', '--mode=K'], + ['--windowWidth=3'], ['--seedTQ=1|&4|','--energyAdd=900']): + failure = d/'failure.svg' + run(options=[f'--out=bpsvg:{failure}', *opts], success=False) + assert not failure.exists() + run(options=['--out=bpsvg:/dev/full'], success=False) + run(options=[f'--out=bpsvg:{svg}', f'--out=bpProb:{svg}'], success=False) + # Multi-FASTA creates one SVG per pair; shared streams emit indivisible roots. + fasta=d/'targets.fa'; fasta.write_text('>a\nGGGG\n>b\nGAGG\n') + for threads in (1,2): + prefix=d/f'multi{threads}.svg' + run(str(fasta), 'CCCC', [f'--threads={threads}', f'--out=bpsvg:{prefix}']) + files=sorted(d.glob(f'multi{threads}*.svg')) + assert len(files)==2 + assert all(len(pairs(load(file)))==16 for file in files) + for stream in ('STDOUT', 'STDERR'): + shared=run(str(fasta), 'CCCC', [f'--threads={threads}', f'--out=bpsvg:{stream}']) + data=shared.stdout if stream=='STDOUT' else shared.stderr + blocks=re.findall(r'', data, re.S) + assert len(blocks)==2 + assert all(len(pairs(ET.fromstring(block)))==16 for block in blocks) + +print('base-pair probability SVG regressions passed') From fa2f84d6cdbb1e120a98eab9d2dfe3f8154ebad5 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 12:11:59 +0200 Subject: [PATCH 10/30] docs: depict ensemble and pair-probability recurrences (step 9) --- ChangeLog | 13 ++++ doc/recursions/IntaRNA.PredictorMfeEns2d.svg | 60 +++++++++++++++++ .../IntaRNA.PredictorMfeEns2dHeuristic.svg | 51 ++++++++++++++ ...redictorMfeEns2dHeuristicSeedExtension.svg | 51 ++++++++++++++ ....PredictorMfeEns2dSeedExtension.legacy.svg | 61 +++++++++++++++++ ...IntaRNA.PredictorMfeEns2dSeedExtension.svg | 54 +++++++++++++++ .../IntaRNA.PredictorMfeEnsSeedOnly.svg | 41 ++++++++++++ ...IntaRNA.SeededPartitionFunction.inside.svg | 59 ++++++++++++++++ ...ntaRNA.SeededPartitionFunction.outside.svg | 67 +++++++++++++++++++ 9 files changed, 457 insertions(+) create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2d.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.legacy.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg create mode 100644 doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg create mode 100644 doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg diff --git a/ChangeLog b/ChangeLog index d04228f5..613764ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -50,6 +50,9 @@ ## Technical changes and Optimizations +- depict ensemble predictor recurrences and the exact base-pair inside/outside + computation in internal SVG documentation (PR #258, step 9) + - validate exact pair probabilities on independent mathematical/native oracles, release/debug suites and installed consumers; document measured runtime and storage, and supply the required logging link dependency (issue #257, step 7) @@ -160,6 +163,16 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * doc/recursions/IntaRNA.PredictorMfeEns*.svg, + doc/recursions/IntaRNA.SeededPartitionFunction.*.svg : + + add eight standalone internal SVG depictions for unseeded exact/heuristic, + seed-only, exact stack-seeded, legacy and heuristic seed-extension paths + + show noLP transitions, seed admission and energy ownership, the production + suffix inside/outside recurrences, actual-pair marking and regional merging + + include implementation anchors and retain editable vector text; validate + XML, glyph coverage and rendered layouts (PR #258 step 9, PR #259) + 261009 Alexander Mitrofanov * BasePairProbabilityWriter, BasePairProbabilitySvg, CommandLineParsing : + add success-only SVG dot plots on files, gzip and synchronized standard diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2d.svg b/doc/recursions/IntaRNA.PredictorMfeEns2d.svg new file mode 100644 index 00000000..674ba774 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2d.svg @@ -0,0 +1,60 @@ + +Unseeded ensemble · fixed right boundary +PredictorMfeEns2d | --model=P --mode=M --noSeed. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Unseeded ensemble · fixed right boundary +PredictorMfeEns2d | --model=P --mode=M --noSeed + +Pair indices i=(i₁,i₂), k and j increase on both strands; RNA 2 is reversed. +i ≺ k means strict increase in both coordinates. d=BW(E_init), t(i,k)=BW(E_interLeft(i,k)). +Hⱼ(i) = hybrid partition for all admitted chains from i to a fixed j + +Initiation + +Direct pair-to-pair step + +Recursive continuation + + + +i = j +d + + + +i + +j +t(i,j) · d + +Hⱼ(k) + + + +i + +k + +j +t(i,k) · Hⱼ(k) +Lonely pairs allowed +Hⱼ(j)=d; Hⱼ(i)=t(i,j)d + ∑ t(i,k)Hⱼ(k) (i ≺ k ≺ j) +All other or noncomplementary states are zero. Sum only loop-size-admissible steps. +For every valid right pair j, fill i in decreasing order within the span limits. + +noLP: the left boundary must stack to i⁺ = i + (1,1) +Hⱼ(j)=0; Hⱼ(i)=t(i,i⁺) · [ (d if i⁺=j, else Hⱼ(i⁺)) + ∑ t(i⁺,k)Hⱼ(k) ] +The sum requires i⁺ ≺ k ≺ j and k ≠ i⁺+(1,1): the direct stack is already in Hⱼ(i⁺). +Require a complementary i⁺. Unreachable suffixes are zero; the adjacent two-pair case contributes t(i,j)d. +Thus initialization is a stack, and each nonstack continuation starts another stacked suffix. +Zall += B(i,j) · Hⱼ(i); E_site = energy.getE(i,j, energy.getE(Hⱼ(i))) +B(i,j)=BW(energy.getE(i₁,j₁,i₂,j₂,0)) for admitted boundaries; otherwise B=0. +This adds full-interval ED and terminal terms once; updateCompleteZ submits each boundary once. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2d.cpp: predict(), fillHybridZ() +PredictorMfeEns.cpp: updateCompleteZ(), addPartitionContribution() + diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg new file mode 100644 index 00000000..fd048c48 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg @@ -0,0 +1,51 @@ + +Unseeded ensemble · heuristic continuation +PredictorMfeEns2dHeuristic | --model=P --mode=H --noSeed. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Unseeded ensemble · heuristic continuation +PredictorMfeEns2dHeuristic | --model=P --mode=H --noSeed + +C(i)=(vᵢ,jᵢ) retains one hybrid weight and right boundary for each left pair i. +Use d=BW(E_init), t(i,k)=BW(E_interLeft(i,k)); all pair coordinates increase internally. + +Enumerate candidates at i, visiting i in decreasing order + +vₖ + + + +i + +k + +jₖ +Lonely pairs allowed: + initial candidate (d, i) + continuation (t(i,k) · vₖ, jₖ) for each reachable k ≻ i + within loop limits and the complete span limits. + + +Accumulate every considered candidate +updateZ(i,j,v,true) +Adds B(i,j)v to Zall and v to the boundary map. +B is the admitted complete-boundary Boltzmann factor. + +Retain only the best continuation at i +C(i) ← argmin E_full(i,j,v) +Compare complete energy for each candidate. +Strict improvement replaces vᵢ and jᵢ. + +noLP candidate set +Let i⁺=i+(1,1) be complementary and s=t(i,i⁺). +Initialize (s·d, i⁺); add direct continuation (s·vᵢ⁺, jᵢ⁺). +Other candidates are (s·t(i⁺,k)·vₖ, jₖ), with k ≻ i⁺ and k ≠ i⁺+(1,1). +The adjacent continuation is excluded from the loop sum because it was handled directly. +The boundary map sums contributions, but the DP cell keeps one right continuation: this is a restricted ensemble. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2dHeuristic.cpp: fillHybridZ() +PredictorMfeEns.cpp: updateZ(), updateOptimaUsingZ() + diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg new file mode 100644 index 00000000..6f7c7fa8 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg @@ -0,0 +1,51 @@ + +Seeded ensemble · one selected right extension +PredictorMfeEns2dHeuristicSeedExtension | --model=P --mode=H. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Seeded ensemble · one selected right extension +PredictorMfeEns2dHeuristicSeedExtension | --model=P --mode=H + +For anchor seed a…b: S=BW(E_seed), d=BW(E_init), L=Lₐ(i), R=R_b(j). +L and R use the inherited legacy extension recurrences, even when the seed handler is stack-only. + +1 Compute R, submit true right extensions, and select j* +updateZ(a,j, d · S · R_b(j), true) for j ≻ b +j* = argmin E_full(a,j, d · S · R_b(j)) over right-end-admissible candidates +updateOptRightZ compares complete energy and the right GU-end restriction; ties retain the first candidate. + + +2 Compute corrected L and submit left-side combinations + +L + + + + +i + +a + + +b + +j* +updateZ(i,b, Lₐ(i) · S, true) +Includes the seed-only case i=a. +updateZ(i,j*, Lₐ(i) · S · R_b(j*), true) +Only for a true left extension and a true selected right extension, +with both complete spans within their limits. + +Candidate family per anchor +All true right-only extensions + all corrected left-only extensions + left extensions paired with just j*. +The second phase runs if noGUend is off, or a valid right optimum exists, or anchor end b is not GU. +Initialize j*=b and E_right_opt=∞. A positive selected R is required before combining both sides. +No full Cartesian product of left and right boundaries is retained, and bpProb / bpsvg are unsupported. +Every update is filtered and accumulated by PredictorMfeEns::updateZ; ranked reporting is a separate step. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2dHeuristicSeedExtension.cpp: predict(), fillHybridZ_right(), fillHybridZ_left() +PredictorMfeEns2dHeuristicSeedExtension.h: updateOptRightZ(); inherited legacy fillHybridZ kernels + diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.legacy.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.legacy.svg new file mode 100644 index 00000000..f19dc623 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.legacy.svg @@ -0,0 +1,61 @@ + +Legacy seeded extensions · anchor factorization +Bulged-seed fallback in mode M; also the extension kernels used by the seeded mode H predictor. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Legacy seeded extensions · anchor factorization +Bulged-seed fallback in mode M; also the extension kernels used by the seeded mode H predictor + +For one retained anchor seed a…b, use S=BW(E_seed(a)), d=BW(E_init), t(i,k)=BW(E_interLeft(i,k)). +Lₐ(i) extends left to a (includes initiation); R_b(j) extends right from b (excludes initiation). + +Lₐ(i) + + + + +i + +a + + + + +b + +j + + +R_b(j) + + + + +hybrid contribution = Lₐ(i) · S · R_b(j) + +Lonely pairs allowed +Lₐ(a)=d; Lₐ(i)=∑ t(i,k)Lₐ(k) − Cₐ(i) (i ≺ k ≼ a) +R_b(b)=1; R_b(j)=∑ R_b(k)t(k,j) (b ≼ k ≺ j) +Only nonzero, complementary states and loop-size-admissible terms participate. Sums use componentwise bounds. + +noLP: outward boundary stacks are explicit +Lₐ(i)=t(i,i⁺) · [Lₐ(i⁺) + ∑ t(i⁺,k)Lₐ(k)] − Cₐ(i) (i⁺ ≺ k ≼ a) +R_b(j)=t(j⁻,j) · [1{j⁻=b}R_b(b) + ∑ R_b(k)t(k,j⁻)] (b ≼ k ≺ j⁻) +i⁺=i+(1,1), j⁻=j−(1,1); require the extra stacked pair. Anchor bases still have Lₐ(a)=d and R_b(b)=1. +These are the legacy loop bounds. The exact stack-seed path instead uses the suffix-state recurrence. + +Left-seed correction Cₐ(i) +At a competing seed start i: process it if its end is left of a, or it loop-overlaps the anchor. +If a leftmost loop-overlapping seed h is found, subtract BW(getNonOverlappingEnergy(i,h)) · Lₐ(h). +Otherwise subtract its seed weight times the continuation at its end; noLP adds explicit loop continuations. +Apply the implementation’s overlap/loop guards, then clamp a negative corrected L value to zero. +updateZ(i,j, Lₐ(i) · S · R_b(j), true) for each nonzero span-admissible combination +Complete-site filters and B(i,j) are applied by updateZ. The anchor’s hybrid seed energy is charged once. +This legacy subtraction-based path has no actual-pair outside pass; do not substitute it for the exact seeded union. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2dSeedExtension.cpp: predict() legacy branch, fillHybridZ_left(), fillHybridZ_right() +PredictorMfeEns2dSeedExtension.cpp: getNonOverlappingEnergy(); PredictorMfeEns.cpp: updateZ() + diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.svg new file mode 100644 index 00000000..b04a6fe1 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dSeedExtension.svg @@ -0,0 +1,54 @@ + +Seeded ensemble · exact stack-seed adapter +PredictorMfeEns2dSeedExtension | --model=P --mode=M with stack-only seed capability. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Seeded ensemble · exact stack-seed adapter +PredictorMfeEns2dSeedExtension | --model=P --mode=M with stack-only seed capability + + +SeedHandler: guaranteesStackOnlySeeds() +true → predictStackSeeds(): unique seeded-chain partition +false → legacy anchor extensions; no pair-probability sink + +Capability is geometric +Computed or explicit seeds; mixed lengths and singletons. +Preserve handler admission and per-start selection rules. + +1 Compile active seed occurrences and eligible left boundaries + + + + +p + +a + + +b + +q +max(0, bₖ − Wₖ + 1) ≤ pₖ ≤ aₖ (k=1,2) +Admitted stack a…b must fit both the region and span limits W. +Enumerate the union of these p-boxes once, then require a valid p. +At p, clip the working grid to both inclusive spans. + +2 Give the suffix kernel one consistent energy objective +d(p)=BW(E_init); t(r,q)=BW(E_interLeft(r₁,q₁,r₂,q₂)) +B(p,q)=BW(energy.getE(p₁,q₁,p₂,q₂,0)) if the complete site is admitted +Invalid / inaccessible / noncomplementary vertices and forbidden transitions cannot contribute. +B owns complete-span ED, native dangles, terminal penalties and energyAdd. Seed energies only admit seeds. + +3 Commit a completed region +Z_region = ∑ B(p,q) · T₁,p(q) +Complete boundary callback and outside initialization share exactly the same B. +updateExactCompleteZ preserves the partition objective. +Pair masses and optional seed masks map through energy.getBasePair(). +Only a successful computation reaches BasePairProbabilities::addRegion(). + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2dSeedExtension.cpp: predict(), predictStackSeeds(), exactBoundaryWeight() +StackSeedDomain.cpp; SeededPartitionFunction.cpp; PredictorMfeEns.cpp: updateExactCompleteZ() + diff --git a/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg b/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg new file mode 100644 index 00000000..0a10ebe9 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg @@ -0,0 +1,41 @@ + +Seed-only ensemble · retained seed enumeration +PredictorMfeEnsSeedOnly | --model=P --mode=S. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Seed-only ensemble · retained seed enumeration +PredictorMfeEnsSeedOnly | --model=P --mode=S + + +1 Fill the seed handler for the active region +Retain the handler-selected seed at each admitted start a; its end is e. +Both endpoints must lie in the region. The handler supplies seed pairs and hybrid energy E_seed(a). +A filled handler with no admitted seeds reports an empty result. + + +2 One complete contribution per retained seed + + + + +a + + + + +e +H_seed = E_seed(a) + E_init +E_full = energy.getE(a₁,e₁,a₂,e₂,H_seed) +Zall += BW(E_full) when needZall is enabled + +3 Report and trace the retained seed +updateOptima(..., isHybridE=true, incrementZall=true) applies the complete-site filters and ranking. +traceBackSeed() supplies its interior pairs; initiation is charged exactly once. +There is no extension recurrence, no sum over alternative seed structures at the same start, and no bpProb output. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEnsSeedOnly.cpp: predict(), traceBack() +PredictorMfe.cpp: updateOptima(); Predictor.h: updateZall() (inherited update path) + diff --git a/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg b/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg new file mode 100644 index 00000000..b944efbb --- /dev/null +++ b/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg @@ -0,0 +1,59 @@ + +Exact seeded inside recurrence · suffix automaton +SeededPartitionFunction::compute() | fixed left boundary p; visit q in increasing pair order. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Exact seeded inside recurrence · suffix automaton +SeededPartitionFunction::compute() | fixed left boundary p; visit q in increasing pair order + +C=max(2, longest admitted seed length). ℓ is the current consecutive stack-suffix length, capped at C. +e(q)=shortest admitted seed ending at q, or 0. A run accepts a seed iff 0 < e(q) ≤ ℓ. + +Uℓ: still seed-free +ℓ=1…C; no seed has occurred. +Stack increments ℓ; a loop resets it. + +S₁: seeded, singleton suffix +A seed has occurred; current run is length 1. +Under noLP, must stack before leaving. + +S₂: seeded, usable suffix +Under noLP: current run has length ≥2. +Otherwise: all seeded suffixes use S₂. + + + +first seed accepted on a stack +stack → S₂ + + +1 Initialize and extend every state +H(p,c₀)=d(p); other H=0. c₀ = accept(p,1) ? (noLP ? S₁ : S₂) : U₁ +Stack r=q−(1,1): H(q,D(c,stack)) += t(r,q) · H(r,c) +For Uℓ: let ℓ′=min(C,ℓ+1); D=Uℓ′ unless the ending seed is accepted, then D=S₂. +For S₁ or S₂: D=S₂. Once seeded, a later seed never creates a second copy of the path. + +2 Aggregate eligible departures, then handle nonstack edges +T₀(q)=∑ H(q,Uℓ) over ℓ≥(noLP ? 2 : 1) +T₁(q)=H(q,S₂) + (noLP ? 0 : H(q,S₁)) +For each nonstack r→q and b∈{0,1}: H(q,D(b,loop)) += t(r,q) · T_b(r) +Nonstack means both coordinates increase, within loop limits, excluding r=q−(1,1). +Reset the suffix to 1; remain seeded if b=1, or accept a singleton seed at q if b=0. + +3 Complete seeded paths at every q +Z += B(p,q) · T₁(q) +Seed-free states never terminate an interaction. +With noLP, singleton runs cannot terminate or cross a nonstack edge. +Such states remain available for a later stack. +Loop destinations: seeded → S₁ under noLP, otherwise S₂; unseeded → U₁ unless a singleton seed accepts. +The Boolean seeded state counts each ordered pair chain once, including paths with several seed witnesses. +Termination-only GU / ED filters belong to B; they do not discard prefixes that can still extend. +d, t and B are the initiation, transition and complete-boundary factors supplied by Weights. + + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +SeededPartitionFunction.cpp: accepts, dest, nonstack, h, total; StackSeedDomain::seedEndingLength() +Code states: Uℓ ↔ c=ℓ−1; S₁ ↔ c=cap; S₂ ↔ c=cap+1. Checked nonnegative arithmetic throughout. + diff --git a/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg new file mode 100644 index 00000000..c98b9271 --- /dev/null +++ b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg @@ -0,0 +1,67 @@ + +Actual base-pair probabilities · outside recurrence +Reverse differentiation of the suffix inside computation; same fixed p and same boundary weights B. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Actual base-pair probabilities · outside recurrence +Reverse differentiation of the suffix inside computation; same fixed p and same boundary weights B + +H and T are forward state/aggregate weights. Their adjoints are A=∂Z/∂H and G=∂Z/∂T. +M(q) is the raw mass of complete chains containing actual pair q; it is not site-coverage mass. + +H(r,c) + + + +p + +r + +q + +terminal + +Reverse the incoming edge r → q. +Only actual pair q receives this edge’s mass. + + +1 Initialize the reverse objective +A(q,S₂)=B(p,q); A(q,S₁)=B(p,q) only without noLP; other A and G = 0 +Initialize at reachable positive seeded totals; a zero terminal coefficient contributes nothing. + +2 Reverse q in decreasing order; first scatter aggregate adjoints +A(q,Uℓ) += G₀(q) for the same departure-eligible ℓ as in the forward sum +A(q,S₂) += G₁(q); A(q,S₁) += G₁(q) only without noLP +This scatter must precede reversing the incoming edges at q. + +3a Reverse a stack edge r→q +a = A(q,D(c,stack)) · t(r,q) +A(r,c) += a +M(q) += a · H(r,c) +Repeat for every forward source state c. +The transition marker belongs to destination q. + +3b Reverse a nonstack edge r→q +a = A(q,D(b,loop)) · t(r,q) +Gᵦ(r) += a +M(q) += a · T_b(r) +Repeat for b=0 and b=1 using the same forward totals. +Do not mark the unpaired nucleotides in the loop. + +4 Mark the source pair after the reverse sweep +M(p) += A(p,c₀) · d(p) +Every other pair was marked when its incoming edge was reversed. + +5 Merge successful disjoint search regions before normalization +Z_total = ∑ Z_region; M_total(i,j) = ∑ M_region(i,j); P(i,j)=M_total(i,j)/Z_total +Map regional pairs with energy.getBasePair() into original target/query coordinates, then finalize explicitly. +Z_total=0 → NA. Failed/cancelled computation → no matrix. No unbound-state weight is added. +Seed admission and B are fixed while differentiating. Masses sum across every eligible left boundary p. + + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +SeededPartitionFunction.cpp: dh (A), dt (G), mass and initial-pair contribution +PredictorMfeEns2dSeedExtension::predictStackSeeds(); BasePairProbabilities::addRegion(), finalize(), probabilities() + From 202651e792e01ed188c0253a5251decb193a2b77 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 12:29:07 +0200 Subject: [PATCH 11/30] fix: address probability output and recurrence audit findings --- ChangeLog | 24 ++++ README.md | 8 +- .../seeded-probabilities-validation.md | 113 ++++++++++++++++++ .../IntaRNA.PredictorMfeEns2dHeuristic.svg | 9 +- .../IntaRNA.PredictorMfeEnsSeedOnly.svg | 3 +- ...IntaRNA.SeededPartitionFunction.inside.svg | 12 +- ...ntaRNA.SeededPartitionFunction.outside.svg | 2 +- src/IntaRNA/BasePairProbabilitySvg.cpp | 25 ++-- src/IntaRNA/BasePairProbabilityWriter.cpp | 3 +- src/IntaRNA/BasePairProbabilityWriter.h | 12 +- src/bin/CommandLineParsing.cpp | 16 ++- src/bin/CommandLineParsing.h | 19 ++- tests/BasePairProbabilities_test.cpp | 19 ++- tests/testBasePairProbabilitySvg.py | 53 ++++++-- 14 files changed, 267 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 613764ff..44268e50 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ ## Interface and handling +- BUGFIX : use each strand's accessibility energy scale for SVG frames and + unpaired-probability exports, including mixed computed/imported inputs + - --out=bpsvg:FILE renders actual-pair probabilities with nucleotide/accessibility frames, a shared CSS palette, signed-coordinate guides, hover values and admitted seed outlines (PR #258, step 8) @@ -50,6 +53,9 @@ ## Technical changes and Optimizations +- transfer completed probability-output buffers without copying; independently + audit performance, accessibility conversion and recurrence depictions (PR #259) + - depict ensemble predictor recurrences and the exact base-pair inside/outside computation in internal SVG documentation (PR #258, step 9) @@ -163,6 +169,24 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * BasePairProbabilityWriter, BasePairProbabilitySvg, CommandLineParsing : + * pass and validate separate target/query accessibility RT values for SVG + frames; imported probabilities no longer inherit the interaction model RT + * share the source-specific conversion with tPu/qPu, correcting their earlier + physical-temperature conversion of computed base-pair accessibility (RT=1) + * move completed SVG/CSV stream buffers instead of copying large documents + * tests, README.md, doc/analysis/seeded-probabilities-validation.md : + + cover imported Pu/ED, mixed sources, nondefault temperatures and invalid RT; + compare against input probabilities and independent ED conversion + + document independent review, controlled runtime/RSS comparisons and final + release/debug, installation and public-header checks (PR #259) + * doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg, + doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg, + doc/recursions/IntaRNA.SeededPartitionFunction.*.svg : + * show the heuristic initial right-end GU gate and complete-site filters; + place nonstack arrivals before inside aggregation and clarify outside notation + 261009 Alexander Mitrofanov * doc/recursions/IntaRNA.PredictorMfeEns*.svg, doc/recursions/IntaRNA.SeededPartitionFunction.*.svg : diff --git a/README.md b/README.md index fcb80537..d627414e 100644 --- a/README.md +++ b/README.md @@ -1893,6 +1893,9 @@ The column separator within tabular CSV output (defaulting to `;`) can be change using `--outSep`, e.g. to produce tab-separated `.tsv` output. Note further, `qPu:`|`tPu:` will report unpaired probability values based on rounded accessibility (ED) values. +The ED-to-probability conversion uses each strand's accessibility energy scale: +computed base-pair accessibility uses RT=1, while ViennaRNA and imported +accessibility use the configured temperature, independently of the interaction model. Thus, these values will most likely differ from values eg. produced by the program RNAplfold. We therefore strongly recommend to store `qAcc:`|`tAcc:` values when you want to use them as input for subsequent IntaRNA calls! @@ -2116,7 +2119,10 @@ a standalone SVG on standard output, redirect ordinary output, for example produce consecutive SVG documents; use filenames for separate viewable files. Library callers can use `BasePairProbabilityWriter::writeSvg()` or -`writeSvgFile()` with a finalized result and the original energy model. Construct +`writeSvgFile()` with a finalized result, the original energy model, and separate +target/query accessibility RT values. These must match the ED conversion scales +of the accessibility sources, which can differ from the interaction model's RT +(for example, imported Pu with a base-pair interaction model). Construct `BasePairProbabilities(targetLength, queryLength, true)` to retain seed annotations; the default CSV-only result does not allocate that mask. SVG creation buffers the document and uses one rectangle per pair plus the frames, so its size grows diff --git a/doc/analysis/seeded-probabilities-validation.md b/doc/analysis/seeded-probabilities-validation.md index a08848d0..3449a5ba 100644 --- a/doc/analysis/seeded-probabilities-validation.md +++ b/doc/analysis/seeded-probabilities-validation.md @@ -157,3 +157,116 @@ OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 /usr/bin/time -v src/bin/IntaRNA \ Both runs report Eall=-6.52 kcal/mol with the native presentation convention. These measurements quantify added output work; they are not a speedup claim over the legacy seeded ensemble, whose multi-anchor partition is a different result. + + +## Steps 8–9: independent output and documentation audit (2026-10-09) + +Separate agents reviewed code correctness, recurrence depictions, and performance +before the audit fixes were pushed to PR #259. The audit found and corrected: + +- SVG accessibility frames used the interaction model's RT for imported Pu/ED. + The writer now requires separate target/query accessibility RT values. CLI + conversion selects RT=1 for computed base-pair accessibility and the configured + physical temperature for ViennaRNA/imported accessibility. The same selection + fixes the pre-existing `tPu`/`qPu` conversion of computed base-pair ED. +- SVG and CSV stream buffers were copied when returned; moving the completed + strings removes that duplicate allocation without changing document bytes. +- The heuristic depiction omitted its initial right-end GU gate, and the inside + depiction placed aggregation before incoming nonstack contributions. These + were corrected, along with complete-site filtering and outside-index notation. + +The added CLI regressions fail on the pre-audit binary and pass after the fix. +They cover Pu/ED imports, mixed computed/imported strands, 22/37°C, original +query orientation, independent input-Pu round trips (allowing ED quantization), +and computed-base-pair ED conversion at RT=1. API tests reject nonpositive or +nonfinite accessibility scales without writing output. GCC 14.4 release and +debug `make tests -j2` each pass all seven suites, with 91,394 API assertions in +86 cases. Installed public headers and an installed SVG consumer compile; the +consumer produces the expected matrix and seed annotations. Corrected SVG +recurrence documents parse, render, and were visually inspected. Apple Clang +was not available locally. + +### Performance method and existing-mode comparison + +Compared base `48efc541e23571ce1bab88e2754e1db7445cbc89` with steps 8–9 at +`fa2f84d6cdbb1e120a98eab9d2dfe3f8154ebad5`, then compared that saved binary with +the audit fixes. All builds use identical GCC 14.4 C++23 `-O3 +-fno-strict-aliasing -fopenmp`, ViennaRNA 2.7.2, Boost 1.85 and Kokkos mdspan +settings on Linux x86-64 / Ryzen 5 7530U. Processes are pinned to CPU 0 with +`--threads=1`, `OMP_NUM_THREADS=1`, `OPENBLAS_NUM_THREADS=1`, and +`OMP_DYNAMIC=FALSE`. Builds and tests are stopped during measurements. + +There is one warmup per configuration, then seven fresh-process runs in shuffled, +interleaved order for the base comparison. Time is wall time around the GNU time +subprocess; RSS is GNU time `%M`, including executable/libraries, prediction and +serialization. Output goes to the same local filesystem. Initial median seconds: + +| Input | Base ensemble only | Steps 8–9 ensemble only | Base CSV | Steps 8–9 CSV | Steps 8–9 SVG | Steps 8–9 both | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| OxyS/fhlA | 0.0975 | 0.0978 | 0.1710 | 0.1703 | 0.1910 | 0.1947 | +| dense40 | 0.3876 | 0.3913 | 0.8958 | 0.9026 | 0.9090 | 0.9060 | +| sparse400 | 0.1191 | 0.1202 | 0.1616 | 0.1633 | 0.7326 | 0.8260 | +| sparse1000 | 1.5816 | 1.5801 | 1.8284 | 1.8372 | 4.0202 | 4.2578 | + +Existing-mode medians differ by −0.4% to +1.1%, within observed timing variation. +Ensemble outputs and CSVs are byte-identical across base/candidate and modes; +all SVG pair values equal their CSV values. Sparse SVG timings have wider +variation than the compute-heavy cases; these are local workload measurements, +not a general throughput guarantee. Full SVG size scales with all target/query +pairs: the sparse1000 document is 220,685,460 bytes despite having few seed cells. + +Reproduce each process with the following command from the corresponding build; +add either/both output options for the CSV/SVG configurations: + +```sh +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_DYNAMIC=FALSE \ + /usr/bin/time -f '%e %U %S %M' taskset -c 0 src/bin/IntaRNA \ + --target=doc/handson/fhlA.fasta --query=doc/handson/OxyS.fasta \ + --model=P --mode=M --seedBP=4 --intLenMax=30 \ + --qIntLoopMax=3 --tIntLoopMax=3 --threads=1 \ + --outNoLP=false --outNoGUend=false --outNumber=0 --outMode=E \ + --default-log-file=/dev/null --out=ensemble.txt +# Optional: --out=bpProb:pairs.csv --out=bpsvg:plot.svg +``` + +For dense40, replace the inputs with `G`×40 and `C`×40 and add `--tAcc=N +--qAcc=N`. For sparse length n (400 or 1000), use target `A`×(n/2) + `G`×8 + +`A`×(n/2−8), query with `C`×8 instead of `G`×8, and the same disabled-accessibility +options. All other flags remain identical. + + +### Audit-fix comparison + +Five interleaved fresh-process runs per configuration compare the retained +`fa2f84d` binary with the rebuilt audit fix. All complete SVG, CSV and ensemble +outputs match byte-for-byte on these native-energy inputs. Time is median +[min, max] seconds; RSS is the maximum over the five runs, in KiB. + +| Input/output | Before time | Fixed time | Before RSS | Fixed RSS | +| --- | ---: | ---: | ---: | ---: | +| OxyS SVG | 0.1938 [0.1932, 0.1968] | 0.1934 [0.1918, 0.1938] | 22,132 | 20,720 | +| OxyS both | 0.1965 [0.1945, 0.1976] | 0.1959 [0.1955, 0.1967] | 22,004 | 20,596 | +| sparse400 SVG | 0.5020 [0.5003, 0.7993] | 0.7491 [0.4806, 0.9672] | 87,876 | 84,548 | +| sparse400 both | 0.5553 [0.5441, 0.8527] | 0.7544 [0.5118, 0.8899] | 87,872 | 84,420 | +| sparse1000 SVG | 4.0260 [3.9535, 5.8240] | 3.8527 [3.8373, 5.5779] | 470,084 | 301,128 | +| sparse1000 both | 4.2407 [4.2148, 6.2242] | 4.0592 [4.0216, 4.1580] | 469,960 | 301,260 | + +Transferring the document buffer reduces 1000×1000 SVG peak RSS by **35.9%**. +There is no general elapsed-time speedup claim: filesystem writes varied widely, +and the sparse400 median increased even though its median process CPU time fell +from 0.44 to 0.42 seconds (SVG) and 0.48 to 0.46 seconds (both). Dense40 controls +remain comparable: ensemble-only 0.3848→0.3902 seconds (+1.4%) and CSV +0.8982→0.8869 seconds (−1.3%), with unchanged output bytes. The output is still +buffered as a full document, so very large matrices remain memory intensive. + +One comparison process received an unexplained SIGTERM after the shorter cases; +partial long-case warmups were discarded and the long case completed in a new +process. The table includes only complete five-run groups. + +To separate serializer cost from file-write stalls, a final sparse400 control +sent SVG to `/dev/null` and ensemble output to STDOUT redirected to `/dev/null`. +Seven interleaved runs gave wall medians 0.412419→0.390979 seconds (ranges +0.411222–0.413401 and 0.388902–0.392267), CPU medians 0.40→0.38 seconds, and peak +RSS 87,744→84,544 KiB. The slowdown did not reproduce without filesystem writes; +the fixed serializer was 5.2% faster in this specific control. Use the same +sparse400 command with `--out=STDOUT --out=bpsvg:/dev/null > /dev/null` to repeat. diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg index fd048c48..aceeb06c 100644 --- a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.svg @@ -10,6 +10,7 @@ C(i)=(vᵢ,jᵢ) retains one hybrid weight and right boundary for each left pair i. Use d=BW(E_init), t(i,k)=BW(E_interLeft(i,k)); all pair coordinates increase internally. +B(i,j)=BW(energy.getE(i₁,j₁,i₂,j₂,0)) is the complete-boundary Boltzmann factor. Enumerate candidates at i, visiting i in decreasing order @@ -23,15 +24,15 @@ jₖ Lonely pairs allowed: - initial candidate (d, i) + initial candidate (d, i), only if !noGUend or i is not GU continuation (t(i,k) · vₖ, jₖ) for each reachable k ≻ i within loop limits and the complete span limits. Accumulate every considered candidate updateZ(i,j,v,true) -Adds B(i,j)v to Zall and v to the boundary map. -B is the admitted complete-boundary Boltzmann factor. +For admitted complete sites: add B(i,j)v to Zall +and v to the boundary map; otherwise discard the update. Retain only the best continuation at i C(i) ← argmin E_full(i,j,v) @@ -40,7 +41,7 @@ noLP candidate set Let i⁺=i+(1,1) be complementary and s=t(i,i⁺). -Initialize (s·d, i⁺); add direct continuation (s·vᵢ⁺, jᵢ⁺). +Initialize (s·d, i⁺) only if !noGUend or i⁺ is not GU; direct continuation is (s·vᵢ⁺, jᵢ⁺). Other candidates are (s·t(i⁺,k)·vₖ, jₖ), with k ≻ i⁺ and k ≠ i⁺+(1,1). The adjacent continuation is excluded from the loop sum because it was handled directly. The boundary map sums contributions, but the DP cell keeps one right continuation: this is a restricted ensemble. diff --git a/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg b/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg index 0a10ebe9..0eabab07 100644 --- a/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg +++ b/doc/recursions/IntaRNA.PredictorMfeEnsSeedOnly.svg @@ -28,7 +28,8 @@ e H_seed = E_seed(a) + E_init E_full = energy.getE(a₁,e₁,a₂,e₂,H_seed) -Zall += BW(E_full) when needZall is enabled +Zall += BW(E_full) +Only when needZall is enabled and complete-site filters pass. 3 Report and trace the retained seed updateOptima(..., isHybridE=true, incrementZall=true) applies the complete-site filters and ranking. diff --git a/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg b/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg index b944efbb..e41542a1 100644 --- a/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg +++ b/doc/recursions/IntaRNA.SeededPartitionFunction.inside.svg @@ -35,12 +35,12 @@ For Uℓ: let ℓ′=min(C,ℓ+1); D=Uℓ′ unless the ending seed is accepted, then D=S₂. For S₁ or S₂: D=S₂. Once seeded, a later seed never creates a second copy of the path. -2 Aggregate eligible departures, then handle nonstack edges -T₀(q)=∑ H(q,Uℓ) over ℓ≥(noLP ? 2 : 1) -T₁(q)=H(q,S₂) + (noLP ? 0 : H(q,S₁)) -For each nonstack r→q and b∈{0,1}: H(q,D(b,loop)) += t(r,q) · T_b(r) -Nonstack means both coordinates increase, within loop limits, excluding r=q−(1,1). -Reset the suffix to 1; remain seeded if b=1, or accept a singleton seed at q if b=0. +2 Add nonstack arrivals, then aggregate eligible departures +For each nonstack r→q and b∈{0,1}: H(q,D(b,loop)) += t(r,q) · T_b(r) +T₀(q)=∑ H(q,Uℓ) over ℓ≥(noLP ? 2 : 1) +T₁(q)=H(q,S₂) + (noLP ? 0 : H(q,S₁)) +T_b(r) is already computed. Nonstack: strict increase within loop limits, excluding r=q−(1,1). +Reset the suffix to 1; remain seeded if b=1, or accept a singleton seed at q if b=0. 3 Complete seeded paths at every q Z += B(p,q) · T₁(q) diff --git a/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg index c98b9271..fe546876 100644 --- a/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg +++ b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg @@ -45,7 +45,7 @@ 3b Reverse a nonstack edge r→q a = A(q,D(b,loop)) · t(r,q) -Gᵦ(r) += a +G_b(r) += a M(q) += a · T_b(r) Repeat for b=0 and b=1 using the same forward totals. Do not mark the unpaired nucleotides in the loop. diff --git a/src/IntaRNA/BasePairProbabilitySvg.cpp b/src/IntaRNA/BasePairProbabilitySvg.cpp index 1b9e5b91..fda2d1cd 100644 --- a/src/IntaRNA/BasePairProbabilitySvg.cpp +++ b/src/IntaRNA/BasePairProbabilitySvg.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace IntaRNA { namespace { @@ -42,10 +43,10 @@ size_t colorClass(Z_type probability) return breaks.size()-2; } -Z_type unpaired(const Accessibility & accessibility,size_t i,const InteractionEnergy & energy) +Z_type unpaired(const Accessibility & accessibility,size_t i,Z_type RT) { const E_type ed=accessibility.getED(i,i); - const Z_type p=E_isINF(ed)?Z_type(0):energy.getBoltzmannWeight(ed); + const Z_type p=E_isINF(ed)?Z_type(0):Z_exp(-E_2_Z(ed)/RT); if (!std::isfinite(p) || p<0 || p>1) throw std::runtime_error("bpsvg: invalid single-nucleotide unpaired probability"); return p; @@ -54,8 +55,11 @@ Z_type unpaired(const Accessibility & accessibility,size_t i,const InteractionEn } // namespace std::string -BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const InteractionEnergy & energy) +BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const InteractionEnergy & energy, + Z_type targetRT,Z_type queryRT) { + if (!std::isfinite(targetRT) || targetRT<=0 || !std::isfinite(queryRT) || queryRT<=0) + throw std::invalid_argument("bpsvg: accessibility energy scales must be positive and finite"); const auto & at=energy.getAccessibility1(); const auto & aq=energy.getAccessibility2().getAccessibilityOrigin(); const auto & t=at.getSequence(); @@ -131,7 +135,7 @@ text { font-family: sans-serif; fill: #243247; } const auto & seq=target?t:q; const auto & acc=target?at:aq; for(size_t i=0;i\n"; - return out.str(); + // Transfer the potentially large document instead of copying its buffer. + return std::move(out).str(); } -void BasePairProbabilityWriter::writeSvg(std::ostream & out,const BasePairProbabilities & result,const InteractionEnergy & energy) +void BasePairProbabilityWriter::writeSvg(std::ostream & out,const BasePairProbabilities & result,const InteractionEnergy & energy, + Z_type targetRT,Z_type queryRT) { - emit(out,svgBlock(result,energy)); + emit(out,svgBlock(result,energy,targetRT,queryRT)); } -void BasePairProbabilityWriter::writeSvgFile(const std::string & filename,const BasePairProbabilities & result,const InteractionEnergy & energy) +void BasePairProbabilityWriter::writeSvgFile(const std::string & filename,const BasePairProbabilities & result,const InteractionEnergy & energy, + Z_type targetRT,Z_type queryRT) { - emitFile(filename,svgBlock(result,energy)); + emitFile(filename,svgBlock(result,energy,targetRT,queryRT)); } } // namespace IntaRNA diff --git a/src/IntaRNA/BasePairProbabilityWriter.cpp b/src/IntaRNA/BasePairProbabilityWriter.cpp index 6d679fdd..d38a744b 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.cpp +++ b/src/IntaRNA/BasePairProbabilityWriter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace IntaRNA { @@ -25,7 +26,7 @@ std::string BasePairProbabilityWriter::block(const BasePairProbabilities & resul for(size_t j=0;j::infinity(), + std::numeric_limits::quiet_NaN()}) { + std::ostringstream invalid; + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(invalid,r,energy,rt,1)); + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(invalid,r,energy,1,rt)); + REQUIRE(invalid.str().empty()); + } + BasePairProbabilityWriter::writeSvg(svg,r,energy,1,1); REQUIRE(svg.str().find("target<&")!=std::string::npos); REQUIRE(svg.str().find("Orange outline:")!=std::string::npos); std::ostringstream broken;broken.setstate(std::ios::badbit); - REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(broken,r,energy)); + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(broken,r,energy,1,1)); r.fail();std::ostringstream failed; - REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(failed,r,energy)); + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(failed,r,energy,1,1)); REQUIRE(failed.str().empty()); BasePairProbabilities missing(4,5,true); REQUIRE_THROWS(missing.addRegion({0,1},{1,2},2,mass)); @@ -82,10 +89,10 @@ TEST_CASE("seed annotations commit with regional masses and SVG requires success BasePairProbabilities plain(4,5); REQUIRE_FALSE(plain.collectsSeedPairs());REQUIRE(plain.seedPairs().size1()==0); plain.finalize();std::ostringstream empty; - BasePairProbabilityWriter::writeSvg(empty,plain,energy); + BasePairProbabilityWriter::writeSvg(empty,plain,energy,1,1); REQUIRE(empty.str().find("data-probability=\"NA\"")!=std::string::npos); REQUIRE(empty.str().find("Orange outline:")==std::string::npos); BasePairProbabilities wrongSize(1,1);wrongSize.finalize();std::ostringstream wrong; - REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(wrong,wrongSize,energy)); + REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(wrong,wrongSize,energy,1,1)); REQUIRE(wrong.str().empty()); } diff --git a/tests/testBasePairProbabilitySvg.py b/tests/testBasePairProbabilitySvg.py index 29047ef3..55e5e576 100644 --- a/tests/testBasePairProbabilitySvg.py +++ b/tests/testBasePairProbabilitySvg.py @@ -18,6 +18,8 @@ def run(t='GGAGGG', q='CCCC', options=(), success=True): keys = {o.split('=')[0] for o in options} + if keys & {'--tAcc', '--qAcc'}: + keys.add('--acc') p = subprocess.run([BIN, f'--target={t}', f'--query={q}', *[o for o in COMMON if o.split('=')[0] not in keys], *options], text=True, capture_output=True) @@ -112,17 +114,48 @@ def plot(t='GGAGGG', q='CCCC', opts=()): named = plot(opts=[f'--tId={name}']) assert name in named.find('s:title', NS).text assert name+" (5' to 3')" in [e.text for e in named.findall('.//s:text', NS)] - # Native accessibility values must use original (not reversed) query order. + # Accessibility uses its own per-strand scale and original query order. pu_t, pu_q = d/'target.pu', d/'query.pu' - native = plot('GGGGAAAACCCC', 'GCGCGAAAACGCGC', - ['--energy=V', '--acc=C', f'--out=tPu:{pu_t}', f'--out=qPu:{pu_q}']) - for strand, path in (('target', pu_t), ('query', pu_q)): - expected_pu = [float(line.split()[1]) for line in path.read_text().splitlines() - if line.strip() and not line.lstrip().startswith('#')] - assert any(0 < v < .9 for v in expected_pu) - for r in native.findall(f'.//s:rect[@data-type="unpaired"][@data-strand="{strand}"]', NS): - assert math.isclose(float(r.attrib['data-probability']), expected_pu[int(r.attrib['data-index'])-1], - rel_tol=2e-5, abs_tol=1e-6) + + def one_nt(path): + return [float(line.split()[1]) for line in path.read_text().splitlines() + if line.strip() and not line.lstrip().startswith('#')] + + def check_frame(root, target_pu, query_pu): + for strand, values in (('target', target_pu), ('query', query_pu)): + assert any(0 < v < .9 for v in values) + for r in root.findall(f'.//s:rect[@data-type="unpaired"][@data-strand="{strand}"]', NS): + assert math.isclose(float(r.attrib['data-probability']), values[int(r.attrib['data-index'])-1], + rel_tol=2e-5, abs_tol=1e-6), (strand, r.attrib, values) + + for temperature in (22, 37): + acc_options = [f'--temperature={temperature}', f'--out=tPu:{pu_t}', f'--out=qPu:{pu_q}'] + native = plot('GGGGAAAACCCC', 'GCGCGAAAACGCGC', ['--energy=V', '--acc=C', *acc_options]) + check_frame(native, one_nt(pu_t), one_nt(pu_q)) + input_t, input_q = d/'input-target.pu', d/'input-query.pu' + input_t.write_bytes(pu_t.read_bytes()); input_q.write_bytes(pu_q.read_bytes()) + for t_mode, q_mode in (('P', 'P'), ('P', 'C'), ('C', 'P'), ('E', 'E'), ('C', 'C')): + ed_t, ed_q = d/'target.ed', d/'query.ed' + options = ['--energy=B', f'--tAcc={t_mode}', f'--qAcc={q_mode}', *acc_options] + if t_mode in ('P', 'E'): + options.append(f'--tAccFile={input_t if t_mode == "P" else ed_t}') + if q_mode in ('P', 'E'): + options.append(f'--qAccFile={input_q if q_mode == "P" else ed_q}') + # The first imported-Pu run also supplies ED fixtures for the E case. + if (t_mode, q_mode) in (('P', 'P'), ('C', 'C')): + options += [f'--out=tAcc:{ed_t}', f'--out=qAcc:{ed_q}'] + converted = plot('GGGGAAAACCCC', 'GCGCGAAAACGCGC', options) + check_frame(converted, one_nt(pu_t), one_nt(pu_q)) + for mode, input_pu, output_pu in ((t_mode, input_t, pu_t), (q_mode, input_q, pu_q)): + if mode in ('P', 'E'): + # Independent input reference; ED quantization loses <2% + # at either temperature, but using interaction RT loses far more. + assert all(math.isclose(actual, original, rel_tol=.02, abs_tol=1e-10) + for actual, original in zip(one_nt(output_pu), one_nt(input_pu))) + if t_mode == q_mode == 'C': + # Nussinov accessibility has RT=1, independent of temperature. + check_frame(converted, [math.exp(-ed) for ed in one_nt(ed_t)], + [math.exp(-ed) for ed in one_nt(ed_q)]) for t,q in (('AAAA','AAAA'), ('G','C')): empty = plot(t,q) assert all(r.attrib['data-probability'] == 'NA' for r in pairs(empty).values()) From b492ed695b0ba66ebcc84988347c4bcdd9bf7edc Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 22:14:58 +0200 Subject: [PATCH 12/30] feat: revise probability SVG layout and highlight best interaction --- ChangeLog | 13 ++++ README.md | 28 +++++---- src/IntaRNA/BasePairProbabilitySvg.cpp | 84 ++++++++++++++++++------- src/IntaRNA/BasePairProbabilityWriter.h | 11 ++-- src/bin/CommandLineParsing.cpp | 9 +-- src/bin/CommandLineParsing.h | 8 ++- src/bin/IntaRNA.cpp | 8 ++- tests/testBasePairProbabilitySvg.py | 65 ++++++++++++++++--- 8 files changed, 169 insertions(+), 57 deletions(-) diff --git a/ChangeLog b/ChangeLog index 44268e50..19fb5983 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ ## Interface and handling +- revise SVG axes, sequence labels and rounded tooltips; show intramolecular + pairing (1-Pu) frames and the best interaction region (PR #259 review) + - BUGFIX : use each strand's accessibility energy scale for SVG frames and unpaired-probability exports, including mixed computed/imported inputs @@ -169,6 +172,16 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * BasePairProbabilitySvg, BasePairProbabilityWriter, CommandLineParsing, IntaRNA : + * place target origin at bottom left and query name below the matrix; include + nondefault sequence names in a subtitle and names in rounded hover values + * show 1-Pu intramolecular pairing probabilities on the nucleotide frame + + outline the best reported interaction region in green, including when + ranked output is disabled; preserve ordinary output counts (PR #259 review) + * README.md, tests/testBasePairProbabilitySvg.py : + * document and cover coordinates, frame probabilities, tooltips and MFE bounds + 261009 Alexander Mitrofanov * BasePairProbabilityWriter, BasePairProbabilitySvg, CommandLineParsing : * pass and validate separate target/query accessibility RT values for SVG diff --git a/README.md b/README.md index d627414e..ca099554 100644 --- a/README.md +++ b/README.md @@ -1876,7 +1876,7 @@ targeted file/stream name: - `qSpotProb:`/`tSpotProb:` [query/target's spot probability profile](#profileSpotProb) (CSV format), respectively - `spotProb:` [all spot probabilities](#spotProb) (CSV format) -- `bpsvg:` [SVG base-pair probability dot plot](#bpsvg), with accessibility frames and seed outlines +- `bpsvg:` [SVG base-pair probability dot plot](#bpsvg), with pairing-probability frames and seed/MFE outlines - `bpProb:` [actual base-pair probabilities](#bpProb) for exact stack-seeded ensembles (CSV matrix) - `qMinE:`/`tMinE:` [the query/target's minimal interaction energy profile](profileMinE) (CSV format), respectively - `pMinE:` [minimal interaction energy for all query-target index pairs](pairMinE) (CSV format) @@ -2082,15 +2082,18 @@ IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ --model=P --mode=M --out=bpsvg:pairs.svg --out=bpProb:pairs.csv ``` -The query is the horizontal axis and the target the vertical axis, both in -original 5' to 3' order. Sequence names label the axes; nucleotides appear on all -four sides. Their colored backgrounds show **single-nucleotide unpaired -probabilities**, computed from the prediction accessibility model's opening -energies. These are distinct from the interaction-conditional probabilities in -the matrix. Disabled accessibility gives probability one at unconstrained bases. -Each probability rectangle has a hover title with its index (pair), type and -full-precision value. An empty interaction ensemble uses gray `NA` cells while -its accessibility frame remains defined. +The query runs left to right and the target bottom to top, both in original +5' to 3' order, starting at the bottom-left corner. Sequence names label the +axes, with the query name below the matrix. A subtitle names both sequences +when either name differs from the defaults `query` and `target`. Nucleotides +appear on all four sides. Their colored backgrounds show **intramolecular +pairing probability**, `1-Pu`, computed from the accessibility model's opening +energies. These are distinct from the interaction-conditional pair probabilities +in the matrix. Disabled accessibility gives zero at unconstrained bases. +Each probability rectangle has a hover title with sequence names, indices, +probability type and three decimal places (scientific notation with two decimal +places below 0.001). Full-precision values remain in `data-probability` attributes. +An empty interaction ensemble uses gray `NA` cells while its frame remains defined. A shared seven-step, white-to-dark-blue palette encodes both probability types: `[0,0.01)`, `[0.01,0.1)`, `[0.1,0.25)`, `[0.25,0.5)`, `[0.5,0.75)`, @@ -2110,7 +2113,10 @@ contained in searched regions, including overlapping, singleton and mixed-length explicit seeds. This annotation uses the predictor's retained seed family and span limits; it is independent of ranked interaction reporting and can include zero-probability cells. Extension-only pairs have no outline. A seed legend is -included whenever an outline is present. +included whenever an outline is present. A green rectangle encloses the best +reported interaction's site, with a matching legend. The best site is retained +for SVG even with `--outNumber=0`, without adding ranked text output. If no site +passes the reporting filters, the rectangle and its legend are omitted. Filenames support `.gz` compression and the usual `-t#q#` multi-FASTA suffixes. `STDOUT` and `STDERR` emit each complete SVG as one synchronized block. To obtain diff --git a/src/IntaRNA/BasePairProbabilitySvg.cpp b/src/IntaRNA/BasePairProbabilitySvg.cpp index fda2d1cd..d350547f 100644 --- a/src/IntaRNA/BasePairProbabilitySvg.cpp +++ b/src/IntaRNA/BasePairProbabilitySvg.cpp @@ -1,5 +1,6 @@ #include "IntaRNA/BasePairProbabilityWriter.h" #include "IntaRNA/InteractionEnergy.h" +#include "IntaRNA/Interaction.h" #include #include #include @@ -56,7 +57,7 @@ Z_type unpaired(const Accessibility & accessibility,size_t i,Z_type RT) std::string BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const InteractionEnergy & energy, - Z_type targetRT,Z_type queryRT) + Z_type targetRT,Z_type queryRT,const Interaction * mfe) { if (!std::isfinite(targetRT) || targetRT<=0 || !std::isfinite(queryRT) || queryRT<=0) throw std::invalid_argument("bpsvg: accessibility energy scales must be positive and finite"); @@ -72,21 +73,35 @@ BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const I const auto p=empty?Matrix():result.probabilities(); const double w=cell*q.size(), h=cell*t.size(); // Leave space for names even for very short sequences; keep a fixed nt pitch. - const double x=std::max(130.0,(std::max(820.0,9.0*q.getId().size()+80)-w)/2); + const double nameWidth=std::max(9.0*q.getId().size()+80,7.0*(t.getId().size()+q.getId().size())+100); + const double x=std::max(130.0,(std::max(820.0,nameWidth)-w)/2); const double y=std::max(120.0,(9.0*t.getId().size()-h)/2+60); - const double width=2*x+w, height=2*y+h+160; + const double width=2*x+w, height=2*y+h+185; + // SVG y increases downwards, while target coordinates increase upwards. + auto targetY=[&](size_t i) { return y+cell*(t.size()-1-i); }; + const bool markMfe=mfe && !mfe->isEmpty(); + size_t tStart=t.size(),tEnd=0,qStart=q.size(),qEnd=0; + if (markMfe) { + for (const auto & bp:mfe->basePairs) { + if (bp.first>=t.size() || bp.second>=q.size()) + throw std::invalid_argument("bpsvg: MFE interaction coordinates exceed sequence dimensions"); + tStart=std::min(tStart,bp.first); tEnd=std::max(tEnd,bp.first); + qStart=std::min(qStart,bp.second); qEnd=std::max(qEnd,bp.second); + } + } std::ostringstream out; out.imbue(std::locale::classic()); out<::max_digits10); out<<"\n" <<"Base-pair probabilities: "<\n" - <<"Target rows and query columns run 5' to 3'. Pair probabilities are conditional on an allowed interaction. " - <<"The frame shows single-nucleotide unpaired probabilities from the accessibility model.\n" + <<"Target rows run 5' to 3' upwards and query columns run 5' to 3' to the right. Pair probabilities are conditional on an allowed interaction. " + <<"The frame shows single-nucleotide intramolecular pairing probabilities (1 - Pu) from the accessibility model.\n" < text { font-family: sans-serif; fill: #243247; } .heading { font-size: 18px; font-weight: bold; } .axis { font-size: 15px; } +.subtitle { font-size: 13px; } .index, .legend { font-size: 11px; } .nt { font-family: monospace; font-size: 13px; pointer-events: none; } .nt-light { fill: #fff; } @@ -104,6 +119,7 @@ text { font-family: sans-serif; fill: #243247; } .origin { stroke: #888; stroke-width: 1.2; stroke-dasharray: 3 2; } .border { fill: none; stroke: #aaa; stroke-width: 0.7; pointer-events: none; } .seed { fill: none; stroke: #c65d00; stroke-width: 1.8; pointer-events: none; } +.mfe { fill: none; stroke: #16823b; stroke-width: 2.5; pointer-events: none; } )SVG"; out<<"\n"; @@ -111,7 +127,9 @@ text { font-family: sans-serif; fill: #243247; } out<<""<\n"; }; text(width/2,30,"Base-pair probabilities","heading"); - text(x+w/2,y-65,q.getId()+" (5' to 3')","axis"); + if (t.getId()!="target" || q.getId()!="query") + text(width/2,53,t.getId()+" / "+q.getId(),"subtitle"); + text(x+w/2,y+h+65,q.getId()+" (5' to 3')","axis"); out<<""<\n"; auto rect=[&](double rx,double ry,Z_type value,bool missing,const std::string & attributes,const std::string & title) { @@ -120,28 +138,33 @@ text { font-family: sans-serif; fill: #243247; } <<" data-probability=\""; if (missing) out<<"NA"; else out<"<<xml(title)<<": "; - if (missing) out<<"NA (empty interaction ensemble)"; else out<<value; + if (missing) out<<"NA (empty interaction ensemble)"; + else { + if (value<0.001) out<<std::scientific<<std::setprecision(2); + else out<<std::fixed<<std::setprecision(3); + out<<value<<std::defaultfloat<<std::setprecision(std::numeric_limits<Z_type>::max_digits10); + } out<<"\n"; }; out<<"\n"; for(size_t i=0;i\n\n"; for(bool target:{false,true}) { const auto & seq=target?t:q; const auto & acc=target?at:aq; for(size_t i=0;i=.75?"nt nt-light":"nt"); } } @@ -158,12 +181,12 @@ text { font-family: sans-serif; fill: #243247; } const double offset=cell*(i+(idx>0 && !origin?1:0)); if (idx%10==0 || origin) out<<"\n"; + <<"\" x1=\""<<(target?x-cell:x+offset)<<"\" y1=\""<<(target?y+h-offset:y-cell) + <<"\" x2=\""<<(target?x+w+cell:x+offset)<<"\" y2=\""<<(target?y+h-offset:y+h+cell)<<"\"/>\n"; const std::string label=idx==1?"+1":std::to_string(idx); if(target) { - text(x-26,y+cell*(i+.5)+4,label,"index","end"); - text(x+w+26,y+cell*(i+.5)+4,label,"index","start"); + text(x-26,targetY(i)+cell/2+4,label,"index","end"); + text(x+w+26,targetY(i)+cell/2+4,label,"index","start"); } else { text(x+cell*(i+.5),y-28,label,"index"); text(x+cell*(i+.5),y+h+38,label,"index"); @@ -177,12 +200,20 @@ text { font-family: sans-serif; fill: #243247; } for(size_t i=0;i\n"; + <<"\" x=\""<\n"; } out<<"\n"; } - const double legendX=(width-7*86)/2, legendY=y+h+85; - text(width/2,legendY-14,"Probability ranges (base pairs and single-nucleotide accessibility)","legend"); + if (markMfe) { + out<<"\n"; + } + const double legendX=(width-7*86)/2, legendY=y+h+110; + text(width/2,legendY-14,"Probability ranges (intermolecular base pairs and intramolecular pairing)","legend"); out<<"\n"; for(size_t k=0;k\n"; @@ -196,6 +227,11 @@ text { font-family: sans-serif; fill: #243247; } out<<"\n"; text(legendX+28,legendY+61,"Orange outline: pair in an admitted seed within a searched region","legend","start"); } + if (markMfe) { + const double mfeLegendY=legendY+(annotated?76:48); + out<<"\n"; + text(legendX+28,mfeLegendY+13,"Green outline: minimum-free-energy interaction region","legend","start"); + } if(empty) text(width/2,legendY+90,"Gray / NA: empty interaction ensemble; accessibility remains defined","legend"); out<<"\n"; // Transfer the potentially large document instead of copying its buffer. @@ -203,15 +239,15 @@ text { font-family: sans-serif; fill: #243247; } } void BasePairProbabilityWriter::writeSvg(std::ostream & out,const BasePairProbabilities & result,const InteractionEnergy & energy, - Z_type targetRT,Z_type queryRT) + Z_type targetRT,Z_type queryRT,const Interaction * mfe) { - emit(out,svgBlock(result,energy,targetRT,queryRT)); + emit(out,svgBlock(result,energy,targetRT,queryRT,mfe)); } void BasePairProbabilityWriter::writeSvgFile(const std::string & filename,const BasePairProbabilities & result,const InteractionEnergy & energy, - Z_type targetRT,Z_type queryRT) + Z_type targetRT,Z_type queryRT,const Interaction * mfe) { - emitFile(filename,svgBlock(result,energy,targetRT,queryRT)); + emitFile(filename,svgBlock(result,energy,targetRT,queryRT,mfe)); } } // namespace IntaRNA diff --git a/src/IntaRNA/BasePairProbabilityWriter.h b/src/IntaRNA/BasePairProbabilityWriter.h index 56acabf3..64773536 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.h +++ b/src/IntaRNA/BasePairProbabilityWriter.h @@ -7,6 +7,7 @@ namespace IntaRNA { class InteractionEnergy; +class Interaction; /** Explicit writer for a completed actual-pair matrix, independent of trackers. * Whole blocks are synchronized for shared streams. No destructor emits data. */ @@ -32,7 +33,7 @@ class BasePairProbabilityWriter { static void writeFile(const std::string & filename,const BasePairProbabilities & result, const RnaSequence & target,const RnaSequence & query,const std::string & separator=";"); /** Write a complete SVG dot plot with original target rows/query columns. - * Includes 1-nt accessibility, signed coordinate guides and a CSS color legend. + * Includes intramolecular pairing, signed coordinate guides and a CSS color legend. * Seed outlines are included if the result collected admitted seed pairs. * @param out destination stream; an I/O failure throws * @param result explicitly finalized raw result @@ -40,9 +41,10 @@ class BasePairProbabilityWriter { * @param targetRT positive finite energy scale used to convert target ED to Pu * @param queryRT positive finite energy scale used to convert query ED to Pu; * accessibility scales can differ from the interaction model's RT + * @param mfe optional best interaction in original coordinates for a region outline */ static void writeSvg(std::ostream & out,const BasePairProbabilities & result, - const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT); + const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT,const Interaction * mfe = nullptr); /** Write a complete SVG to a file or STDOUT/STDERR, including gzip support. * Validates the whole document before opening the destination. * @param filename destination (already expanded for multi-FASTA) @@ -50,12 +52,13 @@ class BasePairProbabilityWriter { * @param energy original, unshifted energy/accessibility model * @param targetRT positive finite target accessibility energy scale * @param queryRT positive finite query accessibility energy scale + * @param mfe optional best interaction in original coordinates for a region outline */ static void writeSvgFile(const std::string & filename,const BasePairProbabilities & result, - const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT); + const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT,const Interaction * mfe = nullptr); private: static std::string svgBlock(const BasePairProbabilities & result,const InteractionEnergy & energy, - Z_type targetRT,Z_type queryRT); + Z_type targetRT,Z_type queryRT,const Interaction * mfe); static void emitFile(const std::string & name,const std::string & data); static std::string block(const BasePairProbabilities & result,const RnaSequence & target, const RnaSequence & query,const std::string & separator); diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index 756d8b0f..7f6f1423 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -2234,7 +2234,7 @@ getEnergyHandler( const Accessibility& accTarget, const ReverseAccessibility& ac OutputConstraint CommandLineParsing:: -getOutputConstraint( const InteractionEnergy & energy ) const +getOutputConstraint( const InteractionEnergy & energy, bool forPrediction ) const { checkIfParsed(); if (!rri.empty()) { @@ -2250,7 +2250,7 @@ getOutputConstraint( const InteractionEnergy & energy ) const default : throw std::runtime_error("CommandLineParsing::getOutputConstraint() : unsupported outOverlap value "+toString(outOverlap.val)); } return OutputConstraint( - outNumber.val + (forPrediction && !outPrefix2streamName.at(OP_bpsvg).empty() ? std::max(size_t(1),size_t(outNumber.val)) : outNumber.val) , overlap , Ekcal_2_E(outMaxE.val) , Ekcal_2_E(outDeltaE.val) @@ -2501,14 +2501,15 @@ BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const I throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); return new BasePairProbabilities(energy.size1(),energy.size2(),!outPrefix2streamName.at(OP_bpsvg).empty()); } -void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const { +void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy, + const Interaction * mfe) const { const auto & t=energy.getAccessibility1().getSequence(); const auto & q=energy.getAccessibility2().getAccessibilityOrigin().getSequence(); if (!outPrefix2streamName.at(OP_bpProb).empty()) BasePairProbabilityWriter::writeFile(getFullFilename(outPrefix2streamName.at(OP_bpProb),&t,&q),result,t,q,outSep); if (!outPrefix2streamName.at(OP_bpsvg).empty()) BasePairProbabilityWriter::writeSvgFile(getFullFilename(outPrefix2streamName.at(OP_bpsvg),&t,&q),result,energy, - getAccessibilityRT(tAcc.val),getAccessibilityRT(qAcc.val)); + getAccessibilityRT(tAcc.val),getAccessibilityRT(qAcc.val),mfe); } Predictor* diff --git a/src/bin/CommandLineParsing.h b/src/bin/CommandLineParsing.h index 14e010f5..f0e796ad 100644 --- a/src/bin/CommandLineParsing.h +++ b/src/bin/CommandLineParsing.h @@ -282,7 +282,7 @@ class CommandLineParsing { * parameters * @param energy the interaction energy handler to be used * @param output the output handler to be used - * @param pairProbabilities optional non-owning raw result sink for exact stack seeds + * @param pairProbabilities optional non-owning raw result sink for supported ensemble predictors * @return the newly allocated Predictor object to be deleted by the calling * function */ @@ -296,7 +296,8 @@ class CommandLineParsing { */ BasePairProbabilities * getBasePairProbabilityResult(const InteractionEnergy & energy) const; /** Publish a successful sequence-pair result using ordinary filename rules. */ - void writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy) const; + void writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy, + const Interaction * mfe = nullptr) const; /** * Provides the seed constraint according to the user settings @@ -331,9 +332,10 @@ class CommandLineParsing { /** * The constraints to be applied to the interaction output generation * @param energy the interaction energy to be used for computation + * @param forPrediction retain a best site for SVG even with zero ranked output * @return the output constraints to be applied */ - OutputConstraint getOutputConstraint( const InteractionEnergy & energy ) const; + OutputConstraint getOutputConstraint( const InteractionEnergy & energy, bool forPrediction = false ) const; /** * The stream to write the interaction output to diff --git a/src/bin/IntaRNA.cpp b/src/bin/IntaRNA.cpp index bb971e29..e67b4876 100644 --- a/src/bin/IntaRNA.cpp +++ b/src/bin/IntaRNA.cpp @@ -239,9 +239,9 @@ int main(int argc, char **argv){ // setup collecting output handler to ensure // k-best output per query-target combination // and not per region combination if not requested - OutputHandlerInteractionList bestInteractions( parameters.getOutputConstraint(*energy), + OutputHandlerInteractionList bestInteractions( parameters.getOutputConstraint(*energy,true), (parameters.reportBestPerRegion() ? std::numeric_limits::max() : 1 ) - * parameters.getOutputConstraint(*energy).reportMax ); + * parameters.getOutputConstraint(*energy,true).reportMax ); // One owner spans all disjoint searched regions for this sequence pair. std::unique_ptr pairProbabilities(parameters.getBasePairProbabilityResult(*energy)); @@ -356,12 +356,14 @@ int main(int argc, char **argv){ : (*bestInteractions.begin())->energy + bestInteractions.getOutputConstraint().deltaE; // The collector is sorted by energy, so all later entries are worse. for( const Interaction * inter : bestInteractions) { + if (output->getOutputConstraint().reportMax==0) break; if (inter->energy > maxMergedE) { break; } output->add(*inter); } } - if (pairProbabilities) parameters.writeBasePairProbabilities(*pairProbabilities,*energy); + if (pairProbabilities) parameters.writeBasePairProbabilities(*pairProbabilities,*energy, + bestInteractions.empty()?nullptr:*bestInteractions.begin()); } // successful sequence-pair computation #if INTARNA_MULITHREADING diff --git a/tests/testBasePairProbabilitySvg.py b/tests/testBasePairProbabilitySvg.py index 55e5e576..597afd6a 100644 --- a/tests/testBasePairProbabilitySvg.py +++ b/tests/testBasePairProbabilitySvg.py @@ -55,6 +55,15 @@ def plot(t='GGAGGG', q='CCCC', opts=()): assert len(cells) == 24 assert '+1' in [e.text for e in root.findall('.//s:text[@class="index"]', NS)] assert not root.findall('.//s:line[@data-index="1"]', NS) + assert root.find('.//s:text[@class="subtitle"]', NS) is None + query_axis = root.find('.//s:text[@class="axis"][@x]', NS) + assert query_axis.text == "query (5' to 3')" + assert float(query_axis.attrib['y']) > max(float(r.attrib['y']) for r in cells.values())+20 + # Original target coordinate 1 is at the bottom; query coordinate 1 at left. + for i in range(1, 7): + for j in range(1, 5): + assert float(cells[i, j].attrib['y']) == float(cells[1, 1].attrib['y'])-20*(i-1) + assert float(cells[i, j].attrib['x']) == float(cells[1, 1].attrib['x'])+20*(j-1) for i, row in enumerate(rows[1:], 1): for j, value in enumerate(row[1:], 1): r = cells[i, j] @@ -62,22 +71,57 @@ def plot(t='GGAGGG', q='CCCC', opts=()): bounds = [0.01, 0.1, 0.25, 0.5, 0.75, 0.9] expected_color = sum(float(value) >= boundary for boundary in bounds) assert f'p{expected_color}' in r.attrib['class'].split() - assert 'Base-pair probability' in r.find('s:title', NS).text + title = r.find('s:title', NS).text + assert title.startswith(f'Base-pair probability (target {i}, query {j}): ') + formatted = f'{float(value):.2e}' if float(value) < .001 else f'{float(value):.3f}' + assert title.endswith(': '+formatted), title assert all(float(cells[3, j].attrib['data-probability']) == 0 for j in range(1, 5)) - frame = root.findall('.//s:rect[@data-type="unpaired"]', NS) + frame = root.findall('.//s:rect[@data-type="intramolecular-pairing"]', NS) assert len(frame) == 2*(6+4) - assert all(float(r.attrib['data-probability']) == 1 for r in frame) - assert all('Unpaired probability' in r.find('s:title', NS).text for r in frame) + assert all(float(r.attrib['data-probability']) == 0 for r in frame) + assert all('Intramolecular pairing probability' in r.find('s:title', NS).text for r in frame) css = root.find('s:style', NS).text assert all(f'.p{i} {{ fill:' in css for i in range(7)) assert len(root.findall('.//s:g[@class="probability-legend"]/s:rect', NS)) == 7 # All admitted computed two-pair stacks, in original query orientation. expected = {(i+k+1, 4-j-k) for i in (0, 3, 4) for j in range(3) for k in range(2)} assert seeds(root) == expected, seeds(root) + for r in root.findall('.//s:rect[@class="seed"][@data-target]', NS): + bp = cells[int(r.attrib['data-target']), int(r.attrib['data-query'])] + assert float(r.attrib['x']) == float(bp.attrib['x'])+1 + assert float(r.attrib['y']) == float(bp.attrib['y'])+1 + for r in frame: + idx = int(r.attrib['data-index']) + axis, bp = ('y', cells[idx, 1]) if r.attrib['data-strand'] == 'target' else ('x', cells[1, idx]) + assert float(r.attrib[axis]) == float(bp.attrib[axis]) # Requesting SVG must not affect CSV or ensemble/ranked output. baseline = run(options=['--outNumber=1', '--outMode=C', '--outCsvCols=Zall,Eall']) plotted = run(options=['--outNumber=1', '--outMode=C', '--outCsvCols=Zall,Eall', f'--out=bpsvg:{svg}']) assert baseline.stdout == plotted.stdout + # The green rectangle covers the reported MFE region, including when + # ranked output is disabled. Query coordinates retain their original order. + for target, query, options in ( + ('GGAGGG', 'CCCC', []), + ('AAGGGGAA', 'AACCCCAA', []), + ('GGAGGG', 'CCCC', ['--tIdxPos0=-4', '--qIdxPos0=-2']), + ): + reference = run(target, query, [*options, '--outNumber=1', '--outMode=C', + '--outCsvCols=start1,end1,start2,end2']) + row = next(csv.DictReader(reference.stdout.splitlines(), delimiter=';')) + for count in (0, 1): + mfe_plot = plot(target, query, [*options, f'--outNumber={count}']) + outline = mfe_plot.find('.//s:rect[@class="mfe"][@data-target-start]', NS) + assert outline is not None + bounds = tuple(int(outline.attrib[k]) for k in ( + 'data-target-start', 'data-target-end', 'data-query-start', 'data-query-end')) + assert bounds == tuple(int(row[k]) for k in ('start1', 'end1', 'start2', 'end2')) + ts, te, qs, qe = bounds + matrix = pairs(mfe_plot) + assert float(outline.attrib['x']) == float(matrix[ts, qs].attrib['x']) + assert float(outline.attrib['y']) == float(matrix[te, qs].attrib['y']) + assert float(outline.attrib['width']) == float(matrix[ts, qe].attrib['x'])-float(matrix[ts, qs].attrib['x'])+20 + assert float(outline.attrib['height']) == float(matrix[ts, qs].attrib['y'])-float(matrix[te, qs].attrib['y'])+20 + assert 'Green outline:' in ''.join(mfe_plot.itertext()) # Explicit singleton/mixed seed masks exclude extension-only cells. opts = ['--seedTQ=1|&4|,2||&2||'] mixed = plot('GGGG', 'CCCC', opts) @@ -103,7 +147,7 @@ def plot(t='GGAGGG', q='CCCC', opts=()): idx = int(g.attrib['data-index']) r = signed_cells[idx,-15] if strand == 'target' else signed_cells[-15,idx] axis = 'y' if strand == 'target' else 'x' - expected_pos = float(r.attrib[axis]) + (20 if idx > 1 else 0) + expected_pos = float(r.attrib[axis]) + (0 if idx > 1 else 20) if strand == 'target' else float(r.attrib[axis]) + (20 if idx > 1 else 0) assert float(g.attrib[axis+'1']) == expected_pos assert ('major' in g.attrib['class']) == (idx % 50 == 0) assert ('origin' in g.attrib['class']) == (idx == 1) @@ -113,6 +157,10 @@ def plot(t='GGAGGG', q='CCCC', opts=()): name = '' named = plot(opts=[f'--tId={name}']) assert name in named.find('s:title', NS).text + assert named.find('.//s:text[@class="subtitle"]', NS).text == name+' / query' + assert all(name in r.find('s:title', NS).text for r in pairs(named).values()) + assert all(name in r.find('s:title', NS).text for r in named.findall( + './/s:rect[@data-type="intramolecular-pairing"][@data-strand="target"]', NS)) assert name+" (5' to 3')" in [e.text for e in named.findall('.//s:text', NS)] # Accessibility uses its own per-strand scale and original query order. pu_t, pu_q = d/'target.pu', d/'query.pu' @@ -124,9 +172,9 @@ def one_nt(path): def check_frame(root, target_pu, query_pu): for strand, values in (('target', target_pu), ('query', query_pu)): assert any(0 < v < .9 for v in values) - for r in root.findall(f'.//s:rect[@data-type="unpaired"][@data-strand="{strand}"]', NS): - assert math.isclose(float(r.attrib['data-probability']), values[int(r.attrib['data-index'])-1], - rel_tol=2e-5, abs_tol=1e-6), (strand, r.attrib, values) + for r in root.findall(f'.//s:rect[@data-type="intramolecular-pairing"][@data-strand="{strand}"]', NS): + assert math.isclose(float(r.attrib['data-probability']), 1-values[int(r.attrib['data-index'])-1], + rel_tol=2e-5, abs_tol=5e-6), (strand, r.attrib, values) for temperature in (22, 37): acc_options = [f'--temperature={temperature}', f'--out=tPu:{pu_t}', f'--out=qPu:{pu_q}'] @@ -160,6 +208,7 @@ def check_frame(root, target_pu, query_pu): empty = plot(t,q) assert all(r.attrib['data-probability'] == 'NA' for r in pairs(empty).values()) assert not seeds(empty) + assert not empty.findall('.//s:rect[@class="mfe"]', NS) assert 'empty interaction ensemble' in ''.join(empty.itertext()) compressed = d/'pairs.svg.gz' run(options=[f'--out=bpsvg:{compressed}']) From 322031fc124eec6d01667fbf1f8bedf0ff35f038 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 22:16:07 +0200 Subject: [PATCH 13/30] feat: add exact unseeded base-pair probabilities (step 10) --- ChangeLog | 12 ++ README.md | 17 +- src/IntaRNA/PredictorMfeEns2d.cpp | 122 ++++++++++- src/IntaRNA/PredictorMfeEns2d.h | 24 ++- src/bin/CommandLineParsing.cpp | 30 +-- tests/Makefile.am | 1 + tests/PredictorUnseededProbability_test.cpp | 217 ++++++++++++++++++++ tests/testBasePairProbabilities.py | 15 +- tests/testBasePairProbabilitySvg.py | 7 +- 9 files changed, 418 insertions(+), 27 deletions(-) create mode 100644 tests/PredictorUnseededProbability_test.cpp diff --git a/ChangeLog b/ChangeLog index 19fb5983..e818a899 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ ## Interface and handling +- enable base-pair CSV/SVG probabilities for exact unseeded model P/mode M + predictions (PR #259, step 10) + - revise SVG axes, sequence labels and rounded tooltips; show intramolecular pairing (1-Pu) frames and the best interaction region (PR #259 review) @@ -172,6 +175,15 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * PredictorMfeEns2d, CommandLineParsing : + + compute actual-pair masses by reversing the existing unseeded recurrence; + preserve site filters, noLP and numerical cutoffs, and merge disjoint regions + * enable CSV/SVG with --noSeed; fix default-ended region offset truncation + * tests/PredictorUnseededProbability_test.cpp, tests/Makefile.am, README.md : + + compare against independent chain enumeration, filters, regions and failures; + document the public optional sink (PR #259 step 10) + 261009 Alexander Mitrofanov * BasePairProbabilitySvg, BasePairProbabilityWriter, CommandLineParsing, IntaRNA : * place target origin at bottom left and query name below the matrix; include diff --git a/README.md b/README.md index ca099554..888f143f 100644 --- a/README.md +++ b/README.md @@ -2016,8 +2016,8 @@ or `STDERR` instead of a file name. ## Actual base-pair probabilities `--out=bpProb:FILE` writes the probability that each target/query nucleotide pair -actually pairs, conditional on an allowed seeded interaction. It requires -`--model=P --mode=M` and a stack-only seed handler. For example: +actually pairs, conditional on an allowed interaction. It requires +`--model=P --mode=M` with `--noSeed` or a stack-only seed handler. For example: ```sh IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ @@ -2031,7 +2031,7 @@ Computed seeds preserve their handler-specific admission thresholds. Explicit stacked seeds may have different lengths, including singletons; competing explicit patterns at the same start still retain only the selected minimum-energy pattern. Other predictors retain their existing behavior. Heuristic, kinetic, -evaluation, seed-only, unseeded and bulged-seed `bpProb` requests are rejected. +evaluation, seed-only and bulged-seed `bpProb` requests are rejected. Rows follow the original target and columns the original query, both 5' to 3'. The header starts with `bpProb`; labels contain each nucleotide and its display @@ -2054,14 +2054,19 @@ bulge can have positive site coverage and zero actual-pair probability. There is no added unbound-state weight. Individual pair events can coexist, so `1 - sum(pair probabilities)` is not a probability of no interaction. -The backend checks nonnegative arithmetic and reports numerical range failures +The unseeded exact predictor is enabled by `--noSeed` with the same CSV/SVG +outputs. Its outside pass follows the existing fixed-right-boundary recurrence, +including its numerical zero cutoffs, without changing ordinary prediction. + +The seeded backend checks nonnegative arithmetic and reports numerical range failures as errors, including underflow, overflow and NaN. Positive subnormals are rejected; there is currently no scaling fallback. Failed or cancelled regional work never produces a successful-looking probability matrix. Ordinary floating-point rounding remains; "exact" describes the enumerated ensemble, not real arithmetic. For library use, keep a `BasePairProbabilities` owner alive across predictions, -pass its optional non-owning pointer to `PredictorMfeEns2dSeedExtension`, and call +pass its optional non-owning pointer to `PredictorMfeEns2dSeedExtension` (stack +seeds) or `PredictorMfeEns2d` (unseeded), and call `finalize()` only after all requested disjoint regions succeed. A writer is called explicitly after finalization; its destructor publishes nothing. The output constraint must enable `needZall`. The [standalone API example](doc/seeded-probabilities-example.cpp) @@ -2074,7 +2079,7 @@ show the public API and measured limits. `--out=bpsvg:FILE` renders the same actual-pair probabilities as `bpProb` in a standalone SVG. Both outputs can be requested together. The same exact -`--model=P --mode=M` stack-seed restrictions, regional ensemble, normalization +`--model=P --mode=M` unseeded/stack-seed restrictions, regional ensemble, normalization and success-only publication rules apply. ```sh diff --git a/src/IntaRNA/PredictorMfeEns2d.cpp b/src/IntaRNA/PredictorMfeEns2d.cpp index 9b9bb28e..490bfe9b 100644 --- a/src/IntaRNA/PredictorMfeEns2d.cpp +++ b/src/IntaRNA/PredictorMfeEns2d.cpp @@ -1,5 +1,6 @@ #include "IntaRNA/PredictorMfeEns2d.h" +#include "IntaRNA/PartitionArithmetic.h" #include @@ -11,10 +12,14 @@ PredictorMfeEns2d:: PredictorMfeEns2d( const InteractionEnergy & energy , OutputHandler & output - , PredictionTracker * predTracker ) + , PredictionTracker * predTracker + , BasePairProbabilities * pairProbabilities ) : PredictorMfeEns(energy,output,predTracker) + , pairProbabilities(pairProbabilities) , hybridZ( 0,0 ) { + if (pairProbabilities && !output.getOutputConstraint().needZall) + throw std::invalid_argument("unseeded pair probabilities require needZall"); } @@ -33,6 +38,13 @@ void PredictorMfeEns2d:: predict( const IndexRange & r1 , const IndexRange & r2 ) +{ + try { predictRegion(r1,r2); } + catch (...) { if (pairProbabilities) pairProbabilities->fail(); throw; } +} + +void +PredictorMfeEns2d::predictRegion(const IndexRange & r1,const IndexRange & r2) { #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_logOutput) @@ -47,20 +59,34 @@ predict( const IndexRange & r1 throw std::runtime_error("PredictorMfeEns2d::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif + if (pairProbabilities && (!r1.isAscending() || !r2.isAscending() + || r1.from>=energy.getAccessibility1().getSequence().size() + || r2.from>=energy.getAccessibility2().getSequence().size())) + throw std::invalid_argument("unseeded pair probabilities: invalid region"); + if (pairProbabilities && pairProbabilities->status()!=BasePairProbabilities::Status::pending) + throw std::logic_error("unseeded pair probabilities: accumulator is not pending"); + // set index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); // resize matrix hybridZ.resize( std::min( energy.size1() - , (r1.to==RnaSequence::lastPos?energy.size1()-1:r1.to)-r1.from+1 ) + , r1.to==RnaSequence::lastPos?energy.size1():r1.to-r1.from+1 ) , std::min( energy.size2() - , (r2.to==RnaSequence::lastPos?energy.size2()-1:r2.to)-r2.from+1 ) ); + , r2.to==RnaSequence::lastPos?energy.size2():r2.to-r2.from+1 ) ); // initialize mfe interaction for updates initOptima(); // initialize overall partition function for updates initZ(); + // No probability buffers or reverse work are needed for ordinary prediction. + Z2dMatrix outside, masses; + Z_type denominator=0; + if (pairProbabilities) { + outside.resize(hybridZ.size1(),hybridZ.size2()); + masses.resize(hybridZ.size1(),hybridZ.size2()); + } // for all right ends j1 for (size_t j1 = hybridZ.size1(); j1-- > 0; ) { @@ -78,12 +104,26 @@ predict( const IndexRange & r1 // fill matrix and store best interaction fillHybridZ( j1, j2, 0, 0, true ); + if (pairProbabilities) + accumulatePairMasses(j1,j2,outside,masses,denominator); } } + if (pairProbabilities && denominator!=PartitionArithmetic::check(Zall)) + throw std::logic_error("unseeded pair probabilities: updateZ override changed the partition objective"); // report mfe interaction reportOptima(); + if (pairProbabilities) { + const size_t n=hybridZ.size1(), m=hybridZ.size2(); + const auto first=energy.getBasePair(0,0), last=energy.getBasePair(n-1,m-1); + // An unseeded prediction has no seed annotations, even for an API sink + // that requested a mask for use by a shared writer. + const bool annotate=pairProbabilities->collectsSeedPairs(); + Matrix seeds(annotate?n:0,annotate?m:0,0); + pairProbabilities->addRegion(IndexRange(first.first,last.first), + IndexRange(last.second,first.second),denominator,masses,annotate?&seeds:nullptr); + } } //////////////////////////////////////////////////////////////////////////// @@ -201,5 +241,81 @@ fillHybridZ( const size_t j1, const size_t j2 //////////////////////////////////////////////////////////////////////////// +void +PredictorMfeEns2d::accumulatePairMasses(size_t j1,size_t j2,Z2dMatrix & outside, + Z2dMatrix & masses,Z_type & denominator) const +{ + using A=PartitionArithmetic; + const bool noLP=output.getOutputConstraint().noLP; + const size_t shift=noLP?1:0; + // Use exactly the rectangle and epsilon gates of fillHybridZ/updateZ: this + // reverses the represented ensemble, including its existing zero cutoff. + const size_t begin1=j1-std::min(j1,energy.getAccessibility1().getMaxLength()+1); + const size_t begin2=j2-std::min(j2,energy.getAccessibility2().getMaxLength()+1); + for(size_t i1=j1+1;i1-- > begin1;) for(size_t i2=j2+1;i2-- > begin2;) { + outside(i1,i2)=0; + const Z_type h=A::check(hybridZ(i1,i2)); + if (!Z_equal(h,0) && isValidOutputSite(i1,j1,i2,j2)) { + const Z_type b=A::check(energy.getBoltzmannWeight(energy.getE(i1,j1,i2,j2,E_type(0)))); + outside(i1,i2)=b; + denominator=A::add(denominator,A::multiply(h,b)); + } + } + + const auto first=energy.getBasePair(0,0); + const auto last=energy.getBasePair(masses.size1()-1,masses.size2()-1); + auto addMass=[&](size_t i1,size_t i2,Z_type value) { + const auto bp=energy.getBasePair(i1,i2); + auto & mass=masses(bp.first-first.first,bp.second-last.second); + mass=A::add(mass,value); + }; + const Z_type initiation=A::check(energy.getBoltzmannWeight(energy.getE_init())); + // Every suffix state contains its left pair once. Reverse topological + // traversal gathers all enclosing left boundaries before visiting a suffix. + for(size_t i1=begin1;i1<=j1;++i1) for(size_t i2=begin2;i2<=j2;++i2) { + const Z_type a=outside(i1,i2), h=hybridZ(i1,i2); + if (a==0 || h==0) continue; + addMass(i1,i2,A::multiply(a,h)); + const size_t w1=j1-i1+1, w2=j2-i2+1; + if (w1<=1 || w2<=1) continue; + + Z_type stack=1; + if (!noLP) { + const Z_type edge=A::multiply(a, + energy.getBoltzmannWeight(energy.getE_interLeft(i1,j1,i2,j2))); + outside(j1,j2)=A::add(outside(j1,j2),edge); + } else { + if (!energy.areComplementary(i1+1,i2+1)) continue; + stack=A::check(energy.getBoltzmannWeight(energy.getE_interLeft(i1,i1+1,i2,i2+1))); + const Z_type edge=A::multiply(a,stack); + if (w1==2 && w2==2) { + // The noLP terminal has no singleton H state: its second pair + // is implicit in stack * initiation and must be counted here. + addMass(j1,j2,A::multiply(edge,initiation)); + } else { + outside(i1+1,i2+1)=A::add(outside(i1+1,i2+1),edge); + } + } + if (w1<=2 || w2<=2) continue; + for(size_t k1=std::min(j1-1,i1+energy.getMaxInternalLoopSize1()+1+shift);k1>i1+shift;--k1) { + for(size_t k2=std::min(j2-1,i2+energy.getMaxInternalLoopSize2()+1+shift);k2>i2+shift;--k2) { + if (noLP && k1-1==i1+shift && k2-1==i2+shift) continue; + if (Z_equal(hybridZ(k1,k2),0)) continue; + const Z_type loop=A::check(energy.getBoltzmannWeight( + energy.getE_interLeft(i1+shift,k1,i2+shift,k2))); + const Z_type edge=A::multiply(a,A::multiply(stack,loop)); + outside(k1,k2)=A::add(outside(k1,k2),edge); + if (noLP) { + // This decomposition consumes (i,i+) before jumping over + // the loop to k; only i and k have explicit suffix states. + addMass(i1+1,i2+1,A::multiply(edge,hybridZ(k1,k2))); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////////////// + } // namespace diff --git a/src/IntaRNA/PredictorMfeEns2d.h b/src/IntaRNA/PredictorMfeEns2d.h index 57b7f5c3..169b9881 100644 --- a/src/IntaRNA/PredictorMfeEns2d.h +++ b/src/IntaRNA/PredictorMfeEns2d.h @@ -3,6 +3,7 @@ #define INTARNA_PREDICTORMFEENS2D_H_ #include "IntaRNA/PredictorMfeEns.h" +#include "IntaRNA/BasePairProbabilities.h" #include "IntaRNA/Interaction.h" #include "IntaRNA/Matrix.h" @@ -34,10 +35,13 @@ class PredictorMfeEns2d: public PredictorMfeEns { * @param predTracker the prediction tracker to be used or NULL if no * tracking is to be done; if non-NULL, the tracker gets deleted * on this->destruction. + * @param pairProbabilities optional non-owning sink; requires needZall and + * must remain pending until all disjoint regions have succeeded */ PredictorMfeEns2d( const InteractionEnergy & energy , OutputHandler & output - , PredictionTracker * predTracker ); + , PredictionTracker * predTracker + , BasePairProbabilities * pairProbabilities = nullptr ); virtual ~PredictorMfeEns2d(); @@ -58,6 +62,9 @@ class PredictorMfeEns2d: public PredictorMfeEns { protected: + //! optional sequence-pair owner, never deleted or finalized by this predictor + BasePairProbabilities * pairProbabilities; + //! access to the interaction energy handler of the super class using PredictorMfeEns::energy; @@ -70,6 +77,21 @@ class PredictorMfeEns2d: public PredictorMfeEns { protected: + /** Compute one region, allowing predict() to fail the sink on any exception. */ + void predictRegion(const IndexRange & r1,const IndexRange & r2); + + /** Reverse the current fixed-right hybrid recurrence. Adjoint seeds obey the + * same site filters and numerical-zero tests as updateZ(). Implicit noLP + * stacking partners are counted in addition to the explicit DP-state pairs. + * @param j1 right boundary in local target coordinates + * @param j2 right boundary in local reversed-query coordinates + * @param outside scratch adjoints, same dimensions as hybridZ + * @param masses accumulated numerators in original regional orientation + * @param denominator accumulated boundary contributions, checked against Zall + */ + void accumulatePairMasses(size_t j1,size_t j2,Z2dMatrix & outside, + Z2dMatrix & masses,Z_type & denominator) const; + /** * Computes all entries of the hybridE matrix for interactions ending in * p=j1 and q=j2 and reports every complete boundary via updateCompleteZ(), diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index 7f6f1423..d5a0f30e 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -958,7 +958,7 @@ CommandLineParsing::CommandLineParsing( const Personality personality ) "\n 'tPu:' (target) unpaired probability values (RNAplfold format OR '.agz' file ending for IntaRNA's binary format)." "\n 'pMinE:' (target+query) for each index pair the minimal energy of any interaction covering the pair (CSV format)" "\n 'bpsvg:' (target+query) SVG actual-pair probability plot with accessibility frame and seed annotations; same requirements as bpProb" - "\n 'bpProb:' (target+query) actual-pair conditional probabilities for exact --model=P --mode=M stack-seed ensembles (CSV matrix, original strand order; NA for an empty ensemble)" + "\n 'bpProb:' (target+query) actual-pair conditional probabilities for --model=P --mode=M with --noSeed or stack-only seeds (CSV matrix, original strand order; NA for an empty ensemble)" "\n 'spotProb:' (target+query) tracks for a given set of interaction spots their probability to be covered by an interaction. If no spots are provided, probabilities for all index combinations are computed. Spots are encoded by comma-separated 'idxT&idxQ' pairs (target-query). For each spot a probability is provided in concert with the probability that none of the spots (encoded by '0&0') is covered (CSV format). The spot encoding is followed colon-separated by the output stream/file name, eg. '--out=\"spotProb:3&76,59&2:STDERR\"'. NOTE: value has to be quoted due to '&' symbol!" "\nFor each, provide a file name or STDOUT/STDERR to write to the respective output stream." ).c_str()) @@ -1246,8 +1246,8 @@ parse(int argc, char** argv) outSep = unescaped_string::getUnescaped( outSep ); if (hasBasePairProbabilityOutput()) { - if (model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) - throw error("bpProb/bpsvg requires --model=P --mode=M with stack-only seeds and no windows"); + if (model.val!='P' || mode.val!='M' || !rri.empty() || windowWidth.val!=0) + throw error("bpProb/bpsvg requires --model=P --mode=M with --noSeed or stack-only seeds and no windows"); outNeedsZall=true; } // K needs a seed even before the usual --noSeed model normalization. @@ -2496,10 +2496,12 @@ bool CommandLineParsing::hasBasePairProbabilityOutput() const { } BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const InteractionEnergy & energy) const { if (!hasBasePairProbabilityOutput()) return nullptr; - std::unique_ptr handler(getSeedHandler(energy)); - if (!handler->guaranteesStackOnlySeeds()) - throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); - return new BasePairProbabilities(energy.size1(),energy.size2(),!outPrefix2streamName.at(OP_bpsvg).empty()); + if (!noSeedRequired) { + std::unique_ptr handler(getSeedHandler(energy)); + if (!handler->guaranteesStackOnlySeeds()) + throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); + } + return new BasePairProbabilities(energy.size1(),energy.size2(),!noSeedRequired && !outPrefix2streamName.at(OP_bpsvg).empty()); } void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy, const Interaction * mfe) const { @@ -2518,11 +2520,13 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair { std::unique_ptr pairSeeds; if (pairProbabilities) { - if(model.val!='P' || mode.val!='M' || noSeedRequired || !rri.empty() || windowWidth.val!=0) - throw std::invalid_argument("bpProb/bpsvg requires --model=P --mode=M with stack-only seeds and no windows"); - pairSeeds.reset(getSeedHandler(energy)); - if(!pairSeeds->guaranteesStackOnlySeeds()) - throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); + if(model.val!='P' || mode.val!='M' || !rri.empty() || windowWidth.val!=0) + throw std::invalid_argument("bpProb/bpsvg requires --model=P --mode=M with --noSeed or stack-only seeds and no windows"); + if (!noSeedRequired) { + pairSeeds.reset(getSeedHandler(energy)); + if(!pairSeeds->guaranteesStackOnlySeeds()) + throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); + } } // set up hub for prediction tracking (if needed) PredictionTrackerHub * predTracker = new PredictionTrackerHub(); @@ -2624,7 +2628,7 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair case 'P' : { switch ( mode.val ) { case 'H' : return new PredictorMfeEns2dHeuristic( energy, output, predTracker ); - case 'M' : return new PredictorMfeEns2d( energy, output, predTracker ); + case 'M' : return new PredictorMfeEns2d( energy, output, predTracker, pairProbabilities ); default : INTARNA_NOT_IMPLEMENTED("mode "+toString(mode.val)+" not available for model "+toString(model.val)); } } break; diff --git a/tests/Makefile.am b/tests/Makefile.am index 4d184d6b..a500da8e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,6 +30,7 @@ runApiTests_SOURCES = \ SeededNativeBenchmark_test.cpp \ StackSeedDomain_test.cpp \ BasePairProbabilities_test.cpp \ + PredictorUnseededProbability_test.cpp \ testEasyLoggingSetup.icc \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ diff --git a/tests/PredictorUnseededProbability_test.cpp b/tests/PredictorUnseededProbability_test.cpp new file mode 100644 index 00000000..7124d6e5 --- /dev/null +++ b/tests/PredictorUnseededProbability_test.cpp @@ -0,0 +1,217 @@ +#include "catch.hpp" + +#include "IntaRNA/AccessibilityConstraint.h" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/BasePairProbabilities.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/OutputHandlerInteractionList.h" +#include "IntaRNA/PredictorMfeEns2d.h" +#include "IntaRNA/ReverseAccessibility.h" + +#include +#include +#include +#include +#include + +using namespace IntaRNA; + +namespace { + +struct UnseededOracle { + Z_type z=0; + Matrix mass; + UnseededOracle(size_t n,size_t m) : mass(n,m,0) {} +}; + +// Enumerate increasing pair chains directly, without the predictor's suffix +// recurrence. Filter lonely pairs by inspecting both neighbours in the chain. +UnseededOracle enumerateUnseeded(const InteractionEnergy & energy, + const OutputConstraint & constraint,const IndexRange & r1,const IndexRange & r2) +{ + UnseededOracle result(energy.size1(),energy.size2()); + const size_t end1=r1.to==RnaSequence::lastPos?energy.size1()-1:r1.to; + const size_t end2=r2.to==RnaSequence::lastPos?energy.size2()-1:r2.to; + std::vector> chain; + std::function visit=[&](E_type hybrid) { + const auto [i1,i2]=chain.front(); + const auto [j1,j2]=chain.back(); + bool accepted=true; + if (constraint.noLP) { + for(size_t k=0;k0 && chain[k-1].first+1==chain[k].first + && chain[k-1].second+1==chain[k].second; + const bool right=k+1constraint.maxED || energy.getED2(i2,j2)>constraint.maxED) accepted=false; + const E_type complete=energy.getE(i1,j1,i2,j2,hybrid); + if (accepted && E_isNotINF(complete)) { + const Z_type weight=energy.getBoltzmannWeight(complete); + result.z+=weight; + for (const auto & p:chain) { + const auto bp=energy.getBasePair(p.first,p.second); + result.mass(bp.first,bp.second)+=weight; + } + } + for(size_t k1=j1+1;k1<=end1;++k1) for(size_t k2=j2+1;k2<=end2;++k2) { + if (!energy.isAccessible1(k1) || !energy.isAccessible2(k2) + || !energy.areComplementary(k1,k2)) continue; + const E_type loop=energy.getE_interLeft(j1,k1,j2,k2); + if (E_isINF(loop)) continue; + chain.emplace_back(k1,k2);visit(hybrid+loop);chain.pop_back(); + } + }; + for(size_t i1=r1.from;i1<=end1;++i1) for(size_t i2=r2.from;i2<=end2;++i2) { + if (!energy.isAccessible1(i1) || !energy.isAccessible2(i2) + || !energy.areComplementary(i1,i2)) continue; + chain.emplace_back(i1,i2);visit(energy.getE_init());chain.pop_back(); + } + return result; +} + +void compareUnseeded(const InteractionEnergy & energy,const OutputConstraint & constraint, + const IndexRange & r1,const IndexRange & r2,bool compareOracle=true) +{ + OutputHandlerInteractionList ordinary(constraint,10), annotated(constraint,10); + PredictorMfeEns2d baseline(energy,ordinary,nullptr); + BasePairProbabilities result(energy.size1(),energy.size2(),true); + PredictorMfeEns2d predictor(energy,annotated,nullptr,&result); + baseline.predict(r1,r2); + predictor.predict(r1,r2); + result.finalize(); + REQUIRE(predictor.getZall()==baseline.getZall()); + REQUIRE(result.getZ()==baseline.getZall()); + REQUIRE(std::distance(ordinary.begin(),ordinary.end())==std::distance(annotated.begin(),annotated.end())); + auto a=ordinary.begin(), b=annotated.begin(); + for(;a!=ordinary.end();++a,++b) { + REQUIRE((*a)->energy==(*b)->energy); + REQUIRE((*a)->basePairs==(*b)->basePairs); + } + for(size_t i=0;igetMaxLength()+1?ED_UPPER_BOUND:E_type((to-from+1)*perNt); + } +private: + E_type perNt; +}; + +class TinyInitiationEnergy : public InteractionEnergyBasePair { +public: + using InteractionEnergyBasePair::InteractionEnergyBasePair; + E_type getE_init() const override { return Ekcal_2_E(60); } +}; + +class ChangedObjectivePredictor : public PredictorMfeEns2d { +public: + using PredictorMfeEns2d::PredictorMfeEns2d; +protected: + void updateZ(size_t i1,size_t j1,size_t i2,size_t j2,Z_type z,bool hybrid) override { + PredictorMfeEns::updateZ(i1,j1,i2,j2,2*z,hybrid); + } +}; + +} + +TEST_CASE("exact unseeded pair probabilities enumerate physical pair chains", "[UnseededProbability][PredictorTinyOracle]") { + #include "testEasyLoggingSetup.icc" + for (const auto & sequence:std::vector>{ + {"GGGGGG","CCCCCC"},{"GUGCA","UGCGC"},{"AAGGGAA","ACCCUAA"}, + {"AAAA","AAAA"},{"G","C"}}) { + for(bool noLP:{false,true}) for(bool noGU:{false,true}) for(size_t loop:{0,2}) { + CAPTURE(sequence.first,sequence.second,noLP,noGU,loop); + RnaSequence target("target",sequence.first), query("query",sequence.second); + AccessibilityDisabled targetAcc(target,0,nullptr), queryAcc(query,0,nullptr); + ReverseAccessibility reverse(queryAcc); + InteractionEnergyBasePair energy(targetAcc,reverse,loop,loop,false,0.7,Ekcal_2_E(-0.8),3,Ekcal_2_E(0.13)); + OutputConstraint constraint(4,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,noGU,true,true); + compareUnseeded(energy,constraint,{0,RnaSequence::lastPos},{0,RnaSequence::lastPos}); + } + } +} + +TEST_CASE("unseeded pair probabilities preserve filtered offset ensembles", "[UnseededProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence target("target","GUGGGCG"),query("query","CUCCCCC"); + for(bool noLP:{false,true}) for(bool noGU:{false,true}) { + ProbabilityWidthAccessibility targetAcc(target,3,Ekcal_2_E(0.10)); + ProbabilityWidthAccessibility queryAcc(query,4,Ekcal_2_E(0.15)); + ReverseAccessibility reverse(queryAcc); + InteractionEnergyBasePair energy(targetAcc,reverse,1,2,false,1,Ekcal_2_E(-1),3,0,true,false); + OutputConstraint constraint(5,OutputConstraint::OVERLAP_NONE,E_INF,E_INF,false,noLP,noGU,true,true,Ekcal_2_E(0.45)); + compareUnseeded(energy,constraint,{1,5},{2,6}); + compareUnseeded(energy,constraint,{2,RnaSequence::lastPos},{1,RnaSequence::lastPos}); + } + AccessibilityConstraint blocked(target,"...b...",0,"","",""); + AccessibilityDisabled targetAcc(target,0,&blocked),queryAcc(query,0,nullptr); + ReverseAccessibility reverse(queryAcc); + InteractionEnergyBasePair energy(targetAcc,reverse,2,2); + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,false,false,true,true); + compareUnseeded(energy,constraint,{1,5},{1,5}); + + BasePairProbabilities result(target.size(),query.size()); + OutputHandlerInteractionList output(constraint,4); + PredictorMfeEns2d predictor(energy,output,nullptr,&result); + predictor.predict({0,2},{0,2}); + predictor.predict({4,6},{3,6}); + result.finalize(); + const auto first=enumerateUnseeded(energy,constraint,{0,2},{0,2}); + const auto second=enumerateUnseeded(energy,constraint,{4,6},{3,6}); + REQUIRE(result.getZ()==Approx(first.z+second.z).epsilon(2e-12)); + for(size_t i=0;i0 for v in covered[3][1:]);assert all(v==0 for v in with_spot[2]) compressed=d/'pairs.csv.gz';run(options=[f'--out=bpProb:{compressed}']);same(matrix(compressed)[2],values) # Unsupported requests fail explicitly and never create a probability file. - for opts in (['--mode=H'],['--mode=S'],['--model=X','--mode=K'],['--noSeed'],['--windowWidth=3','--windowOverlap=2'],['--seedMaxUP=1'],['--seedTQ=1|.|&2||']): + for opts in (['--mode=H'],['--mode=S'],['--model=X','--mode=K'],['--windowWidth=3','--windowOverlap=2'],['--seedMaxUP=1'],['--seedTQ=1|.|&2||']): fail=d/'unsupported.csv';run(options=[f'--out=bpProb:{fail}',*opts],success=False);assert not fail.exists() fail=d/'range.csv';run('GGGG','CCCC',[f'--out=bpProb:{fail}',*seedopt,'--energyAdd=900'],success=False);assert not fail.exists() run(options=['--out=bpProb:/dev/full'],success=False) diff --git a/tests/testBasePairProbabilitySvg.py b/tests/testBasePairProbabilitySvg.py index 597afd6a..0cbb64b6 100644 --- a/tests/testBasePairProbabilitySvg.py +++ b/tests/testBasePairProbabilitySvg.py @@ -122,6 +122,11 @@ def plot(t='GGAGGG', q='CCCC', opts=()): assert float(outline.attrib['width']) == float(matrix[ts, qe].attrib['x'])-float(matrix[ts, qs].attrib['x'])+20 assert float(outline.attrib['height']) == float(matrix[ts, qs].attrib['y'])-float(matrix[te, qs].attrib['y'])+20 assert 'Green outline:' in ''.join(mfe_plot.itertext()) + unseeded = plot(opts=['--noSeed', f'--out=bpProb:{table}']) + assert not seeds(unseeded) + rows = list(csv.reader(table.open(), delimiter=';')) + assert all(float(pairs(unseeded)[i,j].attrib['data-probability']) == float(rows[i][j]) + for i in range(1,7) for j in range(1,5)) # Explicit singleton/mixed seed masks exclude extension-only cells. opts = ['--seedTQ=1|&4|,2||&2||'] mixed = plot('GGGG', 'CCCC', opts) @@ -214,7 +219,7 @@ def check_frame(root, target_pu, query_pu): run(options=[f'--out=bpsvg:{compressed}']) assert len(pairs(load(compressed))) == 24 # Validation/numerical failures must not publish a partial document. - for opts in (['--mode=H'], ['--noSeed'], ['--seedMaxUP=1'], ['--model=X', '--mode=K'], + for opts in (['--mode=H'], ['--seedMaxUP=1'], ['--model=X', '--mode=K'], ['--windowWidth=3'], ['--seedTQ=1|&4|','--energyAdd=900']): failure = d/'failure.svg' run(options=[f'--out=bpsvg:{failure}', *opts], success=False) From ae0eb78da255c76765e6745d2dc4040766f7b791 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 22:30:47 +0200 Subject: [PATCH 14/30] feat: add approximate heuristic pair probabilities (step 11) --- ChangeLog | 22 ++ README.md | 32 ++- .../seeded-probabilities-validation.md | 48 ++++ .../IntaRNA.PredictorMfeEns2d.outside.svg | 69 ++++++ ...edictorMfeEns2dHeuristic.probabilities.svg | 80 ++++++ src/IntaRNA/BasePairProbabilities.cpp | 4 + src/IntaRNA/BasePairProbabilities.h | 8 + src/IntaRNA/BasePairProbabilitySvg.cpp | 7 +- src/IntaRNA/BasePairProbabilityWriter.cpp | 3 +- src/IntaRNA/PartitionArithmetic.h | 12 +- src/IntaRNA/PredictorMfeEns2d.cpp | 5 +- src/IntaRNA/PredictorMfeEns2d.h | 4 +- src/IntaRNA/PredictorMfeEns2dHeuristic.cpp | 99 +++++++- src/IntaRNA/PredictorMfeEns2dHeuristic.h | 36 ++- ...redictorMfeEns2dHeuristicSeedExtension.cpp | 41 ++- .../PredictorMfeEns2dHeuristicSeedExtension.h | 4 +- .../PredictorMfeEns2dSeedExtension.cpp | 211 +++++++++++++++- src/IntaRNA/PredictorMfeEns2dSeedExtension.h | 22 ++ src/bin/CommandLineParsing.cpp | 18 +- tests/Makefile.am | 2 + tests/PredictorHeuristicProbability_test.cpp | 234 ++++++++++++++++++ ...edictorHeuristicSeededProbability_test.cpp | 201 +++++++++++++++ tests/PredictorUnseededProbability_test.cpp | 6 + tests/testBasePairProbabilities.py | 12 +- tests/testBasePairProbabilitySvg.py | 11 +- 25 files changed, 1137 insertions(+), 54 deletions(-) create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2d.outside.svg create mode 100644 doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.probabilities.svg create mode 100644 tests/PredictorHeuristicProbability_test.cpp create mode 100644 tests/PredictorHeuristicSeededProbability_test.cpp diff --git a/ChangeLog b/ChangeLog index e818a899..eaea2fe1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,12 @@ ## Interface and handling +- enable approximate actual-pair CSV/SVG probabilities for unseeded and seeded + heuristic ensembles, labelled explicitly as approximate (PR #259, step 11) + +- reject nonfinite partition totals and finite-energy boundary underflow before + publishing exact unseeded or heuristic pair probabilities (PR #259 review) + - enable base-pair CSV/SVG probabilities for exact unseeded model P/mode M predictions (PR #259, step 10) @@ -175,6 +181,22 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * PredictorMfeEns2dHeuristic, PredictorMfeEns2dHeuristicSeedExtension, + PredictorMfeEns2dSeedExtension, BasePairProbabilities, probability writers : + + reverse retained candidate chains and signed seed-extension recurrences to + obtain marginals of the existing heuristic ensemble; preserve denominator + and seed-overlap corrections, validate all masses before publication + + expose approximation metadata in CSV/SVG and retain seed provenance + * bound cropped noLP continuations by the active region and correct default + region offsets; reject unsupported singleton heuristic seeds explicitly + * CommandLineParsing, tests, README.md, doc/recursions : + + enable model P/mode H probability output; add independent tiny-chain and + signed-extension checks plus CLI parity/metadata coverage (PR #259 step 11) + * PredictorMfeEns2d, probability validation tests and report : + * reject finite-energy boundary underflow and nonfinite heuristic denominators + before publication; record independent review, full suites and performance + 261009 Alexander Mitrofanov * PredictorMfeEns2d, CommandLineParsing : + compute actual-pair masses by reversing the existing unseeded recurrence; diff --git a/README.md b/README.md index 888f143f..f3089346 100644 --- a/README.md +++ b/README.md @@ -2017,7 +2017,8 @@ or `STDERR` instead of a file name. `--out=bpProb:FILE` writes the probability that each target/query nucleotide pair actually pairs, conditional on an allowed interaction. It requires -`--model=P --mode=M` with `--noSeed` or a stack-only seed handler. For example: +`--model=P --mode=M` with `--noSeed` or a stack-only seed handler, or +`--model=P --mode=H` for an approximate heuristic ensemble. For example: ```sh IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ @@ -2030,12 +2031,19 @@ is also used without `bpProb`, so requesting the matrix does not change `Zall`. Computed seeds preserve their handler-specific admission thresholds. Explicit stacked seeds may have different lengths, including singletons; competing explicit patterns at the same start still retain only the selected minimum-energy -pattern. Other predictors retain their existing behavior. Heuristic, kinetic, -evaluation, seed-only and bulged-seed `bpProb` requests are rejected. +pattern. With `--mode=H`, probabilities describe the predictor's retained +candidate ensemble, not all possible interactions. Unseeded H retains one +continuation per left pair; seeded H retains its seed-extension candidates and +selected right extension, including the existing seed-overlap corrections. +Seeded H supports computed or explicit seeds with at least two pairs, including +bulged seeds. These heuristic ensembles can retain path multiplicities and +must not be interpreted as the exact unique-chain ensemble of mode M. +Kinetic, evaluation, seed-only and exact bulged-seed probability requests remain +unsupported. Rows follow the original target and columns the original query, both 5' to 3'. -The header starts with `bpProb`; labels contain each nucleotide and its display -index (including `tIdxPos0`/`qIdxPos0` shifts). Entries use sufficient significant +The header starts with `bpProb` (exact) or `bpProbApproximate` (heuristic); labels +contain each nucleotide and its display index (including `tIdxPos0`/`qIdxPos0` shifts). Entries use sufficient significant digits for the partition type, with scientific notation for small probabilities. A successful empty ensemble contains `NA`; pairs absent from a nonempty ensemble have probability zero. The output uses `outSep`, the usual multi-FASTA filename @@ -2061,12 +2069,14 @@ including its numerical zero cutoffs, without changing ordinary prediction. The seeded backend checks nonnegative arithmetic and reports numerical range failures as errors, including underflow, overflow and NaN. Positive subnormals are rejected; there is currently no scaling fallback. Failed or cancelled regional work never -produces a successful-looking probability matrix. Ordinary floating-point -rounding remains; "exact" describes the enumerated ensemble, not real arithmetic. +produces a successful-looking probability matrix. Heuristic reverse passes likewise +validate finite arithmetic and reject invalid masses before publication. Ordinary +floating-point rounding remains; "exact" describes the enumerated ensemble, not +real arithmetic. For library use, keep a `BasePairProbabilities` owner alive across predictions, pass its optional non-owning pointer to `PredictorMfeEns2dSeedExtension` (stack -seeds) or `PredictorMfeEns2d` (unseeded), and call +seeds), `PredictorMfeEns2d` (unseeded), or their heuristic subclasses, and call `finalize()` only after all requested disjoint regions succeed. A writer is called explicitly after finalization; its destructor publishes nothing. The output constraint must enable `needZall`. The [standalone API example](doc/seeded-probabilities-example.cpp) @@ -2078,9 +2088,9 @@ show the public API and measured limits. ### SVG base-pair probability dot plots `--out=bpsvg:FILE` renders the same actual-pair probabilities as `bpProb` in a -standalone SVG. Both outputs can be requested together. The same exact -`--model=P --mode=M` unseeded/stack-seed restrictions, regional ensemble, normalization -and success-only publication rules apply. +standalone SVG. Both outputs can be requested together. The same supported +model/mode combinations, regional ensemble, normalization and success-only +publication rules apply. Heuristic SVGs are explicitly labelled approximate. ```sh IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ diff --git a/doc/analysis/seeded-probabilities-validation.md b/doc/analysis/seeded-probabilities-validation.md index 3449a5ba..56c83716 100644 --- a/doc/analysis/seeded-probabilities-validation.md +++ b/doc/analysis/seeded-probabilities-validation.md @@ -270,3 +270,51 @@ Seven interleaved runs gave wall medians 0.412419→0.390979 seconds (ranges RSS 87,744→84,544 KiB. The slowdown did not reproduce without filesystem writes; the fixed serializer was 5.2% faster in this specific control. Use the same sparse400 command with `--out=STDOUT --out=bpsvg:/dev/null > /dev/null` to repeat. + +## PR #259 review: SVG revisions and steps 10–11 (2026-10-09) + +Martin's review adds unseeded and heuristic probability outputs to the original +SVG/documentation scope. Exact unseeded mode M now reverses each fixed-right-end +inside table. Unseeded mode H reverses its retained continuation chains; seeded +mode H traces its signed left/right extension arithmetic, including the existing +seed-overlap corrections and selected right extensions. Heuristic output is +explicitly approximate (`bpProbApproximate` and an approximate SVG title). Its +candidate ensemble can retain path multiplicities and is not the exact seeded +unique-chain ensemble. Existing numerical-zero cutoffs remain in the legacy +unseeded/heuristic objectives. + +The SVG now starts both strands at the bottom-left origin, places the query name +below the matrix, uses sequence names and rounded values in tooltips, displays +intramolecular pairing as `1-Pu`, and outlines the best reported interaction region +in green. Machine-readable probability attributes keep full precision. The MFE +outline is available with `--outNumber=0`; ordinary ranked output stays suppressed. +An outline is omitted if no interaction passes the reporting filters. + +Independent agents implemented and reviewed the recurrences, checked actual-pair +ownership, and evaluated performance before push. The new API tests compare exact +and unseeded heuristic results with independently enumerated tiny-chain ensembles. +Seeded heuristic tests change one pair's Boltzmann fugacity and compare the change +in the scalar partition function with that pair's numerator. They freeze the +baseline selected right extension and concrete computed seed family, so the test +checks marginals of the implemented conditional ensemble rather than a different +family selected after perturbation. Cases include overlapping/disjoint and bulged +seeds, LP/noLP, regions, seed annotations, successful empty results, singleton seed +rejection, and failure publication. Separate symbolic checks of 500 randomized +small legacy seeded extension polynomials found no negative or over-total pair +masses in the sampled cases. + +The review found and fixed active-region offset/dimension errors, finite-boundary +Boltzmann underflow masquerading as an empty result, and a nonfinite seeded-H +denominator bypassing a relative-tolerance comparison. These fixes have regression +coverage. CSV/SVG CLI tests cover approximation metadata, numeric parity, seed +masks, MFE geometry, signed coordinates and unchanged ordinary output. + +The final GCC 14.4 release and debug runs each pass all seven `make tests -j2` +suites and 100,947 API assertions in 97 cases. All 74 installed public headers +compile independently. Installed pkg-config consumers for exact/heuristic, +seeded/unseeded predictors compile, link and produce CSV/SVG with the expected +approximation metadata. `make dist` contains the new predictor tests +and implementation. All revised text is UTF-8/LF and `git diff --check` passes. +Both new recurrence SVGs parse, render and passed independent equation review; +representative output SVGs were rendered and visually inspected. Apple Clang was +not available locally. diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2d.outside.svg b/doc/recursions/IntaRNA.PredictorMfeEns2d.outside.svg new file mode 100644 index 00000000..a44c7205 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2d.outside.svg @@ -0,0 +1,69 @@ + +Unseeded pair probabilities · fixed-right outside pass +PredictorMfeEns2d | --model=P --mode=M --noSeed | reverse the existing hybrid recurrence. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Unseeded pair probabilities · fixed-right outside pass +PredictorMfeEns2d | --model=P --mode=M --noSeed | reverse the existing hybrid recurrence + +A pair i=(i₁,i₂) uses local energy coordinates; RNA 2 is reversed. i ≺ k increases both coordinates. +Fix right pair j. Hⱼ(i) is the forward hybrid weight; Aⱼ(i)=∂Z/∂Hⱼ(i) is its outside adjoint. +M(i) sums complete-interaction weights containing actual pair i, over every right boundary j. +d=BW(E_init); t(i,k)=BW(E_interLeft(i,k)); i⁺=i+(1,1); s=t(i,i⁺) + +1 Initialize the same completed-boundary objective +Aⱼ(i) ← B(i,j); Z += B(i,j)Hⱼ(i) +B(i,j)=BW(energy.getE(i₁,j₁,i₂,j₂,0)) only when !Z_equal(Hⱼ(i),0) and isValidOutputSite() pass. +Otherwise B=0. Complete ED / GU-end filters and native terminal terms match updateZ(). + +2 Reverse i in increasing order; credit its explicit pair +M(i) += Aⱼ(i)Hⱼ(i) +Every represented suffix contains its left pair i exactly once. Process only nonzero Aⱼ(i) and Hⱼ(i). +Then propagate Aⱼ(i) through each forward term below; later suffix states receive all enclosing mass. + +3a Lonely pairs allowed +Hⱼ(j)=d +Aⱼ(k) += Aⱼ(i)t(i,k) +Direct terminal: k=j; its pair is counted at j. +Intermediate k: i ≺ k ≺ j, allowed loop, +and !Z_equal(Hⱼ(k),0) as in the forward sum. + +3b noLP: direct stacking +Aⱼ(i⁺) += Aⱼ(i)s if i⁺ ≠ j +M(j) += Aⱼ(i)s d if i⁺ = j +A noLP terminal has Hⱼ(j)=0. +Its second pair j is implicit in the two-pair +initialization and is credited separately. + +3c noLP: stack, then a nonstack loop + +Hⱼ(k) + + + +i + +i⁺ + +k + +j +a = Aⱼ(i)s t(i⁺,k) +Aⱼ(k) += a +M(i⁺) += a Hⱼ(k) +Require complementary i⁺; i⁺ ≺ k ≺ j; k ≠ i⁺+(1,1); the same loop limits and epsilon gate on Hⱼ(k). +Here i⁺ is an actual pair skipped by the DP-state jump. Unpaired loop positions receive no mass. + +4 Commit successful regions and normalize only once +P(i,j) = ∑ M_region(i,j) / ∑ Z_region +Check the accumulated objective equals the unchanged Zall; map pairs with energy.getBasePair(). +Original target/query orientation is stored in the sink. Z=0 → NA; failed arithmetic → no result. +Conditional on an allowed interaction; no unbound-state weight. No outside buffers when output is disabled. +Exact for the existing forward recurrence, including its numerical-zero gates; no alternative ensemble is substituted. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2d.cpp: fillHybridZ(), accumulatePairMasses(), predictRegion() +PredictorMfeEns.cpp: addPartitionContribution(); BasePairProbabilities: addRegion(), finalize() + diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.probabilities.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.probabilities.svg new file mode 100644 index 00000000..3a11d160 --- /dev/null +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristic.probabilities.svg @@ -0,0 +1,80 @@ + +Unseeded heuristic probabilities · candidate flow +PredictorMfeEns2dHeuristic | --model=P --mode=H --noSeed | approximate retained ensemble. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. + + + +INTARNA / RECURRENCE NOTES +Unseeded heuristic probabilities · candidate flow +PredictorMfeEns2dHeuristic | --model=P --mode=H --noSeed | approximate retained ensemble + +Each retained cell C(i) represents one selected pair chain, with hybrid weight vᵢ and right boundary jᵢ. +Pair coordinates increase on both strands internally; RNA 2 is reversed. Set i⁺=i+(1,1). +d=BW(E_init), t(i,k)=BW(E_interLeft(i,k)), and B(i,j)=BW(energy.getE(i₁,j₁,i₂,j₂,0)). + +Approximate means the continuation ensemble is pruned +Visit i in decreasing order. Generate candidates from already selected child chains C(k). +Every admitted candidate contributes to Z; only the candidate with the lowest complete site energy +is retained as C(i) for later extensions. Strict improvement wins; native energy rounding and ties are preserved. + +1 Record which new pairs each candidate owns +Candidate +Hybrid weight v +Owned pairs S +Child k +LP: initial pair +d +{i} +none +LP: loop / stack to k +t(i,k)vₖ +{i} +k +noLP: initial stack +s d, s=t(i,i⁺) +{i, i⁺} +none +noLP: direct stacking +s vᵢ⁺ +{i} +i⁺ +noLP: nonstack loop to k +s t(i⁺,k)vₖ +{i, i⁺} +k +Native pair, loop/span and right-end GU constraints apply; noLP loops exclude the adjacent child k=i⁺+(1,1). + +2 Initialize mass and flow from every admitted candidate +w = B(i,j)v; Z += w; M(p) += w for p ∈ S; F(k) += w if a child exists +M starts at zero; F is accumulated incoming weight for a retained child chain. This includes unselected candidates. +Use exactly updateZ() admission: !Z_equal(v,0), complete-site GU / ED filters, and finite arithmetic. +The current candidate owns S; every remaining pair belongs to its selected child continuation. + +3 Propagate incoming mass through selected chains + +C(k) + + + +i + +k + +jₖ +Sweep i in increasing order. Let Sᵢ and kᵢ describe C(i). +M(p) += F(i) for every p ∈ Sᵢ; +F(kᵢ) += F(i) when C(i) has a child. +Every edge increases both indices, so all parents arrive first. +No division by vᵢ is needed: a retained cell represents one chain, so its full incoming weight follows every pair. + +4 Publish marginals of the retained candidate ensemble +P(i,j) = ∑ M_region(i,j) / ∑ Z_region +Map energy coordinates back to original strand order, merge disjoint regions, then validate and normalize. +The denominator must equal the unchanged heuristic Zall. Mark the result approximate, including empty results. +Missing continuation chains are not recovered. These marginals differ from the exact --mode=M ensemble. +Only actual paired nucleotides receive mass; positions merely enclosed by a retained interaction remain unmarked. + +Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion +PredictorMfeEns2dHeuristic.cpp: fillHybridZ(), recordProbabilityCandidate(), commitProbabilityRegion() +BasePairProbabilities: markApproximate(), addRegion(), finalize(); PredictorMfeEns.cpp: updateZ() + diff --git a/src/IntaRNA/BasePairProbabilities.cpp b/src/IntaRNA/BasePairProbabilities.cpp index 0496e534..4d06f327 100644 --- a/src/IntaRNA/BasePairProbabilities.cpp +++ b/src/IntaRNA/BasePairProbabilities.cpp @@ -4,6 +4,10 @@ #include namespace IntaRNA { +void BasePairProbabilities::markApproximate() { + if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); + approximate=true; +} BasePairProbabilities::BasePairProbabilities(size_t n,size_t m,bool collectSeedPairs) : mass(n,m,0), collectSeeds(collectSeedPairs), seeds(collectSeedPairs?n:0,collectSeedPairs?m:0,0) {} void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, diff --git a/src/IntaRNA/BasePairProbabilities.h b/src/IntaRNA/BasePairProbabilities.h index bae84b8d..9e62d048 100644 --- a/src/IntaRNA/BasePairProbabilities.h +++ b/src/IntaRNA/BasePairProbabilities.h @@ -49,6 +49,12 @@ class BasePairProbabilities { const Matrix & rawMasses() const; /** @return whether this sink requests admitted seed-cell annotations */ bool collectsSeedPairs() const; + /** Mark an ensemble restricted by heuristic continuation choices. + * Must be called while pending, before publication. + */ + void markApproximate(); + /** @return whether the represented interaction ensemble is heuristic */ + bool isApproximate() const; /** @return seed-cell mask in original coordinates, empty when not requested */ const Matrix & seedPairs() const; /** @return validated matrix; only available for finalized nonempty results. @@ -57,6 +63,7 @@ class BasePairProbabilities { Matrix probabilities() const; private: Status state=Status::pending; + bool approximate=false; Z_type z=0; Matrix mass; const bool collectSeeds; @@ -67,6 +74,7 @@ inline BasePairProbabilities::Status BasePairProbabilities::status() const { ret inline Z_type BasePairProbabilities::getZ() const { return z; } inline const Matrix & BasePairProbabilities::rawMasses() const { return mass; } inline bool BasePairProbabilities::collectsSeedPairs() const { return collectSeeds; } +inline bool BasePairProbabilities::isApproximate() const { return approximate; } inline const Matrix & BasePairProbabilities::seedPairs() const { return seeds; } inline void BasePairProbabilities::fail() noexcept { state=Status::failed; } } diff --git a/src/IntaRNA/BasePairProbabilitySvg.cpp b/src/IntaRNA/BasePairProbabilitySvg.cpp index d350547f..5b7620ee 100644 --- a/src/IntaRNA/BasePairProbabilitySvg.cpp +++ b/src/IntaRNA/BasePairProbabilitySvg.cpp @@ -94,9 +94,10 @@ BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const I out<::max_digits10); out<<"\n" - <<"Base-pair probabilities: "<\n" + <<""<<(result.isApproximate()?"Approximate base-pair probabilities: ":"Base-pair probabilities: ")<<xml(t.getId())<<" / "<<xml(q.getId())<<"\n" <<"Target rows run 5' to 3' upwards and query columns run 5' to 3' to the right. Pair probabilities are conditional on an allowed interaction. " - <<"The frame shows single-nucleotide intramolecular pairing probabilities (1 - Pu) from the accessibility model.\n" + <<"The frame shows single-nucleotide intramolecular pairing probabilities (1 - Pu) from the accessibility model. " + <<(result.isApproximate()?"Approximate probabilities describe the retained heuristic ensemble.":"Probabilities describe the exact predictor ensemble.")<<"\n" < text { font-family: sans-serif; fill: #243247; } .heading { font-size: 18px; font-weight: bold; } @@ -126,7 +127,7 @@ text { font-family: sans-serif; fill: #243247; } auto text=[&](double tx,double ty,const std::string & value,const std::string & cls,const char * anchor="middle") { out<<""<\n"; }; - text(width/2,30,"Base-pair probabilities","heading"); + text(width/2,30,result.isApproximate()?"Approximate base-pair probabilities":"Base-pair probabilities","heading"); if (t.getId()!="target" || q.getId()!="query") text(width/2,53,t.getId()+" / "+q.getId(),"subtitle"); text(x+w/2,y+h+65,q.getId()+" (5' to 3')","axis"); diff --git a/src/IntaRNA/BasePairProbabilityWriter.cpp b/src/IntaRNA/BasePairProbabilityWriter.cpp index d38a744b..b8ee89bf 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.cpp +++ b/src/IntaRNA/BasePairProbabilityWriter.cpp @@ -18,7 +18,8 @@ std::string BasePairProbabilityWriter::block(const BasePairProbabilities & resul // Validate every normalized value before opening a file or writing a header. const auto p=empty?Matrix():result.probabilities(); std::ostringstream data; - data<::max_digits10)<<"bpProb"; + data<::max_digits10) + <<(result.isApproximate()?"bpProbApproximate":"bpProb"); for(size_t j=0;j namespace IntaRNA { -/** Checked nonnegative arithmetic for exact seeded partitions only. +/** Checked nonnegative arithmetic for pair-probability partitions. * Structural zero is exactly zero. Positive subnormals are rejected because * their relative precision is not guaranteed. Ordinary sum rounding remains. */ @@ -24,7 +24,7 @@ class PartitionArithmetic { inline Z_type PartitionArithmetic::check(const Z_type x) { if (!(x>=0 && x<=std::numeric_limits::max()) || (x>0 && x::min())) - throw std::range_error("exact seeded partition: numerical range exceeded"); + throw std::range_error("partition arithmetic: numerical range exceeded"); return x; } inline Z_type PartitionArithmetic::add(const Z_type a,const Z_type b) { @@ -34,19 +34,19 @@ inline Z_type PartitionArithmetic::multiply(const Z_type a,const Z_type b) { check(a); check(b); if (a==0 || b==0) return 0; const Z_type r=check(a*b); - if (r==0) throw std::range_error("exact seeded partition: product underflow"); + if (r==0) throw std::range_error("partition arithmetic: product underflow"); return r; } inline Z_type PartitionArithmetic::divide(const Z_type a,const Z_type b) { check(a); check(b); - if (b==0) throw std::range_error("exact seeded partition: zero denominator"); + if (b==0) throw std::range_error("partition arithmetic: zero denominator"); const Z_type r=check(a/b); - if (a>0 && r==0) throw std::range_error("exact seeded partition: quotient underflow"); + if (a>0 && r==0) throw std::range_error("partition arithmetic: quotient underflow"); return r; } inline Z_type PartitionArithmetic::exp(const Z_type exponent) { const Z_type r=check(Z_exp(exponent)); - if (r==0) throw std::range_error("exact seeded partition: exponential underflow"); + if (r==0) throw std::range_error("partition arithmetic: exponential underflow"); return r; } } diff --git a/src/IntaRNA/PredictorMfeEns2d.cpp b/src/IntaRNA/PredictorMfeEns2d.cpp index 490bfe9b..a03506da 100644 --- a/src/IntaRNA/PredictorMfeEns2d.cpp +++ b/src/IntaRNA/PredictorMfeEns2d.cpp @@ -256,7 +256,10 @@ PredictorMfeEns2d::accumulatePairMasses(size_t j1,size_t j2,Z2dMatrix & outside, outside(i1,i2)=0; const Z_type h=A::check(hybridZ(i1,i2)); if (!Z_equal(h,0) && isValidOutputSite(i1,j1,i2,j2)) { - const Z_type b=A::check(energy.getBoltzmannWeight(energy.getE(i1,j1,i2,j2,E_type(0)))); + const E_type boundaryEnergy=energy.getE(i1,j1,i2,j2,E_type(0)); + const Z_type b=A::check(energy.getBoltzmannWeight(boundaryEnergy)); + if (b==0 && E_isNotINF(boundaryEnergy)) + throw std::range_error("unseeded pair probabilities: boundary weight underflow"); outside(i1,i2)=b; denominator=A::add(denominator,A::multiply(h,b)); } diff --git a/src/IntaRNA/PredictorMfeEns2d.h b/src/IntaRNA/PredictorMfeEns2d.h index 169b9881..2b77bb67 100644 --- a/src/IntaRNA/PredictorMfeEns2d.h +++ b/src/IntaRNA/PredictorMfeEns2d.h @@ -36,7 +36,9 @@ class PredictorMfeEns2d: public PredictorMfeEns { * tracking is to be done; if non-NULL, the tracker gets deleted * on this->destruction. * @param pairProbabilities optional non-owning sink; requires needZall and - * must remain pending until all disjoint regions have succeeded + * must remain pending until all disjoint regions have succeeded. + * Collection reverses this class's fillHybridZ recurrence; subclasses + * changing that recurrence must also supply their own collection. */ PredictorMfeEns2d( const InteractionEnergy & energy , OutputHandler & output diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp b/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp index 806ea98a..93b57cbf 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp +++ b/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp @@ -1,5 +1,6 @@ #include "IntaRNA/PredictorMfeEns2dHeuristic.h" +#include "IntaRNA/PartitionArithmetic.h" #include @@ -11,8 +12,9 @@ PredictorMfeEns2dHeuristic:: PredictorMfeEns2dHeuristic( const InteractionEnergy & energy , OutputHandler & output - , PredictionTracker * predTracker ) - : PredictorMfeEns2d(energy,output,predTracker) + , PredictionTracker * predTracker + , BasePairProbabilities * pairProbabilities ) + : PredictorMfeEns2d(energy,output,predTracker,pairProbabilities) { } @@ -33,6 +35,14 @@ PredictorMfeEns2dHeuristic:: predict( const IndexRange & r1 , const IndexRange & r2 ) +{ + try { predictRegionHeuristic(r1,r2); } + catch (...) { if (pairProbabilities) pairProbabilities->fail(); throw; } +} + +void +PredictorMfeEns2dHeuristic:: +predictRegionHeuristic(const IndexRange & r1,const IndexRange & r2) { #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_logOutput) @@ -48,15 +58,28 @@ predict( const IndexRange & r1 #endif + if (!r1.isAscending() || !r2.isAscending() + || r1.from>=energy.getAccessibility1().getSequence().size() + || r2.from>=energy.getAccessibility2().getSequence().size()) + throw std::invalid_argument("heuristic ensemble prediction: invalid region"); + if (pairProbabilities && pairProbabilities->status()!=BasePairProbabilities::Status::pending) + throw std::logic_error("heuristic pair probabilities: accumulator is not pending"); + if (pairProbabilities) pairProbabilities->markApproximate(); // set index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); // resize matrix hybridZ.resize( std::min( energy.size1() - , (r1.to==RnaSequence::lastPos?energy.size1()-1:r1.to)-r1.from+1 ) + , r1.to==RnaSequence::lastPos?energy.size1():r1.to-r1.from+1 ) , std::min( energy.size2() - , (r2.to==RnaSequence::lastPos?energy.size2()-1:r2.to)-r2.from+1 ) ); + , r2.to==RnaSequence::lastPos?energy.size2():r2.to-r2.from+1 ) ); + if (pairProbabilities) { + probabilityContinuation = Matrix(hybridZ.size1(),hybridZ.size2()); + probabilityFlow = Matrix(hybridZ.size1(),hybridZ.size2(),0); + probabilityMass = Matrix(hybridZ.size1(),hybridZ.size2(),0); + probabilityDenominator = 0; + } // init mfe for later updates initOptima(); @@ -68,6 +91,7 @@ predict( const IndexRange & r1 // trace back and output handler update reportOptima(); + if (pairProbabilities) commitProbabilityRegion(); } @@ -109,8 +133,8 @@ fillHybridZ() // no lp allowed if (noLpShift != 0) { // check if right-side stacking of (i1,i2) is possible - if ( i1+noLpShift < energy.size1() - && i2+noLpShift < energy.size2() + if ( i1+noLpShift < hybridZ.size1() + && i2+noLpShift < hybridZ.size2() && energy.areComplementary(i1+noLpShift,i2+noLpShift)) { // get stacking term to avoid recomputation @@ -130,6 +154,10 @@ fillHybridZ() curCellEtotal = energy.getE(i1,i1+noLpShift, i2,i2+noLpShift ,energy.getE(curCell->val)); // update overall partition function information for initial bps only updateZ( i1,curCell->j1, i2,curCell->j2, curCell->val, true ); + if (pairProbabilities) { + probabilityContinuation(i1,i2)={RnaSequence::lastPos,RnaSequence::lastPos,noLpShift!=0}; + recordProbabilityCandidate(i1,curCell->j1,i2,curCell->j2,curCell->val,probabilityContinuation(i1,i2)); + } } @@ -150,6 +178,8 @@ fillHybridZ() // update overall partition function information for current right extension updateZ( i1,rightExt->j1, i2,rightExt->j2, curZ, true ); + if (pairProbabilities) + recordProbabilityCandidate(i1,rightExt->j1,i2,rightExt->j2,curZ,{i1+noLpShift,i2+noLpShift,false}); // check if this combination yields better energy curEtotal = energy.getE(i1,rightExt->j1, i2,rightExt->j2, energy.getE(curZ)); @@ -164,6 +194,8 @@ fillHybridZ() curCell->val = curZ; // store total energy to avoid recomputation curCellEtotal = curEtotal; + if (pairProbabilities) + probabilityContinuation(i1,i2)={i1+noLpShift,i2+noLpShift,false}; } } } @@ -196,6 +228,8 @@ fillHybridZ() // update overall partition function information for current right extension updateZ( i1,rightExt->j1, i2,rightExt->j2, curZ, true ); + if (pairProbabilities) + recordProbabilityCandidate(i1,rightExt->j1,i2,rightExt->j2,curZ,{i1+noLpShift+w1,i2+noLpShift+w2,noLpShift!=0}); // check if this combination yields better energy curEtotal = energy.getE(i1,rightExt->j1, i2,rightExt->j2, energy.getE(curZ)); @@ -210,6 +244,8 @@ fillHybridZ() curCell->val = curZ; // store total energy to avoid recomputation curCellEtotal = curEtotal; + if (pairProbabilities) + probabilityContinuation(i1,i2)={i1+noLpShift+w1,i2+noLpShift+w2,noLpShift!=0}; } } // w2 @@ -224,4 +260,55 @@ fillHybridZ() //////////////////////////////////////////////////////////////////////////// +void PredictorMfeEns2dHeuristic::recordProbabilityCandidate(size_t i1,size_t j1, + size_t i2,size_t j2,Z_type hybrid,const ProbabilityContinuation & continuation) +{ + // Match the legacy heuristic's numerical-zero and output-site admission. + if (Z_equal(hybrid,0) || !isValidOutputSite(i1,j1,i2,j2)) return; + const E_type boundaryEnergy=energy.getE(i1,j1,i2,j2,E_type(0)); + const Z_type boundary=energy.getBoltzmannWeight(boundaryEnergy); + if (boundary==0 && !E_isINF(boundaryEnergy)) + throw std::range_error("heuristic pair probabilities: boundary weight underflow"); + const Z_type weight=PartitionArithmetic::multiply(hybrid,boundary); + probabilityDenominator=PartitionArithmetic::add(probabilityDenominator,weight); + probabilityMass(i1,i2)=PartitionArithmetic::add(probabilityMass(i1,i2),weight); + if (continuation.extraPair) + probabilityMass(i1+1,i2+1)=PartitionArithmetic::add(probabilityMass(i1+1,i2+1),weight); + if (continuation.next1!=RnaSequence::lastPos) + probabilityFlow(continuation.next1,continuation.next2)=PartitionArithmetic::add( + probabilityFlow(continuation.next1,continuation.next2),weight); +} + +void PredictorMfeEns2dHeuristic::commitProbabilityRegion() +{ + if (probabilityDenominator!=Zall) + throw std::logic_error("heuristic updateZ override did not preserve the pair-probability objective"); + const size_t n=hybridZ.size1(),m=hybridZ.size2(); + // Every retained cell represents exactly one chain. Its incoming candidate + // mass can therefore pass unchanged to the selected child, without division. + for(size_t i=0;i original(n,m,0); + for(size_t i=0;icollectsSeedPairs(); + Matrix seeds(annotate?n:0,annotate?m:0,0); + pairProbabilities->addRegion(target,query,Zall,original,annotate?&seeds:nullptr); +} + +//////////////////////////////////////////////////////////////////////////// + } // namespace diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristic.h b/src/IntaRNA/PredictorMfeEns2dHeuristic.h index 8167c1c5..75e5b935 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristic.h +++ b/src/IntaRNA/PredictorMfeEns2dHeuristic.h @@ -18,6 +18,10 @@ namespace IntaRNA { * all possible interaction ranges. * * This yields a quadratic time and space complexity. + * Optional pair probabilities refer to all admitted candidate chains considered + * by this pruning rule, with the same Zall as ordinary heuristic prediction. + * They approximate the unrestricted interaction ensemble. Collection adds + * quadratic storage and a reverse traversal of the retained continuations. * * @author Martin Raden * @author Frank Gelhausen @@ -40,10 +44,13 @@ class PredictorMfeEns2dHeuristic: public PredictorMfeEns2d { * @param predTracker the prediction tracker to be used or NULL if no * tracking is to be done; if non-NULL, the tracker gets deleted * on this->destruction. + * @param pairProbabilities optional non-owning sink for probabilities of the + * pruned candidate ensemble; requires needZall */ PredictorMfeEns2dHeuristic( const InteractionEnergy & energy , OutputHandler & output - , PredictionTracker * predTracker ); + , PredictionTracker * predTracker + , BasePairProbabilities * pairProbabilities = nullptr ); virtual ~PredictorMfeEns2dHeuristic(); @@ -75,8 +82,35 @@ class PredictorMfeEns2dHeuristic: public PredictorMfeEns2d { //! energy of all interaction hybrids starting in i1,i2 Z2dMatrix hybridZ; + //! The selected chain owns its first pair and, optionally, its next stack pair. + struct ProbabilityContinuation { + size_t next1 = RnaSequence::lastPos, next2 = RnaSequence::lastPos; + bool extraPair = false; + }; + //! Allocated only for probability output; selected continuations form a DAG. + Matrix probabilityContinuation; + //! Complete candidate weight flowing into each retained continuation. + Matrix probabilityFlow; + //! Numerators in local target/reversed-query coordinates until region commit. + Matrix probabilityMass; + //! Independently accumulated objective, required to agree exactly with Zall. + Z_type probabilityDenominator = 0; + protected: + /** Compute one region under predict()'s failure guard. */ + void predictRegionHeuristic(const IndexRange & r1,const IndexRange & r2); + + /** Record precisely the accepted updateZ candidate, crediting newly prepended + * pairs and routing its full weight to the retained child chain. Coordinates + * are local and the query is reversed; lastPos denotes an initial candidate. + */ + void recordProbabilityCandidate(size_t i1,size_t j1,size_t i2,size_t j2, + Z_type hybrid,const ProbabilityContinuation & continuation); + + /** Propagate candidate weights through selected chains and commit this region. */ + void commitProbabilityRegion(); + /** * Computes all entries of the hybridE matrix * and reports all valid interactions via updateOptima() diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp index 4cf8aeaa..278eb740 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp @@ -9,14 +9,16 @@ PredictorMfeEns2dHeuristicSeedExtension( const InteractionEnergy & energy , OutputHandler & output , PredictionTracker * predTracker - , SeedHandler * seedHandlerInstance ) + , SeedHandler * seedHandlerInstance + , BasePairProbabilities * pairProbabilities ) : - PredictorMfeEns2dSeedExtension(energy,output,predTracker,seedHandlerInstance) + PredictorMfeEns2dSeedExtension(energy,output,predTracker,seedHandlerInstance,pairProbabilities) , E_right_opt(E_INF) , j1opt(0) , j2opt(0) { - assert( seedHandler.getConstraint().getBasePairs() > 1 ); + if(seedHandler.getConstraint().getBasePairs()<2 && seedHandler.getConstraint().getExplicitSeeds().empty()) + throw std::invalid_argument("heuristic seed extension requires at least two seed pairs"); } ////////////////////////////////////////////////////////////////////////// @@ -32,6 +34,12 @@ void PredictorMfeEns2dHeuristicSeedExtension:: predict( const IndexRange & r1, const IndexRange & r2 ) { + try { + if(pairProbabilities) pairProbabilities->markApproximate(); + if(!r1.isAscending() || !r2.isAscending() + || r1.from>=energy.getAccessibility1().getSequence().size() + || r2.from>=energy.getAccessibility2().getAccessibilityOrigin().getSequence().size()) + throw std::invalid_argument("heuristic seeded prediction: invalid region"); #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_logOutput) #endif @@ -52,16 +60,21 @@ predict( const IndexRange & r1, const IndexRange & r2 ) seedHandler.setOffset2(r2.from); const size_t range_size1 = std::min( energy.size1() - , (r1.to==RnaSequence::lastPos?energy.size1()-1:r1.to)-r1.from+1 ); + , r1.to==RnaSequence::lastPos?energy.size1():r1.to-r1.from+1 ); const size_t range_size2 = std::min( energy.size2() - , (r2.to==RnaSequence::lastPos?energy.size2()-1:r2.to)-r2.from+1 ); + , r2.to==RnaSequence::lastPos?energy.size2():r2.to-r2.from+1 ); + + initExtensionProbabilities(range_size1,range_size2); // compute seed interactions for whole range // and check if any seed possible - if (seedHandler.fillSeed( 0, range_size1-1, 0, range_size2-1 ) == 0) { + if ((seedHandler.getConstraint().getExplicitSeeds().empty() + && (range_size1 range_size1 || sj2 > range_size2) + if (sj1 >= range_size1 || sj2 >= range_size2 + || sl1>energy.getAccessibility1().getMaxLength() || sl2>energy.getAccessibility2().getMaxLength()) continue; const size_t maxMatrixLen1 = energy.getAccessibility1().getMaxLength()-sl1+1; const size_t maxMatrixLen2 = energy.getAccessibility2().getMaxLength()-sl2+1; @@ -93,6 +108,8 @@ predict( const IndexRange & r1, const IndexRange & r2 ) j2opt = sj2; E_right_opt = E_INF; + beginExtensionProbabilitySeed(si1,si2); + // ER hybridZ_right.resize( std::min(range_size1-sj1, maxMatrixLen1), std::min(range_size2-sj2, maxMatrixLen2) ); fillHybridZ_right(sj1, sj2, si1, si2); @@ -104,10 +121,13 @@ predict( const IndexRange & r1, const IndexRange & r2 ) fillHybridZ_left(si1, si2); } + finishExtensionProbabilitySeed(); } // si1 / si2 // report mfe interaction + commitExtensionProbabilities(); reportOptima(); + } catch (...) { if(pairProbabilities) pairProbabilities->fail(); throw; } } @@ -141,6 +161,7 @@ fillHybridZ_right( const size_t sj1, const size_t sj2 // update overall partition function information for true right-extensions of the current seed // seed only not covered due to min-val of r1,r2 updateZ(si1, sj1+r1, si2, sj2+r2, seedZ * rightExtZ * initZ, true); + addExtensionProbabilityRoot(si1,sj1+r1,si2,sj2+r2,seedZ*rightExtZ*initZ,false,true); } } @@ -175,6 +196,7 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) // Z( left + seed ); covers seed only updateZ(si1-l1, sj1, si2-l2, sj2, curZ * seedZ, true); + addExtensionProbabilityRoot(si1-l1,sj1,si2-l2,sj2,curZ*seedZ,true,false); // Z( left + seed + rightOpt ) and rightOpt true seed extension if ( l1 > 0 // true left seed extension @@ -186,6 +208,7 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) ) { updateZ(si1-l1, j1opt, si2-l2, j2opt, curZ * seedZ * rightOptZ, true); + addExtensionProbabilityRoot(si1-l1,j1opt,si2-l2,j2opt,curZ*seedZ*rightOptZ,true,true); } } } // i2 diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h index ff8deada..a0ea94c7 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h @@ -32,13 +32,15 @@ class PredictorMfeEns2dHeuristicSeedExtension: public PredictorMfeEns2dSeedExten * @param predTracker the prediction tracker to be used or NULL if no * tracking is to be done; if non-NULL, the tracker gets deleted * on this->destruction. + * @param pairProbabilities optional non-owning sink for this approximate ensemble * @param seedHandler the seed handler to be used */ PredictorMfeEns2dHeuristicSeedExtension( const InteractionEnergy & energy , OutputHandler & output , PredictionTracker * predTracker - , SeedHandler * seedHandler ); + , SeedHandler * seedHandler + , BasePairProbabilities * pairProbabilities = nullptr ); /** diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index fb7c8929..7fd217f0 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -2,9 +2,188 @@ #include "IntaRNA/PredictorMfeEns2dSeedExtension.h" #include "IntaRNA/SeededPartitionFunction.h" #include "IntaRNA/PartitionArithmetic.h" +#include +#include namespace IntaRNA { +// The heuristic objective contains signed seed-overlap corrections. Trace the +// implemented arithmetic directly, retaining pair ownership on every term. +// This is allocated only for requested probability output; ordinary prediction +// follows its original arithmetic and storage path. +struct PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace { + using Pair=Interaction::BasePair; + struct Edge { size_t child; Z_type weight; std::vector pairs; }; + struct Tape { + size_t columns=0; + std::vector> edges; + std::vector adjoint; + void reset(size_t n,size_t m); + void add(size_t p1,size_t p2,size_t c1,size_t c2,Z_type weight,std::vector pairs); + void reverse(const Matrix & values,ExtensionProbabilityTrace & trace); + }; + Matrix mass,absoluteMass; + Matrix seeds; + Tape left,right; + std::vector anchor; + size_t si1=0,si2=0,sj1=0,sj2=0; + Z_type seedZ=0,z=0; + explicit ExtensionProbabilityTrace(size_t n,size_t m); + static Z_type finite(Z_type x); + static Z_type product(Z_type a,Z_type b); + void addMass(const Pair & bp,Z_type value); +}; + +void +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::Tape::reset(size_t n,size_t m) +{ + columns=m; + edges.assign(n*m,{}); + adjoint.assign(n*m,0); +} + +void +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::Tape::add( + size_t p1,size_t p2,size_t c1,size_t c2,Z_type weight,std::vector pairs) +{ + finite(weight); + edges.at(p1*columns+p2).push_back({c1*columns+c2,weight,std::move(pairs)}); +} + +void +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::Tape::reverse( + const Matrix & values,ExtensionProbabilityTrace & trace) +{ + for(size_t parent=edges.size();parent-- >0;) { + finite(values(parent/columns,parent%columns)); + for(const auto & edge:edges[parent]) { + const Z_type a=product(adjoint[parent],edge.weight); + adjoint[edge.child]=finite(adjoint[edge.child]+a); + const Z_type mass=product(a,values(edge.child/columns,edge.child%columns)); + for(const auto & bp:edge.pairs) trace.addMass(bp,mass); + } + } +} + +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::ExtensionProbabilityTrace(size_t n,size_t m) + : mass(n,m,0),absoluteMass(n,m,0),seeds(n,m,0) +{} + +Z_type +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::finite(Z_type x) +{ + if (!std::isfinite(x)) throw std::range_error("heuristic seeded probabilities: nonfinite arithmetic"); + return x; +} + +Z_type +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::product(Z_type a,Z_type b) +{ + finite(a); finite(b); + const Z_type value=finite(a*b); + if(a!=0 && b!=0 && (value==0 || std::abs(value)::min())) + throw std::range_error("heuristic seeded probabilities: product underflow"); + return value; +} + +void +PredictorMfeEns2dSeedExtension::ExtensionProbabilityTrace::addMass(const Pair & bp,Z_type value) +{ + mass(bp.first,bp.second)=finite(mass(bp.first,bp.second)+value); + absoluteMass(bp.first,bp.second)=finite(absoluteMass(bp.first,bp.second)+std::abs(value)); +} + +void PredictorMfeEns2dSeedExtension::initExtensionProbabilities(size_t n,size_t m) +{ + if(pairProbabilities) extensionProbabilityTrace=std::make_unique(n,m); +} + +std::vector +PredictorMfeEns2dSeedExtension::extensionSeedPrefix(size_t i1,size_t i2,size_t before) const +{ + Interaction seed(energy.getAccessibility1().getSequence(),energy.getAccessibility2().getAccessibilityOrigin().getSequence()); + seed.basePairs.push_back(energy.getBasePair(i1,i2)); + seedHandler.traceBackSeed(seed,i1,i2); + seed.basePairs.push_back(energy.getBasePair(i1+seedHandler.getSeedLength1(i1,i2)-1, + i2+seedHandler.getSeedLength2(i1,i2)-1)); + std::vector pairs; + for(const auto & bp:seed.basePairs) { + const size_t p=energy.getIndex1(bp),q=energy.getIndex2(bp); + if(p::epsilon()*Z_type(n+m+1); + if(std::abs(Zall-trace.z)>tolerance*std::max(std::abs(trace.z),std::abs(Zall))) + throw std::logic_error("heuristic seeded probabilities: outside objective differs from reported partition"); + const auto first=energy.getBasePair(0,0),last=energy.getBasePair(n-1,m-1); + const IndexRange target(first.first,last.first),query(last.second,first.second); + Matrix original(n,m,0); + Matrix annotations(n,m,0); + for(size_t i=0;iaddRegion(target,query,trace.z,original,&annotations); + extensionProbabilityTrace.reset(); +} + ////////////////////////////////////////////////////////////////////////// PredictorMfeEns2dSeedExtension:: @@ -21,8 +200,8 @@ PredictorMfeEns2dSeedExtension( , hybridZ_left( 0,0 ) , hybridZ_right( 0,0 ) { - if (pairProbabilities && (!seedHandler.guaranteesStackOnlySeeds() || !output.getOutputConstraint().needZall)) - throw std::invalid_argument("pair probabilities require a stack-only exact predictor and needZall"); + if (pairProbabilities && !output.getOutputConstraint().needZall) + throw std::invalid_argument("pair probabilities require needZall"); } ////////////////////////////////////////////////////////////////////////// @@ -43,6 +222,10 @@ predict( const IndexRange & r1, const IndexRange & r2 ) catch (...) { if (pairProbabilities) pairProbabilities->fail(); throw; } return; } + if (pairProbabilities) { + pairProbabilities->fail(); + throw std::invalid_argument("exact bulged-seed pair probabilities are unsupported"); + } if (seedHandler.getConstraint().getBasePairs()<2) throw std::invalid_argument("legacy bulged seed extension requires at least two seed pairs"); #if INTARNA_MULITHREADING @@ -252,6 +435,7 @@ void PredictorMfeEns2dSeedExtension:: fillHybridZ_left( const size_t si1, const size_t si2 ) { + if(extensionProbabilityTrace) extensionProbabilityTrace->left.reset(hybridZ_left.size1(),hybridZ_left.size2()); // temporary access const OutputConstraint & outConstraint = output.getOutputConstraint(); #if INTARNA_IN_DEBUG_MODE @@ -297,6 +481,7 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) iStackZ = energy.getBoltzmannWeight(energy.getE_interLeft(i1,i1+noLpShift,i2,i2+noLpShift)); // check just stacked curZ += iStackZ * hybridZ_left(l1-noLpShift,l2-noLpShift); + if(extensionProbabilityTrace) extensionProbabilityTrace->left.add(l1,l2,l1-noLpShift,l2-noLpShift,iStackZ,{{i1,i2}}); } // check all combinations of decompositions into (i1,i2)..(k1,k2)-(j1,j2) @@ -311,6 +496,12 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) curZ += (iStackZ * energy.getBoltzmannWeight(energy.getE_interLeft(i1+noLpShift,k1,i2+noLpShift,k2)) * hybridZ_left(si1-k1,si2-k2)); + if(extensionProbabilityTrace) { + std::vector pairs{{i1,i2}}; + if(noLpShift) pairs.emplace_back(i1+1,i2+1); + extensionProbabilityTrace->left.add(l1,l2,si1-k1,si2-k2, + iStackZ*energy.getBoltzmannWeight(energy.getE_interLeft(i1+noLpShift,k1,i2+noLpShift,k2)),std::move(pairs)); + } } } // k2 } // k1 @@ -356,9 +547,12 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) Z_type correctionTerm = energy.getBoltzmannWeight( nonOverlapE ) * hybridZ_left( si1-si1overlap, si2-si2overlap); curZ -= correctionTerm; + if(extensionProbabilityTrace) extensionProbabilityTrace->left.add(l1,l2,si1-si1overlap,si2-si2overlap, + -energy.getBoltzmannWeight(nonOverlapE),extensionSeedPrefix(i1,i2,si1overlap)); // sanity insurance if (curZ < 0) { curZ = Z_type(0.0); + if(extensionProbabilityTrace) extensionProbabilityTrace->left.edges[l1*extensionProbabilityTrace->left.columns+l2].clear(); } } } else { @@ -371,6 +565,8 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) Z_type correctionTerm = seedZ_rm * hybridZ_left( si1-sj1_rm , si2-sj2_rm ); + if(extensionProbabilityTrace) extensionProbabilityTrace->left.add(l1,l2,si1-sj1_rm,si2-sj2_rm, + -seedZ_rm,extensionSeedPrefix(i1,i2,sj1_rm)); // if noLP : handle explicit loop right of current seed if (outConstraint.noLP) { for (k1=sj1_rm; k1++ < si1; ) { @@ -386,6 +582,8 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) correctionTerm += (seedZ_rm * energy.getBoltzmannWeight(energy.getE_interLeft(sj1_rm,k1,sj2_rm,k2)) * hybridZ_left(si1-k1,si2-k2) ); + if(extensionProbabilityTrace) extensionProbabilityTrace->left.add(l1,l2,si1-k1,si2-k2, + -seedZ_rm*energy.getBoltzmannWeight(energy.getE_interLeft(sj1_rm,k1,sj2_rm,k2)),extensionSeedPrefix(i1,i2,k1)); } } // k2 } // k1 @@ -395,6 +593,7 @@ fillHybridZ_left( const size_t si1, const size_t si2 ) // sanity insurance if (curZ < 0) { curZ = Z_type(0.0); + if(extensionProbabilityTrace) extensionProbabilityTrace->left.edges[l1*extensionProbabilityTrace->left.columns+l2].clear(); } } } // substractThisSeed @@ -413,6 +612,7 @@ void PredictorMfeEns2dSeedExtension:: fillHybridZ_right( const size_t sj1, const size_t sj2 ) { + if(extensionProbabilityTrace) extensionProbabilityTrace->right.reset(hybridZ_right.size1(),hybridZ_right.size2()); // temporary access const OutputConstraint & outConstraint = output.getOutputConstraint(); #if INTARNA_IN_DEBUG_MODE @@ -457,6 +657,7 @@ fillHybridZ_right( const size_t sj1, const size_t sj2 ) // check just stacked seed extension if (j1-noLpShift==sj1 && j2-noLpShift==sj2) { curZ += iStackZ * hybridZ_right(0,0); + if(extensionProbabilityTrace) extensionProbabilityTrace->right.add(j1-sj1,j2-sj2,0,0,iStackZ,{{j1,j2}}); } } @@ -473,6 +674,12 @@ fillHybridZ_right( const size_t sj1, const size_t sj2 ) curZ += ( hybridZ_right(k1-sj1,k2-sj2) * energy.getBoltzmannWeight(energy.getE_interLeft(k1,j1-noLpShift,k2,j2-noLpShift)) * iStackZ ); + if(extensionProbabilityTrace) { + std::vector pairs{{j1,j2}}; + if(noLpShift) pairs.emplace_back(j1-1,j2-1); + extensionProbabilityTrace->right.add(j1-sj1,j2-sj2,k1-sj1,k2-sj2, + energy.getBoltzmannWeight(energy.getE_interLeft(k1,j1-noLpShift,k2,j2-noLpShift))*iStackZ,std::move(pairs)); + } } } // k2 } // k1 diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h index e046de5b..0b344e4a 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h @@ -6,6 +6,7 @@ #include "IntaRNA/BasePairProbabilities.h" #include "IntaRNA/Matrix.h" #include "IntaRNA/SeedHandlerIdxOffset.h" +#include namespace IntaRNA { @@ -85,6 +86,27 @@ class PredictorMfeEns2dSeedExtension: public PredictorMfeEns { /** Run the selected disjoint stack-seed partition backend. */ void predictStackSeeds(const IndexRange & r1,const IndexRange & r2); + //! Optional signed reverse trace for the legacy anchored extension recurrences. + struct ExtensionProbabilityTrace; + std::unique_ptr extensionProbabilityTrace; + /** Begin a regional trace; no allocation if no probability sink was requested. */ + void initExtensionProbabilities(size_t n,size_t m); + /** Store the current anchor seed's actual pairs, including its endpoints. */ + void beginExtensionProbabilitySeed(size_t i1,size_t i2); + /** Add one accepted left/seed/right objective to the optional reverse trace. + * All coordinates are local, with seed boundaries in increasing DP order. + * @param left whether the left matrix is a factor (otherwise use initiation) + * @param right whether the right matrix is a factor + */ + void addExtensionProbabilityRoot(size_t i1,size_t j1,size_t i2,size_t j2, + Z_type partZ,bool left,bool right); + /** Reverse both extension traces and accumulate actual pair numerators. */ + void finishExtensionProbabilitySeed(); + /** Validate and atomically commit this region in original sequence order. */ + void commitExtensionProbabilities(); + /** Actual seed pairs strictly before the given local target coordinate. */ + std::vector extensionSeedPrefix(size_t i1,size_t i2,size_t before) const; + //! optional sequence-pair owner, never deleted or finalized by the predictor BasePairProbabilities * pairProbabilities; diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index d5a0f30e..623235d3 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -958,7 +958,7 @@ CommandLineParsing::CommandLineParsing( const Personality personality ) "\n 'tPu:' (target) unpaired probability values (RNAplfold format OR '.agz' file ending for IntaRNA's binary format)." "\n 'pMinE:' (target+query) for each index pair the minimal energy of any interaction covering the pair (CSV format)" "\n 'bpsvg:' (target+query) SVG actual-pair probability plot with accessibility frame and seed annotations; same requirements as bpProb" - "\n 'bpProb:' (target+query) actual-pair conditional probabilities for --model=P --mode=M with --noSeed or stack-only seeds (CSV matrix, original strand order; NA for an empty ensemble)" + "\n 'bpProb:' (target+query) actual-pair conditional probabilities for --model=P --mode=M/H (M: --noSeed or stack-only seeds; H: approximate retained ensemble) (CSV matrix, original strand order; NA for an empty ensemble)" "\n 'spotProb:' (target+query) tracks for a given set of interaction spots their probability to be covered by an interaction. If no spots are provided, probabilities for all index combinations are computed. Spots are encoded by comma-separated 'idxT&idxQ' pairs (target-query). For each spot a probability is provided in concert with the probability that none of the spots (encoded by '0&0') is covered (CSV format). The spot encoding is followed colon-separated by the output stream/file name, eg. '--out=\"spotProb:3&76,59&2:STDERR\"'. NOTE: value has to be quoted due to '&' symbol!" "\nFor each, provide a file name or STDOUT/STDERR to write to the respective output stream." ).c_str()) @@ -1246,8 +1246,8 @@ parse(int argc, char** argv) outSep = unescaped_string::getUnescaped( outSep ); if (hasBasePairProbabilityOutput()) { - if (model.val!='P' || mode.val!='M' || !rri.empty() || windowWidth.val!=0) - throw error("bpProb/bpsvg requires --model=P --mode=M with --noSeed or stack-only seeds and no windows"); + if (model.val!='P' || (mode.val!='M' && mode.val!='H') || !rri.empty() || windowWidth.val!=0) + throw error("bpProb/bpsvg requires --model=P --mode=M/H with no windows (M: --noSeed or stack-only seeds)"); outNeedsZall=true; } // K needs a seed even before the usual --noSeed model normalization. @@ -2498,7 +2498,7 @@ BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const I if (!hasBasePairProbabilityOutput()) return nullptr; if (!noSeedRequired) { std::unique_ptr handler(getSeedHandler(energy)); - if (!handler->guaranteesStackOnlySeeds()) + if (mode.val=='M' && !handler->guaranteesStackOnlySeeds()) throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); } return new BasePairProbabilities(energy.size1(),energy.size2(),!noSeedRequired && !outPrefix2streamName.at(OP_bpsvg).empty()); @@ -2520,11 +2520,11 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair { std::unique_ptr pairSeeds; if (pairProbabilities) { - if(model.val!='P' || mode.val!='M' || !rri.empty() || windowWidth.val!=0) - throw std::invalid_argument("bpProb/bpsvg requires --model=P --mode=M with --noSeed or stack-only seeds and no windows"); + if(model.val!='P' || (mode.val!='M' && mode.val!='H') || !rri.empty() || windowWidth.val!=0) + throw std::invalid_argument("bpProb/bpsvg requires --model=P --mode=M/H with no windows (M: --noSeed or stack-only seeds)"); if (!noSeedRequired) { pairSeeds.reset(getSeedHandler(energy)); - if(!pairSeeds->guaranteesStackOnlySeeds()) + if(mode.val=='M' && !pairSeeds->guaranteesStackOnlySeeds()) throw std::invalid_argument("bpProb/bpsvg requires a stack-only seed handler; bulged seeds are unsupported"); } } @@ -2627,7 +2627,7 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair // single-site mfe ensemble interactions (contain only interior loops) case 'P' : { switch ( mode.val ) { - case 'H' : return new PredictorMfeEns2dHeuristic( energy, output, predTracker ); + case 'H' : return new PredictorMfeEns2dHeuristic( energy, output, predTracker, pairProbabilities ); case 'M' : return new PredictorMfeEns2d( energy, output, predTracker, pairProbabilities ); default : INTARNA_NOT_IMPLEMENTED("mode "+toString(mode.val)+" not available for model "+toString(model.val)); } @@ -2672,7 +2672,7 @@ getPredictor( const InteractionEnergy & energy, OutputHandler & output, BasePair // single-site max-prob interactions (contain only interior loops) case 'P' : { switch ( mode.val ) { - case 'H' : return new PredictorMfeEns2dHeuristicSeedExtension( energy, output, predTracker, getSeedHandler( energy ) ); + case 'H' : return new PredictorMfeEns2dHeuristicSeedExtension( energy, output, predTracker, pairSeeds ? pairSeeds.release() : getSeedHandler(energy), pairProbabilities ); case 'M' : return new PredictorMfeEns2dSeedExtension( energy, output, predTracker, pairSeeds ? pairSeeds.release() : getSeedHandler( energy ), pairProbabilities ); case 'S' : return new PredictorMfeEnsSeedOnly( energy, output, predTracker, getSeedHandler(energy) ); default : INTARNA_NOT_IMPLEMENTED("mode "+toString(mode.val)+" not available for model "+toString(model.val)); diff --git a/tests/Makefile.am b/tests/Makefile.am index a500da8e..7ff2a868 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -31,6 +31,8 @@ runApiTests_SOURCES = \ StackSeedDomain_test.cpp \ BasePairProbabilities_test.cpp \ PredictorUnseededProbability_test.cpp \ + PredictorHeuristicProbability_test.cpp \ + PredictorHeuristicSeededProbability_test.cpp \ testEasyLoggingSetup.icc \ AccessibilityConstraint_test.cpp \ AccessibilityFromStream_test.cpp \ diff --git a/tests/PredictorHeuristicProbability_test.cpp b/tests/PredictorHeuristicProbability_test.cpp new file mode 100644 index 00000000..96fdf6b8 --- /dev/null +++ b/tests/PredictorHeuristicProbability_test.cpp @@ -0,0 +1,234 @@ +#include "catch.hpp" + +#undef NDEBUG + +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/AccessibilityBasePair.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/InteractionEnergyIdxOffset.h" +#include "IntaRNA/OutputHandlerInteractionList.h" +#include "IntaRNA/PredictorMfeEns2dHeuristic.h" +#include "IntaRNA/ReverseAccessibility.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace IntaRNA; + +namespace { + +using Pair=std::array; +using Chain=std::vector; + +struct ChainCandidate { + Chain chain; + E_type hybrid; +}; + +struct HeuristicReference { + Z_type z=0; + std::map mass; + size_t candidates=0; +}; + +bool adjacent(Pair p,Pair q) { + return p[0]+1==q[0] && p[1]+1==q[1]; +} + +/** Enumerate complete chains first, then filter by the documented selected + * suffix rule. Unlike the implementation, this stores complete pair lists, + * sums integer chain energies and directly counts pairs in admitted chains. + */ +HeuristicReference enumerateHeuristic(const InteractionEnergy & energy, + const OutputConstraint & constraint,size_t n,size_t m) +{ + std::map> chains; + Chain chain; + std::function enumerate=[&](E_type hybrid) { + const Pair left=chain.front(),right=chain.back(); + if (right[0]-left[0]+1>energy.getAccessibility1().getMaxLength() + || right[1]-left[1]+1>energy.getAccessibility2().getMaxLength()) return; + chains[left].push_back({chain,hybrid}); + for(size_t i=right[0]+1;i selected; + for(size_t i=n;i-- >0;) for(size_t j=m;j-- >0;) { + const Pair left{i,j}; + std::vector,const ChainCandidate *>> admitted; + for(const auto & candidate:chains[left]) { + const auto & c=candidate.chain; + if (constraint.noGUend && energy.isGU(c.back()[0],c.back()[1])) continue; + size_t suffix=1; + std::array order{0,0,0}; + if (constraint.noLP) { + if (c.size()<2 || !adjacent(c[0],c[1])) continue; + if (c.size()==2) suffix=c.size(); + else if (selected[c[1]]==Chain(c.begin()+1,c.end())) order={1,0,0}; + else { + if (adjacent(c[1],c[2])) continue; + suffix=2;order={2,c[2][0],c[2][1]}; + } + } else if (c.size()>1) order={1,c[1][0],c[1][1]}; + if (suffixconstraint.maxED || energy.getED2(j,right[1])>constraint.maxED)) continue; + const Z_type weight=energy.getBoltzmannWeight(full); + result.z+=weight;++result.candidates; + for(const Pair pair:candidate.chain) result.mass[pair]+=weight; + } + } + return result; +} + +class FailingHeuristic : public PredictorMfeEns2dHeuristic { +public: + using PredictorMfeEns2dHeuristic::PredictorMfeEns2dHeuristic; + bool reject=false; +protected: + void updateZ(size_t i1,size_t j1,size_t i2,size_t j2,Z_type z,bool hybrid) override { + if (reject) throw std::runtime_error("test probability failure"); + PredictorMfeEns::updateZ(i1,j1,i2,j2,z,hybrid); + } +}; + +class HeuristicTinyInitiation : public InteractionEnergyBasePair { +public: + using InteractionEnergyBasePair::InteractionEnergyBasePair; + E_type getE_init() const override { return Ekcal_2_E(60); } +}; + +} + +TEST_CASE("heuristic pair probabilities count the retained candidate chains", "[BasePairProbabilities][PredictorHeuristicProbability]") { + #include "testEasyLoggingSetup.icc" + for(const auto & sequences:std::vector>{ + {"GGGGG","CCCCC"},{"GGAGG","CCUCC"},{"GUGGC","GCCUU"}}) { + RnaSequence target("target",sequences.first),query("query",sequences.second); + AccessibilityBasePair at(target,4,nullptr),aq(query,4,nullptr);ReverseAccessibility reverse(aq); + InteractionEnergyBasePair energy(at,reverse,2,1); + for(bool noLP:{false,true}) for(bool noGU:{false,true}) for(unsigned regionKind:{0u,1u,2u}) for(E_type maxED:{Accessibility::ED_UPPER_BOUND,E_type(50)}) { + const IndexRange region=regionKind==0?IndexRange(0,4):regionKind==1?IndexRange(1,3):IndexRange(2,RnaSequence::lastPos); + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,noGU,true,true,maxED); + InteractionEnergyIdxOffset shifted(energy,region.from,region.from); + const size_t length=region.to==RnaSequence::lastPos?target.size()-region.from:region.to-region.from+1; + const auto reference=enumerateHeuristic(shifted,constraint,length,length); + OutputHandlerInteractionList enabled(constraint,1),disabled(constraint,1); + BasePairProbabilities result(target.size(),query.size(),true); + PredictorMfeEns2dHeuristic on(energy,enabled,nullptr,&result),off(energy,disabled,nullptr); + on.predict(region,region);off.predict(region,region);result.finalize(); + REQUIRE(result.isApproximate()); + CAPTURE(sequences.first,sequences.second,noLP,noGU,regionKind,maxED); + REQUIRE(on.getZall()==off.getZall()); + REQUIRE(result.getZ()==on.getZall()); + REQUIRE(result.getZ()==Approx(reference.z).epsilon(3e-12)); + Matrix expected(target.size(),query.size(),0); + for(const auto & entry:reference.mass) { + const auto bp=shifted.getBasePair(entry.first[0],entry.first[1]); + expected(bp.first,bp.second)=entry.second; + } + for(size_t i=0;ienergy==(*b)->energy); + REQUIRE((*a)->basePairs==(*b)->basePairs); + } + } + } +} + +TEST_CASE("heuristic pair probabilities handle empty and singleton domains", "[BasePairProbabilities][PredictorHeuristicProbability]") { + #include "testEasyLoggingSetup.icc" + for(const std::string & queryText:{std::string("C"),std::string("A")}) for(bool noLP:{false,true}) { + RnaSequence target("target","G"),query("query",queryText); + AccessibilityDisabled at(target,0,nullptr),aq(query,0,nullptr);ReverseAccessibility reverse(aq); + InteractionEnergyBasePair energy(at,reverse); + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,false,true,false); + OutputHandlerInteractionList output(constraint,1); + BasePairProbabilities result(1,1); + PredictorMfeEns2dHeuristic predictor(energy,output,nullptr,&result); + predictor.predict();result.finalize(); + REQUIRE(result.isApproximate()); + const bool empty=noLP || queryText=="A"; + REQUIRE((result.status()==BasePairProbabilities::Status::empty)==empty); + if (!empty) REQUIRE(result.probabilities()(0,0)==1); + } +} + +TEST_CASE("heuristic pair owner merges disjoint regions and rejects failed results", "[BasePairProbabilities][PredictorHeuristicProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence target("target","GGGGGG"),query("query","CCCCCC"); + AccessibilityDisabled at(target,0,nullptr),aq(query,0,nullptr);ReverseAccessibility reverse(aq); + InteractionEnergyBasePair energy(at,reverse,2,2); + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,true,false,true,false); + OutputHandlerInteractionList output(constraint,1); + BasePairProbabilities result(6,6); + FailingHeuristic predictor(energy,output,nullptr,&result); + predictor.predict({0,2},{0,2});const Z_type first=predictor.getZall(); + predictor.predict({3,5},{3,5});result.finalize(); + REQUIRE(result.getZ()==2*first); + REQUIRE(result.probabilities()(0,0)==0); + BasePairProbabilities failed(6,6);OutputHandlerInteractionList failedOutput(constraint,1); + FailingHeuristic failing(energy,failedOutput,nullptr,&failed); + failing.predict({0,2},{0,2});failing.reject=true; + REQUIRE_THROWS(failing.predict({3,5},{3,5})); + REQUIRE(failed.status()==BasePairProbabilities::Status::failed); + REQUIRE_THROWS(failed.finalize()); + BasePairProbabilities invalid(6,6);OutputHandlerInteractionList invalidOutput(constraint,1); + FailingHeuristic badRange(energy,invalidOutput,nullptr,&invalid); + REQUIRE_THROWS(badRange.predict({8,9},{0,2})); + REQUIRE(invalid.status()==BasePairProbabilities::Status::failed); +} + +TEST_CASE("heuristic probability collection preserves numerical-zero admission", "[BasePairProbabilities][PredictorHeuristicProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence target("target","GGGGG"),query("query","CCCCC"); + AccessibilityDisabled at(target,0,nullptr),aq(query,0,nullptr);ReverseAccessibility reverse(aq); + HeuristicTinyInitiation tiny(at,reverse,2,2,false,1,Ekcal_2_E(-40)); + InteractionEnergyBasePair vanishing(at,reverse,2,2,false,1,Ekcal_2_E(50)); + for(const InteractionEnergy * energy:{static_cast(&tiny),static_cast(&vanishing)}) for(bool noLP:{false,true}) { + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,false,true,false); + OutputHandlerInteractionList baselineOut(constraint,1),probabilityOut(constraint,1); + BasePairProbabilities result(5,5); + PredictorMfeEns2dHeuristic baseline(*energy,baselineOut,nullptr),predictor(*energy,probabilityOut,nullptr,&result); + baseline.predict();predictor.predict();result.finalize(); + REQUIRE(result.getZ()==baseline.getZall()); + REQUIRE(predictor.getZall()==baseline.getZall()); + if (energy==&vanishing) REQUIRE(result.status()==BasePairProbabilities::Status::empty); + } + OutputConstraint constraint(0,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,false,false,true,false); + OutputHandlerInteractionList output(constraint,1); + BasePairProbabilities failed(5,5); + InteractionEnergyBasePair underflow(at,reverse,2,2,false,0.01,Ekcal_2_E(-1),3,Ekcal_2_E(1000)); + PredictorMfeEns2dHeuristic predictor(underflow,output,nullptr,&failed); + REQUIRE_THROWS(predictor.predict()); + REQUIRE(failed.status()==BasePairProbabilities::Status::failed); +} diff --git a/tests/PredictorHeuristicSeededProbability_test.cpp b/tests/PredictorHeuristicSeededProbability_test.cpp new file mode 100644 index 00000000..6a15c116 --- /dev/null +++ b/tests/PredictorHeuristicSeededProbability_test.cpp @@ -0,0 +1,201 @@ +#include "catch.hpp" +#include "IntaRNA/AccessibilityDisabled.h" +#include "IntaRNA/InteractionEnergyBasePair.h" +#include "IntaRNA/OutputHandlerInteractionList.h" +#include "IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h" +#include "IntaRNA/SeedHandlerExplicit.h" +#include "IntaRNA/SeedHandlerMfe.h" +#include +#include +#include +#include + +using namespace IntaRNA; + +namespace { +// Introduce an independent fugacity x at one actual pair: every loop pays for +// its left pair, and the complete boundary pays for the last pair. Every +// physical chain is multilinear in x, including signed overlap corrections. +// Consequently Z(x)=Z_without+x*M_pair and a finite difference gives M exactly, +// without consulting the reverse tape or its pair-ownership bookkeeping. +class PairFugacityEnergy: public InteractionEnergyBasePair { +public: + using InteractionEnergyBasePair::InteractionEnergyBasePair; + size_t marked1=RnaSequence::lastPos,marked2=RnaSequence::lastPos; + E_type penalty=0; + E_type getE_interLeft(size_t i1,size_t j1,size_t i2,size_t j2) const override { + const E_type e=InteractionEnergyBasePair::getE_interLeft(i1,j1,i2,j2); + return E_isINF(e)?e:e+((i1==marked1 && i2==marked2)?penalty:0); + } + E_type getE(size_t i1,size_t j1,size_t i2,size_t j2,E_type hybrid) const override { + const E_type e=InteractionEnergyBasePair::getE(i1,j1,i2,j2,hybrid); + return E_isINF(e)?e:e+((j1==marked1 && j2==marked2)?penalty:0); + } +}; + +class FixedChoiceSeededHeuristic: public PredictorMfeEns2dHeuristicSeedExtension { +public: + using PredictorMfeEns2dHeuristicSeedExtension::PredictorMfeEns2dHeuristicSeedExtension; + using Key=std::array; + struct Choice { size_t j1,j2; Z_type e; }; + using Choices=std::map; + Choices choices; + const Choices * fixed=nullptr; +protected: + void fillHybridZ_right(size_t sj1,size_t sj2,size_t si1,size_t si2) override { + PredictorMfeEns2dHeuristicSeedExtension::fillHybridZ_right(sj1,sj2,si1,si2); + // Condition on the original heuristic's chosen domain; a fugacity is a + // counting variable and must not change the domain being differentiated. + if(fixed) { + const auto & c=fixed->at({si1,si2}); j1opt=c.j1; j2opt=c.j2; E_right_opt=c.e; + } else choices[{si1,si2}]={j1opt,j2opt,E_right_opt}; + } +}; + +class CorruptSeededHeuristicPartition: public PredictorMfeEns2dHeuristicSeedExtension { +public: + using PredictorMfeEns2dHeuristicSeedExtension::PredictorMfeEns2dHeuristicSeedExtension; + Z_type injected=0; +protected: + void updateZ(size_t i1,size_t j1,size_t i2,size_t j2,Z_type partZ,bool isHybridZ) override; +}; + +void CorruptSeededHeuristicPartition::updateZ(size_t i1,size_t j1,size_t i2,size_t j2, + Z_type partZ,bool isHybridZ) +{ + PredictorMfeEns2dHeuristicSeedExtension::updateZ(i1,j1,i2,j2,partZ,isHybridZ); + Zall=injected; +} + +SeedConstraint explicitSeeds(const std::string & patterns,size_t nominal=2) { + return SeedConstraint(nominal,6,3,3,E_INF,E_INF,E_INF,IndexRangeList(),IndexRangeList(),patterns,true,true,true); +} +OutputConstraint constraints(bool noLP,bool noGU=false) { + return OutputConstraint(0,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF,false,noLP,noGU,true,false); +} +} + +TEST_CASE("seeded heuristic outside agrees with independent pair fugacities", "[BasePairProbabilities][HeuristicSeededProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGGGGG"),q("q","CCCCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr); ReverseAccessibility ar(aq); + PairFugacityEnergy energy(at,ar,2,2); + // Isolated and competing seeds exercise left/right/both objectives, + // overlapping and nonoverlapping subtraction, and bulged actual pairs. + for(const auto & patterns:{"3||&3||","2||&4||,3||&3||","1||&5||,4||&2||", + "2|.|&3||","1|.||&3|||,3||&3||","2|||&3|||,4||&2||"}) { + const auto sc=explicitSeeds(patterns); + for(bool noLP:{false,true}) { + CAPTURE(patterns,noLP); + const auto oc=constraints(noLP); + energy.penalty=0; + BasePairProbabilities result(t.size(),q.size(),true); + OutputHandlerInteractionList output(oc,0); + FixedChoiceSeededHeuristic predictor(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),&result); + predictor.predict(); result.finalize(); + REQUIRE(result.isApproximate()); + REQUIRE(result.getZ()==Approx(predictor.getZall()).epsilon(2e-12)); + const Z_type baseline=result.getZ(); + for(size_t i=0;i0); + predictor.predict({3,RnaSequence::lastPos},{3,RnaSequence::lastPos});result.finalize(); + REQUIRE(result.getZ()==Approx(2*first).epsilon(2e-12)); + REQUIRE(result.seedPairs()(0,5)==1);REQUIRE(result.seedPairs()(1,4)==1); + REQUIRE(result.seedPairs()(3,2)==1);REQUIRE(result.seedPairs()(4,1)==1); + REQUIRE(result.probabilities()(0,0)==0); + for(const std::string pattern:{"2||&4||","1|||&4|||"}) { + const auto shortSc=explicitSeeds(pattern); + BasePairProbabilities empty(6,6,true);OutputHandlerInteractionList emptyOutput(oc,0); + FixedChoiceSeededHeuristic p(energy,emptyOutput,nullptr,new SeedHandlerExplicit(energy,shortSc),&empty); + p.predict({0,1},{0,1});empty.finalize(); + REQUIRE(empty.status()==BasePairProbabilities::Status::empty); + } + const auto singleton=explicitSeeds("2|&4|",2); + BasePairProbabilities failed(6,6);OutputHandlerInteractionList failedOutput(oc,0); + FixedChoiceSeededHeuristic bad(energy,failedOutput,nullptr,new SeedHandlerExplicit(energy,singleton),&failed); + REQUIRE_THROWS(bad.predict());REQUIRE(failed.status()==BasePairProbabilities::Status::failed); +} + +TEST_CASE("computed bulged seeded heuristic marginals condition on the selected seed family", "[BasePairProbabilities][HeuristicSeededProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGAGGCG"),q("q","CCGCACC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + PairFugacityEnergy energy(at,ar,2,2); + const SeedConstraint sc(3,2,1,1,E_INF,E_INF,E_INF,IndexRangeList(),IndexRangeList(),"",true,true,true); + // Freeze the concrete MFE seed selected at every start by encoding its + // actual pairs explicitly. Perturbations then cannot select another seed. + SeedHandlerMfe selected(energy,sc);selected.fillSeed(0,t.size()-1,0,q.size()-1); + std::string patterns; + size_t i=RnaSequence::lastPos,j=RnaSequence::lastPos; + while(selected.updateToNextSeed(i,j,0,t.size()-1,0,q.size()-1)) { + const size_t sl1=selected.getSeedLength1(i,j),sl2=selected.getSeedLength2(i,j); + Interaction seed(t,q);seed.basePairs.push_back(energy.getBasePair(i,j)); + selected.traceBackSeed(seed,i,j);seed.basePairs.push_back(energy.getBasePair(i+sl1-1,j+sl2-1)); + const size_t qStart=q.size()-j-sl2; + std::string ts(sl1,'.'),qs(sl2,'.'); + for(const auto & bp:seed.basePairs) { ts[bp.first-i]='|';qs[bp.second-qStart]='|'; } + if(!patterns.empty()) patterns+=','; + patterns+=std::to_string(i+1)+ts+'&'+std::to_string(qStart+1)+qs; + } + REQUIRE_FALSE(patterns.empty()); + const auto fixedSeeds=explicitSeeds(patterns,3); + for(bool noLP:{false,true}) { + const auto oc=constraints(noLP);energy.penalty=0; + BasePairProbabilities result(t.size(),q.size());OutputHandlerInteractionList output(oc,0); + FixedChoiceSeededHeuristic predictor(energy,output,nullptr,new SeedHandlerMfe(energy,sc),&result); + predictor.predict();result.finalize(); + for(size_t a=0;a::infinity(),std::numeric_limits::quiet_NaN()}) { + CAPTURE(injected); + BasePairProbabilities result(t.size(),q.size());OutputHandlerInteractionList output(oc,0); + CorruptSeededHeuristicPartition predictor(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),&result); + predictor.injected=injected; + REQUIRE_THROWS_AS(predictor.predict(),std::range_error); + REQUIRE(result.status()==BasePairProbabilities::Status::failed); + REQUIRE(result.getZ()==0); + REQUIRE_THROWS(result.finalize()); + } +} diff --git a/tests/PredictorUnseededProbability_test.cpp b/tests/PredictorUnseededProbability_test.cpp index 7124d6e5..c45858e4 100644 --- a/tests/PredictorUnseededProbability_test.cpp +++ b/tests/PredictorUnseededProbability_test.cpp @@ -214,4 +214,10 @@ TEST_CASE("unseeded probability collection follows legacy numerical gates and fa PredictorMfeEns2d huge(extreme,output,nullptr,&overflow); REQUIRE_THROWS(huge.predict()); REQUIRE(overflow.status()==BasePairProbabilities::Status::failed); + BasePairProbabilities underflow(5,5); + InteractionEnergyBasePair underflowEnergy(targetAcc,reverse,2,2,false,1, + Ekcal_2_E(-1),3,Ekcal_2_E(1000)); + PredictorMfeEns2d disappearing(underflowEnergy,output,nullptr,&underflow); + REQUIRE_THROWS(disappearing.predict()); + REQUIRE(underflow.status()==BasePairProbabilities::Status::failed); } diff --git a/tests/testBasePairProbabilities.py b/tests/testBasePairProbabilities.py index 09572802..bafed239 100644 --- a/tests/testBasePairProbabilities.py +++ b/tests/testBasePairProbabilities.py @@ -27,7 +27,7 @@ def run(t='GGAGGG', q='CCCC', options=(), success=True): def matrix(path): with (gzip.open(path, 'rt') if str(path).endswith('.gz') else open(path)) as f: rows = list(csv.reader(f, delimiter=';')) - assert rows[0][0] == 'bpProb', rows + assert rows[0][0] in ('bpProb', 'bpProbApproximate'), rows return rows[0][1:], [r[0] for r in rows[1:]], [[None if v=='NA' else float(v) for v in r[1:]] for r in rows[1:]] def oracle(t, q, regions=None, no_lp=False, seeds=None, unseeded=False): @@ -116,8 +116,16 @@ def predict(t='GGAGGG',q='CCCC',extra=()): covered=list(csv.reader(spot.open(),delimiter=';')) assert any(float(v)>0 for v in covered[3][1:]);assert all(v==0 for v in with_spot[2]) compressed=d/'pairs.csv.gz';run(options=[f'--out=bpProb:{compressed}']);same(matrix(compressed)[2],values) + # Heuristic outputs retain their own denominator and declare approximation. + for seed_opts in ([], ['--noSeed'], ['--seedMaxUP=1']): + for no_lp in ('false', 'true'): + opts=['--mode=H', f'--outNoLP={no_lp}', *seed_opts] + predicted, probs=predict(extra=opts) + assert predicted.stdout==run(options=opts).stdout + assert path.read_text().startswith('bpProbApproximate;') + assert all(v is None or 0<=v<=1 for row in probs for v in row) # Unsupported requests fail explicitly and never create a probability file. - for opts in (['--mode=H'],['--mode=S'],['--model=X','--mode=K'],['--windowWidth=3','--windowOverlap=2'],['--seedMaxUP=1'],['--seedTQ=1|.|&2||']): + for opts in (['--mode=S'],['--model=X','--mode=K'],['--windowWidth=3','--windowOverlap=2'],['--seedMaxUP=1'],['--seedTQ=1|.|&2||']): fail=d/'unsupported.csv';run(options=[f'--out=bpProb:{fail}',*opts],success=False);assert not fail.exists() fail=d/'range.csv';run('GGGG','CCCC',[f'--out=bpProb:{fail}',*seedopt,'--energyAdd=900'],success=False);assert not fail.exists() run(options=['--out=bpProb:/dev/full'],success=False) diff --git a/tests/testBasePairProbabilitySvg.py b/tests/testBasePairProbabilitySvg.py index 0cbb64b6..1e7fab9d 100644 --- a/tests/testBasePairProbabilitySvg.py +++ b/tests/testBasePairProbabilitySvg.py @@ -127,6 +127,15 @@ def plot(t='GGAGGG', q='CCCC', opts=()): rows = list(csv.reader(table.open(), delimiter=';')) assert all(float(pairs(unseeded)[i,j].attrib['data-probability']) == float(rows[i][j]) for i in range(1,7) for j in range(1,5)) + for seed_opts in ([], ['--noSeed'], ['--seedMaxUP=1']): + approximate=plot(opts=['--mode=H', f'--out=bpProb:{table}', *seed_opts]) + assert 'Approximate' in approximate.find('s:title', NS).text + rows=list(csv.reader(table.open(), delimiter=';')) + assert rows[0][0]=='bpProbApproximate' + assert all(float(pairs(approximate)[i,j].attrib['data-probability']) == float(rows[i][j]) + for i in range(1,7) for j in range(1,5)) + if '--noSeed' in seed_opts: + assert not seeds(approximate) # Explicit singleton/mixed seed masks exclude extension-only cells. opts = ['--seedTQ=1|&4|,2||&2||'] mixed = plot('GGGG', 'CCCC', opts) @@ -219,7 +228,7 @@ def check_frame(root, target_pu, query_pu): run(options=[f'--out=bpsvg:{compressed}']) assert len(pairs(load(compressed))) == 24 # Validation/numerical failures must not publish a partial document. - for opts in (['--mode=H'], ['--seedMaxUP=1'], ['--model=X', '--mode=K'], + for opts in (['--mode=S'], ['--seedMaxUP=1'], ['--model=X', '--mode=K'], ['--windowWidth=3'], ['--seedTQ=1|&4|','--energyAdd=900']): failure = d/'failure.svg' run(options=[f'--out=bpsvg:{failure}', *opts], success=False) From 5fc10e99ba46af6ff95720753acbb2fe94c57272 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 9 Oct 2026 22:34:20 +0200 Subject: [PATCH 15/30] docs: record revision audits and probability performance --- ChangeLog | 8 +++ .../seeded-probabilities-validation.md | 65 +++++++++++++++++++ ...redictorMfeEns2dHeuristicSeedExtension.svg | 4 +- 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index eaea2fe1..e1911f1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -65,6 +65,9 @@ ## Technical changes and Optimizations +- document independent release/debug, installed-API and performance validation + for the reviewed unseeded and heuristic probability outputs (PR #259) + - transfer completed probability-output buffers without copying; independently audit performance, accessibility conversion and recurrence depictions (PR #259) @@ -181,6 +184,11 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261009 Alexander Mitrofanov + * doc/analysis/seeded-probabilities-validation.md, doc/recursions : + * record final review validation and controlled runtime/memory measurements; + update the seeded-heuristic depiction for approximate pair output (PR #259) + 261009 Alexander Mitrofanov * PredictorMfeEns2dHeuristic, PredictorMfeEns2dHeuristicSeedExtension, PredictorMfeEns2dSeedExtension, BasePairProbabilities, probability writers : diff --git a/doc/analysis/seeded-probabilities-validation.md b/doc/analysis/seeded-probabilities-validation.md index 56c83716..b3c44bd6 100644 --- a/doc/analysis/seeded-probabilities-validation.md +++ b/doc/analysis/seeded-probabilities-validation.md @@ -318,3 +318,68 @@ and implementation. All revised text is UTF-8/LF and `git diff --check` passes. Both new recurrence SVGs parse, render and passed independent equation review; representative output SVGs were rendered and visually inspected. Apple Clang was not available locally. + +### Controlled revision performance + +A separate agent compared the retained `202651e` executable with the final +implementation at `ae0eb78`, using the same GCC 14.4 release configuration, +ViennaRNA 2.7.2, Boost 1.85 and Kokkos mdspan on the machine described above. +Each configuration has one correctness/warmup run and seven deterministically +shuffled, interleaved fresh-process measurements, pinned to CPU 0 and one thread. +No build or test ran during timing. Both ensemble and probability outputs went to +`/dev/null` for timing; separate file-output checks compared complete ensemble +bytes and every CSV/SVG probability cell. + +Wall seconds (median [minimum, maximum], including process startup): + +| Input / predictor | Before, output off | Revised, output off | Revised CSV | Revised SVG | +| --- | ---: | ---: | ---: | ---: | +| OxyS-fhlA / noSeed-M | 0.4116 [0.4077, 0.4262] | 0.4165 [0.4105, 0.4220] | 0.7309 [0.7236, 0.7500] | 0.8490 [0.8415, 0.8668] | +| OxyS-fhlA / noSeed-H | 0.0644 [0.0641, 0.0661] | 0.0646 [0.0637, 0.0668] | 0.0728 [0.0720, 0.0767] | 0.0971 [0.0962, 0.1041] | +| OxyS-fhlA / seeded-H | 0.0517 [0.0510, 0.0532] | 0.0517 [0.0513, 0.0557] | 0.0566 [0.0562, 0.0569] | 0.0772 [0.0771, 0.0784] | +| dense40 / noSeed-M | 0.3409 [0.3396, 0.3412] | 0.3489 [0.3455, 0.3559] | 0.6691 [0.6619, 0.6936] | 0.7231 [0.7143, 0.7377] | +| dense40 / noSeed-H | 0.0195 [0.0194, 0.0207] | 0.0195 [0.0191, 0.0197] | 0.0227 [0.0223, 0.0234] | 0.0264 [0.0261, 0.0271] | +| dense40 / seeded-H | 1.8790 [1.8324, 1.8980] | 1.8767 [1.8584, 1.8861] | 2.7876 [2.7391, 2.8202] | 2.8354 [2.7850, 2.8720] | +| dense32-noLP / noSeed-M | 0.1001 [0.0991, 0.1021] | 0.1014 [0.1000, 0.1042] | 0.1873 [0.1861, 0.1903] | 0.2041 [0.2023, 0.2078] | +| dense32-noLP / noSeed-H | 0.0159 [0.0158, 0.0169] | 0.0157 [0.0155, 0.0159] | 0.0174 [0.0172, 0.0186] | 0.0198 [0.0195, 0.0204] | +| dense32-noLP / seeded-H | 0.5690 [0.5631, 0.5791] | 0.5692 [0.5652, 0.5783] | 0.8398 [0.8290, 0.9217] | 0.8561 [0.8473, 0.8731] | + +Maximum observed process RSS (KiB): + +| Input / predictor | Before, output off | Revised, output off | Revised CSV | Revised SVG | +| --- | ---: | ---: | ---: | ---: | +| OxyS-fhlA / noSeed-M | 17,180 | 17,308 | 17,312 | 20,844 | +| OxyS-fhlA / noSeed-H | 18,208 | 18,204 | 18,844 | 22,328 | +| OxyS-fhlA / seeded-H | 17,184 | 17,180 | 17,184 | 20,848 | +| dense40 / noSeed-M | 16,388 | 16,392 | 16,256 | 16,292 | +| dense40 / noSeed-H | 16,736 | 16,608 | 16,720 | 16,724 | +| dense40 / seeded-H | 34,740 | 34,744 | 35,564 | 35,696 | +| dense32-noLP / noSeed-M | 16,376 | 16,380 | 16,256 | 16,152 | +| dense32-noLP / noSeed-H | 16,564 | 16,564 | 16,608 | 16,604 | +| dense32-noLP / seeded-H | 21,508 | 21,504 | 22,324 | 22,592 | + +Existing output-off medians vary from −1.2% to +2.4% against the pre-revision +binary across these nine cases. All ensemble outputs are byte-identical across +both binaries and all output configurations; every SVG pair value matches CSV. +Probability collection has a measured additional cost: CSV is 1.09–1.92 times +revised output-off wall time here, and SVG is 1.26–2.07 times. These are local +workload measurements, not a universal overhead bound or speedup claim. + +Reproduce one process from each corresponding build (change mode to H for the +unseeded heuristic; remove `--noSeed` for seeded H): + +```sh +OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 OMP_DYNAMIC=FALSE \ + /usr/bin/time -f '%e %U %S %M' taskset -c 0 src/bin/IntaRNA \ + --target=doc/handson/fhlA.fasta --query=doc/handson/OxyS.fasta \ + --model=P --mode=M --noSeed --seedBP=4 --intLenMax=30 \ + --qIntLoopMax=3 --tIntLoopMax=3 --threads=1 \ + --outNoLP=false --outNoGUend=false --outNumber=0 --outMode=E \ + --default-log-file=/dev/null --out=STDOUT > /dev/null +# Add --out=bpProb:/dev/null or --out=bpsvg:/dev/null. +``` + +For dense40, replace target/query with G×40/C×40 and add `--tAcc=N --qAcc=N`. +For dense32-noLP, use G×32/C×32 with disabled accessibility and +`--outNoLP=true`. Keep all remaining flags identical. Repeat seven times with +interleaved configurations; capture process RSS with GNU time `%M`. diff --git a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg index 6f7c7fa8..2d38e87d 100644 --- a/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg +++ b/doc/recursions/IntaRNA.PredictorMfeEns2dHeuristicSeedExtension.svg @@ -1,6 +1,6 @@ Seeded ensemble · one selected right extension -PredictorMfeEns2dHeuristicSeedExtension | --model=P --mode=H. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. +PredictorMfeEns2dHeuristicSeedExtension | --model=P --mode=H. Internal recurrence documentation for steps 9 and 11 of PR 258 / PR 259. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. @@ -42,7 +42,7 @@ All true right-only extensions + all corrected left-only extensions + left extensions paired with just j*. The second phase runs if noGUend is off, or a valid right optimum exists, or anchor end b is not GU. Initialize j*=b and E_right_opt=∞. A positive selected R is required before combining both sides. -No full Cartesian product of left and right boundaries is retained, and bpProb / bpsvg are unsupported. +No full left/right Cartesian product; bpProb / bpsvg trace this retained family and are labelled approximate. Every update is filtered and accumulated by PredictorMfeEns::updateZ; ranked reporting is a separate step. Implementation anchors (src/IntaRNA/) · BW(E)=exp(−E/RT) after native energy-unit conversion From c0ef61f1603ffc41c2ae4f8bbcf98b56027880e9 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Mon, 12 Oct 2026 12:41:32 +0200 Subject: [PATCH 16/30] fix: align probability dot plots and trace MFE pair outlines --- ChangeLog | 13 ++ README.md | 15 ++- .../seeded-probabilities-validation.md | 9 +- src/IntaRNA/BasePairProbabilitySvg.cpp | 38 ++++-- src/IntaRNA/BasePairProbabilityWriter.h | 4 +- src/bin/CommandLineParsing.cpp | 124 +++++++++++++++++- tests/testBasePairProbabilitySvg.py | 45 ++++++- 7 files changed, 223 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index e1911f1d..5e0eb3f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,9 @@ ## Interface and handling +- orient probability SVGs from the upper-left corner and highlight every MFE + pair, splitting seed/MFE cell borders into orange and green (PR #259 review) + - enable approximate actual-pair CSV/SVG probabilities for unseeded and seeded heuristic ensembles, labelled explicitly as approximate (PR #259, step 11) @@ -184,6 +187,16 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261012 Alexander Mitrofanov + * BasePairProbabilitySvg, BasePairProbabilityWriter, CommandLineParsing + * restore top-to-bottom target indexing, aligned frames/guides and MFE region + + reconstruct the MFE structure inside the best ensemble site for SVG only + + highlight each MFE pair; shared seed pairs use orange vertical and green + horizontal edges, preserving both annotations (PR #259 review) + * testBasePairProbabilitySvg.py, README.md, seeded-probabilities-validation.md + + verify complete internal MFE pairs, shared borders, signed guides and + output-off parity; document the fixed-boundary traceback and orientation + 261009 Alexander Mitrofanov * doc/analysis/seeded-probabilities-validation.md, doc/recursions : * record final review validation and controlled runtime/memory measurements; diff --git a/README.md b/README.md index f3089346..7a118b25 100644 --- a/README.md +++ b/README.md @@ -2097,8 +2097,8 @@ IntaRNA -t GGAGGG -q CCCC --energy=B --acc=N --seedBP=2 \ --model=P --mode=M --out=bpsvg:pairs.svg --out=bpProb:pairs.csv ``` -The query runs left to right and the target bottom to top, both in original -5' to 3' order, starting at the bottom-left corner. Sequence names label the +The query runs left to right and the target top to bottom, both in original +5' to 3' order, starting at the upper-left corner. Sequence names label the axes, with the query name below the matrix. A subtitle names both sequences when either name differs from the defaults `query` and `target`. Nucleotides appear on all four sides. Their colored backgrounds show **intramolecular @@ -2127,9 +2127,14 @@ Dark orange outlines mark the union of pairs belonging to **admitted seeds** contained in searched regions, including overlapping, singleton and mixed-length explicit seeds. This annotation uses the predictor's retained seed family and span limits; it is independent of ranked interaction reporting and can include -zero-probability cells. Extension-only pairs have no outline. A seed legend is -included whenever an outline is present. A green rectangle encloses the best -reported interaction's site, with a matching legend. The best site is retained +zero-probability cells. A seed legend is included whenever an outline is present. +A green rectangle encloses the best reported ensemble interaction's site, and +green cell borders mark a minimum-energy structure reconstructed within that +site using the seed, loop and noLP constraints. Ensemble predictors ordinarily +report only site boundaries; the SVG requests this additional MFE traceback. +It does not change site ranking or the probability matrix. Where an MFE pair +also belongs to an admitted seed, its left/right edges are orange and its +top/bottom edges are green. Both annotations have legends. The best site is retained for SVG even with `--outNumber=0`, without adding ranked text output. If no site passes the reporting filters, the rectangle and its legend are omitted. diff --git a/doc/analysis/seeded-probabilities-validation.md b/doc/analysis/seeded-probabilities-validation.md index b3c44bd6..924c7048 100644 --- a/doc/analysis/seeded-probabilities-validation.md +++ b/doc/analysis/seeded-probabilities-validation.md @@ -283,10 +283,15 @@ candidate ensemble can retain path multiplicities and is not the exact seeded unique-chain ensemble. Existing numerical-zero cutoffs remain in the legacy unseeded/heuristic objectives. -The SVG now starts both strands at the bottom-left origin, places the query name +The SVG now starts both strands at the upper-left origin, places the query name below the matrix, uses sequence names and rounded values in tooltips, displays intramolecular pairing as `1-Pu`, and outlines the best reported interaction region -in green. Machine-readable probability attributes keep full precision. The MFE +in green. Individual cells mark a minimum-energy structure reconstructed inside +the selected ensemble site. Shared seed/MFE cells have orange vertical edges and +green horizontal edges. This SVG-only fixed-boundary traceback reuses the MFE +recurrences and adds computation proportional to the selected site, without +changing ordinary ensemble output. Machine-readable probability attributes keep +full precision. The MFE outline is available with `--outNumber=0`; ordinary ranked output stays suppressed. An outline is omitted if no interaction passes the reporting filters. diff --git a/src/IntaRNA/BasePairProbabilitySvg.cpp b/src/IntaRNA/BasePairProbabilitySvg.cpp index 5b7620ee..349f2c4f 100644 --- a/src/IntaRNA/BasePairProbabilitySvg.cpp +++ b/src/IntaRNA/BasePairProbabilitySvg.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -77,9 +78,10 @@ BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const I const double x=std::max(130.0,(std::max(820.0,nameWidth)-w)/2); const double y=std::max(120.0,(9.0*t.getId().size()-h)/2+60); const double width=2*x+w, height=2*y+h+185; - // SVG y increases downwards, while target coordinates increase upwards. - auto targetY=[&](size_t i) { return y+cell*(t.size()-1-i); }; + // Both original sequence indices increase from the upper-left corner. + auto targetY=[&](size_t i) { return y+cell*i; }; const bool markMfe=mfe && !mfe->isEmpty(); + std::set mfePairs; size_t tStart=t.size(),tEnd=0,qStart=q.size(),qEnd=0; if (markMfe) { for (const auto & bp:mfe->basePairs) { @@ -87,6 +89,7 @@ BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const I throw std::invalid_argument("bpsvg: MFE interaction coordinates exceed sequence dimensions"); tStart=std::min(tStart,bp.first); tEnd=std::max(tEnd,bp.first); qStart=std::min(qStart,bp.second); qEnd=std::max(qEnd,bp.second); + mfePairs.insert(bp); } } std::ostringstream out; @@ -95,7 +98,7 @@ BasePairProbabilityWriter::svgBlock(const BasePairProbabilities & result,const I out<<"\n" <<""<<(result.isApproximate()?"Approximate base-pair probabilities: ":"Base-pair probabilities: ")<\n" - <<"Target rows run 5' to 3' upwards and query columns run 5' to 3' to the right. Pair probabilities are conditional on an allowed interaction. " + <<"Target rows run 5' to 3' downwards and query columns run 5' to 3' to the right, starting at the upper-left corner. Pair probabilities are conditional on an allowed interaction. " <<"The frame shows single-nucleotide intramolecular pairing probabilities (1 - Pu) from the accessibility model. " <<(result.isApproximate()?"Approximate probabilities describe the retained heuristic ensemble.":"Probabilities describe the exact predictor ensemble.")<<"\n" < @@ -132,7 +135,7 @@ text { font-family: sans-serif; fill: #243247; } text(width/2,53,t.getId()+" / "+q.getId(),"subtitle"); text(x+w/2,y+h+65,q.getId()+" (5' to 3')","axis"); out<<""<\n"; + <<") rotate(90)\" text-anchor=\"middle\">"<\n"; auto rect=[&](double rx,double ry,Z_type value,bool missing,const std::string & attributes,const std::string & title) { out<<"0 && !origin?1:0)); if (idx%10==0 || origin) out<<"\n"; + <<"\" x1=\""<<(target?x-cell:x+offset)<<"\" y1=\""<<(target?y+offset:y-cell) + <<"\" x2=\""<<(target?x+w+cell:x+offset)<<"\" y2=\""<<(target?y+offset:y+h+cell)<<"\"/>\n"; const std::string label=idx==1?"+1":std::to_string(idx); if(target) { text(x-26,targetY(i)+cell/2+4,label,"index","end"); @@ -200,17 +203,35 @@ text { font-family: sans-serif; fill: #243247; } out<<"\n"; for(size_t i=0;i\n" + <<"\n" + <<"\n\n"; + continue; + } out<<"\n"; } out<<"\n"; } if (markMfe) { + out<<"\n"; + for (const auto & bp:mfePairs) { + if (result.collectsSeedPairs() && result.seedPairs()(bp.first,bp.second)) continue; + out<<"\n"; + } + out<<"\n"; out<<"\n"; } const double legendX=(width-7*86)/2, legendY=y+h+110; @@ -231,7 +252,8 @@ text { font-family: sans-serif; fill: #243247; } if (markMfe) { const double mfeLegendY=legendY+(annotated?76:48); out<<"\n"; - text(legendX+28,mfeLegendY+13,"Green outline: minimum-free-energy interaction region","legend","start"); + text(legendX+28,mfeLegendY+13,"Green outline: minimum-free-energy interaction region and base pairs","legend","start"); + if (annotated) text(legendX+28,mfeLegendY+31,"Seed + MFE pair: orange left/right edges, green top/bottom edges","legend","start"); } if(empty) text(width/2,legendY+90,"Gray / NA: empty interaction ensemble; accessibility remains defined","legend"); out<<"\n"; diff --git a/src/IntaRNA/BasePairProbabilityWriter.h b/src/IntaRNA/BasePairProbabilityWriter.h index 64773536..31ec316c 100644 --- a/src/IntaRNA/BasePairProbabilityWriter.h +++ b/src/IntaRNA/BasePairProbabilityWriter.h @@ -41,7 +41,7 @@ class BasePairProbabilityWriter { * @param targetRT positive finite energy scale used to convert target ED to Pu * @param queryRT positive finite energy scale used to convert query ED to Pu; * accessibility scales can differ from the interaction model's RT - * @param mfe optional best interaction in original coordinates for a region outline + * @param mfe optional traced MFE structure in original coordinates for region/pair outlines */ static void writeSvg(std::ostream & out,const BasePairProbabilities & result, const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT,const Interaction * mfe = nullptr); @@ -52,7 +52,7 @@ class BasePairProbabilityWriter { * @param energy original, unshifted energy/accessibility model * @param targetRT positive finite target accessibility energy scale * @param queryRT positive finite query accessibility energy scale - * @param mfe optional best interaction in original coordinates for a region outline + * @param mfe optional traced MFE structure in original coordinates for region/pair outlines */ static void writeSvgFile(const std::string & filename,const BasePairProbabilities & result, const InteractionEnergy & energy,Z_type targetRT,Z_type queryRT,const Interaction * mfe = nullptr); diff --git a/src/bin/CommandLineParsing.cpp b/src/bin/CommandLineParsing.cpp index 623235d3..6650cde4 100644 --- a/src/bin/CommandLineParsing.cpp +++ b/src/bin/CommandLineParsing.cpp @@ -38,6 +38,7 @@ extern "C" { #include "IntaRNA/InteractionEnergyBasePair.h" #include "IntaRNA/InteractionEnergyVrna.h" +#include "IntaRNA/OutputHandlerInteractionList.h" #include "IntaRNA/PredictorMfe2dHeuristic.h" #include "IntaRNA/PredictorMfe2dHelixBlockHeuristic.h" @@ -2503,15 +2504,134 @@ BasePairProbabilities * CommandLineParsing::getBasePairProbabilityResult(const I } return new BasePairProbabilities(energy.size1(),energy.size2(),!noSeedRequired && !outPrefix2streamName.at(OP_bpsvg).empty()); } +namespace { + +// Ensemble output stores boundaries, not a molecular structure. Reuse the +// existing fixed-boundary MFE recurrences solely for the SVG's pair overlay. +class SvgMfeTrace : public PredictorMfe2d { +public: + using PredictorMfe2d::PredictorMfe2d; + void trace(Interaction & interaction); +}; + +void +SvgMfeTrace::trace(Interaction & interaction) +{ + interaction.sort(); + const auto first=interaction.basePairs.front(), last=interaction.basePairs.back(); + energy.setOffset1(first.first); + energy.setOffset2(energy.getAccessibility2().getReversedIndex(first.second)); + hybridE_pq.resize(last.first-first.first+1,first.second-last.second+1); + traceBack(interaction); +} + +class SvgSeededMfeTrace : public PredictorMfe2dSeed { +public: + using PredictorMfe2dSeed::PredictorMfe2dSeed; + bool trace(Interaction & interaction); +}; + +bool +SvgSeededMfeTrace::trace(Interaction & interaction) +{ + interaction.sort(); + const auto first=interaction.basePairs.front(), last=interaction.basePairs.back(); + const size_t offset2=energy.getAccessibility2().getReversedIndex(first.second); + energy.setOffset1(first.first); energy.setOffset2(offset2); + seedHandler.setOffset1(first.first); seedHandler.setOffset2(offset2); + const size_t n=last.first-first.first+1, m=first.second-last.second+1; + hybridE_pq.resize(n,m); hybridE_pq_seed.resize(n,m); + if (seedHandler.fillSeed(0,n-1,0,m-1)==0) return false; + fillHybridE(n-1,m-1,0,0,false); + if (E_isINF(hybridE_pq_seed(0,0))) return false; + interaction.energy=energy.getE(0,n-1,0,m-1,hybridE_pq_seed(0,0)); + traceBack(interaction); + return true; +} + +// Explicit seeds define their own lengths, including singletons. The legacy +// MFE traceback's nominal length check must not reject shorter explicit seeds. +class SvgSeedConstraint : public SeedConstraint { +public: + explicit SvgSeedConstraint(const SeedConstraint & original); +}; + +inline +SvgSeedConstraint::SvgSeedConstraint(const SeedConstraint & original) : SeedConstraint(original) +{ + if (!getExplicitSeeds().empty()) bp=2; +} + +} // namespace + void CommandLineParsing::writeBasePairProbabilities(const BasePairProbabilities & result,const InteractionEnergy & energy, const Interaction * mfe) const { const auto & t=energy.getAccessibility1().getSequence(); const auto & q=energy.getAccessibility2().getAccessibilityOrigin().getSequence(); if (!outPrefix2streamName.at(OP_bpProb).empty()) BasePairProbabilityWriter::writeFile(getFullFilename(outPrefix2streamName.at(OP_bpProb),&t,&q),result,t,q,outSep); - if (!outPrefix2streamName.at(OP_bpsvg).empty()) + if (!outPrefix2streamName.at(OP_bpsvg).empty()) { + std::unique_ptr structure; + if (mfe && !mfe->isEmpty()) { + structure=std::make_unique(*mfe); + const auto original=getOutputConstraint(energy,true); + OutputConstraint constraint(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF, + original.bestSeedOnly,original.noLP,original.noGUend,false,true,original.maxED); + OutputHandlerInteractionList unused(constraint,1); + if (noSeedRequired) { + SvgMfeTrace trace(energy,unused,nullptr); + trace.trace(*structure); + } else if (structure->basePairs.size()>1) { + SvgSeedConstraint seedConstraint(getSeedConstraint(energy)); + if (seedConstraint.getExplicitSeeds().empty()) { + SvgSeededMfeTrace trace(energy,unused,nullptr,getSeedHandler(energy)); + if (!trace.trace(*structure)) throw std::runtime_error("bpsvg: no MFE structure in selected seeded site"); + } else { + // Trace each explicit seed separately so mixed lengths and + // alternatives at the same start remain available in this site. + std::vector variants; + const auto first=structure->basePairs.front(), last=structure->basePairs.back(); + const size_t lo2=energy.getAccessibility2().getReversedIndex(first.second); + const size_t hi2=energy.getAccessibility2().getReversedIndex(last.second); + std::istringstream encodings(seedConstraint.getExplicitSeeds()); + for (std::string encoding; std::getline(encodings,encoding,','); ) { + seedConstraint.getExplicitSeeds()=encoding; + SeedHandlerExplicit seed(energy,seedConstraint); + size_t a=RnaSequence::lastPos,b=RnaSequence::lastPos; + if (!seed.updateToNextSeed(a,b,first.first,last.first,lo2,hi2)) continue; + if (seed.getSeedLength1(a,b)>1) { variants.push_back(encoding); continue; } + // A singleton in a multi-pair chain has a neighbor. Expand + // it into each feasible two-pair seed; noLP requires a stack. + for (int direction : {-1,1}) { + const size_t max1=original.noLP?1:energy.getMaxInternalLoopSize1()+1; + const size_t max2=original.noLP?1:energy.getMaxInternalLoopSize2()+1; + for (size_t d1=1; d1<=max1; ++d1) for (size_t d2=1; d2<=max2; ++d2) { + if ((direction<0 && (d1>a-first.first || d2>b-lo2)) + || (direction>0 && (d1>last.first-a || d2>hi2-b))) continue; + const size_t c=direction<0?a-d1:a+d1, d=direction<0?b-d2:b+d2; + if (!energy.areComplementary(c,d)) continue; + variants.push_back(std::to_string(t.getInOutIndex(std::min(a,c)))+"|"+std::string(d1-1,'.')+"|&" + +std::to_string(q.getInOutIndex(energy.getAccessibility2().getReversedIndex(std::max(b,d)))) + +"|"+std::string(d2-1,'.')+"|"); + } + } + } + E_type best=E_INF; + for (const auto & encoding : variants) { + seedConstraint.getExplicitSeeds()=encoding; + SvgSeededMfeTrace trace(energy,unused,nullptr,new SeedHandlerExplicit(energy,seedConstraint)); + Interaction candidate(*mfe); + if (trace.trace(candidate) && candidate.energy max(float(r.attrib['y']) for r in cells.values())+20 - # Original target coordinate 1 is at the bottom; query coordinate 1 at left. + # Both lowest original indices are at the upper-left corner. for i in range(1, 7): for j in range(1, 5): - assert float(cells[i, j].attrib['y']) == float(cells[1, 1].attrib['y'])-20*(i-1) + assert float(cells[i, j].attrib['y']) == float(cells[1, 1].attrib['y'])+20*(i-1) assert float(cells[i, j].attrib['x']) == float(cells[1, 1].attrib['x'])+20*(j-1) for i, row in enumerate(rows[1:], 1): for j, value in enumerate(row[1:], 1): @@ -118,10 +125,36 @@ def plot(t='GGAGGG', q='CCCC', opts=()): ts, te, qs, qe = bounds matrix = pairs(mfe_plot) assert float(outline.attrib['x']) == float(matrix[ts, qs].attrib['x']) - assert float(outline.attrib['y']) == float(matrix[te, qs].attrib['y']) + assert float(outline.attrib['y']) == float(matrix[ts, qs].attrib['y']) assert float(outline.attrib['width']) == float(matrix[ts, qe].attrib['x'])-float(matrix[ts, qs].attrib['x'])+20 - assert float(outline.attrib['height']) == float(matrix[ts, qs].attrib['y'])-float(matrix[te, qs].attrib['y'])+20 + assert float(outline.attrib['height']) == float(matrix[te, qs].attrib['y'])-float(matrix[ts, qs].attrib['y'])+20 assert 'Green outline:' in ''.join(mfe_plot.itertext()) + assert len(mfe_pairs(mfe_plot)) > 2 # includes internal pairs, not just site endpoints + # An unambiguous four-pair stack checks every internal MFE pair against + # independent ordinary MFE output, including the two-color seed overlap. + for seed_options in ([], ['--noSeed'], ['--seedMaxUP=1'], ['--seedTQ=2||&2||']): + for mode in ('M', 'H'): + if '--seedMaxUP=1' in seed_options and mode == 'M': + continue # exact bulged-seed probability output is unsupported + stack = plot('GGGG', 'CCCC', [*seed_options, f'--mode={mode}']) + reference = run('GGGG', 'CCCC', [*seed_options, '--model=X', '--mode=M', + '--outNumber=1', '--outMode=C', '--outCsvCols=bpList']) + row = next(csv.DictReader(reference.stdout.splitlines(), delimiter=';')) + expected = {tuple(map(int, bp)) for bp in re.findall(r'\((\d+),(\d+)\)', row['bpList'])} + assert set(mfe_pairs(stack)) == expected == {(1,4), (2,3), (3,2), (4,1)} + for bp, outline in mfe_pairs(stack).items(): + cell = pairs(stack)[bp] + left, top = float(cell.attrib['x'])+1, float(cell.attrib['y'])+1 + if bp in seeds(stack): + assert outline.tag.endswith('g') + assert outline.find('s:path[@class="seed"]', NS).attrib['d'] == f'M {left:g} {top:g} v 18 M {left+18:g} {top:g} v 18' + assert outline.find('s:path[@class="mfe"]', NS).attrib['d'] == f'M {left:g} {top:g} h 18 M {left:g} {top+18:g} h 18' + else: + assert float(outline.attrib['x']) == left + assert float(outline.attrib['y']) == top + # A sole admitted seed fixes a shorter best site; singleton plotting remains valid. + singleton = plot('GGGG', 'CCCC', ['--seedTQ=4|&1|']) + assert (4,1) in mfe_pairs(singleton) unseeded = plot(opts=['--noSeed', f'--out=bpProb:{table}']) assert not seeds(unseeded) rows = list(csv.reader(table.open(), delimiter=';')) @@ -161,7 +194,7 @@ def plot(t='GGAGGG', q='CCCC', opts=()): idx = int(g.attrib['data-index']) r = signed_cells[idx,-15] if strand == 'target' else signed_cells[-15,idx] axis = 'y' if strand == 'target' else 'x' - expected_pos = float(r.attrib[axis]) + (0 if idx > 1 else 20) if strand == 'target' else float(r.attrib[axis]) + (20 if idx > 1 else 0) + expected_pos = float(r.attrib[axis]) + (20 if idx > 1 else 0) assert float(g.attrib[axis+'1']) == expected_pos assert ('major' in g.attrib['class']) == (idx % 50 == 0) assert ('origin' in g.attrib['class']) == (idx == 1) From 0d61d4b07caca760d64a46ee82afc4e3730ba034 Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Tue, 13 Oct 2026 11:25:31 +0200 Subject: [PATCH 17/30] tmp folder --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7654a03d..ee819cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,7 @@ doxygen-doc doxygen-doc/* conf3108* test-driver - +tmp/* # temp dir tmp From 6d8272673d1b8b0c2c4efaff746af03a6e2f09af Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Tue, 13 Oct 2026 11:53:06 +0200 Subject: [PATCH 18/30] compute allSeedStacked only once --- src/IntaRNA/SeedHandlerExplicit.cpp | 10 +++++----- src/IntaRNA/SeedHandlerExplicit.h | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/IntaRNA/SeedHandlerExplicit.cpp b/src/IntaRNA/SeedHandlerExplicit.cpp index 7deb8b2c..fa98dd96 100644 --- a/src/IntaRNA/SeedHandlerExplicit.cpp +++ b/src/IntaRNA/SeedHandlerExplicit.cpp @@ -17,6 +17,7 @@ SeedHandlerExplicit( ) : SeedHandler(energy,seedConstraint) , seedForLeftEnd() + , allSeedsStacked(true) { #if INTARNA_IN_DEBUG_MODE if (seedConstraint.getExplicitSeeds().empty()) throw std::runtime_error("SeedHandlerExplicit() : explicit seed encoding is empty"); @@ -41,6 +42,8 @@ SeedHandlerExplicit( if (!seedData.isValid()) { LOG(WARNING) <<"explicit seed '"< seedForLeftEnd; + //! whether or not all seeds are stacked (i.e. no bulges or internal loops) + bool allSeedsStacked; + }; } /* namespace IntaRNA */ From 7a522358274f8ae636634bd89e51e2d8d7ee86b6 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Tue, 13 Oct 2026 12:35:58 +0200 Subject: [PATCH 19/30] fix: address PR #258 seed-domain review Use Interaction::BasePair and hash maps for sparse seed lookup, make the stack-length invariant debug-only, and document probability computation steps and contributor conventions. Validate overlapping seed endings, deterministic traversal, and input rejection. Release and debug make tests -j2 each pass all seven suites (100967 assertions in 98 API cases); installed headers, pkg-config consumer, assertion-mode probe, and before/after CLI output checks also pass. --- AGENTS.md | 15 ++++++++++ ChangeLog | 19 +++++++++++++ src/IntaRNA/BasePairProbabilities.cpp | 5 ++++ .../PredictorMfeEns2dSeedExtension.cpp | 14 +++++++--- src/IntaRNA/SeededPartitionFunction.cpp | 18 ++++++++++++ src/IntaRNA/SeededPartitionFunction.h | 4 +-- src/IntaRNA/StackSeedDomain.cpp | 24 ++++++++++++++-- src/IntaRNA/StackSeedDomain.h | 8 +++--- tests/PredictorHeuristicProbability_test.cpp | 24 ++++++++-------- tests/SeededChainOracle.h | 11 ++++---- tests/SeededPartitionFunction_test.cpp | 18 ++++++------ tests/SeededStackKernel.h | 1 + tests/SeededTheoryFixtures.h | 8 +++--- tests/StackSeedDomain_test.cpp | 28 ++++++++++++++++++- 14 files changed, 153 insertions(+), 44 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 46333509..4f78aaf9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,17 @@ Consult those checks for build or public API changes. `INTARNA_..._H_` include guards, and existing class/member naming. Keep edits focused; do not reformat unrelated code or modernize vendored `src/easylogging++.*` or `tests/catch.hpp` as part of an ordinary fix. +- Represent base-pair coordinates with `Interaction::BasePair` rather than + `std::pair` or `std::array`; document the coordinate orientation when it differs + from the original sequences. For sparse lookup tables such as seed starts, use + `std::unordered_map`. + Use ordered storage only when ordering is required; preserve deterministic + traversal explicitly when iterating hash-based data affects results or output. +- Check internal invariants with `assert` or debug-only checks guarded by + `#ifndef NDEBUG` (or the existing `INTARNA_IN_DEBUG_MODE` convention). Keep + release checks for public input validation and failures possible during normal + execution, including numerical range errors, I/O errors and unsupported + extension-hook behavior. Assertions must have no required side effects. - Keep class bodies focused on declarations. Define very short functions **explicitly `inline` after the class definition in the same header**, inside the namespace and include guard. Put nontrivial non-template implementations @@ -167,6 +178,10 @@ constraints, ownership and exceptions. Use `//!` for brief member documentation and implementation comments to explain recurrences and assumptions. Update comments with the code; preserve existing author attribution. Doxygen setup is in `doc/doxygen.cfg`; update `README.md` and CLI help for user-visible changes. +Within nontrivial implementations, introduce each logical step with a concise +inline comment explaining its purpose and relevant invariants, especially domain +filtering, recurrence transitions, coordinate conversion and result publication. +Use these comments to make the sequence of operations easy to review. Update **[ChangeLog](ChangeLog)** (exact capitalization) for every introduced change, including documentation, tests and build changes, in the same PR: diff --git a/ChangeLog b/ChangeLog index 5e0eb3f0..b39ddb81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,10 @@ ## Technical changes and Optimizations +- use hashed base-pair keys for sparse stack-seed lookup, assert internal seed + geometry in debug builds, and document probability computation steps and + contributor conventions (PR #258 review) + - document independent release/debug, installed-API and performance validation for the reviewed unseeded and heuristic probability outputs (PR #259) @@ -187,6 +191,21 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261013 Alexander Mitrofanov + * StackSeedDomain, SeededPartitionFunction, PredictorMfeEns2dSeedExtension + * use Interaction::BasePair coordinates and hashed sparse seed lookup tables + * replace the stack-only handler's equal-length invariant with a debug assert; + retain runtime validation of public input and numerical/extension failures + + explain domain filtering, deterministic start traversal, inside/outside + recurrence steps and coordinate conversion (PR #258 review) + * BasePairProbabilities, AGENTS.md + + explain validation and publication steps; document base-pair/hash storage, + debug-only invariants and implementation-comment conventions + * StackSeedDomain_test, seeded and heuristic probability oracle fixtures/tests + + verify shortest shared seed endings, duplicate and unordered occurrences, + row-major traversal and release-time rejection of invalid seed inputs + * use the common base-pair representation while retaining independent oracles + 261012 Alexander Mitrofanov * BasePairProbabilitySvg, BasePairProbabilityWriter, CommandLineParsing * restore top-to-bottom target indexing, aligned frames/guides and MFE region diff --git a/src/IntaRNA/BasePairProbabilities.cpp b/src/IntaRNA/BasePairProbabilities.cpp index 4d06f327..c97d7988 100644 --- a/src/IntaRNA/BasePairProbabilities.cpp +++ b/src/IntaRNA/BasePairProbabilities.cpp @@ -15,15 +15,18 @@ void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, { using A=PartitionArithmetic; try { + // Validate the caller's lifecycle, dimensions and optional annotation mask. if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); if (!t.isAscending() || !q.isAscending() || t.to>=mass.size1() || q.to>=mass.size2() || values.size1()!=t.to-t.from+1 || values.size2()!=q.to-q.from+1) throw std::invalid_argument("pair probabilities: invalid region dimensions"); if (collectSeeds && (!seedPairs || seedPairs->size1()!=values.size1() || seedPairs->size2()!=values.size2())) throw std::invalid_argument("pair probabilities: missing or invalid regional seed mask"); + // Disjoint searched rectangles ensure each interaction contributes once. for (const auto & r:regions) if (t.from<=r.first.to && r.first.from<=t.to && q.from<=r.second.to && r.second.from<=q.to) throw std::invalid_argument("pair probabilities: searched regions overlap"); + // Check every proposed sum before committing any regional data. const Z_type merged=A::add(z,denominator); for (size_t i=0;i BasePairProbabilities::probabilities() const { const Z_type tolerance=512*std::numeric_limits::epsilon() *Z_type(std::max(size_t(1),mass.size1()+mass.size2())); std::vector columns(mass.size2(),0); + // Normalize each mass and validate that a nucleotide has at most one partner. for(size_t i=0;i BasePairProbabilities::probabilities() const { } void BasePairProbabilities::finalize() { try { + // Successful completion includes empty searches; failed results stay failed. if(state!=Status::pending) throw std::logic_error("pair probabilities: completion requires a pending result"); PartitionArithmetic::check(z); state=z==0?Status::empty:Status::nonempty; diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index 7fd217f0..0b6eb098 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -336,6 +336,7 @@ PredictorMfeEns2dSeedExtension::exactBoundaryWeight(size_t i1,size_t j1,size_t i void PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const IndexRange & r2) { + // Validate public regions before translating both strands to local coordinates. const size_t size1=energy.getAccessibility1().getSequence().size(); const size_t size2=energy.getAccessibility2().getSequence().size(); if (!r1.isAscending() || !r2.isAscending() || r1.from>=size1 || r2.from>=size2) @@ -344,27 +345,31 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In seedHandler.setOffset1(r1.from); seedHandler.setOffset2(r2.from); const size_t n=r1.to==RnaSequence::lastPos?energy.size1():std::min(energy.size1(),r1.to-r1.from+1); const size_t m=r2.to==RnaSequence::lastPos?energy.size2():std::min(energy.size2(),r2.to-r2.from+1); + // Compile admitted, contained seeds using the active spans for this prediction. initOptima(); initZ(); Zall=0; const size_t seedCount=seedHandler.fillSeed(0,n-1,0,m-1); const size_t span1=std::min(n,energy.getAccessibility1().getMaxLength()); const size_t span2=std::min(m,energy.getAccessibility2().getMaxLength()); StackSeedDomain seeds=seedCount ? StackSeedDomain(seedHandler,n,m,span1,span2) : StackSeedDomain(std::vector(),n,m,span1,span2); + // Supply native weights once per initiation, transition and complete boundary. using K=SeededPartitionFunction; const K::Domain domain{n,m,span1,span2,energy.getMaxInternalLoopSize1(),energy.getMaxInternalLoopSize2(),output.getOutputConstraint().noLP}; auto boltzmann=[&](E_type e) { return E_isINF(e)?Z_type(0):PartitionArithmetic::exp(-E_2_Z(e)/energy.getRT()); }; K::Weights weights{ - [&](K::Pair p){return energy.isAccessible1(p[0]) && energy.isAccessible2(p[1]) && energy.areComplementary(p[0],p[1]);}, + [&](K::Pair p){return energy.isAccessible1(p.first) && energy.isAccessible2(p.second) && energy.areComplementary(p.first,p.second);}, [&](K::Pair){return boltzmann(energy.getE_init());}, - [&](K::Pair p,K::Pair q){return boltzmann(energy.getE_interLeft(p[0],q[0],p[1],q[1]));}, - [&](K::Pair p,K::Pair q){return exactBoundaryWeight(p[0],q[0],p[1],q[1]);}, - [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p[0],q[0],p[1],q[1],h,b);} + [&](K::Pair p,K::Pair q){return boltzmann(energy.getE_interLeft(p.first,q.first,p.second,q.second));}, + [&](K::Pair p,K::Pair q){return exactBoundaryWeight(p.first,q.first,p.second,q.second);}, + [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p.first,q.first,p.second,q.second,h,b);} }; const auto result=K::compute(domain,seeds,weights,pairProbabilities!=nullptr); + // Custom updateZ overrides can change the objective even in release builds. if (result.z!=Zall) throw std::logic_error("exact seeded updateZ override did not preserve the partition objective"); output.setExactPartition(true); reportOptima(); if (pairProbabilities) { + // Restore original strand orientation and offsets before merging this region. const auto first=energy.getBasePair(0,0), last=energy.getBasePair(n-1,m-1); const IndexRange target(first.first,last.first), query(last.second,first.second); Matrix original(n,m,0); @@ -379,6 +384,7 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In seedPairs(seedPair.first-target.from,seedPair.second-query.from)=1; } } + // Publish regional masses only after the complete computation has succeeded. pairProbabilities->addRegion(target,query,result.z,original,annotate?&seedPairs:nullptr); } } diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index 63e2007f..f18a753d 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -1,5 +1,6 @@ #include "IntaRNA/SeededPartitionFunction.h" #include "IntaRNA/PartitionArithmetic.h" +#include // Seed-free suffix lengths plus two absorbing seeded states (singleton/run). // Nonstack edges operate on aggregate totals, independently of seed length. @@ -13,11 +14,13 @@ SeededPartitionFunction::Result SeededPartitionFunction::compute( using A=PartitionArithmetic; using Pair=SeededPartitionFunction::Pair; SeededPartitionFunction::Result out; + // Empty seed families or zero spans admit no interaction, even with pair output. if (pairs) out.mass=Matrix(d.n,d.m,0); if (!seeds.maxSeedLength() || !d.span1 || !d.span2) return out; // At least two length states are necessary for noLP even with singleton seeds. const size_t cap=std::max(size_t(2),seeds.maxSeedLength()), s1=cap, s2=cap+1, states=cap+2; seeds.forEachStart([&](size_t p1,size_t p2) { + // Each eligible left boundary owns a separate forward and reverse problem. const Pair p{p1,p2}; if (!w.valid(p)) return; const size_t n=std::min(d.span1,d.n-p1),m=std::min(d.span2,d.m-p2); Matrix h(matrix_detail::product(n,m),states),dh; @@ -25,31 +28,39 @@ SeededPartitionFunction::Result SeededPartitionFunction::compute( Matrix valid(n,m); Matrix ending(n,m); if (pairs) { dh.resize(n*m,states); dt.resize(n,m); } + // Cache vertex admission and seed endings in this start's cropped domain. for (size_t i=0;i=cap) return stacked || !d.noLP?s2:s1; const size_t len=stacked?std::min(cap,c+2):1; return accepts(i,j,len)?(len>=2 || !d.noLP?s2:s1):len-1; }; + // Enumerate loop predecessors separately from the direct stacking edge. auto nonstack=[&](size_t i,size_t j,const auto & visit) { for(size_t u=1;u<=i && u-1<=d.loop1;++u) for(size_t v=1;v<=j && v-1<=d.loop2;++v) if((u!=1 || v!=1) && valid(i-u,j-v)) visit(i-u,j-v); }; + // Initiation belongs to the leftmost pair exactly once, including singletons. const size_t initial=accepts(0,0,1)?(d.noLP?s1:s2):0; const Z_type init=A::check(w.initiation(p)); h(0,initial)=init; + // Forward pass: extend prefixes and finalize every admitted right boundary. for(size_t i=0;i0) { + // Share the same boundary coefficient with reporting and the outside pass. const Z_type b=A::check(w.boundary(p,{p1+i,p2+j})); out.z=A::add(out.z,A::multiply(b,total(i,j)[1])); if(pairs) { dh(q,s2)=b;if(!d.noLP) dh(q,s1)=b; } @@ -67,12 +80,16 @@ SeededPartitionFunction::Result SeededPartitionFunction::compute( } } if(!pairs) return; + // Reverse pass: propagate boundary derivatives back through the same edges. for(size_t ii=n;ii>0;--ii) for(size_t jj=m;jj>0;--jj) { const size_t i=ii-1,j=jj-1,q=i*m+j; if(!valid(i,j)) continue; + // Distribute derivatives of the two aggregate totals to their suffix states. for(size_t c=d.noLP?1:0;c #include namespace IntaRNA { @@ -14,7 +14,7 @@ namespace IntaRNA { class SeededPartitionFunction { public: //! Local zero-based pair, second strand in reversed coordinates. - using Pair = std::array; + using Pair = Interaction::BasePair; /** Active rectangle, inclusive spans, loop limits and structural restriction. */ struct Domain { size_t n,m,span1,span2,loop1,loop2; bool noLP; }; /** Energy adapter. Complete boundary callbacks are made once per positive diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp index baf99c5a..1591d2aa 100644 --- a/src/IntaRNA/StackSeedDomain.cpp +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -1,43 +1,61 @@ #include "IntaRNA/StackSeedDomain.h" #include +#include #include +#include namespace IntaRNA { StackSeedDomain::StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t w1, size_t w2) { + // Reject unsupported input before interpreting seed lengths as diagonal runs. if (!handler.guaranteesStackOnlySeeds()) throw std::invalid_argument("stack seed domain requires a stack-only handler"); if (n==0 || m==0) return; + // Use the range end as an invalid initial cursor, avoiding lastPos+offset wrap. size_t i=n, j=m; while (handler.updateToNextSeed(i,j,0,n-1,0,m-1)) { + // Stack-only admission guarantees equal strand lengths internally. const size_t len=handler.getSeedLength1(i,j); - if (len != handler.getSeedLength2(i,j)) throw std::logic_error("stack seed has unequal strand lengths"); + assert(len == handler.getSeedLength2(i,j)); + // Retain only occurrences fully contained in the active interaction domain. add({i,j,len},n,m,w1,w2); } } StackSeedDomain::StackSeedDomain(const std::vector & seeds, size_t n, size_t m, size_t w1, size_t w2) { + // Apply the same containment and span filters to externally supplied seeds. for (auto s:seeds) add(s,n,m,w1,w2); } void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2) { + // Check containment using remaining lengths so endpoint arithmetic cannot wrap. w1=std::min(w1,n); w2=std::min(w2,m); if (s.length==0 || s.i>=n || s.j>=m || s.length>n-s.i || s.length>m-s.j || s.length>w1 || s.length>w2) return; - auto [p,inserted]=lengths.emplace(std::make_pair(s.i,s.j),s.length); + + // A start must identify one retained length, including for explicit input. + auto [p,inserted]=lengths.emplace(Interaction::BasePair(s.i,s.j),s.length); if (!inserted && p->second!=s.length) throw std::invalid_argument("multiple retained seed lengths at one start"); - auto [end, fresh]=endingLengths.emplace(std::make_pair(s.i+s.length-1,s.j+s.length-1),s.length); + + // The shortest seed ending here suffices to accept a stacked path suffix. + auto [end, fresh]=endingLengths.emplace(Interaction::BasePair(s.i+s.length-1,s.j+s.length-1),s.length); if (!fresh) end->second=std::min(end->second,s.length); maximum=std::max(maximum,s.length); + + // A left boundary can precede this seed only while its end fits both spans. const size_t end1=s.i+s.length, end2=s.j+s.length; boxes.push_back({end1>w1?end1-w1:0,s.i,end2>w2?end2-w2:0,s.j}); } void StackSeedDomain::forEachStart(const std::function & visit) const { if (boxes.empty()) return; + // Restrict the row sweep to rows covered by at least one eligible-start box. size_t first=boxes.front().i0, last=0; for (auto b:boxes) { first=std::min(first,b.i0); last=std::max(last,b.i1); } + // These pairs are inclusive intervals on strand two, not base-pair coordinates. std::vector> intervals; for (size_t i=first;i<=last;++i) { + // Sort this row's column intervals independently of seed insertion order. intervals.clear(); for (auto b:boxes) if (b.i0<=i && i<=b.i1) intervals.emplace_back(b.j0,b.j1); std::sort(intervals.begin(),intervals.end()); + // Skip already visited columns where boxes overlap; emit row-major order. size_t next=0; for (auto [lo,hi]:intervals) { for (size_t j=std::max(lo,next);j<=hi;++j) visit(i,j); diff --git a/src/IntaRNA/StackSeedDomain.h b/src/IntaRNA/StackSeedDomain.h index a6c82f64..c64f26f8 100644 --- a/src/IntaRNA/StackSeedDomain.h +++ b/src/IntaRNA/StackSeedDomain.h @@ -1,10 +1,10 @@ #ifndef INTARNA_STACKSEEDDOMAIN_H_ #define INTARNA_STACKSEEDDOMAIN_H_ +#include "IntaRNA/Interaction.h" #include "IntaRNA/SeedHandler.h" #include -#include -#include +#include #include namespace IntaRNA { @@ -45,9 +45,9 @@ class StackSeedDomain { void forEachStart(const std::function & visit) const; private: //! admitted occurrences keyed by local start - std::map, size_t> lengths; + std::unordered_map lengths; //! shortest retained length per ending vertex for suffix acceptance - std::map, size_t> endingLengths; + std::unordered_map endingLengths; //! eligible inclusive rectangles [i0,i1] x [j0,j1] struct Box { size_t i0, i1, j0, j1; }; std::vector boxes; diff --git a/tests/PredictorHeuristicProbability_test.cpp b/tests/PredictorHeuristicProbability_test.cpp index 96fdf6b8..d2805808 100644 --- a/tests/PredictorHeuristicProbability_test.cpp +++ b/tests/PredictorHeuristicProbability_test.cpp @@ -22,7 +22,7 @@ using namespace IntaRNA; namespace { -using Pair=std::array; +using Pair=Interaction::BasePair; using Chain=std::vector; struct ChainCandidate { @@ -37,7 +37,7 @@ struct HeuristicReference { }; bool adjacent(Pair p,Pair q) { - return p[0]+1==q[0] && p[1]+1==q[1]; + return p.first+1==q.first && p.second+1==q.second; } /** Enumerate complete chains first, then filter by the documented selected @@ -51,12 +51,12 @@ HeuristicReference enumerateHeuristic(const InteractionEnergy & energy, Chain chain; std::function enumerate=[&](E_type hybrid) { const Pair left=chain.front(),right=chain.back(); - if (right[0]-left[0]+1>energy.getAccessibility1().getMaxLength() - || right[1]-left[1]+1>energy.getAccessibility2().getMaxLength()) return; + if (right.first-left.first+1>energy.getAccessibility1().getMaxLength() + || right.second-left.second+1>energy.getAccessibility2().getMaxLength()) return; chains[left].push_back({chain,hybrid}); - for(size_t i=right[0]+1;i,const ChainCandidate *>> admitted; for(const auto & candidate:chains[left]) { const auto & c=candidate.chain; - if (constraint.noGUend && energy.isGU(c.back()[0],c.back()[1])) continue; + if (constraint.noGUend && energy.isGU(c.back().first,c.back().second)) continue; size_t suffix=1; std::array order{0,0,0}; if (constraint.noLP) { @@ -81,9 +81,9 @@ HeuristicReference enumerateHeuristic(const InteractionEnergy & energy, else if (selected[c[1]]==Chain(c.begin()+1,c.end())) order={1,0,0}; else { if (adjacent(c[1],c[2])) continue; - suffix=2;order={2,c[2][0],c[2][1]}; + suffix=2;order={2,c[2].first,c[2].second}; } - } else if (c.size()>1) order={1,c[1][0],c[1][1]}; + } else if (c.size()>1) order={1,c[1].first,c[1].second}; if (suffixconstraint.maxED || energy.getED2(j,right[1])>constraint.maxED)) continue; + && (energy.getED1(i,right.first)>constraint.maxED || energy.getED2(j,right.second)>constraint.maxED)) continue; const Z_type weight=energy.getBoltzmannWeight(full); result.z+=weight;++result.candidates; for(const Pair pair:candidate.chain) result.mass[pair]+=weight; @@ -148,7 +148,7 @@ TEST_CASE("heuristic pair probabilities count the retained candidate chains", "[ REQUIRE(result.getZ()==Approx(reference.z).epsilon(3e-12)); Matrix expected(target.size(),query.size(),0); for(const auto & entry:reference.mass) { - const auto bp=shifted.getBasePair(entry.first[0],entry.first[1]); + const auto bp=shifted.getBasePair(entry.first.first,entry.first.second); expected(bp.first,bp.second)=entry.second; } for(size_t i=0;i #include #include @@ -11,7 +12,7 @@ // with both strands increasing. Weight callbacks return long double, so expected // values never pass through native integer energy conversion or the tested DP. namespace seeded_oracle { -using Pair = std::array; +using Pair = IntaRNA::Interaction::BasePair; using Boundary = std::array; using Chain = std::vector; struct Model { @@ -28,7 +29,7 @@ struct Result { std::map mass; }; inline bool stack(Pair a, Pair b) { - return a[0]+1 == b[0] && a[1]+1 == b[1]; + return a.first+1 == b.first && a.second+1 == b.second; } inline Result enumerate(const Model & model) { Result out; @@ -46,13 +47,13 @@ inline Result enumerate(const Model & model) { const auto w = hybrid * model.boundary(p, q); if (seeded) { out.z += w; - out.boundaries[{p[0], p[1], q[0], q[1]}] += w; + out.boundaries[{p.first, p.second, q.first, q.second}] += w; out.pairCount += w * chain.size(); for (Pair pair : chain) out.mass[pair] += w; } else out.unseeded += w; } - for (size_t i=q[0]+1; i seeds; - for(const auto & s:m.seeds) seeds.push_back({s[0][0],s[0][1],s.size()}); + for(const auto & s:m.seeds) seeds.push_back({s[0].first,s[0].second,s.size()}); return {seeds,m.n,m.m,m.span1,m.span2}; } Kernel::Weights weightsFor(const seeded_oracle::Model & m) { @@ -76,7 +76,7 @@ void compareKernels(const seeded_oracle::Model & m) { CAPTURE(suffix); auto w=weightsFor(m); std::map boundaries; - w.complete=[&](auto p,auto q,auto h,auto b){boundaries[{p[0],p[1],q[0],q[1]}]+=h*b;}; + w.complete=[&](auto p,auto q,auto h,auto b){boundaries[{p.first,p.second,q.first,q.second}]+=h*b;}; auto got=suffix?Kernel::compute(d,seeds,w,true):seededStack(d,seeds,w,true); REQUIRE(double(got.z)==Approx(double(oracle.z)).epsilon(2e-12)); for(auto [b,z]:oracle.boundaries) REQUIRE(double(boundaries[b])==Approx(double(z)).epsilon(2e-12)); @@ -102,10 +102,10 @@ TEST_CASE("heterogeneous weighted kernels match independent enumeration", "[Seed for(size_t trial=0;trial<100;++trial) { CAPTURE(trial); Model m{6,5,2+rng()%5,2+rng()%4,bool(trial%2),{}, - [trial](Pair p){return (p[0]+3*p[1]+trial)%7!=0;}, - [](Pair p){return .7L+.13L*p[0];}, - [trial](Pair p,Pair q){return (p[0]+q[1]+trial)%11==0?0.L:.1L+.17L*(q[0]-p[0])+.21L*(q[1]-p[1]);}, - [trial](Pair p,Pair q){return (q[0]+p[1]+trial)%5==0?0.L:.2L+.07L*(q[0]+p[1]);}}; + [trial](Pair p){return (p.first+3*p.second+trial)%7!=0;}, + [](Pair p){return .7L+.13L*p.first;}, + [trial](Pair p,Pair q){return (p.first+q.second+trial)%11==0?0.L:.1L+.17L*(q.first-p.first)+.21L*(q.second-p.second);}, + [trial](Pair p,Pair q){return (q.first+p.second+trial)%5==0?0.L:.2L+.07L*(q.first+p.second);}}; for(size_t i=0;i +#include #include inline IntaRNA::SeededPartitionFunction::Result seededStack(const IntaRNA::SeededPartitionFunction::Domain & d, const IntaRNA::StackSeedDomain & seeds, diff --git a/tests/SeededTheoryFixtures.h b/tests/SeededTheoryFixtures.h index c7ae833d..83980445 100644 --- a/tests/SeededTheoryFixtures.h +++ b/tests/SeededTheoryFixtures.h @@ -29,15 +29,15 @@ inline std::vector theoryFixtures() { auto & f=fixtures.back(); if (tag == "Z") file >> f.expected.z; else if (tag == "S") { - Pair p; size_t len; file >> p[0] >> p[1] >> len; - Chain s; for (size_t k=0;k> p.first >> p.second >> len; + Chain s; for (size_t k=0;k> i; file >> f.terminal[b] >> f.seeded[b] >> f.free[b]; f.expected.boundaries[b]=f.terminal[b]*f.seeded[b]; } else if (tag == "P") { - Pair p; file >> p[0] >> p[1] >> f.valid[p] >> f.expected.mass[p]; + Pair p; file >> p.first >> p.second >> f.valid[p] >> f.expected.mass[p]; } } } @@ -46,7 +46,7 @@ inline std::vector theoryFixtures() { f.model.init=[](Pair){return std::exp(1.L);}; f.model.edge=[](Pair,Pair){return std::exp(1.L);}; f.model.boundary=[b=f.terminal](Pair p,Pair q){ - auto it=b.find({p[0],p[1],q[0],q[1]});return it==b.end()?0.L:it->second; + auto it=b.find({p.first,p.second,q.first,q.second});return it==b.end()?0.L:it->second; }; } return fixtures; diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index 454e7210..4054c13d 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -23,6 +23,7 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); SeedHandlerMfe bulge(energy,bulged); REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); + REQUIRE_THROWS_AS(StackSeedDomain(bulge,6,6,6,6),std::invalid_argument); SeedConstraint explicitC(1,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, IndexRangeList(),IndexRangeList(),"1|&6|,2|||&3|||,5||&1||",false,false,false); SeedHandlerIdxOffset explicitH(new SeedHandlerExplicit(energy,explicitC)); @@ -35,7 +36,7 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") REQUIRE(domain.maxSeedLength()==3); StackSeedDomain mixed({{0,0,1},{2,2,2},{4,4,3}},6,6,4,3); REQUIRE(mixed.maxSeedLength()==2); - std::set> starts; + std::set starts; size_t calls=0; mixed.forEachStart([&](size_t i,size_t j){ ++calls; starts.emplace(i,j); }); REQUIRE(calls==starts.size()); @@ -49,6 +50,31 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") REQUIRE_FALSE(eb.guaranteesStackOnlySeeds()); } +TEST_CASE("stack-domain lookups and traversal preserve overlapping seed geometry", "[StackSeedDomain]") { + // Different starts can share an ending pair; the shortest suffix must win + // independently of insertion order, while duplicate seeds add no new starts. + std::vector seeds{{3,2,1},{1,0,3},{2,1,2},{3,2,1},{0,4,2}}; + const std::vector expected{{0,0},{0,1},{0,2},{0,3},{0,4}, + {1,0},{1,1},{1,2},{2,0},{2,1},{2,2},{3,0},{3,1},{3,2}}; + for (bool reverse:{false,true}) { + if (reverse) std::reverse(seeds.begin(),seeds.end()); + StackSeedDomain domain(seeds,6,6,4,4); + REQUIRE(domain.seedLength(1,0)==3); + REQUIRE(domain.seedLength(2,1)==2); + REQUIRE(domain.seedLength(3,2)==1); + REQUIRE(domain.seedLength(0,1)==0); // swapped coordinates are a different pair + REQUIRE(domain.seedEndingLength(3,2)==1); + REQUIRE(domain.seedEndingLength(1,5)==2); + REQUIRE(domain.seedEndingLength(2,3)==0); + REQUIRE(domain.maxSeedLength()==3); + std::vector starts; + domain.forEachStart([&](size_t i,size_t j){ starts.emplace_back(i,j); }); + REQUIRE(starts==expected); + } + // Conflicting externally supplied lengths remain an input error in release. + REQUIRE_THROWS_AS((StackSeedDomain({{1,1,1},{1,1,2}},4,4,4,4)),std::invalid_argument); +} + TEST_CASE("stack-domain admission preserves handler-specific threshold equality", "[StackSeedDomain]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGG"),q("q","CCC"); From e3479d693ae335780537c8bf3311aaa1b5ac6f76 Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Tue, 13 Oct 2026 14:41:25 +0200 Subject: [PATCH 20/30] debug-only checks --- src/IntaRNA/BasePairProbabilities.cpp | 27 ++++++++++++++++------ src/IntaRNA/Matrix.h | 2 ++ src/IntaRNA/PredictorMfeEns2d.cpp | 8 +++---- src/IntaRNA/PredictorMfeEns2dHeuristic.cpp | 11 ++++----- src/IntaRNA/SeededPartitionFunction.cpp | 4 +++- src/IntaRNA/StackSeedDomain.cpp | 24 +++++++++++++++---- 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/IntaRNA/BasePairProbabilities.cpp b/src/IntaRNA/BasePairProbabilities.cpp index c97d7988..27fe7e0e 100644 --- a/src/IntaRNA/BasePairProbabilities.cpp +++ b/src/IntaRNA/BasePairProbabilities.cpp @@ -4,11 +4,16 @@ #include namespace IntaRNA { -void BasePairProbabilities::markApproximate() { + +void +BasePairProbabilities:: +markApproximate() { if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); approximate=true; } -BasePairProbabilities::BasePairProbabilities(size_t n,size_t m,bool collectSeedPairs) + +BasePairProbabilities:: +BasePairProbabilities(size_t n,size_t m,bool collectSeedPairs) : mass(n,m,0), collectSeeds(collectSeedPairs), seeds(collectSeedPairs?n:0,collectSeedPairs?m:0,0) {} void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, Z_type denominator,const Matrix & values,const Matrix * seedPairs) @@ -17,15 +22,17 @@ void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, try { // Validate the caller's lifecycle, dimensions and optional annotation mask. if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); +#if INTARNA_IN_DEBUG_MODE if (!t.isAscending() || !q.isAscending() || t.to>=mass.size1() || q.to>=mass.size2() || values.size1()!=t.to-t.from+1 || values.size2()!=q.to-q.from+1) throw std::invalid_argument("pair probabilities: invalid region dimensions"); if (collectSeeds && (!seedPairs || seedPairs->size1()!=values.size1() || seedPairs->size2()!=values.size2())) throw std::invalid_argument("pair probabilities: missing or invalid regional seed mask"); - // Disjoint searched rectangles ensure each interaction contributes once. - for (const auto & r:regions) + // Disjoint searched rectangles ensure each interaction contributes once. + for (const auto & r:regions) if (t.from<=r.first.to && r.first.from<=t.to && q.from<=r.second.to && r.second.from<=q.to) - throw std::invalid_argument("pair probabilities: searched regions overlap"); + throw std::invalid_argument("pair probabilities: searched regions overlap"); +#endif // Check every proposed sum before committing any regional data. const Z_type merged=A::add(z,denominator); for (size_t i=0;i BasePairProbabilities::probabilities() const { + +Matrix +BasePairProbabilities:: +probabilities() const { using A=PartitionArithmetic; if (state!=Status::nonempty) throw std::logic_error("pair probabilities: no successful nonempty result"); Matrix p(mass.size1(),mass.size2(),0); @@ -66,7 +76,10 @@ Matrix BasePairProbabilities::probabilities() const { for(auto c:columns) if(c>1+tolerance) throw std::logic_error("pair probabilities: query exclusivity violated"); return p; } -void BasePairProbabilities::finalize() { + +void +BasePairProbabilities:: +finalize() { try { // Successful completion includes empty searches; failed results stay failed. if(state!=Status::pending) throw std::logic_error("pair probabilities: completion requires a pending result"); diff --git a/src/IntaRNA/Matrix.h b/src/IntaRNA/Matrix.h index c5390f82..d714ce28 100644 --- a/src/IntaRNA/Matrix.h +++ b/src/IntaRNA/Matrix.h @@ -543,7 +543,9 @@ template inline std::size_t UpperBandedMatrix::width(std::size_t columns, std::size_t lower, std::size_t upper) { +#if INTARNA_IN_DEBUG_MODE if (lower != 0) throw std::invalid_argument("upper-banded matrix requires lower=0"); +#endif return upper >= columns ? columns : upper + 1; } diff --git a/src/IntaRNA/PredictorMfeEns2d.cpp b/src/IntaRNA/PredictorMfeEns2d.cpp index a03506da..63dece5f 100644 --- a/src/IntaRNA/PredictorMfeEns2d.cpp +++ b/src/IntaRNA/PredictorMfeEns2d.cpp @@ -55,14 +55,12 @@ PredictorMfeEns2d::predictRegion(const IndexRange & r1,const IndexRange & r2) #if INTARNA_IN_DEBUG_MODE // check indices - if (!(r1.isAscending() && r2.isAscending()) ) + if (!(r1.isAscending() && r2.isAscending()) + || r1.from>=energy.getAccessibility1().getSequence().size() + || r2.from>=energy.getAccessibility2().getSequence().size())) throw std::runtime_error("PredictorMfeEns2d::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif - if (pairProbabilities && (!r1.isAscending() || !r2.isAscending() - || r1.from>=energy.getAccessibility1().getSequence().size() - || r2.from>=energy.getAccessibility2().getSequence().size())) - throw std::invalid_argument("unseeded pair probabilities: invalid region"); if (pairProbabilities && pairProbabilities->status()!=BasePairProbabilities::Status::pending) throw std::logic_error("unseeded pair probabilities: accumulator is not pending"); diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp b/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp index 93b57cbf..df0f3e75 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp +++ b/src/IntaRNA/PredictorMfeEns2dHeuristic.cpp @@ -53,17 +53,14 @@ predictRegionHeuristic(const IndexRange & r1,const IndexRange & r2) #if INTARNA_IN_DEBUG_MODE // check indices - if (!(r1.isAscending() && r2.isAscending()) ) + if (!(r1.isAscending() && r2.isAscending()) + || r1.from>=energy.getAccessibility1().getSequence().size() + || r2.from>=energy.getAccessibility2().getSequence().size()) throw std::runtime_error("PredictorMfeEns2dHeuristic::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif - - if (!r1.isAscending() || !r2.isAscending() - || r1.from>=energy.getAccessibility1().getSequence().size() - || r2.from>=energy.getAccessibility2().getSequence().size()) - throw std::invalid_argument("heuristic ensemble prediction: invalid region"); if (pairProbabilities && pairProbabilities->status()!=BasePairProbabilities::Status::pending) - throw std::logic_error("heuristic pair probabilities: accumulator is not pending"); + throw std::logic_error("PredictorMfeEns2dHeuristic::predict: heuristic pair probabilities: accumulator is not pending"); if (pairProbabilities) pairProbabilities->markApproximate(); // set index offset energy.setOffset1(r1.from); diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index f18a753d..f5e560e9 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -5,7 +5,9 @@ // Seed-free suffix lengths plus two absorbing seeded states (singleton/run). // Nonstack edges operate on aggregate totals, independently of seed length. namespace IntaRNA { -SeededPartitionFunction::Result SeededPartitionFunction::compute( +SeededPartitionFunction::Result +SeededPartitionFunction:: +compute( const IntaRNA::SeededPartitionFunction::Domain & d, const IntaRNA::StackSeedDomain & seeds, const IntaRNA::SeededPartitionFunction::Weights & w, bool pairs) diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp index 1591d2aa..ca486142 100644 --- a/src/IntaRNA/StackSeedDomain.cpp +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -5,9 +5,13 @@ #include namespace IntaRNA { -StackSeedDomain::StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t w1, size_t w2) { + +StackSeedDomain:: +StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t w1, size_t w2) { // Reject unsupported input before interpreting seed lengths as diagonal runs. +#if INTARNA_IN_DEBUG_MODE if (!handler.guaranteesStackOnlySeeds()) throw std::invalid_argument("stack seed domain requires a stack-only handler"); +#endif if (n==0 || m==0) return; // Use the range end as an invalid initial cursor, avoiding lastPos+offset wrap. @@ -20,11 +24,18 @@ StackSeedDomain::StackSeedDomain(const SeedHandler & handler, size_t n, size_t m add({i,j,len},n,m,w1,w2); } } -StackSeedDomain::StackSeedDomain(const std::vector & seeds, size_t n, size_t m, size_t w1, size_t w2) { + + +StackSeedDomain:: +StackSeedDomain(const std::vector & seeds, size_t n, size_t m, size_t w1, size_t w2) { // Apply the same containment and span filters to externally supplied seeds. for (auto s:seeds) add(s,n,m,w1,w2); } -void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2) { + + +void +StackSeedDomain:: +add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2) { // Check containment using remaining lengths so endpoint arithmetic cannot wrap. w1=std::min(w1,n); w2=std::min(w2,m); if (s.length==0 || s.i>=n || s.j>=m || s.length>n-s.i || s.length>m-s.j @@ -32,7 +43,9 @@ void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2 // A start must identify one retained length, including for explicit input. auto [p,inserted]=lengths.emplace(Interaction::BasePair(s.i,s.j),s.length); +#if INTARNA_IN_DEBUG_MODE if (!inserted && p->second!=s.length) throw std::invalid_argument("multiple retained seed lengths at one start"); +#endif // The shortest seed ending here suffices to accept a stacked path suffix. auto [end, fresh]=endingLengths.emplace(Interaction::BasePair(s.i+s.length-1,s.j+s.length-1),s.length); @@ -43,7 +56,10 @@ void StackSeedDomain::add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2 const size_t end1=s.i+s.length, end2=s.j+s.length; boxes.push_back({end1>w1?end1-w1:0,s.i,end2>w2?end2-w2:0,s.j}); } -void StackSeedDomain::forEachStart(const std::function & visit) const { + +void +StackSeedDomain:: +forEachStart(const std::function & visit) const { if (boxes.empty()) return; // Restrict the row sweep to rows covered by at least one eligible-start box. size_t first=boxes.front().i0, last=0; From cb1c9d419f96c7d2a10bae864d922dcb0234f4d4 Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Wed, 14 Oct 2026 14:23:00 +0200 Subject: [PATCH 21/30] minor test fixes --- src/IntaRNA/AccessibilityFromStream.cpp | 2 +- src/IntaRNA/PredictorMfeEns2d.cpp | 2 +- src/IntaRNA/SeededPartitionFunction.cpp | 6 +++++- tests/Matrix_test.cpp | 2 +- tests/StackSeedDomain_test.cpp | 11 ++++++----- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/IntaRNA/AccessibilityFromStream.cpp b/src/IntaRNA/AccessibilityFromStream.cpp index acc747b0..0f9b7b51 100644 --- a/src/IntaRNA/AccessibilityFromStream.cpp +++ b/src/IntaRNA/AccessibilityFromStream.cpp @@ -117,7 +117,7 @@ parseRNAplfold_text( std::istream & inStream, const Z_type RT, const bool parseP #if INTARNA_MULITHREADING #pragma omp critical(intarna_omp_logOutput) #endif - { LOG(INFO) <<"initializing ED data for sequence '"<=energy.getAccessibility1().getSequence().size() - || r2.from>=energy.getAccessibility2().getSequence().size())) + || r2.from>=energy.getAccessibility2().getSequence().size()) throw std::runtime_error("PredictorMfeEns2d::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index f5e560e9..3c5113c0 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -54,12 +54,16 @@ compute( // Forward pass: extend prefixes and finalize every admitted right boundary. for(size_t i=0;i(2, 2, 1, 1), std::invalid_argument); + //REQUIRE_THROWS_AS(UpperBandedMatrix(2, 2, 1, 1), std::invalid_argument); // in debug mode only } TEST_CASE("Oversized matrix dimensions fail before allocation", "[Matrix]") { diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index 4054c13d..be595ada 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -20,10 +20,11 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") SeedHandlerNoBulge stack(energy,computed); REQUIRE(mfe.guaranteesStackOnlySeeds()); REQUIRE(stack.guaranteesStackOnlySeeds()); - SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); - SeedHandlerMfe bulge(energy,bulged); - REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); - REQUIRE_THROWS_AS(StackSeedDomain(bulge,6,6,6,6),std::invalid_argument); + // only in debug mode the following checks are enabled + // SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + // SeedHandlerMfe bulge(energy,bulged); + // REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); + // REQUIRE_THROWS_AS(StackSeedDomain(bulge,6,6,6,6),std::invalid_argument); SeedConstraint explicitC(1,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, IndexRangeList(),IndexRangeList(),"1|&6|,2|||&3|||,5||&1||",false,false,false); SeedHandlerIdxOffset explicitH(new SeedHandlerExplicit(energy,explicitC)); @@ -105,7 +106,7 @@ TEST_CASE("explicit stack capability inspects the retained minimum-energy patter SeedConstraint sc(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, IndexRangeList(),IndexRangeList(),"1|.|&2|.|,1|||&2|||",false,false,false); SeedHandlerExplicit handler(e,sc); - REQUIRE(handler.guaranteesStackOnlySeeds()); + REQUIRE(!handler.guaranteesStackOnlySeeds()); REQUIRE(handler.fillSeed(0,3,0,3)==1); StackSeedDomain domain(handler,4,4,4,4); REQUIRE(domain.seedLength(0,0)==3); From 392357c00f0314659cd4c665446f3f6185f4814a Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Wed, 14 Oct 2026 15:45:53 +0200 Subject: [PATCH 22/30] feat: compute canonical seed-extension partitions and pair masses --- ChangeLog | 47 +++ README.md | 28 +- doc/Makefile.am | 9 + doc/analysis/seed-extension-partition.md | 295 ++++++++++++++++++ doc/benchmarks/seed-extension/README.md | 60 ++++ doc/benchmarks/seed-extension/build.py | 64 ++++ doc/benchmarks/seed-extension/driver.cpp | 54 ++++ .../seed-extension/recorded-results.json | 146 +++++++++ .../seed-extension/recorded-samples.tsv | 49 +++ .../recorded-source-hashes.json | 17 + doc/benchmarks/seed-extension/run.py | 85 +++++ ...IntaRNA.SeededPartitionFunction.inside.svg | 4 +- ...ntaRNA.SeededPartitionFunction.outside.svg | 4 +- src/IntaRNA/BasePairProbabilities.cpp | 8 +- ...redictorMfeEns2dHeuristicSeedExtension.cpp | 49 ++- .../PredictorMfeEns2dHeuristicSeedExtension.h | 21 ++ .../PredictorMfeEns2dSeedExtension.cpp | 16 +- src/IntaRNA/PredictorMfeEns2dSeedExtension.h | 18 ++ src/IntaRNA/SeededPartitionFunction.cpp | 230 +++++++++----- src/IntaRNA/SeededPartitionFunction.h | 13 +- src/IntaRNA/StackSeedDomain.cpp | 7 +- ...edictorHeuristicSeededProbability_test.cpp | 59 ++++ tests/PredictorMfeEnsRegression_test.cpp | 4 +- tests/PredictorSeedOracle_test.cpp | 77 ++++- tests/SeededChainOracle.h | 25 +- tests/SeededPartitionFunction_test.cpp | 182 ++++++++++- tests/StackSeedDomain_test.cpp | 35 ++- 27 files changed, 1486 insertions(+), 120 deletions(-) create mode 100644 doc/analysis/seed-extension-partition.md create mode 100644 doc/benchmarks/seed-extension/README.md create mode 100644 doc/benchmarks/seed-extension/build.py create mode 100644 doc/benchmarks/seed-extension/driver.cpp create mode 100644 doc/benchmarks/seed-extension/recorded-results.json create mode 100644 doc/benchmarks/seed-extension/recorded-samples.tsv create mode 100644 doc/benchmarks/seed-extension/recorded-source-hashes.json create mode 100644 doc/benchmarks/seed-extension/run.py diff --git a/ChangeLog b/ChangeLog index b39ddb81..dd36858e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,16 @@ ## Technical changes and Optimizations +- compute exact and heuristic stack-seeded ensembles by first-seed ownership, + seed-avoiding left prefixes and unrestricted right extensions; differentiate + the same nonnegative retained ensemble for actual-pair probabilities (#264) + +- initialize seed iteration outside the complete remaining sequence so a seed + beyond a cropped region cannot hide an earlier valid seed + +- validate probability-region dimensions, seed masks and overlap in release + builds before accepting data or dereferencing a required annotation mask + - use hashed base-pair keys for sparse stack-seed lookup, assert internal seed geometry in debug builds, and document probability computation steps and contributor conventions (PR #258 review) @@ -191,6 +201,43 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261014 Alexander Mitrofanov + * SeededPartitionFunction.* + * Replace the combined absorbing-state recurrence by separate seed-free + prefixes, canonical first-hit injections and shared right extensions. + Apply full-boundary weights once after summing all seed owners; propagate + pair masses through the same nonnegative recurrence. Co-ending variable + seed lengths use their shortest admitted occurrence as canonical owner. + * PredictorMfeEns2d{,Heuristic}SeedExtension.* + * Route stack-seeded heuristic prediction through the common kernel with + frozen right choices. Retain one-sided and selected-endpoint two-sided + paths once; preserve legacy gapped-seed behavior and extension hooks. + * SeededChainOracle.h, SeededPartitionFunction_test.cpp, + PredictorSeedOracle_test.cpp, PredictorHeuristicSeededProbability_test.cpp, + PredictorMfeEnsRegression_test.cpp + + Check first-hit ownership, complete boundaries, raw pair masses and + heuristic subsets with independent tiny-chain enumeration and fugacities. + * StackSeedDomain.cpp, StackSeedDomain_test.cpp + * Start iteration at the remaining sequence end; avoid colliding with an + explicit seed outside a cropped active region. Cover offset and repeated + regional predictions. Reject conflicting externally supplied lengths in + release builds to keep seed-start and seed-end lookup consistent. + * Align the conservative-handler capability test with the documented domain + precondition in debug builds while checking retained seed geometry. + * BasePairProbabilities.cpp + * Restore public region/mask/overlap validation in release builds; the full + suite exposed a missing-mask null dereference on the target branch. + * README.md, doc/analysis/seed-extension-partition.md, doc/Makefile.am, + doc/recursions/IntaRNA.SeededPartitionFunction.{inside,outside}.svg, + doc/benchmarks/seed-extension/* + + Explain the Step 6 theory transfer, numerical/structural constraints, + changed heuristic semantics and validation; distribute the analysis and + identify the superseded suffix-recurrence diagrams as historical. + + Supply reproducible baseline comparisons of all boundary/pair masses, + timing and process RSS, including the measured exact-kernel slowdown. + * Implement https://github.com/BackofenLab/IntaRNA/issues/264 on its + requested docs/257-base-pair-probability-plan base. + 261013 Alexander Mitrofanov * StackSeedDomain, SeededPartitionFunction, PredictorMfeEns2dSeedExtension * use Interaction::BasePair coordinates and hashed sparse seed lookup tables diff --git a/README.md b/README.md index 7a118b25..0e2d8855 100644 --- a/README.md +++ b/README.md @@ -2031,13 +2031,19 @@ is also used without `bpProb`, so requesting the matrix does not change `Zall`. Computed seeds preserve their handler-specific admission thresholds. Explicit stacked seeds may have different lengths, including singletons; competing explicit patterns at the same start still retain only the selected minimum-energy -pattern. With `--mode=H`, probabilities describe the predictor's retained -candidate ensemble, not all possible interactions. Unseeded H retains one -continuation per left pair; seeded H retains its seed-extension candidates and -selected right extension, including the existing seed-overlap corrections. -Seeded H supports computed or explicit seeds with at least two pairs, including -bulged seeds. These heuristic ensembles can retain path multiplicities and -must not be interpreted as the exact unique-chain ensemble of mode M. +pattern. Stack-seeded predictions use a seed-avoiding left extension up to the +first completed admitted seed and an unrestricted right extension. This +canonical seed owns the complete chain even if later seeds occur. +With `--mode=H`, probabilities describe the predictor's retained candidate +ensemble. Unseeded H retains one continuation per left pair. Stack-seeded H +retains each canonical seed's one-sided extensions and two-sided extensions +ending at its selected right endpoint, counting each retained chain once. +Its partition is therefore at most the exact stack-seeded partition with the +same constraints; values can differ from the former signed-overlap heuristic. +Seeded H supports computed or explicit seeds with at least two pairs. Bulged +seeds retain the legacy extension/correction algorithm, which can retain path +multiplicities. The [seed-extension recurrence](doc/analysis/seed-extension-partition.md) +defines ownership, endpoint selection, and the shared inside/outside calculation. Kinetic, evaluation, seed-only and exact bulged-seed probability requests remain unsupported. @@ -2537,9 +2543,11 @@ Autotools, and publishing instructions. The [seeded base-pair probability implementation plan](doc/analysis/base-pair-probabilities-plan.md) records the reviewed algorithms, numerical contracts, integration, and validation -for issue #257. The implementation selects the suffix backend after the -[measured kernel comparison](doc/analysis/seeded-kernel-comparison.md); -[actual base-pair output](#bpProb) is available for exact stack-seeded ensembles. +for issue #257, including the historical +[measured kernel comparison](doc/analysis/seeded-kernel-comparison.md). +The current [seed-extension backend](doc/analysis/seed-extension-partition.md) +implements first-seed ownership for exact and heuristic stack-seeded ensembles, +including [actual base-pair output](#bpProb). When IntaRNA is build while `pkg-config` is present, according pkg-config information is generated and installed too. diff --git a/doc/Makefile.am b/doc/Makefile.am index 9d47b6ab..701676fc 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -8,6 +8,7 @@ EXTRA_DIST = \ analysis/base-pair-probabilities-plan.md \ analysis/seeded-kernel-comparison.md \ analysis/seeded-probabilities-validation.md \ + analysis/seed-extension-partition.md \ seeded-probabilities-example.cpp \ api-documentation.md \ build-api.sh \ @@ -24,3 +25,11 @@ EXTRA_DIST = \ latex-deps/tocloft.sty \ latex-deps/trimclip.sty \ latex-deps/xtab.sty + +EXTRA_DIST += benchmarks/seed-extension/README.md \ + benchmarks/seed-extension/build.py \ + benchmarks/seed-extension/run.py \ + benchmarks/seed-extension/driver.cpp \ + benchmarks/seed-extension/recorded-results.json \ + benchmarks/seed-extension/recorded-samples.tsv \ + benchmarks/seed-extension/recorded-source-hashes.json diff --git a/doc/analysis/seed-extension-partition.md b/doc/analysis/seed-extension-partition.md new file mode 100644 index 00000000..6addca19 --- /dev/null +++ b/doc/analysis/seed-extension-partition.md @@ -0,0 +1,295 @@ +# Seed-extension partition functions and actual-pair probabilities + +This document describes the nonnegative stack-seed extension formulation for +[issue #264](https://github.com/BackofenLab/IntaRNA/issues/264). Its reference is +the IntaRNA-probabilities branch +[`theory-seed-extension`](https://github.com/BackofenLab/IntaRNA-probabilities/tree/theory-seed-extension), +reviewed at revision +[`e904b65101294f782b0938590335f3e23631a1aa`](https://github.com/BackofenLab/IntaRNA-probabilities/tree/e904b65101294f782b0938590335f3e23631a1aa), +especially the +[seed-extension chapter](https://github.com/BackofenLab/IntaRNA-probabilities/blob/e904b65101294f782b0938590335f3e23631a1aa/theory/seed-extension.md) +and its `SE-LEFT-REC`, `SE-FIRST-HIT`, and `SE-PAIR-FIRST-HIT` equations. +The historical subtraction formulas discussed in that chapter are not the +recurrences used here. + +## Ensemble and weight ownership + +An interaction is an ordered nonempty chain of complementary intermolecular +pairs. Coordinates below are local, zero-based, and increase on both strands; +the second strand has already been reversed. A legal edge joins successive +pairs and respects the interaction loop limits. An admitted seed is a concrete +stack occurrence retained by the active seed handler, after its energy, +accessibility, position, and seed-specific pairing filters. + +For an interaction with outer pairs `p` and `r`, the hybrid weight is its +initiation weight multiplied by one transition weight for each successive pair. +The complete boundary coefficient contains accessibility and the native +boundary energy corrections. It is evaluated for the full outer intervals and +multiplied once. A seed's stored energy is used by seed admission; it is not an +additional factor in the exact chain weight. This avoids counting initiation, +anchor pairs, or their internal loops twice. + +The exact ensemble contains every legal chain with an admitted seed, once, +regardless of how many overlapping or disjoint seed occurrences it contains. +Interaction span limits are inclusive on both strands. Whole-interaction +restrictions such as `noLP` are part of the recurrence; endpoint restrictions +such as `noGUend` and complete-span accessibility are applied to the full +boundary. Neither ranked-result limits nor display cutoffs select the ensemble. + +## Unique first acceptance + +Scan a chain from its left boundary. Its canonical anchor is the first admitted +seed to finish. Before that completion, the prefix contains no admitted seed; +after it, the remaining chain has no further seed requirement. This event is +unique even when later seeds overlap the anchor. + +Strict stack occurrences ending at the same pair can have different lengths +when explicit seeds are used. Choose the shortest admitted occurrence ending +there as canonical owner. Every chain containing a longer occurrence ending at +that pair also contains this shorter suffix, so these simultaneous matches form +one acceptance event. Ownership is chosen before applying the heuristic +selector described below. + +To detect an event, the nonaccepting state retains the terminal strict-stack +run length, capped at the longest relevant seed length. A new pair accepts when +its terminal run is at least the shortest admitted seed length ending there. +The run can exceed that length because earlier potential seed windows may have +been filtered out. A filtered-out window neither accepts nor resets the run. +A nonstack transition resets the run to one pair. + +The empty declared seed family produces an empty seeded ensemble. A singleton +explicit seed can first accept at the initial pair or at a later appended pair; +these alternatives are disjoint. + +## Seed-free left prefixes, first hits, and right extensions + +Fix the exact left boundary `p`. Let `N[p,h,a]` be the hybrid weight of prefixes +ending at pair `h` in nonaccepting state `a`. The initial prefix has the sole +initiation factor. Its weight is assigned to `N` only when the initial pair is +not itself a declared singleton seed. Each legal transition appends its weight +to `N` only if the resulting state remains nonaccepting. + +Let `F[p,q,c]` hold first-hit weights ending at pair `q`, with structural state +`c` needed by the right extension. It receives: + +- the initiation weight for a singleton source hit at `p = q`; or +- `N[p,h,a] * transition(h,q)` for an accepting edge from a seed-free prefix. + +An accepting contribution leaves `N`; it is not retained in both families. +The left prefix owns initiation and all pairs before `q`. The accepting edge +owns the new pair `q`. Thus `F` already includes the complete anchor and must +not be multiplied by another standalone seed weight. + +The theory writes a normalized right continuation `B` with value one for an +empty continuation. In the absence of additional structural state, its +first-hit contraction is + +```text +Hseed[p,r] = sum_q F[p,q] * B[q,r]. +``` + +The implementation shares the work of all anchors through an inhomogeneous +right-extension recurrence: + +```text +T[p,q,c] = F[p,q,c] + + sum_(h,b -> q,c) T[p,h,b] * transition(h,q). +``` + +Here the edge relation includes any structural-state transition. The right +recurrence has no seed state, seed test, or second initiation factor. Expanding +it gives exactly the sum of first-hit weights multiplied by their unrestricted +right continuations. Combining injections before extending is valid by +distributivity: each complete chain still has its unique first-hit event. +This shares continuation work without enumerating complete interactions or +subtracting overlapping seed classes. + +Sum terminally admissible `T` states to obtain the seeded hybrid partition for +each `(p,r)`. Apply the complete boundary coefficient and publish that boundary +once, after all anchor contributions have been combined. Keeping `p` throughout +the calculation is necessary because accessibility and span restrictions can +couple the two outer ends. + +## The `noLP` structural state + +Seed acceptance does not remove the requirement that every pair have a stacked +neighbor. Before acceptance, a terminal run of length one needs support from a +future stack. A run of length at least two is already supported. Nonstack edges +may leave only supported states. + +After acceptance, the right extension retains these two structural states: + +- **Pending:** the terminal pair has no stacked neighbor yet. +- **Supported:** the terminal pair already has a stacked neighbor. + +A stack from either state enters the supported state. A nonstack edge can leave +only the supported state and enters the pending state. Only the supported +state may terminate. A singleton source hit therefore starts pending and can +become a valid interaction through a right stack. The same rule handles a +singleton first hit following a nonstack edge. + +Consequently the right extension is unrestricted with respect to seeds, but +requires structural state when `noLP` is enabled. Using a scalar unconstrained +continuation in that case would admit lonely pairs. + +## A defined heuristic subset + +The stack-seed heuristic uses the same canonical first-hit ownership as the +exact computation. For canonical anchor `S`, let `start(S)` and `end(S)` be its +outer pairs, and let `selectedRight(S)` be the deterministic endpoint selected +by the heuristic's right-extension policy. Select the endpoint before the +partition and outside calculations; marking a pair must not change it. + +The default predictor preserves its existing right-extension selection hooks. +It enumerates strict right extensions in increasing first-strand endpoint, +then increasing reversed-second-strand endpoint. Candidates pass the existing +right-partition numerical-zero test. Their scores are the full seed-plus-right +ensemble energies, including initiation and the complete boundary energy for +`start(S)` and the candidate endpoint. `noGUend` excludes GU right endpoints. +An endpoint replaces the current choice only for a strictly lower score, so +equal scores retain the first visited endpoint. If no candidate qualifies, +the choice remains `end(S)`. Custom overrides of the right-extension hooks +remain responsible for deterministic choices. The default heuristic predictor +retains its existing rejection of singleton seeds, although the shared kernel +and exact predictor support them. + +Keep a complete chain with outer boundaries `(p,r)` precisely when it satisfies +the ordinary interaction constraints and at least one of these equalities: + +```text +p == start(S) OR r == end(S) OR r == selectedRight(S). +``` + +The first alternative retains every right extension with no left extension. +The second retains every left extension with no right extension. The third +retains both-sided extensions ending at the selected right endpoint. The +selector is a union: a chain satisfying several alternatives is counted once. +All legal right paths to an admitted endpoint are retained, rather than only +one representative path. The canonical owner is fixed independently of whether +a later seed would offer a different selected endpoint. + +For fixed `p`, anchors with `p == start(S)` can share an unrestricted right pass. +For the remaining anchors, add the zero-right-extension contribution directly +at `end(S)`, and group the other first-hit injections by their selected right +endpoint. Each group shares one right recurrence and contributes only at that +group's terminal endpoint. If the selected endpoint equals the anchor end, +the direct contribution already covers it. Anchors admitted by the unrestricted +case must not also contribute through the restricted cases. + +Grouping is valid because the right recurrence is linear and independent of +seed history. Keeping separate selected-endpoint groups preserves the selector +for each canonical anchor; merging all groups into one unconstrained pass would +incorrectly admit other endpoints. Sum the retained contributions for each +complete boundary before reporting it or initializing its outside coefficient. + +With unchanged nonnegative weights, this defines a subset of the exact seeded +ensemble, hence `0 <= Zheuristic <= Zexact`. The heuristic probabilities are +conditional on that retained subset. They need not equal the exact seeded +probabilities, and this definition does not promise numerical identity with +the historical signed-overlap heuristic. + +## Actual-pair probabilities by the outside pass + +The raw numerator for pair `k` is the sum of complete interaction weights whose +chains actually contain `k`. It is not the sum of weights of boundary rectangles +covering `k`; the latter is a spot probability. + +Initialize outside derivatives from the same complete boundary coefficients +used by forward accumulation. Reverse the right-extension passes, transfer +their derivatives into the corresponding first-hit injections, and then +reverse the accepting edges and seed-free prefix recurrence. A transition +owns its destination pair; the initialization owns the first pair. Accumulating +each state's forward weight times its outside derivative, `N * dN + T * dT`, +counts that pair exactly once in every retained complete chain, including +anchor pairs. First-hit states are not marked separately because their final +pair already belongs to the right-extension state. A directly retained +zero-right-extension branch accounts for its accepting pair in the same way. + +For heuristic calculations, reverse the same grouped passes and frozen +endpoint selector. The outside calculation must not independently recompute a +different ensemble or normalize each anchor separately. Merge raw region pair +numerators and denominators before final normalization. An empty ensemble has +undefined conditional probabilities; a numerical failure is an error, not an +empty ensemble. + +## Scope, checks, and resource considerations + +The run-length automaton supports admitted stack occurrences, including +different-length and singleton explicit seeds. A general gapped seed requires +a different prefix-matching automaton. The existing legacy gapped-seed fallback +is retained; the stack-seed proof and nonnegative formulation do not establish +its correctness or expand its supported probability modes. + +The production weights use native IntaRNA energies and `Z_type` arithmetic. +"Exact" describes the ensemble and recurrence, not exact real-number +arithmetic. Checked partition operations must report nonfinite values and +unsupported numerical ranges rather than silently discard positive terms. + +The independent tests should compare complete boundary partitions and raw +pair numerators, not only rounded final probabilities. Small path-enumeration +oracles should cover overlapping and disjoint seeds, filtered windows, +co-ending different-length seeds, singleton source and edge hits, both span +limits, and `noLP` states on both sides of acceptance. Heuristic checks must use +the same frozen selector, verify each retained chain once, and compare its +partition with the exact ensemble. + +The seed-free table still retains run-length state for each active left +boundary. Sharing right extensions avoids repeating identical continuation +work for every anchor, but does not imply a lower asymptotic bound for the +whole calculation. Heuristic cost also depends on the number of distinct +selected endpoints. Pair output adds outside storage and work. Performance or +memory improvements require measurements against the previous implementation +with identical inputs, options, build configuration, and thread count; the +factorization alone is not a performance claim. + +## Measured exact-kernel tradeoff + +The [reproducible kernel benchmark](../benchmarks/seed-extension/README.md) +compares the exact backend with revision +`cb1c9d419f96c7d2a10bae864d922dcb0234f4d4`, using GCC 14.4.0 release flags +on an AMD Ryzen 5 7530U. Every reported boundary hybrid value, boundary +coefficient, total partition, and pair numerator was compared before timing: +328,236 entries agree with maximum relative difference `1.30e-14`. + +Median kernel seconds over three alternating fresh-process samples are below. +Both versions use the same deterministic weights, spans, loop limits, and seed +families. The benchmark directory contains individual samples and source hashes. + +| Fixture | Seeds | Partition only, previous / revised | With pair numerators, previous / revised | +| --- | ---: | ---: | ---: | +| Dense 40 by 40 | 1,369 | 0.0618 / 0.0951 | 0.1911 / 0.2509 | +| Dense 40 by 40 | 3 | 0.0446 / 0.0606 | 0.1349 / 0.1580 | +| Banded 60 by 60 | 387 | 0.00960 / 0.0169 | 0.0240 / 0.0335 | +| Banded 60 by 60 | 3 | 0.00811 / 0.0130 | 0.0205 / 0.0277 | + +The revised exact kernel is slower on these fixtures: `1.36–1.76` times the +previous partition-only cost and `1.17–1.40` times the cost with pair numerators. +Whole-process median maximum RSS is `3,840–4,096 KiB` for both versions; these +small cases do not resolve the recurrence's allocation cost separately from +runtime and allocator overhead. Samples were not CPU-pinned and other work +could run on the workstation. These measurements characterize the cost of this +factorization on the stated fixtures and do not establish a general runtime +or memory bound. + +## Validation of the delivered implementation + +GCC 14.4 release and `--enable-debug` builds both pass `make tests -j2`: all +seven API/CLI suites pass in each build. The API suite reports 106 cases with +175,007 assertions in release and 175,008 in debug. Coverage includes independent +complete-chain enumeration for exact and frozen-selector ensembles, randomized +tiny selectors, native predictor integration, singleton and mixed-length +anchors, endpoint/region constraints, `noLP`, and outside pair numerators. +The installed pkg-config example and standalone compilation of the changed +public headers also pass. The benchmark's portable build and runner were +executed from the committed scripts, and `git diff --check` passes. + +Validation also exposed existing boundary-admission problems that are repaired +with this change. Seed-handler iteration now starts from the remaining +sequence ends, which are invalid cursor positions even when an explicit seed +lies exactly at a cropped region's excluded end. This prevents an out-of-range +seed from causing the first valid seed in the region to be skipped. Conflicting +retained seed lengths at one start are rejected in release as well as debug. +The public probability collector validates region dimensions, annotation-mask +dimensions, and overlapping searched regions in both build modes. A nonfinite +partition produced by a custom predictor override is explicitly rejected before +comparison with the kernel result. diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md new file mode 100644 index 00000000..5dd86345 --- /dev/null +++ b/doc/benchmarks/seed-extension/README.md @@ -0,0 +1,60 @@ +# Seed-extension kernel comparison + +This Linux benchmark compares the exact seeded kernel at +`cb1c9d419f96c7d2a10bae864d922dcb0234f4d4` with the current source checkout. +It first checks every reported boundary hybrid weight and boundary coefficient, +the total partition, and every raw pair numerator. It then times partition-only +and pair-output calculations in separate processes, alternating baseline and +candidate order. Three repetitions are the default. + +Configure the source tree first so `src/IntaRNA/intarna_config.h` is available. +Requirements are a C++23 GCC toolchain, the project's headers and Boost headers, +Python 3, and GNU `/usr/bin/time`. Run from the repository root: + +```sh +python3 doc/benchmarks/seed-extension/build.py \ + --source "$PWD" --output /tmp/intarna-seed-extension-benchmark --cxx g++-14 +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-seed-extension-benchmark --repetitions 3 +``` + +For dependencies outside the compiler's search paths, add +`--include /path/to/dependencies/include` to the build command. An optional +`--runtime-library-dir /path/to/compiler/lib` embeds a runtime library search +path. Both options can be repeated. `--baseline` selects another Git revision +when a different comparison is intended. + +The build script snapshots both versions of `SeededPartitionFunction` and +`StackSeedDomain`, including their headers; other project headers and configured +types are shared. It records the compiler, exact commands, baseline commit, +and SHA-256 source digests in `manifest.json`. The runner writes full verification +dumps, `samples.tsv`, and `results.json` in the build directory. Timed runs do not +write the full boundary dumps. + +## Workloads and measurement limits + +The dense cases admit every vertex on a 40 by 40 grid. The banded cases use a +60 by 60 grid and admit only vertices with `abs(i-j) <= 3`. All cases use +inclusive interaction spans of 24 on each strand, loop limits of three unpaired +bases per strand, and stack seeds of length four. The many-seed cases admit all +contained seeds on valid diagonals: 1,369 dense or 387 banded occurrences. The +few-seed cases have three diagonal seeds starting at `n/4`, `n/2`, and `3n/4`. +`noLP` is disabled; separate oracle tests validate that restriction. + +`driver.cpp` declares deterministic coordinate-dependent initiation, transition, +and full-boundary weights. It measures the complete kernel invocation, including +a small boundary callback. Seed construction, native thermodynamic/accessibility +computation, and serialization are outside this timing. These fixtures exercise +the recurrence, rather than representing complete biological workloads. + +Recorded runs used GCC 14.4.0, `-std=c++23 -O3 -DNDEBUG`, one thread, and an +AMD Ryzen 5 7530U on 2026-10-14. Samples were not CPU-pinned and other build/test +work could run on the same workstation. Treat small differences as noise. +`recorded-results.json` contains correctness differences and timing medians with +minimum/maximum values; `recorded-samples.tsv` contains the individual samples; +`recorded-source-hashes.json` identifies the measured source snapshots. + +GNU time records whole-process maximum RSS, including runtime libraries and +allocator overhead. These small fixtures do not isolate the recurrence's peak +allocation. Timings and RSS are local observations, not asymptotic bounds. +See the [algorithm and measured tradeoff](../../analysis/seed-extension-partition.md). diff --git a/doc/benchmarks/seed-extension/build.py b/doc/benchmarks/seed-extension/build.py new file mode 100644 index 00000000..73f71d2c --- /dev/null +++ b/doc/benchmarks/seed-extension/build.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +"""Build isolated baseline/current seeded kernels using a configured source tree.""" +import argparse +import hashlib +import json +from pathlib import Path +import subprocess + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True, + help="configured IntaRNA source checkout") + parser.add_argument("--output", type=Path, required=True, + help="directory for source snapshots and executables") + parser.add_argument("--cxx", default="g++", help="C++23 compiler executable") + parser.add_argument("--baseline", default="cb1c9d419f96c7d2a10bae864d922dcb0234f4d4") + parser.add_argument("--include", type=Path, action="append", default=[], + help="extra dependency include directory; repeat as needed") + parser.add_argument("--runtime-library-dir", type=Path, action="append", default=[], + help="optional runtime library directory to embed as rpath") + args = parser.parse_args() + source, output = args.source.resolve(), args.output.resolve() + output.mkdir(parents=True, exist_ok=True) + driver = Path(__file__).with_name("driver.cpp").resolve() + (output / "driver.cpp").write_bytes(driver.read_bytes()) + names = ("SeededPartitionFunction.cpp", "SeededPartitionFunction.h", + "StackSeedDomain.cpp", "StackSeedDomain.h") + for variant in ("base", "candidate"): + (output / variant / "IntaRNA").mkdir(parents=True, exist_ok=True) + for name in names: + content = (subprocess.check_output( + ["git", "-C", str(source), "show", args.baseline + ":src/IntaRNA/" + name]) + if variant == "base" else (source / "src" / "IntaRNA" / name).read_bytes()) + (output / variant / "IntaRNA" / name).write_bytes(content) + commands = [] + for variant in ("base", "candidate"): + command = [args.cxx, "-std=c++23", "-O3", "-DNDEBUG", + "-ffunction-sections", "-fdata-sections", + "-I" + str(output / variant), "-I" + str(source / "src"), + *("-I" + str(p.resolve()) for p in args.include), + str(output / "driver.cpp"), + str(output / variant / "IntaRNA/SeededPartitionFunction.cpp"), + str(output / variant / "IntaRNA/StackSeedDomain.cpp"), "-Wl,--gc-sections", + *("-Wl,-rpath," + str(p.resolve()) for p in args.runtime_library_dir), + "-o", str(output / variant / "benchmark")] + subprocess.run(command, check=True) + commands.append(command) + files = [output / "driver.cpp", + *(output / variant / "IntaRNA" / name + for variant in ("base", "candidate") for name in names)] + manifest = { + "baseline": subprocess.check_output( + ["git", "-C", str(source), "rev-parse", args.baseline], text=True).strip(), + "compiler": subprocess.check_output([args.cxx, "--version"], text=True).splitlines()[0], + "commands": commands, + "sha256": {str(p.relative_to(output)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in files}, + } + (output / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") + + +if __name__ == "__main__": + main() diff --git a/doc/benchmarks/seed-extension/driver.cpp b/doc/benchmarks/seed-extension/driver.cpp new file mode 100644 index 00000000..b93854bc --- /dev/null +++ b/doc/benchmarks/seed-extension/driver.cpp @@ -0,0 +1,54 @@ +#include "IntaRNA/SeededPartitionFunction.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc,char **argv) { + using namespace IntaRNA; + using K=SeededPartitionFunction; + if(argc!=6) return 2; + const std::string topology=argv[1],density=argv[2],dump=argv[5]; + const size_t n=std::stoul(argv[3]),span=std::min(n,size_t(24)),length=4; + const bool pairs=std::stoi(argv[4]); + auto valid=[&](K::Pair p) {return topology=="dense" || (p.first>p.second?p.first-p.second:p.second-p.first)<=3;}; + std::vector occurrences; + if(density=="many") { + for(size_t i=0;i+length<=n;++i) for(size_t j=0;j+length<=n;++j) + if(valid({i,j})) occurrences.push_back({i,j,length}); + } else { + for(size_t i:{n/4,n/2,3*n/4}) if(i+length<=n) occurrences.push_back({i,i,length}); + } + const StackSeedDomain seeds(occurrences,n,n,span,span); + struct Cell {K::Pair p,r;Z_type h,b;}; + std::vector cells; + size_t boundaries=0; + Z_type checksum=0; + K::Weights weights{ + valid, + [](K::Pair p) {return Z_type(0.71)+Z_type((p.first+2*p.second)%7)/100;}, + [](K::Pair p,K::Pair q) {return Z_type(0.57)+Z_type((p.first+q.first+3*p.second+q.second)%11)/100;}, + [](K::Pair p,K::Pair q) {return Z_type(1)/(1+Z_type(q.first-p.first+q.second-p.second));}, + [&](K::Pair p,K::Pair r,Z_type h,Z_type b) { + ++boundaries; checksum+=h*b; + if(dump!="-") cells.push_back({p,r,h,b}); + } + }; + const K::Domain domain{n,n,span,span,3,3,false}; + const auto begin=std::chrono::steady_clock::now(); + const auto result=K::compute(domain,seeds,weights,pairs); + const double seconds=std::chrono::duration(std::chrono::steady_clock::now()-begin).count(); + Z_type moment=0; + if(pairs) for(size_t i=0;i Exact seeded inside recurrence · suffix automaton -SeededPartitionFunction::compute() | fixed left boundary p; visit q in increasing pair order. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. +SeededPartitionFunction::compute() | fixed left boundary p; visit q in increasing pair order. Internal recurrence documentation for step 9 of PR 258. Historical equations before issue 264; see ../analysis/seed-extension-partition.md for the current recurrence; RNA 2 uses reversed energy coordinates. -INTARNA / RECURRENCE NOTES +INTARNA / HISTORICAL RECURRENCE BEFORE ISSUE #264 Exact seeded inside recurrence · suffix automaton SeededPartitionFunction::compute() | fixed left boundary p; visit q in increasing pair order diff --git a/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg index fe546876..8481896c 100644 --- a/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg +++ b/doc/recursions/IntaRNA.SeededPartitionFunction.outside.svg @@ -1,10 +1,10 @@ Actual base-pair probabilities · outside recurrence -Reverse differentiation of the suffix inside computation; same fixed p and same boundary weights B. Internal recurrence documentation for step 9 of PR 258. Equations describe the current implementation; RNA 2 uses reversed energy coordinates. +Reverse differentiation of the suffix inside computation; same fixed p and same boundary weights B. Internal recurrence documentation for step 9 of PR 258. Historical equations before issue 264; see ../analysis/seed-extension-partition.md for the current recurrence; RNA 2 uses reversed energy coordinates. -INTARNA / RECURRENCE NOTES +INTARNA / HISTORICAL RECURRENCE BEFORE ISSUE #264 Actual base-pair probabilities · outside recurrence Reverse differentiation of the suffix inside computation; same fixed p and same boundary weights B diff --git a/src/IntaRNA/BasePairProbabilities.cpp b/src/IntaRNA/BasePairProbabilities.cpp index 27fe7e0e..0269d5a2 100644 --- a/src/IntaRNA/BasePairProbabilities.cpp +++ b/src/IntaRNA/BasePairProbabilities.cpp @@ -22,17 +22,15 @@ void BasePairProbabilities::addRegion(const IndexRange & t,const IndexRange & q, try { // Validate the caller's lifecycle, dimensions and optional annotation mask. if (state!=Status::pending) throw std::logic_error("pair probabilities: accumulator is not pending"); -#if INTARNA_IN_DEBUG_MODE if (!t.isAscending() || !q.isAscending() || t.to>=mass.size1() || q.to>=mass.size2() || values.size1()!=t.to-t.from+1 || values.size2()!=q.to-q.from+1) throw std::invalid_argument("pair probabilities: invalid region dimensions"); if (collectSeeds && (!seedPairs || seedPairs->size1()!=values.size1() || seedPairs->size2()!=values.size2())) throw std::invalid_argument("pair probabilities: missing or invalid regional seed mask"); - // Disjoint searched rectangles ensure each interaction contributes once. - for (const auto & r:regions) + // Disjoint searched rectangles ensure each interaction contributes once. + for (const auto & r:regions) if (t.from<=r.first.to && r.first.from<=t.to && q.from<=r.second.to && r.second.from<=q.to) - throw std::invalid_argument("pair probabilities: searched regions overlap"); -#endif + throw std::invalid_argument("pair probabilities: searched regions overlap"); // Check every proposed sum before committing any regional data. const Z_type merged=A::add(z,denominator); for (size_t i=0;imarkApproximate(); + // Stack seeds share the nonnegative canonical-anchor recurrence with exact + // prediction. The frozen right choices restrict both its forward and outside. + if(seedHandler.guaranteesStackOnlySeeds()) { + PredictorMfeEns2dSeedExtension::predict(r1,r2); + return; + } if(!r1.isAscending() || !r2.isAscending() || r1.from>=energy.getAccessibility1().getSequence().size() || r2.from>=energy.getAccessibility2().getAccessibilityOrigin().getSequence().size()) @@ -132,6 +138,43 @@ predict( const IndexRange & r1, const IndexRange & r2 ) } +void +PredictorMfeEns2dHeuristicSeedExtension::prepareStackSeedExtensions( + size_t n,size_t m,const StackSeedDomain & seeds) +{ + // Preserve the right-extension customization hooks while suppressing partial + // partition updates; only the common kernel publishes completed boundaries. + stackSeedRight.clear(); + struct RestoreSelection { + bool & slot; + bool previous; + ~RestoreSelection() { slot=previous; } + } restore{selectingStackSeedRight,selectingStackSeedRight}; + selectingStackSeedRight=true; + for(size_t i=0;i +PredictorMfeEns2dHeuristicSeedExtension::stackSeedRightSelector() const +{ + return [this](Interaction::BasePair first,Interaction::BasePair) { + return stackSeedRight.at(first); + }; +} + + //////////////////////////////////////////////////////////////////////////// void @@ -160,8 +203,10 @@ fillHybridZ_right( const size_t sj1, const size_t sj2 updateOptRightZ( si1,sj1+r1,si2,sj2+r2, energy.getE(seedZ * rightExtZ * initZ) ); // update overall partition function information for true right-extensions of the current seed // seed only not covered due to min-val of r1,r2 - updateZ(si1, sj1+r1, si2, sj2+r2, seedZ * rightExtZ * initZ, true); - addExtensionProbabilityRoot(si1,sj1+r1,si2,sj2+r2,seedZ*rightExtZ*initZ,false,true); + if(!selectingStackSeedRight) { + updateZ(si1, sj1+r1, si2, sj2+r2, seedZ * rightExtZ * initZ, true); + addExtensionProbabilityRoot(si1,sj1+r1,si2,sj2+r2,seedZ*rightExtZ*initZ,false,true); + } } } diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h index a0ea94c7..e7ada51f 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h @@ -4,6 +4,7 @@ #include "IntaRNA/PredictorMfeEns2dSeedExtension.h" #include "IntaRNA/SeedHandlerIdxOffset.h" +#include namespace IntaRNA { @@ -14,6 +15,9 @@ namespace IntaRNA { * Note, for each seed start (i1,i2) only the mfe seed is considered for the * overall interaction computation instead of considering all possible seeds * starting at (i1,i2). + * For stack-only seeds, the first completed seed uniquely owns each chain. + * Retain its one-sided extensions and the left extensions ending at its + * preferred right endpoint. Pair probabilities describe this retained ensemble. * * @author Frank Gelhausen * @author Martin Raden @@ -69,6 +73,23 @@ class PredictorMfeEns2dHeuristicSeedExtension: public PredictorMfeEns2dSeedExten protected: + /** Freeze one preferred right endpoint for each admitted stack anchor. + * The existing virtual right-extension hooks determine the choice; partition + * contributions are deferred to the common seed-avoiding recurrence. + * @param n first active range length + * @param m second active range length + * @param seeds contained stack occurrences in the active range + */ + void prepareStackSeedExtensions(size_t n,size_t m,const StackSeedDomain & seeds) override; + /** @return the frozen right-endpoint selector for the approximate ensemble */ + std::function + stackSeedRightSelector() const override; + + //! Only rank right extensions while preparing canonical stack-seed choices. + bool selectingStackSeedRight = false; + //! Preferred local endpoint keyed by the local first pair of each seed. + std::unordered_map stackSeedRight; + //! access to the interaction energy handler of the super class using PredictorMfeEns2dSeedExtension::energy; diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp index 0b6eb098..33a5d99d 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp @@ -333,6 +333,16 @@ PredictorMfeEns2dSeedExtension::exactBoundaryWeight(size_t i1,size_t j1,size_t i return E_isINF(e)?Z_type(0):PartitionArithmetic::exp(-E_2_Z(e)/energy.getRT()); } +void +PredictorMfeEns2dSeedExtension::prepareStackSeedExtensions(size_t,size_t,const StackSeedDomain &) +{} + +std::function +PredictorMfeEns2dSeedExtension::stackSeedRightSelector() const +{ + return {}; +} + void PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const IndexRange & r2) { @@ -352,6 +362,8 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In const size_t span2=std::min(m,energy.getAccessibility2().getMaxLength()); StackSeedDomain seeds=seedCount ? StackSeedDomain(seedHandler,n,m,span1,span2) : StackSeedDomain(std::vector(),n,m,span1,span2); + // Freeze heuristic choices before computing the shared forward/outside objective. + prepareStackSeedExtensions(n,m,seeds); // Supply native weights once per initiation, transition and complete boundary. using K=SeededPartitionFunction; const K::Domain domain{n,m,span1,span2,energy.getMaxInternalLoopSize1(),energy.getMaxInternalLoopSize2(),output.getOutputConstraint().noLP}; @@ -361,10 +373,12 @@ PredictorMfeEns2dSeedExtension::predictStackSeeds(const IndexRange & r1,const In [&](K::Pair){return boltzmann(energy.getE_init());}, [&](K::Pair p,K::Pair q){return boltzmann(energy.getE_interLeft(p.first,q.first,p.second,q.second));}, [&](K::Pair p,K::Pair q){return exactBoundaryWeight(p.first,q.first,p.second,q.second);}, - [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p.first,q.first,p.second,q.second,h,b);} + [&](K::Pair p,K::Pair q,Z_type h,Z_type b){updateExactCompleteZ(p.first,q.first,p.second,q.second,h,b);}, + stackSeedRightSelector() }; const auto result=K::compute(domain,seeds,weights,pairProbabilities!=nullptr); // Custom updateZ overrides can change the objective even in release builds. + PartitionArithmetic::check(Zall); if (result.z!=Zall) throw std::logic_error("exact seeded updateZ override did not preserve the partition objective"); output.setExactPartition(true); reportOptima(); diff --git a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h index 0b344e4a..5c509295 100644 --- a/src/IntaRNA/PredictorMfeEns2dSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dSeedExtension.h @@ -6,6 +6,8 @@ #include "IntaRNA/BasePairProbabilities.h" #include "IntaRNA/Matrix.h" #include "IntaRNA/SeedHandlerIdxOffset.h" +#include "IntaRNA/StackSeedDomain.h" +#include #include namespace IntaRNA { @@ -17,6 +19,9 @@ namespace IntaRNA { * Note, for each seed start (i1,i2) only the mfe seed is considered for the * overall interaction computation instead of considering all possible seeds * starting at (i1,i2). + * Stack-only seeds use a seed-avoiding left extension and an unconstrained + * right extension at the first completed seed, counting each interaction once. + * Gapped seeds retain the legacy extension implementation. * * @author Frank Gelhausen * @author Martin Raden @@ -85,6 +90,19 @@ class PredictorMfeEns2dSeedExtension: public PredictorMfeEns { virtual Z_type exactBoundaryWeight(size_t i1,size_t j1,size_t i2,size_t j2) const; /** Run the selected disjoint stack-seed partition backend. */ void predictStackSeeds(const IndexRange & r1,const IndexRange & r2); + /** Prepare optional heuristic right endpoints for the admitted stack seeds. + * @param n first active range length + * @param m second active range length + * @param seeds contained seeds used by the partition recurrence + */ + virtual void prepareStackSeedExtensions(size_t n,size_t m,const StackSeedDomain & seeds); + /** Select one right endpoint per anchor for the heuristic retained ensemble. + * An empty function requests the full exact ensemble. Nonempty selectors use + * local increasing coordinates and receive the anchor's first and last pair. + * @return selector shared by the forward and outside recurrences + */ + virtual std::function + stackSeedRightSelector() const; //! Optional signed reverse trace for the legacy anchored extension recurrences. struct ExtensionProbabilityTrace; diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index 3c5113c0..8c7d2db9 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -1,120 +1,198 @@ #include "IntaRNA/SeededPartitionFunction.h" #include "IntaRNA/PartitionArithmetic.h" #include +#include +#include -// Seed-free suffix lengths plus two absorbing seeded states (singleton/run). -// Nonstack edges operate on aggregate totals, independently of seed length. namespace IntaRNA { -SeededPartitionFunction::Result +SeededPartitionFunction::Result SeededPartitionFunction:: -compute( - const IntaRNA::SeededPartitionFunction::Domain & d, - const IntaRNA::StackSeedDomain & seeds, - const IntaRNA::SeededPartitionFunction::Weights & w, bool pairs) +compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool pairs) { - using namespace IntaRNA; using A=PartitionArithmetic; - using Pair=SeededPartitionFunction::Pair; - SeededPartitionFunction::Result out; - // Empty seed families or zero spans admit no interaction, even with pair output. + using Ends=std::array; // pending lonely pair / stack-supported end + Result out; if (pairs) out.mass=Matrix(d.n,d.m,0); if (!seeds.maxSeedLength() || !d.span1 || !d.span2) return out; - // At least two length states are necessary for noLP even with singleton seeds. - const size_t cap=std::max(size_t(2),seeds.maxSeedLength()), s1=cap, s2=cap+1, states=cap+2; + + // Freeze heuristic choices before either pass. A first hit is owned by the + // shortest admitted seed ending here, including coincident variable lengths. + std::unordered_map selected; + if (w.selectRight) for(size_t i=0;i h(matrix_detail::product(n,m),states),dh; - Matrix> total(n,m),dt; + Matrix left(matrix_detail::product(n,m),cap),leftTotal(n,m); + Matrix hit(n,m); Matrix valid(n,m); Matrix ending(n,m); - if (pairs) { dh.resize(n*m,states); dt.resize(n,m); } - // Cache vertex admission and seed endings in this start's cropped domain. - for (size_t i=0;i=cap) return stacked || !d.noLP?s2:s1; - const size_t len=stacked?std::min(cap,c+2):1; - return accepts(i,j,len)?(len>=2 || !d.noLP?s2:s1):len-1; - }; - // Enumerate loop predecessors separately from the direct stacking edge. + auto accepts=[&](size_t i,size_t j,size_t len) { return ending(i,j) && ending(i,j)<=len; }; auto nonstack=[&](size_t i,size_t j,const auto & visit) { for(size_t u=1;u<=i && u-1<=d.loop1;++u) for(size_t v=1;v<=j && v-1<=d.loop2;++v) if((u!=1 || v!=1) && valid(i-u,j-v)) visit(i-u,j-v); }; - // Initiation belongs to the leftmost pair exactly once, including singletons. - const size_t initial=accepts(0,0,1)?(d.noLP?s1:s2):0; - const Z_type init=A::check(w.initiation(p)); h(0,initial)=init; - // Forward pass: extend prefixes and finalize every admitted right boundary. + auto transition=[&](size_t i,size_t j,size_t u,size_t v) { + return A::check(w.transition({p1+u,p2+v},{p1+i,p2+j})); + }; + auto extendLeft=[&](size_t i,size_t j,size_t len,Z_type weight) { + // An accepting edge leaves the seed-free recurrence permanently. The + // prefix already owns the seed's preceding pairs and their energies. + auto & value=accepts(i,j,len)?hit(i,j)[d.noLP && len==1?0:1]:left(i*m+j,len-1); + value=A::add(value,weight); + }; + // N: nonaccepting prefixes. F: first-hit injections, including singleton + // source hits. Initiation is owned by the source exactly once. + extendLeft(0,0,1,A::check(w.initiation(p))); for(size_t i=0;i0) extendLeft(i,j,std::min(cap,c+2),A::multiply(prefix,t)); } } - // Loop transitions need only the unseeded/seeded predecessor totals. nonstack(i,j,[&](size_t u,size_t v) { - if(total(u,v)[0]==0 && total(u,v)[1]==0) return; - const Z_type t=A::check(w.transition({p1+u,p2+v},{p1+i,p2+j})); - for(size_t c=0;c<2;++c) { - auto & value=h(q,dest(i,j,c? s2:0,false));value=A::add(value,A::multiply(total(u,v)[c],t)); - } + if(leftTotal(u,v)>0) extendLeft(i,j,1,A::multiply(leftTotal(u,v),transition(i,j,u,v))); }); - // Exclude lonely terminal pairs from loop predecessors and completed paths. - for(size_t c=d.noLP?1:0;c0) { - // Share the same boundary coefficient with reporting and the outside pass. - const Z_type b=A::check(w.boundary(p,{p1+i,p2+j})); - out.z=A::add(out.z,A::multiply(b,total(i,j)[1])); - if(pairs) { dh(q,s2)=b;if(!d.noLP) dh(q,s1)=b; } - if(w.complete) w.complete(p,{p1+i,p2+j},total(i,j)[1],b); + // A nonstack edge may leave only a supported end under noLP. + for(size_t c=d.noLP?1:0;c anchors; }; + std::vector groups; + groups.push_back({{n-1,m-1},true,{}}); + std::unordered_map byEnd; + Matrix hybrid(n,m),boundary(n,m); + Matrix dHit; + if(pairs) dHit.resize(n,m); + for(size_t i=0;i=n || end.second-p2>=m) continue; + const Pair local{end.first-p1,end.second-p2}; + auto [at,fresh]=byEnd.emplace(local,groups.size()); + if(fresh) groups.push_back({local,false,{}}); + groups[at->second].anchors.emplace_back(i,j); + } + auto fillRight=[&](const Group & group) { + Matrix right(group.end.first+1,group.end.second+1); + for(auto q:group.anchors) right(q.first,q.second)=hit(q.first,q.second); + // B is seed-unconstrained: no seed lookup or subtraction occurs here. + for(size_t i=0;i0 || prev[1]>0) + right(i,j)[1]=A::add(right(i,j)[1],A::multiply(A::add(prev[0],prev[1]),transition(i,j,i-1,j-1))); + } + nonstack(i,j,[&](size_t u,size_t v) { + if(right(u,v)[1]>0) { + auto & value=right(i,j)[d.noLP?0:1]; + value=A::add(value,A::multiply(right(u,v)[1],transition(i,j,u,v))); + } + }); + } + return right; + }; + Matrix right; + for(const auto & group:groups) { + if(group.anchors.empty()) continue; + right=fillRight(group); + if(group.all) { + for(size_t i=0;i0) { + const Pair q{p1+i,p2+j}; + const Z_type b=A::check(w.boundary(p,q)); boundary(i,j)=b; + out.z=A::add(out.z,A::multiply(hybrid(i,j),b)); + if(w.complete) w.complete(p,q,hybrid(i,j),b); } if(!pairs) return; - // Reverse pass: propagate boundary derivatives back through the same edges. + + // Reverse the retained right contractions. Recompute one group at a time + // to bound memory independently of the number of heuristic choices. + for(size_t g=0;g adjoint(right.size1(),right.size2()); + for(size_t ii=right.size1();ii>0;--ii) for(size_t jj=right.size2();jj>0;--jj) { + const size_t i=ii-1,j=jj-1; if(!valid(i,j)) continue; + auto & a=adjoint(i,j); + if(group.all || (i==group.end.first && j==group.end.second)) a[1]=A::add(a[1],boundary(i,j)); + // A vertex is owned by its most recent pair: T*dT counts every + // right-side pair, including the accepting pair, exactly once. + auto & mass=out.mass(p1+i,p2+j); + for(size_t c=0;c<2;++c) mass=A::add(mass,A::multiply(right(i,j)[c],a[c])); + if(i && j && valid(i-1,j-1)) { + const Z_type value=A::multiply(a[1],transition(i,j,i-1,j-1)); + for(size_t c=0;c<2;++c) adjoint(i-1,j-1)[c]=A::add(adjoint(i-1,j-1)[c],value); + } + nonstack(i,j,[&](size_t u,size_t v) { + adjoint(u,v)[1]=A::add(adjoint(u,v)[1],A::multiply(a[d.noLP?0:1],transition(i,j,u,v))); + }); + } + for(auto q:group.anchors) for(size_t c=0;c<2;++c) + dHit(q.first,q.second)[c]=A::add(dHit(q.first,q.second)[c],adjoint(q.first,q.second)[c]); + } + if(w.selectRight) for(size_t i=0;i dLeft(n*m,cap),dTotal(n,m); + auto destinationAdjoint=[&](size_t i,size_t j,size_t len) { + return accepts(i,j,len)?dHit(i,j)[d.noLP && len==1?0:1]:dLeft(i*m+j,len-1); + }; for(size_t ii=n;ii>0;--ii) for(size_t jj=m;jj>0;--jj) { const size_t i=ii-1,j=jj-1,q=i*m+j; if(!valid(i,j)) continue; - // Distribute derivatives of the two aggregate totals to their suffix states. - for(size_t c=d.noLP?1:0;c namespace IntaRNA { -/** Nonnegative stack-seed suffix inside/outside computation. A path is counted once - * if it contains any admitted seed. Native energy ownership is supplied by the +/** Nonnegative first-seed extension inside/outside computation. A path belongs + * to its first completed admitted seed; seed-free left prefixes are joined to + * unrestricted right extensions. Co-ending seeds use the shortest as owner. + * Native energy ownership is supplied by the * adapter: initiation once, one transition per successive pair, one boundary * factor. All callbacks must be deterministic; zero denotes forbidden weight. */ @@ -26,6 +28,13 @@ class SeededPartitionFunction { std::function initiation; std::function transition, boundary; std::function complete; + /** Optional frozen heuristic selector, called once per canonical anchor. + * Arguments are its start and end; the result is a retained right endpoint. + * Keep paths with no left extension, no right extension, or that endpoint. + * Empty means all right endpoints (exact). Selection cannot depend on pair + * marking; out-of-domain choices contribute no two-sided extension. + */ + std::function selectRight; }; /** Raw unnormalized result; pair masses remain empty in partition-only mode. */ struct Result { Z_type z=0; Matrix mass; }; diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp index ca486142..79f89a0b 100644 --- a/src/IntaRNA/StackSeedDomain.cpp +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -14,8 +14,9 @@ StackSeedDomain(const SeedHandler & handler, size_t n, size_t m, size_t w1, size #endif if (n==0 || m==0) return; - // Use the range end as an invalid initial cursor, avoiding lastPos+offset wrap. - size_t i=n, j=m; + // A cropped range end can coincide with an explicit seed outside the range. + // The remaining sequence end is always invalid, also after offset translation. + size_t i=handler.getInteractionEnergy().size1(), j=handler.getInteractionEnergy().size2(); while (handler.updateToNextSeed(i,j,0,n-1,0,m-1)) { // Stack-only admission guarantees equal strand lengths internally. const size_t len=handler.getSeedLength1(i,j); @@ -43,9 +44,7 @@ add(Occurrence s, size_t n, size_t m, size_t w1, size_t w2) { // A start must identify one retained length, including for explicit input. auto [p,inserted]=lengths.emplace(Interaction::BasePair(s.i,s.j),s.length); -#if INTARNA_IN_DEBUG_MODE if (!inserted && p->second!=s.length) throw std::invalid_argument("multiple retained seed lengths at one start"); -#endif // The shortest seed ending here suffices to accept a stacked path suffix. auto [end, fresh]=endingLengths.emplace(Interaction::BasePair(s.i+s.length-1,s.j+s.length-1),s.length); diff --git a/tests/PredictorHeuristicSeededProbability_test.cpp b/tests/PredictorHeuristicSeededProbability_test.cpp index 6a15c116..94591c5f 100644 --- a/tests/PredictorHeuristicSeededProbability_test.cpp +++ b/tests/PredictorHeuristicSeededProbability_test.cpp @@ -1,4 +1,5 @@ #include "catch.hpp" +#include "SeededChainOracle.h" #include "IntaRNA/AccessibilityDisabled.h" #include "IntaRNA/InteractionEnergyBasePair.h" #include "IntaRNA/OutputHandlerInteractionList.h" @@ -111,6 +112,64 @@ TEST_CASE("seeded heuristic outside agrees with independent pair fugacities", "[ } } +TEST_CASE("stack seeded heuristic and exact native marginals match independent retained paths", "[BasePairProbabilities][HeuristicSeededProbability][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + using namespace seeded_oracle; + RnaSequence t("target","GGGGGG"),q("query","CCCCCC"); + AccessibilityDisabled at(t,5,nullptr),aq(q,6,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,1); + const std::vector> families{ + {{{1,1},{2,2}}}, + {{{1,1},{2,2}},{{2,2},{3,3}}}, + {{{0,0},{1,1}},{{2,2},{3,3}}}, + {{{0,0},{1,1}},{{4,4},{5,5}}}, + {{{1,1},{2,2},{3,3}},{{2,2},{3,3}},{{4,4},{5,5}}} + }; + for(const auto & seeds:families) for(bool noLP:{false,true}) { + std::string patterns; + for(const auto & seed:seeds) { + if(!patterns.empty()) patterns+=','; + const std::string pairs(seed.size(),'|'); + patterns+=std::to_string(seed.front().first+1)+pairs+'&' + +std::to_string(q.size()-seed.back().second)+pairs; + } + CAPTURE(patterns,noLP); + const auto sc=explicitSeeds(patterns); + const auto oc=constraints(noLP); + BasePairProbabilities result(t.size(),q.size()); + OutputHandlerInteractionList output(oc,0); + FixedChoiceSeededHeuristic predictor(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),&result); + predictor.predict();result.finalize(); + Model model{6,6,5,6,noLP,seeds,[](Pair){return true;}, + [](Pair){return std::exp(1.L);}, + [](Pair p,Pair r){return r.first-p.first<=3 && r.second-p.second<=2?std::exp(1.L):0.L;}, + [](Pair,Pair){return 1.L;}}; + const auto exact=enumerate(model); + model.selectRight=[&](Pair start,Pair){ + const auto & choice=predictor.choices.at({start.first,start.second}); + return Pair{choice.j1,choice.j2}; + }; + const auto retained=enumerate(model); + REQUIRE(retained.z>0); + REQUIRE(retained.z<=exact.z); + REQUIRE(result.isApproximate()); + REQUIRE(double(result.getZ())==Approx(double(retained.z)).epsilon(2e-12)); + for(size_t i=0;isecond)).epsilon(2e-12)); + } + BasePairProbabilities exactResult(t.size(),q.size()); + OutputHandlerInteractionList exactOutput(oc,0); + PredictorMfeEns2dSeedExtension exactPredictor(energy,exactOutput,nullptr,new SeedHandlerExplicit(energy,sc),&exactResult); + exactPredictor.predict();exactResult.finalize(); + REQUIRE(double(exactResult.getZ())==Approx(double(exact.z)).epsilon(2e-12)); + for(size_t i=0;isecond)).epsilon(2e-12)); + } + } +} + TEST_CASE("seeded heuristic probabilities handle original coordinates regions and empty domains", "[BasePairProbabilities][HeuristicSeededProbability]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGGGGG"),q("q","CCCCCC"); diff --git a/tests/PredictorMfeEnsRegression_test.cpp b/tests/PredictorMfeEnsRegression_test.cpp index e4f55379..c5003bbc 100644 --- a/tests/PredictorMfeEnsRegression_test.cpp +++ b/tests/PredictorMfeEnsRegression_test.cpp @@ -242,7 +242,9 @@ TEST_CASE("ensemble predictor regressions", "[PredictorMfeEns]") { InspectableEnsemblePredictor heuristic( energy, heuristicOut, new SeedHandlerNoBulge(energy, seedConstraint)); heuristic.predict(); - REQUIRE(heuristic.getPartitionCount() > 0); + // The anchored heuristic also finalizes each retained boundary once. + REQUIRE(heuristic.getPartitionCount() == 0); + REQUIRE(heuristic.getZall() > 0); heuristic.predict(IndexRange(0, 0), IndexRange(0, 0)); REQUIRE(heuristic.getPartitionCount() == 0); REQUIRE(heuristic.getZall() == 0.0); diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 27779cc9..42523506 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -465,8 +465,9 @@ void requireUniqueAnchorEnsemblePartition(const InteractionEnergy & energy, const OutputConstraint & outputConstraint, const IndexRange & range1, const IndexRange & range2, const SeedOracleResult & oracle) { - // Seed-extension Z accounting is only asserted in this proved-trivial - // domain. General multi-anchor X-mode Z is not an exact-oracle contract. + // This helper also checks gapped seeds, whose legacy partition path is + // asserted only in a unique-anchor domain. Stack-only multi-anchor cases + // have independent exact partition and marginal checks below. REQUIRE(oracle.interactions.size() == 1); REQUIRE(oracle.interactions.front().seeds.size() == 1); OutputHandlerInteractionList output(outputConstraint, 1); @@ -782,6 +783,48 @@ TEST_CASE("raw native pair masses share the exact forward denominator", "[BasePa REQUIRE(result.rawMasses()(i,j)==Approx(expected(i,j)).epsilon(2e-12)); } } +TEST_CASE("native first-hit marginals respect filtered seeds wobble ends and active ranges", "[BasePairProbabilities][PredictorSeedOracle][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("target","GUGGGUG"),q("query","CGCCGUC"); + AccessibilityBasePair at(t,6,nullptr),aq(q,5,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,1); + IndexRangeList seedRange1,seedRange2; + seedRange1.push_back({2,5});seedRange2.push_back({2,5}); + for(bool noLP:{false,true}) for(bool noGUend:{false,true}) + for(bool noSeedGU:{false,true}) for(size_t offset:{0,1}) { + CAPTURE(noLP,noGUend,noSeedGU,offset); + const SeedConstraint sc(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + seedRange1,seedRange2,"",noSeedGU,false,false); + const OutputConstraint oc(1,OutputConstraint::OVERLAP_BOTH,E_INF,E_INF, + false,noLP,noGUend,true,false); + const IndexRange r1(offset,6),r2(offset,6); + const auto oracle=enumerateSeededInteractions(energy,sc,oc,r1,r2); + REQUIRE(oracle.unionPartition>0); + BasePairProbabilities result(t.size(),q.size()); + OutputHandlerInteractionList output(oc,1); + ExactSeedProbe p(energy,output,nullptr,new SeedHandlerMfe(energy,sc),&result); + p.predict(r1,r2);result.finalize(); + REQUIRE(result.getZ()==Approx(oracle.unionPartition).epsilon(2e-12)); + Matrix masses(t.size(),q.size(),0); + std::map,Z_type> boundaries; + for(const auto & interaction:oracle.interactions) { + const Z_type weight=energy.getBoltzmannWeight(interaction.energy); + const auto & first=interaction.chain.front();const auto & last=interaction.chain.back(); + boundaries[{first.i1-offset,first.i2-offset,last.i1-offset,last.i2-offset}]+=weight; + for(const auto & pair:interaction.chain) { + const auto bp=energy.getBasePair(pair.i1,pair.i2); + masses(bp.first,bp.second)+=weight; + } + } + for(size_t i=0;i masses(6,6,0); + for(size_t offset:{0,3}) for(const auto & [pair,weight]:expected.mass) + masses(offset+pair.first,5-offset-pair.second)=Z_type(weight); + for(size_t i=0;i<6;++i) for(size_t j=0;j<6;++j) + REQUIRE(result.rawMasses()(i,j)==Approx(masses(i,j)).epsilon(2e-12)); +} + TEST_CASE("native singleton and mixed explicit seeds count overlapping witnesses once", "[BasePairProbabilities][PredictorSeedOracle]") { #include "testEasyLoggingSetup.icc" using namespace seeded_oracle; diff --git a/tests/SeededChainOracle.h b/tests/SeededChainOracle.h index 6ed0858e..d8b05523 100644 --- a/tests/SeededChainOracle.h +++ b/tests/SeededChainOracle.h @@ -22,11 +22,13 @@ struct Model { std::function valid; std::function init; std::function edge, boundary; + // Optional frozen heuristic endpoint for the first completed declared seed. + std::function selectRight; }; struct Result { long double z = 0, unseeded = 0, pairCount = 0; std::map boundaries; - std::map mass; + std::map mass, spots; }; inline bool stack(Pair a, Pair b) { return a.first+1 == b.first && a.second+1 == b.second; @@ -40,17 +42,28 @@ inline Result enumerate(const Model & model) { for (size_t k=0; k0 && stack(chain[k-1], chain[k])) || (k+1size())) { + owner=&seed;firstHit=hit; + } + } if (!model.noLP || noLP) { const auto w = hybrid * model.boundary(p, q); - if (seeded) { + const bool retained=owner && (!model.selectRight || p==owner->front() + || q==owner->back() || q==model.selectRight(owner->front(),owner->back())); + if (retained) { out.z += w; out.boundaries[{p.first, p.second, q.first, q.second}] += w; out.pairCount += w * chain.size(); for (Pair pair : chain) out.mass[pair] += w; - } else out.unseeded += w; + for (size_t i=p.first; i<=q.first; ++i) + for (size_t j=p.second; j<=q.second; ++j) out.spots[{i,j}] += w; + } else if (!owner) out.unseeded += w; } for (size_t i=q.first+1; i boundaries; - w.complete=[&](auto p,auto q,auto h,auto b){boundaries[{p.first,p.second,q.first,q.second}]+=h*b;}; + std::map calls; + w.complete=[&](auto p,auto q,auto h,auto b){ + const seeded_oracle::Boundary boundary{p.first,p.second,q.first,q.second}; + boundaries[boundary]+=h*b; + ++calls[boundary]; + }; auto got=suffix?Kernel::compute(d,seeds,w,true):seededStack(d,seeds,w,true); REQUIRE(double(got.z)==Approx(double(oracle.z)).epsilon(2e-12)); - for(auto [b,z]:oracle.boundaries) REQUIRE(double(boundaries[b])==Approx(double(z)).epsilon(2e-12)); + for(auto [b,z]:oracle.boundaries) { + const auto found=boundaries.find(b); + REQUIRE(double(found==boundaries.end()?0:found->second)==Approx(double(z)).epsilon(2e-12)); + } + for(auto [b,z]:boundaries) { + const auto expected=oracle.boundaries.find(b); + REQUIRE(double(z)==Approx(double(expected==oracle.boundaries.end()?0:expected->second)).epsilon(2e-12)); + REQUIRE(calls[b]==1); + } Z_type expectedCount=0; + std::vector rows(m.n,0),columns(m.m,0); for(size_t i=0;isecond)).epsilon(2e-12)); + Z_type spot=0; + for(auto [b,z]:boundaries) + if(b[0]<=i && i<=b[2] && b[1]<=j && j<=b[3]) spot+=z; + const auto expectedSpot=oracle.spots.find({i,j}); + REQUIRE(double(spot)==Approx(double(expectedSpot==oracle.spots.end()?0:expectedSpot->second)).epsilon(2e-12)); + REQUIRE(got.mass(i,j)>=0); + REQUIRE(got.mass(i,j)<=spot+std::abs(got.z)*2e-12); + REQUIRE(spot<=got.z+std::abs(got.z)*2e-12); + rows[i]+=got.mass(i,j); + columns[j]+=got.mass(i,j); expectedCount+=got.mass(i,j); } + for(const auto mass:rows) REQUIRE(mass<=got.z+std::abs(got.z)*2e-12); + for(const auto mass:columns) REQUIRE(mass<=got.z+std::abs(got.z)*2e-12); REQUIRE(double(expectedCount)==Approx(double(oracle.pairCount)).epsilon(2e-12)); auto only=suffix?Kernel::compute(d,seeds,w,false):seededStack(d,seeds,w,false); REQUIRE(only.z==got.z); @@ -113,8 +142,151 @@ TEST_CASE("heterogeneous weighted kernels match independent enumeration", "[Seed m.seeds.push_back(seed); } compareKernels(m); + if(trial<40) { + m.selectRight=[trial](Pair p,Pair q){return Pair{ + std::min(size_t(5),q.first+(p.first+trial)%3), + std::min(size_t(4),q.second+(p.second+trial)%2)};}; + compareKernels(m); + } + } +} +TEST_CASE("first seed acceptance handles source hits overlapping and separated anchors", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + // The unseeded prefixes and unrestricted suffixes include skipped diagonal + // pairs, so an interior spot need not be an actual interaction pair. + Model m{8,9,8,9,false,{},[](Pair p){return p.first==p.second;}, + [](Pair p){return 1.1L+.1L*p.first;}, + [](Pair p,Pair q){return stack(p,q)?.7L:.2L;}, + [](Pair p,Pair q){return .4L+.03L*p.first+.07L*q.second;}}; + const std::vector>> cases{ + {"empty family",{}}, + {"source singleton",{{{0,0}}}}, + {"later singleton",{{{3,3}}}}, + {"two-pair anchor",{{{2,2},{3,3}}}}, + {"overlapping anchors",{{{1,1},{2,2}},{{2,2},{3,3}}}}, + {"adjacent anchors",{{{1,1},{2,2}},{{3,3},{4,4}}}}, + {"separated anchors",{{{0,0},{1,1}},{{5,5},{6,6}}}}, + {"filtered earlier stacks",{{{4,4},{5,5}}}}, + {"shared accepting pair",{{{1,1},{2,2},{3,3}},{{2,2},{3,3}},{{3,3}}}}, + {"length-seven overlap",{{{0,0},{1,1},{2,2},{3,3},{4,4},{5,5},{6,6}}, + {{1,1},{2,2},{3,3},{4,4},{5,5},{6,6},{7,7}}}} + }; + for(const auto & [name,seeds]:cases) for(bool noLP:{false,true}) { + CAPTURE(name,noLP); + m.seeds=seeds;m.noLP=noLP; + compareKernels(m); + } + // A singleton source is the entire interaction, and noLP rejects it. + m.n=1;m.m=1;m.span1=1;m.span2=1;m.seeds={{{0,0}}}; + for(bool noLP:{false,true}) { m.noLP=noLP;compareKernels(m); } + m.valid=[](Pair){return false;}; + compareKernels(m); +} + +TEST_CASE("first-hit pair masses are derivatives of one frozen declared seed family", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + Model m{6,7,5,6,false,{{{1,1},{2,2}},{{2,2},{3,3}},{{4,4}}}, + [](Pair p){return p.first==p.second || p.first+1==p.second;}, + [](Pair p){return .8L+.07L*p.first;}, + [](Pair p,Pair q){return .2L+.1L*(q.first-p.first)+.03L*(q.second-p.second);}, + [](Pair p,Pair q){return .4L+.02L*p.second+.05L*q.first;}}; + const auto seeds=domainFor(m); + for(bool noLP:{false,true}) { + const Kernel::Domain d{m.n,m.m,m.span1,m.span2,m.n,m.m,noLP}; + const auto baseline=Kernel::compute(d,seeds,weightsFor(m),true); + for(size_t i=0;i0); + REQUIRE(retained.z0); + REQUIRE(double(tiny.z/expected)==Approx(1).epsilon(2e-12)); + REQUIRE(tiny.mass(0,0)==tiny.z); + REQUIRE(tiny.mass(1,1)==tiny.z); + for(Z_type invalid:{Z_type(-1),std::numeric_limits::infinity(),std::numeric_limits::quiet_NaN()}) { + CAPTURE(invalid); + auto weights=weightsFor(m); + weights.initiation=[invalid](Pair){return invalid;}; + REQUIRE_THROWS_AS(Kernel::compute(d,seeds,weights,true),std::range_error); + weights=weightsFor(m); + weights.transition=[invalid](Pair,Pair){return invalid;}; + REQUIRE_THROWS_AS(Kernel::compute(d,seeds,weights,true),std::range_error); + weights=weightsFor(m); + weights.boundary=[invalid](Pair,Pair){return invalid;}; + REQUIRE_THROWS_AS(Kernel::compute(d,seeds,weights,true),std::range_error); + } + for(bool overflow:{false,true}) { + auto weights=weightsFor(m); + weights.initiation=[overflow](Pair){return overflow?std::numeric_limits::max():std::numeric_limits::min();}; + weights.transition=[overflow](Pair,Pair){return overflow?Z_type(2):Z_type(.1);}; + REQUIRE_THROWS_AS(Kernel::compute(d,seeds,weights,false),std::range_error); + } + // A forbidden accepting edge is exact zero, never a tiny positive mass. + auto zero=weightsFor(m); + zero.transition=[](Pair,Pair){return Z_type(0);}; + zero.boundary=[](Pair,Pair)->Z_type{throw std::logic_error("unreachable boundary");}; + const auto empty=Kernel::compute(d,seeds,zero,true); + REQUIRE(empty.z==0); + REQUIRE(empty.mass(0,0)==0); + REQUIRE(empty.mass(1,1)==0); +} + TEST_CASE("checked partition arithmetic distinguishes empty and range failure", "[SeededPartitionFunction]") { using A=IntaRNA::PartitionArithmetic; REQUIRE(A::add(0,1e-100)==Z_type(1e-100)); diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index be595ada..2e3b6c26 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -76,6 +76,26 @@ TEST_CASE("stack-domain lookups and traversal preserve overlapping seed geometry REQUIRE_THROWS_AS((StackSeedDomain({{1,1,1},{1,1,2}},4,4,4,4)),std::invalid_argument); } +TEST_CASE("stack-domain iteration starts before explicit seeds outside the active region", "[StackSeedDomain]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("target","GGGGGG"),q("query","CCCCCC"); + AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar); + const SeedConstraint sc(5,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1||&5||,4||&2||",false,false,false); + for(size_t offset:{0,3}) { + CAPTURE(offset); + SeedHandlerIdxOffset handler(new SeedHandlerExplicit(energy,sc)); + handler.setOffset1(offset);handler.setOffset2(offset); + REQUIRE(handler.fillSeed(0,2,0,2)==1); + // At offset zero, the local range size (3,3) is an actual explicit + // seed start outside the range and cannot be used as a fresh cursor. + const StackSeedDomain domain(handler,3,3,3,3); + REQUIRE(domain.seedLength(0,0)==2); + REQUIRE(domain.maxSeedLength()==2); + } +} + TEST_CASE("stack-domain admission preserves handler-specific threshold equality", "[StackSeedDomain]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGG"),q("q","CCC"); @@ -98,7 +118,7 @@ TEST_CASE("stack-domain admission preserves handler-specific threshold equality" REQUIRE(StackSeedDomain(ex,3,3,3,3).maxSeedLength()==2); } -TEST_CASE("explicit stack capability inspects the retained minimum-energy pattern", "[StackSeedDomain]") { +TEST_CASE("explicit stack capability remains conservative after minimum-energy selection", "[StackSeedDomain]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGGG"),q("q","CCCC"); AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); @@ -108,7 +128,18 @@ TEST_CASE("explicit stack capability inspects the retained minimum-energy patter SeedHandlerExplicit handler(e,sc); REQUIRE(!handler.guaranteesStackOnlySeeds()); REQUIRE(handler.fillSeed(0,3,0,3)==1); - StackSeedDomain domain(handler,4,4,4,4); + REQUIRE(handler.getSeedLength1(0,0)==3); + REQUIRE(handler.getSeedLength2(0,0)==3); + // A retained stack does not override the handler's conservative capability. +#if INTARNA_IN_DEBUG_MODE + REQUIRE_THROWS_AS((StackSeedDomain(handler,4,4,4,4)),std::invalid_argument); +#endif + SeedConstraint stackOnly(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1|||&2|||",false,false,false); + SeedHandlerExplicit stacked(e,stackOnly); + REQUIRE(stacked.guaranteesStackOnlySeeds()); + REQUIRE(stacked.fillSeed(0,3,0,3)==1); + StackSeedDomain domain(stacked,4,4,4,4); REQUIRE(domain.seedLength(0,0)==3); REQUIRE(domain.maxSeedLength()==3); // constraint's minimum BP is only two } From 985ba44bdc16e92162e954b7baffa3f3923d4776 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Thu, 15 Oct 2026 00:18:59 +0200 Subject: [PATCH 23/30] perf: reuse seed-extension partition workspaces --- ChangeLog | 22 +++ doc/Makefile.am | 7 +- doc/analysis/seed-extension-partition.md | 47 ++++- doc/benchmarks/seed-extension/README.md | 66 ++++++- doc/benchmarks/seed-extension/build.py | 2 +- doc/benchmarks/seed-extension/driver.cpp | 30 ++- .../seed-extension/reuse-exact-results.json | 179 ++++++++++++++++++ .../seed-extension/reuse-exact-samples.tsv | 81 ++++++++ .../reuse-heuristic-results.json | 179 ++++++++++++++++++ .../reuse-heuristic-samples.tsv | 81 ++++++++ .../seed-extension/reuse-source-hashes.json | 19 ++ doc/benchmarks/seed-extension/run.py | 12 +- ...redictorMfeEns2dHeuristicSeedExtension.cpp | 7 +- src/IntaRNA/SeededPartitionFunction.cpp | 98 +++++++--- ...edictorHeuristicSeededProbability_test.cpp | 33 ++++ tests/SeededPartitionFunction_test.cpp | 30 +++ 16 files changed, 847 insertions(+), 46 deletions(-) create mode 100644 doc/benchmarks/seed-extension/reuse-exact-results.json create mode 100644 doc/benchmarks/seed-extension/reuse-exact-samples.tsv create mode 100644 doc/benchmarks/seed-extension/reuse-heuristic-results.json create mode 100644 doc/benchmarks/seed-extension/reuse-heuristic-samples.tsv create mode 100644 doc/benchmarks/seed-extension/reuse-source-hashes.json diff --git a/ChangeLog b/ChangeLog index dd36858e..18a38d61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- reuse maximal seed-extension workspaces for exact/heuristic partitions, + endpoint selection and pair-probability differentiation (PR #266 review) + - compute exact and heuristic stack-seeded ensembles by first-seed ownership, seed-avoiding left prefixes and unrestricted right extensions; differentiate the same nonnegative retained ensemble for actual-pair probabilities (#264) @@ -201,6 +204,25 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261015 Alexander Mitrofanov + * SeededPartitionFunction.cpp + * Allocate prefix, first-hit, right-extension and outside matrices once per + computation at the inclusive span bounds. Use active upper index bounds + and reset active cells before accumulation, including shrink/regrowth. + * Reuse flat anchor/group storage and an endpoint index instead of creating + per-group vectors and hash nodes; omit outside buffers in partition mode. + * PredictorMfeEns2dHeuristicSeedExtension.cpp + * Reuse the maximal right-extension buffer while freezing native heuristic + endpoints, including explicit seeds of different lengths. + * SeededPartitionFunction_test.cpp, PredictorHeuristicSeededProbability_test.cpp + + Compare changing workspace shapes with independent chain enumeration and + compare mixed-seed endpoint selection with isolated single-seed predictions. + * doc/analysis/seed-extension-partition.md, doc/benchmarks/seed-extension/*, + doc/Makefile.am + + Document workspace bounds and reproduce exact/heuristic allocation counts, + requested bytes, runtime, RSS and numerical comparisons against PR #266. + * Address https://github.com/BackofenLab/IntaRNA/pull/266#issuecomment-6176325864. + 261014 Alexander Mitrofanov * SeededPartitionFunction.* * Replace the combined absorbing-state recurrence by separate seed-free diff --git a/doc/Makefile.am b/doc/Makefile.am index 701676fc..59c333a6 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -32,4 +32,9 @@ EXTRA_DIST += benchmarks/seed-extension/README.md \ benchmarks/seed-extension/driver.cpp \ benchmarks/seed-extension/recorded-results.json \ benchmarks/seed-extension/recorded-samples.tsv \ - benchmarks/seed-extension/recorded-source-hashes.json + benchmarks/seed-extension/recorded-source-hashes.json \ + benchmarks/seed-extension/reuse-exact-results.json \ + benchmarks/seed-extension/reuse-heuristic-results.json \ + benchmarks/seed-extension/reuse-exact-samples.tsv \ + benchmarks/seed-extension/reuse-heuristic-samples.tsv \ + benchmarks/seed-extension/reuse-source-hashes.json diff --git a/doc/analysis/seed-extension-partition.md b/doc/analysis/seed-extension-partition.md index 6addca19..488ec4b6 100644 --- a/doc/analysis/seed-extension-partition.md +++ b/doc/analysis/seed-extension-partition.md @@ -242,7 +242,50 @@ memory improvements require measurements against the previous implementation with identical inputs, options, build configuration, and thread count; the factorization alone is not a performance claim. -## Measured exact-kernel tradeoff +## Reusable extension workspaces + +The allocation revision requested in [PR #266](https://github.com/BackofenLab/IntaRNA/pull/266#issuecomment-6176325864) +keeps the recurrence and its arithmetic order unchanged. One invocation allocates +its prefix, first-hit, right-extension and (when requested) outside matrices at +`min(span1,n)` by `min(span2,m)`, with the prefix run-length dimension retained. +The inclusive maximum extension offset is `span-1`; storing offset zero requires +`span` cells. This bound covers singleton and mixed-length explicit seeds without +assuming the configured nominal seed length. These shared contractions include +the source and accepting pairs, so reducing the cell count to `span-1` would omit +valid boundaries. + +Each left boundary or endpoint group uses its own upper index bounds within the +same fixed-size buffers. Accumulation buffers are cleared over their active +rectangle; validity and seed-ending tables are fully overwritten there. This also +resets cells when a rectangle shrinks at a sequence end and grows at the next +row. Right contractions and their adjoints share one reusable buffer each across +all endpoint groups. Partition-only calls omit the boundary/outside buffers. + +Groups retain their first-encounter order. A reusable flat anchor list and dense +local endpoint index replace per-group vectors and hash nodes. The native +heuristic selector also preallocates its right-extension table once and overwrites +each active rectangle, preserving the extension hooks and their logical bounds. +Workspaces are local to the invocation (or predictor for native selection), with +no global shared mutable storage. Gapped-seed fallback behavior is unchanged. + +Preallocation retains the worst-case span capacity for the duration of the call; +it does not reduce the asymptotic live-memory bound and can retain more memory +than a particular small active rectangle requires. The goal is to remove repeated +allocation/destruction. The [benchmark](../benchmarks/seed-extension/README.md) +records allocation calls and cumulative requested bytes separately from process +RSS and runtime. + +The reuse revision passes `make tests -j2` in GCC 14.4 release and debug builds: +all seven test drivers pass, with 108 API cases and 188,838 / 188,839 assertions. +The added regressions compare changing active rectangles with independent path +enumeration and mixed-length native selector choices with isolated seeds. +Exact/heuristic benchmark dumps match `392357c` byte-for-byte across 599,242 +entries; 12 native CLI predictions and pair-probability matrices also match. + +## Original factorization measurements + +These measurements describe revision `392357c`, before the workspace reuse +revision above; the benchmark README records the newer allocation comparison. The [reproducible kernel benchmark](../benchmarks/seed-extension/README.md) compares the exact backend with revision @@ -271,7 +314,7 @@ could run on the workstation. These measurements characterize the cost of this factorization on the stated fixtures and do not establish a general runtime or memory bound. -## Validation of the delivered implementation +## Original factorization validation GCC 14.4 release and `--enable-debug` builds both pass `make tests -j2`: all seven API/CLI suites pass in each build. The API suite reports 106 cases with diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md index 5dd86345..3153fcf6 100644 --- a/doc/benchmarks/seed-extension/README.md +++ b/doc/benchmarks/seed-extension/README.md @@ -1,11 +1,17 @@ # Seed-extension kernel comparison -This Linux benchmark compares the exact seeded kernel at -`cb1c9d419f96c7d2a10bae864d922dcb0234f4d4` with the current source checkout. +This Linux benchmark compares the seeded kernel at PR #266 revision +`392357c00f0314659cd4c665446f3f6185f4814a` with the current source checkout. +It runs the exact ensemble by default; `--heuristic` uses frozen right-endpoint +groups to exercise their forward and outside workspace reuse. It first checks every reported boundary hybrid weight and boundary coefficient, the total partition, and every raw pair numerator. It then times partition-only and pair-output calculations in separate processes, alternating baseline and -candidate order. Three repetitions are the default. +candidate order. Three repetitions are the default. Each timed invocation also +counts ordinary C++ allocation calls and cumulative requested bytes during +`compute()`, including callbacks but excluding seed construction and serialization. +The benchmark replaces ordinary `new`/`new[]`; all measured types use fundamental +alignment. These counters measure allocation traffic, not peak live memory. Configure the source tree first so `src/IntaRNA/intarna_config.h` is available. Requirements are a C++23 GCC toolchain, the project's headers and Boost headers, @@ -22,7 +28,10 @@ For dependencies outside the compiler's search paths, add `--include /path/to/dependencies/include` to the build command. An optional `--runtime-library-dir /path/to/compiler/lib` embeds a runtime library search path. Both options can be repeated. `--baseline` selects another Git revision -when a different comparison is intended. +when a different comparison is intended; the current driver requires the +first-acceptance kernel API introduced in #266. To measure heuristic groups, use +`--heuristic` on the runner. Keep separate build directories for exact and +heuristic results, since the runner replaces its output files. The build script snapshots both versions of `SeededPartitionFunction` and `StackSeedDomain`, including their headers; other project headers and configured @@ -39,7 +48,10 @@ inclusive interaction spans of 24 on each strand, loop limits of three unpaired bases per strand, and stack seeds of length four. The many-seed cases admit all contained seeds on valid diagonals: 1,369 dense or 387 banded occurrences. The few-seed cases have three diagonal seeds starting at `n/4`, `n/2`, and `3n/4`. -`noLP` is disabled; separate oracle tests validate that restriction. +`noLP` is disabled; separate oracle tests validate that restriction. Heuristic +runs select `(min(n-1,q.first+2+q.second%3), min(n-1,q.second+2+q.first%3))` for +anchor end `q`, creating both shared and distinct retained endpoints. Selection +is frozen and identical in both versions. `driver.cpp` declares deterministic coordinate-dependent initiation, transition, and full-boundary weights. It measures the complete kernel invocation, including @@ -47,7 +59,14 @@ a small boundary callback. Seed construction, native thermodynamic/accessibility computation, and serialization are outside this timing. These fixtures exercise the recurrence, rather than representing complete biological workloads. -Recorded runs used GCC 14.4.0, `-std=c++23 -O3 -DNDEBUG`, one thread, and an +## Historical factorization measurements + +The `recorded-*` files retain the original exact-kernel comparison between +`cb1c9d419f96c7d2a10bae864d922dcb0234f4d4` and `392357c`, before workspace reuse. +Those runs used the original driver saved by its source hash, without allocation +instrumentation. Their timing does not measure this allocation revision. + +Those recorded runs used GCC 14.4.0, `-std=c++23 -O3 -DNDEBUG`, one thread, and an AMD Ryzen 5 7530U on 2026-10-14. Samples were not CPU-pinned and other build/test work could run on the same workstation. Treat small differences as noise. `recorded-results.json` contains correctness differences and timing medians with @@ -58,3 +77,38 @@ GNU time records whole-process maximum RSS, including runtime libraries and allocator overhead. These small fixtures do not isolate the recurrence's peak allocation. Timings and RSS are local observations, not asymptotic bounds. See the [algorithm and measured tradeoff](../../analysis/seed-extension-partition.md). + +## Workspace reuse measurements (PR #266 review) + +On 2026-10-15, five alternating fresh-process samples per fixture/mode used +the same GCC 14.4.0 flags and workstation as above. No project build or test +was running during these samples; processes were not CPU-pinned and other +workstation activity was uncontrolled. Both variants include the allocation +counters. `reuse-exact-*` and `reuse-heuristic-*` contain results and samples; +`reuse-source-hashes.json` identifies the measured sources. + +All eight full dumps are byte-identical: 599,242 boundary/partition/pair entries. +The allocation counts below exclude dump collection (timed runs pass `-`). +The remaining calls include the frozen selector map, eligible-start traversal +and initial workspace/result allocation. + +| Ensemble | Fixture | Partition allocations, base → reuse | Pair allocations, base → reuse | +| --- | --- | ---: | ---: | +| exact | dense40-many | 23,025 → 21 | 28,502 → 27 | +| exact | dense40-few | 7,376 → 13 | 10,217 → 19 | +| exact | band60-many | 6,384 → 19 | 7,933 → 25 | +| exact | band60-few | 3,139 → 12 | 4,374 → 18 | +| heuristic | dense40-many | 418,631 → 1,399 | 554,966 → 1,405 | +| heuristic | dense40-few | 8,774 → 18 | 12,167 → 24 | +| heuristic | band60-many | 84,243 → 413 | 114,136 → 419 | +| heuristic | band60-few | 4,080 → 17 | 5,658 → 23 | + +Median candidate/base runtime ratios range from 0.943 to 1.058; +median process RSS is 3,840–4,352 KiB. These measurements show the +allocation reduction, with broadly similar runtime and process RSS on these +small fixtures. They do not establish a general speedup or a peak-memory +reduction. For example, dense heuristic pair output reduces cumulative requested +bytes from 817,421,572 to 281,640, while retaining maximum-sized workspaces. + +To reproduce the recorded sample count, pass `--repetitions 5` to the runner. +Run once without `--heuristic` and once with it, using separate build directories. diff --git a/doc/benchmarks/seed-extension/build.py b/doc/benchmarks/seed-extension/build.py index 73f71d2c..aec1f5aa 100644 --- a/doc/benchmarks/seed-extension/build.py +++ b/doc/benchmarks/seed-extension/build.py @@ -14,7 +14,7 @@ def main(): parser.add_argument("--output", type=Path, required=True, help="directory for source snapshots and executables") parser.add_argument("--cxx", default="g++", help="C++23 compiler executable") - parser.add_argument("--baseline", default="cb1c9d419f96c7d2a10bae864d922dcb0234f4d4") + parser.add_argument("--baseline", default="392357c00f0314659cd4c665446f3f6185f4814a") parser.add_argument("--include", type=Path, action="append", default=[], help="extra dependency include directory; repeat as needed") parser.add_argument("--runtime-library-dir", type=Path, action="append", default=[], diff --git a/doc/benchmarks/seed-extension/driver.cpp b/doc/benchmarks/seed-extension/driver.cpp index b93854bc..1aaeb912 100644 --- a/doc/benchmarks/seed-extension/driver.cpp +++ b/doc/benchmarks/seed-extension/driver.cpp @@ -1,5 +1,7 @@ #include "IntaRNA/SeededPartitionFunction.h" #include +#include +#include #include #include #include @@ -7,10 +9,29 @@ #include #include +// Count ordinary C++ allocations only inside compute(), including its callbacks. +// All benchmark/kernel types use fundamental alignment. Seed construction and +// output serialization happen outside this scope; RSS is measured separately. +namespace { +bool countAllocations=false; +size_t allocationCalls=0,allocatedBytes=0; +} +void * operator new(std::size_t size) { + void * memory=std::malloc(size?size:1); + if(!memory) throw std::bad_alloc(); + if(countAllocations) { ++allocationCalls; allocatedBytes+=size; } + return memory; +} +void * operator new[](std::size_t size) { return ::operator new(size); } +void operator delete(void * memory) noexcept { std::free(memory); } +void operator delete[](void * memory) noexcept { std::free(memory); } +void operator delete(void * memory,std::size_t) noexcept { std::free(memory); } +void operator delete[](void * memory,std::size_t) noexcept { std::free(memory); } + int main(int argc,char **argv) { using namespace IntaRNA; using K=SeededPartitionFunction; - if(argc!=6) return 2; + if(argc!=6 && argc!=7) return 2; const std::string topology=argv[1],density=argv[2],dump=argv[5]; const size_t n=std::stoul(argv[3]),span=std::min(n,size_t(24)),length=4; const bool pairs=std::stoi(argv[4]); @@ -37,13 +58,18 @@ int main(int argc,char **argv) { if(dump!="-") cells.push_back({p,r,h,b}); } }; + if(argc==7 && std::string(argv[6])=="heuristic") weights.selectRight=[&](K::Pair,K::Pair q) { + return K::Pair{std::min(n-1,q.first+2+(q.second%3)),std::min(n-1,q.second+2+(q.first%3))}; + }; const K::Domain domain{n,n,span,span,3,3,false}; const auto begin=std::chrono::steady_clock::now(); + countAllocations=true; const auto result=K::compute(domain,seeds,weights,pairs); + countAllocations=false; const double seconds=std::chrono::duration(std::chrono::steady_clock::now()-begin).count(); Z_type moment=0; if(pairs) for(size_t i=0;i #include #include #include @@ -24,13 +25,52 @@ compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool } // Seed-free prefixes retain stack length; right extensions need only noLP. const size_t cap=std::max(size_t(2),seeds.maxSeedLength()); + // Allocate the maximal inclusive span once. Offset zero is a stored pair, + // so an extension of at most span-1 positions needs span cells, even for + // singleton seeds. Keep these shapes fixed and clear only active cells; + // this also prevents stale values when active rectangles shrink/regrow. + const size_t maxN=std::min(d.span1,d.n),maxM=std::min(d.span2,d.m); + const size_t cells=matrix_detail::product(maxN,maxM); + Matrix left(cells,cap),leftTotal(maxN,maxM),hybrid(maxN,maxM); + Matrix hit(maxN,maxM),right(maxN,maxM); + Matrix valid(maxN,maxM); + Matrix ending(maxN,maxM),byEnd; + Matrix boundary,dLeft,dTotal; + Matrix dHit,adjoint; + if(w.selectRight) byEnd.resize(maxN,maxM,false); + if(pairs) { + boundary.resize(maxN,maxM,false); + dLeft.resize(cells,cap,false); dTotal.resize(maxN,maxM,false); + dHit.resize(maxN,maxM,false); adjoint.resize(maxN,maxM,false); + } + auto reset=[](Matrix & matrix,size_t rows,size_t columns) { + // Matrix storage is row-major. Clear contiguous full-width regions in + // one pass; narrower rectangles leave the inactive row tails untouched. + if(columns==matrix.size2()) std::fill_n(&matrix(0,0),rows*columns,T{}); + else for(size_t i=0;i groups; + std::vector anchors; + groups.reserve(w.selectRight?cells+1:1); anchors.reserve(cells); + auto addAnchor=[&](Group & group,Pair q) { + anchors.push_back({q,group.first}); group.first=anchors.size(); + }; + auto forAnchors=[&](const Group & group,const auto & visit) { + for(size_t a=group.first;a;a=anchors[a-1].next) visit(anchors[a-1].pair); + }; seeds.forEachStart([&](size_t p1,size_t p2) { const Pair p{p1,p2}; if (!w.valid(p)) return; const size_t n=std::min(d.span1,d.n-p1),m=std::min(d.span2,d.m-p2); - Matrix left(matrix_detail::product(n,m),cap),leftTotal(n,m); - Matrix hit(n,m); - Matrix valid(n,m); - Matrix ending(n,m); + reset(left,n*m,cap); reset(leftTotal,n,m); reset(hit,n,m); + reset(hybrid,n,m); + if(w.selectRight) reset(byEnd,n,m); + if(pairs) { reset(boundary,n,m); reset(dHit,n,m); } for(size_t i=0;i anchors; }; - std::vector groups; - groups.push_back({{n-1,m-1},true,{}}); - std::unordered_map byEnd; - Matrix hybrid(n,m),boundary(n,m); - Matrix dHit; - if(pairs) dHit.resize(n,m); + groups.clear(); anchors.clear(); + groups.push_back({{n-1,m-1},true}); for(size_t i=0;i=n || end.second-p2>=m) continue; const Pair local{end.first-p1,end.second-p2}; - auto [at,fresh]=byEnd.emplace(local,groups.size()); - if(fresh) groups.push_back({local,false,{}}); - groups[at->second].anchors.emplace_back(i,j); + auto & groupIndex=byEnd(local.first,local.second); + if(!groupIndex) { + groupIndex=groups.size(); groups.push_back({local,false}); + } + addAnchor(groups[groupIndex],{i,j}); } auto fillRight=[&](const Group & group) { - Matrix right(group.end.first+1,group.end.second+1); - for(auto q:group.anchors) right(q.first,q.second)=hit(q.first,q.second); + reset(right,group.end.first+1,group.end.second+1); + forAnchors(group,[&](Pair q) { right(q.first,q.second)=hit(q.first,q.second); }); // B is seed-unconstrained: no seed lookup or subtraction occurs here. - for(size_t i=0;i right; for(const auto & group:groups) { - if(group.anchors.empty()) continue; - right=fillRight(group); + if(!group.first) continue; + fillRight(group); if(group.all) { for(size_t i=0;i0) { const Pair q{p1+i,p2+j}; - const Z_type b=A::check(w.boundary(p,q)); boundary(i,j)=b; + const Z_type b=A::check(w.boundary(p,q)); + if(pairs) boundary(i,j)=b; out.z=A::add(out.z,A::multiply(hybrid(i,j),b)); if(w.complete) w.complete(p,q,hybrid(i,j),b); } @@ -140,10 +176,10 @@ compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool // Reverse the retained right contractions. Recompute one group at a time // to bound memory independently of the number of heuristic choices. for(size_t g=0;g adjoint(right.size1(),right.size2()); - for(size_t ii=right.size1();ii>0;--ii) for(size_t jj=right.size2();jj>0;--jj) { + const auto & group=groups[g]; if(!group.first) continue; + if(groups.size()!=1) fillRight(group); + reset(adjoint,group.end.first+1,group.end.second+1); + for(size_t ii=group.end.first+1;ii>0;--ii) for(size_t jj=group.end.second+1;jj>0;--jj) { const size_t i=ii-1,j=jj-1; if(!valid(i,j)) continue; auto & a=adjoint(i,j); if(group.all || (i==group.end.first && j==group.end.second)) a[1]=A::add(a[1],boundary(i,j)); @@ -159,8 +195,10 @@ compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool adjoint(u,v)[1]=A::add(adjoint(u,v)[1],A::multiply(a[d.noLP?0:1],transition(i,j,u,v))); }); } - for(auto q:group.anchors) for(size_t c=0;c<2;++c) - dHit(q.first,q.second)[c]=A::add(dHit(q.first,q.second)[c],adjoint(q.first,q.second)[c]); + forAnchors(group,[&](Pair q) { + for(size_t c=0;c<2;++c) + dHit(q.first,q.second)[c]=A::add(dHit(q.first,q.second)[c],adjoint(q.first,q.second)[c]); + }); } if(w.selectRight) for(size_t i=0;i dLeft(n*m,cap),dTotal(n,m); + reset(dLeft,n*m,cap); reset(dTotal,n,m); auto destinationAdjoint=[&](size_t i,size_t j,size_t len) { return accepts(i,j,len)?dHit(i,j)[d.noLP && len==1?0:1]:dLeft(i*m+j,len-1); }; diff --git a/tests/PredictorHeuristicSeededProbability_test.cpp b/tests/PredictorHeuristicSeededProbability_test.cpp index 94591c5f..6fa66b8a 100644 --- a/tests/PredictorHeuristicSeededProbability_test.cpp +++ b/tests/PredictorHeuristicSeededProbability_test.cpp @@ -170,6 +170,39 @@ TEST_CASE("stack seeded heuristic and exact native marginals match independent r } } +TEST_CASE("heuristic right selection reuses buffers across mixed seed lengths", "[HeuristicSeededProbability]") { + #include "testEasyLoggingSetup.icc" + RnaSequence t("t","GGGGGG"),q("q","CCCCCC"); + AccessibilityDisabled at(t,5,nullptr),aq(q,6,nullptr); ReverseAccessibility ar(aq); + InteractionEnergyBasePair energy(at,ar,2,1); + // Vary both the seed length and the row/column of its endpoint so the + // active right rectangle repeatedly shrinks and regrows in one prediction. + const std::vector patterns{"1|||&4|||","1||&4||","2||&5||","3|||&2|||","5||&4||"}; + std::string combined; + for(const auto & pattern:patterns) { if(!combined.empty()) combined+=','; combined+=pattern; } + for(bool noLP:{false,true}) { + CAPTURE(noLP); + const auto oc=constraints(noLP); + const auto sc=explicitSeeds(combined); + OutputHandlerInteractionList output(oc,0); + FixedChoiceSeededHeuristic together(energy,output,nullptr,new SeedHandlerExplicit(energy,sc)); + together.predict(); + REQUIRE(together.choices.size()==patterns.size()); + for(const auto & pattern:patterns) { + CAPTURE(pattern); + const auto single=explicitSeeds(pattern); + OutputHandlerInteractionList singleOutput(oc,0); + FixedChoiceSeededHeuristic alone(energy,singleOutput,nullptr,new SeedHandlerExplicit(energy,single)); + alone.predict(); + REQUIRE(alone.choices.size()==1); + const auto & [start,expected]=*alone.choices.begin(); + const auto & got=together.choices.at(start); + REQUIRE(got.j1==expected.j1); REQUIRE(got.j2==expected.j2); + REQUIRE(got.e==expected.e); + } + } +} + TEST_CASE("seeded heuristic probabilities handle original coordinates regions and empty domains", "[BasePairProbabilities][HeuristicSeededProbability]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGGGGG"),q("q","CCCCCC"); diff --git a/tests/SeededPartitionFunction_test.cpp b/tests/SeededPartitionFunction_test.cpp index ab0221bd..be0aac98 100644 --- a/tests/SeededPartitionFunction_test.cpp +++ b/tests/SeededPartitionFunction_test.cpp @@ -246,6 +246,36 @@ TEST_CASE("frozen right extensions retain complete paths by their first seed own compareKernels(m); } +TEST_CASE("seed extension workspaces reset across changing active rectangles", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + Model m{7,8,7,8,false,{}, + [](Pair p){return (p.first+2*p.second)%5!=1;}, + [](Pair p){return .7L+.03L*p.second;}, + [](Pair p,Pair q){return stack(p,q)?.8L:.2L;}, + [](Pair p,Pair q){return (p.first+q.second)%4==0?0.L:.6L;}}; + // Row-major starts shrink at the right edge, then regrow on the next row. + // Mixed lengths include singleton ownership at the smallest span limit. + for(size_t i=0;im.n || j+length>m.m) continue; + Chain seed; + for(size_t k=0;k Date: Fri, 16 Oct 2026 13:47:05 +0200 Subject: [PATCH 24/30] refactor: anchor exact seeded ensembles at left-most seeds --- ChangeLog | 28 ++ doc/Makefile.am | 7 +- doc/analysis/seed-extension-partition.md | 150 +++++++- doc/benchmarks/seed-extension/README.md | 89 ++++- doc/benchmarks/seed-extension/build.py | 10 +- .../leftmost-exact-results.json | 202 ++++++++++ .../seed-extension/leftmost-exact-samples.tsv | 81 ++++ .../leftmost-heuristic-results.json | 202 ++++++++++ .../leftmost-heuristic-samples.tsv | 81 ++++ .../leftmost-source-hashes.json | 123 ++++++ doc/benchmarks/seed-extension/run.py | 36 +- src/IntaRNA/SeededPartitionFunction.cpp | 352 +++++++++++++++++- src/IntaRNA/SeededPartitionFunction.h | 15 +- src/IntaRNA/StackSeedDomain.cpp | 21 ++ src/IntaRNA/StackSeedDomain.h | 7 + tests/PredictorSeedOracle_test.cpp | 90 +++++ tests/SeededPartitionFunction_test.cpp | 107 +++++- 17 files changed, 1560 insertions(+), 41 deletions(-) create mode 100644 doc/benchmarks/seed-extension/leftmost-exact-results.json create mode 100644 doc/benchmarks/seed-extension/leftmost-exact-samples.tsv create mode 100644 doc/benchmarks/seed-extension/leftmost-heuristic-results.json create mode 100644 doc/benchmarks/seed-extension/leftmost-heuristic-samples.tsv create mode 100644 doc/benchmarks/seed-extension/leftmost-source-hashes.json diff --git a/ChangeLog b/ChangeLog index 18a38d61..03d610bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- compute exact stack-seeded ensembles with left-most seed-anchored left/right + extensions and shared bounded workspaces (PR #268 review) + - reuse maximal seed-extension workspaces for exact/heuristic partitions, endpoint selection and pair-probability differentiation (PR #266 review) @@ -204,6 +207,31 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261016 Alexander Mitrofanov + * StackSeedDomain.* + + Enumerate a deterministic irredundant exact anchor family while retaining + the original admitted occurrences for annotations and heuristic choices. + * SeededPartitionFunction.* + * Evaluate exact stack-seeded ensembles by left-most ownership in an + equivalent seed family with redundant containing occurrences removed. + Reuse anchor-local left/right and outside workspaces; combine complete + boundary partitions in bounded batches before publication. + * Differentiate the same nonnegative ensemble for actual-pair masses, + including singleton support from either side under noLP. Preserve the + heuristic's first-completed-seed ownership and gapped-seed fallback. + * SeededPartitionFunction_test.cpp, PredictorSeedOracle_test.cpp + + Compare nested mixed-length seeds, cropped/asymmetric spans and loop + limits, noLP support and native boundary optima with independent chain + enumeration. Check full hybrid callback coefficients, including zero + boundary factors, and identical partition-only/pair publication. + * doc/analysis/seed-extension-partition.md, doc/benchmarks/seed-extension/*, + doc/Makefile.am + + Explain exact seed ownership, extension bounds, outside differentiation + and bounded boundary aggregation; record reproducible correctness, + runtime, allocation and RSS comparisons against PR #268. Add correctness- + only benchmark runs and record harness/shared-header source hashes. + * Address https://github.com/BackofenLab/IntaRNA/pull/268#issuecomment-6212869312. + 261015 Alexander Mitrofanov * SeededPartitionFunction.cpp * Allocate prefix, first-hit, right-extension and outside matrices once per diff --git a/doc/Makefile.am b/doc/Makefile.am index 59c333a6..1c7e9d4a 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -37,4 +37,9 @@ EXTRA_DIST += benchmarks/seed-extension/README.md \ benchmarks/seed-extension/reuse-heuristic-results.json \ benchmarks/seed-extension/reuse-exact-samples.tsv \ benchmarks/seed-extension/reuse-heuristic-samples.tsv \ - benchmarks/seed-extension/reuse-source-hashes.json + benchmarks/seed-extension/reuse-source-hashes.json \ + benchmarks/seed-extension/leftmost-exact-results.json \ + benchmarks/seed-extension/leftmost-heuristic-results.json \ + benchmarks/seed-extension/leftmost-exact-samples.tsv \ + benchmarks/seed-extension/leftmost-heuristic-samples.tsv \ + benchmarks/seed-extension/leftmost-source-hashes.json diff --git a/doc/analysis/seed-extension-partition.md b/doc/analysis/seed-extension-partition.md index 488ec4b6..36682dd7 100644 --- a/doc/analysis/seed-extension-partition.md +++ b/doc/analysis/seed-extension-partition.md @@ -10,7 +10,11 @@ especially the [seed-extension chapter](https://github.com/BackofenLab/IntaRNA-probabilities/blob/e904b65101294f782b0938590335f3e23631a1aa/theory/seed-extension.md) and its `SE-LEFT-REC`, `SE-FIRST-HIT`, and `SE-PAIR-FIRST-HIT` equations. The historical subtraction formulas discussed in that chapter are not the -recurrences used here. +recurrences used here. The exact backend now evaluates anchor-local left/right +extensions as requested in the [PR #268 review](https://github.com/BackofenLab/IntaRNA/pull/268#issuecomment-6212869312). +The heuristic backend retains first-completed-seed ownership and its frozen +endpoint selector. Both compute their respective ensembles with nonnegative +weights and differentiate the same computation for actual-pair probabilities. ## Ensemble and weight ownership @@ -36,7 +40,127 @@ restrictions such as `noLP` are part of the recurrence; endpoint restrictions such as `noGUend` and complete-span accessibility are applied to the full boundary. Neither ranked-result limits nor display cutoffs select the ensemble. -## Unique first acceptance +## Exact left-most seed-anchored evaluation + +The exact backend visits seed anchors and computes left and right extension +tables around each anchor. Its seed family is first reduced to an equivalent +irredundant family: remove an occurrence if it contains another admitted stack +occurrence. Every chain containing the removed seed also contains the retained +inner seed, so the union of interactions, every boundary partition, and every +pair numerator remain unchanged. This reduction is private to exact evaluation; +the admitted seed family used for annotations and heuristic choices is unchanged. + +This matters for variable-length explicit seeds. An outer seed may start before +an inner singleton but finish later. In the irredundant family, such nesting is +absent. Seeds on a common chain are either disjoint or overlap on a strict stack; +their starts and ends therefore have the same order. Every seeded chain has a +unique left-most retained seed. That seed owns the chain, even if later seeds +overlap it or follow it after a loop. + +For an anchor from `s` to `t`, the left table enumerates paths preceding the +anchor while rejecting earlier retained seeds. Its stack-run state detects +seeds completed by prepending a pair, including earlier seeds that overlap the +anchor. The right table extends from `t` without any seed restriction. Both +respect the active loop limits and pair validity. Their contraction includes +one initiation factor at the complete left boundary, one factor for each +transition, and one complete boundary factor at the outer endpoints. Seed +admission energies are not multiplied into the interaction a second time. + +Writing `S[a]` for the product of internal anchor transitions, `L[a,p,h]` for +the left extension, and `R[a,r,k]` for the right extension gives + +```text +H[p,r] = initiation(p) * sum_a S[a] * sum_(h,k compatible) L[a,p,h] * R[a,r,k] +Z = sum_(p,r) H[p,r] * boundary(p,r). +``` + +The support flags `h,k` matter only for singleton anchors under `noLP`: +compatibility requires `h || k`. Longer seeds already support all anchor pairs. +The right table also records whether its current outer pair is supported; +the left table obtains that information from its initial stack-run length. +Only complete-boundary contractions enforce initiation and total span. Table +rectangles are additionally cropped to the sources present in the current batch. + +The left and right offsets include zero, representing no extension. An anchor +of length `k` leaves at most `span-k` extension positions on each strand, shared +between its left and right sides. Each side's table must therefore contain +`span-k+1` cells before cropping at the sequence ends. Full-boundary contraction +checks the sum of the left and right offsets, rather than allowing each side +to consume the whole span independently. Shared buffers are allocated once and +reset only over each anchor's active rectangle. + +The `noLP` states retain whether a terminal pair already has a stacked neighbor. +Only a supported outer end may terminate, and only supported pairs may precede +a nonstack edge. A singleton anchor is supported when its left or right +extension supplies a stack; support from both sides is one interaction. An +isolated singleton and a singleton connected only through nonstack edges are +excluded. The outside pass uses the same compatible states. + +### Bounded boundary aggregation and outside differentiation + +Different anchors can contribute to the same complete boundary. Reporting each +anchor contribution separately would change the complete hybrid coefficient +seen by native ensemble-energy reporting and extension hooks. Instead, the +exact backend accumulates batches of at most 256 left boundaries, then publishes +each positive complete hybrid sum once in row-major boundary order. The same +hybrid coefficient and full-boundary factor determine the total partition and +outside derivatives, even when the boundary factor is zero. + +The anchor-local forward tables and their adjoints are shared across anchors. +For pair output, the backend revisits the anchors of a batch, contracts the +saved boundary coefficients into left/right adjoints, and reverses the same +extension recurrences. Extension pairs are marked by forward weight times +outside derivative; every pair in the anchor receives its complete anchor-class +mass exactly once. No pair is counted merely because it lies inside an outer +boundary rectangle. Partition-only mode omits the outside storage and pass. + +Batching avoids storing all source/endpoint combinations across the sequences. +The batch is additionally limited to a 2 MiB target for its hybrid table, with +at least one left boundary even when a single span rectangle exceeds that +budget. Pair output saves one equally sized table of boundary coefficients. +The bound is independent of whether pair output is requested, preserving the +forward computation in both modes. Larger batches reuse anchor factors across +more sources but retain more boundary coefficients. + +With `W1=min(span1,n)`, `W2=min(span2,m)`, maximum irredundant seed length +`L`, and bounded batch size `B<=256`, workspace storage is +`O(W1*W2*(max(2,L)+B) + n*m + S)` for `S` anchors. The `n*m` term includes +a byte-valued validity cache and, for pair output, the returned mass matrix. +Partition-only mode does not allocate adjoints or saved boundary factors. +Its cost is repeated anchor-table work between batches. This is an explicit +memory/runtime tradeoff, not a claim of a general speedup. The +[benchmark records](../benchmarks/seed-extension/README.md) compare all complete +boundary coefficients, partitions and pair numerators against `985ba44`, and +report allocation calls, requested bytes, runtime and process RSS separately. + +Five-sample kernel measurements show the tradeoff: with three seeds, revised +runtime is `0.059–0.197` times the previous runtime; with many seeds it is +`3.055–5.494` times the previous runtime. Exact median process RSS increases +from `3,840–4,224 KiB` to `4,480–6,144 KiB` on these small fixtures. The anchor +index and boundary batches also increase allocation traffic. These results +characterize the requested factorization, not a universal optimization; consult +the benchmark records for per-case timings, allocation counts and source hashes. + +### Exact rewrite validation + +GCC 14.4.0 release and `--enable-debug` builds both pass `make tests -j2`: +all seven API/CLI drivers pass, with 112 API cases and 399,390 / 399,391 +assertions. Independent complete-chain enumeration checks nested and overlapping +mixed-length seeds, singleton support from either side, cropped/native reversed +coordinates, asymmetric spans and loop limits, full hybrid coefficients with +zero boundary factors, and more than 256 eligible source boundaries. Native +checks also verify the reported optimal boundary and rounded ensemble energy. + +The two changed public headers compile independently after installation, and +the installed pkg-config probability example compiles, links and runs. +The final kernel snapshots agree across 599,242 boundary/partition/pair entries: +exact maximum relative difference is `5.96e-15`; heuristic dumps are identical. +All 12 native CLI predictions match the base byte-for-byte. The six heuristic +pair-probability files are identical; the six exact files agree to maximum +relative difference `1.53e-15`. Floating-point order changes are expected from +the different nonnegative factorization; the scientific ensemble is unchanged. + +## First acceptance in the heuristic backend Scan a chain from its left boundary. Its canonical anchor is the first admitted seed to finish. Before that completion, the prefix contains no admitted seed; @@ -88,7 +212,7 @@ first-hit contraction is Hseed[p,r] = sum_q F[p,q] * B[q,r]. ``` -The implementation shares the work of all anchors through an inhomogeneous +The heuristic backend shares the work of compatible anchors through an inhomogeneous right-extension recurrence: ```text @@ -134,8 +258,9 @@ continuation in that case would admit lonely pairs. ## A defined heuristic subset -The stack-seed heuristic uses the same canonical first-hit ownership as the -exact computation. For canonical anchor `S`, let `start(S)` and `end(S)` be its +The stack-seed heuristic retains the original canonical first-hit ownership. +Unlike exact evaluation, it does not remove redundant containing occurrences: +that could change which anchor selects a right endpoint. For canonical anchor `S`, let `start(S)` and `end(S)` be its outer pairs, and let `selectedRight(S)` be the deterministic endpoint selected by the heuristic's right-extension policy. Select the endpoint before the partition and outside calculations; marking a pair must not change it. @@ -194,8 +319,9 @@ The raw numerator for pair `k` is the sum of complete interaction weights whose chains actually contain `k`. It is not the sum of weights of boundary rectangles covering `k`; the latter is a spot probability. -Initialize outside derivatives from the same complete boundary coefficients -used by forward accumulation. Reverse the right-extension passes, transfer +For the heuristic first-acceptance recurrence, initialize outside derivatives +from the same complete boundary coefficients used by forward accumulation. +Reverse the right-extension passes, transfer their derivatives into the corresponding first-hit injections, and then reverse the accepting edges and seed-free prefix recurrence. A transition owns its destination pair; the initialization owns the first pair. Accumulating @@ -233,8 +359,8 @@ limits, and `noLP` states on both sides of acceptance. Heuristic checks must use the same frozen selector, verify each retained chain once, and compare its partition with the exact ensemble. -The seed-free table still retains run-length state for each active left -boundary. Sharing right extensions avoids repeating identical continuation +The heuristic seed-free table still retains run-length state for each active +left boundary. Sharing right extensions avoids repeating identical continuation work for every anchor, but does not imply a lower asymptotic bound for the whole calculation. Heuristic cost also depends on the number of distinct selected endpoints. Pair output adds outside storage and work. Performance or @@ -242,10 +368,12 @@ memory improvements require measurements against the previous implementation with identical inputs, options, build configuration, and thread count; the factorization alone is not a performance claim. -## Reusable extension workspaces +## First-acceptance workspace reuse (PR #266 review) The allocation revision requested in [PR #266](https://github.com/BackofenLab/IntaRNA/pull/266#issuecomment-6176325864) -keeps the recurrence and its arithmetic order unchanged. One invocation allocates +kept the then-shared exact/heuristic recurrence and its arithmetic order unchanged. +The heuristic still uses these buffers; exact evaluation now uses the anchor-local +batched algorithm above. One first-acceptance invocation allocates its prefix, first-hit, right-extension and (when requested) outside matrices at `min(span1,n)` by `min(span2,m)`, with the prefix run-length dimension retained. The inclusive maximum extension offset is `span-1`; storing offset zero requires diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md index 3153fcf6..7378cd28 100644 --- a/doc/benchmarks/seed-extension/README.md +++ b/doc/benchmarks/seed-extension/README.md @@ -1,7 +1,7 @@ # Seed-extension kernel comparison -This Linux benchmark compares the seeded kernel at PR #266 revision -`392357c00f0314659cd4c665446f3f6185f4814a` with the current source checkout. +This Linux benchmark compares the seeded kernel at revision +`985ba44bdc16e92162e954b7baffa3f3923d4776` with the current source checkout. It runs the exact ensemble by default; `--heuristic` uses frozen right-endpoint groups to exercise their forward and outside workspace reuse. It first checks every reported boundary hybrid weight and boundary coefficient, @@ -30,13 +30,17 @@ For dependencies outside the compiler's search paths, add path. Both options can be repeated. `--baseline` selects another Git revision when a different comparison is intended; the current driver requires the first-acceptance kernel API introduced in #266. To measure heuristic groups, use -`--heuristic` on the runner. Keep separate build directories for exact and -heuristic results, since the runner replaces its output files. +`--heuristic` on the runner. Use separate `--output-dir` directories for exact and +heuristic results, since the runner replaces its output files. The same built +executables can be used for both ensembles. `--verify-only` compares all dumps +without collecting timed samples, which permits checking correctness before a +quiet period reserved for timing. The build script snapshots both versions of `SeededPartitionFunction` and `StackSeedDomain`, including their headers; other project headers and configured types are shared. It records the compiler, exact commands, baseline commit, -and SHA-256 source digests in `manifest.json`. The runner writes full verification +and SHA-256 source digests in `manifest.json`, including the harness and shared +numerical/configuration headers. The runner writes full verification dumps, `samples.tsv`, and `results.json` in the build directory. Timed runs do not write the full boundary dumps. @@ -112,3 +116,78 @@ bytes from 817,421,572 to 281,640, while retaining maximum-sized workspaces. To reproduce the recorded sample count, pass `--repetitions 5` to the runner. Run once without `--heuristic` and once with it, using separate build directories. + +## Left-most exact-anchor comparison (#268) + +On 2026-10-16, the exact anchor rewrite was compared with +`985ba44bdc16e92162e954b7baffa3f3923d4776`, using GCC 14.4.0, +`-std=c++23 -O3 -DNDEBUG`, one thread, and the same AMD Ryzen 5 7530U. +Five alternating fresh-process samples were collected per fixture/output mode. +Exact and heuristic measurements ran sequentially; no project build, test or +installation ran during the timed samples. CPU affinity, frequency and other +workstation activity were uncontrolled. `leftmost-*-results.json` and +`leftmost-*-samples.tsv` retain all timings, allocation counts, requested bytes +and process RSS; `leftmost-source-hashes.json` records the source, executable +and full-dump hashes, compiler, commands and measurement environment. + +All 328,236 exact boundary/partition/pair entries agree within a maximum +relative difference of `5.96e-15` (the comparison tolerance is `1e-12`). +All 271,006 heuristic entries are identical. Each boundary dump includes both +the complete hybrid weight and the full-boundary coefficient; the comparison +also rejects duplicate boundary callbacks and nonfinite values. + +The exact kernel uses at most 256 left boundaries per batch. Each reusable +boundary table targets at most 2 MiB, with one row as the minimum when a single +span rectangle exceeds that budget. All four fixtures use 256-row batches. +The table reports median kernel time in seconds; ratios are candidate/base. + +| Exact fixture | Partition seconds, base → candidate | Ratio | Pair seconds, base → candidate | Ratio | +| --- | ---: | ---: | ---: | ---: | +| dense40-many | 0.093366 → 0.304718 | 3.264 | 0.238276 → 1.219707 | 5.119 | +| dense40-few | 0.055158 → 0.004673 | 0.085 | 0.146767 → 0.008607 | 0.059 | +| band60-many | 0.015683 → 0.047910 | 3.055 | 0.032126 → 0.176494 | 5.494 | +| band60-few | 0.012318 → 0.002428 | 0.197 | 0.026294 → 0.004654 | 0.177 | + +The few-seed fixtures improve, while both many-seed fixtures regress +substantially. Recomputing bounded anchor tables and contracting many anchors +against complete boundaries remains expensive. These measurements do not +establish a general speedup. + +| Exact fixture | Partition allocations, base → candidate | Pair allocations, base → candidate | +| --- | ---: | ---: | +| dense40-many | 21 → 1,393 | 27 → 1,398 | +| dense40-few | 13 → 19 | 19 → 24 | +| band60-many | 19 → 409 | 25 → 414 | +| band60-few | 12 → 18 | 18 → 23 | + +Exact allocation traffic also increases: the anchor index allocates nodes per +retained occurrence, and the larger reusable boundary tables require more +storage. Partition-only requested bytes rise from 65,368–98,072 to +1,261,507–1,404,889; pair-output requested bytes rise from 124,312–156,952 to +2,518,467–2,661,849. Median whole-process RSS rises from 3,840–4,224 KiB to +4,480–6,144 KiB. These figures measure allocation traffic and process RSS, +respectively; neither is a bound on live kernel memory. + +The unchanged heuristic ensemble retains identical allocation counts and +requested bytes, but its measured median runtime ratios are 1.046–1.225. +The largest observed regression is dense40-few partition-only +(0.048834 → 0.059830 seconds). The complete sample ranges are recorded rather +than assuming that an unchanged recurrence has unchanged performance. + +To reproduce the final comparison after configuring the checkout: + +```sh +python3 doc/benchmarks/seed-extension/build.py \ + --source "$PWD" --output /tmp/intarna-leftmost-benchmark --cxx g++-14 \ + --baseline 985ba44bdc16e92162e954b7baffa3f3923d4776 +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-leftmost-benchmark \ + --output-dir /tmp/intarna-leftmost-exact --repetitions 5 +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-leftmost-benchmark \ + --output-dir /tmp/intarna-leftmost-heuristic --repetitions 5 --heuristic +``` + +Add the dependency include/runtime options described above when necessary. +The recorded manifest contains the exact compiler and dependency paths used +for these measurements. diff --git a/doc/benchmarks/seed-extension/build.py b/doc/benchmarks/seed-extension/build.py index aec1f5aa..ad803d93 100644 --- a/doc/benchmarks/seed-extension/build.py +++ b/doc/benchmarks/seed-extension/build.py @@ -14,7 +14,7 @@ def main(): parser.add_argument("--output", type=Path, required=True, help="directory for source snapshots and executables") parser.add_argument("--cxx", default="g++", help="C++23 compiler executable") - parser.add_argument("--baseline", default="392357c00f0314659cd4c665446f3f6185f4814a") + parser.add_argument("--baseline", default="985ba44bdc16e92162e954b7baffa3f3923d4776") parser.add_argument("--include", type=Path, action="append", default=[], help="extra dependency include directory; repeat as needed") parser.add_argument("--runtime-library-dir", type=Path, action="append", default=[], @@ -24,6 +24,8 @@ def main(): output.mkdir(parents=True, exist_ok=True) driver = Path(__file__).with_name("driver.cpp").resolve() (output / "driver.cpp").write_bytes(driver.read_bytes()) + for name in ("build.py", "run.py"): + (output / name).write_bytes(Path(__file__).with_name(name).read_bytes()) names = ("SeededPartitionFunction.cpp", "SeededPartitionFunction.h", "StackSeedDomain.cpp", "StackSeedDomain.h") for variant in ("base", "candidate"): @@ -46,7 +48,7 @@ def main(): "-o", str(output / variant / "benchmark")] subprocess.run(command, check=True) commands.append(command) - files = [output / "driver.cpp", + files = [output / "driver.cpp", output / "build.py", output / "run.py", *(output / variant / "IntaRNA" / name for variant in ("base", "candidate") for name in names)] manifest = { @@ -56,6 +58,10 @@ def main(): "commands": commands, "sha256": {str(p.relative_to(output)): hashlib.sha256(p.read_bytes()).hexdigest() for p in files}, + "shared_header_sha256": { + name: hashlib.sha256((source / "src" / "IntaRNA" / name).read_bytes()).hexdigest() + for name in ("PartitionArithmetic.h", "Matrix.h", "Interaction.h", + "general.h", "intarna_config.h")}, } (output / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") diff --git a/doc/benchmarks/seed-extension/leftmost-exact-results.json b/doc/benchmarks/seed-extension/leftmost-exact-results.json new file mode 100644 index 00000000..9fea5b25 --- /dev/null +++ b/doc/benchmarks/seed-extension/leftmost-exact-results.json @@ -0,0 +1,202 @@ +{ + "heuristic": false, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 5, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 5.957412616444836e-15, + "max_relative_error_by_kind": { + "b": 1.4209049291009378e-15, + "p": 5.957412616444836e-15, + "z": 1.291670716454554e-16 + } + }, + { + "case": "dense40-few", + "compared_entries": 42460, + "max_relative_error": 3.9285075143597306e-15, + "max_relative_error_by_kind": { + "b": 1.4263048792459748e-15, + "p": 3.9285075143597306e-15, + "z": 0.0 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 4.646309793444668e-15, + "max_relative_error_by_kind": { + "b": 1.2438625710083009e-15, + "p": 4.646309793444668e-15, + "z": 4.3892001237381183e-16 + } + }, + { + "case": "band60-few", + "compared_entries": 22796, + "max_relative_error": 2.7284188823642434e-15, + "max_relative_error_by_kind": { + "b": 1.1391417738744533e-15, + "p": 2.7284188823642434e-15, + "z": 1.7980683996367582e-16 + } + } + ], + "summary": [ + { + "case": "dense40-many", + "pairs": 0, + "seeds": 1369, + "base_allocations": 21, + "base_allocated_bytes": 98072, + "base_median_seconds": 0.093365805, + "base_min_seconds": 0.092144595, + "base_max_seconds": 0.093547516, + "base_median_rss_kib": 4224, + "candidate_allocations": 1393, + "candidate_allocated_bytes": 1404889, + "candidate_median_seconds": 0.304717545, + "candidate_min_seconds": 0.301127722, + "candidate_max_seconds": 0.306358563, + "candidate_median_rss_kib": 4992, + "runtime_ratio": 3.2636953647001703, + "rss_ratio": 1.1818181818181819 + }, + { + "case": "dense40-many", + "pairs": 1, + "seeds": 1369, + "base_allocations": 27, + "base_allocated_bytes": 156952, + "base_median_seconds": 0.238276023, + "base_min_seconds": 0.23492087, + "base_max_seconds": 0.243261133, + "base_median_rss_kib": 4096, + "candidate_allocations": 1398, + "candidate_allocated_bytes": 2661849, + "candidate_median_seconds": 1.219706644, + "candidate_min_seconds": 1.199150065, + "candidate_max_seconds": 1.225815565, + "candidate_median_rss_kib": 6144, + "runtime_ratio": 5.11888115574264, + "rss_ratio": 1.5 + }, + { + "case": "dense40-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 13, + "base_allocated_bytes": 65432, + "base_median_seconds": 0.055157979, + "base_min_seconds": 0.054751207, + "base_max_seconds": 0.055426674, + "base_median_rss_kib": 3840, + "candidate_allocations": 19, + "candidate_allocated_bytes": 1261507, + "candidate_median_seconds": 0.004672505, + "candidate_min_seconds": 0.004559463, + "candidate_max_seconds": 0.004963209, + "candidate_median_rss_kib": 4608, + "runtime_ratio": 0.08471131619960187, + "rss_ratio": 1.2 + }, + { + "case": "dense40-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 19, + "base_allocated_bytes": 124312, + "base_median_seconds": 0.14676663, + "base_min_seconds": 0.146359216, + "base_max_seconds": 0.146839087, + "base_median_rss_kib": 3968, + "candidate_allocations": 24, + "candidate_allocated_bytes": 2518467, + "candidate_median_seconds": 0.008607415, + "candidate_min_seconds": 0.008425183, + "candidate_max_seconds": 0.009089309, + "candidate_median_rss_kib": 5888, + "runtime_ratio": 0.05864694856044592, + "rss_ratio": 1.4838709677419355 + }, + { + "case": "band60-many", + "pairs": 0, + "seeds": 387, + "base_allocations": 19, + "base_allocated_bytes": 73496, + "base_median_seconds": 0.015682997, + "base_min_seconds": 0.015321749, + "base_max_seconds": 0.015850961, + "base_median_rss_kib": 3840, + "candidate_allocations": 409, + "candidate_allocated_bytes": 1302851, + "candidate_median_seconds": 0.047910346, + "candidate_min_seconds": 0.046959602, + "candidate_max_seconds": 0.048399103, + "candidate_median_rss_kib": 4608, + "runtime_ratio": 3.054922856900374, + "rss_ratio": 1.2 + }, + { + "case": "band60-many", + "pairs": 1, + "seeds": 387, + "base_allocations": 25, + "base_allocated_bytes": 148376, + "base_median_seconds": 0.03212618, + "base_min_seconds": 0.031852615, + "base_max_seconds": 0.03252675, + "base_median_rss_kib": 3968, + "candidate_allocations": 414, + "candidate_allocated_bytes": 2575811, + "candidate_median_seconds": 0.17649404, + "candidate_min_seconds": 0.173971869, + "candidate_max_seconds": 0.177812895, + "candidate_median_rss_kib": 5760, + "runtime_ratio": 5.493776104099522, + "rss_ratio": 1.4516129032258065 + }, + { + "case": "band60-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 12, + "base_allocated_bytes": 65368, + "base_median_seconds": 0.012317725, + "base_min_seconds": 0.012183713, + "base_max_seconds": 0.013108587, + "base_median_rss_kib": 3840, + "candidate_allocations": 18, + "candidate_allocated_bytes": 1263443, + "candidate_median_seconds": 0.002427834, + "candidate_min_seconds": 0.002320492, + "candidate_max_seconds": 0.002720914, + "candidate_median_rss_kib": 4480, + "runtime_ratio": 0.19710084451471355, + "rss_ratio": 1.1666666666666667 + }, + { + "case": "band60-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 18, + "base_allocated_bytes": 140248, + "base_median_seconds": 0.026293799, + "base_min_seconds": 0.02599001, + "base_max_seconds": 0.026362569, + "base_median_rss_kib": 3840, + "candidate_allocations": 23, + "candidate_allocated_bytes": 2536403, + "candidate_median_seconds": 0.004653539, + "candidate_min_seconds": 0.004458603, + "candidate_max_seconds": 0.005400681, + "candidate_median_rss_kib": 5760, + "runtime_ratio": 0.17698237519804574, + "rss_ratio": 1.5 + } + ] +} diff --git a/doc/benchmarks/seed-extension/leftmost-exact-samples.tsv b/doc/benchmarks/seed-extension/leftmost-exact-samples.tsv new file mode 100644 index 00000000..76798f80 --- /dev/null +++ b/doc/benchmarks/seed-extension/leftmost-exact-samples.tsv @@ -0,0 +1,81 @@ +variant case pairs allocations allocated_bytes seconds rss_kib z pair_moment boundaries checksum seeds repetition +base dense40-many 0 21 98072 0.093547516 4096 3691630940.6779213 0.0 219109 3691630940.6779213 1369 0 +candidate dense40-many 0 1393 1404889 0.301127722 4992 3691630940.677922 0.0 219109 3691630940.677922 1369 0 +candidate dense40-many 0 1393 1404889 0.306358563 4992 3691630940.677922 0.0 219109 3691630940.677922 1369 1 +base dense40-many 0 21 98072 0.09350664 4224 3691630940.6779213 0.0 219109 3691630940.6779213 1369 1 +base dense40-many 0 21 98072 0.092144595 4224 3691630940.6779213 0.0 219109 3691630940.6779213 1369 2 +candidate dense40-many 0 1393 1404889 0.304717545 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 2 +candidate dense40-many 0 1393 1404889 0.305515453 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 3 +base dense40-many 0 21 98072 0.093365805 4224 3691630940.6779213 0.0 219109 3691630940.6779213 1369 3 +base dense40-many 0 21 98072 0.092650062 4096 3691630940.6779213 0.0 219109 3691630940.6779213 1369 4 +candidate dense40-many 0 1393 1404889 0.303027416 4992 3691630940.677922 0.0 219109 3691630940.677922 1369 4 +base dense40-many 1 27 156952 0.235931695 4224 3691630940.6779213 51541049531.7629 219109 3691630940.6779213 1369 0 +candidate dense40-many 1 1398 2661849 1.199150065 6016 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 0 +candidate dense40-many 1 1398 2661849 1.219706644 6272 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 1 +base dense40-many 1 27 156952 0.23492087 4096 3691630940.6779213 51541049531.7629 219109 3691630940.6779213 1369 1 +base dense40-many 1 27 156952 0.243261133 4224 3691630940.6779213 51541049531.7629 219109 3691630940.6779213 1369 2 +candidate dense40-many 1 1398 2661849 1.225815565 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 2 +candidate dense40-many 1 1398 2661849 1.222822503 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 3 +base dense40-many 1 27 156952 0.239018205 4096 3691630940.6779213 51541049531.7629 219109 3691630940.6779213 1369 3 +base dense40-many 1 27 156952 0.238276023 4096 3691630940.6779213 51541049531.7629 219109 3691630940.6779213 1369 4 +candidate dense40-many 1 1398 2661849 1.20108948 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 4 +base dense40-few 0 13 65432 0.05487551 3840 22766642.26492601 0.0 40859 22766642.26492601 3 0 +candidate dense40-few 0 19 1261507 0.004676932 4480 22766642.26492601 0.0 40859 22766642.26492601 3 0 +candidate dense40-few 0 19 1261507 0.004963209 4608 22766642.26492601 0.0 40859 22766642.26492601 3 1 +base dense40-few 0 13 65432 0.054751207 3968 22766642.26492601 0.0 40859 22766642.26492601 3 1 +base dense40-few 0 13 65432 0.055367252 3840 22766642.26492601 0.0 40859 22766642.26492601 3 2 +candidate dense40-few 0 19 1261507 0.004672505 4608 22766642.26492601 0.0 40859 22766642.26492601 3 2 +candidate dense40-few 0 19 1261507 0.004559463 4608 22766642.26492601 0.0 40859 22766642.26492601 3 3 +base dense40-few 0 13 65432 0.055426674 3584 22766642.26492601 0.0 40859 22766642.26492601 3 3 +base dense40-few 0 13 65432 0.055157979 3840 22766642.26492601 0.0 40859 22766642.26492601 3 4 +candidate dense40-few 0 19 1261507 0.004560204 4480 22766642.26492601 0.0 40859 22766642.26492601 3 4 +base dense40-few 1 19 124312 0.146839087 3968 22766642.26492601 322997499.1283487 40859 22766642.26492601 3 0 +candidate dense40-few 1 24 2518467 0.008825203 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 0 +candidate dense40-few 1 24 2518467 0.009089309 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 1 +base dense40-few 1 19 124312 0.14676663 3968 22766642.26492601 322997499.1283487 40859 22766642.26492601 3 1 +base dense40-few 1 19 124312 0.146651354 3840 22766642.26492601 322997499.1283487 40859 22766642.26492601 3 2 +candidate dense40-few 1 24 2518467 0.008526383 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 2 +candidate dense40-few 1 24 2518467 0.008425183 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 3 +base dense40-few 1 19 124312 0.146816945 3840 22766642.26492601 322997499.1283487 40859 22766642.26492601 3 3 +base dense40-few 1 19 124312 0.146359216 3968 22766642.26492601 322997499.1283487 40859 22766642.26492601 3 4 +candidate dense40-few 1 24 2518467 0.008607415 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 4 +base band60-many 0 19 73496 0.015850961 3840 1086387370.7289987 0.0 38669 1086387370.7289987 387 0 +candidate band60-many 0 409 1302851 0.047910346 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 0 +candidate band60-many 0 409 1302851 0.046959602 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 1 +base band60-many 0 19 73496 0.015836264 3840 1086387370.7289987 0.0 38669 1086387370.7289987 387 1 +base band60-many 0 19 73496 0.015682997 3968 1086387370.7289987 0.0 38669 1086387370.7289987 387 2 +candidate band60-many 0 409 1302851 0.04805647 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 2 +candidate band60-many 0 409 1302851 0.048399103 4480 1086387370.7289982 0.0 38669 1086387370.7289982 387 3 +base band60-many 0 19 73496 0.015321749 3968 1086387370.7289987 0.0 38669 1086387370.7289987 387 3 +base band60-many 0 19 73496 0.015506345 3840 1086387370.7289987 0.0 38669 1086387370.7289987 387 4 +candidate band60-many 0 409 1302851 0.047780232 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 4 +base band60-many 1 25 148376 0.031852615 3968 1086387370.7289987 15334043961.83602 38669 1086387370.7289987 387 0 +candidate band60-many 1 414 2575811 0.17649404 5760 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 0 +candidate band60-many 1 414 2575811 0.175079166 5760 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 1 +base band60-many 1 25 148376 0.03212618 3968 1086387370.7289987 15334043961.83602 38669 1086387370.7289987 387 1 +base band60-many 1 25 148376 0.031945791 3968 1086387370.7289987 15334043961.83602 38669 1086387370.7289987 387 2 +candidate band60-many 1 414 2575811 0.177812895 6016 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 2 +candidate band60-many 1 414 2575811 0.177589555 6016 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 3 +base band60-many 1 25 148376 0.03252675 3968 1086387370.7289987 15334043961.83602 38669 1086387370.7289987 387 3 +base band60-many 1 25 148376 0.032213633 4096 1086387370.7289987 15334043961.83602 38669 1086387370.7289987 387 4 +candidate band60-many 1 414 2575811 0.173971869 5760 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 4 +base band60-few 0 12 65368 0.012317725 3840 20718290.245323755 0.0 19195 20718290.245323755 3 0 +candidate band60-few 0 18 1263443 0.002384463 4480 20718290.24532375 0.0 19195 20718290.24532375 3 0 +candidate band60-few 0 18 1263443 0.002320492 4480 20718290.24532375 0.0 19195 20718290.24532375 3 1 +base band60-few 0 12 65368 0.013108587 3840 20718290.245323755 0.0 19195 20718290.245323755 3 1 +base band60-few 0 12 65368 0.012307285 3840 20718290.245323755 0.0 19195 20718290.245323755 3 2 +candidate band60-few 0 18 1263443 0.002427834 4480 20718290.24532375 0.0 19195 20718290.24532375 3 2 +candidate band60-few 0 18 1263443 0.002535516 4352 20718290.24532375 0.0 19195 20718290.24532375 3 3 +base band60-few 0 12 65368 0.012183713 3840 20718290.245323755 0.0 19195 20718290.245323755 3 3 +base band60-few 0 12 65368 0.012406261 3840 20718290.245323755 0.0 19195 20718290.245323755 3 4 +candidate band60-few 0 18 1263443 0.002720914 4352 20718290.24532375 0.0 19195 20718290.24532375 3 4 +base band60-few 1 18 140248 0.026352329 3840 20718290.245323755 296428379.41423106 19195 20718290.245323755 3 0 +candidate band60-few 1 23 2536403 0.004623382 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 0 +candidate band60-few 1 23 2536403 0.004654391 5888 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 1 +base band60-few 1 18 140248 0.026362569 3840 20718290.245323755 296428379.41423106 19195 20718290.245323755 3 1 +base band60-few 1 18 140248 0.026293799 3840 20718290.245323755 296428379.41423106 19195 20718290.245323755 3 2 +candidate band60-few 1 23 2536403 0.005400681 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 2 +candidate band60-few 1 23 2536403 0.004458603 5888 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 3 +base band60-few 1 18 140248 0.026133318 3712 20718290.245323755 296428379.41423106 19195 20718290.245323755 3 3 +base band60-few 1 18 140248 0.02599001 3968 20718290.245323755 296428379.41423106 19195 20718290.245323755 3 4 +candidate band60-few 1 23 2536403 0.004653539 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 4 diff --git a/doc/benchmarks/seed-extension/leftmost-heuristic-results.json b/doc/benchmarks/seed-extension/leftmost-heuristic-results.json new file mode 100644 index 00000000..ca99f2e8 --- /dev/null +++ b/doc/benchmarks/seed-extension/leftmost-heuristic-results.json @@ -0,0 +1,202 @@ +{ + "heuristic": true, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 5, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "dense40-few", + "compared_entries": 3435, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-few", + "compared_entries": 4591, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + } + ], + "summary": [ + { + "case": "dense40-many", + "pairs": 0, + "seeds": 1369, + "base_allocations": 1399, + "base_allocated_bytes": 222760, + "base_median_seconds": 0.389085589, + "base_min_seconds": 0.386887045, + "base_max_seconds": 0.402965835, + "base_median_rss_kib": 4224, + "candidate_allocations": 1399, + "candidate_allocated_bytes": 222760, + "candidate_median_seconds": 0.407022865, + "candidate_min_seconds": 0.406690141, + "candidate_max_seconds": 0.426235313, + "candidate_median_rss_kib": 4352, + "runtime_ratio": 1.0461011060473895, + "rss_ratio": 1.0303030303030303 + }, + { + "case": "dense40-many", + "pairs": 1, + "seeds": 1369, + "base_allocations": 1405, + "base_allocated_bytes": 281640, + "base_median_seconds": 2.546715906, + "base_min_seconds": 2.530385913, + "base_max_seconds": 2.638791819, + "base_median_rss_kib": 4224, + "candidate_allocations": 1405, + "candidate_allocated_bytes": 281640, + "candidate_median_seconds": 2.726610936, + "candidate_min_seconds": 2.64812584, + "candidate_max_seconds": 2.729402014, + "candidate_median_rss_kib": 4224, + "runtime_ratio": 1.070638043912229, + "rss_ratio": 1.0 + }, + { + "case": "dense40-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 18, + "base_allocated_bytes": 88720, + "base_median_seconds": 0.04883356, + "base_min_seconds": 0.048262158, + "base_max_seconds": 0.050365283, + "base_median_rss_kib": 3840, + "candidate_allocations": 18, + "candidate_allocated_bytes": 88720, + "candidate_median_seconds": 0.059829666, + "candidate_min_seconds": 0.058165464, + "candidate_max_seconds": 0.063792269, + "candidate_median_rss_kib": 3968, + "runtime_ratio": 1.2251751869001564, + "rss_ratio": 1.0333333333333334 + }, + { + "case": "dense40-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 24, + "base_allocated_bytes": 147600, + "base_median_seconds": 0.128197658, + "base_min_seconds": 0.125294192, + "base_max_seconds": 0.132416331, + "base_median_rss_kib": 3840, + "candidate_allocations": 24, + "candidate_allocated_bytes": 147600, + "candidate_median_seconds": 0.135749123, + "candidate_min_seconds": 0.132237165, + "candidate_max_seconds": 0.139192692, + "candidate_median_rss_kib": 3968, + "runtime_ratio": 1.0589048592447765, + "rss_ratio": 1.0333333333333334 + }, + { + "case": "band60-many", + "pairs": 0, + "seeds": 387, + "base_allocations": 413, + "base_allocated_bytes": 123320, + "base_median_seconds": 0.053934558, + "base_min_seconds": 0.052355847, + "base_max_seconds": 0.054881975, + "base_median_rss_kib": 3968, + "candidate_allocations": 413, + "candidate_allocated_bytes": 123320, + "candidate_median_seconds": 0.056821804, + "candidate_min_seconds": 0.056022395, + "candidate_max_seconds": 0.058596483, + "candidate_median_rss_kib": 3968, + "runtime_ratio": 1.0535323938317989, + "rss_ratio": 1.0 + }, + { + "case": "band60-many", + "pairs": 1, + "seeds": 387, + "base_allocations": 419, + "base_allocated_bytes": 198200, + "base_median_seconds": 0.273814731, + "base_min_seconds": 0.269566864, + "base_max_seconds": 0.278868291, + "base_median_rss_kib": 3968, + "candidate_allocations": 419, + "candidate_allocated_bytes": 198200, + "candidate_median_seconds": 0.290707218, + "candidate_min_seconds": 0.279244837, + "candidate_max_seconds": 0.294199598, + "candidate_median_rss_kib": 4096, + "runtime_ratio": 1.061693127094758, + "rss_ratio": 1.032258064516129 + }, + { + "case": "band60-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 17, + "base_allocated_bytes": 88656, + "base_median_seconds": 0.009820441, + "base_min_seconds": 0.009427804, + "base_max_seconds": 0.010125613, + "base_median_rss_kib": 3840, + "candidate_allocations": 17, + "candidate_allocated_bytes": 88656, + "candidate_median_seconds": 0.011554484, + "candidate_min_seconds": 0.01135541, + "candidate_max_seconds": 0.011706068, + "candidate_median_rss_kib": 3968, + "runtime_ratio": 1.1765748605383406, + "rss_ratio": 1.0333333333333334 + }, + { + "case": "band60-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 23, + "base_allocated_bytes": 163536, + "base_median_seconds": 0.022700059, + "base_min_seconds": 0.022384748, + "base_max_seconds": 0.023016294, + "base_median_rss_kib": 3840, + "candidate_allocations": 23, + "candidate_allocated_bytes": 163536, + "candidate_median_seconds": 0.024626334, + "candidate_min_seconds": 0.023964181, + "candidate_max_seconds": 0.024715982, + "candidate_median_rss_kib": 3968, + "runtime_ratio": 1.0848577089601397, + "rss_ratio": 1.0333333333333334 + } + ] +} diff --git a/doc/benchmarks/seed-extension/leftmost-heuristic-samples.tsv b/doc/benchmarks/seed-extension/leftmost-heuristic-samples.tsv new file mode 100644 index 00000000..96497f47 --- /dev/null +++ b/doc/benchmarks/seed-extension/leftmost-heuristic-samples.tsv @@ -0,0 +1,81 @@ +variant case pairs allocations allocated_bytes seconds rss_kib z pair_moment boundaries checksum seeds repetition +base dense40-many 0 1399 222760 0.387878455 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 0 +candidate dense40-many 0 1399 222760 0.406939969 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 0 +candidate dense40-many 0 1399 222760 0.406690141 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 1 +base dense40-many 0 1399 222760 0.389085589 4096 797062906.1375248 0.0 219109 797062906.1375248 1369 1 +base dense40-many 0 1399 222760 0.386887045 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 2 +candidate dense40-many 0 1399 222760 0.407022865 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 2 +candidate dense40-many 0 1399 222760 0.419192612 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 3 +base dense40-many 0 1399 222760 0.402965835 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 3 +base dense40-many 0 1399 222760 0.394904655 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 4 +candidate dense40-many 0 1399 222760 0.426235313 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 4 +base dense40-many 1 1405 281640 2.638791819 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 0 +candidate dense40-many 1 1405 281640 2.727327375 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 0 +candidate dense40-many 1 1405 281640 2.729402014 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 1 +base dense40-many 1 1405 281640 2.532448838 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 1 +base dense40-many 1 1405 281640 2.546715906 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 2 +candidate dense40-many 1 1405 281640 2.64812584 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 2 +candidate dense40-many 1 1405 281640 2.71272588 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 3 +base dense40-many 1 1405 281640 2.530385913 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 3 +base dense40-many 1 1405 281640 2.6085785 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 4 +candidate dense40-many 1 1405 281640 2.726610936 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 4 +base dense40-few 0 18 88720 0.050365283 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 0 +candidate dense40-few 0 18 88720 0.058165464 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 0 +candidate dense40-few 0 18 88720 0.059297688 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 1 +base dense40-few 0 18 88720 0.04883356 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 1 +base dense40-few 0 18 88720 0.048262158 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 2 +candidate dense40-few 0 18 88720 0.059829666 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 2 +candidate dense40-few 0 18 88720 0.063792269 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 3 +base dense40-few 0 18 88720 0.048952102 3712 4104189.9800280402 0.0 1834 4104189.9800280402 3 3 +base dense40-few 0 18 88720 0.048803013 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 4 +candidate dense40-few 0 18 88720 0.060384968 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 4 +base dense40-few 1 24 147600 0.132416331 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 0 +candidate dense40-few 1 24 147600 0.136756092 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 0 +candidate dense40-few 1 24 147600 0.135749123 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 1 +base dense40-few 1 24 147600 0.127119946 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 1 +base dense40-few 1 24 147600 0.129949724 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 2 +candidate dense40-few 1 24 147600 0.139192692 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 2 +candidate dense40-few 1 24 147600 0.133927556 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 3 +base dense40-few 1 24 147600 0.128197658 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 3 +base dense40-few 1 24 147600 0.125294192 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 4 +candidate dense40-few 1 24 147600 0.132237165 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 4 +base band60-many 0 413 123320 0.053934558 3968 232701353.12981105 0.0 38669 232701353.12981105 387 0 +candidate band60-many 0 413 123320 0.058596483 3968 232701353.12981105 0.0 38669 232701353.12981105 387 0 +candidate band60-many 0 413 123320 0.056821804 4096 232701353.12981105 0.0 38669 232701353.12981105 387 1 +base band60-many 0 413 123320 0.05373842 3968 232701353.12981105 0.0 38669 232701353.12981105 387 1 +base band60-many 0 413 123320 0.052355847 3968 232701353.12981105 0.0 38669 232701353.12981105 387 2 +candidate band60-many 0 413 123320 0.056042863 4096 232701353.12981105 0.0 38669 232701353.12981105 387 2 +candidate band60-many 0 413 123320 0.057982791 3968 232701353.12981105 0.0 38669 232701353.12981105 387 3 +base band60-many 0 413 123320 0.054230052 3840 232701353.12981105 0.0 38669 232701353.12981105 387 3 +base band60-many 0 413 123320 0.054881975 4096 232701353.12981105 0.0 38669 232701353.12981105 387 4 +candidate band60-many 0 413 123320 0.056022395 3968 232701353.12981105 0.0 38669 232701353.12981105 387 4 +base band60-many 1 419 198200 0.273814731 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 0 +candidate band60-many 1 419 198200 0.294199598 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 0 +candidate band60-many 1 419 198200 0.292134054 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 1 +base band60-many 1 419 198200 0.27547212 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 1 +base band60-many 1 419 198200 0.278868291 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 2 +candidate band60-many 1 419 198200 0.279244837 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 2 +candidate band60-many 1 419 198200 0.288482554 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 3 +base band60-many 1 419 198200 0.269566864 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 3 +base band60-many 1 419 198200 0.272599351 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 4 +candidate band60-many 1 419 198200 0.290707218 3968 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 4 +base band60-few 0 17 88656 0.009677242 3840 3312567.1709916 0.0 990 3312567.1709916 3 0 +candidate band60-few 0 17 88656 0.011554484 3968 3312567.1709916 0.0 990 3312567.1709916 3 0 +candidate band60-few 0 17 88656 0.011436983 3968 3312567.1709916 0.0 990 3312567.1709916 3 1 +base band60-few 0 17 88656 0.009865425 3712 3312567.1709916 0.0 990 3312567.1709916 3 1 +base band60-few 0 17 88656 0.009820441 3840 3312567.1709916 0.0 990 3312567.1709916 3 2 +candidate band60-few 0 17 88656 0.011565213 3968 3312567.1709916 0.0 990 3312567.1709916 3 2 +candidate band60-few 0 17 88656 0.01135541 3840 3312567.1709916 0.0 990 3312567.1709916 3 3 +base band60-few 0 17 88656 0.009427804 3968 3312567.1709916 0.0 990 3312567.1709916 3 3 +base band60-few 0 17 88656 0.010125613 3840 3312567.1709916 0.0 990 3312567.1709916 3 4 +candidate band60-few 0 17 88656 0.011706068 3840 3312567.1709916 0.0 990 3312567.1709916 3 4 +base band60-few 1 23 163536 0.022700059 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 0 +candidate band60-few 1 23 163536 0.024217076 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 0 +candidate band60-few 1 23 163536 0.024715982 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 1 +base band60-few 1 23 163536 0.023016294 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 1 +base band60-few 1 23 163536 0.022969846 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 2 +candidate band60-few 1 23 163536 0.024626334 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 2 +candidate band60-few 1 23 163536 0.02466719 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 3 +base band60-few 1 23 163536 0.022547444 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 3 +base band60-few 1 23 163536 0.022384748 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 4 +candidate band60-few 1 23 163536 0.023964181 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 4 diff --git a/doc/benchmarks/seed-extension/leftmost-source-hashes.json b/doc/benchmarks/seed-extension/leftmost-source-hashes.json new file mode 100644 index 00000000..d956e948 --- /dev/null +++ b/doc/benchmarks/seed-extension/leftmost-source-hashes.json @@ -0,0 +1,123 @@ +{ + "baseline": "985ba44bdc16e92162e954b7baffa3f3923d4776", + "compiler": "x86_64-conda-linux-gnu-g++ (conda-forge gcc 14.4.0-7) 14.4.0", + "commands": [ + [ + "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++", + "-std=c++23", + "-O3", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + "-I/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/base", + "-I/home/alex/CodexProjects/IntaRNA/pr-worktrees/issue-268/src", + "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/driver.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/base/IntaRNA/SeededPartitionFunction.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/base/IntaRNA/StackSeedDomain.cpp", + "-Wl,--gc-sections", + "-Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib", + "-o", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/base/benchmark" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++", + "-std=c++23", + "-O3", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + "-I/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/candidate", + "-I/home/alex/CodexProjects/IntaRNA/pr-worktrees/issue-268/src", + "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/driver.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/candidate/IntaRNA/SeededPartitionFunction.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/candidate/IntaRNA/StackSeedDomain.cpp", + "-Wl,--gc-sections", + "-Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib", + "-o", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark/candidate/benchmark" + ] + ], + "sha256": { + "driver.cpp": "98b0133e334b5174146ed440b0ed0f30faf2f83d32f217a386c3d43196f65b93", + "build.py": "6a086dd9fa12f3fef28789d2401bd77a691321314cbecd495d9e4869e7b79308", + "run.py": "22298ce92cb6940ad23164cac868aa638343d58f251a66ef5e5cc66dae1dee4e", + "base/IntaRNA/SeededPartitionFunction.cpp": "8be22d72f0cb67040a1fb23170da1c88d7f0a8a429b6d2aefae30164fc28795e", + "base/IntaRNA/SeededPartitionFunction.h": "d5002fe387d6ab6a526bd4bff518615c1954862c9a68c3b828f3a74b33080f13", + "base/IntaRNA/StackSeedDomain.cpp": "fccecca0fc31f97ad51e742727d545b1420af534e40b26801deba90850770362", + "base/IntaRNA/StackSeedDomain.h": "5708d0d71b23e66a858a50c0d13d84362e40e208fd69620c4dbf3115e18c9e10", + "candidate/IntaRNA/SeededPartitionFunction.cpp": "3783c8380e5108146cd903cbbb3fa3d0523749baf713395a15a4f436b3dfdd03", + "candidate/IntaRNA/SeededPartitionFunction.h": "8216d82858cca5cf960056d7e11a2ada1118a98e1cf513623839d75fab40381d", + "candidate/IntaRNA/StackSeedDomain.cpp": "a41906b5442e5b8ade69dc482919619c28761b908447faab984a84a807d338ee", + "candidate/IntaRNA/StackSeedDomain.h": "73275a75976d98800db58bfb019f912b7245a9c26072fe17f7ee16dbdaad93c7" + }, + "shared_header_sha256": { + "PartitionArithmetic.h": "5fe71abe6568906057c0a264c2802d3bfd7a902471b21cc838b78f18b04a0bd7", + "Matrix.h": "8ecc55e46643ccde16ed89aa63a415a6da7aa3d3c59fe94dd6b4d46d5f5275af", + "Interaction.h": "c0ce17a0f5b204bcb366529872346a2316993b749da9c4c9927773726296ebaa", + "general.h": "0fff34ca1a1e676debe88326af0a5a0eddeff20e05604abda11096527b12df98", + "intarna_config.h": "4cbc306a33335dd49061ed4e3d70f7f70e776f5aa8a6e0e73ea6715cfa4163c2" + }, + "measurements": { + "date_utc": "2026-10-16T11:45:07.333125+00:00", + "platform": "Linux-6.8.0-147-generic-x86_64-with-glibc2.39", + "cpu": "AMD Ryzen 5 7530U with Radeon Graphics", + "threads": 1, + "repetitions": 5, + "cpu_affinity": "not pinned", + "concurrent_project_builds_or_tests": false, + "commands": [ + [ + "python3", + "doc/benchmarks/seed-extension/run.py", + "--build-dir", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark", + "--output-dir", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-exact", + "--repetitions", + "5" + ], + [ + "python3", + "doc/benchmarks/seed-extension/run.py", + "--build-dir", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-benchmark", + "--output-dir", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/leftmost-heuristic", + "--repetitions", + "5", + "--heuristic" + ] + ], + "notes": [ + "Five alternating baseline/candidate fresh-process samples per fixture and output mode.", + "Exact and heuristic matrices run sequentially, with OMP_NUM_THREADS=1.", + "Other workstation activity and CPU frequency were uncontrolled.", + "RSS is GNU time whole-process maximum resident set size, not isolated kernel peak allocation.", + "Allocation counters measure ordinary new/new[] traffic during compute(), excluding seed construction and serialization." + ] + }, + "executable_sha256": { + "base": "3e6c276b2ecf9eb0543acb8769a5c3bd1067e1448712be1695af01e58ad1509d", + "candidate": "2f8d146f5d9036268a1d59a2c0602f04eb1b3105ad05b8d5be89119806694cb2" + }, + "verification_dump_sha256": { + "leftmost-exact/band60-few-base.txt": "43495b154a29bdffc13d52dc35369a07b6817be30966ca6d9826126dc3ee7d0d", + "leftmost-exact/band60-few-candidate.txt": "f6a8651d3f85153ba1340ca9e7dfbe943fcfaab3c4e6fa3bead711c6735ebe7f", + "leftmost-exact/band60-many-base.txt": "07ef543c5af8fdd86688641d55a491335a61dc335ccc2a8081fede7626bc9138", + "leftmost-exact/band60-many-candidate.txt": "fcdd7b7db7db7d20ae1059580bbdb532303580a21d6118a286054f22e760d94e", + "leftmost-exact/dense40-few-base.txt": "b1584e72356aa146687930b6b7f96e2b32f261ca84924602426c96b303457000", + "leftmost-exact/dense40-few-candidate.txt": "9a5cba3f768f540ff2b2078b82ebd15efcbcb98b18582d3344e379467c548f7e", + "leftmost-exact/dense40-many-base.txt": "dccb884aa314b88c205cf359f82cbf7747c153fc2d6d8d8c7fd0417678622802", + "leftmost-exact/dense40-many-candidate.txt": "3f07dc4f1b9556a0438a9f8521ae32dbcbe981c5421ca4570c79b6bf04103b87", + "leftmost-heuristic/band60-few-base.txt": "b70097c40696705eda01ed157fd8b3863a7d4e4b4b8d1dcd76042a135bf6bff7", + "leftmost-heuristic/band60-few-candidate.txt": "b70097c40696705eda01ed157fd8b3863a7d4e4b4b8d1dcd76042a135bf6bff7", + "leftmost-heuristic/band60-many-base.txt": "fbecd2caeba9e2e0d9191dde17cd6d2598b65793c431b8bee8357159d4f817b6", + "leftmost-heuristic/band60-many-candidate.txt": "fbecd2caeba9e2e0d9191dde17cd6d2598b65793c431b8bee8357159d4f817b6", + "leftmost-heuristic/dense40-few-base.txt": "02c503ef9f86cbde99e40a977963e62e8baf60fe47dc8dad81f7f51264da878f", + "leftmost-heuristic/dense40-few-candidate.txt": "02c503ef9f86cbde99e40a977963e62e8baf60fe47dc8dad81f7f51264da878f", + "leftmost-heuristic/dense40-many-base.txt": "7f9071f1962facde0a9e5691ffa8ff50c7bd14399f009c5997faf9a7313f183e", + "leftmost-heuristic/dense40-many-candidate.txt": "7f9071f1962facde0a9e5691ffa8ff50c7bd14399f009c5997faf9a7313f183e" + } +} diff --git a/doc/benchmarks/seed-extension/run.py b/doc/benchmarks/seed-extension/run.py index c77d44b0..a0ae08de 100644 --- a/doc/benchmarks/seed-extension/run.py +++ b/doc/benchmarks/seed-extension/run.py @@ -3,18 +3,23 @@ import argparse import csv import json +import math import os import statistics import subprocess parser=argparse.ArgumentParser(description='Verify and time the isolated seeded kernels; requires GNU time.') parser.add_argument('--build-dir',type=Path,required=True) +parser.add_argument('--output-dir',type=Path,help='results directory; defaults to the build directory') parser.add_argument('--repetitions',type=int,default=3) parser.add_argument('--heuristic',action='store_true',help='use frozen endpoint groups (requires baseline PR #266 or later)') +parser.add_argument('--verify-only',action='store_true',help='compare full dumps without collecting timed samples') args=parser.parse_args() if args.repetitions<1: parser.error('--repetitions must be positive') root=args.build_dir.resolve() +output=args.output_dir.resolve() if args.output_dir else root +output.mkdir(parents=True,exist_ok=True) env=dict(os.environ,OMP_NUM_THREADS='1') cases=[('dense','many',40),('dense','few',40),('band','many',60),('band','few',60)] samples=[] @@ -25,7 +30,7 @@ def call(variant,case,pairs,dump='-'): command=[str(root/variant/'benchmark'),topology,density,str(n),str(pairs),dump] if args.heuristic: command.append('heuristic') - rss=root/'last-rss.txt' + rss=output/'last-rss.txt' result=subprocess.check_output(['/usr/bin/time','-f','%M','-o',str(rss),*command],text=True,env=env) seconds,z,moment,boundaries,checksum,seeds,allocations,allocated_bytes=result.strip().split('\t') return {'variant':variant,'case':f'{topology}{n}-{density}','pairs':pairs, @@ -37,25 +42,35 @@ def read_dump(path): for line in path.read_text().splitlines(): fields=line.split() if fields[0]=='b': - values[tuple(fields[:5])]=tuple(map(float,fields[5:])) + key,value=tuple(fields[:5]),tuple(map(float,fields[5:])) else: - values[tuple(fields[:-1])]=(float(fields[-1]),) + key,value=tuple(fields[:-1]),(float(fields[-1]),) + assert key not in values,(path,'duplicate dump entry',key) + assert all(math.isfinite(number) for number in value),(path,key,value) + values[key]=value return values for case in cases: name=f'{case[0]}{case[2]}-{case[1]}' - paths={variant:root/f'{name}-{variant}.txt' for variant in ('base','candidate')} + paths={variant:output/f'{name}-{variant}.txt' for variant in ('base','candidate')} for variant,path in paths.items(): call(variant,case,1,str(path)) before,after=(read_dump(paths[variant]) for variant in ('base','candidate')) assert before.keys()==after.keys(),name maximum=0.0 + by_kind={kind:0.0 for kind in ('b','p','z')} for key,values in before.items(): + assert len(values)==len(after[key]),(name,key) for a,b in zip(values,after[key]): relative=abs(a-b)/max(abs(a),abs(b)) if a or b else 0.0 maximum=max(maximum,relative) + by_kind[key[0]]=max(by_kind[key[0]],relative) assert relative<1e-12,(name,key,a,b,relative) - checks.append({'case':name,'compared_entries':len(before),'max_relative_error':maximum}) + checks.append({'case':name,'compared_entries':len(before),'max_relative_error':maximum, + 'max_relative_error_by_kind':by_kind}) + print(name,checks[-1],flush=True) + if args.verify_only: + continue for pairs in (0,1): for repetition in range(args.repetitions): # Alternate order to reduce a consistent order bias. @@ -64,9 +79,14 @@ def read_dump(path): sample=call(variant,case,pairs) sample['repetition']=repetition samples.append(sample) - print(name,checks[-1],flush=True) -with (root/'samples.tsv').open('w') as stream: +metadata={'heuristic':args.heuristic,'threads':1,'relative_tolerance':1e-12, + 'repetitions':0 if args.verify_only else args.repetitions,'checks':checks} +if args.verify_only: + (output/'verification.json').write_text(json.dumps(metadata,indent=2)+'\n') + raise SystemExit(0) + +with (output/'samples.tsv').open('w') as stream: writer=csv.DictWriter(stream,fieldnames=list(samples[0]),delimiter='\t',lineterminator='\n') writer.writeheader();writer.writerows(samples) summary=[] @@ -87,5 +107,5 @@ def read_dump(path): result['runtime_ratio']=result['candidate_median_seconds']/result['base_median_seconds'] result['rss_ratio']=result['candidate_median_rss_kib']/result['base_median_rss_kib'] summary.append(result) -(root/'results.json').write_text(json.dumps({'heuristic':args.heuristic,'checks':checks,'summary':summary},indent=2)+'\n') +(output/'results.json').write_text(json.dumps({**metadata,'summary':summary},indent=2)+'\n') print(json.dumps(summary,indent=2),flush=True) diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index cf95cbe8..f3a643d7 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -6,10 +6,353 @@ #include namespace IntaRNA { +namespace { +using Kernel=SeededPartitionFunction; +using Pair=Kernel::Pair; +using A=PartitionArithmetic; +using Support=std::array; +using RightStates=std::array; + +/** Exact left-most-anchor factorization, with bounded boundary publication. */ +Kernel::Result +computeAnchored(const Kernel::Domain & d,const StackSeedDomain & seeds, + const Kernel::Weights & w,bool pairs) +{ + Kernel::Result out; + if(pairs) out.mass=Matrix(d.n,d.m,0); + if(!d.n || !d.m || !seeds.maxSeedLength() || !d.span1 || !d.span2) return out; + + // Containing seeds add no paths to the exact union. In the remaining family, + // start order equals end order along each diagonal, including overlapping + // seeds. Thus excluding an earlier seed while extending left is sufficient; + // no earlier seed can first finish beyond the anchor's right end. + const auto anchors=seeds.minimalOccurrences(); + std::unordered_map anchorIndex; + anchorIndex.reserve(anchors.size()); + size_t cap=2; + for(size_t a=0;a left(cells,cap),leftTotal(maxN,maxM),rightTotal(maxN,maxM); + Matrix right(maxN,maxM); + Matrix dLeft,dLeftTotal; + Matrix dRight; + if(pairs) { + dLeft.resize(cells,cap,false); dLeftTotal.resize(maxN,maxM,false); + dRight.resize(maxN,maxM,false); + } + Matrix valid(d.n,d.m); + for(size_t i=0;i hybrid(batchSize,cells),boundary; + if(pairs) boundary.resize(batchSize,cells,false); + std::vector starts; + std::vector initiation; + std::vector candidates; + std::vector included(anchors.size(),0); + starts.reserve(batchSize); initiation.reserve(batchSize); + candidates.reserve(anchors.size()); + auto reset=[](Matrix & matrix,size_t rows,size_t columns) { + if(columns==matrix.size2()) std::fill_n(&matrix(0,0),rows*columns,T{}); + else for(size_t i=0;i & matrix,size_t rows,size_t columns) { + // Left states use a fixed maxM row stride and a contiguous stack axis. + for(size_t i=0;isecond].length; + }; + auto nonstack=[&](size_t i,size_t j,const auto & visit) { + for(size_t u=1;u<=i && u-1<=d.loop1;++u) + for(size_t v=1;v<=j && v-1<=d.loop2;++v) + if(u!=1 || v!=1) visit(i-u,j-v); + }; + auto fill=[&](size_t a) { + const auto anchor=anchors[a]; + s={anchor.i,anchor.j}; length=anchor.length; + t={s.first+length-1,s.second+length-1}; + singleton=d.noLP && length==1; + supportBegin=singleton?0:1; + // Crop both extension rectangles to the batch's possible left boundaries. + // Paths to these sources cannot visit positions beyond their leftmost + // coordinates; their mandatory left offsets also bound the right side. + nL=0; mL=0; + size_t minLeft1=maxN,minLeft2=maxM; + for(const Pair p:starts) { + if(p.first>s.first || p.second>s.second) continue; + const size_t u=s.first-p.first,v=s.second-p.second; + if(u>maxN-length || v>maxM-length) continue; + nL=std::max(nL,u+1); mL=std::max(mL,v+1); + minLeft1=std::min(minLeft1,u); minLeft2=std::min(minLeft2,v); + } + if(!nL || !mL) { seedWeight=0; return; } + nR=std::min(d.n-t.first,maxN-length-minLeft1+1); + mR=std::min(d.m-t.second,maxM-length-minLeft2+1); + resetLeft(left,nL,mL); reset(leftTotal,nL,mL); + reset(right,nR,mR); reset(rightTotal,nR,mR); + // Seed energy consists of its actual stack transitions. Initiation is + // attached later to the complete interaction's left boundary exactly once. + seedWeight=1; + for(size_t k=0;ks.first || p.second>s.second) continue; + const size_t u=s.first-p.first,v=s.second-p.second; + if(u0 || leftTotal(u,v)[1]>0)) { reachable=true; break; } + } + if(!reachable) { seedWeight=0; return; } + right(0,0)[singleton?0:3]=1; + for(size_t i=0;is.first || p.second>s.second) continue; + const size_t u=s.first-p.first,v=s.second-p.second; + if(u>=nL || v>=mL || (leftTotal(u,v)[0]==0 && leftTotal(u,v)[1]==0)) continue; + const Z_type source=A::multiply(initiation[b],seedWeight); + Support sourceLeft{}; + for(size_t h=supportBegin;h<2;++h) sourceLeft[h]=A::multiply(source,leftTotal(u,v)[h]); + for(size_t i=0;i0;--ii) for(size_t jj=mL;jj>0;--jj) { + const size_t i=ii-1,j=jj-1; const Pair p=leftPair(i,j); + if(!valid(p.first,p.second)) continue; + const size_t forbidden=earlierLength(p); + for(size_t c=d.noLP && !(singleton && i==0 && j==0)?1:0;c0;--ii) for(size_t jj=mR;jj>0;--jj) { + const size_t i=ii-1,j=jj-1; const Pair q=rightPair(i,j); + if(!valid(q.first,q.second)) continue; + for(size_t c=supportBegin*2;c<4;++c) if(right(i,j)[c]==0) dRight(i,j)[c]=0; + if(i || j) for(size_t c=supportBegin*2;c<4;++c) out.mass(q.first,q.second)=A::add( + out.mass(q.first,q.second),A::multiply(right(i,j)[c],dRight(i,j)[c])); + if(i && j) { + const Pair p=rightPair(i-1,j-1); + if(valid(p.first,p.second)) { + const Z_type edge=A::check(w.transition(p,q)); + for(size_t h=supportBegin;h<2;++h) { + const size_t support=singleton && i==1 && j==1?1:h; + if(dRight(i,j)[support*2+1]==0) continue; + const Z_type adj=A::multiply(edge,dRight(i,j)[support*2+1]); + for(size_t c=0;c<2;++c) if(right(i-1,j-1)[h*2+c]>0) + dRight(i-1,j-1)[h*2+c]=A::add(dRight(i-1,j-1)[h*2+c],adj); + } + } + } + nonstack(i,j,[&](size_t u,size_t v) { + const Pair p=rightPair(u,v); if(!valid(p.first,p.second)) return; + if((dRight(i,j)[d.noLP?0:1]==0 && dRight(i,j)[2+(d.noLP?0:1)]==0) + || (rightTotal(u,v)[0]==0 && rightTotal(u,v)[1]==0)) return; + const Z_type edge=A::check(w.transition(p,q)); + for(size_t h=supportBegin;h<2;++h) { + const size_t frontier=singleton && u==0 && v==0?0:1; + auto & adj=dRight(u,v)[h*2+frontier]; + adj=A::add(adj,A::multiply(edge,dRight(i,j)[h*2+(d.noLP?0:1)])); + } + }); + } + }; + + auto processBatch=[&]() { + reset(hybrid,starts.size(),cells); if(pairs) reset(boundary,starts.size(),cells); + candidates.clear(); initiation.clear(); + // Query only anchor starts inside the batch's span boxes, then restore + // deterministic seed order independently of hash-table iteration order. + for(const Pair p:starts) { + initiation.push_back(A::check(w.initiation(p))); + for(size_t i=p.first;isecond]) continue; + const auto & anchor=anchors[found->second]; + if(anchor.length>maxN-(i-p.first) || anchor.length>maxM-(j-p.second)) continue; + included[found->second]=1; candidates.push_back(found->second); + } + } + std::sort(candidates.begin(),candidates.end()); + for(const size_t a:candidates) { fill(a); contract(false); } + for(size_t b=0;b; // pending lonely pair / stack-supported end Result out; @@ -23,7 +366,8 @@ compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool const size_t length=seeds.seedEndingLength(i,j); if(length) selected.emplace(Pair(i,j),w.selectRight({i+1-length,j+1-length},{i,j})); } - // Seed-free prefixes retain stack length; right extensions need only noLP. + // Seed-free heuristic prefixes retain stack length; right extensions need + // only noLP, together with the frozen right-endpoint selection. const size_t cap=std::max(size_t(2),seeds.maxSeedLength()); // Allocate the maximal inclusive span once. Offset zero is a stored pair, // so an extension of at most span-1 positions needs span cells, even for @@ -108,9 +452,9 @@ compute(const Domain & d, const StackSeedDomain & seeds, const Weights & w, bool for(size_t c=d.noLP?1:0;c namespace IntaRNA { -/** Nonnegative first-seed extension inside/outside computation. A path belongs - * to its first completed admitted seed; seed-free left prefixes are joined to - * unrestricted right extensions. Co-ending seeds use the shortest as owner. - * Native energy ownership is supplied by the - * adapter: initiation once, one transition per successive pair, one boundary - * factor. All callbacks must be deterministic; zero denotes forbidden weight. +/** Nonnegative seeded extension inside/outside computation. Exact paths belong + * to their left-most seed in a union-equivalent family that excludes containing + * occurrences. Shared seed-local tables join seed-avoiding left extensions to + * unrestricted right extensions. Complete boundaries are accumulated in bounded + * batches; anchors crossing batches are recomputed. A heuristic selector retains + * its first-completed-seed ownership, choosing the shortest co-ending seed. + * Native energy ownership is supplied by the adapter: initiation once, one + * transition per successive pair, one boundary factor. All callbacks must be + * deterministic; zero denotes forbidden weight. */ class SeededPartitionFunction { public: diff --git a/src/IntaRNA/StackSeedDomain.cpp b/src/IntaRNA/StackSeedDomain.cpp index 79f89a0b..316ebef5 100644 --- a/src/IntaRNA/StackSeedDomain.cpp +++ b/src/IntaRNA/StackSeedDomain.cpp @@ -78,4 +78,25 @@ forEachStart(const std::function & visit) const { } } } + +std::vector +StackSeedDomain::minimalOccurrences() const +{ + // Every path containing an outer occurrence also contains its embedded seed. + // Removing only these supersets preserves both the union and every pair mass. + std::vector result; + result.reserve(lengths.size()); + for (const auto & [start,length]:lengths) { + bool redundant=false; + for (size_t k=1;k minimalOccurrences() const; /** Enumerate each eligible left boundary once, in row-major order. * The caller intersects these boxes with accessible complementary vertices. * @param visit callback receiving local first/second coordinates diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 42523506..22671495 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -896,3 +896,93 @@ TEST_CASE("native singleton and mixed explicit seeds count overlapping witnesses REQUIRE(double(result.rawMasses()(i,3-j))==Approx(double(ref.mass[{i,j}])).epsilon(2e-12)); } } + +TEST_CASE("native exact nested explicit seeds preserve cropped boundary ensembles", "[BasePairProbabilities][PredictorSeedOracle][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + using namespace seeded_oracle; + RnaSequence target("target","GGCGGCGG"),query("query","CCGCCGCCC"); + AccessibilityBasePair targetAcc(target,6,nullptr),queryAcc(query,5,nullptr); + ReverseAccessibility reverseQuery(queryAcc); + InteractionEnergyBasePair energy(targetAcc,reverseQuery,1,2,false,1,-100,3,37); + const SeedConstraint sc(1,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(), + "2|||||&3|||||,3|&6|,4||&4||,6||&2||,1||&8||",false,false,false); + // These are independent declared occurrences in increasing energy coordinates. + // The five-pair seed starts before, but completes after, the inner singleton. + const std::vector declared{ + {{1,2},{2,3},{3,4},{4,5},{5,6}},{{2,3}}, + {{3,4},{4,5}},{{5,6},{6,7}},{{0,0},{1,1}}}; + for(bool cropped:{false,true}) for(bool noLP:{false,true}) { + CAPTURE(cropped,noLP); + const IndexRange r1=cropped?IndexRange(1,6):IndexRange(0,7); + const IndexRange r2=cropped?IndexRange(2,7):IndexRange(0,8); + const size_t n=r1.to-r1.from+1,m=r2.to-r2.from+1; + auto absolute=[&](Pair p){return Pair{p.first+r1.from,p.second+r2.from};}; + auto weight=[&](E_type e){return E_isINF(e)?0.L:static_cast(energy.getBoltzmannWeight(e));}; + Model model{n,m,6,5,noLP,{}, + [&](Pair p){p=absolute(p);return energy.areComplementary(p.first,p.second);}, + [&](Pair){return weight(energy.getE_init());}, + [&](Pair p,Pair q){ + if(q.first-p.first-1>1 || q.second-p.second-1>2) return 0.L; + p=absolute(p);q=absolute(q); + return weight(energy.getE_interLeft(p.first,q.first,p.second,q.second)); + }, + [&](Pair p,Pair q){ + p=absolute(p);q=absolute(q); + return weight(energy.getE(p.first,q.first,p.second,q.second,E_type(0))); + }}; + for(const auto & seed:declared) { + if(seed.front().firstr1.to || seed.back().second>r2.to) continue; + Chain local; + for(const auto p:seed) local.push_back({p.first-r1.from,p.second-r2.from}); + model.seeds.push_back(local); + } + const auto expected=enumerate(model); + REQUIRE(expected.z>0); + const auto oc=makeOutputConstraint(noLP); + BasePairProbabilities result(target.size(),query.size()); + OutputHandlerInteractionList output(oc,1); + ExactSeedProbe predictor(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),&result); + predictor.predict(r1,r2);result.finalize(); + REQUIRE(double(result.getZ())==Approx(double(expected.z)).epsilon(2e-12)); + REQUIRE(result.getZ()==predictor.getZall()); + REQUIRE(output.getZ()==predictor.getZall()); + for(const auto & [boundary,z]:expected.boundaries) + REQUIRE(double(predictor.boundaries[boundary])==Approx(double(z)).epsilon(2e-12)); + for(const auto & [boundary,z]:predictor.boundaries) { + const auto found=expected.boundaries.find(boundary); + REQUIRE(found!=expected.boundaries.end()); + REQUIRE(double(z)==Approx(double(found->second)).epsilon(2e-12)); + } + Matrix masses(target.size(),query.size(),0); + for(const auto & [pair,z]:expected.mass) { + const Pair p=absolute(pair); + masses(p.first,query.size()-1-p.second)=Z_type(z); + } + for(size_t i=0;i energies; + for(const auto & [boundary,z]:expected.boundaries) { + if(z==0) continue; + const Pair p=absolute({boundary[0],boundary[1]}); + const Pair q=absolute({boundary[2],boundary[3]}); + const E_type boundaryE=energy.getE(p.first,q.first,p.second,q.second,E_type(0)); + const E_type ensemble=energy.getE(Z_type(z/weight(boundaryE)))+boundaryE; + energies[boundary]=ensemble;best=std::min(best,ensemble); + } + REQUIRE_FALSE(output.empty()); + const Interaction & reported=**output.begin(); + REQUIRE(reported.isValid()); + REQUIRE(reported.energy==best); + const auto & first=reported.basePairs.front(); + const auto & last=reported.basePairs.back(); + const Boundary reportedBoundary{first.first-r1.from,query.size()-1-first.second-r2.from, + last.first-r1.from,query.size()-1-last.second-r2.from}; + REQUIRE(energies.at(reportedBoundary)==best); + } +} diff --git a/tests/SeededPartitionFunction_test.cpp b/tests/SeededPartitionFunction_test.cpp index be0aac98..dece4b3b 100644 --- a/tests/SeededPartitionFunction_test.cpp +++ b/tests/SeededPartitionFunction_test.cpp @@ -1,6 +1,7 @@ #include "catch.hpp" #include "SeededChainOracle.h" #include +#include TEST_CASE("seeded chain oracle counts structures instead of seed witnesses", "[SeededPartitionFunction]") { using namespace seeded_oracle; @@ -60,10 +61,24 @@ Kernel::Weights weightsFor(const seeded_oracle::Model & m) { [&](auto p,auto q){return Z_type(m.edge(p,q));}, [&](auto p,auto q){return Z_type(m.boundary(p,q));},{},m.selectRight}; } -void compareKernels(const seeded_oracle::Model & m) { - const auto oracle=seeded_oracle::enumerate(m); +void compareKernels(const seeded_oracle::Model & m, + const size_t loop1=std::numeric_limits::max(), + const size_t loop2=std::numeric_limits::max()) { + // Gate the independent path enumeration explicitly, leaving the kernel's + // weight callback unrestricted so that its loop limits are tested as well. + auto enumerated=m; + enumerated.edge=[&](auto p,auto q) { + return q.first-p.first-1>loop1 || q.second-p.second-1>loop2 + ? 0.L:m.edge(p,q); + }; + const auto oracle=seeded_oracle::enumerate(enumerated); + // Boundary publication includes a positive hybrid even when accessibility + // gives it zero final weight. Enumerate hybrids independently of that factor. + auto hybridModel=enumerated; + hybridModel.boundary=[](auto,auto){return 1.L;}; + const auto hybrids=seeded_oracle::enumerate(hybridModel); const auto seeds=domainFor(m); - Kernel::Domain d{m.n,m.m,m.span1,m.span2,m.n,m.m,m.noLP}; + Kernel::Domain d{m.n,m.m,m.span1,m.span2,std::min(loop1,m.n),std::min(loop2,m.m),m.noLP}; // Every allowed vertex as a singleton seed denotes the unconstrained domain. // Check the partition decomposition on all starts, including those pruned in // the original sparse-seed calculation. @@ -71,7 +86,7 @@ void compareKernels(const seeded_oracle::Model & m) { for(size_t i=0;i boundaries; std::map calls; + std::map> coefficients; w.complete=[&](auto p,auto q,auto h,auto b){ const seeded_oracle::Boundary boundary{p.first,p.second,q.first,q.second}; + REQUIRE(h>0); + const auto expected=hybrids.boundaries.find(boundary); + REQUIRE(expected!=hybrids.boundaries.end()); + REQUIRE(double(h)==Approx(double(expected->second)).epsilon(2e-12)); + REQUIRE(b==Z_type(m.boundary(p,q))); boundaries[boundary]+=h*b; + coefficients[boundary]={h,b}; ++calls[boundary]; }; auto got=suffix?Kernel::compute(d,seeds,w,true):seededStack(d,seeds,w,true); REQUIRE(double(got.z)==Approx(double(oracle.z)).epsilon(2e-12)); + for(const auto & [boundary,hybrid]:hybrids.boundaries) + if(hybrid>0) REQUIRE(calls[boundary]==1); for(auto [b,z]:oracle.boundaries) { const auto found=boundaries.find(b); REQUIRE(double(found==boundaries.end()?0:found->second)==Approx(double(z)).epsilon(2e-12)); @@ -116,9 +140,17 @@ void compareKernels(const seeded_oracle::Model & m) { for(const auto mass:rows) REQUIRE(mass<=got.z+std::abs(got.z)*2e-12); for(const auto mass:columns) REQUIRE(mass<=got.z+std::abs(got.z)*2e-12); REQUIRE(double(expectedCount)==Approx(double(oracle.pairCount)).epsilon(2e-12)); + // Requesting outside masses must leave the publication contract unchanged: + // the same complete hybrid sum and boundary factor appear exactly once. + const auto withPairs=boundaries; + const auto withPairCoefficients=coefficients; + boundaries.clear();calls.clear();coefficients.clear(); auto only=suffix?Kernel::compute(d,seeds,w,false):seededStack(d,seeds,w,false); REQUIRE(only.z==got.z); REQUIRE(only.mass.storageSize()==0); + REQUIRE(boundaries==withPairs); + REQUIRE(coefficients==withPairCoefficients); + for(const auto & [boundary,count]:calls) REQUIRE(count==1); } } } @@ -183,6 +215,48 @@ TEST_CASE("first seed acceptance handles source hits overlapping and separated a compareKernels(m); } +TEST_CASE("exact mixed-length seed unions survive nested ownership and cropped spans", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + // On the full diagonal path the five-pair seed starts first, while the + // enclosed singleton completes first. A left-most decomposition must retain + // all paths of that union, including paths that enter the inner seed directly. + Model m{8,9,8,9,false,{ + {{1,2},{2,3},{3,4},{4,5},{5,6}},{{2,3}}, + {{3,4},{4,5}},{{4,5},{5,6},{6,7}},{{0,0},{1,1}}}, + [](Pair p){return p.second==p.first+1 || (p.first+2*p.second)%7!=3;}, + [](Pair p){return .6L+.11L*p.first+.03L*p.second;}, + [](Pair p,Pair q){return stack(p,q)?.9L:.15L+.04L*(q.first-p.first)+.02L*(q.second-p.second);}, + [](Pair p,Pair q){return (p.first+q.second)%5==0?0.L:.3L+.08L*p.second+.06L*q.first;}}; + for(const auto dimensions:{Pair{8,9},Pair{6,7},Pair{4,5}}) + for(const auto spans:{Pair{1,1},Pair{3,5},Pair{5,3},Pair{8,9}}) + for(bool noLP:{false,true}) for(const auto loops:{Pair{0,0},Pair{1,2}}) { + CAPTURE(dimensions.first,dimensions.second,spans.first,spans.second,noLP,loops.first,loops.second); + m.n=dimensions.first;m.m=dimensions.second; + m.span1=spans.first;m.span2=spans.second;m.noLP=noLP; + compareKernels(m,loops.first,loops.second); + } +} + +TEST_CASE("a singleton seed obtains noLP support independently from either side", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + Model m{5,5,5,5,true,{{{2,2}}}, + [](Pair p){return p.first==p.second;},[](Pair){return 2.L;}, + [](Pair p,Pair q){return stack(p,q)?3.L:0.L;},[](Pair,Pair){return 5.L;}}; + const auto expected=enumerate(m); + REQUIRE(expected.boundaries.count({2,2,2,2})==0); + REQUIRE(expected.boundaries.at({1,1,2,2})==30.L); + REQUIRE(expected.boundaries.at({2,2,3,3})==30.L); + REQUIRE(expected.boundaries.at({1,1,3,3})==90.L); + compareKernels(m,0,0); + // A nonstack connection cannot support the seed; neither end of such an + // interaction is rescued by a separate stacked block on the other side. + m.edge=[](Pair p,Pair q){return stack(p,q)?3.L:7.L;}; + compareKernels(m,2,2); + m.span1=1;m.span2=1; + REQUIRE(enumerate(m).z==0); + compareKernels(m,2,2); +} + TEST_CASE("first-hit pair masses are derivatives of one frozen declared seed family", "[SeededPartitionFunction]") { using namespace seeded_oracle; Model m{6,7,5,6,false,{{{1,1},{2,2}},{{2,2},{3,3}},{{4,4}}}, @@ -276,6 +350,31 @@ TEST_CASE("seed extension workspaces reset across changing active rectangles", " } } +TEST_CASE("exact seed batches preserve short-span ensembles across hundreds of sources", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + // Short spans keep exhaustive enumeration small while hundreds of eligible + // sources force repeated batch publication, including a partial final batch. + Model m{19,21,3,4,false,{}, + [](Pair p){return (p.first+2*p.second)%7!=3;}, + [](Pair p){return .7L+.01L*p.first+.02L*p.second;}, + [](Pair p,Pair q){return stack(p,q)?.8L:.2L+.03L*(q.first-p.first);}, + [](Pair p,Pair q){return (p.first+q.second)%5==0?0.L:.6L+.01L*(q.first-p.first);}}; + for(size_t i=0;i256); + compareKernels(m,1,2); + } +} + TEST_CASE("seeded kernels preserve tiny mass and reject invalid weight arithmetic", "[SeededPartitionFunction]") { using namespace seeded_oracle; Model m{2,2,2,2,false,{{{0,0},{1,1}}},[](Pair p){return p.first==p.second;}, From 21b9d8fc33aadd3c44d788c4da10a91cf6694ce1 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 16 Oct 2026 22:06:07 +0200 Subject: [PATCH 25/30] perf: share exact seed anchors with heuristic ensembles --- ChangeLog | 28 + README.md | 7 +- doc/Makefile.am | 13 +- doc/analysis/heuristic-anchored-runtime.md | 161 +++ doc/analysis/seed-extension-partition.md | 141 ++- doc/benchmarks/seed-extension/README.md | 100 +- .../anchored-exact-results.json | 202 ++++ .../seed-extension/anchored-exact-samples.tsv | 81 ++ .../anchored-heuristic-results.json | 202 ++++ .../anchored-heuristic-samples.tsv | 81 ++ .../anchored-native-results.json | 210 ++++ .../anchored-native-samples.tsv | 41 + .../seed-extension/anchored-native.py | 204 ++++ .../anchored-pairs-results.json | 110 ++ .../seed-extension/anchored-pairs-samples.tsv | 21 + .../anchored-source-hashes.json | 1074 +++++++++++++++++ ...redictorMfeEns2dHeuristicSeedExtension.cpp | 4 +- .../PredictorMfeEns2dHeuristicSeedExtension.h | 6 +- src/IntaRNA/SeededPartitionFunction.cpp | 284 +---- src/IntaRNA/SeededPartitionFunction.h | 22 +- src/IntaRNA/StackSeedDomain.h | 6 +- tests/PredictorSeedOracle_test.cpp | 105 ++ tests/SeededPartitionFunction_test.cpp | 73 +- 23 files changed, 2844 insertions(+), 332 deletions(-) create mode 100644 doc/analysis/heuristic-anchored-runtime.md create mode 100644 doc/benchmarks/seed-extension/anchored-exact-results.json create mode 100644 doc/benchmarks/seed-extension/anchored-exact-samples.tsv create mode 100644 doc/benchmarks/seed-extension/anchored-heuristic-results.json create mode 100644 doc/benchmarks/seed-extension/anchored-heuristic-samples.tsv create mode 100644 doc/benchmarks/seed-extension/anchored-native-results.json create mode 100644 doc/benchmarks/seed-extension/anchored-native-samples.tsv create mode 100755 doc/benchmarks/seed-extension/anchored-native.py create mode 100644 doc/benchmarks/seed-extension/anchored-pairs-results.json create mode 100644 doc/benchmarks/seed-extension/anchored-pairs-samples.tsv create mode 100644 doc/benchmarks/seed-extension/anchored-source-hashes.json diff --git a/ChangeLog b/ChangeLog index 03d610bd..caa5231c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- share exact left-most seed-anchor extensions with heuristic prediction, + combining each true left extension with one retained right endpoint (#270) + - compute exact stack-seeded ensembles with left-most seed-anchored left/right extensions and shared bounded workspaces (PR #268 review) @@ -207,6 +210,31 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261016 Alexander Mitrofanov + * SeededPartitionFunction.*, StackSeedDomain.h + * Share the exact left-most seed-anchor recurrence with heuristic mode. + Remove the separate source-relative heuristic kernel; join true left + extensions with the seed ending and one frozen selected right endpoint. + Keep every right ending when there is no left extension. Crop right tables + in batches without that no-left source and differentiate the same joins. + * Document why pruning containing seeds preserves first-completed ownership, + including shortest co-ending ties; retain singleton noLP support. + * PredictorMfeEns2dHeuristicSeedExtension.* + * Clarify shared factorization while preserving native right-selection hooks, + energy ranking, complete-boundary publication and gapped-seed fallback. + * SeededPartitionFunction_test.cpp, PredictorSeedOracle_test.cpp + + Check nested ownership against the full declared family, one-sided paths, + invalid/cropped selectors, singleton noLP support, batch recomputation and + native virtual hooks against independent chain enumeration. + * README.md, doc/analysis/seed-extension-partition.md, + doc/analysis/heuristic-anchored-runtime.md, doc/benchmarks/seed-extension/*, + doc/Makefile.am + + Document the common recurrence and record reproducible kernel/native + correctness, runtime, allocation and RSS comparisons. Add a native CLI + runner with alternating samples and numerical pair-output verification. + * Address https://github.com/BackofenLab/IntaRNA/pull/270#issuecomment-6216756696 + directly on perf/268-exact-seed-anchored-extension. + 261016 Alexander Mitrofanov * StackSeedDomain.* + Enumerate a deterministic irredundant exact anchor family while retaining diff --git a/README.md b/README.md index 0e2d8855..d0a33708 100644 --- a/README.md +++ b/README.md @@ -2031,9 +2031,10 @@ is also used without `bpProb`, so requesting the matrix does not change `Zall`. Computed seeds preserve their handler-specific admission thresholds. Explicit stacked seeds may have different lengths, including singletons; competing explicit patterns at the same start still retain only the selected minimum-energy -pattern. Stack-seeded predictions use a seed-avoiding left extension up to the -first completed admitted seed and an unrestricted right extension. This -canonical seed owns the complete chain even if later seeds occur. +pattern. Exact and heuristic stack-seeded predictions share left-most seed-anchor +left/right tables. Removing containing seed occurrences leaves the same canonical +owner as the first completed admitted seed, choosing the shortest co-ending seed. +This canonical seed owns the complete chain even if later seeds occur. With `--mode=H`, probabilities describe the predictor's retained candidate ensemble. Unseeded H retains one continuation per left pair. Stack-seeded H retains each canonical seed's one-sided extensions and two-sided extensions diff --git a/doc/Makefile.am b/doc/Makefile.am index 1c7e9d4a..e7d46e22 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -9,6 +9,7 @@ EXTRA_DIST = \ analysis/seeded-kernel-comparison.md \ analysis/seeded-probabilities-validation.md \ analysis/seed-extension-partition.md \ + analysis/heuristic-anchored-runtime.md \ seeded-probabilities-example.cpp \ api-documentation.md \ build-api.sh \ @@ -42,4 +43,14 @@ EXTRA_DIST += benchmarks/seed-extension/README.md \ benchmarks/seed-extension/leftmost-heuristic-results.json \ benchmarks/seed-extension/leftmost-exact-samples.tsv \ benchmarks/seed-extension/leftmost-heuristic-samples.tsv \ - benchmarks/seed-extension/leftmost-source-hashes.json + benchmarks/seed-extension/leftmost-source-hashes.json \ + benchmarks/seed-extension/anchored-native.py \ + benchmarks/seed-extension/anchored-native-results.json \ + benchmarks/seed-extension/anchored-native-samples.tsv \ + benchmarks/seed-extension/anchored-pairs-results.json \ + benchmarks/seed-extension/anchored-pairs-samples.tsv \ + benchmarks/seed-extension/anchored-exact-results.json \ + benchmarks/seed-extension/anchored-exact-samples.tsv \ + benchmarks/seed-extension/anchored-heuristic-results.json \ + benchmarks/seed-extension/anchored-heuristic-samples.tsv \ + benchmarks/seed-extension/anchored-source-hashes.json diff --git a/doc/analysis/heuristic-anchored-runtime.md b/doc/analysis/heuristic-anchored-runtime.md new file mode 100644 index 00000000..816f04a2 --- /dev/null +++ b/doc/analysis/heuristic-anchored-runtime.md @@ -0,0 +1,161 @@ +# Heuristic ensemble prediction with shared seed-anchor tables + +This change implements the [PR #270 request](https://github.com/BackofenLab/IntaRNA/pull/270#issuecomment-6216756696) +to reuse exact prediction's left-most seed-anchor extension tables for heuristic +prediction, combining one chosen right endpoint with the possible left +extensions. The implementation starts directly from parent revision +`3866c62d10846fab1adc38b937d0c9b96cc06e51`, which is also the measurement baseline. + +## Recurrence and retained ensemble + +Exact and heuristic stack-seed prediction now use the same left and right +tables, contraction, complete-boundary publication, and outside pass. For an +anchor starting at `s` and ending at `t`, heuristic mode retains a complete +path with boundaries `(p,r)` when + +```text +p == s OR r == t OR r == selectedRight(anchor). +``` + +Thus a true left extension joins the seed ending and at most one selected right +endpoint. A source at the anchor start still retains every right endpoint. +The seed-ending branch is counted once when the selected endpoint equals it; +invalid or out-of-span choices add no two-sided extension. Each selected +endpoint retains all legal paths to that endpoint, not a single traceback. +These are the existing heuristic ensemble rules. + +Removing containing seed occurrences preserves ownership as well as the exact +union: an inner seed completes earlier, or wins the shorter co-ending tie. +Among the remaining occurrences on a common path, start and end order agree. +The left-most irredundant anchor is therefore the original first-completed +owner, including for nested, overlapping, and mixed-length explicit seeds. + +Choices are frozen once per canonical anchor. Native `fillHybridZ_right` and +`updateOptRightZ` customization hooks still choose endpoints using the original +admitted seed family. Their ranking, tie handling, terminal-GU filtering, and +native singleton-seed rejection remain in place. The kernel's singleton +`noLP` states preserve support supplied by either extension. + +The shared workspace uses bounded batches of complete left boundaries. A batch +without the anchor's own start needs a right table only up to its selected +endpoint, or just the seed-ending cell if that endpoint is unavailable. Both +passes join the same retained roots and use the same complete boundary factors. +This reduces heuristic contraction work; one-sided paths can still require a +full right table. See the [recurrence description](seed-extension-partition.md). + +## Correctness checks + +Release and debug validation each passed all seven test drivers. Both ran 115 +API cases, with 457,827 release and 457,828 debug assertions. Added regressions +compare with enumeration of the full declared seed family, covering ownership, +one-sided paths, invalid selections, singleton support, native extension hooks, +and complete-boundary coefficients across more than 256 sources. + +The isolated comparison checked every boundary hybrid, boundary coefficient, +partition, and raw pair numerator. All 328,236 exact entries were byte-identical. +The 271,006 heuristic entries had maximum relative difference +`6.622292098580306e-15`, below the `1e-12` comparison tolerance. The changed +heuristic factorization changes floating-point summation order. + +The 12 native verification predictions were byte-identical between versions. +The largest relative difference in native pair-probability output was +`2.3073317770873946e-15`; matrix labels and `NA` cells matched exactly. +Installation, standalone compilation of the three changed public headers, and +the installed pkg-config probability example also passed. +Validation was performed on Linux; macOS/Apple Clang was not run locally. + +## Measurements + +Measurements on 2026-10-16 used GCC 14.4.0, ViennaRNA 2.7.2, Boost 1.85, +and an AMD Ryzen 5 7530U running Linux. Both binaries use the C++23 release +configuration with `-O3` and `NDEBUG`; the manifest records full build flags. +Native runs use `--threads=1` and `OMP_NUM_THREADS=1`. +No project builds or tests ran during timing. CPU placement, frequency, and +other workstation activity were uncontrolled. Results describe these inputs +and this machine; they do not establish a general speedup. + +The native workload uses the complete 109-nt OxyS and 113-nt fhlA sequences +recorded in the harness. Five fresh-process samples per variant alternate +baseline and candidate order. Ordinary CLI output is measured separately from +the focused pair-output runs. GNU time includes accessibility, prediction, and +serialization, recording elapsed/user/system time and whole-process maximum RSS. +Below, times are median elapsed seconds; ratios are candidate/baseline. + +| Native case | Baseline seconds | Candidate seconds | Ratio | Median RSS KiB, baseline → candidate | +| --- | ---: | ---: | ---: | ---: | +| P/M seeded | 1.46 | 1.45 | 0.993 | 19,888 → 19,804 | +| P/M unseeded | 12.44 | 12.47 | 1.002 | 16,860 → 16,988 | +| P/H seeded | 14.59 | 1.01 | 0.069 | 17,280 → 19,928 | +| P/H unseeded | 0.23 | 0.23 | 1.000 | 30,512 → 30,604 | +| P/M seeded, pair output | 3.83 | 3.80 | 0.992 | 23,580 → 23,568 | +| P/H seeded, pair output | 42.51 | 2.71 | 0.064 | 18,196 → 23,700 | + +Seeded H drops from 14.59 to 1.01 seconds median wall time (14.4 times faster), +compared with 1.45 seconds for candidate seeded M. Median user time drops from +14.56 to 1.00 seconds. H samples range from 0.99 to 1.03 seconds, versus +14.29 to 14.76 seconds before. Its median process RSS increases from 17,280 +to 19,928 KiB, similar to the exact predictor's 19,804 KiB. All 40 ordinary +CLI samples have identical prediction output. + +The 20 pair-output samples also retain identical prediction output. Their +maximum probability difference is `8.59e-15` relative (`1.55e-15` absolute), +within the `2e-12` relative tolerance. Exact pair CSVs remain byte-identical. + +The isolated driver uses the four documented dense/banded, few/many-seed +fixtures, each with partition-only and pair-output execution. Its eight +heuristic cases measure the kernel without native energy/accessibility setup. +Allocation counts and cumulative requested bytes describe allocation traffic; +GNU time RSS includes the whole process and is not a live-kernel memory bound. + +| Heuristic fixture | Partition seconds, baseline → candidate | Ratio | Pair seconds, baseline → candidate | Ratio | +| --- | ---: | ---: | ---: | ---: | +| dense40-many | 0.424553 → 0.127418 | 0.300 | 2.665865 → 0.417940 | 0.157 | +| dense40-few | 0.059757 → 0.003792 | 0.063 | 0.131882 → 0.005355 | 0.041 | +| band60-many | 0.056363 → 0.017060 | 0.303 | 0.292022 → 0.053131 | 0.182 | +| band60-few | 0.011505 → 0.001943 | 0.169 | 0.024049 → 0.002943 | 0.122 | + +The heuristic kernel improves in all eight measured cases, by 3.3–24.6 times. +Using exact mode's batched boundary tables increases cumulative requested bytes +from 88,656–222,760 to 1,261,555–1,426,793 for partition-only runs and from +147,600–281,640 to 2,518,515–2,683,753 for pair output. Median process RSS rises +from 3,840–4,352 to 4,480–6,144 KiB. This is the shared exact workspace footprint +plus frozen heuristic endpoints; it is a time/space tradeoff against the old +heuristic kernel, not a memory reduction. + +Exact kernel allocation counts and requested bytes are unchanged. Its observed +candidate/baseline median runtime ratios range from 0.745 to 0.991, with +byte-identical numerical output. Small timing changes in the native exact and +unseeded controls should not be interpreted as improvements to those algorithms. + +Raw samples and summaries are retained as `anchored-native-*`, `anchored-pairs-*`, +`anchored-exact-*`, and `anchored-heuristic-*` files in the +[benchmark directory](../benchmarks/seed-extension/). +[`anchored-source-hashes.json`](../benchmarks/seed-extension/anchored-source-hashes.json) +records source/binary hashes, commands, toolchain, environment, and native run +manifests. Sample ranges and unchanged exact/unseeded controls should be read +alongside the medians when assessing noise or regressions. + +## Reproduction + +Build native parent and candidate executables with the same release toolchain +and dependencies. From a configured candidate checkout, use new output folders: + +```sh +python3 doc/benchmarks/seed-extension/anchored-native.py \ + --binary /path/to/parent/IntaRNA --candidate /path/to/candidate/IntaRNA \ + --output-dir /tmp/anchored-native --repetitions 5 +python3 doc/benchmarks/seed-extension/anchored-native.py \ + --binary /path/to/parent/IntaRNA --candidate /path/to/candidate/IntaRNA \ + --output-dir /tmp/anchored-pairs --repetitions 5 \ + --cases M-seeded H-seeded --pair-output +python3 doc/benchmarks/seed-extension/build.py \ + --source "$PWD" --output /tmp/anchored-kernel --cxx g++-14 \ + --baseline 3866c62d10846fab1adc38b937d0c9b96cc06e51 +python3 doc/benchmarks/seed-extension/run.py --build-dir /tmp/anchored-kernel \ + --output-dir /tmp/anchored-exact --repetitions 5 +python3 doc/benchmarks/seed-extension/run.py --build-dir /tmp/anchored-kernel \ + --output-dir /tmp/anchored-heuristic --repetitions 5 --heuristic +``` + +Dependency include/runtime options and fixture definitions are documented in +the [benchmark guide](../benchmarks/seed-extension/README.md). diff --git a/doc/analysis/seed-extension-partition.md b/doc/analysis/seed-extension-partition.md index 36682dd7..beda1712 100644 --- a/doc/analysis/seed-extension-partition.md +++ b/doc/analysis/seed-extension-partition.md @@ -10,11 +10,14 @@ especially the [seed-extension chapter](https://github.com/BackofenLab/IntaRNA-probabilities/blob/e904b65101294f782b0938590335f3e23631a1aa/theory/seed-extension.md) and its `SE-LEFT-REC`, `SE-FIRST-HIT`, and `SE-PAIR-FIRST-HIT` equations. The historical subtraction formulas discussed in that chapter are not the -recurrences used here. The exact backend now evaluates anchor-local left/right -extensions as requested in the [PR #268 review](https://github.com/BackofenLab/IntaRNA/pull/268#issuecomment-6212869312). -The heuristic backend retains first-completed-seed ownership and its frozen -endpoint selector. Both compute their respective ensembles with nonnegative -weights and differentiate the same computation for actual-pair probabilities. +recurrences used here. Exact and heuristic backends share anchor-local +left/right extensions, as +requested in the [PR #268 review](https://github.com/BackofenLab/IntaRNA/pull/268#issuecomment-6212869312) +and the [PR #270 follow-up](https://github.com/BackofenLab/IntaRNA/pull/270#issuecomment-6216756696). +Heuristic prediction joins only one selected true right endpoint to each left +extension, while retaining the existing one-sided branches. Both compute their +respective ensembles with nonnegative weights and differentiate the same +computation for actual-pair probabilities. ## Ensemble and weight ownership @@ -40,15 +43,18 @@ restrictions such as `noLP` are part of the recurrence; endpoint restrictions such as `noGUend` and complete-span accessibility are applied to the full boundary. Neither ranked-result limits nor display cutoffs select the ensemble. -## Exact left-most seed-anchored evaluation +## Shared left-most seed-anchored evaluation -The exact backend visits seed anchors and computes left and right extension -tables around each anchor. Its seed family is first reduced to an equivalent +Both modes visit seed anchors and compute left and right extension +tables around each anchor. The seed family is first reduced to an equivalent irredundant family: remove an occurrence if it contains another admitted stack occurrence. Every chain containing the removed seed also contains the retained inner seed, so the union of interactions, every boundary partition, and every -pair numerator remain unchanged. This reduction is private to exact evaluation; -the admitted seed family used for annotations and heuristic choices is unchanged. +pair numerator remain unchanged. The original admitted family used for +annotations and native endpoint selection remains unchanged. A containing seed cannot be the first completed +heuristic owner: its inner seed finishes earlier, or finishes together and is +shorter. Consequently this reduction also preserves the heuristic owner and its +selected endpoint. This matters for variable-length explicit seeds. An outer seed may start before an inner singleton but finish later. In the irredundant family, such nesting is @@ -70,10 +76,14 @@ Writing `S[a]` for the product of internal anchor transitions, `L[a,p,h]` for the left extension, and `R[a,r,k]` for the right extension gives ```text -H[p,r] = initiation(p) * sum_a S[a] * sum_(h,k compatible) L[a,p,h] * R[a,r,k] +H[p,r] = initiation(p) * sum_a retain(a,p,r) * S[a] * sum_(h,k compatible) L[a,p,h] * R[a,r,k] Z = sum_(p,r) H[p,r] * boundary(p,r). ``` +For exact mode, `retain(a,p,r)` is always one. For heuristic mode it is one +precisely when `p == s`, `r == t`, or `r == selectedRight(a)`, and zero otherwise. +The selected endpoint is frozen before evaluation, as described below. + The support flags `h,k` matter only for singleton anchors under `noLP`: compatibility requires `h || k`. Longer seeds already support all anchor pairs. The right table also records whether its current outer pair is supported; @@ -101,7 +111,7 @@ excluded. The outside pass uses the same compatible states. Different anchors can contribute to the same complete boundary. Reporting each anchor contribution separately would change the complete hybrid coefficient seen by native ensemble-energy reporting and extension hooks. Instead, the -exact backend accumulates batches of at most 256 left boundaries, then publishes +shared backend accumulates batches of at most 256 left boundaries, then publishes each positive complete hybrid sum once in row-major boundary order. The same hybrid coefficient and full-boundary factor determine the total partition and outside derivatives, even when the boundary factor is zero. @@ -141,7 +151,7 @@ index and boundary batches also increase allocation traffic. These results characterize the requested factorization, not a universal optimization; consult the benchmark records for per-case timings, allocation counts and source hashes. -### Exact rewrite validation +### Historical validation of the exact rewrite (#270) GCC 14.4.0 release and `--enable-debug` builds both pass `make tests -j2`: all seven API/CLI drivers pass, with 112 API cases and 399,390 / 399,391 @@ -160,7 +170,7 @@ pair-probability files are identical; the six exact files agree to maximum relative difference `1.53e-15`. Floating-point order changes are expected from the different nonnegative factorization; the scientific ensemble is unchanged. -## First acceptance in the heuristic backend +## Equivalent first-acceptance characterization Scan a chain from its left boundary. Its canonical anchor is the first admitted seed to finish. Before that completion, the prefix contains no admitted seed; @@ -185,7 +195,12 @@ The empty declared seed family produces an empty seeded ensemble. A singleton explicit seed can first accept at the initial pair or at a later appended pair; these alternatives are disjoint. -## Seed-free left prefixes, first hits, and right extensions +## Historical first-acceptance implementation + +Before the shared-anchor rewrite, the heuristic evaluated this equivalent +factorization separately for every left boundary. The following equations +explain the former implementation and the independent first-hit oracle; the +production kernel now evaluates the shared anchor tables described above. Fix the exact left boundary `p`. Let `N[p,h,a]` be the hybrid weight of prefixes ending at pair `h` in nonaccepting state `a`. The initial prefix has the sole @@ -212,7 +227,7 @@ first-hit contraction is Hseed[p,r] = sum_q F[p,q] * B[q,r]. ``` -The heuristic backend shares the work of compatible anchors through an inhomogeneous +The former heuristic backend shared compatible anchors through an inhomogeneous right-extension recurrence: ```text @@ -258,9 +273,10 @@ continuation in that case would admit lonely pairs. ## A defined heuristic subset -The stack-seed heuristic retains the original canonical first-hit ownership. -Unlike exact evaluation, it does not remove redundant containing occurrences: -that could change which anchor selects a right endpoint. For canonical anchor `S`, let `start(S)` and `end(S)` be its +The stack-seed heuristic retains the original canonical first-hit ownership +through the equivalent left-most minimal anchor described above. Removing +containing occurrences does not change the owner or its endpoint choice. +For canonical anchor `S`, let `start(S)` and `end(S)` be its outer pairs, and let `selectedRight(S)` be the deterministic endpoint selected by the heuristic's right-extension policy. Select the endpoint before the partition and outside calculations; marking a pair must not change it. @@ -293,19 +309,28 @@ All legal right paths to an admitted endpoint are retained, rather than only one representative path. The canonical owner is fixed independently of whether a later seed would offer a different selected endpoint. -For fixed `p`, anchors with `p == start(S)` can share an unrestricted right pass. -For the remaining anchors, add the zero-right-extension contribution directly -at `end(S)`, and group the other first-hit injections by their selected right -endpoint. Each group shares one right recurrence and contributes only at that -group's terminal endpoint. If the selected endpoint equals the anchor end, -the direct contribution already covers it. Anchors admitted by the unrestricted -case must not also contribute through the restricted cases. - -Grouping is valid because the right recurrence is linear and independent of -seed history. Keeping separate selected-endpoint groups preserves the selector -for each canonical anchor; merging all groups into one unconstrained pass would -incorrectly admit other endpoints. Sum the retained contributions for each -complete boundary before reporting it or initializing its outside coefficient. +The shared anchor kernel freezes `selectedRight(S)` once per minimal owner, +before any batch or outside pass. For `p != start(S)`, it directly contracts +`L[S,p]` with the right table at `end(S)` and, if distinct and legal, at the +selected endpoint. It does not scan the other right endpoints for each left +source. A selected endpoint must strictly extend both strands and satisfy the +complete source-to-end span constraints; a choice outside the active domain +or span retains only the no-right branch. For `p == start(S)`, the existing +one-sided case still contracts every legal right endpoint once. + +A batch containing the anchor's no-left source needs the full right table. +Other batches crop it to the rectangle ending at the selected endpoint, or +just the anchor cell if that endpoint cannot contribute. Internal right paths +remain unrestricted by seeds; choosing an endpoint retains all legal paths +ending there. Forward contraction and outside initialization use exactly the +same roots and published complete-boundary factors. Left/right and outside +tables, deterministic boundary batches, and memory limits are shared with exact +mode. The former per-source prefix and endpoint-group kernel has been removed. + +The native adapter still calls its existing virtual right-extension hooks to +freeze optimal endpoints, preserving scoring, tie order, boundary restrictions +and custom overrides. That preparation suppresses partition publication; all +completed hybrid partitions and actual-pair masses come from the shared kernel. With unchanged nonnegative weights, this defines a subset of the exact seeded ensemble, hence `0 <= Zheuristic <= Zexact`. The heuristic probabilities are @@ -319,24 +344,18 @@ The raw numerator for pair `k` is the sum of complete interaction weights whose chains actually contain `k`. It is not the sum of weights of boundary rectangles covering `k`; the latter is a spot probability. -For the heuristic first-acceptance recurrence, initialize outside derivatives -from the same complete boundary coefficients used by forward accumulation. -Reverse the right-extension passes, transfer -their derivatives into the corresponding first-hit injections, and then -reverse the accepting edges and seed-free prefix recurrence. A transition -owns its destination pair; the initialization owns the first pair. Accumulating -each state's forward weight times its outside derivative, `N * dN + T * dT`, -counts that pair exactly once in every retained complete chain, including -anchor pairs. First-hit states are not marked separately because their final -pair already belongs to the right-extension state. A directly retained -zero-right-extension branch accounts for its accepting pair in the same way. - -For heuristic calculations, reverse the same grouped passes and frozen -endpoint selector. The outside calculation must not independently recompute a -different ensemble or normalize each anchor separately. Merge raw region pair -numerators and denominators before final normalization. An empty ensemble has -undefined conditional probabilities; a numerical failure is an error, not an -empty ensemble. +Both modes initialize derivatives from the same complete-boundary coefficients +used by forward accumulation and reverse the shared anchor extension tables. +The heuristic applies its frozen endpoint restriction at contraction roots; +its outside pass never selects a different endpoint or normalizes an anchor +separately. Each forced seed pair receives its anchor-class mass once, and +extension pairs receive forward weight times outside derivative, excluding the +initial frontier pair already counted with the seed. The singleton support +flags preserve `noLP` support from either side. + +Merge raw region pair numerators and denominators before final normalization. +An empty ensemble has undefined conditional probabilities; a numerical failure +is an error, not an empty ensemble. ## Scope, checks, and resource considerations @@ -359,21 +378,21 @@ limits, and `noLP` states on both sides of acceptance. Heuristic checks must use the same frozen selector, verify each retained chain once, and compare its partition with the exact ensemble. -The heuristic seed-free table still retains run-length state for each active -left boundary. Sharing right extensions avoids repeating identical continuation -work for every anchor, but does not imply a lower asymptotic bound for the -whole calculation. Heuristic cost also depends on the number of distinct -selected endpoints. Pair output adds outside storage and work. Performance or -memory improvements require measurements against the previous implementation -with identical inputs, options, build configuration, and thread count; the -factorization alone is not a performance claim. +Both modes use the same bounded anchor-local workspaces. Heuristic true-left +contractions visit at most two right roots instead of all right endpoints, and +batches without a no-left source can shorten the right recurrence. Left-table +computation and complete-boundary publication still cost time, so this alone +does not guarantee an improvement for every seed density. Pair output adds the +matching outside storage and work. The [shared-anchor benchmark report](heuristic-anchored-runtime.md) +records numerical comparisons, runtime, allocation traffic and process RSS +against the previous implementation under identical inputs and toolchains. -## First-acceptance workspace reuse (PR #266 review) +## Historical first-acceptance workspace reuse (PR #266 review) The allocation revision requested in [PR #266](https://github.com/BackofenLab/IntaRNA/pull/266#issuecomment-6176325864) kept the then-shared exact/heuristic recurrence and its arithmetic order unchanged. -The heuristic still uses these buffers; exact evaluation now uses the anchor-local -batched algorithm above. One first-acceptance invocation allocates +Both modes now use the anchor-local batched algorithm above. In the historical +implementation, one first-acceptance invocation allocated its prefix, first-hit, right-extension and (when requested) outside matrices at `min(span1,n)` by `min(span2,m)`, with the prefix run-length dimension retained. The inclusive maximum extension offset is `span-1`; storing offset zero requires diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md index 7378cd28..efe35778 100644 --- a/doc/benchmarks/seed-extension/README.md +++ b/doc/benchmarks/seed-extension/README.md @@ -1,9 +1,12 @@ # Seed-extension kernel comparison -This Linux benchmark compares the seeded kernel at revision -`985ba44bdc16e92162e954b7baffa3f3923d4776` with the current source checkout. -It runs the exact ensemble by default; `--heuristic` uses frozen right-endpoint -groups to exercise their forward and outside workspace reuse. +This Linux benchmark compares a selected seeded-kernel revision with the current +source checkout. The current shared-anchor comparison uses baseline +`3866c62d10846fab1adc38b937d0c9b96cc06e51`. +It runs the exact ensemble by default; `--heuristic` applies frozen endpoint +choices to the same anchored forward and outside implementation. True left +extensions retain the no-right endpoint and the selected right endpoint; +interactions beginning at the anchor retain every right endpoint. It first checks every reported boundary hybrid weight and boundary coefficient, the total partition, and every raw pair numerator. It then times partition-only and pair-output calculations in separate processes, alternating baseline and @@ -15,22 +18,18 @@ alignment. These counters measure allocation traffic, not peak live memory. Configure the source tree first so `src/IntaRNA/intarna_config.h` is available. Requirements are a C++23 GCC toolchain, the project's headers and Boost headers, -Python 3, and GNU `/usr/bin/time`. Run from the repository root: - -```sh -python3 doc/benchmarks/seed-extension/build.py \ - --source "$PWD" --output /tmp/intarna-seed-extension-benchmark --cxx g++-14 -python3 doc/benchmarks/seed-extension/run.py \ - --build-dir /tmp/intarna-seed-extension-benchmark --repetitions 3 -``` +Python 3, and GNU `/usr/bin/time`. Run the commands below from the repository +root. For dependencies outside the compiler's search paths, add `--include /path/to/dependencies/include` to the build command. An optional `--runtime-library-dir /path/to/compiler/lib` embeds a runtime library search path. Both options can be repeated. `--baseline` selects another Git revision -when a different comparison is intended; the current driver requires the -first-acceptance kernel API introduced in #266. To measure heuristic groups, use -`--heuristic` on the runner. Use separate `--output-dir` directories for exact and +when a different comparison is intended; its historical default remains +`985ba44bdc16e92162e954b7baffa3f3923d4776`, so specify the intended baseline +explicitly. The current driver requires the first-acceptance kernel API +introduced in #266. To measure the heuristic ensemble, use `--heuristic` on +the runner. Use separate `--output-dir` directories for exact and heuristic results, since the runner replaces its output files. The same built executables can be used for both ensembles. `--verify-only` compares all dumps without collecting timed samples, which permits checking correctness before a @@ -44,6 +43,77 @@ numerical/configuration headers. The runner writes full verification dumps, `samples.tsv`, and `results.json` in the build directory. Timed runs do not write the full boundary dumps. +## Shared anchored heuristic comparison (#270) + +Build both kernels against baseline `3866c62d10846fab1adc38b937d0c9b96cc06e51`. +Verify the exact and heuristic full dumps before collecting timings: + +```sh +python3 doc/benchmarks/seed-extension/build.py \ + --source "$PWD" --output /tmp/intarna-anchored-benchmark --cxx g++-14 \ + --baseline 3866c62d10846fab1adc38b937d0c9b96cc06e51 +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-anchored-benchmark \ + --output-dir /tmp/intarna-anchored-exact-check --verify-only +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-anchored-benchmark \ + --output-dir /tmp/intarna-anchored-heuristic-check --verify-only --heuristic +``` + +After both checks pass, reserve a quiet period without concurrent builds or +tests. Collect five alternating baseline/candidate samples for each fixture, +including partition-only and pair-output calculations: + +```sh +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-anchored-benchmark \ + --output-dir /tmp/intarna-anchored-exact --repetitions 5 +python3 doc/benchmarks/seed-extension/run.py \ + --build-dir /tmp/intarna-anchored-benchmark \ + --output-dir /tmp/intarna-anchored-heuristic --repetitions 5 --heuristic +``` + +The native CLI harness complements those synthetic fixtures with the OxyS/fhlA +reproduction from #270. Build complete baseline and candidate executables with +the same compiler, dependencies and release options. The default four cases +combine `--model=P` with modes M/H and seeded/unseeded prediction, using one +thread and five alternating samples per executable: + +```sh +python3 doc/benchmarks/seed-extension/anchored-native.py \ + --binary /path/to/baseline/src/bin/IntaRNA \ + --candidate /path/to/candidate/src/bin/IntaRNA \ + --output-dir /tmp/intarna-anchored-native --repetitions 5 +``` + +Use a separate invocation for seeded H/M pair output, without timing the +unrelated unseeded pair cases: + +```sh +python3 doc/benchmarks/seed-extension/anchored-native.py \ + --binary /path/to/baseline/src/bin/IntaRNA \ + --candidate /path/to/candidate/src/bin/IntaRNA \ + --output-dir /tmp/intarna-anchored-native-pairs --repetitions 5 \ + --cases H-seeded M-seeded --pair-output +``` + +Native result directories must be new or empty. The harness requires identical +stdout for each case across both binaries and all repetitions. With +`--pair-output`, it also compares every finite CSV probability at relative +tolerance `2e-12`, with no absolute tolerance; matrix labels and `NA` cells must +match exactly. This permits harmless changes in floating-point summation order +without ignoring differences in small probabilities. + +`manifest.json` records executable hashes, versions, environment, and one command +per case/variant. `samples.tsv` records individual timings, maximum process RSS, +output hashes and comparison errors; `results.json` contains the summary and +verification status. Raw stdout, stderr, GNU time output and requested pair CSVs +are retained for every sample. Native timings cover fresh complete CLI processes, +including thermodynamic/accessibility computation and output; they have no +warmup exclusions. They measure a different scope from the isolated kernel +runner. Record the resulting measurements and their interpretation in +[the anchored heuristic runtime analysis](../../analysis/heuristic-anchored-runtime.md). + ## Workloads and measurement limits The dense cases admit every vertex on a 40 by 40 grid. The banded cases use a diff --git a/doc/benchmarks/seed-extension/anchored-exact-results.json b/doc/benchmarks/seed-extension/anchored-exact-results.json new file mode 100644 index 00000000..2dc857c8 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-exact-results.json @@ -0,0 +1,202 @@ +{ + "heuristic": false, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 5, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "dense40-few", + "compared_entries": 42460, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-few", + "compared_entries": 22796, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + } + ], + "summary": [ + { + "case": "dense40-many", + "pairs": 0, + "seeds": 1369, + "base_allocations": 1393, + "base_allocated_bytes": 1404889, + "base_median_seconds": 0.311875057, + "base_min_seconds": 0.300367011, + "base_max_seconds": 0.317534621, + "base_median_rss_kib": 4864, + "candidate_allocations": 1393, + "candidate_allocated_bytes": 1404889, + "candidate_median_seconds": 0.266587512, + "candidate_min_seconds": 0.264549657, + "candidate_max_seconds": 0.274143627, + "candidate_median_rss_kib": 4864, + "runtime_ratio": 0.8547894614092207, + "rss_ratio": 1.0 + }, + { + "case": "dense40-many", + "pairs": 1, + "seeds": 1369, + "base_allocations": 1398, + "base_allocated_bytes": 2661849, + "base_median_seconds": 1.198733847, + "base_min_seconds": 1.196928755, + "base_max_seconds": 1.256651473, + "base_median_rss_kib": 6272, + "candidate_allocations": 1398, + "candidate_allocated_bytes": 2661849, + "candidate_median_seconds": 0.892968105, + "candidate_min_seconds": 0.888198664, + "candidate_max_seconds": 0.918093987, + "candidate_median_rss_kib": 6144, + "runtime_ratio": 0.7449260794919391, + "rss_ratio": 0.9795918367346939 + }, + { + "case": "dense40-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 19, + "base_allocated_bytes": 1261507, + "base_median_seconds": 0.004798063, + "base_min_seconds": 0.00455589, + "base_max_seconds": 0.004907337, + "base_median_rss_kib": 4608, + "candidate_allocations": 19, + "candidate_allocated_bytes": 1261507, + "candidate_median_seconds": 0.00444311, + "candidate_min_seconds": 0.004437609, + "candidate_max_seconds": 0.005392644, + "candidate_median_rss_kib": 4480, + "runtime_ratio": 0.926021604968505, + "rss_ratio": 0.9722222222222222 + }, + { + "case": "dense40-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 24, + "base_allocated_bytes": 2518467, + "base_median_seconds": 0.008528812, + "base_min_seconds": 0.008277933, + "base_max_seconds": 0.008741058, + "base_median_rss_kib": 5888, + "candidate_allocations": 24, + "candidate_allocated_bytes": 2518467, + "candidate_median_seconds": 0.007704972, + "candidate_min_seconds": 0.00743045, + "candidate_max_seconds": 0.008055698, + "candidate_median_rss_kib": 5760, + "runtime_ratio": 0.9034050697799412, + "rss_ratio": 0.9782608695652174 + }, + { + "case": "band60-many", + "pairs": 0, + "seeds": 387, + "base_allocations": 409, + "base_allocated_bytes": 1302851, + "base_median_seconds": 0.048252586, + "base_min_seconds": 0.047256495, + "base_max_seconds": 0.049836425, + "base_median_rss_kib": 4608, + "candidate_allocations": 409, + "candidate_allocated_bytes": 1302851, + "candidate_median_seconds": 0.041544776, + "candidate_min_seconds": 0.041442184, + "candidate_max_seconds": 0.042278346, + "candidate_median_rss_kib": 4608, + "runtime_ratio": 0.8609854816900383, + "rss_ratio": 1.0 + }, + { + "case": "band60-many", + "pairs": 1, + "seeds": 387, + "base_allocations": 414, + "base_allocated_bytes": 2575811, + "base_median_seconds": 0.175600802, + "base_min_seconds": 0.174935228, + "base_max_seconds": 0.181350983, + "base_median_rss_kib": 5888, + "candidate_allocations": 414, + "candidate_allocated_bytes": 2575811, + "candidate_median_seconds": 0.130833667, + "candidate_min_seconds": 0.129684621, + "candidate_max_seconds": 0.134179737, + "candidate_median_rss_kib": 5888, + "runtime_ratio": 0.7450630379239384, + "rss_ratio": 1.0 + }, + { + "case": "band60-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 18, + "base_allocated_bytes": 1263443, + "base_median_seconds": 0.002453783, + "base_min_seconds": 0.00233942, + "base_max_seconds": 0.002850845, + "base_median_rss_kib": 4480, + "candidate_allocations": 18, + "candidate_allocated_bytes": 1263443, + "candidate_median_seconds": 0.002432914, + "candidate_min_seconds": 0.002296881, + "candidate_max_seconds": 0.003017938, + "candidate_median_rss_kib": 4480, + "runtime_ratio": 0.9914951729635424, + "rss_ratio": 1.0 + }, + { + "case": "band60-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 23, + "base_allocated_bytes": 2536403, + "base_median_seconds": 0.004374692, + "base_min_seconds": 0.00427757, + "base_max_seconds": 0.004892529, + "base_median_rss_kib": 5760, + "candidate_allocations": 23, + "candidate_allocated_bytes": 2536403, + "candidate_median_seconds": 0.004235592, + "candidate_min_seconds": 0.003888464, + "candidate_max_seconds": 0.004411871, + "candidate_median_rss_kib": 5632, + "runtime_ratio": 0.9682034758104112, + "rss_ratio": 0.9777777777777777 + } + ] +} diff --git a/doc/benchmarks/seed-extension/anchored-exact-samples.tsv b/doc/benchmarks/seed-extension/anchored-exact-samples.tsv new file mode 100644 index 00000000..4ed05098 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-exact-samples.tsv @@ -0,0 +1,81 @@ +variant case pairs allocations allocated_bytes seconds rss_kib z pair_moment boundaries checksum seeds repetition +base dense40-many 0 1393 1404889 0.311875057 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 0 +candidate dense40-many 0 1393 1404889 0.274143627 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 0 +candidate dense40-many 0 1393 1404889 0.265352274 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 1 +base dense40-many 0 1393 1404889 0.300367011 4992 3691630940.677922 0.0 219109 3691630940.677922 1369 1 +base dense40-many 0 1393 1404889 0.311517339 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 2 +candidate dense40-many 0 1393 1404889 0.266587512 4736 3691630940.677922 0.0 219109 3691630940.677922 1369 2 +candidate dense40-many 0 1393 1404889 0.269009838 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 3 +base dense40-many 0 1393 1404889 0.31357846 4992 3691630940.677922 0.0 219109 3691630940.677922 1369 3 +base dense40-many 0 1393 1404889 0.317534621 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 4 +candidate dense40-many 0 1393 1404889 0.264549657 4864 3691630940.677922 0.0 219109 3691630940.677922 1369 4 +base dense40-many 1 1398 2661849 1.256651473 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 0 +candidate dense40-many 1 1398 2661849 0.909370039 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 0 +candidate dense40-many 1 1398 2661849 0.888198664 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 1 +base dense40-many 1 1398 2661849 1.198733847 6272 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 1 +base dense40-many 1 1398 2661849 1.198326741 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 2 +candidate dense40-many 1 1398 2661849 0.918093987 6016 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 2 +candidate dense40-many 1 1398 2661849 0.892968105 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 3 +base dense40-many 1 1398 2661849 1.247004482 6272 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 3 +base dense40-many 1 1398 2661849 1.196928755 6272 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 4 +candidate dense40-many 1 1398 2661849 0.891786838 6144 3691630940.677922 51541049531.76291 219109 3691630940.677922 1369 4 +base dense40-few 0 19 1261507 0.004874235 4480 22766642.26492601 0.0 40859 22766642.26492601 3 0 +candidate dense40-few 0 19 1261507 0.004668861 4480 22766642.26492601 0.0 40859 22766642.26492601 3 0 +candidate dense40-few 0 19 1261507 0.004442769 4480 22766642.26492601 0.0 40859 22766642.26492601 3 1 +base dense40-few 0 19 1261507 0.004638324 4608 22766642.26492601 0.0 40859 22766642.26492601 3 1 +base dense40-few 0 19 1261507 0.004798063 4608 22766642.26492601 0.0 40859 22766642.26492601 3 2 +candidate dense40-few 0 19 1261507 0.004437609 4480 22766642.26492601 0.0 40859 22766642.26492601 3 2 +candidate dense40-few 0 19 1261507 0.00444311 4480 22766642.26492601 0.0 40859 22766642.26492601 3 3 +base dense40-few 0 19 1261507 0.004907337 4608 22766642.26492601 0.0 40859 22766642.26492601 3 3 +base dense40-few 0 19 1261507 0.00455589 4608 22766642.26492601 0.0 40859 22766642.26492601 3 4 +candidate dense40-few 0 19 1261507 0.005392644 4352 22766642.26492601 0.0 40859 22766642.26492601 3 4 +base dense40-few 1 24 2518467 0.008535794 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 0 +candidate dense40-few 1 24 2518467 0.00743045 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 0 +candidate dense40-few 1 24 2518467 0.007704972 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 1 +base dense40-few 1 24 2518467 0.008528812 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 1 +base dense40-few 1 24 2518467 0.008741058 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 2 +candidate dense40-few 1 24 2518467 0.007433946 5504 22766642.26492601 322997499.128349 40859 22766642.26492601 3 2 +candidate dense40-few 1 24 2518467 0.008055698 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 3 +base dense40-few 1 24 2518467 0.008277933 5888 22766642.26492601 322997499.128349 40859 22766642.26492601 3 3 +base dense40-few 1 24 2518467 0.008374414 5760 22766642.26492601 322997499.128349 40859 22766642.26492601 3 4 +candidate dense40-few 1 24 2518467 0.007813105 5632 22766642.26492601 322997499.128349 40859 22766642.26492601 3 4 +base band60-many 0 409 1302851 0.047814818 4480 1086387370.7289982 0.0 38669 1086387370.7289982 387 0 +candidate band60-many 0 409 1302851 0.041821944 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 0 +candidate band60-many 0 409 1302851 0.04150422 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 1 +base band60-many 0 409 1302851 0.049393877 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 1 +base band60-many 0 409 1302851 0.049836425 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 2 +candidate band60-many 0 409 1302851 0.041442184 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 2 +candidate band60-many 0 409 1302851 0.041544776 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 3 +base band60-many 0 409 1302851 0.047256495 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 3 +base band60-many 0 409 1302851 0.048252586 4736 1086387370.7289982 0.0 38669 1086387370.7289982 387 4 +candidate band60-many 0 409 1302851 0.042278346 4608 1086387370.7289982 0.0 38669 1086387370.7289982 387 4 +base band60-many 1 414 2575811 0.175275925 5888 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 0 +candidate band60-many 1 414 2575811 0.130807267 5888 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 0 +candidate band60-many 1 414 2575811 0.134179737 5632 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 1 +base band60-many 1 414 2575811 0.176753335 6016 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 1 +base band60-many 1 414 2575811 0.181350983 5888 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 2 +candidate band60-many 1 414 2575811 0.13343196 5888 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 2 +candidate band60-many 1 414 2575811 0.130833667 5760 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 3 +base band60-many 1 414 2575811 0.174935228 6016 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 3 +base band60-many 1 414 2575811 0.175600802 5760 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 4 +candidate band60-many 1 414 2575811 0.129684621 5888 1086387370.7289982 15334043961.836025 38669 1086387370.7289982 387 4 +base band60-few 0 18 1263443 0.002850845 4480 20718290.24532375 0.0 19195 20718290.24532375 3 0 +candidate band60-few 0 18 1263443 0.002432914 4480 20718290.24532375 0.0 19195 20718290.24532375 3 0 +candidate band60-few 0 18 1263443 0.002620064 4480 20718290.24532375 0.0 19195 20718290.24532375 3 1 +base band60-few 0 18 1263443 0.00233942 4608 20718290.24532375 0.0 19195 20718290.24532375 3 1 +base band60-few 0 18 1263443 0.002453783 4608 20718290.24532375 0.0 19195 20718290.24532375 3 2 +candidate band60-few 0 18 1263443 0.003017938 4480 20718290.24532375 0.0 19195 20718290.24532375 3 2 +candidate band60-few 0 18 1263443 0.00241968 4480 20718290.24532375 0.0 19195 20718290.24532375 3 3 +base band60-few 0 18 1263443 0.002352124 4480 20718290.24532375 0.0 19195 20718290.24532375 3 3 +base band60-few 0 18 1263443 0.002609975 4352 20718290.24532375 0.0 19195 20718290.24532375 3 4 +candidate band60-few 0 18 1263443 0.002296881 4480 20718290.24532375 0.0 19195 20718290.24532375 3 4 +base band60-few 1 23 2536403 0.004892529 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 0 +candidate band60-few 1 23 2536403 0.004235592 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 0 +candidate band60-few 1 23 2536403 0.004342442 5632 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 1 +base band60-few 1 23 2536403 0.004374692 5888 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 1 +base band60-few 1 23 2536403 0.00427757 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 2 +candidate band60-few 1 23 2536403 0.003888464 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 2 +candidate band60-few 1 23 2536403 0.004411871 5632 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 3 +base band60-few 1 23 2536403 0.004866771 5760 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 3 +base band60-few 1 23 2536403 0.004372769 5888 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 4 +candidate band60-few 1 23 2536403 0.004151595 5632 20718290.24532375 296428379.41423106 19195 20718290.24532375 3 4 diff --git a/doc/benchmarks/seed-extension/anchored-heuristic-results.json b/doc/benchmarks/seed-extension/anchored-heuristic-results.json new file mode 100644 index 00000000..f7576942 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-heuristic-results.json @@ -0,0 +1,202 @@ +{ + "heuristic": true, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 5, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 6.622292098580306e-15, + "max_relative_error_by_kind": { + "b": 8.855415220299877e-16, + "p": 6.622292098580306e-15, + "z": 1.4956070422152216e-16 + } + }, + { + "case": "dense40-few", + "compared_entries": 3435, + "max_relative_error": 3.1583238696495025e-15, + "max_relative_error_by_kind": { + "b": 8.208110010426082e-16, + "p": 3.1583238696495025e-15, + "z": 2.2691994745553064e-16 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 4.389723301013629e-15, + "max_relative_error_by_kind": { + "b": 8.055521556013479e-16, + "p": 4.389723301013629e-15, + "z": 2.5614223541768804e-16 + } + }, + { + "case": "band60-few", + "compared_entries": 4591, + "max_relative_error": 1.8377333584902226e-15, + "max_relative_error_by_kind": { + "b": 8.527781049852308e-16, + "p": 1.8377333584902226e-15, + "z": 1.40574141827393e-16 + } + } + ], + "summary": [ + { + "case": "dense40-many", + "pairs": 0, + "seeds": 1369, + "base_allocations": 1399, + "base_allocated_bytes": 222760, + "base_median_seconds": 0.42455289, + "base_min_seconds": 0.412944986, + "base_max_seconds": 0.42900247, + "base_median_rss_kib": 4352, + "candidate_allocations": 1394, + "candidate_allocated_bytes": 1426793, + "candidate_median_seconds": 0.127417659, + "candidate_min_seconds": 0.12596133, + "candidate_max_seconds": 0.128743787, + "candidate_median_rss_kib": 4864, + "runtime_ratio": 0.300121991867727, + "rss_ratio": 1.1176470588235294 + }, + { + "case": "dense40-many", + "pairs": 1, + "seeds": 1369, + "base_allocations": 1405, + "base_allocated_bytes": 281640, + "base_median_seconds": 2.665865378, + "base_min_seconds": 2.640984908, + "base_max_seconds": 2.750612771, + "base_median_rss_kib": 4352, + "candidate_allocations": 1399, + "candidate_allocated_bytes": 2683753, + "candidate_median_seconds": 0.417939528, + "candidate_min_seconds": 0.415339105, + "candidate_max_seconds": 0.425267658, + "candidate_median_rss_kib": 6144, + "runtime_ratio": 0.15677443109057101, + "rss_ratio": 1.411764705882353 + }, + { + "case": "dense40-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 18, + "base_allocated_bytes": 88720, + "base_median_seconds": 0.059757413, + "base_min_seconds": 0.058153947, + "base_max_seconds": 0.060770896, + "base_median_rss_kib": 3968, + "candidate_allocations": 20, + "candidate_allocated_bytes": 1261555, + "candidate_median_seconds": 0.003791823, + "candidate_min_seconds": 0.003600956, + "candidate_max_seconds": 0.004270237, + "candidate_median_rss_kib": 4480, + "runtime_ratio": 0.0634536003089692, + "rss_ratio": 1.1290322580645162 + }, + { + "case": "dense40-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 24, + "base_allocated_bytes": 147600, + "base_median_seconds": 0.131882487, + "base_min_seconds": 0.130946498, + "base_max_seconds": 0.139568113, + "base_median_rss_kib": 3968, + "candidate_allocations": 25, + "candidate_allocated_bytes": 2518515, + "candidate_median_seconds": 0.005354882, + "candidate_min_seconds": 0.005308165, + "candidate_max_seconds": 0.005923235, + "candidate_median_rss_kib": 5760, + "runtime_ratio": 0.040603435086873974, + "rss_ratio": 1.4516129032258065 + }, + { + "case": "band60-many", + "pairs": 0, + "seeds": 387, + "base_allocations": 413, + "base_allocated_bytes": 123320, + "base_median_seconds": 0.056362771, + "base_min_seconds": 0.056199957, + "base_max_seconds": 0.058239938, + "base_median_rss_kib": 4096, + "candidate_allocations": 410, + "candidate_allocated_bytes": 1309043, + "candidate_median_seconds": 0.017059937, + "candidate_min_seconds": 0.01700261, + "candidate_max_seconds": 0.017299625, + "candidate_median_rss_kib": 4608, + "runtime_ratio": 0.3026809487418566, + "rss_ratio": 1.125 + }, + { + "case": "band60-many", + "pairs": 1, + "seeds": 387, + "base_allocations": 419, + "base_allocated_bytes": 198200, + "base_median_seconds": 0.292021597, + "base_min_seconds": 0.276608727, + "base_max_seconds": 0.292551426, + "base_median_rss_kib": 4096, + "candidate_allocations": 415, + "candidate_allocated_bytes": 2582003, + "candidate_median_seconds": 0.053130625, + "candidate_min_seconds": 0.053028102, + "candidate_max_seconds": 0.053482692, + "candidate_median_rss_kib": 5888, + "runtime_ratio": 0.18194073844476646, + "rss_ratio": 1.4375 + }, + { + "case": "band60-few", + "pairs": 0, + "seeds": 3, + "base_allocations": 17, + "base_allocated_bytes": 88656, + "base_median_seconds": 0.011505492, + "base_min_seconds": 0.011280692, + "base_max_seconds": 0.01178273, + "base_median_rss_kib": 3840, + "candidate_allocations": 19, + "candidate_allocated_bytes": 1263491, + "candidate_median_seconds": 0.001943069, + "candidate_min_seconds": 0.001909336, + "candidate_max_seconds": 0.002126412, + "candidate_median_rss_kib": 4480, + "runtime_ratio": 0.168881869632346, + "rss_ratio": 1.1666666666666667 + }, + { + "case": "band60-few", + "pairs": 1, + "seeds": 3, + "base_allocations": 23, + "base_allocated_bytes": 163536, + "base_median_seconds": 0.024048792, + "base_min_seconds": 0.023996775, + "base_max_seconds": 0.025701949, + "base_median_rss_kib": 3968, + "candidate_allocations": 24, + "candidate_allocated_bytes": 2536451, + "candidate_median_seconds": 0.002943488, + "candidate_min_seconds": 0.002866114, + "candidate_max_seconds": 0.003039107, + "candidate_median_rss_kib": 5760, + "runtime_ratio": 0.12239650124629961, + "rss_ratio": 1.4516129032258065 + } + ] +} diff --git a/doc/benchmarks/seed-extension/anchored-heuristic-samples.tsv b/doc/benchmarks/seed-extension/anchored-heuristic-samples.tsv new file mode 100644 index 00000000..c8f2f7aa --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-heuristic-samples.tsv @@ -0,0 +1,81 @@ +variant case pairs allocations allocated_bytes seconds rss_kib z pair_moment boundaries checksum seeds repetition +base dense40-many 0 1399 222760 0.424850162 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 0 +candidate dense40-many 0 1394 1426793 0.1276376 4864 797062906.137525 0.0 219109 797062906.137525 1369 0 +candidate dense40-many 0 1394 1426793 0.126905733 4864 797062906.137525 0.0 219109 797062906.137525 1369 1 +base dense40-many 0 1399 222760 0.412944986 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 1 +base dense40-many 0 1399 222760 0.423081109 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 2 +candidate dense40-many 0 1394 1426793 0.127417659 4864 797062906.137525 0.0 219109 797062906.137525 1369 2 +candidate dense40-many 0 1394 1426793 0.128743787 4864 797062906.137525 0.0 219109 797062906.137525 1369 3 +base dense40-many 0 1399 222760 0.42900247 4352 797062906.1375248 0.0 219109 797062906.1375248 1369 3 +base dense40-many 0 1399 222760 0.42455289 4224 797062906.1375248 0.0 219109 797062906.1375248 1369 4 +candidate dense40-many 0 1394 1426793 0.12596133 4736 797062906.137525 0.0 219109 797062906.137525 1369 4 +base dense40-many 1 1405 281640 2.650292829 4352 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 0 +candidate dense40-many 1 1399 2683753 0.415339105 5888 797062906.137525 11047332366.451756 219109 797062906.137525 1369 0 +candidate dense40-many 1 1399 2683753 0.416167732 6144 797062906.137525 11047332366.451756 219109 797062906.137525 1369 1 +base dense40-many 1 1405 281640 2.665865378 4224 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 1 +base dense40-many 1 1405 281640 2.750612771 4352 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 2 +candidate dense40-many 1 1399 2683753 0.425267658 6144 797062906.137525 11047332366.451756 219109 797062906.137525 1369 2 +candidate dense40-many 1 1399 2683753 0.425067886 6016 797062906.137525 11047332366.451756 219109 797062906.137525 1369 3 +base dense40-many 1 1405 281640 2.640984908 4352 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 3 +base dense40-many 1 1405 281640 2.667970156 4096 797062906.1375248 11047332366.451756 219109 797062906.1375248 1369 4 +candidate dense40-many 1 1399 2683753 0.417939528 6144 797062906.137525 11047332366.451756 219109 797062906.137525 1369 4 +base dense40-few 0 18 88720 0.058153947 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 0 +candidate dense40-few 0 20 1261555 0.003600956 4480 4104189.980028041 0.0 1834 4104189.980028041 3 0 +candidate dense40-few 0 20 1261555 0.003791823 4480 4104189.980028041 0.0 1834 4104189.980028041 3 1 +base dense40-few 0 18 88720 0.058173554 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 1 +base dense40-few 0 18 88720 0.059757413 3840 4104189.9800280402 0.0 1834 4104189.9800280402 3 2 +candidate dense40-few 0 20 1261555 0.003685394 4480 4104189.980028041 0.0 1834 4104189.980028041 3 2 +candidate dense40-few 0 20 1261555 0.004270237 4480 4104189.980028041 0.0 1834 4104189.980028041 3 3 +base dense40-few 0 18 88720 0.060178799 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 3 +base dense40-few 0 18 88720 0.060770896 3968 4104189.9800280402 0.0 1834 4104189.9800280402 3 4 +candidate dense40-few 0 20 1261555 0.003809546 4480 4104189.980028041 0.0 1834 4104189.980028041 3 4 +base dense40-few 1 24 147600 0.139568113 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 0 +candidate dense40-few 1 25 2518515 0.005354882 5760 4104189.980028041 57647089.617964394 1834 4104189.980028041 3 0 +candidate dense40-few 1 25 2518515 0.005308165 5760 4104189.980028041 57647089.617964394 1834 4104189.980028041 3 1 +base dense40-few 1 24 147600 0.139231765 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 1 +base dense40-few 1 24 147600 0.130946498 3840 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 2 +candidate dense40-few 1 25 2518515 0.005354602 5760 4104189.980028041 57647089.617964394 1834 4104189.980028041 3 2 +candidate dense40-few 1 25 2518515 0.005923235 5632 4104189.980028041 57647089.617964394 1834 4104189.980028041 3 3 +base dense40-few 1 24 147600 0.131882487 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 3 +base dense40-few 1 24 147600 0.131363868 3968 4104189.9800280402 57647089.6179644 1834 4104189.9800280402 3 4 +candidate dense40-few 1 25 2518515 0.005708413 5760 4104189.980028041 57647089.617964394 1834 4104189.980028041 3 4 +base band60-many 0 413 123320 0.05624413 4096 232701353.12981105 0.0 38669 232701353.12981105 387 0 +candidate band60-many 0 410 1309043 0.017059937 4608 232701353.129811 0.0 38669 232701353.129811 387 0 +candidate band60-many 0 410 1309043 0.017095192 4608 232701353.129811 0.0 38669 232701353.129811 387 1 +base band60-many 0 413 123320 0.058239938 4096 232701353.12981105 0.0 38669 232701353.12981105 387 1 +base band60-many 0 413 123320 0.056199957 4096 232701353.12981105 0.0 38669 232701353.12981105 387 2 +candidate band60-many 0 410 1309043 0.017040831 4608 232701353.129811 0.0 38669 232701353.129811 387 2 +candidate band60-many 0 410 1309043 0.01700261 4608 232701353.129811 0.0 38669 232701353.129811 387 3 +base band60-many 0 413 123320 0.056362771 3968 232701353.12981105 0.0 38669 232701353.12981105 387 3 +base band60-many 0 413 123320 0.057328225 4096 232701353.12981105 0.0 38669 232701353.12981105 387 4 +candidate band60-many 0 410 1309043 0.017299625 4608 232701353.129811 0.0 38669 232701353.129811 387 4 +base band60-many 1 419 198200 0.27891877 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 0 +candidate band60-many 1 415 2582003 0.053028102 5888 232701353.129811 3261885389.337893 38669 232701353.129811 387 0 +candidate band60-many 1 415 2582003 0.053482692 5888 232701353.129811 3261885389.337893 38669 232701353.129811 387 1 +base band60-many 1 419 198200 0.292551426 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 1 +base band60-many 1 419 198200 0.292021597 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 2 +candidate band60-many 1 415 2582003 0.053178113 5760 232701353.129811 3261885389.337893 38669 232701353.129811 387 2 +candidate band60-many 1 415 2582003 0.053107782 5888 232701353.129811 3261885389.337893 38669 232701353.129811 387 3 +base band60-many 1 419 198200 0.292272675 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 3 +base band60-many 1 419 198200 0.276608727 4096 232701353.12981105 3261885389.337893 38669 232701353.12981105 387 4 +candidate band60-many 1 415 2582003 0.053130625 5760 232701353.129811 3261885389.337893 38669 232701353.129811 387 4 +base band60-few 0 17 88656 0.011724981 3968 3312567.1709916 0.0 990 3312567.1709916 3 0 +candidate band60-few 0 19 1263491 0.001918414 4480 3312567.1709916005 0.0 990 3312567.1709916005 3 0 +candidate band60-few 0 19 1263491 0.001943069 4480 3312567.1709916005 0.0 990 3312567.1709916005 3 1 +base band60-few 0 17 88656 0.011280692 3840 3312567.1709916 0.0 990 3312567.1709916 3 1 +base band60-few 0 17 88656 0.01178273 3840 3312567.1709916 0.0 990 3312567.1709916 3 2 +candidate band60-few 0 19 1263491 0.001909336 4480 3312567.1709916005 0.0 990 3312567.1709916005 3 2 +candidate band60-few 0 19 1263491 0.002126412 4480 3312567.1709916005 0.0 990 3312567.1709916005 3 3 +base band60-few 0 17 88656 0.011407509 3840 3312567.1709916 0.0 990 3312567.1709916 3 3 +base band60-few 0 17 88656 0.011505492 3968 3312567.1709916 0.0 990 3312567.1709916 3 4 +candidate band60-few 0 19 1263491 0.00201821 4480 3312567.1709916005 0.0 990 3312567.1709916005 3 4 +base band60-few 1 23 163536 0.024716159 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 0 +candidate band60-few 1 24 2536451 0.003039107 5760 3312567.1709916005 47035865.70596409 990 3312567.1709916005 3 0 +candidate band60-few 1 24 2536451 0.003005754 5760 3312567.1709916005 47035865.70596409 990 3312567.1709916005 3 1 +base band60-few 1 23 163536 0.024027822 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 1 +base band60-few 1 23 163536 0.023996775 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 2 +candidate band60-few 1 24 2536451 0.002904235 5760 3312567.1709916005 47035865.70596409 990 3312567.1709916005 3 2 +candidate band60-few 1 24 2536451 0.002866114 5760 3312567.1709916005 47035865.70596409 990 3312567.1709916005 3 3 +base band60-few 1 23 163536 0.024048792 3840 3312567.1709916 47035865.70596409 990 3312567.1709916 3 3 +base band60-few 1 23 163536 0.025701949 3968 3312567.1709916 47035865.70596409 990 3312567.1709916 3 4 +candidate band60-few 1 24 2536451 0.002943488 5760 3312567.1709916005 47035865.70596409 990 3312567.1709916005 3 4 diff --git a/doc/benchmarks/seed-extension/anchored-native-results.json b/doc/benchmarks/seed-extension/anchored-native-results.json new file mode 100644 index 00000000..122a16e0 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-native-results.json @@ -0,0 +1,210 @@ +{ + "complete": true, + "output_checks_passed": true, + "ended_at": "2026-10-16T19:58:21.856215+00:00", + "samples": 40, + "summary": [ + { + "case": "M-seeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 1.46, + "min": 1.45, + "max": 1.46 + }, + "user_seconds": { + "median": 1.45, + "min": 1.44, + "max": 1.46 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 19888, + "min": 19768, + "max": 20060 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 1.45, + "min": 1.44, + "max": 1.45 + }, + "user_seconds": { + "median": 1.44, + "min": 1.43, + "max": 1.44 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 19804, + "min": 19376, + "max": 19868 + } + }, + "candidate_over_base_wall_time": 0.9931506849315068 + }, + { + "case": "M-unseeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 12.44, + "min": 12.28, + "max": 12.48 + }, + "user_seconds": { + "median": 12.42, + "min": 12.27, + "max": 12.47 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 16860, + "min": 16660, + "max": 16992 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 12.47, + "min": 12.41, + "max": 12.91 + }, + "user_seconds": { + "median": 12.45, + "min": 12.39, + "max": 12.83 + }, + "system_seconds": { + "median": 0.01, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 16988, + "min": 16712, + "max": 17120 + } + }, + "candidate_over_base_wall_time": 1.002411575562701 + }, + { + "case": "H-seeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 14.59, + "min": 14.29, + "max": 14.76 + }, + "user_seconds": { + "median": 14.56, + "min": 14.28, + "max": 14.75 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 17280, + "min": 17172, + "max": 17456 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 1.01, + "min": 0.99, + "max": 1.03 + }, + "user_seconds": { + "median": 1.0, + "min": 0.98, + "max": 1.02 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 19928, + "min": 19604, + "max": 20064 + } + }, + "candidate_over_base_wall_time": 0.06922549691569568 + }, + { + "case": "H-unseeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 0.23, + "min": 0.23, + "max": 0.24 + }, + "user_seconds": { + "median": 0.22, + "min": 0.21, + "max": 0.22 + }, + "system_seconds": { + "median": 0.01, + "min": 0.01, + "max": 0.01 + }, + "max_rss_kib": { + "median": 30512, + "min": 30380, + "max": 30712 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 0.23, + "min": 0.23, + "max": 0.24 + }, + "user_seconds": { + "median": 0.22, + "min": 0.21, + "max": 0.23 + }, + "system_seconds": { + "median": 0.01, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 30604, + "min": 30484, + "max": 30836 + } + }, + "candidate_over_base_wall_time": 1.0 + } + ], + "pair_max_absolute_error": 0.0, + "pair_max_relative_error": 0.0 +} diff --git a/doc/benchmarks/seed-extension/anchored-native-samples.tsv b/doc/benchmarks/seed-extension/anchored-native-samples.tsv new file mode 100644 index 00000000..9a59793c --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-native-samples.tsv @@ -0,0 +1,41 @@ +variant case repetition elapsed_seconds user_seconds system_seconds max_rss_kib stdout_sha256 pair_output_sha256 stdout_equal pair_max_absolute_error pair_max_relative_error +base M-seeded 1 1.45 1.45 0.0 20060 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate M-seeded 1 1.44 1.43 0.0 19804 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate M-seeded 2 1.45 1.44 0.0 19808 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base M-seeded 2 1.46 1.45 0.0 19888 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base M-seeded 3 1.45 1.44 0.0 19768 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate M-seeded 3 1.45 1.44 0.0 19868 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate M-seeded 4 1.45 1.44 0.0 19792 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base M-seeded 4 1.46 1.46 0.0 19860 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base M-seeded 5 1.46 1.45 0.0 19936 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate M-seeded 5 1.45 1.44 0.0 19376 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base M-unseeded 1 12.44 12.42 0.01 16992 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +candidate M-unseeded 1 12.45 12.44 0.0 17120 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +candidate M-unseeded 2 12.47 12.45 0.01 16916 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +base M-unseeded 2 12.41 12.4 0.0 16860 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +base M-unseeded 3 12.28 12.27 0.0 16660 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +candidate M-unseeded 3 12.91 12.83 0.01 17072 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +candidate M-unseeded 4 12.71 12.69 0.0 16988 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +base M-unseeded 4 12.46 12.45 0.0 16924 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +base M-unseeded 5 12.48 12.47 0.0 16788 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +candidate M-unseeded 5 12.41 12.39 0.01 16712 fb26371815dc95ab87de0075b9c9607151e8b7f654cffea6d6c4eb5dc8d52e84 True 0.0 0.0 +base H-seeded 1 14.76 14.75 0.0 17280 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate H-seeded 1 1.03 1.02 0.01 19808 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate H-seeded 2 1.01 1.0 0.0 19604 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base H-seeded 2 14.75 14.74 0.0 17208 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base H-seeded 3 14.48 14.46 0.01 17300 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate H-seeded 3 1.01 1.0 0.0 20064 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate H-seeded 4 0.99 0.98 0.0 19928 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base H-seeded 4 14.29 14.28 0.0 17456 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base H-seeded 5 14.59 14.56 0.01 17172 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +candidate H-seeded 5 1.0 0.99 0.0 19988 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c True 0.0 0.0 +base H-unseeded 1 0.23 0.21 0.01 30484 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +candidate H-unseeded 1 0.23 0.21 0.01 30484 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +candidate H-unseeded 2 0.23 0.22 0.01 30572 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +base H-unseeded 2 0.24 0.22 0.01 30712 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +base H-unseeded 3 0.23 0.21 0.01 30512 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +candidate H-unseeded 3 0.23 0.22 0.0 30604 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +candidate H-unseeded 4 0.24 0.23 0.01 30836 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +base H-unseeded 4 0.23 0.22 0.01 30612 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +base H-unseeded 5 0.23 0.22 0.01 30380 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 +candidate H-unseeded 5 0.23 0.22 0.01 30764 3ab194ff6a2b3b4000f1130ae55618d3c239785555cd855af72fcbb56540508e True 0.0 0.0 diff --git a/doc/benchmarks/seed-extension/anchored-native.py b/doc/benchmarks/seed-extension/anchored-native.py new file mode 100755 index 00000000..5982447f --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-native.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +"""Compare native OxyS/fhlA CLI runs; requires GNU time and keeps raw outputs.""" + +import argparse +import csv +import datetime +import hashlib +import json +import math +import os +from pathlib import Path +import platform +import statistics +import subprocess +import sys + +QUERY = "GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC" +TARGET = "AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA" +CASES = ("M-seeded", "M-unseeded", "H-seeded", "H-unseeded") +RELATIVE_TOLERANCE = 2e-12 + + +def digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def save(path, value): + path.write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8") + + +def stamp(): + return datetime.datetime.now(datetime.timezone.utc).isoformat() + + +def read_pairs(path): + """Keep labels and NA cells exact; compare every finite probability numerically.""" + with path.open(newline="") as stream: + rows = list(csv.reader(stream, delimiter=";")) + if not rows or len(rows) != len(TARGET) + 1: + raise ValueError(f"unexpected probability matrix shape: {path}") + if any(len(row) != len(QUERY) + 1 for row in rows): + raise ValueError(f"unexpected probability matrix width: {path}") + labels = (rows[0], [row[0] for row in rows[1:]]) + values = [None if value == "NA" else float(value) + for row in rows[1:] for value in row[1:]] + if any(value is not None and not math.isfinite(value) for value in values): + raise ValueError(f"nonfinite probability: {path}") + return labels, values + + +def compare_pairs(reference, current): + if reference[0] != current[0] or len(reference[1]) != len(current[1]): + raise ValueError("probability matrix labels or dimensions differ") + maximum_absolute = maximum_relative = 0.0 + for before, after in zip(reference[1], current[1]): + if before is None or after is None: + if before is not after: + raise ValueError("probability matrix NA cells differ") + continue + absolute = abs(before - after) + relative = absolute / max(abs(before), abs(after)) if before or after else 0.0 + maximum_absolute = max(maximum_absolute, absolute) + maximum_relative = max(maximum_relative, relative) + return maximum_absolute, maximum_relative + + +def summarize(samples): + summary = [] + for case in dict.fromkeys(row["case"] for row in samples): + item = {"case": case} + for variant in ("base", "candidate"): + rows = [row for row in samples if row["case"] == case and row["variant"] == variant] + if not rows: + continue + item[variant] = {"samples": len(rows)} + for metric in ("elapsed_seconds", "user_seconds", "system_seconds", "max_rss_kib"): + values = [row[metric] for row in rows] + item[variant][metric] = { + "median": statistics.median(values), "min": min(values), "max": max(values)} + if "base" in item and "candidate" in item: + base_time = item["base"]["elapsed_seconds"]["median"] + item["candidate_over_base_wall_time"] = ( + item["candidate"]["elapsed_seconds"]["median"] / base_time if base_time else None) + summary.append(item) + return summary + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--binary", type=Path, required=True, help="baseline IntaRNA executable") + parser.add_argument("--candidate", type=Path, required=True, help="candidate IntaRNA executable") + parser.add_argument("--output-dir", type=Path, required=True, help="new or empty results directory") + parser.add_argument("--repetitions", type=int, default=5) + parser.add_argument("--cases", nargs="+", choices=CASES, default=list(CASES)) + parser.add_argument("--pair-output", action="store_true", help="request and numerically compare bpProb CSV") + parser.add_argument("--time", type=Path, default=Path("/usr/bin/time"), help="GNU time executable") + args = parser.parse_args() + if args.repetitions < 1 or len(set(args.cases)) != len(args.cases): + parser.error("repetitions must be positive and cases must not contain duplicates") + output = args.output_dir.resolve() + if output.exists() and (not output.is_dir() or any(output.iterdir())): + parser.error("--output-dir must be new or empty; raw results are never overwritten") + binaries = {"base": args.binary.resolve(), "candidate": args.candidate.resolve()} + timer = args.time.resolve() + for path in (*binaries.values(), timer): + if not path.is_file() or not os.access(path, os.X_OK): + parser.error(f"not an executable file: {path}") + env = dict(os.environ, OMP_NUM_THREADS="1", LC_ALL="C", LANG="C") + time_version = subprocess.check_output([str(timer), "--version"], text=True, env=env) + if "GNU" not in time_version: + parser.error("--time must identify itself as GNU time") + output.mkdir(parents=True, exist_ok=True) + cpu_info = Path("/proc/cpuinfo") + cpu_model = next((line.split(":", 1)[1].strip() for line in cpu_info.read_text().splitlines() + if line.startswith("model name")), None) if cpu_info.exists() else None + manifest = { + "schema_version": 1, "started_at": stamp(), "invocation": [sys.executable, *sys.argv], + "harness_sha256": digest(Path(__file__).resolve()), "gnu_time": time_version.splitlines()[0], + "workload": {"target": TARGET, "query": QUERY, "target_id": "fhlA", "query_id": "OxyS", + "model": "P", "seed_bp": 7, "threads": 1, "cases": args.cases, + "pair_output": args.pair_output, "repetitions": args.repetitions, "warmups": 0}, + "environment": {"platform": platform.platform(), "cpu_model": cpu_model, + "cpu_affinity": sorted(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else None, + "variables": {key: env[key] for key in ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", + "LD_LIBRARY_PATH", "LC_ALL", "LANG") if key in env}, + "timing_scope": "fresh complete process including accessibility and output", + "workstation_activity_and_cpu_frequency": "uncontrolled"}, + "verification": {"stdout": "byte-identical to first baseline sample of the same case", + "pair_csv": "exact labels/NA cells; every finite value compared", + "relative_tolerance": RELATIVE_TOLERANCE, "absolute_tolerance": 0}, + "raw_file_prefix": "{variant}-{case}-{repetition}", + "timing_format": "%e\\t%U\\t%S\\t%M", "binaries": {}, "commands": {}} + for variant, binary in binaries.items(): + manifest["binaries"][variant] = { + "path": str(binary), "sha256": digest(binary), + "version": subprocess.check_output([str(binary), "--version"], text=True, env=env).strip()} + manifest["commands"][variant] = {} + for case in args.cases: + mode, seed_state = case.split("-") + command = [str(binary), f"--query={QUERY}", f"--target={TARGET}", "--qId=OxyS", "--tId=fhlA", + "--model=P", f"--mode={mode}", "--threads=1", "--seedBP=7", + f"--noSeed={'false' if seed_state == 'seeded' else 'true'}"] + if args.pair_output: + command.append(f"--out=bpProb:{output / (variant + '-' + case + '.bpProb.csv')}") + manifest["commands"][variant][case] = command + save(output / "manifest.json", manifest) + samples, reference_stdout, reference_pairs = [], {}, {} + result = {"complete": False, "output_checks_passed": False} + save(output / "results.json", result) + fields = ("variant", "case", "repetition", "elapsed_seconds", "user_seconds", "system_seconds", + "max_rss_kib", "stdout_sha256", "pair_output_sha256", "stdout_equal", + "pair_max_absolute_error", "pair_max_relative_error") + with (output / "samples.tsv").open("w", newline="") as stream: + writer = csv.DictWriter(stream, fieldnames=fields, delimiter="\t", lineterminator="\n") + writer.writeheader() + for case in args.cases: + for repetition in range(1, args.repetitions + 1): + variants = ("base", "candidate") if repetition % 2 else ("candidate", "base") + for variant in variants: + prefix = f"{variant}-{case}-{repetition}" + command = manifest["commands"][variant][case] + stdout_path, stderr_path = output / f"{prefix}.stdout", output / f"{prefix}.stderr" + timing_path = output / f"{prefix}.time.tsv" + print(f"Running {prefix}", flush=True) + with stdout_path.open("wb") as stdout, stderr_path.open("wb") as stderr: + subprocess.run([str(timer), "-f", "%e\t%U\t%S\t%M", "-o", str(timing_path), *command], + env=env, stdout=stdout, stderr=stderr, check=True) + elapsed, user, system, rss = timing_path.read_text().strip().split("\t") + stdout_hash = digest(stdout_path) + reference_stdout.setdefault(case, stdout_hash) + sample = {"variant": variant, "case": case, "repetition": repetition, + "elapsed_seconds": float(elapsed), "user_seconds": float(user), + "system_seconds": float(system), "max_rss_kib": int(rss), + "stdout_sha256": stdout_hash, "pair_output_sha256": "", + "stdout_equal": stdout_hash == reference_stdout[case], + "pair_max_absolute_error": 0.0, "pair_max_relative_error": 0.0} + if args.pair_output: + pair_path = output / f"{prefix}.bpProb.csv" + (output / f"{variant}-{case}.bpProb.csv").rename(pair_path) + pairs = read_pairs(pair_path) + reference_pairs.setdefault(case, pairs) + absolute, relative = compare_pairs(reference_pairs[case], pairs) + sample.update(pair_output_sha256=digest(pair_path), + pair_max_absolute_error=absolute, pair_max_relative_error=relative) + writer.writerow(sample) + stream.flush() + samples.append(sample) + if not sample["stdout_equal"] or sample["pair_max_relative_error"] > RELATIVE_TOLERANCE: + save(output / "results.json", {**result, "failed_sample": prefix, "summary": summarize(samples)}) + raise RuntimeError(f"output comparison failed: {prefix}; see raw outputs and samples.tsv") + result.update(complete=True, output_checks_passed=True, ended_at=stamp(), + samples=len(samples), summary=summarize(samples), + pair_max_absolute_error=max(row["pair_max_absolute_error"] for row in samples), + pair_max_relative_error=max(row["pair_max_relative_error"] for row in samples)) + save(output / "results.json", result) + print(json.dumps(result, indent=2)) + return 0 + + +if __name__ == "__main__": + try: + sys.exit(main()) + except (OSError, ValueError, KeyError, RuntimeError, subprocess.CalledProcessError) as error: + sys.exit(str(error)) diff --git a/doc/benchmarks/seed-extension/anchored-pairs-results.json b/doc/benchmarks/seed-extension/anchored-pairs-results.json new file mode 100644 index 00000000..bcf7f307 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-pairs-results.json @@ -0,0 +1,110 @@ +{ + "complete": true, + "output_checks_passed": true, + "ended_at": "2026-10-16T20:02:58.410329+00:00", + "samples": 20, + "summary": [ + { + "case": "H-seeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 42.51, + "min": 41.45, + "max": 43.13 + }, + "user_seconds": { + "median": 42.46, + "min": 41.43, + "max": 43.09 + }, + "system_seconds": { + "median": 0.01, + "min": 0.01, + "max": 0.02 + }, + "max_rss_kib": { + "median": 18196, + "min": 18040, + "max": 18384 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 2.71, + "min": 2.61, + "max": 2.74 + }, + "user_seconds": { + "median": 2.69, + "min": 2.6, + "max": 2.72 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 23700, + "min": 23568, + "max": 23752 + } + }, + "candidate_over_base_wall_time": 0.06374970595154082 + }, + { + "case": "M-seeded", + "base": { + "samples": 5, + "elapsed_seconds": { + "median": 3.83, + "min": 3.73, + "max": 3.88 + }, + "user_seconds": { + "median": 3.8, + "min": 3.72, + "max": 3.87 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 23580, + "min": 23476, + "max": 23728 + } + }, + "candidate": { + "samples": 5, + "elapsed_seconds": { + "median": 3.8, + "min": 3.78, + "max": 3.81 + }, + "user_seconds": { + "median": 3.78, + "min": 3.77, + "max": 3.8 + }, + "system_seconds": { + "median": 0.01, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 23568, + "min": 23352, + "max": 23772 + } + }, + "candidate_over_base_wall_time": 0.9921671018276762 + } + ], + "pair_max_absolute_error": 1.5543122344752192e-15, + "pair_max_relative_error": 8.586607421772648e-15 +} diff --git a/doc/benchmarks/seed-extension/anchored-pairs-samples.tsv b/doc/benchmarks/seed-extension/anchored-pairs-samples.tsv new file mode 100644 index 00000000..154139ce --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-pairs-samples.tsv @@ -0,0 +1,21 @@ +variant case repetition elapsed_seconds user_seconds system_seconds max_rss_kib stdout_sha256 pair_output_sha256 stdout_equal pair_max_absolute_error pair_max_relative_error +base H-seeded 1 42.1 42.08 0.01 18196 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 3c3f29f6f3c3e663db0355670f0ebe25dc772e731c76e2a838a3734720a80470 True 0.0 0.0 +candidate H-seeded 1 2.71 2.7 0.0 23644 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 014c3213abf5673d041a12a0925b74b3c53c9964a8a39112d014602aa071fbe3 True 1.5543122344752192e-15 8.586607421772648e-15 +candidate H-seeded 2 2.71 2.69 0.01 23720 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 014c3213abf5673d041a12a0925b74b3c53c9964a8a39112d014602aa071fbe3 True 1.5543122344752192e-15 8.586607421772648e-15 +base H-seeded 2 42.93 42.88 0.02 18196 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 3c3f29f6f3c3e663db0355670f0ebe25dc772e731c76e2a838a3734720a80470 True 0.0 0.0 +base H-seeded 3 42.51 42.46 0.02 18364 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 3c3f29f6f3c3e663db0355670f0ebe25dc772e731c76e2a838a3734720a80470 True 0.0 0.0 +candidate H-seeded 3 2.61 2.6 0.0 23568 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 014c3213abf5673d041a12a0925b74b3c53c9964a8a39112d014602aa071fbe3 True 1.5543122344752192e-15 8.586607421772648e-15 +candidate H-seeded 4 2.64 2.63 0.0 23700 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 014c3213abf5673d041a12a0925b74b3c53c9964a8a39112d014602aa071fbe3 True 1.5543122344752192e-15 8.586607421772648e-15 +base H-seeded 4 43.13 43.09 0.01 18384 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 3c3f29f6f3c3e663db0355670f0ebe25dc772e731c76e2a838a3734720a80470 True 0.0 0.0 +base H-seeded 5 41.45 41.43 0.01 18040 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 3c3f29f6f3c3e663db0355670f0ebe25dc772e731c76e2a838a3734720a80470 True 0.0 0.0 +candidate H-seeded 5 2.74 2.72 0.01 23752 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 014c3213abf5673d041a12a0925b74b3c53c9964a8a39112d014602aa071fbe3 True 1.5543122344752192e-15 8.586607421772648e-15 +base M-seeded 1 3.88 3.87 0.0 23568 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +candidate M-seeded 1 3.79 3.77 0.01 23480 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +candidate M-seeded 2 3.8 3.79 0.01 23700 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +base M-seeded 2 3.81 3.8 0.0 23476 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +base M-seeded 3 3.73 3.72 0.0 23640 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +candidate M-seeded 3 3.8 3.78 0.01 23568 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +candidate M-seeded 4 3.81 3.8 0.0 23772 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +base M-seeded 4 3.83 3.82 0.0 23728 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +base M-seeded 5 3.83 3.8 0.01 23580 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 +candidate M-seeded 5 3.78 3.77 0.01 23352 f97d82bf597d1e9d08527e236bb36c368d775f43a9289a1e746aefd4b825010c 7548e25054548ea3ec9862d907ab7bb72100969b6188d52d64f3d681a00c5bbf True 0.0 0.0 diff --git a/doc/benchmarks/seed-extension/anchored-source-hashes.json b/doc/benchmarks/seed-extension/anchored-source-hashes.json new file mode 100644 index 00000000..cc157be8 --- /dev/null +++ b/doc/benchmarks/seed-extension/anchored-source-hashes.json @@ -0,0 +1,1074 @@ +{ + "baseline": "3866c62d10846fab1adc38b937d0c9b96cc06e51", + "date": "2026-10-16", + "candidate_source_sha256": { + "src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp": "557ad53c17965c988cd31875c7f9e273a6a8b230284c3c973bdf0bba7566e312", + "src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h": "58732493c173bd636e30e37c7dbd266493b1490ee16bed68ebb66c0f5050994f", + "src/IntaRNA/SeededPartitionFunction.cpp": "02bcc821539c6ebf8313fd20e4f3b50f29c93757555da0a1ff86ac3110ae09b9", + "src/IntaRNA/SeededPartitionFunction.h": "77164af4564a8c7bbd60fa3b33e2aa3f9bb777685d018c6c1b77b1326274a12c", + "src/IntaRNA/StackSeedDomain.h": "a0afdf1388b4334edfbb0313153ce0ae2ca4a747f4c24a3b451346f407c6b3f2", + "tests/PredictorSeedOracle_test.cpp": "59e564aa1b6c6f50e4e5e646b626ebdccbbe8556849dadd3ad768986e85cb901", + "tests/SeededPartitionFunction_test.cpp": "ee018018602c145d825c806ff044a360d0dc7ee94b71689e9247263b83e0def7" + }, + "native_build_flags": { + "base": { + "AM_CXXFLAGS": "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread", + "CPPFLAGS": "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX": "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS": "", + "LDFLAGS": "-L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "configure": "CC=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-gcc CXX=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ CPPFLAGS=-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include 'LDFLAGS=-L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib' --with-vrna=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain --with-boost=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain --with-boost-libdir=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib --prefix=/home/alex/CodexProjects/IntaRNA/issue-268-validation/install-release" + }, + "candidate": { + "AM_CXXFLAGS": "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread", + "CPPFLAGS": "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX": "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS": "", + "LDFLAGS": "-L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "configure": "CC=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-gcc CXX=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ CPPFLAGS=-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include 'LDFLAGS=-L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib' --with-vrna=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain --with-boost=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain --with-boost-libdir=/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib --prefix=/home/alex/CodexProjects/IntaRNA/issue-268-validation/install-release" + } + }, + "source_verification": { + "baseline": { + "checked_files": 283, + "mismatches": [] + }, + "release": { + "checked_files": 283, + "mismatches": [] + }, + "debug": { + "checked_files": 283, + "mismatches": [] + } + }, + "kernel_build": { + "baseline": "3866c62d10846fab1adc38b937d0c9b96cc06e51", + "compiler": "x86_64-conda-linux-gnu-g++ (conda-forge gcc 14.4.0-7) 14.4.0", + "commands": [ + [ + "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++", + "-std=c++23", + "-O3", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + "-I/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/base", + "-I/home/alex/CodexProjects/IntaRNA/pr-worktrees/issue-270-anchored/src", + "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/driver.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/base/IntaRNA/SeededPartitionFunction.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/base/IntaRNA/StackSeedDomain.cpp", + "-Wl,--gc-sections", + "-Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib", + "-o", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/base/benchmark" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++", + "-std=c++23", + "-O3", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + "-I/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/candidate", + "-I/home/alex/CodexProjects/IntaRNA/pr-worktrees/issue-270-anchored/src", + "-I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/driver.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/candidate/IntaRNA/SeededPartitionFunction.cpp", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/candidate/IntaRNA/StackSeedDomain.cpp", + "-Wl,--gc-sections", + "-Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib", + "-o", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/kernel/candidate/benchmark" + ] + ], + "sha256": { + "driver.cpp": "98b0133e334b5174146ed440b0ed0f30faf2f83d32f217a386c3d43196f65b93", + "build.py": "6a086dd9fa12f3fef28789d2401bd77a691321314cbecd495d9e4869e7b79308", + "run.py": "22298ce92cb6940ad23164cac868aa638343d58f251a66ef5e5cc66dae1dee4e", + "base/IntaRNA/SeededPartitionFunction.cpp": "3783c8380e5108146cd903cbbb3fa3d0523749baf713395a15a4f436b3dfdd03", + "base/IntaRNA/SeededPartitionFunction.h": "8216d82858cca5cf960056d7e11a2ada1118a98e1cf513623839d75fab40381d", + "base/IntaRNA/StackSeedDomain.cpp": "a41906b5442e5b8ade69dc482919619c28761b908447faab984a84a807d338ee", + "base/IntaRNA/StackSeedDomain.h": "73275a75976d98800db58bfb019f912b7245a9c26072fe17f7ee16dbdaad93c7", + "candidate/IntaRNA/SeededPartitionFunction.cpp": "02bcc821539c6ebf8313fd20e4f3b50f29c93757555da0a1ff86ac3110ae09b9", + "candidate/IntaRNA/SeededPartitionFunction.h": "77164af4564a8c7bbd60fa3b33e2aa3f9bb777685d018c6c1b77b1326274a12c", + "candidate/IntaRNA/StackSeedDomain.cpp": "a41906b5442e5b8ade69dc482919619c28761b908447faab984a84a807d338ee", + "candidate/IntaRNA/StackSeedDomain.h": "a0afdf1388b4334edfbb0313153ce0ae2ca4a747f4c24a3b451346f407c6b3f2" + }, + "shared_header_sha256": { + "PartitionArithmetic.h": "5fe71abe6568906057c0a264c2802d3bfd7a902471b21cc838b78f18b04a0bd7", + "Matrix.h": "8ecc55e46643ccde16ed89aa63a415a6da7aa3d3c59fe94dd6b4d46d5f5275af", + "Interaction.h": "c0ce17a0f5b204bcb366529872346a2316993b749da9c4c9927773726296ebaa", + "general.h": "0fff34ca1a1e676debe88326af0a5a0eddeff20e05604abda11096527b12df98", + "intarna_config.h": "4cbc306a33335dd49061ed4e3d70f7f70e776f5aa8a6e0e73ea6715cfa4163c2" + } + }, + "kernel_binary_sha256": { + "base": "2f8d146f5d9036268a1d59a2c0602f04eb1b3105ad05b8d5be89119806694cb2", + "candidate": "c94e6089671208b6aabdab81b11bb173ddbd54a571406d6413aa76205be56c20" + }, + "kernel_dump_sha256": { + "kernel-exact-verify/band60-few-base.txt": "f6a8651d3f85153ba1340ca9e7dfbe943fcfaab3c4e6fa3bead711c6735ebe7f", + "kernel-exact-verify/band60-few-candidate.txt": "f6a8651d3f85153ba1340ca9e7dfbe943fcfaab3c4e6fa3bead711c6735ebe7f", + "kernel-exact-verify/band60-many-base.txt": "fcdd7b7db7db7d20ae1059580bbdb532303580a21d6118a286054f22e760d94e", + "kernel-exact-verify/band60-many-candidate.txt": "fcdd7b7db7db7d20ae1059580bbdb532303580a21d6118a286054f22e760d94e", + "kernel-exact-verify/dense40-few-base.txt": "9a5cba3f768f540ff2b2078b82ebd15efcbcb98b18582d3344e379467c548f7e", + "kernel-exact-verify/dense40-few-candidate.txt": "9a5cba3f768f540ff2b2078b82ebd15efcbcb98b18582d3344e379467c548f7e", + "kernel-exact-verify/dense40-many-base.txt": "3f07dc4f1b9556a0438a9f8521ae32dbcbe981c5421ca4570c79b6bf04103b87", + "kernel-exact-verify/dense40-many-candidate.txt": "3f07dc4f1b9556a0438a9f8521ae32dbcbe981c5421ca4570c79b6bf04103b87", + "kernel-heuristic-verify/band60-few-base.txt": "b70097c40696705eda01ed157fd8b3863a7d4e4b4b8d1dcd76042a135bf6bff7", + "kernel-heuristic-verify/band60-few-candidate.txt": "fa508860983c46d00fbb98484a2b60ca113849af6479eb054d6a0350126ec4cc", + "kernel-heuristic-verify/band60-many-base.txt": "fbecd2caeba9e2e0d9191dde17cd6d2598b65793c431b8bee8357159d4f817b6", + "kernel-heuristic-verify/band60-many-candidate.txt": "360a706a1934d0a876a43c47b178cf23c83a26ac3af13a32dffc6b2fa1e5d2f8", + "kernel-heuristic-verify/dense40-few-base.txt": "02c503ef9f86cbde99e40a977963e62e8baf60fe47dc8dad81f7f51264da878f", + "kernel-heuristic-verify/dense40-few-candidate.txt": "6443bc50ae27f5fbca878899f02e19e82f6f68d0351c68f14557c287e980af62", + "kernel-heuristic-verify/dense40-many-base.txt": "7f9071f1962facde0a9e5691ffa8ff50c7bd14399f009c5997faf9a7313f183e", + "kernel-heuristic-verify/dense40-many-candidate.txt": "39eebf52edcf702afc91b2e307ef2d15248a3a420ba19e3c81b2ec9183ee2ac5" + }, + "native_manifests": { + "ordinary": { + "schema_version": 1, + "started_at": "2026-10-16T19:54:41.787838+00:00", + "invocation": [ + "/home/alex/miniconda3/bin/python3", + "doc/benchmarks/seed-extension/anchored-native.py", + "--binary", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--candidate", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--output-dir", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-runtime", + "--repetitions", + "5" + ], + "harness_sha256": "cb34d155d393ee36b73a8b3c4c4a2c64b988835a0a5f62902a6110cd89dd0b4b", + "gnu_time": "time (GNU Time) UNKNOWN", + "workload": { + "target": "AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "query": "GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "target_id": "fhlA", + "query_id": "OxyS", + "model": "P", + "seed_bp": 7, + "threads": 1, + "cases": [ + "M-seeded", + "M-unseeded", + "H-seeded", + "H-unseeded" + ], + "pair_output": false, + "repetitions": 5, + "warmups": 0 + }, + "environment": { + "platform": "Linux-6.8.0-147-generic-x86_64-with-glibc2.39", + "cpu_model": "AMD Ryzen 5 7530U with Radeon Graphics", + "cpu_affinity": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ], + "variables": { + "OMP_NUM_THREADS": "1", + "LC_ALL": "C", + "LANG": "C" + }, + "timing_scope": "fresh complete process including accessibility and output", + "workstation_activity_and_cpu_frequency": "uncontrolled" + }, + "verification": { + "stdout": "byte-identical to first baseline sample of the same case", + "pair_csv": "exact labels/NA cells; every finite value compared", + "relative_tolerance": 2e-12, + "absolute_tolerance": 0 + }, + "raw_file_prefix": "{variant}-{case}-{repetition}", + "timing_format": "%e\\t%U\\t%S\\t%M", + "binaries": { + "base": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "sha256": "de42e09bdabe46a2865a6978bc86241689c9416c41f011ed70fdafc1d9845c9c", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + }, + "candidate": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "sha256": "4dac44fd068710e11a11c3e001ab5ae3a22948f41311461b6f5ef4637c8da55b", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + } + }, + "commands": { + "base": { + "M-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=false" + ], + "M-unseeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=true" + ], + "H-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=false" + ], + "H-unseeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=true" + ] + }, + "candidate": { + "M-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=false" + ], + "M-unseeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=true" + ], + "H-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=false" + ], + "H-unseeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=true" + ] + } + } + }, + "pair_output": { + "schema_version": 1, + "started_at": "2026-10-16T19:58:34.537235+00:00", + "invocation": [ + "/home/alex/miniconda3/bin/python3", + "doc/benchmarks/seed-extension/anchored-native.py", + "--binary", + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--candidate", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--output-dir", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-pairs", + "--repetitions", + "5", + "--cases", + "H-seeded", + "M-seeded", + "--pair-output" + ], + "harness_sha256": "cb34d155d393ee36b73a8b3c4c4a2c64b988835a0a5f62902a6110cd89dd0b4b", + "gnu_time": "time (GNU Time) UNKNOWN", + "workload": { + "target": "AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "query": "GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "target_id": "fhlA", + "query_id": "OxyS", + "model": "P", + "seed_bp": 7, + "threads": 1, + "cases": [ + "H-seeded", + "M-seeded" + ], + "pair_output": true, + "repetitions": 5, + "warmups": 0 + }, + "environment": { + "platform": "Linux-6.8.0-147-generic-x86_64-with-glibc2.39", + "cpu_model": "AMD Ryzen 5 7530U with Radeon Graphics", + "cpu_affinity": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ], + "variables": { + "OMP_NUM_THREADS": "1", + "LC_ALL": "C", + "LANG": "C" + }, + "timing_scope": "fresh complete process including accessibility and output", + "workstation_activity_and_cpu_frequency": "uncontrolled" + }, + "verification": { + "stdout": "byte-identical to first baseline sample of the same case", + "pair_csv": "exact labels/NA cells; every finite value compared", + "relative_tolerance": 2e-12, + "absolute_tolerance": 0 + }, + "raw_file_prefix": "{variant}-{case}-{repetition}", + "timing_format": "%e\\t%U\\t%S\\t%M", + "binaries": { + "base": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "sha256": "de42e09bdabe46a2865a6978bc86241689c9416c41f011ed70fdafc1d9845c9c", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + }, + "candidate": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "sha256": "4dac44fd068710e11a11c3e001ab5ae3a22948f41311461b6f5ef4637c8da55b", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + } + }, + "commands": { + "base": { + "H-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=false", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-pairs/base-H-seeded.bpProb.csv" + ], + "M-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=false", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-pairs/base-M-seeded.bpProb.csv" + ] + }, + "candidate": { + "H-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=H", + "--threads=1", + "--seedBP=7", + "--noSeed=false", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-pairs/candidate-H-seeded.bpProb.csv" + ], + "M-seeded": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=OxyS", + "--tId=fhlA", + "--model=P", + "--mode=M", + "--threads=1", + "--seedBP=7", + "--noSeed=false", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-pairs/candidate-M-seeded.bpProb.csv" + ] + } + } + } + }, + "native_verification": { + "commands": [ + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-M-false-base.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-M-false-candidate.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-M-true-base.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-M-true-candidate.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-H-false-base.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-H-false-candidate.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-H-true-base.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGGGGGGGGGGGGGG", + "--query=CCCCCCCCCCCCCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/dense-H-true-candidate.csv", + "--default-log-file=/dev/null" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-M-false-base.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-M-false-candidate.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-M-true-base.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-M-true-candidate.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-H-false-base.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-H-false-candidate.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-H-true-base.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGGGG", + "--query=CCCCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/mixed-H-true-candidate.csv", + "--default-log-file=/dev/null", + "--seedTQ=1|||&4|||,1||&4||,2||&5||,3|||&2|||,5||&4||" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-M-false-base.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-M-false-candidate.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-M-true-base.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=M", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-M-true-candidate.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-H-false-base.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=false", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-H-false-candidate.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-268-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-H-true-base.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ], + [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--target=GGGAGGGG", + "--query=CCCCUCCC", + "--model=P", + "--mode=H", + "--seedBP=2", + "--threads=1", + "--acc=N", + "--outNoLP=true", + "--outNoGUend=false", + "--intLenMax=8", + "--outMode=C", + "--outCsvCols=id1,id2,start1,end1,start2,end2,E,Zall,Eall", + "--out=bpProb:/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/native-comparison/cropped-H-true-candidate.csv", + "--default-log-file=/dev/null", + "--tRegion=2-7", + "--qRegion=2-7" + ] + ], + "checks": [ + { + "case": "cropped-H-false", + "probabilities": 64, + "max_relative_error": 5.016701488012286e-16, + "byte_identical": false + }, + { + "case": "cropped-H-true", + "probabilities": 64, + "max_relative_error": 3.6509232762887005e-16, + "byte_identical": false + }, + { + "case": "cropped-M-false", + "probabilities": 64, + "max_relative_error": 0.0, + "byte_identical": true + }, + { + "case": "cropped-M-true", + "probabilities": 64, + "max_relative_error": 0.0, + "byte_identical": true + }, + { + "case": "dense-H-false", + "probabilities": 306, + "max_relative_error": 2.3073317770873946e-15, + "byte_identical": false + }, + { + "case": "dense-H-true", + "probabilities": 306, + "max_relative_error": 5.496759884311985e-16, + "byte_identical": false + }, + { + "case": "dense-M-false", + "probabilities": 306, + "max_relative_error": 0.0, + "byte_identical": true + }, + { + "case": "dense-M-true", + "probabilities": 306, + "max_relative_error": 0.0, + "byte_identical": true + }, + { + "case": "mixed-H-false", + "probabilities": 36, + "max_relative_error": 6.684860863120327e-16, + "byte_identical": false + }, + { + "case": "mixed-H-true", + "probabilities": 36, + "max_relative_error": 2.532544440625586e-16, + "byte_identical": false + }, + { + "case": "mixed-M-false", + "probabilities": 36, + "max_relative_error": 0.0, + "byte_identical": true + }, + { + "case": "mixed-M-true", + "probabilities": 36, + "max_relative_error": 0.0, + "byte_identical": true + } + ] + }, + "validation": { + "release": "make tests -j2: 7/7 drivers; 115 API cases; 457827 assertions", + "debug": "make tests -j2: 7/7 drivers; 115 API cases; 457828 assertions", + "kernel_exact": { + "heuristic": false, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 0, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "dense40-few", + "compared_entries": 42460, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + }, + { + "case": "band60-few", + "compared_entries": 22796, + "max_relative_error": 0.0, + "max_relative_error_by_kind": { + "b": 0.0, + "p": 0.0, + "z": 0.0 + } + } + ] + }, + "kernel_heuristic": { + "heuristic": true, + "threads": 1, + "relative_tolerance": 1e-12, + "repetitions": 0, + "checks": [ + { + "case": "dense40-many", + "compared_entries": 220710, + "max_relative_error": 6.622292098580306e-15, + "max_relative_error_by_kind": { + "b": 8.855415220299877e-16, + "p": 6.622292098580306e-15, + "z": 1.4956070422152216e-16 + } + }, + { + "case": "dense40-few", + "compared_entries": 3435, + "max_relative_error": 3.1583238696495025e-15, + "max_relative_error_by_kind": { + "b": 8.208110010426082e-16, + "p": 3.1583238696495025e-15, + "z": 2.2691994745553064e-16 + } + }, + { + "case": "band60-many", + "compared_entries": 42270, + "max_relative_error": 4.389723301013629e-15, + "max_relative_error_by_kind": { + "b": 8.055521556013479e-16, + "p": 4.389723301013629e-15, + "z": 2.5614223541768804e-16 + } + }, + { + "case": "band60-few", + "compared_entries": 4591, + "max_relative_error": 1.8377333584902226e-15, + "max_relative_error_by_kind": { + "b": 8.527781049852308e-16, + "p": 1.8377333584902226e-15, + "z": 1.40574141827393e-16 + } + } + ] + }, + "installation": "make install; three changed public headers compiled independently; installed pkg-config probability example passed" + }, + "measurement_conditions": { + "repetitions": 5, + "threads": 1, + "ordering": "alternating baseline/candidate fresh processes", + "concurrent_project_build_test_install": false, + "other_workstation_activity_cpu_affinity_frequency": "uncontrolled", + "platform_validation": "Linux GCC14.4; macOS/Apple Clang not run" + } +} diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp index 9428587b..84faaab0 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp @@ -36,8 +36,8 @@ predict( const IndexRange & r1, const IndexRange & r2 ) { try { if(pairProbabilities) pairProbabilities->markApproximate(); - // Stack seeds share the nonnegative canonical-anchor recurrence with exact - // prediction. The frozen right choices restrict both its forward and outside. + // Stack seeds share exact prediction's left-most anchor tables. Frozen right + // choices restrict the same left/right contractions and their outside pass. if(seedHandler.guaranteesStackOnlySeeds()) { PredictorMfeEns2dSeedExtension::predict(r1,r2); return; diff --git a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h index e7ada51f..27b87452 100644 --- a/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h +++ b/src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h @@ -15,7 +15,9 @@ namespace IntaRNA { * Note, for each seed start (i1,i2) only the mfe seed is considered for the * overall interaction computation instead of considering all possible seeds * starting at (i1,i2). - * For stack-only seeds, the first completed seed uniquely owns each chain. + * Stack-only seeds share the exact predictor's left-most anchor tables. The + * first completed seed uniquely owns each chain, equivalently the left-most + * seed after removing containing occurrences. * Retain its one-sided extensions and the left extensions ending at its * preferred right endpoint. Pair probabilities describe this retained ensemble. * @@ -75,7 +77,7 @@ class PredictorMfeEns2dHeuristicSeedExtension: public PredictorMfeEns2dSeedExten /** Freeze one preferred right endpoint for each admitted stack anchor. * The existing virtual right-extension hooks determine the choice; partition - * contributions are deferred to the common seed-avoiding recurrence. + * contributions are deferred to the shared left-most-anchor recurrence. * @param n first active range length * @param m second active range length * @param seeds contained stack occurrences in the active range diff --git a/src/IntaRNA/SeededPartitionFunction.cpp b/src/IntaRNA/SeededPartitionFunction.cpp index f3a643d7..dc4afff6 100644 --- a/src/IntaRNA/SeededPartitionFunction.cpp +++ b/src/IntaRNA/SeededPartitionFunction.cpp @@ -13,7 +13,7 @@ using A=PartitionArithmetic; using Support=std::array; using RightStates=std::array; -/** Exact left-most-anchor factorization, with bounded boundary publication. */ +/** Shared left-most-anchor factorization, with bounded boundary publication. */ Kernel::Result computeAnchored(const Kernel::Domain & d,const StackSeedDomain & seeds, const Kernel::Weights & w,bool pairs) @@ -22,17 +22,27 @@ computeAnchored(const Kernel::Domain & d,const StackSeedDomain & seeds, if(pairs) out.mass=Matrix(d.n,d.m,0); if(!d.n || !d.m || !seeds.maxSeedLength() || !d.span1 || !d.span2) return out; - // Containing seeds add no paths to the exact union. In the remaining family, - // start order equals end order along each diagonal, including overlapping - // seeds. Thus excluding an earlier seed while extending left is sufficient; - // no earlier seed can first finish beyond the anchor's right end. + // A containing seed cannot be the first completed owner: its inner seed + // finishes earlier, or finishes together and is shorter. Among the remaining + // occurrences on a path, start and end order agree; reversing their order + // would require overlapping diagonal stacks and hence containment. Therefore + // the left-most minimal anchor is also the heuristic's first completed seed, + // including its shortest co-ending tie break. Both modes exclude earlier + // minimal seeds while extending left and leave the right side unrestricted. const auto anchors=seeds.minimalOccurrences(); std::unordered_map anchorIndex; anchorIndex.reserve(anchors.size()); + std::vector retainedRight; + if(w.selectRight) retainedRight.reserve(anchors.size()); size_t cap=2; for(size_t a=0;as.first || p.second>s.second) continue; const size_t u=s.first-p.first,v=s.second-p.second; if(u>maxN-length || v>maxM-length) continue; nL=std::max(nL,u+1); mL=std::max(mL,v+1); minLeft1=std::min(minLeft1,u); minLeft2=std::min(minLeft2,v); + if(p==s) unrestrictedRight=true; } if(!nL || !mL) { seedWeight=0; return; } nR=std::min(d.n-t.first,maxN-length-minLeft1+1); mR=std::min(d.m-t.second,maxM-length-minLeft2+1); + selectedRight=false; + if(w.selectRight) { + const Pair end=retainedRight[a]; + // Only a strictly later endpoint denotes a two-sided extension. Check + // coordinates before subtracting, including user-supplied selectors. + if(end.first>t.first && end.second>t.second && end.first; // pending lonely pair / stack-supported end - Result out; - if (pairs) out.mass=Matrix(d.n,d.m,0); - if (!seeds.maxSeedLength() || !d.span1 || !d.span2) return out; - - // Freeze heuristic choices before either pass. A first hit is owned by the - // shortest admitted seed ending here, including coincident variable lengths. - std::unordered_map selected; - if (w.selectRight) for(size_t i=0;i left(cells,cap),leftTotal(maxN,maxM),hybrid(maxN,maxM); - Matrix hit(maxN,maxM),right(maxN,maxM); - Matrix valid(maxN,maxM); - Matrix ending(maxN,maxM),byEnd; - Matrix boundary,dLeft,dTotal; - Matrix dHit,adjoint; - if(w.selectRight) byEnd.resize(maxN,maxM,false); - if(pairs) { - boundary.resize(maxN,maxM,false); - dLeft.resize(cells,cap,false); dTotal.resize(maxN,maxM,false); - dHit.resize(maxN,maxM,false); adjoint.resize(maxN,maxM,false); - } - auto reset=[](Matrix & matrix,size_t rows,size_t columns) { - // Matrix storage is row-major. Clear contiguous full-width regions in - // one pass; narrower rectangles leave the inactive row tails untouched. - if(columns==matrix.size2()) std::fill_n(&matrix(0,0),rows*columns,T{}); - else for(size_t i=0;i groups; - std::vector anchors; - groups.reserve(w.selectRight?cells+1:1); anchors.reserve(cells); - auto addAnchor=[&](Group & group,Pair q) { - anchors.push_back({q,group.first}); group.first=anchors.size(); - }; - auto forAnchors=[&](const Group & group,const auto & visit) { - for(size_t a=group.first;a;a=anchors[a-1].next) visit(anchors[a-1].pair); - }; - seeds.forEachStart([&](size_t p1,size_t p2) { - const Pair p{p1,p2}; if (!w.valid(p)) return; - const size_t n=std::min(d.span1,d.n-p1),m=std::min(d.span2,d.m-p2); - reset(left,n*m,cap); reset(leftTotal,n,m); reset(hit,n,m); - reset(hybrid,n,m); - if(w.selectRight) reset(byEnd,n,m); - if(pairs) { reset(boundary,n,m); reset(dHit,n,m); } - for(size_t i=0;i0) extendLeft(i,j,std::min(cap,c+2),A::multiply(prefix,t)); - } - } - nonstack(i,j,[&](size_t u,size_t v) { - if(leftTotal(u,v)>0) extendLeft(i,j,1,A::multiply(leftTotal(u,v),transition(i,j,u,v))); - }); - // A nonstack edge may leave only a supported end under noLP. - for(size_t c=d.noLP?1:0;c=n || end.second-p2>=m) continue; - const Pair local{end.first-p1,end.second-p2}; - auto & groupIndex=byEnd(local.first,local.second); - if(!groupIndex) { - groupIndex=groups.size(); groups.push_back({local,false}); - } - addAnchor(groups[groupIndex],{i,j}); - } - auto fillRight=[&](const Group & group) { - reset(right,group.end.first+1,group.end.second+1); - forAnchors(group,[&](Pair q) { right(q.first,q.second)=hit(q.first,q.second); }); - // B is seed-unconstrained: no seed lookup or subtraction occurs here. - for(size_t i=0;i<=group.end.first;++i) for(size_t j=0;j<=group.end.second;++j) { - if(!valid(i,j)) continue; - if(i && j && valid(i-1,j-1)) { - const auto & prev=right(i-1,j-1); - if(prev[0]>0 || prev[1]>0) - right(i,j)[1]=A::add(right(i,j)[1],A::multiply(A::add(prev[0],prev[1]),transition(i,j,i-1,j-1))); - } - nonstack(i,j,[&](size_t u,size_t v) { - if(right(u,v)[1]>0) { - auto & value=right(i,j)[d.noLP?0:1]; - value=A::add(value,A::multiply(right(u,v)[1],transition(i,j,u,v))); - } - }); - } - }; - for(const auto & group:groups) { - if(!group.first) continue; - fillRight(group); - if(group.all) { - for(size_t i=0;i0) { - const Pair q{p1+i,p2+j}; - const Z_type b=A::check(w.boundary(p,q)); - if(pairs) boundary(i,j)=b; - out.z=A::add(out.z,A::multiply(hybrid(i,j),b)); - if(w.complete) w.complete(p,q,hybrid(i,j),b); - } - if(!pairs) return; - - // Reverse the retained right contractions. Recompute one group at a time - // to bound memory independently of the number of heuristic choices. - for(size_t g=0;g0;--ii) for(size_t jj=group.end.second+1;jj>0;--jj) { - const size_t i=ii-1,j=jj-1; if(!valid(i,j)) continue; - auto & a=adjoint(i,j); - if(group.all || (i==group.end.first && j==group.end.second)) a[1]=A::add(a[1],boundary(i,j)); - // A vertex is owned by its most recent pair: T*dT counts every - // right-side pair, including the accepting pair, exactly once. - auto & mass=out.mass(p1+i,p2+j); - for(size_t c=0;c<2;++c) mass=A::add(mass,A::multiply(right(i,j)[c],a[c])); - if(i && j && valid(i-1,j-1)) { - const Z_type value=A::multiply(a[1],transition(i,j,i-1,j-1)); - for(size_t c=0;c<2;++c) adjoint(i-1,j-1)[c]=A::add(adjoint(i-1,j-1)[c],value); - } - nonstack(i,j,[&](size_t u,size_t v) { - adjoint(u,v)[1]=A::add(adjoint(u,v)[1],A::multiply(a[d.noLP?0:1],transition(i,j,u,v))); - }); - } - forAnchors(group,[&](Pair q) { - for(size_t c=0;c<2;++c) - dHit(q.first,q.second)[c]=A::add(dHit(q.first,q.second)[c],adjoint(q.first,q.second)[c]); - }); - } - if(w.selectRight) for(size_t i=0;i0;--ii) for(size_t jj=m;jj>0;--jj) { - const size_t i=ii-1,j=jj-1,q=i*m+j; if(!valid(i,j)) continue; - for(size_t c=d.noLP?1:0;c namespace IntaRNA { -/** Nonnegative seeded extension inside/outside computation. Exact paths belong - * to their left-most seed in a union-equivalent family that excludes containing - * occurrences. Shared seed-local tables join seed-avoiding left extensions to - * unrestricted right extensions. Complete boundaries are accumulated in bounded - * batches; anchors crossing batches are recomputed. A heuristic selector retains - * its first-completed-seed ownership, choosing the shortest co-ending seed. +/** Nonnegative seeded extension inside/outside computation. Paths belong to + * their left-most seed after removing containing occurrences. This is also the + * first completed seed, choosing the shortest co-ending seed: a containing seed + * cannot complete first, and remaining overlapping stacks have the same start + * and end order. Exact and heuristic prediction share seed-local tables joining + * seed-avoiding left extensions to unrestricted right extensions. A heuristic + * selector keeps one right endpoint for true two-sided extensions and retains + * every one-sided extension. Complete boundaries are accumulated in bounded + * batches; anchors crossing batches are recomputed. Batches without the seed's + * own start as a source need right tables only up to the chosen endpoint. * Native energy ownership is supplied by the adapter: initiation once, one * transition per successive pair, one boundary factor. All callbacks must be * deterministic; zero denotes forbidden weight. @@ -31,11 +35,13 @@ class SeededPartitionFunction { std::function initiation; std::function transition, boundary; std::function complete; - /** Optional frozen heuristic selector, called once per canonical anchor. + /** Optional heuristic selector, called once per minimal canonical anchor + * before batching; its choices remain fixed for the forward and outside passes. * Arguments are its start and end; the result is a retained right endpoint. * Keep paths with no left extension, no right extension, or that endpoint. * Empty means all right endpoints (exact). Selection cannot depend on pair - * marking; out-of-domain choices contribute no two-sided extension. + * marking; choices outside the domain or the complete interaction's spans, + * or not strictly later on both strands, contribute no two-sided extension. */ std::function selectRight; }; diff --git a/src/IntaRNA/StackSeedDomain.h b/src/IntaRNA/StackSeedDomain.h index 95ca7998..376dc0b6 100644 --- a/src/IntaRNA/StackSeedDomain.h +++ b/src/IntaRNA/StackSeedDomain.h @@ -40,8 +40,10 @@ class StackSeedDomain { size_t maxSeedLength() const; /** Return the union-equivalent family without containing occurrences. * A seed containing another admitted seed on its diagonal is redundant for - * exact union counting. The original family remains available for annotations - * and heuristic ownership. Results are ordered by their starting pair. + * exact union counting and cannot be the heuristic's first-completed owner: + * its inner seed completes earlier or wins the shortest co-ending tie. + * The original family remains available for annotations and native endpoint + * selection. Results are ordered by their starting pair. * @return admitted occurrences containing no other admitted occurrence */ std::vector minimalOccurrences() const; diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 22671495..d35296e1 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -9,6 +9,7 @@ #include "IntaRNA/PredictorMfe2dSeed.h" #include "IntaRNA/PredictorMfe2dSeedExtension.h" #include "IntaRNA/PredictorMfeEns2dSeedExtension.h" +#include "IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h" #include "IntaRNA/PredictorMfeEnsSeedOnly.h" #include "IntaRNA/PredictorMfeSeedOnly.h" #include "IntaRNA/ReverseAccessibility.h" @@ -986,3 +987,107 @@ TEST_CASE("native exact nested explicit seeds preserve cropped boundary ensemble REQUIRE(energies.at(reportedBoundary)==best); } } + +namespace { +class AnchoredHeuristicProbe : public PredictorMfeEns2dHeuristicSeedExtension { +public: + using PredictorMfeEns2dHeuristicSeedExtension::PredictorMfeEns2dHeuristicSeedExtension; + using Pair=Interaction::BasePair; + std::map choices; + std::map fillCalls; + size_t rankingCalls=0; + std::map> coefficients; + std::map completeCalls; +protected: + void fillHybridZ_right(size_t sj1,size_t sj2,size_t si1,size_t si2) override; + void updateOptRightZ(size_t i1,size_t j1,size_t i2,size_t j2,E_type hybrid) override; + void updateZ(size_t i1,size_t j1,size_t i2,size_t j2,Z_type z,bool hybrid) override; +}; + +void AnchoredHeuristicProbe::fillHybridZ_right(size_t sj1,size_t sj2,size_t si1,size_t si2) +{ + ++fillCalls[{si1,si2}]; + PredictorMfeEns2dHeuristicSeedExtension::fillHybridZ_right(sj1,sj2,si1,si2); + // A custom native hook fixes the retained endpoint after ordinary ranking. + // The shared kernel must use that choice without selecting it again. + const auto selected=choices.at({si1,si2}); + j1opt=selected.first;j2opt=selected.second;E_right_opt=0; +} + +void AnchoredHeuristicProbe::updateOptRightZ(size_t i1,size_t j1,size_t i2,size_t j2,E_type hybrid) +{ + ++rankingCalls; + PredictorMfeEns2dHeuristicSeedExtension::updateOptRightZ(i1,j1,i2,j2,hybrid); +} + +void AnchoredHeuristicProbe::updateZ(size_t i1,size_t j1,size_t i2,size_t j2,Z_type z,bool hybrid) +{ + // Right-selection hooks must not publish partial partitions. The common + // anchor contraction reports each complete boundary with its final factor. + REQUIRE(exactContribution!=nullptr); + REQUIRE(updateZisComplete); + const seeded_oracle::Boundary boundary{i1,i2,j1,j2}; + coefficients[boundary]=*exactContribution; + ++completeCalls[boundary]; + PredictorMfeEns2dHeuristicSeedExtension::updateZ(i1,j1,i2,j2,z,hybrid); +} +} + +TEST_CASE("native anchored heuristics preserve right-choice hooks and complete boundary coefficients", "[BasePairProbabilities][PredictorSeedOracle][SeededPartitionFunction]") { + #include "testEasyLoggingSetup.icc" + using namespace seeded_oracle; + RnaSequence target("target","GGGGGGG"),query("query","CCCCCCC"); + AccessibilityBasePair targetAcc(target,6,nullptr),queryAcc(query,5,nullptr); + ReverseAccessibility reverseQuery(queryAcc); + InteractionEnergyBasePair energy(targetAcc,reverseQuery,2,1); + const SeedConstraint sc(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, + IndexRangeList(),IndexRangeList(),"1|||||&3|||||,2||&5||,4||&3||",false,false,false); + const std::vector declared{ + {{0,0},{1,1},{2,2},{3,3},{4,4}},{{1,1},{2,2}},{{3,3},{4,4}}}; + const std::map choices{{{0,0},{6,6}},{{1,1},{5,5}},{{3,3},{6,6}}}; + auto weight=[&](E_type e){return E_isINF(e)?0.L:static_cast(energy.getBoltzmannWeight(e));}; + for(bool noLP:{false,true}) { + CAPTURE(noLP); + Model model{7,7,6,5,noLP,declared,[](Pair){return true;}, + [&](Pair){return weight(energy.getE_init());}, + [&](Pair p,Pair q){ + if(q.first-p.first-1>2 || q.second-p.second-1>1) return 0.L; + return weight(energy.getE_interLeft(p.first,q.first,p.second,q.second)); + }, + [&](Pair p,Pair q){return weight(energy.getE(p.first,q.first,p.second,q.second,E_type(0)));}, + [&](Pair p,Pair){return choices.at(p);}}; + const auto expected=enumerate(model); + REQUIRE(expected.z>0); + auto hybridModel=model;hybridModel.boundary=[](Pair,Pair){return 1.L;}; + const auto hybrids=enumerate(hybridModel); + const auto oc=makeOutputConstraint(noLP); + std::map> previousCoefficients; + for(bool pairs:{false,true}) { + CAPTURE(pairs); + BasePairProbabilities result(target.size(),query.size()); + OutputHandlerInteractionList output(oc,1); + AnchoredHeuristicProbe predictor(energy,output,nullptr,new SeedHandlerExplicit(energy,sc),pairs?&result:nullptr); + predictor.choices=choices;predictor.predict(); + REQUIRE(predictor.rankingCalls>0); + REQUIRE(predictor.fillCalls.size()==declared.size()); + for(const auto & seed:declared) REQUIRE(predictor.fillCalls.at(seed.front())==1); + REQUIRE(double(predictor.getZall())==Approx(double(expected.z)).epsilon(2e-12)); + REQUIRE(output.getZ()==predictor.getZall()); + REQUIRE(predictor.coefficients.size()==hybrids.boundaries.size()); + for(const auto & [boundary,h]:hybrids.boundaries) { + REQUIRE(predictor.completeCalls.at(boundary)==1); + const auto [actualH,b]=predictor.coefficients.at(boundary); + REQUIRE(double(actualH)==Approx(double(h)).epsilon(2e-12)); + REQUIRE(double(b)==Approx(double(model.boundary({boundary[0],boundary[1]},{boundary[2],boundary[3]}))).epsilon(2e-12)); + } + if(!pairs) { previousCoefficients=predictor.coefficients;continue; } + REQUIRE(predictor.coefficients==previousCoefficients); + result.finalize();REQUIRE(result.isApproximate()); + REQUIRE(result.getZ()==predictor.getZall()); + for(size_t i=0;isecond)).epsilon(2e-12)); + } + } + } +} diff --git a/tests/SeededPartitionFunction_test.cpp b/tests/SeededPartitionFunction_test.cpp index dece4b3b..3c9da136 100644 --- a/tests/SeededPartitionFunction_test.cpp +++ b/tests/SeededPartitionFunction_test.cpp @@ -350,7 +350,57 @@ TEST_CASE("seed extension workspaces reset across changing active rectangles", " } } -TEST_CASE("exact seed batches preserve short-span ensembles across hundreds of sources", "[SeededPartitionFunction]") { +TEST_CASE("heuristic anchors preserve the first owner of the complete declared seed family", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + // The long outer seed completes after the inner seed. Another containing + // seed co-ends with that inner seed, whose shorter length wins ownership. + // The final singleton likewise owns paths containing its two-pair superset. + Model m{8,8,8,8,false,{ + {{0,0},{1,1},{2,2},{3,3},{4,4},{5,5}}, + {{1,1},{2,2},{3,3}},{{2,2},{3,3}},{{4,4},{5,5}},{{5,5}}}, + [](Pair p){return p.first==p.second;},[](Pair){return 2.L;}, + [](Pair p,Pair q){return stack(p,q)?3.L:0.L;}, + [](Pair,Pair){return 5.L;}, + [](Pair start,Pair){return start==Pair{2,2}?Pair{5,5}:Pair{7,7};}}; + for(bool noLP:{false,true}) { + CAPTURE(noLP); + m.noLP=noLP; + const auto oracle=enumerate(m); + // The original family, not the kernel's reduced anchor family, defines + // these expectations. Neither outer nor later seeds may rescue this path. + REQUIRE(oracle.boundaries.count({0,0,7,7})==0); + REQUIRE(oracle.boundaries.at({2,2,7,7})>0); // no-left keeps all right ends + REQUIRE(oracle.boundaries.at({0,0,3,3})>0); // no-right keeps all left ends + REQUIRE(oracle.boundaries.at({0,0,5,5})>0); // both sides use the chosen end + REQUIRE(oracle.boundaries.at({4,4,5,5})>0); // singleton supported on the left + REQUIRE(oracle.boundaries.at({5,5,6,6})>0); // singleton supported on the right + REQUIRE(oracle.boundaries.at({4,4,7,7})>0); // singleton supported on both sides + REQUIRE((oracle.boundaries.count({5,5,5,5})!=0)==!noLP); + compareKernels(m,0,0); + } +} + +TEST_CASE("heuristic anchor choices retain one-sided paths outside the selected domain", "[SeededPartitionFunction]") { + using namespace seeded_oracle; + Model m{7,7,7,7,false,{{{2,2},{3,3}}}, + [](Pair p){return p.first==p.second;},[](Pair){return 2.L;}, + [](Pair p,Pair q){return stack(p,q)?3.L:0.L;},[](Pair,Pair){return 5.L;}}; + // Cover the no-extension fallback, backward/mixed-direction selections, + // an out-of-domain endpoint, and an endpoint outside some source spans. + for(const Pair selected:{Pair{3,3},Pair{0,0},Pair{2,6},Pair{20,20},Pair{5,5}}) + for(size_t span:{size_t(4),size_t(7)}) for(bool noLP:{false,true}) { + CAPTURE(selected.first,selected.second,span,noLP); + m.span1=span;m.span2=span;m.noLP=noLP; + m.selectRight=[selected](Pair,Pair){return selected;}; + const auto oracle=enumerate(m); + REQUIRE(oracle.boundaries.at({0,0,3,3})>0); + REQUIRE(oracle.boundaries.at({2,2,5,5})>0); + REQUIRE(oracle.boundaries.count({1,1,4,4})==0); + compareKernels(m,0,0); + } +} + +TEST_CASE("seed anchor batches preserve short-span ensembles across hundreds of sources", "[SeededPartitionFunction]") { using namespace seeded_oracle; // Short spans keep exhaustive enumeration small while hundreds of eligible // sources force repeated batch publication, including a partial final batch. @@ -365,13 +415,30 @@ TEST_CASE("exact seed batches preserve short-span ensembles across hundreds of s for(size_t k=0;k256); compareKernels(m,1,2); + if(heuristic) { + const auto seeds=domainFor(m); + auto weights=weightsFor(m); + std::map selections; + weights.selectRight=[&](Pair first,Pair last) { + ++selections[first];return m.selectRight(first,last); + }; + const Kernel::Domain d{m.n,m.m,m.span1,m.span2,1,2,m.noLP}; + Kernel::compute(d,seeds,weights,true); + // Batch and outside recomputation must reuse the frozen choices. + REQUIRE(selections.size()==seeds.minimalOccurrences().size()); + for(const auto & [first,count]:selections) REQUIRE(count==1); + } } } From d734e88d9131c6ac7bb6eb87de8a7a5d79282377 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 16 Oct 2026 23:30:36 +0200 Subject: [PATCH 26/30] perf: reuse MFE seed-extension work and physical loop energies --- .../PredictorMfe2dHeuristicSeedExtension.cpp | 54 ++-- src/IntaRNA/PredictorMfe2dSeedExtension.cpp | 134 +++++--- src/IntaRNA/PredictorMfe2dSeedExtension.h | 42 +++ tests/PredictorSeedOracle_test.cpp | 288 +++++++++++++++++- 4 files changed, 456 insertions(+), 62 deletions(-) diff --git a/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp b/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp index eb52cc84..8b952b34 100644 --- a/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp @@ -46,6 +46,9 @@ predict( const IndexRange & r1, const IndexRange & r2 ) throw std::runtime_error("PredictorMfe2dHeuristicSeedExtension::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif + // Cached physical coordinates belong only to this prediction. + resetExtensionCache(); + // setup index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); @@ -69,6 +72,9 @@ predict( const IndexRange & r1, const IndexRange & r2 ) return; } + // Reuse scratch capacity and physical loop energies across seed anchors. + prepareExtensionWorkspace(range_size1, range_size2); + size_t si1 = RnaSequence::lastPos, si2 = RnaSequence::lastPos; while( seedHandler.updateToNextSeed(si1,si2 , 0, range_size1+1-seedHandler.getConstraint().getBasePairs() @@ -91,13 +97,13 @@ predict( const IndexRange & r1, const IndexRange & r2 ) E_right_opt = E_INF; // ER : update for seed + ER - hybridE_right.resize( std::min(range_size1-sj1, maxMatrixLen1), std::min(range_size2-sj2, maxMatrixLen2) ); + hybridE_right.resize( std::min(range_size1-sj1, maxMatrixLen1), std::min(range_size2-sj2, maxMatrixLen2), false ); fillHybridE_right(sj1, sj2, si1, si2); // ensure there is a valid right-extension if (!output.getOutputConstraint().noGUend || (!E_isINF(E_right_opt) || !energy.isGU(sj1,sj2))) { // EL - hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2) ); + hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2), false ); fillHybridE_left(si1, si2); } @@ -128,6 +134,8 @@ fillHybridE_right( const size_t sj1, const size_t sj2 // ensure a stacking to the right of the left boundary (i1,i2) const size_t noLpShift = outConstraint.noLP ? 1 : 0; E_type iStackE = E_type(0); + const size_t maxLoop1 = energy.getMaxInternalLoopSize1(); + const size_t maxLoop2 = energy.getMaxInternalLoopSize2(); // current minimal value E_type curMinE = E_INF; @@ -146,18 +154,18 @@ fillHybridE_right( const size_t sj1, const size_t sj2 // check if complementary if (sj1 sj1; ) { - // ensure maximal loop length - if (j1-noLpShift-k1 > energy.getMaxInternalLoopSize1()+1) break; - for (k2=j2-noLpShift; k2-- > sj2; ) { - // ensure maximal loop length - if (j2-noLpShift-k2 > energy.getMaxInternalLoopSize2()+1) break; + // Bound predecessor scans once per cell while retaining their order. + const size_t k1min = j1-noLpShift-std::min(j1-noLpShift-sj1,maxLoop1+1); + const size_t k2min = j2-noLpShift-std::min(j2-noLpShift-sj2,maxLoop2+1); + for (k1=j1-noLpShift; k1-- > k1min; ) { + for (k2=j2-noLpShift; k2-- > k2min; ) { // check if (k1,k2) are valid left boundary if ( E_isNotINF( hybridE_right(k1-sj1,k2-sj2) ) ) { curMinE = std::min( curMinE, (hybridE_right(k1-sj1,k2-sj2) // left part - + energy.getE_interLeft(k1,j1-noLpShift,k2,j2-noLpShift) // loop + + extensionLoopE(k1,j1-noLpShift,k2,j2-noLpShift) // loop + iStackE) // right stack if no-LP ); } @@ -206,7 +213,7 @@ fillHybridE_left( const size_t si1, const size_t si2 ) #if INTARNA_IN_DEBUG_MODE // check indices - if (!energy.areComplementary(si1,si2) ) + if (!extensionPairIsValid(si1,si2) ) throw std::runtime_error("PredictorMfe2dSeedExtension::fillHybridE_left("+toString(si1)+","+toString(si2)+",..) are not complementary"); #endif @@ -224,6 +231,8 @@ fillHybridE_left( const size_t si1, const size_t si2 ) // ensure a stacking to the right of the left boundary (i1,i2) const size_t noLpShift = outConstraint.noLP ? 1 : 0; E_type iStackE = E_type(0); + const size_t maxLoop1 = energy.getMaxInternalLoopSize1(); + const size_t maxLoop2 = energy.getMaxInternalLoopSize2(); // iterate over all window starts for (size_t l1=0; l1 < hybridE_left.size1(); l1++) { @@ -240,33 +249,32 @@ fillHybridE_left( const size_t si1, const size_t si2 ) // check if complementary if (i1 energy.getMaxInternalLoopSize1()+1) break; - for (k2=i2+noLpShift; k2++ < si2; ) { - // ensure maximal loop length - if (k2-i2-noLpShift > energy.getMaxInternalLoopSize2()+1) break; + // Bound predecessor scans once per cell while retaining their order. + const size_t k1max = i1+noLpShift+std::min(si1-i1-noLpShift,maxLoop1+1); + const size_t k2max = i2+noLpShift+std::min(si2-i2-noLpShift,maxLoop2+1); + for (k1=i1+noLpShift; k1++ < k1max; ) { + for (k2=i2+noLpShift; k2++ < k2max; ) { // check if (k1,k2) are valid left boundary if ( E_isNotINF( hybridE_left(si1-k1,si2-k2) ) ) { curMinE = std::min( curMinE, (iStackE // i stacking if no-LP - + energy.getE_interLeft(i1+noLpShift,k1,i2+noLpShift,k2) // loop + + extensionLoopE(i1+noLpShift,k1,i2+noLpShift,k2) // loop + hybridE_left(si1-k1,si2-k2) ) // right part up to seed ); } diff --git a/src/IntaRNA/PredictorMfe2dSeedExtension.cpp b/src/IntaRNA/PredictorMfe2dSeedExtension.cpp index f0f9699b..c69c9adc 100644 --- a/src/IntaRNA/PredictorMfe2dSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfe2dSeedExtension.cpp @@ -29,6 +29,64 @@ PredictorMfe2dSeedExtension:: ////////////////////////////////////////////////////////////////////////// +E_type +PredictorMfe2dSeedExtension::extensionLoopE(size_t i1, size_t j1, size_t i2, size_t j2) +{ + // Traceback also probes long direct loops, which are outside the cache. + if(extensionLoopEnergy.empty() || j1<=i1 || j2<=i2 + || j1-i1>extensionLoopWidth1 || j2-i2>extensionLoopWidth2) + return energy.getE_interLeft(i1,j1,i2,j2); + const size_t pair=extensionPairIndex(i1,i2); + if(!pair) return energy.getE_interLeft(i1,j1,i2,j2); + const size_t slot=((pair-1)*extensionLoopWidth1+j1-i1-1)*extensionLoopWidth2+j2-i2-1; + if(!extensionLoopKnown[slot]) { + extensionLoopEnergy[slot]=energy.getE_interLeft(i1,j1,i2,j2); + extensionLoopKnown[slot]=1; + } + return extensionLoopEnergy[slot]; +} + +void +PredictorMfe2dSeedExtension::resetExtensionCache() +{ + // Release old cache storage as well as values before offsets can change. + extensionPairIndex=Matrix(); + extensionLoopEnergy=std::vector(); + extensionLoopKnown=std::vector(); + extensionLoopWidth1=extensionLoopWidth2=0; +} + +void +PredictorMfe2dSeedExtension::prepareExtensionWorkspace(size_t n, size_t m) +{ + // Extension matrices grow on demand via nonpreserving resize. Reserving the + // entire range here would waste space for sparse or centrally placed seeds. + + // Bound the combined dense pair index, lazy energies and computed flags. + // Large regions retain direct evaluation rather than growing a global cache. + constexpr size_t cacheBytes=16*1024*1024; + if(!n || !m || n>cacheBytes/sizeof(size_t)/m) return; + const size_t pairBytes=n*m*sizeof(size_t); + extensionPairIndex.resize(n,m,false); + size_t pairs=0; + for(size_t i=0;iavailable/extensionLoopWidth1/extensionLoopWidth2) return; + const size_t slots=pairs*extensionLoopWidth1*extensionLoopWidth2; + extensionLoopEnergy.resize(slots); + extensionLoopKnown.assign(slots,0); +} + +////////////////////////////////////////////////////////////////////////// + void PredictorMfe2dSeedExtension:: predict( const IndexRange & r1, const IndexRange & r2 ) @@ -46,6 +104,9 @@ predict( const IndexRange & r1, const IndexRange & r2 ) throw std::runtime_error("PredictorMfe2d::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif + // Cached physical coordinates belong only to this prediction. + resetExtensionCache(); + // setup index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); @@ -70,6 +131,9 @@ predict( const IndexRange & r1, const IndexRange & r2 ) // initialize mfe interaction for updates initOptima(); + // Reuse scratch capacity and physical loop energies across seed anchors. + prepareExtensionWorkspace(range_size1, range_size2); + size_t si1 = RnaSequence::lastPos, si2 = RnaSequence::lastPos; while( seedHandler.updateToNextSeed(si1,si2 , 0, range_size1+1-seedHandler.getConstraint().getBasePairs() @@ -88,11 +152,11 @@ predict( const IndexRange & r1, const IndexRange & r2 ) const size_t maxMatrixLen2 = energy.getAccessibility2().getMaxLength()-sl2+1; // EL - hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2) ); + hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2), false ); fillHybridE_left(si1, si2); // ER - hybridE_right.resize( std::min(range_size1-sj1, maxMatrixLen1), std::min(range_size2-sj2, maxMatrixLen2) ); + hybridE_right.resize( std::min(range_size1-sj1, maxMatrixLen1), std::min(range_size2-sj2, maxMatrixLen2), false ); fillHybridE_right(sj1, sj2); // update Optimum for all boundary combinations @@ -132,7 +196,7 @@ fillHybridE_left( const size_t si1, const size_t si2 ) const OutputConstraint & outConstraint = output.getOutputConstraint(); #if INTARNA_IN_DEBUG_MODE // check indices - if (!energy.areComplementary(si1,si2) ) + if (!extensionPairIsValid(si1,si2) ) throw std::runtime_error("PredictorMfe2dSeedExtension::fillHybridE_left("+toString(si1)+","+toString(si2)+",..) are not complementary"); #endif @@ -143,6 +207,8 @@ fillHybridE_left( const size_t si1, const size_t si2 ) // ensure a stacking to the right of the left boundary (i1,i2) const size_t noLpShift = outConstraint.noLP ? 1 : 0; E_type iStackE = E_type(0); + const size_t maxLoop1 = energy.getMaxInternalLoopSize1(); + const size_t maxLoop2 = energy.getMaxInternalLoopSize2(); // iterate over all window starts i1 (seq1) and i2 (seq2) // via their distance l1/l2 to the left seed boundary si1/si2 @@ -160,35 +226,34 @@ fillHybridE_left( const size_t si1, const size_t si2 ) // check if complementary if (i1 energy.getMaxInternalLoopSize1()+1) break; - for (k2=i2+noLpShift; k2++ < si2; ) { - // ensure maximal loop length - if (k2-i2-noLpShift > energy.getMaxInternalLoopSize2()+1) break; + // Bound predecessor scans once per cell while retaining their order. + const size_t k1max = i1+noLpShift+std::min(si1-i1-noLpShift,maxLoop1+1); + const size_t k2max = i2+noLpShift+std::min(si2-i2-noLpShift,maxLoop2+1); + for (k1=i1+noLpShift; k1++ < k1max; ) { + for (k2=i2+noLpShift; k2++ < k2max; ) { // check if (k1,k2) are valid left boundary if ( energy.isAccessible1(k1) && energy.isAccessible2(k2) ) if ( E_isNotINF( hybridE_left(si1-k1,si2-k2) ) ) { curE = std::min( curE, (iStackE - + energy.getE_interLeft(i1+noLpShift,k1,i2+noLpShift,k2) + + extensionLoopE(i1+noLpShift,k1,i2+noLpShift,k2) + hybridE_left(si1-k1,si2-k2) ) ); } @@ -210,7 +275,7 @@ fillHybridE_right( const size_t sj1, const size_t sj2 ) const OutputConstraint & outConstraint = output.getOutputConstraint(); #if INTARNA_IN_DEBUG_MODE // check indices - if (!energy.areComplementary(sj1,sj2) ) + if (!extensionPairIsValid(sj1,sj2) ) throw std::runtime_error("PredictorMfe2dSeedExtension::fillHybridE_right("+toString(sj1)+","+toString(sj2)+",..) are not complementary"); #endif @@ -221,6 +286,8 @@ fillHybridE_right( const size_t sj1, const size_t sj2 ) // ensure a stacking to the right of the left boundary (i1,i2) const size_t noLpShift = outConstraint.noLP ? 1 : 0; E_type iStackE = E_type(0); + const size_t maxLoop1 = energy.getMaxInternalLoopSize1(); + const size_t maxLoop2 = energy.getMaxInternalLoopSize2(); // iterate over all window starts j1 (seq1) and j2 (seq2) for (j1=sj1; j1-sj1 < hybridE_right.size1(); j1++) { @@ -236,18 +303,18 @@ fillHybridE_right( const size_t sj1, const size_t sj2 ) // check if complementary if (sj1 sj1; ) { - // ensure maximal loop length - if (j1-noLpShift-k1 > energy.getMaxInternalLoopSize1()+1) break; - for (k2=j2-noLpShift; k2-- > sj2; ) { - // ensure maximal loop length - if (j2-noLpShift-k2 > energy.getMaxInternalLoopSize2()+1) break; + // Bound predecessor scans once per cell while retaining their order. + const size_t k1min = j1-noLpShift-std::min(j1-noLpShift-sj1,maxLoop1+1); + const size_t k2min = j2-noLpShift-std::min(j2-noLpShift-sj2,maxLoop2+1); + for (k1=j1-noLpShift; k1-- > k1min; ) { + for (k2=j2-noLpShift; k2-- > k2min; ) { // check if (k1,k2) are valid left boundary if ( energy.isAccessible1(k1) && energy.isAccessible2(k2) ) if ( E_isNotINF( hybridE_right(k1-sj1,k2-sj2) ) ) { curE = std::min( curE, (hybridE_right(k1-sj1,k2-sj2) - + energy.getE_interLeft(k1,j1-noLpShift,k2,j2-noLpShift) + + extensionLoopE(k1,j1-noLpShift,k2,j2-noLpShift) + iStackE ) ); } @@ -355,9 +421,9 @@ traceBack( Interaction & interaction ) continue; } - hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2) ); + hybridE_left.resize( std::min(si1+1, maxMatrixLen1), std::min(si2+1, maxMatrixLen2), false ); fillHybridE_left( si1, si2 ); - hybridE_right.resize( std::min(j1-sj1+1, maxMatrixLen1), std::min(j2-sj2+1, maxMatrixLen2) ); + hybridE_right.resize( std::min(j1-sj1+1, maxMatrixLen1), std::min(j2-sj2+1, maxMatrixLen2), false ); fillHybridE_right( sj1, sj2 ); if ( E_equal( fullE, @@ -373,18 +439,18 @@ traceBack( Interaction & interaction ) // right-stacking of i if no-LP if (outConstraint.noLP) { // assure stacking is possible - assert(energy.areComplementary(i1+noLpShift,i2+noLpShift)); + assert(extensionPairIsValid(i1+noLpShift,i2+noLpShift)); // check if just stacking loop if ( si1-i1==2 && si2-i2==2 ) { interaction.basePairs.push_back( energy.getBasePair(i1+noLpShift,i2+noLpShift) ); break; } // get stacking energy to avoid recomputation in recursion below - iStackE = energy.getE_interLeft(i1,i1+noLpShift,i2,i2+noLpShift); + iStackE = extensionLoopE(i1,i1+noLpShift,i2,i2+noLpShift); } // check if just internal loop - if ( E_equal( curE, (iStackE + energy.getE_interLeft(i1+noLpShift,si1,i2+noLpShift,si2) + hybridE_left(0,0)) ) ) + if ( E_equal( curE, (iStackE + extensionLoopE(i1+noLpShift,si1,i2+noLpShift,si2) + hybridE_left(0,0)) ) ) { // store noLP base pair if ( outConstraint.noLP ) { @@ -414,7 +480,7 @@ traceBack( Interaction & interaction ) // check if (k1,k2) are valid left boundary if ( E_isNotINF( hybridE_left(si1-k1,si2-k2) ) ) { if ( E_equal( curE, - (iStackE + energy.getE_interLeft(i1+noLpShift,k1,i2+noLpShift,k2) + hybridE_left(si1-k1,si2-k2)) ) ) + (iStackE + extensionLoopE(i1+noLpShift,k1,i2+noLpShift,k2) + hybridE_left(si1-k1,si2-k2)) ) ) { // stop searching traceNotFound = false; @@ -458,18 +524,18 @@ traceBack( Interaction & interaction ) // left-stacking of j if no-LP if (outConstraint.noLP) { // assure stacking is possible - assert (energy.areComplementary(j1-noLpShift,j2-noLpShift)); + assert (extensionPairIsValid(j1-noLpShift,j2-noLpShift)); // handle stacking only if (j1-sj1==2 && j2-sj2==2) { interaction.basePairs.push_back( energy.getBasePair(j1-noLpShift,j2-noLpShift) ); break; } // get stacking energy to avoid recomputation in recursion below - iStackE = energy.getE_interLeft(j1-noLpShift,j1,j2-noLpShift,j2); + iStackE = extensionLoopE(j1-noLpShift,j1,j2-noLpShift,j2); } // check if just internal loop - if ( E_equal( curE, (hybridE_right(0,0) + energy.getE_interLeft(sj1,j1-noLpShift,sj2,j2-noLpShift) + iStackE) ) ) + if ( E_equal( curE, (hybridE_right(0,0) + extensionLoopE(sj1,j1-noLpShift,sj2,j2-noLpShift) + iStackE) ) ) { // store no-LP base pair left of j if (outConstraint.noLP) { @@ -488,7 +554,7 @@ traceBack( Interaction & interaction ) // check if (k1,k2) are valid left boundary if ( E_isNotINF( hybridE_right(k1-sj1,k2-sj2) ) ) { if ( E_equal( curE, - (iStackE + energy.getE_interLeft(k1,j1-noLpShift,k2,j2-noLpShift) + hybridE_right(k1-sj1,k2-sj2)) ) ) + (iStackE + extensionLoopE(k1,j1-noLpShift,k2,j2-noLpShift) + hybridE_right(k1-sj1,k2-sj2)) ) ) { // stop searching traceNotFound = false; diff --git a/src/IntaRNA/PredictorMfe2dSeedExtension.h b/src/IntaRNA/PredictorMfe2dSeedExtension.h index 176e14a1..9d18b30b 100644 --- a/src/IntaRNA/PredictorMfe2dSeedExtension.h +++ b/src/IntaRNA/PredictorMfe2dSeedExtension.h @@ -4,6 +4,7 @@ #include "IntaRNA/PredictorMfe2d.h" #include "IntaRNA/SeedHandlerIdxOffset.h" +#include namespace IntaRNA { @@ -93,6 +94,39 @@ class PredictorMfe2dSeedExtension: public PredictorMfe2d { protected: + //! One-based complementary-pair indices; zero denotes an unavailable pair. + Matrix extensionPairIndex; + //! Seed-independent loop energies and flags; E_INF is a cacheable result. + std::vector extensionLoopEnergy; + std::vector extensionLoopKnown; + //! Indexed positive loop displacements on the two increasing local strands. + size_t extensionLoopWidth1 = 0, extensionLoopWidth2 = 0; + + /** Discard cached values before a prediction changes the local offsets. */ + void resetExtensionCache(); + /** Prepare reusable seed-independent pair/loop + * information for this prediction. Cache storage is limited to 16 MiB; + * domains exceeding that limit retain direct energy evaluation. + * @param n number of active positions on sequence 1 + * @param m number of active positions on reversed sequence 2 + */ + void prepareExtensionWorkspace(size_t n, size_t m); + /** Check pair feasibility in the current prediction's local coordinates. + * @param i1 position in sequence 1 + * @param i2 position in reversed sequence 2 + * @return whether the pair is complementary and accessible + */ + bool extensionPairIsValid(size_t i1, size_t i2) const; + /** Reuse a seed-independent loop energy, with direct evaluation outside + * the bounded cache. Both pairs use increasing local strand coordinates. + * @param i1 left pair's position in sequence 1 + * @param j1 right pair's position in sequence 1 + * @param i2 left pair's position in reversed sequence 2 + * @param j2 right pair's position in reversed sequence 2 + * @return loop energy in hundredths of kcal/mol, or E_INF + */ + E_type extensionLoopE(size_t i1, size_t j1, size_t i2, size_t j2); + /** * Computes all entries of the hybridE matrix for interactions starting in * i1 and i2 and report all valid interactions to updateOptima() @@ -134,6 +168,14 @@ class PredictorMfe2dSeedExtension: public PredictorMfe2d { ////////////////////////////////////////////////////////////////////////// +inline +bool +PredictorMfe2dSeedExtension::extensionPairIsValid(size_t i1, size_t i2) const +{ + return extensionPairIndex.size1() ? extensionPairIndex(i1,i2) != 0 + : energy.areComplementary(i1,i2); +} + inline void PredictorMfe2dSeedExtension:: diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index d35296e1..655be0fe 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -8,6 +8,7 @@ #include "IntaRNA/OutputHandlerInteractionList.h" #include "IntaRNA/PredictorMfe2dSeed.h" #include "IntaRNA/PredictorMfe2dSeedExtension.h" +#include "IntaRNA/PredictorMfe2dHeuristicSeedExtension.h" #include "IntaRNA/PredictorMfeEns2dSeedExtension.h" #include "IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.h" #include "IntaRNA/PredictorMfeEnsSeedOnly.h" @@ -348,12 +349,12 @@ Interaction::PairingVec seedOracleBasePairs(const InteractionEnergy & energy, return basePairs; } -const SeededOracleInteraction * findOptimalTrace( +const SeededOracleInteraction * findSeededTrace( const InteractionEnergy & energy, const SeedOracleResult & oracle, const Interaction & interaction) { for (const SeededOracleInteraction & candidate : oracle.interactions) { - if (candidate.energy == oracle.mfe + if (candidate.energy == interaction.energy && seedOracleBasePairs(energy, candidate.chain) == interaction.basePairs) { @@ -374,12 +375,12 @@ bool matchesSeed(const Interaction::Seed & reported, && E_equal(reported.energy, expected.totalEnergy); } -void requireValidSeededMfe(const InteractionEnergy & energy, +void requireValidSeededInteraction(const InteractionEnergy & energy, const SeedOracleResult & oracle, const Interaction & interaction) { REQUIRE_FALSE(interaction.isEmpty()); - REQUIRE(interaction.energy == oracle.mfe); - const SeededOracleInteraction * trace = findOptimalTrace(energy, oracle, + REQUIRE(interaction.isValid()); + const SeededOracleInteraction * trace = findSeededTrace(energy, oracle, interaction); REQUIRE(trace != nullptr); REQUIRE(interaction.seed != nullptr); @@ -393,6 +394,13 @@ void requireValidSeededMfe(const InteractionEnergy & energy, } } +void requireValidSeededMfe(const InteractionEnergy & energy, + const SeedOracleResult & oracle, const Interaction & interaction) +{ + REQUIRE(interaction.energy == oracle.mfe); + requireValidSeededInteraction(energy, oracle, interaction); +} + void compareExactSeededMfePredictors(const InteractionEnergy & energy, const SeedConstraint & seedConstraint, const OutputConstraint & outputConstraint, const IndexRange & range1, @@ -677,6 +685,276 @@ TEST_CASE("tiny exhaustive seed oracle for exact predictor families", #include "IntaRNA/SeedHandlerNoBulge.h" #include "IntaRNA/OutputHandlerHub.h" +namespace { + +class CountingExtensionEnergy : public InteractionEnergyBasePair { +public: + using InteractionEnergyBasePair::InteractionEnergyBasePair; + mutable size_t loopCalls = 0; + E_type getE_interLeft(size_t i1, size_t j1, size_t i2, size_t j2) const override; +}; + +E_type CountingExtensionEnergy::getE_interLeft(size_t i1, size_t j1, + size_t i2, size_t j2) const +{ + ++loopCalls; + return InteractionEnergyBasePair::getE_interLeft(i1, j1, i2, j2); +} + +class MfeExtensionCacheProbe : public PredictorMfe2dSeedExtension { +public: + using PredictorMfe2dSeedExtension::PredictorMfe2dSeedExtension; + using PredictorMfe2dSeedExtension::extensionLoopE; + void prepare(size_t offset1, size_t offset2, size_t n, size_t m); + bool hasLoopCache() const; + bool hasPairCache() const; +}; + +void MfeExtensionCacheProbe::prepare(size_t offset1, size_t offset2, + size_t n, size_t m) +{ + resetExtensionCache(); + energy.setOffset1(offset1); + energy.setOffset2(offset2); + prepareExtensionWorkspace(n, m); +} + +inline bool MfeExtensionCacheProbe::hasLoopCache() const +{ + return !extensionLoopEnergy.empty(); +} + +inline bool MfeExtensionCacheProbe::hasPairCache() const +{ + return extensionPairIndex.size1() != 0; +} + +template +void checkMfeExtensionWorkspaceReuse(const InteractionEnergy & energy, + const SeedConstraint & seedConstraint, + const OutputConstraint & outputConstraint, const bool exact) +{ + // Change both dimensions and offsets, including a seedless prediction, + // before returning to the original shape. Each call must overwrite every + // active cell even when its backing allocation came from a larger matrix. + const std::vector> ranges{ + {IndexRange(0, 6), IndexRange(0, 6)}, + {IndexRange(1, 5), IndexRange(2, 5)}, + {IndexRange(2, 2), IndexRange(1, 4)}, + {IndexRange(0, 6), IndexRange(1, 6)}, + {IndexRange(0, 6), IndexRange(0, 6)} + }; + SeedRecordingOutput reusedOutput(outputConstraint); + PredictorType reused(energy, reusedOutput, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + for (const auto & [range1, range2] : ranges) { + CAPTURE(range1.from, range1.to, range2.from, range2.to); + const auto oracle = enumerateSeededInteractions(energy, seedConstraint, + outputConstraint, range1, range2); + const size_t previousCalls = reusedOutput.calls.size(); + reused.predict(range1, range2); + REQUIRE(reusedOutput.calls.size() == previousCalls+1); + + SeedRecordingOutput freshOutput(outputConstraint); + PredictorType fresh(energy, freshOutput, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + fresh.predict(range1, range2); + REQUIRE(freshOutput.calls.size() == 1); + const Interaction & actual = reusedOutput.last(); + const Interaction & expected = freshOutput.last(); + REQUIRE(actual.isEmpty() == expected.isEmpty()); + if (oracle.interactions.empty()) { + REQUIRE(actual.isEmpty()); + continue; + } + + // The heuristic's retained right extension is not assumed globally + // optimal. Its reported energy, full traceback and every seed witness + // must nevertheless describe one independently enumerated interaction. + if (exact) requireValidSeededMfe(energy, oracle, actual); + else requireValidSeededInteraction(energy, oracle, actual); + REQUIRE(actual.energy == expected.energy); + REQUIRE(actual.basePairs == expected.basePairs); + REQUIRE(expected.seed != nullptr); + REQUIRE(*actual.seed == *expected.seed); + } + // Equal-energy choices must also survive the intervening range changes. + REQUIRE(reusedOutput.calls.front()->basePairs == reusedOutput.last().basePairs); + REQUIRE(reusedOutput.calls.front()->energy == reusedOutput.last().energy); +} + +} // namespace + +TEST_CASE("MFE extension loop caching preserves infinity offsets and bounded fallback", + "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") +{ + #include "testEasyLoggingSetup.icc" + + const auto seedConstraint = makeSeedConstraint(2, 0, 0, 0, false); + const auto outputConstraint = makeOutputConstraint(false); + SECTION("finite and infinite transitions are cached once per prediction") { + RnaSequence target("target", "GGAGGGG"), query("query", "CCCCCCC"); + AccessibilityDisabled targetAcc(target, 2, nullptr), queryAcc(query, 2, nullptr); + ReverseAccessibility reverseQuery(queryAcc); + CountingExtensionEnergy energy(targetAcc, reverseQuery, 2, 1); + SeedRecordingOutput output(outputConstraint); + MfeExtensionCacheProbe predictor(energy, output, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + predictor.prepare(0, 0, 7, 7); + REQUIRE(predictor.hasLoopCache()); + for (size_t repeat = 0; repeat < 3; ++repeat) { + REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); + REQUIRE(E_isINF(predictor.extensionLoopE(0, 2, 0, 1))); + REQUIRE(predictor.extensionLoopE(0, 3, 0, 1) == Ekcal_2_E(-1)); + } + REQUIRE(energy.loopCalls == 3); + // Traceback may query a direct loop longer than the recurrence bounds. + REQUIRE(E_isINF(predictor.extensionLoopE(0, 4, 0, 1))); + REQUIRE(E_isINF(predictor.extensionLoopE(0, 4, 0, 1))); + REQUIRE(energy.loopCalls == 5); + // The same local key now names a different physical pair, ending in A. + predictor.prepare(1, 0, 6, 7); + REQUIRE(E_isINF(predictor.extensionLoopE(0, 1, 0, 1))); + REQUIRE(E_isINF(predictor.extensionLoopE(0, 1, 0, 1))); + REQUIRE(energy.loopCalls == 6); + predictor.prepare(0, 0, 7, 7); + REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); + REQUIRE(energy.loopCalls == 7); + } + SECTION("large cache domains retain direct energy evaluation") { + RnaSequence target("target", std::string(1500, 'G')); + RnaSequence query("query", std::string(1500, 'C')); + AccessibilityDisabled targetAcc(target, 2, nullptr), queryAcc(query, 2, nullptr); + ReverseAccessibility reverseQuery(queryAcc); + CountingExtensionEnergy energy(targetAcc, reverseQuery, 2, 2); + SeedRecordingOutput output(outputConstraint); + MfeExtensionCacheProbe predictor(energy, output, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + for (size_t length : {600, 1500}) { + CAPTURE(length); + // At 600 the loop table exceeds the cap; at 1500 so does the + // pair map. Neither path needs a full prediction to test fallback. + predictor.prepare(0, 0, length, length); + REQUIRE_FALSE(predictor.hasLoopCache()); + REQUIRE(predictor.hasPairCache() == (length == 600)); + const size_t previousCalls = energy.loopCalls; + REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); + REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); + REQUIRE(energy.loopCalls == previousCalls+2); + } + } +} + +TEST_CASE("MFE seed extensions preserve bounded recurrences and reused workspaces", + "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") +{ + #include "testEasyLoggingSetup.icc" + + RnaSequence target("target", "GUGGCGG"), query("query", "CCGCCUC"); + AccessibilityBasePair targetAcc(target, 6, nullptr), queryAcc(query, 5, nullptr); + ReverseAccessibility reverseQuery(queryAcc); + VrnaHandler vrna; + AccessibilityVrna nativeTarget(target, 6, nullptr, vrna); + AccessibilityVrna nativeQuery(query, 5, nullptr, vrna); + ReverseAccessibility nativeReverse(nativeQuery); + const SeedConstraint seedConstraint = makeSeedConstraint(2, 0, 0, 0, false); + for (const auto & [loop1, loop2] : std::vector>{ + {0, 0}, {0, 2}, {2, 0}, {2, 1}}) + { + InteractionEnergyBasePair simple(targetAcc, reverseQuery, loop1, loop2); + InteractionEnergyVrna nearest(nativeTarget, nativeReverse, vrna, loop1, loop2); + for (bool native : {false, true}) for (bool noLP : {false, true}) + for (bool noGUend : {false, true}) { + CAPTURE(loop1, loop2, native, noLP, noGUend); + const InteractionEnergy & energy = native + ? static_cast(nearest) : simple; + const OutputConstraint outputConstraint(1, + OutputConstraint::OVERLAP_BOTH, E_INF, E_INF, false, + noLP, noGUend, false, true); + checkMfeExtensionWorkspaceReuse( + energy, seedConstraint, outputConstraint, true); + checkMfeExtensionWorkspaceReuse( + energy, seedConstraint, outputConstraint, false); + } + } +} + +TEST_CASE("dense MFE seed extension ties retain optimal chains and all seeds", + "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") +{ + #include "testEasyLoggingSetup.icc" + + RnaSequence target("target", "GGGGGGG"), query("query", "CCCCCCC"); + AccessibilityDisabled targetAcc(target, 6, nullptr), queryAcc(query, 5, nullptr); + ReverseAccessibility reverseQuery(queryAcc); + InteractionEnergyBasePair energy(targetAcc, reverseQuery, 2, 1); + const SeedConstraint seedConstraint = makeSeedConstraint(2, 0, 0, 0, false); + for (bool noLP : {false, true}) { + CAPTURE(noLP); + const auto outputConstraint = makeOutputConstraint(noLP); + const auto oracle = enumerateSeededInteractions(energy, seedConstraint, + outputConstraint, IndexRange(0, 6), IndexRange(0, 6)); + // Five stacked pairs attain the maximum query interaction length. + // Both predictors must find this optimum despite many equal-energy seeds. + REQUIRE(oracle.mfe == Ekcal_2_E(-5)); + SeedRecordingOutput output(outputConstraint); + PredictorMfe2dHeuristicSeedExtension predictor(energy, output, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + predictor.predict(); + REQUIRE(output.calls.size() == 1); + requireValidSeededMfe(energy, oracle, output.last()); + REQUIRE(output.last().seed->size() == 4); + checkMfeExtensionWorkspaceReuse( + energy, seedConstraint, outputConstraint, true); + checkMfeExtensionWorkspaceReuse( + energy, seedConstraint, outputConstraint, true); + } +} + +TEST_CASE("MFE seed extensions include the maximal loop on both sides of a seed", + "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") +{ + #include "testEasyLoggingSetup.icc" + + for (bool gapInTarget : {false, true}) for (bool noLP : {false, true}) + for (size_t gapLimit : {0, 1}) { + CAPTURE(gapInTarget, noLP, gapLimit); + RnaSequence target("target", gapInTarget ? "GGAGGAGG" : "GGGGGG"); + RnaSequence query("query", gapInTarget ? "CCCCCC" : "CCACCACC"); + AccessibilityDisabled targetAcc(target, 0, nullptr), queryAcc(query, 0, nullptr); + ReverseAccessibility reverseQuery(queryAcc); + InteractionEnergyBasePair energy(targetAcc, reverseQuery, + gapInTarget ? gapLimit : 0, gapInTarget ? 0 : gapLimit); + IndexRangeList seedRange1, seedRange2; + seedRange1.push_back(gapInTarget ? IndexRange(3, 4) : IndexRange(2, 3)); + seedRange2.push_back(gapInTarget ? IndexRange(2, 3) : IndexRange(3, 4)); + const SeedConstraint seedConstraint(2, 0, 0, 0, E_INF, + Accessibility::ED_UPPER_BOUND, E_INF, seedRange1, seedRange2, + "", false, false, noLP); + const auto outputConstraint = makeOutputConstraint(noLP); + const IndexRange range1(0, target.size()-1), range2(0, query.size()-1); + const auto oracle = enumerateSeededInteractions(energy, seedConstraint, + outputConstraint, range1, range2); + // Only the central block can be a seed. Extending to the flanking + // blocks therefore requires a maximal-size loop on each side. + REQUIRE(oracle.mfe == Ekcal_2_E((gapLimit == 1 ? -6 : -2))); + SeedRecordingOutput exactOutput(outputConstraint); + PredictorMfe2dSeedExtension exact(energy, exactOutput, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + exact.predict(range1, range2); + REQUIRE(exactOutput.calls.size() == 1); + requireValidSeededMfe(energy, oracle, exactOutput.last()); + REQUIRE(exactOutput.last().seed->size() == 1); + SeedRecordingOutput output(outputConstraint); + PredictorMfe2dHeuristicSeedExtension predictor(energy, output, nullptr, + new SeedHandlerMfe(energy, seedConstraint)); + predictor.predict(range1, range2); + REQUIRE(output.calls.size() == 1); + requireValidSeededMfe(energy, oracle, output.last()); + REQUIRE(output.last().seed->size() == 1); + } +} + namespace { class ExactSeedProbe : public PredictorMfeEns2dSeedExtension { public: From 4994edf7bb754d305c00503f57bbb3d937bfad5b Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Fri, 16 Oct 2026 23:40:22 +0200 Subject: [PATCH 27/30] docs: compare and benchmark MFE seed-extension reuse --- ChangeLog | 17 + doc/Makefile.am | 7 + doc/analysis/mfe-seed-extension-reuse.md | 151 +++++ doc/benchmarks/seed-extension/README.md | 8 + .../seed-extension/mfe-native-manifest.json | 428 ++++++++++++ .../seed-extension/mfe-native-results.json | 608 ++++++++++++++++++ .../seed-extension/mfe-native-samples.tsv | 169 +++++ doc/benchmarks/seed-extension/mfe-native.py | 119 ++++ .../seed-extension/mfe-source-hashes.json | 136 ++++ 9 files changed, 1643 insertions(+) create mode 100644 doc/analysis/mfe-seed-extension-reuse.md create mode 100644 doc/benchmarks/seed-extension/mfe-native-manifest.json create mode 100644 doc/benchmarks/seed-extension/mfe-native-results.json create mode 100644 doc/benchmarks/seed-extension/mfe-native-samples.tsv create mode 100644 doc/benchmarks/seed-extension/mfe-native.py create mode 100644 doc/benchmarks/seed-extension/mfe-source-hashes.json diff --git a/ChangeLog b/ChangeLog index caa5231c..85e05913 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- reuse MFE seed-extension scratch matrices and bounded physical loop energies + across seeds in exact and heuristic prediction; document benchmarks (#274) + - share exact left-most seed-anchor extensions with heuristic prediction, combining each true left extension with one retained right endpoint (#270) @@ -210,6 +213,20 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261016 Alexander Mitrofanov + * PredictorMfe2dSeedExtension, PredictorMfe2dHeuristicSeedExtension + * reuse nonpreserving extension workspace capacity across seeds and traceback + + cache seed-independent pair feasibility and loop energies within a bounded + 16 MiB budget per prediction; invalidate caches before changing offsets + * bound predecessor scans without changing transition/update order (#274) + * tests/PredictorSeedOracle_test.cpp + + exercise repeated exact/heuristic predictions and cache correctness + * doc/analysis/mfe-seed-extension-reuse.md, doc/benchmarks/seed-extension/ + + compare ensemble/MFE reuse strategies and record reproducible native + runtime, memory and byte-exact output comparisons against 21b9d8f + * doc/Makefile.am + + distribute the analysis and benchmark evidence + 261016 Alexander Mitrofanov * SeededPartitionFunction.*, StackSeedDomain.h * Share the exact left-most seed-anchor recurrence with heuristic mode. diff --git a/doc/Makefile.am b/doc/Makefile.am index e7d46e22..0ccf998a 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = \ analysis/seeded-probabilities-validation.md \ analysis/seed-extension-partition.md \ analysis/heuristic-anchored-runtime.md \ + analysis/mfe-seed-extension-reuse.md \ seeded-probabilities-example.cpp \ api-documentation.md \ build-api.sh \ @@ -54,3 +55,9 @@ EXTRA_DIST += benchmarks/seed-extension/README.md \ benchmarks/seed-extension/anchored-heuristic-results.json \ benchmarks/seed-extension/anchored-heuristic-samples.tsv \ benchmarks/seed-extension/anchored-source-hashes.json + +EXTRA_DIST += benchmarks/seed-extension/mfe-native.py \ + benchmarks/seed-extension/mfe-native-results.json \ + benchmarks/seed-extension/mfe-native-samples.tsv \ + benchmarks/seed-extension/mfe-native-manifest.json \ + benchmarks/seed-extension/mfe-source-hashes.json diff --git a/doc/analysis/mfe-seed-extension-reuse.md b/doc/analysis/mfe-seed-extension-reuse.md new file mode 100644 index 00000000..24ea4cc7 --- /dev/null +++ b/doc/analysis/mfe-seed-extension-reuse.md @@ -0,0 +1,151 @@ +# Reusing work in MFE seed extensions + +This implements [issue #274](https://github.com/BackofenLab/IntaRNA/issues/274). +The analysis and measurement baseline is `perf/270-anchored-heuristic` at +`21b9d8fc33aadd3c44d788c4da10a91cf6694ce1`. Both native MFE seed-extension +predictors are selected with `--model=X`: `--mode=M` is exact over the +handler-selected MFE seeds, and `--mode=H` chooses one optimal right extension +per seed for combination with left extensions. As before, neither considers +every possible seed structure at a given start. + +## Comparison with ensemble prediction + +The ensemble predictors (`--model=P`) now share left-most seed anchors and +bounded reusable left/right workspaces. See the +[ensemble recurrence](seed-extension-partition.md) and +[heuristic analysis](heuristic-anchored-runtime.md). +The following distinctions determine which techniques transfer safely. + +| Technique in the ensemble implementation | MFE application | +| --- | --- | +| Reuse workspace; reshape without retaining obsolete cells | Applied to both MFE extension matrices, including traceback. Each recurrence initializes every active cell before reading it. | +| Evaluate a pair domain once and restrict recurrence transitions | Cache complementary-pair information; compute loop predecessor bounds once per cell. Preserve predecessor traversal order. | +| Share reusable work across many seed anchors | Cache physical loop energies by the two endpoint pairs, independent of the current seed. Both left/right fills and traceback share these values. | +| Left-most seed ownership and elimination of redundant containing anchors | Not applied. Ensemble ownership prevents double counting in sums; MFE predictions retain their existing seed enumeration, tie handling, tracker updates and approximate `Zall` accounting. | +| Batch complete boundaries and differentiate partition functions for pair probabilities | Not needed for MFE minimization. It would add state and alter the organization of candidate publication. | + +An extension matrix cell is the optimal path **to or from a particular seed +boundary**. Overlapping seeds have different conditioned subproblems; copying a +matrix cell merely because the seeds overlap is incorrect. The ensemble kernel +also resets its per-anchor tables rather than copying numerical values between +arbitrary overlapping anchors. This implementation reuses their storage and +the underlying loop energies, while recomputing each anchor's MFE recurrence. +The loop cache is an additional application of sharing seed-independent work; +it is not a claim that the ensemble kernel already has the same cache. + +## Implementation and limits + +`PredictorMfe2dSeedExtension` owns scratch preparation and the cache, used by +both exact and heuristic MFE extension prediction. The `resize(..., false)` calls +grow extension matrices on demand, reuse capacity, and avoid copying values that +are immediately overwritten. They do not eagerly reserve the full sequence +rectangle, which would waste space when only sparse or central seeds exist. + +Within one prediction, a physical loop energy depends on its two endpoint +pairs and the fixed energy model, not on the seed being extended. A dense +pair-index map assigns complementary pairs compact indices. Lazy cache slots +are indexed by that pair and positive bounded displacements along both strands. +C++ consumers must rebuild with matching headers and library because the +predictor storage layout changes. A separate known flag permits caching `E_INF` without confusing it with an +uncomputed slot. The native energy model still supplies every new energy. + +The combined pair map, energy slots and flags have an element-storage budget of +16 MiB per predictor. If the complete loop table would exceed this limit, loop +energies are evaluated directly while the bounded pair map remains available. +If even the pair map exceeds it, pair checks also use direct evaluation. +Allocator metadata and the two existing dynamic-programming matrices are +outside this cache budget. This is a time/memory tradeoff, not a memory reduction. +All cached values are discarded before each prediction changes sequence +offsets, including a prediction that subsequently finds no seed. Long direct +loop probes during traceback fall back to the energy model. + +The predecessor ranges are the same inclusive ranges as before; only their +construction moves out of the innermost scans. Energy arithmetic, seed order, +candidate update order, right-end ties, output filtering, traceback and the +existing underestimated MFE partition remain unchanged. Worst-case recurrence +complexity is unchanged. The exact predictor's exhaustive left/right +combination can still dominate runtime, whereas the heuristic spends more of +its time building extension tables. + +## Validation and measurements + +Release and debug `make tests -j2` each passed all seven test drivers. The API +suite passed 119 cases, with 462,526 assertions in release and 462,527 in debug. +Four new focused cases passed 4,691 assertions in each configuration. They +check independently enumerated MFE/valid heuristic traces and seed witnesses, +native and base-pair energy models, equal-energy ties, noLP/noGUend, asymmetric +and maximal loop bounds, shrinking/growing offset ranges, and seedless calls. +Cache probes verify finite/infinite energy reuse, invalidation, direct long-loop +fallback, and both memory-budget fallback paths. Installed public-header +compilation and source-snapshot checks also passed. A separate deterministic +120-case CLI comparison also matched byte-for-byte across M/H, native/base-pair +energies, noLP/noGUend, cropped ranges, bulged seeds and asymmetric loop limits. +Linux/GCC validation was +performed locally; macOS/Apple Clang was not run. + +The measurement setup uses GCC 14.4.0, ViennaRNA 2.7.2, Boost 1.85, C++23 release +builds (`-O3`, `NDEBUG`), and an AMD Ryzen 5 7530U on Linux. No project builds or +tests ran during timing. CPU placement/frequency and other workstation activity +were uncontrolled. These are measurements of the listed inputs on this machine, +not a general speedup guarantee. Seven fresh-process samples per variant were +run in alternating order for each case; values below are median wall seconds. + +| Case | Baseline s | Candidate s | Candidate / baseline | Median RSS KiB, baseline → candidate | +| --- | ---: | ---: | ---: | ---: | +| oxys-M | 0.2282 | 0.2106 | 0.923 | 16,992 → 20,244 | +| oxys-H | 0.0783 | 0.0634 | 0.810 | 16,944 → 20,572 | +| oxys-noacc-M | 0.6930 | 0.6169 | 0.890 | 14,416 → 18,176 | +| oxys-noacc-H | 0.1597 | 0.0835 | 0.523 | 14,652 → 18,176 | +| dense-M | 3.6327 | 2.7054 | 0.745 | 14,520 → 16,256 | +| dense-H | 1.1801 | 0.3434 | 0.291 | 14,644 → 16,384 | +| dense-nolp-M | 2.4051 | 1.7558 | 0.730 | 14,524 → 16,384 | +| dense-nolp-H | 0.9321 | 0.2791 | 0.299 | 14,656 → 16,256 | +| bulged-M | 20.4365 | 18.7458 | 0.917 | 15,872 → 20,096 | +| bulged-H | 3.1374 | 1.2525 | 0.399 | 16,000 → 20,224 | +| no-seeds-M | 0.0089 | 0.0089 | 0.999 | 14,276 → 14,464 | +| no-seeds-H | 0.0088 | 0.0090 | 1.020 | 14,432 → 14,464 | + +All 168 measured predictions were byte-identical to the corresponding baseline, +including energies, boundaries, traceback, seed starts and `Eall`. The ordinary +OxyS/fhlA case improves by about 8% in exact mode and 19% in heuristic mode. +With accessibility disabled, the heuristic case improves by about 48%. Dense +heuristic cases take about 29–30% of baseline time (roughly 3.3–3.4 times faster). +Exact dense cases improve less because all left/right boundary combinations +still need to be published. The bulged exact case likewise remains dominated +by combination work. The table includes the no-seed controls; their small +startup-time differences do not measure the optimized recurrences. + +Peak RSS increases on seeded cases, consistent with the bounded loop cache. +The recorded sample ranges and whole-process memory measurements should be +considered alongside these medians. The implementation does not claim universal +improvement, especially for very sparse seeds or cheap custom energy functions. + +Raw timing samples, full command/binary manifests, summaries, and source/build +verification are retained in the benchmark directory as +[`mfe-native-samples.tsv`](../benchmarks/seed-extension/mfe-native-samples.tsv), +[`mfe-native-manifest.json`](../benchmarks/seed-extension/mfe-native-manifest.json), +[`mfe-native-results.json`](../benchmarks/seed-extension/mfe-native-results.json), and +[`mfe-source-hashes.json`](../benchmarks/seed-extension/mfe-source-hashes.json). + +## Reproduction + +Build the baseline revision above and this revision with the same release +compiler, dependencies and flags. From this checkout run: + +```sh +python3 doc/benchmarks/seed-extension/mfe-native.py \ + --binary /path/to/baseline/src/bin/IntaRNA \ + --candidate /path/to/candidate/src/bin/IntaRNA \ + --output-dir /tmp/mfe-seed-extension --repetitions 7 +``` + +The harness records full commands, sequences, binary hashes, versions and +environment, alternating baseline/candidate order. It compares byte-exact CSV +output including interaction boundaries, energies, `Eall`, hybrid traceback and +seed starts for up to five interactions. Every process uses `--threads=1` and +`OMP_NUM_THREADS=1`. Fresh-process wall time includes accessibility, prediction, +traceback and serialization; GNU time records CPU time and peak process RSS. +The OxyS/fhlA cases use the same full sequences as the ensemble benchmark. +Additional cases exercise disabled accessibility, dense overlapping seeds, +`noLP`, bulged seeds and a no-seed control. Process startup dominates the +no-seed case, which should not be interpreted as an algorithmic speedup. diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md index efe35778..235d47c9 100644 --- a/doc/benchmarks/seed-extension/README.md +++ b/doc/benchmarks/seed-extension/README.md @@ -261,3 +261,11 @@ python3 doc/benchmarks/seed-extension/run.py \ Add the dependency include/runtime options described above when necessary. The recorded manifest contains the exact compiler and dependency paths used for these measurements. + +## MFE extensions (#274) + +[`mfe-native.py`](mfe-native.py) compares native `--model=X --mode=M/H` +executables with byte-exact energy, traceback, seed and `Eall` output. It includes +OxyS/fhlA, dense, noLP, bulged-seed and no-seed cases. See the +[analysis and reproduction instructions](../../analysis/mfe-seed-extension-reuse.md) +and the `mfe-native-*`/`mfe-source-hashes.json` evidence files. diff --git a/doc/benchmarks/seed-extension/mfe-native-manifest.json b/doc/benchmarks/seed-extension/mfe-native-manifest.json new file mode 100644 index 00000000..3b4574b3 --- /dev/null +++ b/doc/benchmarks/seed-extension/mfe-native-manifest.json @@ -0,0 +1,428 @@ +{ + "started_at": "2026-10-16T21:27:16.959097+00:00", + "invocation": [ + "doc/benchmarks/seed-extension/mfe-native.py", + "--binary", + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--candidate", + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--output-dir", + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/native-runtime", + "--repetitions", + "7" + ], + "platform": "Linux-6.8.0-147-generic-x86_64-with-glibc2.39", + "cpu": "AMD Ryzen 5 7530U with Radeon Graphics", + "environment": { + "OMP_NUM_THREADS": "1", + "OPENBLAS_NUM_THREADS": "1", + "LC_ALL": "C" + }, + "timing": "fresh complete process; perf_counter elapsed; GNU time user/system/RSS", + "workstation_activity_and_cpu_frequency": "uncontrolled", + "repetitions": 7, + "warmups": 0, + "binaries": { + "base": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "sha256": "4dac44fd068710e11a11c3e001ab5ae3a22948f41311461b6f5ef4637c8da55b", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + }, + "candidate": { + "path": "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "sha256": "4db07def83829b495ddd5690dbdeeb133aca67398a1a2a0b398c9cef8b48df16", + "version": "IntaRNA 3.4.1\n using Vienna RNA package 2.7.2 and boost 1.85.0" + } + }, + "harness_sha256": "2154cf1541f6b9d4b8937fb9885a763a0e1b708d23eb780ffe0c58f62b54c906", + "commands": { + "base": { + "oxys-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2" + ], + "oxys-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2" + ], + "oxys-noacc-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "oxys-noacc-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "dense-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24" + ], + "dense-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24" + ], + "dense-nolp-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24", + "--outNoLP=true" + ], + "dense-nolp-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24", + "--outNoLP=true" + ], + "bulged-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--seedBP=5", + "--seedMaxUP=2" + ], + "bulged-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--seedBP=5", + "--seedMaxUP=2" + ], + "no-seeds-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "no-seeds-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-270-anchored-validation/release/src/bin/IntaRNA", + "--query=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ] + }, + "candidate": { + "oxys-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2" + ], + "oxys-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2" + ], + "oxys-noacc-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "oxys-noacc-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "dense-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24" + ], + "dense-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24" + ], + "dense-nolp-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24", + "--outNoLP=true" + ], + "dense-nolp-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--qIntLenMax=24", + "--tIntLenMax=24", + "--outNoLP=true" + ], + "bulged-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--seedBP=5", + "--seedMaxUP=2" + ], + "bulged-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=GAAACGGAGCGGCACCUCUUUUAACCCUUGAAGUCACUGCCCGUUUCGAGAGUUUCUCAACUCGAAUAACUAAAGCCAACGUGAACUUUUGCGGAUCUCCAGGAUCCGC", + "--target=AGUUAGUCAAUGACCUUUUGCACCGCUUUGCGGUGCUUUCCUGGAACAACAAAAUGUCAUAUACACCGAUGAGUGAUCUCGGACAACAAGGGUUGUUCGACAUCACUCGGACA", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N", + "--seedBP=5", + "--seedMaxUP=2" + ], + "no-seeds-M": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=M", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ], + "no-seeds-H": [ + "/home/alex/CodexProjects/IntaRNA/issue-274-validation/release/src/bin/IntaRNA", + "--query=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "--target=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "--qId=query", + "--tId=target", + "--model=X", + "--mode=H", + "--threads=1", + "--outMode=C", + "--outNumber=5", + "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + "--acc=N" + ] + } + } +} diff --git a/doc/benchmarks/seed-extension/mfe-native-results.json b/doc/benchmarks/seed-extension/mfe-native-results.json new file mode 100644 index 00000000..5441f189 --- /dev/null +++ b/doc/benchmarks/seed-extension/mfe-native-results.json @@ -0,0 +1,608 @@ +{ + "complete": true, + "output_checks_passed": true, + "samples": 168, + "ended_at": "2026-10-16T21:34:12.126486+00:00", + "summary": [ + { + "case": "oxys-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.22817429999849992, + "min": 0.22598438699787948, + "max": 0.252588850002212 + }, + "user_seconds": { + "median": 0.22, + "min": 0.21, + "max": 0.24 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16992, + "min": 16788, + "max": 17120 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.21060160100023495, + "min": 0.20387787200161256, + "max": 0.2150785600024392 + }, + "user_seconds": { + "median": 0.2, + "min": 0.19, + "max": 0.2 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 20244, + "min": 20060, + "max": 20576 + } + }, + "candidate_over_base_wall_time": 0.9229856342349665 + }, + { + "case": "oxys-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.0783231879977393, + "min": 0.0770573260015226, + "max": 0.08071975899656536 + }, + "user_seconds": { + "median": 0.07, + "min": 0.06, + "max": 0.07 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16944, + "min": 16776, + "max": 17116 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.06341629400412785, + "min": 0.06252601600135677, + "max": 0.06667442999605555 + }, + "user_seconds": { + "median": 0.05, + "min": 0.05, + "max": 0.05 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 20572, + "min": 20116, + "max": 20832 + } + }, + "candidate_over_base_wall_time": 0.8096745756308883 + }, + { + "case": "oxys-noacc-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.6930307310030912, + "min": 0.6711645039977157, + "max": 0.7156457750024856 + }, + "user_seconds": { + "median": 0.68, + "min": 0.66, + "max": 0.7 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14416, + "min": 14032, + "max": 14544 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.6169371109936037, + "min": 0.5964780709982733, + "max": 0.7198363159986911 + }, + "user_seconds": { + "median": 0.6, + "min": 0.59, + "max": 0.71 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 18176, + "min": 17920, + "max": 18304 + } + }, + "candidate_over_base_wall_time": 0.8902016655172711 + }, + { + "case": "oxys-noacc-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.15969202099950053, + "min": 0.15305990299384575, + "max": 0.1611605329962913 + }, + "user_seconds": { + "median": 0.15, + "min": 0.14, + "max": 0.15 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14652, + "min": 14040, + "max": 14792 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.08354390999738825, + "min": 0.08077238599798875, + "max": 0.08493679000093834 + }, + "user_seconds": { + "median": 0.07, + "min": 0.07, + "max": 0.07 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 18176, + "min": 18048, + "max": 18304 + } + }, + "candidate_over_base_wall_time": 0.5231564449776083 + }, + { + "case": "dense-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 3.6327195359990583, + "min": 3.541711801997735, + "max": 3.727804116002517 + }, + "user_seconds": { + "median": 3.61, + "min": 3.53, + "max": 3.72 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14520, + "min": 14396, + "max": 14848 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 2.7053802220034413, + "min": 2.598231374002353, + "max": 2.7622646969975904 + }, + "user_seconds": { + "median": 2.69, + "min": 2.59, + "max": 2.75 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16256, + "min": 16000, + "max": 16384 + } + }, + "candidate_over_base_wall_time": 0.7447258714013045 + }, + { + "case": "dense-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 1.1800887389981654, + "min": 1.1654914179962361, + "max": 1.2308894189991406 + }, + "user_seconds": { + "median": 1.17, + "min": 1.16, + "max": 1.22 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14644, + "min": 14260, + "max": 14848 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.343396803997166, + "min": 0.32344084000214934, + "max": 0.3658891910017701 + }, + "user_seconds": { + "median": 0.33, + "min": 0.31, + "max": 0.35 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16384, + "min": 16128, + "max": 16384 + } + }, + "candidate_over_base_wall_time": 0.290992357310936 + }, + { + "case": "dense-nolp-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 2.4051032779971138, + "min": 2.335149795995676, + "max": 2.4875883429995156 + }, + "user_seconds": { + "median": 2.39, + "min": 2.32, + "max": 2.48 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14524, + "min": 14288, + "max": 14720 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 1.7558094339983654, + "min": 1.6993050569944899, + "max": 1.7743743329992867 + }, + "user_seconds": { + "median": 1.74, + "min": 1.69, + "max": 1.76 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16384, + "min": 16000, + "max": 16384 + } + }, + "candidate_over_base_wall_time": 0.7300349428073386 + }, + { + "case": "dense-nolp-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.9321481669976492, + "min": 0.8969694949992117, + "max": 0.9356616340010078 + }, + "user_seconds": { + "median": 0.92, + "min": 0.89, + "max": 0.93 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14656, + "min": 14520, + "max": 14848 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.2790719119948335, + "min": 0.2682166619997588, + "max": 0.28544693699950585 + }, + "user_seconds": { + "median": 0.27, + "min": 0.25, + "max": 0.28 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16256, + "min": 15872, + "max": 16384 + } + }, + "candidate_over_base_wall_time": 0.299385786375244 + }, + { + "case": "bulged-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 20.436534836997453, + "min": 20.100940208001703, + "max": 21.027037519997975 + }, + "user_seconds": { + "median": 20.42, + "min": 20.08, + "max": 21.0 + }, + "system_seconds": { + "median": 0.01, + "min": 0.0, + "max": 0.01 + }, + "max_rss_kib": { + "median": 15872, + "min": 15744, + "max": 16128 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 18.745807493003667, + "min": 18.53029503900325, + "max": 19.909435503002896 + }, + "user_seconds": { + "median": 18.72, + "min": 18.5, + "max": 19.89 + }, + "system_seconds": { + "median": 0.01, + "min": 0.0, + "max": 0.02 + }, + "max_rss_kib": { + "median": 20096, + "min": 19840, + "max": 20352 + } + }, + "candidate_over_base_wall_time": 0.9172693728423587 + }, + { + "case": "bulged-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 3.137401313993905, + "min": 2.9955135850032093, + "max": 3.246840963998693 + }, + "user_seconds": { + "median": 3.12, + "min": 2.98, + "max": 3.23 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 16000, + "min": 15872, + "max": 16128 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 1.2525030039978446, + "min": 1.2405925029961509, + "max": 1.3304343269992387 + }, + "user_seconds": { + "median": 1.24, + "min": 1.22, + "max": 1.31 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 20224, + "min": 20096, + "max": 20480 + } + }, + "candidate_over_base_wall_time": 0.3992167015457168 + }, + { + "case": "no-seeds-M", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.008868766999512445, + "min": 0.008531804996891879, + "max": 0.00978454299911391 + }, + "user_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14276, + "min": 14128, + "max": 14592 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.00886398799775634, + "min": 0.008400880004046485, + "max": 0.009290887996030506 + }, + "user_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14464, + "min": 14284, + "max": 14592 + } + }, + "candidate_over_base_wall_time": 0.9994611424839137 + }, + { + "case": "no-seeds-H", + "base": { + "samples": 7, + "elapsed_seconds": { + "median": 0.008815576999040786, + "min": 0.008339455001987517, + "max": 0.009695836000901181 + }, + "user_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14432, + "min": 14176, + "max": 14720 + } + }, + "candidate": { + "samples": 7, + "elapsed_seconds": { + "median": 0.008991034999780823, + "min": 0.008685433997015934, + "max": 0.009464904003834818 + }, + "user_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "system_seconds": { + "median": 0.0, + "min": 0.0, + "max": 0.0 + }, + "max_rss_kib": { + "median": 14464, + "min": 14324, + "max": 14592 + } + }, + "candidate_over_base_wall_time": 1.0199031782898758 + } + ] +} diff --git a/doc/benchmarks/seed-extension/mfe-native-samples.tsv b/doc/benchmarks/seed-extension/mfe-native-samples.tsv new file mode 100644 index 00000000..d687c9ab --- /dev/null +++ b/doc/benchmarks/seed-extension/mfe-native-samples.tsv @@ -0,0 +1,169 @@ +variant case repetition elapsed_seconds user_seconds system_seconds max_rss_kib stdout_sha256 stdout_equal +base oxys-M 1 0.22598438699787948 0.21 0.0 16876 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 1 0.20725019100063946 0.19 0.0 20244 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 2 0.20387787200161256 0.19 0.0 20372 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 2 0.22815612699923804 0.22 0.0 16912 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 3 0.2322664889943553 0.22 0.0 17100 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 3 0.21482347300479887 0.2 0.01 20372 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 4 0.21060160100023495 0.19 0.0 20060 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 4 0.22808215799886966 0.21 0.0 16992 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 5 0.22817429999849992 0.22 0.0 17116 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 5 0.21207566400698852 0.2 0.0 20576 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 6 0.20958351300214417 0.2 0.0 20244 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 6 0.23690201499994146 0.23 0.0 17120 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-M 7 0.252588850002212 0.24 0.0 16788 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-M 7 0.2150785600024392 0.2 0.0 20244 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 1 0.0789738169987686 0.07 0.0 17116 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 1 0.06530389100225875 0.05 0.0 20580 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 2 0.06667442999605555 0.05 0.0 20116 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 2 0.08022125400020741 0.07 0.0 16944 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 3 0.0770573260015226 0.06 0.0 17076 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 3 0.06252601600135677 0.05 0.0 20572 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 4 0.06328389599366346 0.05 0.0 20356 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 4 0.07754475899855606 0.07 0.0 16988 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 5 0.07738689300458645 0.07 0.0 16916 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 5 0.06341629400412785 0.05 0.0 20700 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 6 0.06332346099952701 0.05 0.0 20832 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 6 0.0783231879977393 0.07 0.0 16788 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-H 7 0.08071975899656536 0.07 0.0 16776 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +candidate oxys-H 7 0.06368671100062784 0.05 0.0 20244 b8a9cda08304484e865f0a11f0dcd10aff72c22e47b903a1403511662d5c6495 True +base oxys-noacc-M 1 0.6766480329970364 0.67 0.0 14416 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 1 0.6151194659978501 0.6 0.0 18048 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 2 0.6211269929990522 0.61 0.0 18304 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 2 0.6930307310030912 0.68 0.0 14544 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 3 0.7038532110018423 0.69 0.0 14416 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 3 0.6169371109936037 0.61 0.0 18176 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 4 0.6184007729971199 0.6 0.0 17920 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 4 0.6753768080016016 0.67 0.0 14032 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 5 0.6711645039977157 0.66 0.0 14196 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 5 0.5964780709982733 0.59 0.0 18176 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 6 0.6017645979954978 0.59 0.0 18176 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 6 0.7068893559990101 0.7 0.0 14380 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-M 7 0.7156457750024856 0.7 0.0 14416 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +candidate oxys-noacc-M 7 0.7198363159986911 0.71 0.0 18304 8101e75923553d0293d29f349297083c5018529e281a9b2759a110987f2aea51 True +base oxys-noacc-H 1 0.1602084490004927 0.15 0.0 14672 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 1 0.08382219199847896 0.07 0.0 18176 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 2 0.08482118399842875 0.07 0.0 18048 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 2 0.1611605329962913 0.15 0.0 14672 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 3 0.15969202099950053 0.15 0.0 14792 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 3 0.08323501099948771 0.07 0.0 18176 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 4 0.08077238599798875 0.07 0.0 18176 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 4 0.15957796799921198 0.15 0.0 14548 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 5 0.1554835849965457 0.14 0.0 14652 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 5 0.08106468400364975 0.07 0.0 18304 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 6 0.08354390999738825 0.07 0.0 18304 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 6 0.15305990299384575 0.14 0.0 14040 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base oxys-noacc-H 7 0.16029668400005903 0.15 0.0 14380 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +candidate oxys-noacc-H 7 0.08493679000093834 0.07 0.0 18304 29f890919543c31a9dfc8ea4094cc04373251f9b550a373630f582e1c9a5c8fe True +base dense-M 1 3.6327195359990583 3.61 0.0 14520 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 1 2.7053802220034413 2.69 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 2 2.598231374002353 2.59 0.0 16000 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 2 3.575308015999326 3.56 0.0 14848 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 3 3.693429796003329 3.68 0.0 14396 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 3 2.7302272509987233 2.72 0.0 16128 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 4 2.7622646969975904 2.75 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 4 3.604954287002329 3.59 0.0 14400 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 5 3.6378560879966244 3.62 0.0 14520 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 5 2.630451375996927 2.62 0.0 16256 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 6 2.623274773002777 2.61 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 6 3.541711801997735 3.53 0.0 14720 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-M 7 3.727804116002517 3.72 0.0 14520 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-M 7 2.7249852280001505 2.71 0.0 16000 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 1 1.1711650179931894 1.16 0.0 14544 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 1 0.3658891910017701 0.35 0.0 16128 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 2 0.34761808500479674 0.34 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 2 1.2308894189991406 1.22 0.0 14592 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 3 1.2281713940028567 1.22 0.0 14848 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 3 0.32344084000214934 0.31 0.0 16256 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 4 0.343396803997166 0.33 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 4 1.2221546780056087 1.21 0.0 14260 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 5 1.168427400996734 1.16 0.0 14848 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 5 0.32840239699726226 0.32 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 6 0.3448373330029426 0.33 0.0 16128 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 6 1.1654914179962361 1.16 0.0 14648 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-H 7 1.1800887389981654 1.17 0.0 14644 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-H 7 0.33886814700235846 0.33 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 1 2.4051032779971138 2.39 0.0 14548 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 1 1.7444648710006732 1.73 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 2 1.7743743329992867 1.76 0.0 16256 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 2 2.3618700449951575 2.35 0.0 14652 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 3 2.4875883429995156 2.48 0.0 14524 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 3 1.7682978880038718 1.75 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 4 1.7558094339983654 1.74 0.0 16000 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 4 2.480539543001214 2.47 0.0 14720 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 5 2.335149795995676 2.32 0.0 14420 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 5 1.6993050569944899 1.69 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 6 1.7149285490013426 1.7 0.0 16128 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 6 2.3533414129997254 2.34 0.0 14288 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-M 7 2.4587285719971987 2.45 0.0 14288 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-M 7 1.7677196110016666 1.75 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 1 0.8969694949992117 0.89 0.0 14672 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 1 0.2682166619997588 0.26 0.0 16256 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 2 0.2790719119948335 0.27 0.0 16128 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 2 0.9356616340010078 0.93 0.0 14656 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 3 0.932304933994601 0.92 0.0 14540 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 3 0.2829632639986812 0.27 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 4 0.28544693699950585 0.28 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 4 0.9316706840036204 0.92 0.0 14520 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 5 0.9338388760006637 0.92 0.0 14848 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 5 0.2840632040024502 0.27 0.0 15872 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 6 0.27883467699575704 0.27 0.0 16384 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 6 0.8995752819973859 0.89 0.0 14520 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base dense-nolp-H 7 0.9321481669976492 0.92 0.0 14668 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +candidate dense-nolp-H 7 0.26881684500403935 0.25 0.0 16000 aa5e88d04e83657b2be72ef014b33cb0f6a25120ca82985dfea922b069dba61b True +base bulged-M 1 20.436534836997453 20.42 0.0 15872 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 1 18.53029503900325 18.5 0.0 19840 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 2 18.998647555999923 18.95 0.02 20096 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 2 20.507915898997453 20.48 0.01 15744 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 3 20.494875176002097 20.46 0.01 16000 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 3 18.745807493003667 18.72 0.01 20096 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 4 18.685262033999607 18.67 0.01 20096 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 4 20.100940208001703 20.08 0.0 15872 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 5 20.383257036999566 20.35 0.01 16128 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 5 19.768755132994556 19.75 0.0 20352 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 6 19.909435503002896 19.89 0.0 20352 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 6 20.22209912299877 20.2 0.0 15872 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-M 7 21.027037519997975 21.0 0.01 15872 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +candidate bulged-M 7 18.58526384599827 18.56 0.01 20352 7f692c257d0a95baaaa9e47ad559403e603e59de880e9845294e25f420234235 True +base bulged-H 1 2.9955135850032093 2.98 0.0 15872 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 1 1.2525030039978446 1.24 0.0 20096 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 2 1.3304343269992387 1.31 0.0 20224 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 2 3.15794147500128 3.14 0.0 16128 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 3 3.2408405609967303 3.22 0.0 16000 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 3 1.2405925029961509 1.22 0.0 20352 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 4 1.249580372001219 1.23 0.0 20352 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 4 3.1268223670049338 3.11 0.0 15872 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 5 3.137401313993905 3.12 0.0 16000 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 5 1.3267657429969404 1.31 0.0 20224 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 6 1.2479881339968415 1.23 0.0 20480 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 6 3.246840963998693 3.23 0.0 16128 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base bulged-H 7 3.131314537997241 3.12 0.0 16000 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +candidate bulged-H 7 1.2600363640012802 1.24 0.0 20224 4cb0b5337ca4ae7f5e55123f6e381f286108f5ed01db71ed40b5db0def0cdc91 True +base no-seeds-M 1 0.009707708995847497 0.0 0.0 14132 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 1 0.009022495003591757 0.0 0.0 14284 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 2 0.008400880004046485 0.0 0.0 14464 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 2 0.00978454299911391 0.0 0.0 14276 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 3 0.009072268003365025 0.0 0.0 14128 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 3 0.00895364600000903 0.0 0.0 14336 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 4 0.008688709000125527 0.0 0.0 14592 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 4 0.008868766999512445 0.0 0.0 14592 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 5 0.008531804996891879 0.0 0.0 14464 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 5 0.009290887996030506 0.0 0.0 14592 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 6 0.00886398799775634 0.0 0.0 14452 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 6 0.008773178000410553 0.0 0.0 14264 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-M 7 0.008644417001050897 0.0 0.0 14300 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-M 7 0.008445913998002652 0.0 0.0 14464 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 1 0.009695836000901181 0.0 0.0 14320 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 1 0.00902825500088511 0.0 0.0 14552 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 2 0.008736267998756375 0.0 0.0 14560 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 2 0.009530686998914462 0.0 0.0 14592 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 3 0.00875467299920274 0.0 0.0 14720 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 3 0.009464904003834818 0.0 0.0 14464 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 4 0.009450587000173982 0.0 0.0 14324 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 4 0.008815576999040786 0.0 0.0 14176 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 5 0.008339455001987517 0.0 0.0 14424 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 5 0.008685433997015934 0.0 0.0 14412 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 6 0.008991034999780823 0.0 0.0 14592 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 6 0.009079811999981757 0.0 0.0 14432 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +base no-seeds-H 7 0.008487442006298807 0.0 0.0 14456 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True +candidate no-seeds-H 7 0.008804637000139337 0.0 0.0 14420 9a7d41f91dac04e7e2656ce6bbab1a47474391c367c06cfb042de0ff3e9101a0 True diff --git a/doc/benchmarks/seed-extension/mfe-native.py b/doc/benchmarks/seed-extension/mfe-native.py new file mode 100644 index 00000000..8af962a7 --- /dev/null +++ b/doc/benchmarks/seed-extension/mfe-native.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +"""Compare MFE seed extensions with identical commands and byte-exact output.""" + +import argparse +import csv +import importlib.util +import os +from pathlib import Path +import platform +import subprocess +import sys +import time + +# Share the recorded biological sequences and summary format with the ensemble +# benchmark; this runner always selects model X and includes traceback and Eall. +spec = importlib.util.spec_from_file_location( + "native", Path(__file__).with_name("anchored-native.py")) +native = importlib.util.module_from_spec(spec) +spec.loader.exec_module(native) + +WORKLOADS = { + "oxys": (native.QUERY, native.TARGET, []), + "oxys-noacc": (native.QUERY, native.TARGET, ["--acc=N"]), + "dense": ("G" * 48, "C" * 48, + ["--acc=N", "--qIntLenMax=24", "--tIntLenMax=24"]), + "dense-nolp": ("G" * 48, "C" * 48, + ["--acc=N", "--qIntLenMax=24", "--tIntLenMax=24", "--outNoLP=true"]), + "bulged": (native.QUERY, native.TARGET, + ["--acc=N", "--seedBP=5", "--seedMaxUP=2"]), + "no-seeds": ("A" * 109, "C" * 113, ["--acc=N"]), +} +CASES = tuple(f"{name}-{mode}" for name in WORKLOADS for mode in ("M", "H")) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--binary", type=Path, required=True) + parser.add_argument("--candidate", type=Path, required=True) + parser.add_argument("--output-dir", type=Path, required=True) + parser.add_argument("--repetitions", type=int, default=7) + parser.add_argument("--cases", nargs="+", choices=CASES, default=CASES) + args = parser.parse_args() + if args.repetitions < 1 or len(set(args.cases)) != len(args.cases): + parser.error("repetitions must be positive and cases unique") + output = args.output_dir.resolve() + if output.exists() and any(output.iterdir()): + parser.error("output directory must be new or empty") + output.mkdir(parents=True, exist_ok=True) + env = dict(os.environ, OMP_NUM_THREADS="1", OPENBLAS_NUM_THREADS="1", LC_ALL="C") + binaries = {"base": args.binary.resolve(), "candidate": args.candidate.resolve()} + manifest = { + "started_at": native.stamp(), "invocation": sys.argv, + "platform": platform.platform(), "cpu": next( + (line.split(":", 1)[1].strip() for line in Path("/proc/cpuinfo").read_text().splitlines() + if line.startswith("model name")), None), + "environment": {key: env[key] for key in + ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "LC_ALL")}, + "timing": "fresh complete process; perf_counter elapsed; GNU time user/system/RSS", + "workstation_activity_and_cpu_frequency": "uncontrolled", + "repetitions": args.repetitions, "warmups": 0, + "binaries": {name: {"path": str(path), "sha256": native.digest(path), + "version": subprocess.check_output( + [str(path), "--version"], text=True, env=env).strip()} + for name, path in binaries.items()}, + "harness_sha256": native.digest(Path(__file__)), "commands": {}, + } + for variant, binary in binaries.items(): + manifest["commands"][variant] = {} + for case in args.cases: + workload, mode = case.rsplit("-", 1) + query, target, extra = WORKLOADS[workload] + manifest["commands"][variant][case] = [ + str(binary), f"--query={query}", f"--target={target}", + "--qId=query", "--tId=target", "--model=X", f"--mode={mode}", + "--threads=1", "--outMode=C", "--outNumber=5", "--outOverlap=B", + "--outCsvCols=start1,end1,start2,end2,E,Eall,hybridDP,seedStart1,seedStart2", + *extra, + ] + native.save(output / "manifest.json", manifest) + samples, reference = [], {} + fields = ("variant", "case", "repetition", "elapsed_seconds", "user_seconds", + "system_seconds", "max_rss_kib", "stdout_sha256", "stdout_equal") + with (output / "samples.tsv").open("w", newline="") as stream: + writer = csv.DictWriter(stream, fieldnames=fields, delimiter="\t", lineterminator="\n") + writer.writeheader() + for case in args.cases: + for repetition in range(1, args.repetitions + 1): + variants = ("base", "candidate") if repetition % 2 else ("candidate", "base") + for variant in variants: + prefix = output / f"{variant}-{case}-{repetition}" + print(prefix.name, flush=True) + timing = prefix.with_suffix(".time.tsv") + with prefix.with_suffix(".stdout").open("wb") as stdout, \ + prefix.with_suffix(".stderr").open("wb") as stderr: + start = time.perf_counter() + subprocess.run(["/usr/bin/time", "-f", "%U\t%S\t%M", "-o", str(timing), + *manifest["commands"][variant][case]], + env=env, stdout=stdout, stderr=stderr, check=True) + elapsed = time.perf_counter() - start + user, system, rss = timing.read_text().strip().split("\t") + digest = native.digest(prefix.with_suffix(".stdout")) + reference.setdefault(case, digest) + row = dict(variant=variant, case=case, repetition=repetition, + elapsed_seconds=elapsed, user_seconds=float(user), + system_seconds=float(system), max_rss_kib=int(rss), + stdout_sha256=digest, stdout_equal=digest == reference[case]) + writer.writerow(row) + stream.flush() + samples.append(row) + if not row["stdout_equal"]: + raise RuntimeError(f"output differs: {prefix}; inspect retained raw files") + native.save(output / "results.json", { + "complete": True, "output_checks_passed": True, "samples": len(samples), + "ended_at": native.stamp(), "summary": native.summarize(samples), + }) + + +if __name__ == "__main__": + main() diff --git a/doc/benchmarks/seed-extension/mfe-source-hashes.json b/doc/benchmarks/seed-extension/mfe-source-hashes.json new file mode 100644 index 00000000..bf6819db --- /dev/null +++ b/doc/benchmarks/seed-extension/mfe-source-hashes.json @@ -0,0 +1,136 @@ +{ + "baseline_revision": "21b9d8fc33aadd3c44d788c4da10a91cf6694ce1", + "source_verification": { + "base": { + "checked_files": 190, + "mismatches": [] + }, + "release": { + "checked_files": 190, + "mismatches": [] + }, + "debug": { + "checked_files": 190, + "mismatches": [] + } + }, + "modified_source_sha256": { + "src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp": "40dd8809ab97b3f0aed2ed691d269bdb3a6e8138a90dcf3bc6b300afa68e3d3e", + "src/IntaRNA/PredictorMfe2dSeedExtension.cpp": "1b05d1368f4c1d1c7cc6e47c222f6acd7747eacf25882933fbfffeafcebe4dd3", + "src/IntaRNA/PredictorMfe2dSeedExtension.h": "08621ecb0b1a0516f6ec96b2b7132c34a9335a83480bdf6995828c03ac76194a", + "tests/PredictorSeedOracle_test.cpp": "b7275381b803246be41e3bbb277e612dc3ee568bf89034e74494c928e3a74c14" + }, + "build_flags": { + "base": { + "Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib" + ], + "src/IntaRNA/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "libIntaRNA_a_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ], + "src/bin/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "IntaRNA_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ] + }, + "release": { + "Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib" + ], + "src/IntaRNA/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "libIntaRNA_a_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ], + "src/bin/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -O3 -fno-strict-aliasing -Wno-uninitialized -Wno-deprecated -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "IntaRNA_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ] + }, + "debug": { + "Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -g -O0 -Wno-uninitialized -Wno-deprecated -DELPP_DEBUG_ERRORS -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib" + ], + "src/IntaRNA/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -g -O0 -Wno-uninitialized -Wno-deprecated -DELPP_DEBUG_ERRORS -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "libIntaRNA_a_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ], + "src/bin/Makefile": [ + "AM_CXXFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -g -O0 -Wno-uninitialized -Wno-deprecated -DELPP_DEBUG_ERRORS -fopenmp -DELPP_FEATURE_PERFORMANCE_TRACKING -DELPP_NO_DEFAULT_LOG_FILE -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA -pthread ", + "CPPFLAGS = -I/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -I/include", + "CXX = /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain-gcc14/bin/x86_64-conda-linux-gnu-g++ -std=c++23", + "CXXFLAGS = ", + "DEFS = -DHAVE_CONFIG_H", + "LDFLAGS = -L/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -Wl,-rpath,/home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/lib -L/lib", + "IntaRNA_CXXFLAGS = $(AM_CXXFLAGS) -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include -isystem /home/alex/CodexProjects/IntaRNA/issue-246-validation/toolchain/include/ViennaRNA" + ] + } + }, + "binaries": { + "base": { + "sha256": "4dac44fd068710e11a11c3e001ab5ae3a22948f41311461b6f5ef4637c8da55b" + }, + "release": { + "sha256": "4db07def83829b495ddd5690dbdeeb133aca67398a1a2a0b398c9cef8b48df16" + }, + "debug": { + "sha256": "f69a3a3c6bb8a8a850b5bdfee5597c19f99f14f9812781b97f31a3670e85c854" + } + }, + "compiler": "x86_64-conda-linux-gnu-g++ (conda-forge gcc 14.4.0-7) 14.4.0", + "candidate_source_commit": "d734e88d9131c6ac7bb6eb87de8a7a5d79282377", + "configuration_header_sha256": { + "base": { + "src/config.h": "94a62d9fffc397911039ed947d07b9bfc44412ba9b449bff57e66577974efdf4", + "src/IntaRNA/intarna_config.h": "4cbc306a33335dd49061ed4e3d70f7f70e776f5aa8a6e0e73ea6715cfa4163c2" + }, + "release": { + "src/config.h": "94a62d9fffc397911039ed947d07b9bfc44412ba9b449bff57e66577974efdf4", + "src/IntaRNA/intarna_config.h": "4cbc306a33335dd49061ed4e3d70f7f70e776f5aa8a6e0e73ea6715cfa4163c2" + }, + "debug": { + "src/config.h": "e31a3eee0944882cf8f60f545ade876a528345c4237af730424242cc0b51682a", + "src/IntaRNA/intarna_config.h": "4cbc306a33335dd49061ed4e3d70f7f70e776f5aa8a6e0e73ea6715cfa4163c2" + } + } +} From 2e308d011e4ab30a467529dd699c5b1e3eedafba Mon Sep 17 00:00:00 2001 From: Martin Raden Date: Mon, 19 Oct 2026 16:14:52 +0200 Subject: [PATCH 28/30] remove refactoring doc files Removed several files from EXTRA_DIST in Makefile. --- doc/Makefile.am | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index e7d46e22..cb78b081 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -5,12 +5,6 @@ EXTRA_DIST = \ conda.txt \ - analysis/base-pair-probabilities-plan.md \ - analysis/seeded-kernel-comparison.md \ - analysis/seeded-probabilities-validation.md \ - analysis/seed-extension-partition.md \ - analysis/heuristic-anchored-runtime.md \ - seeded-probabilities-example.cpp \ api-documentation.md \ build-api.sh \ check-api.py \ @@ -26,31 +20,3 @@ EXTRA_DIST = \ latex-deps/tocloft.sty \ latex-deps/trimclip.sty \ latex-deps/xtab.sty - -EXTRA_DIST += benchmarks/seed-extension/README.md \ - benchmarks/seed-extension/build.py \ - benchmarks/seed-extension/run.py \ - benchmarks/seed-extension/driver.cpp \ - benchmarks/seed-extension/recorded-results.json \ - benchmarks/seed-extension/recorded-samples.tsv \ - benchmarks/seed-extension/recorded-source-hashes.json \ - benchmarks/seed-extension/reuse-exact-results.json \ - benchmarks/seed-extension/reuse-heuristic-results.json \ - benchmarks/seed-extension/reuse-exact-samples.tsv \ - benchmarks/seed-extension/reuse-heuristic-samples.tsv \ - benchmarks/seed-extension/reuse-source-hashes.json \ - benchmarks/seed-extension/leftmost-exact-results.json \ - benchmarks/seed-extension/leftmost-heuristic-results.json \ - benchmarks/seed-extension/leftmost-exact-samples.tsv \ - benchmarks/seed-extension/leftmost-heuristic-samples.tsv \ - benchmarks/seed-extension/leftmost-source-hashes.json \ - benchmarks/seed-extension/anchored-native.py \ - benchmarks/seed-extension/anchored-native-results.json \ - benchmarks/seed-extension/anchored-native-samples.tsv \ - benchmarks/seed-extension/anchored-pairs-results.json \ - benchmarks/seed-extension/anchored-pairs-samples.tsv \ - benchmarks/seed-extension/anchored-exact-results.json \ - benchmarks/seed-extension/anchored-exact-samples.tsv \ - benchmarks/seed-extension/anchored-heuristic-results.json \ - benchmarks/seed-extension/anchored-heuristic-samples.tsv \ - benchmarks/seed-extension/anchored-source-hashes.json From 56aeff7741eab82f93b0629fc1b0a3f7c19b5892 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Tue, 20 Oct 2026 01:57:56 +0200 Subject: [PATCH 29/30] Remove local MFE energy caches per PR #276 review --- ChangeLog | 20 +++- doc/analysis/mfe-seed-extension-reuse.md | 78 +++++++++----- doc/benchmarks/seed-extension/README.md | 5 +- .../PredictorMfe2dHeuristicSeedExtension.cpp | 24 ++--- src/IntaRNA/PredictorMfe2dSeedExtension.cpp | 100 ++++------------- src/IntaRNA/PredictorMfe2dSeedExtension.h | 42 -------- tests/PredictorSeedOracle_test.cpp | 102 ------------------ 7 files changed, 99 insertions(+), 272 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7518e73a..09158edc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,8 +68,8 @@ ## Technical changes and Optimizations -- reuse MFE seed-extension scratch matrices and bounded physical loop energies - across seeds in exact and heuristic prediction; document benchmarks (#274) +- reuse MFE seed-extension scratch matrices and bound predecessor scans in exact + and heuristic prediction; evaluate physical energies directly (PR #276 review) - share exact left-most seed-anchor extensions with heuristic prediction, combining each true left extension with one retained right endpoint (#270) @@ -221,6 +221,22 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261020 Alexander Mitrofanov + * PredictorMfe2dSeedExtension.*, PredictorMfe2dHeuristicSeedExtension.cpp + - Remove extensionLoopE(), the local loop-energy table, known-value flags, + pair-index map and cache initialization/reset/fallback machinery requested + in https://github.com/BackofenLab/IntaRNA/pull/276#discussion_r4312235144 + * Restore direct energy.getE_interLeft() and energy.areComplementary() calls + in both predictors and traceback. Retain nonpreserving scratch resize, + bounded predecessor scans and their existing traversal order. + * PredictorSeedOracle_test.cpp + - Remove obsolete cache probes; retain independent recurrence, workspace, + tie, maximal-loop, range/offset, traceback and seed-witness regressions. + * doc/analysis/mfe-seed-extension-reuse.md, + doc/benchmarks/seed-extension/README.md + * Describe direct evaluation and label the archived cached-prototype timing + evidence explicitly so it is not attributed to the revised implementation. + 261016 Alexander Mitrofanov * PredictorMfe2dSeedExtension, PredictorMfe2dHeuristicSeedExtension * reuse nonpreserving extension workspace capacity across seeds and traceback diff --git a/doc/analysis/mfe-seed-extension-reuse.md b/doc/analysis/mfe-seed-extension-reuse.md index 24ea4cc7..a75b5014 100644 --- a/doc/analysis/mfe-seed-extension-reuse.md +++ b/doc/analysis/mfe-seed-extension-reuse.md @@ -1,6 +1,12 @@ # Reusing work in MFE seed extensions This implements [issue #274](https://github.com/BackofenLab/IntaRNA/issues/274). +Following the [PR #276 review](https://github.com/BackofenLab/IntaRNA/pull/276#discussion_r4312235144), +the current implementation retains matrix-capacity reuse and bounded predecessor +scans, and evaluates pairing and physical loop energies directly through the +energy model. The local loop-energy cache and its supporting pair map have been +removed. Historical measurements below describe the earlier cached prototype. + The analysis and measurement baseline is `perf/270-anchored-heuristic` at `21b9d8fc33aadd3c44d788c4da10a91cf6694ce1`. Both native MFE seed-extension predictors are selected with `--model=X`: `--mode=M` is exact over the @@ -19,8 +25,8 @@ The following distinctions determine which techniques transfer safely. | Technique in the ensemble implementation | MFE application | | --- | --- | | Reuse workspace; reshape without retaining obsolete cells | Applied to both MFE extension matrices, including traceback. Each recurrence initializes every active cell before reading it. | -| Evaluate a pair domain once and restrict recurrence transitions | Cache complementary-pair information; compute loop predecessor bounds once per cell. Preserve predecessor traversal order. | -| Share reusable work across many seed anchors | Cache physical loop energies by the two endpoint pairs, independent of the current seed. Both left/right fills and traceback share these values. | +| Evaluate a pair domain once and restrict recurrence transitions | Compute loop predecessor bounds once per cell. Preserve predecessor traversal order and evaluate complementary pairs directly. | +| Share reusable work across many seed anchors | Reuse scratch capacity across seeds. Evaluate physical loop energies directly in both left/right fills and traceback, as requested in review. | | Left-most seed ownership and elimination of redundant containing anchors | Not applied. Ensemble ownership prevents double counting in sums; MFE predictions retain their existing seed enumeration, tie handling, tracker updates and approximate `Zall` accounting. | | Batch complete boundaries and differentiate partition functions for pair probabilities | Not needed for MFE minimization. It would add state and alter the organization of candidate publication. | @@ -28,36 +34,23 @@ An extension matrix cell is the optimal path **to or from a particular seed boundary**. Overlapping seeds have different conditioned subproblems; copying a matrix cell merely because the seeds overlap is incorrect. The ensemble kernel also resets its per-anchor tables rather than copying numerical values between -arbitrary overlapping anchors. This implementation reuses their storage and -the underlying loop energies, while recomputing each anchor's MFE recurrence. -The loop cache is an additional application of sharing seed-independent work; -it is not a claim that the ensemble kernel already has the same cache. +arbitrary overlapping anchors. This implementation reuses their storage while +recomputing each anchor's MFE recurrence. Physical loop energies still come from +`energy.getE_interLeft()`. ## Implementation and limits -`PredictorMfe2dSeedExtension` owns scratch preparation and the cache, used by -both exact and heuristic MFE extension prediction. The `resize(..., false)` calls +`PredictorMfe2dSeedExtension` owns the two scratch matrices used by both exact +and heuristic MFE extension prediction. The `resize(..., false)` calls grow extension matrices on demand, reuse capacity, and avoid copying values that are immediately overwritten. They do not eagerly reserve the full sequence rectangle, which would waste space when only sparse or central seeds exist. -Within one prediction, a physical loop energy depends on its two endpoint -pairs and the fixed energy model, not on the seed being extended. A dense -pair-index map assigns complementary pairs compact indices. Lazy cache slots -are indexed by that pair and positive bounded displacements along both strands. -C++ consumers must rebuild with matching headers and library because the -predictor storage layout changes. A separate known flag permits caching `E_INF` without confusing it with an -uncomputed slot. The native energy model still supplies every new energy. - -The combined pair map, energy slots and flags have an element-storage budget of -16 MiB per predictor. If the complete loop table would exceed this limit, loop -energies are evaluated directly while the bounded pair map remains available. -If even the pair map exceeds it, pair checks also use direct evaluation. -Allocator metadata and the two existing dynamic-programming matrices are -outside this cache budget. This is a time/memory tradeoff, not a memory reduction. -All cached values are discarded before each prediction changes sequence -offsets, including a prediction that subsequently finds no seed. Long direct -loop probes during traceback fall back to the energy model. +Pair checks use `energy.areComplementary()` and loop-energy queries use +`energy.getE_interLeft()` in recurrence fills and traceback. There is no local +pair map, loop table, known-value flag array, cache budget or cache lifecycle. +Removing these members changes the predictor object layout; C++ consumers must +rebuild with matching headers and library. The predecessor ranges are the same inclusive ranges as before; only their construction moves out of the innermost scans. Energy arithmetic, seed order, @@ -67,7 +60,34 @@ complexity is unchanged. The exact predictor's exhaustive left/right combination can still dominate runtime, whereas the heuristic spends more of its time building extension tables. -## Validation and measurements +## Cache-removal review validation + +GCC 14.4.0 release and debug `make tests -j2` each passed all eight test drivers +and 119 API cases. The independent recurrence, workspace, tie, maximal-loop, +range-change, traceback and seed-witness tests remain enabled. Both affected +installed public headers compile independently. + +One alternating baseline/candidate pass across all 12 native harness cases +produced 24 byte-identical outputs, including energies, boundaries, traceback, +seed starts and `Eall`. This comparison used the cached PR #276 head +`cd193f7b58f3706882ce2d1f24bf0cb9e4f448cd` as baseline and the direct-evaluation +revision as candidate. It checks output equivalence; one sample per variant +does not establish a performance result. All 177 tracked `src` files in each +validation copy were checked against the intended source revision. + +The shared base contains the separate [issue #282](https://github.com/BackofenLab/IntaRNA/issues/282) +Autotools bootstrap defect. Its two Makefile corrections were applied only to +disposable validation copies so these tests could run; they remain a separate +change from this cache-removal review. Linux was validated locally; macOS was +not run. + +## Historical cached-prototype validation and measurements + +The following results belong to the cached implementation at +`4994edf7bb754d305c00503f57bbb3d937bfad5b`; they do not measure the current +implementation after cache removal. Cache-only probes were removed along with +the cache. Independent recurrence, tie, maximal-loop, range-change and traceback +regressions remain applicable to the current implementation. Release and debug `make tests -j2` each passed all seven test drivers. The API suite passed 119 cases, with 462,526 assertions in release and 462,527 in debug. @@ -129,8 +149,10 @@ verification are retained in the benchmark directory as ## Reproduction -Build the baseline revision above and this revision with the same release -compiler, dependencies and flags. From this checkout run: +To reproduce the historical cached-prototype measurements, build the baseline +revision above and candidate `4994edf7bb754d305c00503f57bbb3d937bfad5b` with the +same release compiler, dependencies and flags. The harness can also compare +any other two revisions, but the historical timings do not apply to them. Run: ```sh python3 doc/benchmarks/seed-extension/mfe-native.py \ diff --git a/doc/benchmarks/seed-extension/README.md b/doc/benchmarks/seed-extension/README.md index 235d47c9..027c5c25 100644 --- a/doc/benchmarks/seed-extension/README.md +++ b/doc/benchmarks/seed-extension/README.md @@ -268,4 +268,7 @@ for these measurements. executables with byte-exact energy, traceback, seed and `Eall` output. It includes OxyS/fhlA, dense, noLP, bulged-seed and no-seed cases. See the [analysis and reproduction instructions](../../analysis/mfe-seed-extension-reuse.md) -and the `mfe-native-*`/`mfe-source-hashes.json` evidence files. +and the `mfe-native-*`/`mfe-source-hashes.json` evidence files. Those measurements +describe the historical cached prototype at `4994edf`, before the PR #276 +review removed local pairing and loop-energy caches; they do not measure the +current direct-evaluation implementation. diff --git a/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp b/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp index 8b952b34..df2d7bd1 100644 --- a/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp +++ b/src/IntaRNA/PredictorMfe2dHeuristicSeedExtension.cpp @@ -46,9 +46,6 @@ predict( const IndexRange & r1, const IndexRange & r2 ) throw std::runtime_error("PredictorMfe2dHeuristicSeedExtension::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif - // Cached physical coordinates belong only to this prediction. - resetExtensionCache(); - // setup index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); @@ -72,9 +69,6 @@ predict( const IndexRange & r1, const IndexRange & r2 ) return; } - // Reuse scratch capacity and physical loop energies across seed anchors. - prepareExtensionWorkspace(range_size1, range_size2); - size_t si1 = RnaSequence::lastPos, si2 = RnaSequence::lastPos; while( seedHandler.updateToNextSeed(si1,si2 , 0, range_size1+1-seedHandler.getConstraint().getBasePairs() @@ -154,18 +148,18 @@ fillHybridE_right( const size_t sj1, const size_t sj2 // check if complementary if (sj1extensionLoopWidth1 || j2-i2>extensionLoopWidth2) - return energy.getE_interLeft(i1,j1,i2,j2); - const size_t pair=extensionPairIndex(i1,i2); - if(!pair) return energy.getE_interLeft(i1,j1,i2,j2); - const size_t slot=((pair-1)*extensionLoopWidth1+j1-i1-1)*extensionLoopWidth2+j2-i2-1; - if(!extensionLoopKnown[slot]) { - extensionLoopEnergy[slot]=energy.getE_interLeft(i1,j1,i2,j2); - extensionLoopKnown[slot]=1; - } - return extensionLoopEnergy[slot]; -} - -void -PredictorMfe2dSeedExtension::resetExtensionCache() -{ - // Release old cache storage as well as values before offsets can change. - extensionPairIndex=Matrix(); - extensionLoopEnergy=std::vector(); - extensionLoopKnown=std::vector(); - extensionLoopWidth1=extensionLoopWidth2=0; -} - -void -PredictorMfe2dSeedExtension::prepareExtensionWorkspace(size_t n, size_t m) -{ - // Extension matrices grow on demand via nonpreserving resize. Reserving the - // entire range here would waste space for sparse or centrally placed seeds. - - // Bound the combined dense pair index, lazy energies and computed flags. - // Large regions retain direct evaluation rather than growing a global cache. - constexpr size_t cacheBytes=16*1024*1024; - if(!n || !m || n>cacheBytes/sizeof(size_t)/m) return; - const size_t pairBytes=n*m*sizeof(size_t); - extensionPairIndex.resize(n,m,false); - size_t pairs=0; - for(size_t i=0;iavailable/extensionLoopWidth1/extensionLoopWidth2) return; - const size_t slots=pairs*extensionLoopWidth1*extensionLoopWidth2; - extensionLoopEnergy.resize(slots); - extensionLoopKnown.assign(slots,0); -} - -////////////////////////////////////////////////////////////////////////// - void PredictorMfe2dSeedExtension:: predict( const IndexRange & r1, const IndexRange & r2 ) @@ -104,9 +46,6 @@ predict( const IndexRange & r1, const IndexRange & r2 ) throw std::runtime_error("PredictorMfe2d::predict("+toString(r1)+","+toString(r2)+") is not sane"); #endif - // Cached physical coordinates belong only to this prediction. - resetExtensionCache(); - // setup index offset energy.setOffset1(r1.from); energy.setOffset2(r2.from); @@ -131,9 +70,6 @@ predict( const IndexRange & r1, const IndexRange & r2 ) // initialize mfe interaction for updates initOptima(); - // Reuse scratch capacity and physical loop energies across seed anchors. - prepareExtensionWorkspace(range_size1, range_size2); - size_t si1 = RnaSequence::lastPos, si2 = RnaSequence::lastPos; while( seedHandler.updateToNextSeed(si1,si2 , 0, range_size1+1-seedHandler.getConstraint().getBasePairs() @@ -196,7 +132,7 @@ fillHybridE_left( const size_t si1, const size_t si2 ) const OutputConstraint & outConstraint = output.getOutputConstraint(); #if INTARNA_IN_DEBUG_MODE // check indices - if (!extensionPairIsValid(si1,si2) ) + if (!energy.areComplementary(si1,si2) ) throw std::runtime_error("PredictorMfe2dSeedExtension::fillHybridE_left("+toString(si1)+","+toString(si2)+",..) are not complementary"); #endif @@ -226,18 +162,18 @@ fillHybridE_left( const size_t si1, const size_t si2 ) // check if complementary if (i1 namespace IntaRNA { @@ -94,39 +93,6 @@ class PredictorMfe2dSeedExtension: public PredictorMfe2d { protected: - //! One-based complementary-pair indices; zero denotes an unavailable pair. - Matrix extensionPairIndex; - //! Seed-independent loop energies and flags; E_INF is a cacheable result. - std::vector extensionLoopEnergy; - std::vector extensionLoopKnown; - //! Indexed positive loop displacements on the two increasing local strands. - size_t extensionLoopWidth1 = 0, extensionLoopWidth2 = 0; - - /** Discard cached values before a prediction changes the local offsets. */ - void resetExtensionCache(); - /** Prepare reusable seed-independent pair/loop - * information for this prediction. Cache storage is limited to 16 MiB; - * domains exceeding that limit retain direct energy evaluation. - * @param n number of active positions on sequence 1 - * @param m number of active positions on reversed sequence 2 - */ - void prepareExtensionWorkspace(size_t n, size_t m); - /** Check pair feasibility in the current prediction's local coordinates. - * @param i1 position in sequence 1 - * @param i2 position in reversed sequence 2 - * @return whether the pair is complementary and accessible - */ - bool extensionPairIsValid(size_t i1, size_t i2) const; - /** Reuse a seed-independent loop energy, with direct evaluation outside - * the bounded cache. Both pairs use increasing local strand coordinates. - * @param i1 left pair's position in sequence 1 - * @param j1 right pair's position in sequence 1 - * @param i2 left pair's position in reversed sequence 2 - * @param j2 right pair's position in reversed sequence 2 - * @return loop energy in hundredths of kcal/mol, or E_INF - */ - E_type extensionLoopE(size_t i1, size_t j1, size_t i2, size_t j2); - /** * Computes all entries of the hybridE matrix for interactions starting in * i1 and i2 and report all valid interactions to updateOptima() @@ -168,14 +134,6 @@ class PredictorMfe2dSeedExtension: public PredictorMfe2d { ////////////////////////////////////////////////////////////////////////// -inline -bool -PredictorMfe2dSeedExtension::extensionPairIsValid(size_t i1, size_t i2) const -{ - return extensionPairIndex.size1() ? extensionPairIndex(i1,i2) != 0 - : energy.areComplementary(i1,i2); -} - inline void PredictorMfe2dSeedExtension:: diff --git a/tests/PredictorSeedOracle_test.cpp b/tests/PredictorSeedOracle_test.cpp index 655be0fe..20b97de8 100644 --- a/tests/PredictorSeedOracle_test.cpp +++ b/tests/PredictorSeedOracle_test.cpp @@ -687,48 +687,6 @@ TEST_CASE("tiny exhaustive seed oracle for exact predictor families", namespace { -class CountingExtensionEnergy : public InteractionEnergyBasePair { -public: - using InteractionEnergyBasePair::InteractionEnergyBasePair; - mutable size_t loopCalls = 0; - E_type getE_interLeft(size_t i1, size_t j1, size_t i2, size_t j2) const override; -}; - -E_type CountingExtensionEnergy::getE_interLeft(size_t i1, size_t j1, - size_t i2, size_t j2) const -{ - ++loopCalls; - return InteractionEnergyBasePair::getE_interLeft(i1, j1, i2, j2); -} - -class MfeExtensionCacheProbe : public PredictorMfe2dSeedExtension { -public: - using PredictorMfe2dSeedExtension::PredictorMfe2dSeedExtension; - using PredictorMfe2dSeedExtension::extensionLoopE; - void prepare(size_t offset1, size_t offset2, size_t n, size_t m); - bool hasLoopCache() const; - bool hasPairCache() const; -}; - -void MfeExtensionCacheProbe::prepare(size_t offset1, size_t offset2, - size_t n, size_t m) -{ - resetExtensionCache(); - energy.setOffset1(offset1); - energy.setOffset2(offset2); - prepareExtensionWorkspace(n, m); -} - -inline bool MfeExtensionCacheProbe::hasLoopCache() const -{ - return !extensionLoopEnergy.empty(); -} - -inline bool MfeExtensionCacheProbe::hasPairCache() const -{ - return extensionPairIndex.size1() != 0; -} - template void checkMfeExtensionWorkspaceReuse(const InteractionEnergy & energy, const SeedConstraint & seedConstraint, @@ -785,66 +743,6 @@ void checkMfeExtensionWorkspaceReuse(const InteractionEnergy & energy, } // namespace -TEST_CASE("MFE extension loop caching preserves infinity offsets and bounded fallback", - "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") -{ - #include "testEasyLoggingSetup.icc" - - const auto seedConstraint = makeSeedConstraint(2, 0, 0, 0, false); - const auto outputConstraint = makeOutputConstraint(false); - SECTION("finite and infinite transitions are cached once per prediction") { - RnaSequence target("target", "GGAGGGG"), query("query", "CCCCCCC"); - AccessibilityDisabled targetAcc(target, 2, nullptr), queryAcc(query, 2, nullptr); - ReverseAccessibility reverseQuery(queryAcc); - CountingExtensionEnergy energy(targetAcc, reverseQuery, 2, 1); - SeedRecordingOutput output(outputConstraint); - MfeExtensionCacheProbe predictor(energy, output, nullptr, - new SeedHandlerMfe(energy, seedConstraint)); - predictor.prepare(0, 0, 7, 7); - REQUIRE(predictor.hasLoopCache()); - for (size_t repeat = 0; repeat < 3; ++repeat) { - REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); - REQUIRE(E_isINF(predictor.extensionLoopE(0, 2, 0, 1))); - REQUIRE(predictor.extensionLoopE(0, 3, 0, 1) == Ekcal_2_E(-1)); - } - REQUIRE(energy.loopCalls == 3); - // Traceback may query a direct loop longer than the recurrence bounds. - REQUIRE(E_isINF(predictor.extensionLoopE(0, 4, 0, 1))); - REQUIRE(E_isINF(predictor.extensionLoopE(0, 4, 0, 1))); - REQUIRE(energy.loopCalls == 5); - // The same local key now names a different physical pair, ending in A. - predictor.prepare(1, 0, 6, 7); - REQUIRE(E_isINF(predictor.extensionLoopE(0, 1, 0, 1))); - REQUIRE(E_isINF(predictor.extensionLoopE(0, 1, 0, 1))); - REQUIRE(energy.loopCalls == 6); - predictor.prepare(0, 0, 7, 7); - REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); - REQUIRE(energy.loopCalls == 7); - } - SECTION("large cache domains retain direct energy evaluation") { - RnaSequence target("target", std::string(1500, 'G')); - RnaSequence query("query", std::string(1500, 'C')); - AccessibilityDisabled targetAcc(target, 2, nullptr), queryAcc(query, 2, nullptr); - ReverseAccessibility reverseQuery(queryAcc); - CountingExtensionEnergy energy(targetAcc, reverseQuery, 2, 2); - SeedRecordingOutput output(outputConstraint); - MfeExtensionCacheProbe predictor(energy, output, nullptr, - new SeedHandlerMfe(energy, seedConstraint)); - for (size_t length : {600, 1500}) { - CAPTURE(length); - // At 600 the loop table exceeds the cap; at 1500 so does the - // pair map. Neither path needs a full prediction to test fallback. - predictor.prepare(0, 0, length, length); - REQUIRE_FALSE(predictor.hasLoopCache()); - REQUIRE(predictor.hasPairCache() == (length == 600)); - const size_t previousCalls = energy.loopCalls; - REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); - REQUIRE(predictor.extensionLoopE(0, 1, 0, 1) == Ekcal_2_E(-1)); - REQUIRE(energy.loopCalls == previousCalls+2); - } - } -} - TEST_CASE("MFE seed extensions preserve bounded recurrences and reused workspaces", "[PredictorSeedOracle][MfeSeedExtensionWorkspace]") { From aaa7e09bc468a561c04605da7cca0499c91d5bc3 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Tue, 20 Oct 2026 01:58:10 +0200 Subject: [PATCH 30/30] Fix Autotools bootstrap and portability errors (#282) --- .github/workflows/build.yml | 1 + ChangeLog | 14 ++++++++++++++ Makefile.am | 2 +- tests/Makefile.am | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9459ab05..18668a64 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,6 +87,7 @@ jobs: - name: Bootstrap and configure run: | bash autotools-init.sh + automake --warnings=portability,error ./configure \ --prefix="$RUNNER_TEMP/intarna-install" \ --with-vrna="$CONDA_PREFIX" \ diff --git a/ChangeLog b/ChangeLog index e47a1662..8a108914 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- BUGFIX : restore clean Autotools configuration with dependency tracking and + use portable Doxygen header prerequisites (issue #282) + - share exact left-most seed-anchor extensions with heuristic prediction, combining each true left extension with one retained right endpoint (#270) @@ -218,6 +221,17 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261020 Alexander Mitrofanov + * Makefile.am + * Use a make prerequisite wildcard for public Doxygen headers instead of the + GNU wildcard function that triggered Automake's portability warning. + * tests/Makefile.am + * Restore the test-script list continuation lost during a merge, fixing the + generated Makefile parse error in config.status dependency bootstrapping + and retaining all seven CLI regression drivers (issue #282). + * .github/workflows/build.yml + + Reject Automake portability warnings before configuring in build CI. + 261016 Alexander Mitrofanov * SeededPartitionFunction.*, StackSeedDomain.h * Share the exact left-most seed-anchor recurrence with heuristic mode. diff --git a/Makefile.am b/Makefile.am index 8d6de4cc..4005a548 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,7 +27,7 @@ if enable_doxygen @DX_RULES@ # Refresh the API after changes to public headers, the overview, or branding. -$(DX_DOCDIR)/$(PACKAGE).tag: $(wildcard $(srcdir)/src/IntaRNA/*.h) \ +$(DX_DOCDIR)/$(PACKAGE).tag: $(srcdir)/src/IntaRNA/*.h \ $(srcdir)/doc/doxygen/mainpage.md $(srcdir)/doc/doxygen/header.html \ $(srcdir)/doc/doxygen/footer.html $(srcdir)/doc/doxygen/intarna.css else diff --git a/tests/Makefile.am b/tests/Makefile.am index 63611f50..5ac04457 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,7 @@ SH_LOG_COMPILER = $(SHELL) dist_check_SCRIPTS = \ runAccessibilityBinary.sh \ runAccessibilityThreads.sh \ - runBasePairProbabilities.sh + runBasePairProbabilities.sh \ runIntaRNA.sh \ runIntaRNAeval.sh \ runKineticSeedExtension.sh \