Skip to content

Commit 72ee0fe

Browse files
committed
refactor(web): make SearchResult's node-path accessible
This was formerly part of #14987 and has been extracted for ease of review. Build-bot: skip build:web Test-bot: skip
1 parent f16542f commit 72ee0fe

File tree

2 files changed

+19
-42
lines changed

2 files changed

+19
-42
lines changed

web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { LexicalModelTypes } from '@keymanapp/common-types';
55

66
import { ClassicalDistanceCalculation } from './classical-calculation.js';
77
import { ExecutionTimer, STANDARD_TIME_BETWEEN_DEFERS } from './execution-timer.js';
8-
import { QUEUE_NODE_COMPARATOR, SearchQuotientSpur } from './search-quotient-spur.js';
9-
import { PathResult } from './search-quotient-node.js';
8+
import { PathResult, SearchQuotientNode } from './search-quotient-node.js';
109
import { subsetByChar, subsetByInterval, mergeSubset, TransformSubset } from '../transform-subsets.js';
1110
import TransformUtils from '../transformUtils.js';
1211

@@ -571,22 +570,22 @@ export class SearchNode {
571570
}
572571

573572
export class SearchResult {
574-
private resultNode: SearchNode;
573+
readonly node: SearchNode;
575574

576575
constructor(node: SearchNode) {
577-
this.resultNode = node;
576+
this.node = node;
578577
}
579578

580579
get inputSequence(): ProbabilityMass<Transform>[] {
581-
return this.resultNode.priorInput;
580+
return this.node.priorInput;
582581
}
583582

584583
get matchSequence(): TraversableToken<string>[] {
585-
return this.resultNode.calculation.matchSequence.map((char, i) => ({key: char, traversal: this.resultNode.matchedTraversals[i+1]}));
584+
return this.node.calculation.matchSequence.map((char, i) => ({key: char, traversal: this.node.matchedTraversals[i+1]}));
586585
};
587586

588587
get matchString(): string {
589-
return this.resultNode.resultKey;
588+
return this.node.resultKey;
590589
}
591590

592591
/**
@@ -597,15 +596,15 @@ export class SearchResult {
597596
* `totalCost`.)
598597
*/
599598
get knownCost(): number {
600-
return this.resultNode.editCount;
599+
return this.node.editCount;
601600
}
602601

603602
/**
604603
* Gets the "input sampling cost" of the edge, which should be considered as the
605604
* negative log-likelihood of the input path taken to reach the node.
606605
*/
607606
get inputSamplingCost(): number {
608-
return this.resultNode.inputSamplingCost;
607+
return this.node.inputSamplingCost;
609608
}
610609

611610
/**
@@ -615,41 +614,34 @@ export class SearchResult {
615614
* to the resulting output.
616615
*/
617616
get totalCost(): number {
618-
return this.resultNode.currentCost;
617+
return this.node.currentCost;
619618
}
620619

621620
get finalTraversal(): LexiconTraversal {
622-
return this.resultNode.currentTraversal;
621+
return this.node.currentTraversal;
623622
}
624623

625624
get spaceId(): number {
626-
return this.resultNode.spaceId;
625+
return this.node.spaceId;
627626
}
628627
}
629628

630629
// Current best guesstimate of how compositor will retrieve ideal corrections.
631-
export async function *getBestMatches(searchSpace: SearchQuotientSpur, timer: ExecutionTimer): AsyncGenerator<SearchResult> {
630+
export async function *getBestMatches(searchSpace: SearchQuotientNode, timer: ExecutionTimer): AsyncGenerator<SearchResult> {
632631
let currentReturns: {[resultKey: string]: SearchNode} = {};
633632

634633
// Stage 1 - if we already have extracted results, build a queue just for them and iterate over it first.
635-
const returnedValues = Object.values(searchSpace.returnedValues);
634+
const returnedValues = Object.values(searchSpace.previousResults);
636635
if(returnedValues.length > 0) {
637-
let preprocessedQueue = new PriorityQueue<SearchNode>(QUEUE_NODE_COMPARATOR, returnedValues);
636+
let preprocessedQueue = new PriorityQueue<SearchResult>((a, b) => a.totalCost - b.totalCost, returnedValues);
638637

639638
while(preprocessedQueue.count > 0) {
640639
const entryFromCache = timer.time(() => {
641640
let entry = preprocessedQueue.dequeue();
642641

643-
// Is the entry a reasonable result?
644-
if(entry.isFullReplacement) {
645-
// If the entry's 'match' fully replaces the input string, we consider it
646-
// unreasonable and ignore it.
647-
return null;
648-
}
649-
650-
currentReturns[entry.resultKey] = entry;
642+
currentReturns[entry.node.resultKey] = entry.node;
651643
// Do not track yielded time.
652-
return new SearchResult(entry);
644+
return entry;
653645
}, TimedTaskTypes.CACHED_RESULT);
654646

655647
if(entryFromCache) {

web/src/engine/predictive-text/worker-thread/src/main/correction/search-quotient-spur.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,11 @@ export class SearchQuotientSpur implements SearchQuotientNode {
3333
private parentPath: SearchQuotientSpur;
3434
readonly spaceId: number;
3535

36-
// We use an array and not a PriorityQueue b/c batch-heapifying at a single point in time
37-
// is cheaper than iteratively building a priority queue.
38-
/**
39-
* This tracks all paths that have reached the end of a viable input-matching path - even
40-
* those of lower cost that produce the same correction as other paths.
41-
*
42-
* When new input is received, its entries are then used to append edges to the path in order
43-
* to find potential paths to reach a new viable end.
44-
*/
45-
private completedPaths?: SearchNode[] = [];
46-
4736
/**
4837
* Marks all results that have already been returned since the last input was received.
4938
* Is cleared after .addInput() calls.
5039
*/
51-
public returnedValues?: {[resultKey: string]: SearchNode} = {}; // TODO: make it private again!
40+
private returnedValues?: {[resultKey: string]: SearchNode} = {};
5241

5342
/**
5443
* Provides a heuristic for the base cost at each depth if the best
@@ -75,15 +64,14 @@ export class SearchQuotientSpur implements SearchQuotientNode {
7564
this.lowestCostAtDepth = parentNode.lowestCostAtDepth.concat(logTierCost);
7665
this.parentPath = parentNode;
7766

78-
this.addEdgesForNodes(parentNode.completedPaths);
67+
this.addEdgesForNodes(parentNode.previousResults.map(v => v.node));
7968

8069
return;
8170
}
8271

8372
const model = arg1 as LexicalModel;
8473
this.selectionQueue.enqueue(new SearchNode(model.traverseFromRoot(), this.spaceId, t => model.toKey(t)));
8574
this.lowestCostAtDepth = [];
86-
this.completedPaths = [];
8775
}
8876

8977
/**
@@ -236,9 +224,6 @@ export class SearchQuotientSpur implements SearchQuotientNode {
236224
this.selectionQueue.enqueueAll(insertionEdges);
237225
}
238226

239-
// It was the final tier - store the node for future reference.
240-
this.completedPaths?.push(currentNode);
241-
242227
if((this.returnedValues[currentNode.resultKey]?.currentCost ?? Number.POSITIVE_INFINITY) > currentNode.currentCost) {
243228
this.returnedValues[currentNode.resultKey] = currentNode;
244229
} else {
@@ -255,6 +240,6 @@ export class SearchQuotientSpur implements SearchQuotientNode {
255240
}
256241

257242
public get previousResults(): SearchResult[] {
258-
return Object.values(this.returnedValues).map(v => new SearchResult(v));
243+
return Object.values(this.returnedValues ?? {}).map(v => new SearchResult(v));
259244
}
260245
}

0 commit comments

Comments
 (0)