Ejemplo n.º 1
0
bool
SVLocus::
isNoiseNode(
    const unsigned minMergeEdgeCount,
    const NodeIndexType nodeIndex) const
{
    const SVLocusNode& node(getNode(nodeIndex));
    const SVLocusEdgeManager edgeMap(node.getEdgeManager());
    for (const SVLocusEdgesType::value_type& edge : edgeMap.getMap())
    {
        if (edge.second.getCount() >= minMergeEdgeCount) return false;
        if (getEdge(edge.first,nodeIndex).getCount() >= minMergeEdgeCount) return false;
    }
    return true;
}
Ejemplo n.º 2
0
void
SVLocus::
clearNodeEdges(NodeIndexType nodePtr)
{
    using namespace illumina::common;

    static const std::string logtag("SVLocus::clearNodeEdges");

#ifdef DEBUG_SVL
    log_os << logtag << " from nodeIndex: " << nodePtr << "\n";
#endif

    SVLocusNode& node(getNode(nodePtr));
    const SVLocusEdgeManager edgeMap(node.getEdgeManager());
    for (const SVLocusEdgesType::value_type& edgeIter : edgeMap.getMap())
    {

#ifdef DEBUG_SVL
        log_os << logtag << " clearing remote Index: " << edgeIter.first << "\n";
#endif
        // skip self edge (otherwise we invalidate iterators in this foreach loop)
        if (edgeIter.first == nodePtr) continue;

        SVLocusNode& remoteNode(getNode(edgeIter.first));
        try
        {
            remoteNode.eraseEdge(nodePtr);
        }
        catch (illumina::common::ExceptionData& e)
        {
            std::ostringstream oss;
            oss << "ERROR: " << logtag << " no return edge on remote node.\n"
                << "\tlocal_node: " << node
                << "\tremote_node: " << remoteNode;
            e << illumina::common::ExceptionMsg(oss.str());
            throw;
        }
    }

    node.clear();
}
Ejemplo n.º 3
0
void
SVLocus::
mergeNode(
    const NodeIndexType fromIndex,
    const NodeIndexType toIndex,
    flyweight_observer_t* obs)
{
    using namespace illumina::common;

    assert(fromIndex != toIndex);

#ifdef DEBUG_SVL
    static const std::string logtag("SVLocus::mergeNode");
    log_os << logtag << " from: " << fromIndex << " to: " << toIndex << " size: " << size() << "\n";
#endif

    SVLocusNode& fromNode(getNode(fromIndex));
    SVLocusNode& toNode(getNode(toIndex));

#ifdef DEBUG_SVL
    log_os << logtag << " BEFORE fromNode: " << fromNode;
    log_os << logtag << " BEFORE toNode: " << toNode;
#endif

    if (fromNode.getInterval().tid != toNode.getInterval().tid)
    {
        std::ostringstream oss;
        oss << "ERROR: Attempting to merge nodes on different chromosomes\n"
            << "\tNode1: " << fromNode
            << "\tNode2: " << toNode;
        BOOST_THROW_EXCEPTION(LogicException(oss.str()));
    }

    notifyDelete(obs,toIndex);

    toNode.setIntervalRange(merge_range(toNode.getInterval().range,fromNode.getInterval().range));
    const bool isToCount(toNode.isOutCount());
    const bool isFromCount(fromNode.isOutCount());
    if     ((! isToCount) && (isFromCount))
    {
        toNode.setEvidenceRange(fromNode.getEvidenceRange());
    }
    else if ((! isFromCount) && (isToCount))
    {
        // pass (keep toNode value as is)
    }
    else
    {
        toNode.setEvidenceRange(merge_range(toNode.getEvidenceRange(),fromNode.getEvidenceRange()));
    }

    notifyAdd(obs,toIndex);

    // now take all fromNode edges and 'redirect' them to the toNode index
    //
    const SVLocusEdgeManager edgeMap(fromNode.getEdgeManager());
    for (const SVLocusEdgesType::value_type& fromNodeEdgeIter : edgeMap.getMap())
    {
        // alias value_type components (not required, but makes the logic easier to follow):
        const NodeIndexType& fromNodeEdgeIndex(fromNodeEdgeIter.first);
        const SVLocusEdge* fromNodeEdgePtr(&(fromNodeEdgeIter.second));

#ifdef DEBUG_SVL
        // is this edge between the to and from nodes?
        const bool isToFromEdge(fromNodeEdgeIndex == toIndex);

        log_os << logtag << " handle fromEdge: " << _index << ":" << fromNodeEdgeIndex << " isToFromEdge: " << isToFromEdge << "\n";
#endif

        // is this a self edge of the from node?
        const bool isSelfFromEdge(fromNodeEdgeIndex == fromIndex);

        if (isSelfFromEdge)
        {
            // self-edge needs to be handled as a special case:
            toNode.mergeEdge(toIndex,*(fromNodeEdgePtr));
            continue;
        }

        // Check for the special case when there is an edge between from and to, in this case
        // the counts have to be handled so that counts in each region still approximate
        // fragment support. Normally (the chimera case) -- a single fragment will create
        // edges and nodes with weight X. If this is a non-chimera and the nodes collide and
        // merge, we want to prevent the evidence from being doubled to 2X when it should not be.
        //
        // To achieve this, we take the max edge counts from the two nodes being merged instead
        // of the sum. This is an approximate solution, but very simple to add into the
        // graph without blowing up per-node/edge storage.
        //
        const bool isFromToEdge(fromNodeEdgeIndex == toIndex);
        unsigned mergeCount(0);
        if (isFromToEdge)
        {
            auto getNodeEdgeCount = [](const SVLocusNode& node, const NodeIndexType index) -> unsigned
            {
                if (! node.isEdge(index)) return 0u;
                return node.getEdge(index).getCount();
            };

            // determine what the override edge count should be:
            const unsigned fromCount(fromNodeEdgePtr->getCount());
            const unsigned toCount(getNodeEdgeCount(toNode,fromIndex));
            const unsigned maxCount(std::max(fromCount,toCount));
            mergeCount = getNodeEdgeCount(toNode,toIndex) + maxCount;
        }

        // update local edge:
        toNode.mergeEdge(fromNodeEdgeIndex,*(fromNodeEdgePtr));

        if (isFromToEdge)
        {
            toNode.setEdgeCount(toIndex,mergeCount);
            toNode.setEdgeCount(fromIndex,0);
        }

        // update remote inputNodeEdgeIter
        {
            SVLocusNode& remoteNode(getNode(fromNodeEdgeIndex));
            try
            {
                const SVLocusEdge remoteEdge(remoteNode.getEdge(fromIndex));
                remoteNode.mergeEdge(toIndex, remoteEdge);
            }
            catch (illumina::common::ExceptionData& e)
            {
                // decorate an in-flight exception:
                std::ostringstream oss;
                oss << "ERROR: Can't find return edge to node index: " << _index << ":" << fromIndex << " in remote node index: " << _index << ":" << fromNodeEdgeIter.first << "\n"
                    << "\tlocal_node: " << fromNode
                    << "\tremote_node: " << remoteNode;
                e << illumina::common::ExceptionMsg(oss.str());
                throw;
            }
        }
    }

#ifdef DEBUG_SVL
    log_os << logtag << " AFTER toNode: " << toNode;
#endif

    clearNodeEdges(fromIndex);
}
Ejemplo n.º 4
0
void
SVLocus::
eraseNode(
    const NodeIndexType nodePtr,
    flyweight_observer_t* obs)
{
    using namespace illumina::common;

    if (nodePtr >= _graph.size()) return;

    clearNodeEdges(nodePtr);

    NodeIndexType fromPtr(_graph.size()-1);

#ifdef DEBUG_SVL
    static const std::string logtag("SVLocus::eraseNode");
    log_os << logtag << " " << _index << ":" << nodePtr << " transfer_in: " << _index << ":" << fromPtr << " \n";

    log_os << logtag << " BEFORE: " << getNode(nodePtr) << "\n";
#endif

    if (fromPtr != nodePtr)
    {
#ifdef DEBUG_SVL
        log_os << logtag << " transfer_in: BEFORE: " << getNode(fromPtr) << "\n";
#endif
        // reassign fromNode's remote edges before shifting its address:
        //
        bool isHandleSelfEdge(false);
        SVLocusNode& fromNode(getNode(fromPtr));
        const SVLocusEdgeManager edgeMap(fromNode.getEdgeManager());
        for (const SVLocusEdgesType::value_type& edgeIter : edgeMap.getMap())
        {
            const bool isSelfEdge(edgeIter.first == fromPtr);

            if (isSelfEdge)
            {
                // have to handle this outside the foreach loop so that we
                // don't invalidate our iterators:
                isHandleSelfEdge=true;
                continue;
            }

            SVLocusNode& remoteNode(getNode(edgeIter.first));
            remoteNode.moveEdge(fromPtr,nodePtr);
        }

        if (isHandleSelfEdge)
        {
            fromNode.moveEdge(fromPtr,nodePtr);
        }

        notifyDelete(obs,nodePtr);
        _graph[nodePtr] = _graph[fromPtr];
        notifyAdd(obs,nodePtr);

#ifdef DEBUG_SVL
        log_os << logtag << " transfer_in: AFTER: " << getNode(nodePtr) << "\n";
#endif
    }
    notifyDelete(obs,fromPtr);
    _graph.resize(fromPtr);
}
Ejemplo n.º 5
0
unsigned
SVLocus::
cleanNodeCore(
    const unsigned minMergeEdgeCount,
    const NodeIndexType nodeIndex,
    std::set<NodeIndexType>& emptyNodes)
{
#ifdef DEBUG_SVL
    static const std::string logtag("SVLocus::cleanNodeCore");
    log_os << logtag << " nodeAddy: " << _index << ":" << nodeIndex << "\n";
#endif

    unsigned totalCleaned(0);
    SVLocusNode& queryNode(getNode(nodeIndex));

    std::vector<NodeIndexType> eraseEdges;
    const SVLocusEdgeManager edgeMap(queryNode.getEdgeManager());
    for (const SVLocusEdgesType::value_type& edgeIter : edgeMap.getMap())
    {
        const SVLocusEdge* edgePtr(&(edgeIter.second));
        if (0 != edgePtr->getCount())
        {
            if (edgePtr->getCount() < minMergeEdgeCount)
            {
                // clean criteria met -- go ahead and erase edge count:
                totalCleaned += edgePtr->getCount();
                queryNode.setEdgeCount(edgeIter.first,0);

                // we've just snuck around the const iterator by calling the clearEdge function against this edge,
                // so we have to fix this by hand:
                edgePtr=&(queryNode.getEdge(edgeIter.first));
            }
        }

        if (0 == edgePtr->getCount())
        {
            // if the out edge count is zero, see if the in-edge count is also zero --
            // if so, erase edge
            //
            const SVLocusEdge& fromRemoteEdge(getEdge(edgeIter.first,nodeIndex));
            if (0 == fromRemoteEdge.getCount())
            {
                eraseEdges.push_back(edgeIter.first);

                // also check to see if the remote node will be empty after
                // this edge deletion:
                const SVLocusNode& remoteNode(getNode(edgeIter.first));
                if ((! remoteNode.isOutCount()) &&
                        (1 == remoteNode.size()))
                {
                    emptyNodes.insert(edgeIter.first);
                }
            }
        }
    }

    // delete empty edges:
    for (const NodeIndexType toIndex : eraseEdges)
    {
#ifdef DEBUG_SVL
        log_os << logtag << " deleting edge: " << _index << ":" << nodeIndex << "->" << _index << ":" << toIndex << "\n";
#endif
        eraseEdgePair(nodeIndex,toIndex);
    }

    // if true add the target node to the erase list:
    if ((queryNode.empty()) && (! queryNode.isOutCount()))
    {
        emptyNodes.insert(nodeIndex);
    }

#ifdef DEBUG_SVL
    log_os << logtag << " emptyEdges:\n";
    for (const NodeIndexType toIndex : eraseEdges)
    {
        log_os << logtag << "\tedge: " << _index << ":" << nodeIndex << "->" << _index << ":" << toIndex << "\n";
    }

    log_os << "cleanNodeCore emptyNodes\n";
    for (const NodeIndexType nodeIndex2 : emptyNodes)
    {
        log_os << logtag << "\tnodeAddy: " << _index << ":" << nodeIndex2 << "\n";
    }

    log_os << logtag << " totalCleaned: " << totalCleaned << "\n";
#endif

    return totalCleaned;
}
Ejemplo n.º 6
0
void
EdgeRetrieverJumpBin::
advanceEdge()
{
    typedef SVLocusEdgesType::const_iterator edgeiter_t;

    const bool isFilterNodes(_graphNodeMaxEdgeCount>0);

    // advance to next edge unless this is the first iteration:
    if (0 != _edgeIndex) _edge.nodeIndex2++;

    while (_edge.locusIndex < _set.size())
    {
        const SVLocus& locus(_set.getLocus(_edge.locusIndex));
        while (_edge.nodeIndex1<locus.size())
        {
            const SVLocusNode& node1(locus.getNode(_edge.nodeIndex1));
            const bool isEdgeFilterNode1(isFilterNodes && (node1.size()>_graphNodeMaxEdgeCount));
            const SVLocusEdgeManager node1Manager(node1.getEdgeManager());
            edgeiter_t edgeIter(node1Manager.getMap().lower_bound(_edge.nodeIndex2));
            const edgeiter_t edgeIterEnd(node1Manager.getMap().cend());

            for (; edgeIter != edgeIterEnd; ++edgeIter)
            {
                _edge.nodeIndex2 = edgeIter->first;

                // if both nodes have high edge counts we filter out the edge:
                if (isEdgeFilterNode1)
                {
                    const SVLocusNode& node2(locus.getNode(_edge.nodeIndex2));
                    const bool isEdgeFilterNode2(node2.size()>_graphNodeMaxEdgeCount);
                    if (isEdgeFilterNode2)
                    {
#ifdef DEBUG_EDGER
                        log_os << "EDGER: advance filtering @ index: " << _edgeIndex << "\n";
#endif
                        continue;
                    }
                }


                const unsigned firstTargetBin(_edgeIndex%_binCount);
                unsigned targetBin(firstTargetBin);

                do
                {
                    if (_binTotalCount[targetBin] < _avgBinTotalCount) break;
                    targetBin=((targetBin+1)%_binCount);
                }
                while (targetBin != firstTargetBin);

#ifdef DEBUG_EDGER
                log_os << "EDGER: edgeIndex,ftarget,target,binIndex " << _edgeIndex << " " << firstTargetBin << " " << targetBin << " " << _binIndex << "\n";
#endif

                _edgeIndex++;
                if (targetBin == _binIndex)
                {
                    // get edge count:
                    unsigned edgeCount(edgeIter->second.getCount());
                    {
                        const bool isSelfEdge(edgeIter->first == _edge.nodeIndex1);
                        if (! isSelfEdge) edgeCount += locus.getEdge(edgeIter->first,_edge.nodeIndex1).getCount();
                    }

                    _binTotalCount[targetBin] += edgeCount;
                    return;
                }
            }
            ++_edge.nodeIndex1;
            _edge.nodeIndex2=_edge.nodeIndex1;
        }
        ++_edge.locusIndex;
        _edge.nodeIndex1=0;
        _edge.nodeIndex2=0;
    }
}
Ejemplo n.º 7
0
void
EdgeRetrieverBin::
jumpToFirstEdge()
{
    typedef SVLocusEdgesType::const_iterator edgeiter_t;

    const bool isFilterNodes(_graphNodeMaxEdgeCount>0);

    // first catch headCount up to the begin edge if required:
    while (true)
    {
        assert(_edge.locusIndex<_set.size());

        const SVLocus& locus(_set.getLocus(_edge.locusIndex));
        const unsigned locusObservationCount(locus.totalObservationCount());

        if ((_headCount+locusObservationCount) > _beginCount)
        {
            while (true)
            {
                const SVLocusNode& node1(locus.getNode(_edge.nodeIndex1));
                const bool isEdgeFilterNode1(isFilterNodes && (node1.size()>_graphNodeMaxEdgeCount));

                const SVLocusEdgeManager node1Manager(node1.getEdgeManager());
                edgeiter_t edgeIter(node1Manager.getMap().lower_bound(_edge.nodeIndex1));
                const edgeiter_t edgeiterEnd(node1Manager.getMap().end());

                for (; edgeIter != edgeiterEnd; ++edgeIter)
                {
                    unsigned edgeCount(edgeIter->second.getCount());
                    const bool isSelfEdge(edgeIter->first == _edge.nodeIndex1);
                    if (! isSelfEdge) edgeCount += locus.getEdge(edgeIter->first,_edge.nodeIndex1).getCount();
                    _headCount += edgeCount;
                    if (_headCount > _beginCount)
                    {
                        _edge.nodeIndex2 = edgeIter->first;

                        // if both nodes have high edge counts we filter out the edge:
                        if (isEdgeFilterNode1)
                        {
                            const SVLocusNode& node2(locus.getNode(_edge.nodeIndex2));
                            const bool isEdgeFilterNode2(node2.size()>_graphNodeMaxEdgeCount);
                            if (isEdgeFilterNode2)
                            {
#ifdef DEBUG_EDGER
                                log_os << "EDGER: jump filtering @ hc: " << _headCount << "\n";
#endif
                                continue;
                            }
                        }
                        return;
                    }
                }

                _edge.nodeIndex1++;
            }
            assert(_headCount >= _beginCount);
        }
        _headCount += locusObservationCount;
        _edge.locusIndex++;
    }

    assert(false && "jumpToFirstEdge: invalid state");
}
Ejemplo n.º 8
0
void
EdgeRetrieverBin::
advanceEdge()
{
    typedef SVLocusEdgesType::const_iterator edgeiter_t;

    const bool isFilterNodes(_graphNodeMaxEdgeCount>0);

    if (0 != _headCount) _edge.nodeIndex2++;

    bool isLastFiltered(false);

    while (true)
    {
        if (isLastFiltered && (_edge.locusIndex == _set.size()))
        {
            _headCount = (_endCount + 1);
            return;
        }
        assert(_edge.locusIndex<_set.size());

        const SVLocus& locus(_set.getLocus(_edge.locusIndex));
        while (_edge.nodeIndex1<locus.size())
        {
            const SVLocusNode& node1(locus.getNode(_edge.nodeIndex1));
            const bool isEdgeFilterNode1(isFilterNodes && (node1.size()>_graphNodeMaxEdgeCount));
            const SVLocusEdgeManager node1Manager(node1.getEdgeManager());
            edgeiter_t edgeIter(node1Manager.getMap().lower_bound(_edge.nodeIndex2));
            const edgeiter_t edgeIterEnd(node1Manager.getMap().end());

            for (; edgeIter != edgeIterEnd; ++edgeIter)
            {
                unsigned edgeCount(edgeIter->second.getCount());
                const bool isSelfEdge(edgeIter->first == _edge.nodeIndex1);
                if (! isSelfEdge) edgeCount += locus.getEdge(edgeIter->first,_edge.nodeIndex1).getCount();
                _headCount += edgeCount;
                _edge.nodeIndex2 = edgeIter->first;

                // if both nodes have high edge counts we filter out the edge:
                if (isEdgeFilterNode1)
                {
                    const SVLocusNode& node2(locus.getNode(_edge.nodeIndex2));
                    const bool isEdgeFilterNode2(node2.size()>_graphNodeMaxEdgeCount);
                    if (isEdgeFilterNode2)
                    {
#ifdef DEBUG_EDGER
                        log_os << "EDGER: advance filtering @ hc: " << _headCount << "\n";
#endif
                        isLastFiltered=true;
                        continue;
                    }
                }

                return;
            }
            ++_edge.nodeIndex1;
            _edge.nodeIndex2=_edge.nodeIndex1;
        }
        ++_edge.locusIndex;
        _edge.nodeIndex1=0;
        _edge.nodeIndex2=0;
    }

    assert(false && "advanceEdge: invalid state");
}
Ejemplo n.º 9
0
void
SVLocusSet::
getNodeMergeableIntersect(
    const LocusIndexType inputLocusIndex,
    const NodeIndexType inputNodeIndex,
    const bool isInputLocusMoved,
    std::set<NodeAddressType>& mergeIntersectNodes) const
{
    //
    // TODO: There's room for significant optimization of these methods. The improvements are not trivial,
    //   but they would allow us to filter fewer nodes from being merged when node intersection counts become large.
    //

    //
    // There are two ways sets of mergeable nodes can occur:
    //
    // (1) There is a set of nodes which overlap with both input node and one
    // of the remote nodes that the input points to (ie they have a shared edge).
    // When totaled together, the edge count of this set + the inputNode edge
    // exceeds minMergeEdgeCount.
    //
    // (2) The input node either contains an edge which is greater than minMergeEdgeCount
    // or will contain such an edge due to (1), in this case the input node can be merged
    // with a locally overlapping node which also contains an edge which is greater than
    // minMergeEdgeCount. Note that in case (2) remote node intersection is not required.
    //

    const NodeAddressType inputAddy(std::make_pair(inputLocusIndex,inputNodeIndex));
    const SVLocusNode& inputNode(getNode(inputAddy));

#ifdef DEBUG_SVL
    static const std::string logtag("SVLocusSet::getNodeMergableIntersect");
    log_os << logtag << " inputNode: " << inputAddy << " " << inputNode;
    checkState();
#endif

    // reuse this intersectNodes as a temporary throughout the methods below
    std::set<NodeAddressType> intersectNodes;

    //
    // build a new index, which contains, for all nodes x which intersect the input, an
    // enumeration of the remote nodes Y connected by edges to node x (remoteIntersectNodes)
    // and a map for each node y \in Y pointing back to node x (remoteIntersectNodeToLocalNodeMap)
    //
    LocusSetIndexerType remoteIntersectNodes(*this);
    EdgeMapType remoteIntersectNodeToLocalNodeMap;

    // nodes which intersect the input and have already been certified as signal:
    std::set<NodeAddressType> signalIntersectNodes;
    {
        // get a standard intersection of the input node:
        getNodeIntersect(inputLocusIndex, inputNodeIndex, intersectNodes);

        //
        // 1. build the new remoteIntersectNodes/remoteIntersectNodeToLocalNodeMap index
        //
        for (const NodeAddressType& intersectAddy : intersectNodes)
        {
            const SVLocusNode& intersectNode(getNode(intersectAddy));

            // get the remotes of each node which intersect with the query node,
            // place these in remoteIntersectNodes
            const SVLocusEdgeManager edgeMap(intersectNode.getEdgeManager());
            for (const SVLocusEdgesType::value_type& intersectEdge : edgeMap.getMap())
            {
                // build remote <-> local indexing structures:
                NodeAddressType remoteAddy(std::make_pair(intersectAddy.first,intersectEdge.first));
                remoteIntersectNodes.data().insert(remoteAddy);
                remoteIntersectNodeToLocalNodeMap.insert(std::make_pair(remoteAddy,intersectAddy.second));
            }
        }

#ifdef DEBUG_SVL
        log_os << logtag << " remoteIntersectNodes.size(): " << remoteIntersectNodes.data().size() << "\n";
        for (const NodeAddressType& addy : remoteIntersectNodes.data())
        {
            log_os << logtag << "\tremoteIntersectNode: " << addy << " " << getNode(addy);
        }
#endif

        //
        // 2. get the signal node set:
        //
        // Note that the signal node search is not transitive b/c we have required all signal nodes
        // in the graph to have merged already.
        //
        for (const NodeAddressType& intersectAddy : intersectNodes)
        {
            if (! isNoiseNode(intersectAddy))
            {
                signalIntersectNodes.insert(intersectAddy);
            }
        }

#ifdef DEBUG_SVL
        log_os << logtag << " signalIntersect.size(): " << signalIntersectNodes.size() << "\n";
        for (const NodeAddressType& addy : signalIntersectNodes)
        {
            log_os << logtag << "\tsignalIntersectNode: " << addy << " " << getNode(addy);
        }
#endif
    }

    //
    // begin building the primary function output, mergeIntersectNodes, by enumerating all edges of the input node
    //
    mergeIntersectNodes.clear();

    // loop through each edge connected to the input node
    const SVLocusEdgeManager edgeMap(inputNode.getEdgeManager());
    for (const SVLocusEdgesType::value_type& inputEdge : edgeMap.getMap())
    {
#ifdef DEBUG_SVL
        log_os << logtag << " processing edge: " << inputAddy << "->" << inputLocusIndex << ":" << inputEdge.first << "\n";
        checkState();
#endif

        //
        // for each edge from the input node, get all intersecting edges
        //
        // 'intersecting edge' means that the nodes connected by the two edges each overlap
        //
        std::vector<EdgeInfoType> inputIntersectEdges;
        getIntersectingEdgeNodes(inputLocusIndex, inputEdge.first, remoteIntersectNodeToLocalNodeMap, remoteIntersectNodes, inputIntersectEdges);

        unsigned intersectCount(inputIntersectEdges.size());
        if (! isInputLocusMoved)
        {
            /// TODO: doc this adjustment, does this normalize the edge count to always include self-intersect?
            intersectCount++;
        }

        // isRegionCheck initiates a more detailed evidence signal threshold check process
        //
        // - The default process checks the total evidence summed over the entire
        // Node intersect set. This neglects to account for the possibility that that evidence
        // density could be low, and yet a high evidence sum could be achieved by transitive over
        // lap of many nodes.
        //
        // - The regioncheck pathway sums up evidence at each genomic region. It more accurately
        // reflects peak evidence but is somewhat slower to compute.
        //
        // Example:
        //
        // Assume each node below has an evidence count of 1.
        //
        // |---node1-----|
        //           |-----node2-----|
        //                        |-----node3---|
        //
        // Default evidence count:
        // 33333333333333333333333333333333333333
        //
        // isRegionCheck evidence count:
        // 11111111112222211111111222211111111111
        //
        //

        // peak RegionCheck count will always equal default count when 2 or fewer nodes exist,
        // so there's no reason to turn it on until we have more nodes
        const bool isRegionCheck(intersectCount>2);

        if (isRegionCheck)
        {
            _mergeRegions.clear();
        }

        // enumerate counts as part of the (non-RegionCheck) process to determine if the intersection set
        // contains sufficient evidence to initiate a merge
        unsigned mergedLocalEdgeCount(0);
        unsigned mergedRemoteEdgeCount(0);

        ///
        /// enumerate node evidence using either the default or RegionCheck process:
        ///
        auto addEdgeEvidenceCount = [&](
                                        const SVLocus& edgeLocus,
                                        const NodeIndexType localNodeIndex,
                                        const NodeIndexType remoteNodeIndex)
        {
            // total edge counts on the remote->local edge:
            const unsigned remoteEdgeCount = edgeLocus.getEdge(remoteNodeIndex,localNodeIndex).getCount();

            // total edge counts on the local->remote edge:
            const unsigned localEdgeCount = edgeLocus.getEdge(localNodeIndex,remoteNodeIndex).getCount();

            if (isRegionCheck)
            {
                const known_pos_range2& localRange(edgeLocus.getNode(localNodeIndex).getInterval().range);
                const known_pos_range2& remoteRange(edgeLocus.getNode(remoteNodeIndex).getInterval().range);

                _mergeRegions.localNodeOutbound.add(localRange,localEdgeCount);
                _mergeRegions.localNodeInbound.add(localRange,remoteEdgeCount);
                _mergeRegions.remoteNodeOutbound.add(remoteRange,remoteEdgeCount);
                _mergeRegions.remoteNodeInbound.add(remoteRange,localEdgeCount);
            }
            else
            {
                mergedLocalEdgeCount += localEdgeCount;
                mergedRemoteEdgeCount += remoteEdgeCount;
            }
        };

        for (const EdgeInfoType& edgeInfo : inputIntersectEdges)
        {
            addEdgeEvidenceCount(getLocus(edgeInfo.first.first),edgeInfo.first.second,edgeInfo.second);
        }

        // if the input hasn't been moved into the primary locus graph yet, then we need to include the inputLocus
        // in order to get an accurate edge intersection count:
        if (! isInputLocusMoved)
        {
            addEdgeEvidenceCount(getLocus(inputAddy.first),inputNodeIndex,inputEdge.first);
        }

        if (isRegionCheck)
        {
            mergedLocalEdgeCount=(std::min(_mergeRegions.localNodeOutbound.maxVal(),_mergeRegions.remoteNodeInbound.maxVal()));
            mergedRemoteEdgeCount=(std::min(_mergeRegions.localNodeInbound.maxVal(),_mergeRegions.remoteNodeOutbound.maxVal()));
        }

#ifdef DEBUG_SVL
        log_os << logtag << " isRegionCheck: " << isRegionCheck << "\n";
        log_os << logtag << " final merge counts"
               << " local: " << mergedLocalEdgeCount
               << " remote: " << mergedRemoteEdgeCount
               << "\n";
        checkState();
#endif

        if ((mergedLocalEdgeCount < getMinMergeEdgeCount()) &&
            (mergedRemoteEdgeCount < getMinMergeEdgeCount())) continue;

        //
        // Add type1 mergeable nodes:
        //
        for (const EdgeInfoType& edgeInfo : inputIntersectEdges)
        {
            mergeIntersectNodes.insert(edgeInfo.first);
        }

        /// for each type1 node, add any new intersections to the signal node set:
        ///
        /// this is not very efficient for now -- each type1 edge added in potentially
        /// expands the current node to intersect new signal nodes
        /// -- this loop looks for those new signal nodes
        ///

        {
            // this is used to search for the (rare) case where the intersection set
            // locals overlap with the intersection set remotes
            std::set<NodeAddressType> inputIntersectRemotes;
            for (const EdgeInfoType& edgeInfo : inputIntersectEdges)
            {
                inputIntersectRemotes.insert(std::make_pair(edgeInfo.first.first,edgeInfo.second));
            }

            bool isIntersectRemotes(false);

            // check both the original node and intersected nodes for intersection to
            // any of the group's remotes, and for new type2 signal intersect:
            findSignalNodes(inputLocusIndex, inputAddy, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes);
            for (const EdgeInfoType& edgeInfo : inputIntersectEdges)
            {
                findSignalNodes(inputLocusIndex, edgeInfo.first, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes);
            }

            if (isIntersectRemotes)
            {
                for (const NodeAddressType& intersectAddy : inputIntersectRemotes)
                {
#ifdef DEBUG_SVL
                    log_os << logtag << " adding ownRemote: " << intersectAddy << "\n";
#endif
                    mergeIntersectNodes.insert(intersectAddy);

                    // check to see if this adds even more signal nodes!
                    findSignalNodes(inputLocusIndex, intersectAddy, signalIntersectNodes, inputIntersectRemotes, isIntersectRemotes);
                }
            }
        }
        //
        // Add type2 mergeable nodes:
        //
        for (const NodeAddressType& signalAddy : signalIntersectNodes)
        {
            mergeIntersectNodes.insert(signalAddy);
        }
    }

#ifdef DEBUG_SVL
    log_os << logtag << " END. IntersectNodeSize: " << mergeIntersectNodes.size() << " Nodes:\n";
    for (const NodeAddressType addy : mergeIntersectNodes)
    {
        log_os << logtag << "\tInode: " << addy << "\n";
    }
#endif
}