diff --git a/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts index a5285f754ba..14df6264b5a 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts @@ -5,8 +5,7 @@ import { LexicalModelTypes } from '@keymanapp/common-types'; import { ClassicalDistanceCalculation } from './classical-calculation.js'; import { ExecutionTimer, STANDARD_TIME_BETWEEN_DEFERS } from './execution-timer.js'; -import { QUEUE_NODE_COMPARATOR, SearchQuotientSpur } from './search-quotient-spur.js'; -import { PathResult } from './search-quotient-node.js'; +import { PathResult, SearchQuotientNode } from './search-quotient-node.js'; import { subsetByChar, subsetByInterval, mergeSubset, TransformSubset } from '../transform-subsets.js'; import TransformUtils from '../transformUtils.js'; @@ -571,22 +570,22 @@ export class SearchNode { } export class SearchResult { - private resultNode: SearchNode; + readonly node: SearchNode; constructor(node: SearchNode) { - this.resultNode = node; + this.node = node; } get inputSequence(): ProbabilityMass[] { - return this.resultNode.priorInput; + return this.node.priorInput; } get matchSequence(): TraversableToken[] { - return this.resultNode.calculation.matchSequence.map((char, i) => ({key: char, traversal: this.resultNode.matchedTraversals[i+1]})); + return this.node.calculation.matchSequence.map((char, i) => ({key: char, traversal: this.node.matchedTraversals[i+1]})); }; get matchString(): string { - return this.resultNode.resultKey; + return this.node.resultKey; } /** @@ -597,7 +596,7 @@ export class SearchResult { * `totalCost`.) */ get knownCost(): number { - return this.resultNode.editCount; + return this.node.editCount; } /** @@ -605,7 +604,7 @@ export class SearchResult { * negative log-likelihood of the input path taken to reach the node. */ get inputSamplingCost(): number { - return this.resultNode.inputSamplingCost; + return this.node.inputSamplingCost; } /** @@ -615,41 +614,34 @@ export class SearchResult { * to the resulting output. */ get totalCost(): number { - return this.resultNode.currentCost; + return this.node.currentCost; } get finalTraversal(): LexiconTraversal { - return this.resultNode.currentTraversal; + return this.node.currentTraversal; } get spaceId(): number { - return this.resultNode.spaceId; + return this.node.spaceId; } } // Current best guesstimate of how compositor will retrieve ideal corrections. -export async function *getBestMatches(searchSpace: SearchQuotientSpur, timer: ExecutionTimer): AsyncGenerator { +export async function *getBestMatches(searchSpace: SearchQuotientNode, timer: ExecutionTimer): AsyncGenerator { let currentReturns: {[resultKey: string]: SearchNode} = {}; // Stage 1 - if we already have extracted results, build a queue just for them and iterate over it first. - const returnedValues = Object.values(searchSpace.returnedValues); + const returnedValues = Object.values(searchSpace.previousResults); if(returnedValues.length > 0) { - let preprocessedQueue = new PriorityQueue(QUEUE_NODE_COMPARATOR, returnedValues); + let preprocessedQueue = new PriorityQueue((a, b) => a.totalCost - b.totalCost, returnedValues); while(preprocessedQueue.count > 0) { const entryFromCache = timer.time(() => { let entry = preprocessedQueue.dequeue(); - // Is the entry a reasonable result? - if(entry.isFullReplacement) { - // If the entry's 'match' fully replaces the input string, we consider it - // unreasonable and ignore it. - return null; - } - - currentReturns[entry.resultKey] = entry; + currentReturns[entry.node.resultKey] = entry.node; // Do not track yielded time. - return new SearchResult(entry); + return entry; }, TimedTaskTypes.CACHED_RESULT); if(entryFromCache) { diff --git a/web/src/engine/predictive-text/worker-thread/src/main/correction/search-quotient-spur.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/search-quotient-spur.ts index fdaa77d8eb2..b8b2a4d5456 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/correction/search-quotient-spur.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/correction/search-quotient-spur.ts @@ -33,22 +33,11 @@ export class SearchQuotientSpur implements SearchQuotientNode { private parentPath: SearchQuotientSpur; readonly spaceId: number; - // We use an array and not a PriorityQueue b/c batch-heapifying at a single point in time - // is cheaper than iteratively building a priority queue. - /** - * This tracks all paths that have reached the end of a viable input-matching path - even - * those of lower cost that produce the same correction as other paths. - * - * When new input is received, its entries are then used to append edges to the path in order - * to find potential paths to reach a new viable end. - */ - private completedPaths?: SearchNode[] = []; - /** * Marks all results that have already been returned since the last input was received. * Is cleared after .addInput() calls. */ - public returnedValues?: {[resultKey: string]: SearchNode} = {}; // TODO: make it private again! + private returnedValues?: {[resultKey: string]: SearchNode} = {}; /** * Provides a heuristic for the base cost at each depth if the best @@ -75,7 +64,7 @@ export class SearchQuotientSpur implements SearchQuotientNode { this.lowestCostAtDepth = parentNode.lowestCostAtDepth.concat(logTierCost); this.parentPath = parentNode; - this.addEdgesForNodes(parentNode.completedPaths); + this.addEdgesForNodes(parentNode.previousResults.map(v => v.node)); return; } @@ -83,7 +72,6 @@ export class SearchQuotientSpur implements SearchQuotientNode { const model = arg1 as LexicalModel; this.selectionQueue.enqueue(new SearchNode(model.traverseFromRoot(), this.spaceId, t => model.toKey(t))); this.lowestCostAtDepth = []; - this.completedPaths = []; } /** @@ -236,9 +224,6 @@ export class SearchQuotientSpur implements SearchQuotientNode { this.selectionQueue.enqueueAll(insertionEdges); } - // It was the final tier - store the node for future reference. - this.completedPaths?.push(currentNode); - if((this.returnedValues[currentNode.resultKey]?.currentCost ?? Number.POSITIVE_INFINITY) > currentNode.currentCost) { this.returnedValues[currentNode.resultKey] = currentNode; } else { @@ -255,6 +240,6 @@ export class SearchQuotientSpur implements SearchQuotientNode { } public get previousResults(): SearchResult[] { - return Object.values(this.returnedValues).map(v => new SearchResult(v)); + return Object.values(this.returnedValues ?? {}).map(v => new SearchResult(v)); } } \ No newline at end of file