decode method
Future<List<FutoSwipeCandidate> >
decode(
- List<
FutoSwipePoint> trace, - FutoSwipeLayout layout, {
- List<
String> ? previousWords, - required int maxCandidates,
Ranks lexicon words for trace.
When previousWords is null, ranking uses the encoder+decoder weights.
Otherwise it adds ContextLM scores; an empty list means start of text.
Implementation
Future<List<FutoSwipeCandidate>> decode(
List<FutoSwipePoint> trace,
FutoSwipeLayout layout, {
List<String>? previousWords,
required int maxCandidates,
}) async {
final refined = await emissions(trace, layout);
// Both bundle configurations use the same pruning weights, so the beam
// does not depend on whether context is enabled.
final weights = previousWords == null ? decoderWeights : contextWeights;
final states = ctcPrefixSearch(
refined.logScores,
futoSwipeTimeSteps,
futoSwipeClassCount,
_root,
beam: _beamWidth,
gamma: weights.gammaPrune,
beta: weights.betaPrune,
);
final state = previousWords == null
? null
: await _contextStateFor(previousWords);
final ranked = <FutoSwipeCandidate>[];
for (final entry in states.entries) {
final node = entry.key;
if (!node.isWord) continue;
final length = node.text.length;
final base =
entry.value.total / math.pow(length, weights.gamma) +
weights.beta * length;
final surfaces = <(String, double)>[
for (final surface in node.surfaces)
(
surface.text,
base +
weights.lambda * surface.frequency +
(state == null
? 0
: weights.alpha * _scoreWord(state, surface.text)),
),
]..sort((a, b) => b.$2.compareTo(a.$2));
ranked.add(
FutoSwipeCandidate(
word: surfaces.first.$1,
searchKey: node.text,
alternatives: surfaces.skip(1).map((s) => s.$1),
score: surfaces.first.$2,
),
);
}
ranked.sort((a, b) => b.score.compareTo(a.score));
return ranked.take(maxCandidates).toList(growable: false);
}