2525
2626#include < algorithm>
2727#include < chrono>
28- #include < limits>
29- #include < stack>
3028#include < string>
3129#include < utility>
3230#include < vector>
3331
32+ #include " walk_strategy.h"
33+
3434namespace Formosa ::Gramambular2 {
3535
3636void ReadingGrid::clear () {
3737 cursor_ = 0 ;
3838 readings_.clear ();
3939 spans_.clear ();
40+ fixedSpans_.clear ();
4041}
4142
4243void ReadingGrid::setCursor (size_t cursor) {
@@ -121,83 +122,68 @@ int64_t GetEpochNowInMicroseconds() {
121122
122123} // namespace
123124
124- // Find the weightiest path in the grid graph. The path represents the most
125- // likely hidden chain of events from the observations.
126- // We use the Viterbi algorithm to compute such path.
127- // Instead of computing the path with the shortest distance, though, we compute
128- // the path with the longest distance (so the weightiest), since with log
129- // probability a larger value means a larger probability. The algorithm runs in
130- // O(|V| + |E|) time for G = (V, E) where G is a DAG. This means the walk is
131- // fairly economical even when the grid is large.
125+ void ReadingGrid::setWalkStrategy (std::shared_ptr<WalkStrategy> strategy) {
126+ walkStrategy_ = std::move (strategy);
127+ }
128+
129+ void ReadingGrid::fixSpan (size_t position, NodePtr node) {
130+ assert (node != nullptr );
131+ assert (position < readings_.size ());
132+ assert (position + node->spanningLength () <= readings_.size ());
133+ size_t newEnd = position + node->spanningLength ();
134+ auto it = fixedSpans_.begin ();
135+ while (it != fixedSpans_.end ()) {
136+ size_t existStart = it->first ;
137+ size_t existEnd = existStart + it->second ->spanningLength ();
138+ // Two spans overlap if their ranges intersect.
139+ if (existStart < newEnd && position < existEnd) {
140+ it = fixedSpans_.erase (it);
141+ } else {
142+ ++it;
143+ }
144+ }
145+ fixedSpans_[position] = std::move (node);
146+ }
147+
148+ void ReadingGrid::clearFixedSpans () {
149+ for (auto & [pos, node] : fixedSpans_) {
150+ node->reset ();
151+ }
152+ fixedSpans_.clear ();
153+ }
154+
132155ReadingGrid::WalkResult ReadingGrid::walk () {
133156 WalkResult result;
134157 if (spans_.empty ()) {
135158 return result;
136159 }
137160 int64_t start = GetEpochNowInMicroseconds ();
138161
139- // Defines a state in the DP table. This structure tracks the maximum
140- // accumulated score and the back-pointer required for path reconstruction in
141- // the Viterbi algorithm.
142- struct State {
143- size_t fromIndex = 0 ;
144- ReadingGrid::NodePtr fromNode = nullptr ;
145- double maxScore = -std::numeric_limits<double >::infinity();
146- };
147-
148- const size_t readingLen = readings_.size();
149- std::vector<State> viterbi (readingLen + 1 );
150- viterbi[0 ].maxScore = 0.0 ;
151-
152- // Iterate through the grid and compute the maximum accumulated score for each
153- // reachable position. Since the grid is a lattice where edges only point
154- // forward, processing nodes in index order is equivalent to processing them
155- // in topological order.
156- size_t reachableStates = 0 ;
157- size_t evaluatedEdges = 0 ;
158- for (size_t i = 0 ; i < readingLen; ++i) {
159- ++reachableStates;
160-
161- const ReadingGrid::Span& span = spans_[i];
162- const size_t maxSpanLen = span.maxLength ();
163-
164- for (size_t spanLen = 1 ; spanLen <= maxSpanLen; ++spanLen) {
165- const ReadingGrid::NodePtr& node = span.nodeOf (spanLen);
166- if (node == nullptr ) {
167- continue ;
168- }
169- ++evaluatedEdges;
170-
171- // Performs a relaxation on a transition. This updates the destination
172- // state if the path through the current node yields a higher score than
173- // the previously known best path. This is the core operation of the
174- // Viterbi algorithm, adapted for finding the maximum likelihood path.
175- double score = viterbi[i].maxScore + node->score ();
176- State& target = viterbi[i + spanLen];
177- if (score > target.maxScore ) {
178- target.maxScore = score;
179- target.fromNode = node;
180- target.fromIndex = i;
181- }
182- }
162+ if (!walkStrategy_) {
163+ walkStrategy_ = std::make_shared<ViterbiStrategy>();
183164 }
184- // Vertices are the reachable states
185- // Edges are the candidate word transitions
186- result.vertices = reachableStates;
187- result.edges = evaluatedEdges;
188165
189- // Reconstruct the most likely path by tracing back from the end of the grid
190- // to the root using the back-pointers
166+ const std::map<size_t , NodePtr>* fixedPtr =
167+ fixedSpans_.empty () ? nullptr : &fixedSpans_;
168+ WalkStrategy::WalkInput input{spans_, readings_.size (), fixedPtr};
169+ result.nodes = walkStrategy_->walk (input);
170+
191171 size_t totalReadingLen = 0 ;
192- for (size_t curr = readingLen; curr > 0 ; curr = viterbi[curr].fromIndex) {
193- assert (viterbi[curr].fromNode != nullptr );
194- totalReadingLen += viterbi[curr].fromNode ->spanningLength ();
195- result.nodes .emplace_back (std::move (viterbi[curr].fromNode ));
172+ size_t vertices = 0 ;
173+ for (const auto & node : result.nodes ) {
174+ totalReadingLen += node->spanningLength ();
175+ }
176+ for (size_t i = 0 , len = spans_.size (); i < len; ++i) {
177+ const Span& span = spans_[i];
178+ for (size_t j = 1 , maxSpanLen = span.maxLength (); j <= maxSpanLen; ++j) {
179+ if (span.nodeOf (j) != nullptr ) {
180+ ++vertices;
181+ }
182+ }
196183 }
197- std::reverse (result.nodes.begin(), result.nodes.end());
198- assert (totalReadingLen == readingLen);
199- result.totalReadings = totalReadingLen;
200184
185+ result.totalReadings = totalReadingLen;
186+ result.vertices = vertices;
201187 result.elapsedMicroseconds = GetEpochNowInMicroseconds () - start;
202188 return result;
203189}
@@ -292,7 +278,7 @@ void ReadingGrid::removeAffectedNodes(size_t loc) {
292278 if (spans_.empty ()) {
293279 return ;
294280 }
295- size_t affectedLength = kMaximumSpanLength - 1 ;
281+ size_t affectedLength = maxSpanLength_ - 1 ;
296282 size_t begin = loc <= affectedLength ? 0 : loc - affectedLength;
297283 size_t end = loc >= 1 ? loc - 1 : 0 ;
298284 for (size_t i = begin; i <= end; ++i) {
@@ -333,14 +319,14 @@ bool ReadingGrid::hasNodeAt(size_t loc, size_t readingLen,
333319
334320void ReadingGrid::update () {
335321 size_t begin =
336- (cursor_ <= kMaximumSpanLength ) ? 0 : cursor_ - kMaximumSpanLength ;
337- size_t end = cursor_ + kMaximumSpanLength ;
322+ (cursor_ <= maxSpanLength_ ) ? 0 : cursor_ - maxSpanLength_ ;
323+ size_t end = cursor_ + maxSpanLength_ ;
338324 if (end > readings_.size ()) {
339325 end = readings_.size ();
340326 }
341327
342328 for (size_t pos = begin; pos < end; pos++) {
343- for (size_t len = 1 ; len <= kMaximumSpanLength && pos + len <= end; len++) {
329+ for (size_t len = 1 ; len <= maxSpanLength_ && pos + len <= end; len++) {
344330 std::string combinedReading =
345331 combineReading (readings_.begin () + static_cast <ptrdiff_t >(pos),
346332 readings_.begin () + static_cast <ptrdiff_t >(pos + len));
@@ -419,7 +405,7 @@ std::vector<ReadingGrid::NodeInSpan> ReadingGrid::overlappingNodesAt(
419405 }
420406 }
421407
422- size_t begin = loc - std::min (loc, kMaximumSpanLength - 1 );
408+ size_t begin = loc - std::min (loc, maxSpanLength_ - 1 );
423409 for (size_t i = begin; i < loc; ++i) {
424410 size_t beginLen = loc - i + 1 ;
425411 size_t endLen = spans_[i].maxLength ();
@@ -541,22 +527,25 @@ std::vector<std::string> ReadingGrid::WalkResult::readingsAsStrings() const {
541527}
542528
543529void ReadingGrid::Span::clear () {
544- nodes_.fill ( nullptr );
530+ nodes_.clear ( );
545531 maxLength_ = 0 ;
546532}
547533
548534void ReadingGrid::Span::add (const ReadingGrid::NodePtr& node) {
549- assert (node->spanningLength () > 0 &&
550- node->spanningLength () <= kMaximumSpanLength );
551- nodes_[node->spanningLength () - 1 ] = node;
535+ assert (node->spanningLength () > 0 );
536+ size_t idx = node->spanningLength () - 1 ;
537+ if (idx >= nodes_.size ()) {
538+ nodes_.resize (idx + 1 );
539+ }
540+ nodes_[idx] = node;
552541 if (node->spanningLength () >= maxLength_) {
553542 maxLength_ = node->spanningLength ();
554543 }
555544}
556545
557546void ReadingGrid::Span::removeNodesOfOrLongerThan (size_t length) {
558- assert (length > 0 && length <= kMaximumSpanLength );
559- for (size_t i = length - 1 ; i < kMaximumSpanLength ; ++i) {
547+ assert (length > 0 );
548+ for (size_t i = length - 1 ; i < nodes_. size () ; ++i) {
560549 nodes_[i] = nullptr ;
561550 }
562551 maxLength_ = 0 ;
@@ -580,7 +569,10 @@ void ReadingGrid::Span::removeNodesOfOrLongerThan(size_t length) {
580569}
581570
582571ReadingGrid::NodePtr ReadingGrid::Span::nodeOf (size_t length) const {
583- assert (length > 0 && length <= kMaximumSpanLength );
572+ assert (length > 0 );
573+ if (length - 1 >= nodes_.size ()) {
574+ return nullptr ;
575+ }
584576 return nodes_[length - 1 ];
585577}
586578
0 commit comments