Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0b20908
test: document phase 1 audit and expose baseline defects
Alexander-Mitrofanov Aug 15, 2026
11ac391
fix: preserve interactions on self-assignment
Alexander-Mitrofanov Aug 15, 2026
f498659
fix: compare optional interaction seeds safely
Alexander-Mitrofanov Aug 15, 2026
8db8b0c
fix: handle zero-capacity interaction storage
Alexander-Mitrofanov Aug 15, 2026
9e2a39e
fix: give invalid Nussinov pairs zero weight
Alexander-Mitrofanov Aug 15, 2026
0f901cc
fix: exclude the empty structure from base-pair ES
Alexander-Mitrofanov Aug 15, 2026
302bd38
fix: count noLP heuristic ensemble paths once
Alexander-Mitrofanov Aug 15, 2026
f9612c3
fix: apply site filters before ensemble accumulation
Alexander-Mitrofanov Aug 15, 2026
eae6555
fix: isolate target base-pair accessibility limits
Alexander-Mitrofanov Aug 15, 2026
a129205
test: correct base-pair accessibility oracle
Alexander-Mitrofanov Aug 15, 2026
18bdac7
fix: preserve no-seed ranges with outMinPu
Alexander-Mitrofanov Aug 15, 2026
eefbda5
fix: reject double-counted window partition output
Alexander-Mitrofanov Aug 15, 2026
77e01d3
fix: retain distinct equal-energy seeds
Alexander-Mitrofanov Aug 15, 2026
b7a4ba1
fix: exclude inaccessible split positions from ranges
Alexander-Mitrofanov Aug 15, 2026
c132a61
fix: honor base-pair span in Vienna ES
Alexander-Mitrofanov Aug 15, 2026
595029f
fix: release ViennaRNA ensemble resources
Alexander-Mitrofanov Aug 15, 2026
de135fd
test: expose seed ensemble algebra and reuse defects
Alexander-Mitrofanov Aug 15, 2026
1ed5819
fix: multiply stacked seed partition factors
Alexander-Mitrofanov Aug 15, 2026
a975c4d
fix: clear seed ensemble state on empty ranges
Alexander-Mitrofanov Aug 15, 2026
70920c6
docs: finalize phase 1 audit and corrections
Alexander-Mitrofanov Aug 15, 2026
08d375e
test: expose stale heuristic cell state
Alexander-Mitrofanov Aug 15, 2026
96c14b3
fix: reset heuristic cell state
Alexander-Mitrofanov Aug 15, 2026
ade93dd
docs: record heuristic cell corrections
Alexander-Mitrofanov Aug 15, 2026
0c9cbc1
test: expose output hub reporting defects
Alexander-Mitrofanov Aug 15, 2026
ce0599b
fix: align output hub forwarding signature
Alexander-Mitrofanov Aug 15, 2026
87f45f1
fix: report maximum output hub count
Alexander-Mitrofanov Aug 15, 2026
ba5f9eb
docs: record output hub corrections
Alexander-Mitrofanov Aug 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 221 additions & 0 deletions doc/refactor/1-current-state.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/IntaRNA/Accessibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ decomposeByMaxED( IndexRangeList & ranges, const E_type maxED, const size_t minR
for (size_t i= range->from; i <= range->to; i++) {
if (E_isINF(getED(i,i)) || (getED(i,i) > maxED && !E_equal(getED(i,i),maxED))) {
// check if end of range found and to be stored
if (lastStart < i && minRangeLength <= (i +1 - lastStart)) {
out.push_back(IndexRange(lastStart,i));
if (lastStart < i && minRangeLength <= (i - lastStart)) {
out.push_back(IndexRange(lastStart,i - 1));
}
lastStart = range->to +1;
} else {
Expand Down
6 changes: 5 additions & 1 deletion src/IntaRNA/Interaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ Interaction &
Interaction::
operator= ( const Interaction & toCopy )
{
if (this == &toCopy) {
return *this;
}
#if INTARNA_IN_DEBUG_MODE
if (!toCopy.isValid())
throw std::runtime_error("Interaction::=("+toString(toCopy)+") not valid!");
Expand Down Expand Up @@ -227,7 +230,8 @@ operator == ( const Interaction &i ) const
&& s2 == i.s2
&& E_equal( energy, i.energy )
&& basePairs == i.basePairs
&& (seed == i.seed || *seed == *(i.seed))
&& (seed == i.seed
|| (seed != NULL && i.seed != NULL && *seed == *(i.seed)))
;
}

Expand Down
20 changes: 15 additions & 5 deletions src/IntaRNA/Interaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,26 @@ class Interaction {
E_type energy;

/**
* order definition: first by increasing energy using increasing seq1
* index as tie breaker.
* order definition: first by increasing energy, then lexicographically
* by all seed boundaries as tie breakers.
* @param s the seed to compare to
* @return true if this seed is considered smaller than s
*/
const bool
operator < ( const Seed &s ) const {
return ( energy < s.energy
|| (E_equal(energy,s.energy) && (bp_i.first < s.bp_i.first))
);
if (energy != s.energy) {
return energy < s.energy;
}
if (bp_i.first != s.bp_i.first) {
return bp_i.first < s.bp_i.first;
}
if (bp_i.second != s.bp_i.second) {
return bp_i.second < s.bp_i.second;
}
if (bp_j.first != s.bp_j.first) {
return bp_j.first < s.bp_j.first;
}
return bp_j.second < s.bp_j.second;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion src/IntaRNA/InteractionEnergyBasePair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ void InteractionEnergyBasePair::computeES(const RnaSequence &seq,
if (Z_equal(q_val, 1.0)) {
logQ(i, j) = E_INF;
} else {
logQ(i, j) = getE(q_val);
// getES* covers only structures containing at least one base pair.
// The full monomer partition Q also contains the empty structure with
// unit weight, which has to be removed here.
logQ(i, j) = getE(q_val - Z_type(1.0));
}
}
}
Expand Down
44 changes: 33 additions & 11 deletions src/IntaRNA/InteractionEnergyVrna.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "IntaRNA/AccessibilityVrna.h"

#include <cassert>
#include <cstdlib>
#include <memory>
#include <set>

// ES computation
Expand All @@ -16,6 +18,26 @@ extern "C" {
}


namespace {

struct VrnaAllocatedDeleter {
void operator()( char * data ) const {
free(data);
}
};

struct VrnaFoldCompoundDeleter {
void operator()( vrna_fold_compound_t * foldCompound ) const {
vrna_fold_compound_free(foldCompound);
}
};

typedef std::unique_ptr<char, VrnaAllocatedDeleter> VrnaAllocatedPtr;
typedef std::unique_ptr<vrna_fold_compound_t, VrnaFoldCompoundDeleter> VrnaFoldCompoundPtr;

}



namespace IntaRNA {

Expand Down Expand Up @@ -94,8 +116,10 @@ computeES( const Accessibility & acc, InteractionEnergyVrna::EsMatrix & esToFill
const Z_type RT = getRT();

// VRNA compatible data structures
char * sequence = (char *) vrna_alloc(sizeof(char) * (seqLength + 1));
char * structureConstraint = (char *) vrna_alloc(sizeof(char) * (seqLength + 1));
VrnaAllocatedPtr sequenceOwner( (char *) vrna_alloc(sizeof(char) * (seqLength + 1)) );
VrnaAllocatedPtr structureConstraintOwner( (char *) vrna_alloc(sizeof(char) * (seqLength + 1)) );
char * const sequence = sequenceOwner.get();
char * const structureConstraint = structureConstraintOwner.get();
for (int i=0; i<seqLength; i++) {
// copy sequence
sequence[i] = acc.getSequence().asString().at(i);
Expand All @@ -112,7 +136,8 @@ computeES( const Accessibility & acc, InteractionEnergyVrna::EsMatrix & esToFill
curModel.max_bp_span = -1;
}
// TODO check if VRNA_OPTION_WINDOW reasonable to speedup
vrna_fold_compound_t * foldData = vrna_fold_compound( sequence, &foldModel, VRNA_OPTION_PF);
VrnaFoldCompoundPtr foldDataOwner( vrna_fold_compound( sequence, &curModel, VRNA_OPTION_PF) );
vrna_fold_compound_t * const foldData = foldDataOwner.get();

// Adding hard constraints from pseudo dot-bracket
unsigned int constraint_options = VRNA_CONSTRAINT_DB_DEFAULT;
Expand Down Expand Up @@ -157,11 +182,6 @@ computeES( const Accessibility & acc, InteractionEnergyVrna::EsMatrix & esToFill
}
}
}
// garbage collection
vrna_fold_compound_free(foldData);
free(structureConstraint);
free(sequence);

}

////////////////////////////////////////////////////////////////////////////
Expand All @@ -178,14 +198,16 @@ computeIntraEall( const Accessibility & acc ) const
const int length = acc.getSequence().size();

// copy sequence into C data structure
char * sequence = (char *) vrna_alloc(sizeof(char) * (length + 1));
VrnaAllocatedPtr sequenceOwner( (char *) vrna_alloc(sizeof(char) * (length + 1)) );
char * const sequence = sequenceOwner.get();
for (int i=0; i<length; i++) {
sequence[i] = acc.getSequence().asString().at(i);
}
sequence[length] = '\0';

// setup folding data
vrna_fold_compound_t * fold_compound = vrna_fold_compound( sequence, &curModel, VRNA_OPTION_DEFAULT );
// setup folding data
VrnaFoldCompoundPtr foldCompoundOwner( vrna_fold_compound( sequence, &curModel, VRNA_OPTION_DEFAULT ) );
vrna_fold_compound_t * const fold_compound = foldCompoundOwner.get();

// add accessibility constraints
AccessibilityVrna::addConstraints( *fold_compound, acc );
Expand Down
2 changes: 1 addition & 1 deletion src/IntaRNA/NussinovHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ NussinovHandler::getQb(const size_t i, const size_t j, const RnaSequence &seq,
const Z_type bpWeight, const size_t minLoopLength,
NussinovHandler::Z2dMatrix &Q, NussinovHandler::Z2dMatrix &Qb) {
if (j >= seq.size()) {
return 1.0;
return 0.0;
}
if (i + minLoopLength >= j) {
return 0.0;
Expand Down
6 changes: 3 additions & 3 deletions src/IntaRNA/OutputHandlerHub.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ addOutputHandler( OutputHandler * handler )
inline
void
OutputHandlerHub::
add( const Interaction & inter, const OutputConstraint & outConstraint )
add( const Interaction & inter )
{
// forward to all in list
for (auto it=outList.begin(); it!=outList.end(); it++) {
(*it)->add(inter,outConstraint);
(*it)->add(inter);
}
}

Expand All @@ -222,7 +222,7 @@ reported() const
size_t maxReported = 0;
// get maximal number of reports among all handlers
for (auto it=outList.begin(); it!=outList.end(); it++) {
maxReported = std::min( maxReported, (*it)->reported() );
maxReported = std::max( maxReported, (*it)->reported() );
}
// return maximum
return maxReported;
Expand Down
4 changes: 3 additions & 1 deletion src/IntaRNA/OutputHandlerInteractionList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ add( const Interaction & interaction )
{
// count interaction
reportedInteractions++;
if (storage.size() < maxToStore || lessThan_StorageContainer( &interaction, *(storage.rbegin()) )) {
if (maxToStore > 0
&& (storage.size() < maxToStore
|| lessThan_StorageContainer( &interaction, *(storage.rbegin()) ))) {
// find where to insert this interaction
StorageContainer::iterator insertPos = std::lower_bound( storage.begin(), storage.end(), &interaction, lessThan_StorageContainer );
// check if interaction is NOT already part of the list
Expand Down
1 change: 1 addition & 0 deletions src/IntaRNA/PredictorMfe2dHeuristic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fillHybridE()

// init as invalid boundary
*curCell = BestInteractionE(E_INF, RnaSequence::lastPos, RnaSequence::lastPos);
curCellEtotal = E_INF;

// check if positions can form interaction
if ( energy.isAccessible1(i1)
Expand Down
1 change: 1 addition & 0 deletions src/IntaRNA/PredictorMfe2dHeuristicSeed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fillHybridE()
// init as invalid boundary
*curCell = BestInteractionE(E_INF, RnaSequence::lastPos, RnaSequence::lastPos);
*curCellSeed = BestInteractionE(E_INF, RnaSequence::lastPos, RnaSequence::lastPos);
curCellEtotal = E_INF;

// check if positions can form interaction
if ( energy.isAccessible1(i1)
Expand Down
16 changes: 16 additions & 0 deletions src/IntaRNA/PredictorMfeEns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ updateZ( const size_t i1, const size_t j1
// check if something to be done
if (Z_equal(partZ,0) || Z_isINF(Zall))
return;

// Apply the same site filters used for MFE candidates before changing
// either the global or boundary-specific partition.
const OutputConstraint & outConstraint = output.getOutputConstraint();
if (outConstraint.noGUend
&& (energy.isGU(i1,i2) || energy.isGU(j1,j2)))
{
return;
}
if (outConstraint.maxED < Accessibility::ED_UPPER_BOUND
&& (energy.getED1(i1,j1) > outConstraint.maxED
|| energy.getED2(i2,j2) > outConstraint.maxED))
{
return;
}

// handle whether or not partZ includes ED values or not
Z_type partZ_withED = 0, partZ_noED = 0;
if (isHybridZ) {
Expand Down
35 changes: 19 additions & 16 deletions src/IntaRNA/PredictorMfeEns2dHeuristic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ fillHybridZ()

// init as invalid boundary
*curCell = BestInteractionZ(0.0, RnaSequence::lastPos, RnaSequence::lastPos);
curCellEtotal = E_INF;

// check if positions can form interaction
if ( energy.isAccessible1(i1)
Expand Down Expand Up @@ -134,24 +135,20 @@ fillHybridZ()
// update overall partition function information for initial bps only
updateZ( i1,curCell->j1, i2,curCell->j2, curCell->val, true );

if(outConstraint.noLP) {
/////////////////////////////////////////
// check direct extension to the right of the noLP stacking
/////////////////////////////////////////
}

// direct cell access (const)
rightExt = &(hybridZ(i1+noLpShift,i2+noLpShift));
// check if right side can pair
if (Z_equal(rightExt->val, 0.0)) {
continue;
}
// check if interaction length is within boundary
if ( (rightExt->j1 +1 -i1) > energy.getAccessibility1().getMaxLength()
|| (rightExt->j2 +1 -i2) > energy.getAccessibility2().getMaxLength() )
{
continue;
}
if(outConstraint.noLP) {
/////////////////////////////////////////
// check direct extension to the right of the noLP stacking
/////////////////////////////////////////

// direct cell access (const)
rightExt = &(hybridZ(i1+noLpShift,i2+noLpShift));
// check if right side can pair and interaction length is within boundary
if (!Z_equal(rightExt->val, 0.0)
&& (rightExt->j1 +1 -i1) <= energy.getAccessibility1().getMaxLength()
&& (rightExt->j2 +1 -i2) <= energy.getAccessibility2().getMaxLength() )
{
// compute Z for direct extension with stacking
curZ = iStackZ * rightExt->val;

Expand Down Expand Up @@ -179,6 +176,12 @@ fillHybridZ()
// iterate over all loop sizes w1 (seq1) and w2 (seq2) (minus 1)
for (w1=1; w1-1 <= energy.getMaxInternalLoopSize1() && i1+w1+noLpShift<hybridZ.size1(); w1++) {
for (w2=1; w2-1 <= energy.getMaxInternalLoopSize2() && i2+w2+noLpShift<hybridZ.size2(); w2++) {
// For noLP, the adjacent continuation is already represented
// by the direct extension above. Counting it again as the
// (w1,w2)=(1,1) loop duplicates the same interaction paths.
if (noLpShift != 0 && w1 == 1 && w2 == 1) {
continue;
}
// direct cell access (const)
rightExt = &(hybridZ(i1+noLpShift+w1,i2+noLpShift+w2));
// check if right side can pair
Expand Down
1 change: 1 addition & 0 deletions src/IntaRNA/PredictorMfeEns2dHeuristicSeedExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ predict( const IndexRange & r1, const IndexRange & r2 )
if (seedHandler.fillSeed( 0, range_size1-1, 0, range_size2-1 ) == 0) {
// trigger empty interaction reporting
initOptima();
initZ();
reportOptima();
// stop computation
return;
Expand Down
3 changes: 2 additions & 1 deletion src/IntaRNA/PredictorMfeEns2dSeedExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ predict( const IndexRange & r1, const IndexRange & r2 )
if (seedHandler.fillSeed( 0, range_size1-1, 0, range_size2-1 ) == 0) {
// trigger empty interaction reporting
initOptima();
initZ();
reportOptima();
// stop computation
return;
Expand Down Expand Up @@ -229,7 +230,7 @@ fillHybridZ_left( const size_t si1, const size_t si2 )
// get stacking energy to avoid recomputation in recursion below
iStackZ = energy.getBoltzmannWeight(energy.getE_interLeft(i1,i1+noLpShift,i2,i2+noLpShift));
// check just stacked
curZ += iStackZ + hybridZ_left(l1-noLpShift,l2-noLpShift);
curZ += iStackZ * hybridZ_left(l1-noLpShift,l2-noLpShift);
}

// check all combinations of decompositions into (i1,i2)..(k1,k2)-(j1,j2)
Expand Down
17 changes: 10 additions & 7 deletions src/bin/CommandLineParsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,12 @@ parse(int argc, char** argv)
if (outNumber.val > 1 && outOverlap.val != 'B') {
throw error("window-based computation: non-overlapping subopt output (-n > 1) only supported for --outOverlap=B");
}
const bool windowNeedsZall = outMode.val == 'E'
|| (outMode.val == 'C'
&& OutputHandlerCsv::needsZall(OutputHandlerCsv::string2list(outCsvCols)));
if (windowNeedsZall) {
throw error("window-based computation cannot provide Zall/Eall output: overlapping windows count interactions more than once");
}
}


Expand Down Expand Up @@ -2015,8 +2021,8 @@ getTargetAccessibility( const size_t sequenceNumber ) const
case 'B' : // base-pair based accessibility
return new AccessibilityBasePair(
seq
, std::min( qIntLenMax.val == 0 ? seq.size() : qIntLenMax.val
, qAccW.val == 0 ? seq.size() : qAccW.val )
, std::min( tIntLenMax.val == 0 ? seq.size() : tIntLenMax.val
, tAccW.val == 0 ? seq.size() : tAccW.val )
, &accConstraint
);

Expand Down Expand Up @@ -2634,7 +2640,7 @@ getQueryRanges( const InteractionEnergy & energy, const size_t sequenceNumber, c
if (outMinPu.val > Z_type(0) && !Z_equal(outMinPu.val, Z_type(0))) {
// decompose ranges based in minimal unpaired probability value per position
// since all ranges covering a position will have a lower unpaired probability
acc.decomposeByMaxED( qRegion[sequenceNumber], energy.getE( outMinPu.val ), (noSeedRequired ? RnaSequence::lastPos : seedBP.val ) );
acc.decomposeByMaxED( qRegion[sequenceNumber], energy.getE( outMinPu.val ), (noSeedRequired ? 1 : seedBP.val ) );
}

return qRegion.at(sequenceNumber);
Expand Down Expand Up @@ -2668,7 +2674,7 @@ getTargetRanges( const InteractionEnergy & energy, const size_t sequenceNumber,
if (outMinPu.val > Z_type(0) && !Z_equal(outMinPu.val, Z_type(0))) {
// decompose ranges based in minimal unpaired probability value per position
// since all ranges covering a position will have a lower unpaired probability
acc.decomposeByMaxED( tRegion[sequenceNumber], energy.getE( outMinPu.val ), (noSeedRequired ? RnaSequence::lastPos : seedBP.val ) );
acc.decomposeByMaxED( tRegion[sequenceNumber], energy.getE( outMinPu.val ), (noSeedRequired ? 1 : seedBP.val ) );
}

return tRegion.at(sequenceNumber);
Expand Down Expand Up @@ -2776,6 +2782,3 @@ getPersonality( int argc, char ** argv )


////////////////////////////////////////////////////////////////////////////



Loading
Loading