@higher.archi/boe 1.0.20 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/types/rule.d.ts +1 -1
- package/dist/core/types/rule.d.ts.map +1 -1
- package/dist/engines/ensemble/index.d.ts +2 -0
- package/dist/engines/ensemble/index.d.ts.map +1 -1
- package/dist/engines/ensemble/index.js +8 -1
- package/dist/engines/ensemble/index.js.map +1 -1
- package/dist/engines/ensemble/members.d.ts +66 -0
- package/dist/engines/ensemble/members.d.ts.map +1 -0
- package/dist/engines/ensemble/members.js +86 -0
- package/dist/engines/ensemble/members.js.map +1 -0
- package/dist/engines/ranking/compiler.d.ts +12 -0
- package/dist/engines/ranking/compiler.d.ts.map +1 -0
- package/dist/engines/ranking/compiler.js +163 -0
- package/dist/engines/ranking/compiler.js.map +1 -0
- package/dist/engines/ranking/engine.d.ts +48 -0
- package/dist/engines/ranking/engine.d.ts.map +1 -0
- package/dist/engines/ranking/engine.js +89 -0
- package/dist/engines/ranking/engine.js.map +1 -0
- package/dist/engines/ranking/index.d.ts +9 -0
- package/dist/engines/ranking/index.d.ts.map +1 -0
- package/dist/engines/ranking/index.js +23 -0
- package/dist/engines/ranking/index.js.map +1 -0
- package/dist/engines/ranking/strategy.d.ts +21 -0
- package/dist/engines/ranking/strategy.d.ts.map +1 -0
- package/dist/engines/ranking/strategy.js +250 -0
- package/dist/engines/ranking/strategy.js.map +1 -0
- package/dist/engines/ranking/types.d.ts +142 -0
- package/dist/engines/ranking/types.d.ts.map +1 -0
- package/dist/engines/ranking/types.js +46 -0
- package/dist/engines/ranking/types.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/types/rule.ts +1 -1
- package/src/engines/ensemble/index.ts +9 -0
- package/src/engines/ensemble/members.ts +156 -0
- package/src/engines/ranking/compiler.ts +194 -0
- package/src/engines/ranking/engine.ts +120 -0
- package/src/engines/ranking/index.ts +46 -0
- package/src/engines/ranking/strategy.ts +333 -0
- package/src/engines/ranking/types.ts +231 -0
- package/src/index.ts +41 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensemble Member Adapter Factories
|
|
3
|
+
*
|
|
4
|
+
* Factory functions that wrap non-scoring engines (Bayesian, Fuzzy, etc.)
|
|
5
|
+
* into CustomMemberDef or ScoringMemberDef for use in ensemble rulesets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FactInput } from '../../core';
|
|
9
|
+
import type { ScoreExtractor, ConfidenceExtractor, CustomMemberDef, ScoringMemberDef } from './types';
|
|
10
|
+
import type { CompiledBayesianRuleSet, BayesianOptions, BayesianResult } from '../bayesian';
|
|
11
|
+
import type { CompiledFuzzyRuleSet, FuzzyOptions, FuzzyResult } from '../fuzzy';
|
|
12
|
+
import type { CompiledMonteCarloRuleSet, MonteCarloOptions, MonteCarloResult } from '../monte-carlo';
|
|
13
|
+
import type { CompiledExpertRuleSet, ExpertOptions, ExpertResult } from '../expert';
|
|
14
|
+
import type { CompiledScoringRuleSet, ScoringOptions } from '../scoring/types';
|
|
15
|
+
import { BayesianEngine } from '../bayesian';
|
|
16
|
+
import { FuzzyEngine } from '../fuzzy';
|
|
17
|
+
import { MonteCarloEngine } from '../monte-carlo';
|
|
18
|
+
import { ExpertEngine } from '../expert';
|
|
19
|
+
|
|
20
|
+
// ========================================
|
|
21
|
+
// Config Types
|
|
22
|
+
// ========================================
|
|
23
|
+
|
|
24
|
+
export type BayesianMemberConfig = {
|
|
25
|
+
id: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
weight: number;
|
|
28
|
+
ruleset: CompiledBayesianRuleSet;
|
|
29
|
+
extractScore: ScoreExtractor<BayesianResult>;
|
|
30
|
+
extractConfidence?: ConfidenceExtractor<BayesianResult>;
|
|
31
|
+
options?: BayesianOptions;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type FuzzyMemberConfig = {
|
|
35
|
+
id: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
weight: number;
|
|
38
|
+
ruleset: CompiledFuzzyRuleSet;
|
|
39
|
+
extractScore: ScoreExtractor<FuzzyResult>;
|
|
40
|
+
extractConfidence?: ConfidenceExtractor<FuzzyResult>;
|
|
41
|
+
options?: FuzzyOptions;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type MonteCarloMemberConfig = {
|
|
45
|
+
id: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
weight: number;
|
|
48
|
+
ruleset: CompiledMonteCarloRuleSet;
|
|
49
|
+
extractScore: ScoreExtractor<MonteCarloResult>;
|
|
50
|
+
extractConfidence?: ConfidenceExtractor<MonteCarloResult>;
|
|
51
|
+
options?: MonteCarloOptions;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ExpertMemberConfig = {
|
|
55
|
+
id: string;
|
|
56
|
+
name?: string;
|
|
57
|
+
weight: number;
|
|
58
|
+
ruleset: CompiledExpertRuleSet;
|
|
59
|
+
extractScore: ScoreExtractor<ExpertResult>;
|
|
60
|
+
extractConfidence?: ConfidenceExtractor<ExpertResult>;
|
|
61
|
+
options?: ExpertOptions;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type ScoringMemberConfig = {
|
|
65
|
+
id: string;
|
|
66
|
+
name?: string;
|
|
67
|
+
weight: number;
|
|
68
|
+
ruleset: CompiledScoringRuleSet;
|
|
69
|
+
options?: ScoringOptions;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// ========================================
|
|
73
|
+
// Factory Functions
|
|
74
|
+
// ========================================
|
|
75
|
+
|
|
76
|
+
/** Wrap a Bayesian engine as an ensemble member. Default confidence uses result.confidence. */
|
|
77
|
+
export function bayesianMember(config: BayesianMemberConfig): CustomMemberDef {
|
|
78
|
+
const {
|
|
79
|
+
id, name, weight, ruleset, extractScore,
|
|
80
|
+
extractConfidence = (r: BayesianResult) => r.confidence,
|
|
81
|
+
options
|
|
82
|
+
} = config;
|
|
83
|
+
return {
|
|
84
|
+
id, name, weight,
|
|
85
|
+
execute: (facts: FactInput[]) => {
|
|
86
|
+
const engine = new BayesianEngine();
|
|
87
|
+
for (const f of facts) engine.add(f);
|
|
88
|
+
return engine.execute(ruleset, options);
|
|
89
|
+
},
|
|
90
|
+
extractScore,
|
|
91
|
+
extractConfidence
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Wrap a Fuzzy engine as an ensemble member. Default confidence is 1.0 (outputs are crisp). */
|
|
96
|
+
export function fuzzyMember(config: FuzzyMemberConfig): CustomMemberDef {
|
|
97
|
+
const {
|
|
98
|
+
id, name, weight, ruleset, extractScore,
|
|
99
|
+
extractConfidence = () => 1.0,
|
|
100
|
+
options
|
|
101
|
+
} = config;
|
|
102
|
+
return {
|
|
103
|
+
id, name, weight,
|
|
104
|
+
execute: (facts: FactInput[]) => {
|
|
105
|
+
const engine = new FuzzyEngine();
|
|
106
|
+
for (const f of facts) engine.add(f);
|
|
107
|
+
return engine.execute(ruleset, options);
|
|
108
|
+
},
|
|
109
|
+
extractScore,
|
|
110
|
+
extractConfidence
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Wrap a Monte Carlo engine as an ensemble member. Default confidence is 1.0 (domain-specific). */
|
|
115
|
+
export function monteCarloMember(config: MonteCarloMemberConfig): CustomMemberDef {
|
|
116
|
+
const {
|
|
117
|
+
id, name, weight, ruleset, extractScore,
|
|
118
|
+
extractConfidence = () => 1.0,
|
|
119
|
+
options
|
|
120
|
+
} = config;
|
|
121
|
+
return {
|
|
122
|
+
id, name, weight,
|
|
123
|
+
execute: (facts: FactInput[]) => {
|
|
124
|
+
const engine = new MonteCarloEngine();
|
|
125
|
+
for (const f of facts) engine.add(f);
|
|
126
|
+
return engine.execute(ruleset, options);
|
|
127
|
+
},
|
|
128
|
+
extractScore,
|
|
129
|
+
extractConfidence
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Wrap an Expert engine as an ensemble member. Default confidence is 1.0 (per-conclusion only). */
|
|
134
|
+
export function expertMember(config: ExpertMemberConfig): CustomMemberDef {
|
|
135
|
+
const {
|
|
136
|
+
id, name, weight, ruleset, extractScore,
|
|
137
|
+
extractConfidence = () => 1.0,
|
|
138
|
+
options
|
|
139
|
+
} = config;
|
|
140
|
+
return {
|
|
141
|
+
id, name, weight,
|
|
142
|
+
execute: (facts: FactInput[]) => {
|
|
143
|
+
const engine = new ExpertEngine();
|
|
144
|
+
for (const f of facts) engine.add(f);
|
|
145
|
+
return engine.execute(ruleset, options);
|
|
146
|
+
},
|
|
147
|
+
extractScore,
|
|
148
|
+
extractConfidence
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Shorthand for creating a ScoringMemberDef. Consistency helper, no wrapping needed. */
|
|
153
|
+
export function scoringMember(config: ScoringMemberConfig): ScoringMemberDef {
|
|
154
|
+
const { id, name, weight, ruleset, options } = config;
|
|
155
|
+
return { id, name, weight, ruleset, options };
|
|
156
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ranking Engine Compiler
|
|
3
|
+
*
|
|
4
|
+
* Validates ranking rulesets and resolves defaults.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CompilationError } from '../../core/errors';
|
|
8
|
+
import { SEMANTIC_PRIORITY_VALUES, isSemanticPriority, type SemanticPriority } from '../utility/types';
|
|
9
|
+
|
|
10
|
+
import type { TierDefinition } from '../scoring/types';
|
|
11
|
+
import type {
|
|
12
|
+
RankingRuleSet,
|
|
13
|
+
CompiledRankingRuleSet,
|
|
14
|
+
CompiledScoreRankingRuleSet,
|
|
15
|
+
CompiledEloRankingRuleSet,
|
|
16
|
+
CompiledHeadToHeadRankingRuleSet,
|
|
17
|
+
CompiledRankingCriterion,
|
|
18
|
+
KFactorPreset
|
|
19
|
+
} from './types';
|
|
20
|
+
import { K_FACTOR_VALUES, isKFactorPreset } from './types';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Compile and validate a ranking ruleset.
|
|
24
|
+
*/
|
|
25
|
+
export function compileRankingRuleSet<T extends TierDefinition = TierDefinition>(
|
|
26
|
+
ruleSet: RankingRuleSet<T>
|
|
27
|
+
): CompiledRankingRuleSet<T> {
|
|
28
|
+
if (!ruleSet.id) {
|
|
29
|
+
throw new CompilationError('Ranking ruleset requires an id');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (ruleSet.mode !== 'ranking') {
|
|
33
|
+
throw new CompilationError(`Expected mode 'ranking', got '${ruleSet.mode}'`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!ruleSet.entityType || ruleSet.entityType.trim() === '') {
|
|
37
|
+
throw new CompilationError('entityType is required and must be non-empty');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
switch (ruleSet.strategy) {
|
|
41
|
+
case 'score':
|
|
42
|
+
return compileScore(ruleSet) as CompiledRankingRuleSet<T>;
|
|
43
|
+
case 'elo':
|
|
44
|
+
return compileElo(ruleSet as any) as CompiledRankingRuleSet<T>;
|
|
45
|
+
case 'head-to-head':
|
|
46
|
+
return compileHeadToHead(ruleSet as any) as CompiledRankingRuleSet<T>;
|
|
47
|
+
default:
|
|
48
|
+
throw new CompilationError(`Unknown ranking strategy: '${(ruleSet as any).strategy}'`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function compileScore<T extends TierDefinition>(
|
|
53
|
+
ruleSet: RankingRuleSet<T> & { strategy: 'score' }
|
|
54
|
+
): CompiledScoreRankingRuleSet<T> {
|
|
55
|
+
if (!ruleSet.scoringRuleset) {
|
|
56
|
+
throw new CompilationError('Score strategy requires a scoringRuleset');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (ruleSet.scoringRuleset.mode !== 'scoring') {
|
|
60
|
+
throw new CompilationError(`scoringRuleset must have mode 'scoring', got '${ruleSet.scoringRuleset.mode}'`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Validate tier IDs are unique
|
|
64
|
+
const tiers = ruleSet.config?.tiers;
|
|
65
|
+
if (tiers) {
|
|
66
|
+
const tierIds = new Set<string>();
|
|
67
|
+
for (const tier of tiers) {
|
|
68
|
+
if (tierIds.has(tier.id)) {
|
|
69
|
+
throw new CompilationError(`Duplicate tier id: '${tier.id}'`);
|
|
70
|
+
}
|
|
71
|
+
tierIds.add(tier.id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
id: ruleSet.id,
|
|
77
|
+
name: ruleSet.name,
|
|
78
|
+
mode: 'ranking',
|
|
79
|
+
strategy: 'score',
|
|
80
|
+
scoringRuleset: ruleSet.scoringRuleset,
|
|
81
|
+
scoringOptions: ruleSet.scoringOptions,
|
|
82
|
+
entityType: ruleSet.entityType,
|
|
83
|
+
config: {
|
|
84
|
+
direction: ruleSet.config?.direction ?? 'highest-first',
|
|
85
|
+
tiers: ruleSet.config?.tiers
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function compileElo(
|
|
91
|
+
ruleSet: RankingRuleSet & { strategy: 'elo' }
|
|
92
|
+
): CompiledEloRankingRuleSet {
|
|
93
|
+
const config = ruleSet.config ?? {};
|
|
94
|
+
|
|
95
|
+
// Resolve kFactor
|
|
96
|
+
let kFactor: number;
|
|
97
|
+
if (config.kFactor === undefined) {
|
|
98
|
+
kFactor = K_FACTOR_VALUES['standard'];
|
|
99
|
+
} else if (isKFactorPreset(config.kFactor)) {
|
|
100
|
+
kFactor = K_FACTOR_VALUES[config.kFactor as KFactorPreset];
|
|
101
|
+
} else if (typeof config.kFactor === 'number') {
|
|
102
|
+
if (config.kFactor <= 0) {
|
|
103
|
+
throw new CompilationError('kFactor must be a positive number');
|
|
104
|
+
}
|
|
105
|
+
kFactor = config.kFactor;
|
|
106
|
+
} else {
|
|
107
|
+
throw new CompilationError(`Invalid kFactor: '${config.kFactor}'`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const initialRating = config.initialRating ?? 1500;
|
|
111
|
+
if (initialRating <= 0) {
|
|
112
|
+
throw new CompilationError('initialRating must be a positive number');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
id: ruleSet.id,
|
|
117
|
+
name: ruleSet.name,
|
|
118
|
+
mode: 'ranking',
|
|
119
|
+
strategy: 'elo',
|
|
120
|
+
entityType: ruleSet.entityType,
|
|
121
|
+
matchType: (ruleSet as any).matchType ?? 'MatchResult',
|
|
122
|
+
config: {
|
|
123
|
+
initialRating,
|
|
124
|
+
kFactor,
|
|
125
|
+
direction: config.direction ?? 'highest-first'
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function compileHeadToHead(
|
|
131
|
+
ruleSet: RankingRuleSet & { strategy: 'head-to-head' }
|
|
132
|
+
): CompiledHeadToHeadRankingRuleSet {
|
|
133
|
+
const criteria = (ruleSet as any).criteria;
|
|
134
|
+
|
|
135
|
+
if (!criteria || !Array.isArray(criteria) || criteria.length === 0) {
|
|
136
|
+
throw new CompilationError('Head-to-head strategy requires at least one criterion');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Validate no duplicate criterion IDs
|
|
140
|
+
const criterionIds = new Set<string>();
|
|
141
|
+
for (const c of criteria) {
|
|
142
|
+
if (!c.id) {
|
|
143
|
+
throw new CompilationError('Each criterion requires an id');
|
|
144
|
+
}
|
|
145
|
+
if (criterionIds.has(c.id)) {
|
|
146
|
+
throw new CompilationError(`Duplicate criterion id: '${c.id}'`);
|
|
147
|
+
}
|
|
148
|
+
criterionIds.add(c.id);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Resolve semantic weights to numeric
|
|
152
|
+
const resolvedCriteria: CompiledRankingCriterion[] = criteria.map((c: any) => {
|
|
153
|
+
let weight: number;
|
|
154
|
+
if (isSemanticPriority(c.weight)) {
|
|
155
|
+
weight = SEMANTIC_PRIORITY_VALUES[c.weight as SemanticPriority];
|
|
156
|
+
} else if (typeof c.weight === 'number') {
|
|
157
|
+
weight = c.weight;
|
|
158
|
+
} else {
|
|
159
|
+
throw new CompilationError(`Invalid weight for criterion '${c.id}': '${c.weight}'`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!c.direction) {
|
|
163
|
+
throw new CompilationError(`Criterion '${c.id}' requires a direction`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
id: c.id,
|
|
168
|
+
name: c.name,
|
|
169
|
+
weight,
|
|
170
|
+
direction: c.direction
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Normalize weights to sum to 1.0
|
|
175
|
+
const totalWeight = resolvedCriteria.reduce((sum, c) => sum + c.weight, 0);
|
|
176
|
+
if (totalWeight === 0) {
|
|
177
|
+
throw new CompilationError('Criteria weights must not all be zero');
|
|
178
|
+
}
|
|
179
|
+
for (const c of resolvedCriteria) {
|
|
180
|
+
c.weight = c.weight / totalWeight;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
id: ruleSet.id,
|
|
185
|
+
name: ruleSet.name,
|
|
186
|
+
mode: 'ranking',
|
|
187
|
+
strategy: 'head-to-head',
|
|
188
|
+
entityType: ruleSet.entityType,
|
|
189
|
+
criteria: resolvedCriteria,
|
|
190
|
+
config: {
|
|
191
|
+
direction: (ruleSet as any).config?.direction ?? 'highest-first'
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ranking Engine
|
|
3
|
+
*
|
|
4
|
+
* Comparative scoring engine that ranks N entities relative to each other.
|
|
5
|
+
* Supports score-based ranking, Elo ratings, and head-to-head comparison.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const engine = new RankingEngine();
|
|
10
|
+
* engine.add({ type: 'Vendor', data: { id: 'v1', revenue: 500000 } });
|
|
11
|
+
* engine.add({ type: 'Vendor', data: { id: 'v2', revenue: 120000 } });
|
|
12
|
+
*
|
|
13
|
+
* const result = engine.execute(compiledRanking);
|
|
14
|
+
* console.log(result.rankings[0]); // { rank: 1, entityId: 'v1', percentileLabel: 'top-1%', ... }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
WorkingMemory,
|
|
20
|
+
Fact,
|
|
21
|
+
FactInput,
|
|
22
|
+
FactChange
|
|
23
|
+
} from '../../core';
|
|
24
|
+
|
|
25
|
+
import type { TierDefinition } from '../scoring/types';
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
CompiledRankingRuleSet,
|
|
29
|
+
RankingOptions,
|
|
30
|
+
RankingResult
|
|
31
|
+
} from './types';
|
|
32
|
+
|
|
33
|
+
import { RankingExecutor } from './strategy';
|
|
34
|
+
|
|
35
|
+
export class RankingEngine {
|
|
36
|
+
private wm: WorkingMemory;
|
|
37
|
+
private strategy: RankingExecutor;
|
|
38
|
+
|
|
39
|
+
constructor(workingMemory?: WorkingMemory) {
|
|
40
|
+
this.wm = workingMemory ?? new WorkingMemory();
|
|
41
|
+
this.strategy = new RankingExecutor();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ========================================
|
|
45
|
+
// IWorkingMemory Implementation
|
|
46
|
+
// ========================================
|
|
47
|
+
|
|
48
|
+
add<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
|
|
49
|
+
return this.wm.add(input);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
remove(factId: string): Fact | undefined {
|
|
53
|
+
return this.wm.remove(factId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
update<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
|
|
57
|
+
return this.wm.update(input);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get(factId: string): Fact | undefined {
|
|
61
|
+
return this.wm.get(factId);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getByType(type: string): Fact[] {
|
|
65
|
+
return this.wm.getByType(type);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getAll(): Fact[] {
|
|
69
|
+
return this.wm.getAll();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
has(factId: string): boolean {
|
|
73
|
+
return this.wm.has(factId);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
size(): number {
|
|
77
|
+
return this.wm.size();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
clear(): void {
|
|
81
|
+
this.wm.clear();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getChanges(): FactChange[] {
|
|
85
|
+
return this.wm.getChanges();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
clearChanges(): void {
|
|
89
|
+
this.wm.clearChanges();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ========================================
|
|
93
|
+
// Engine Execution
|
|
94
|
+
// ========================================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Execute a ranking ruleset.
|
|
98
|
+
*
|
|
99
|
+
* Scores all entities of the configured type and produces
|
|
100
|
+
* a ranked list with percentiles and optional tier/movement tracking.
|
|
101
|
+
*
|
|
102
|
+
* @param ruleSet - Compiled ranking ruleset
|
|
103
|
+
* @param options - Runtime options (previousRankings for movement, onRank callback)
|
|
104
|
+
* @returns Ranking result with sorted entities
|
|
105
|
+
*/
|
|
106
|
+
execute<T extends TierDefinition = TierDefinition>(
|
|
107
|
+
ruleSet: CompiledRankingRuleSet<T>,
|
|
108
|
+
options: RankingOptions = {}
|
|
109
|
+
): RankingResult<T> {
|
|
110
|
+
return this.strategy.run(ruleSet, this.wm, options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ========================================
|
|
114
|
+
// Utility Methods
|
|
115
|
+
// ========================================
|
|
116
|
+
|
|
117
|
+
getWorkingMemory(): WorkingMemory {
|
|
118
|
+
return this.wm;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ranking Engine — Comparative Entity Ranking
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export type {
|
|
7
|
+
RankingStrategy,
|
|
8
|
+
RankingDirection,
|
|
9
|
+
PercentileLabel,
|
|
10
|
+
Movement,
|
|
11
|
+
KFactorPreset,
|
|
12
|
+
RankingCriterion,
|
|
13
|
+
CompiledRankingCriterion,
|
|
14
|
+
ScoreRankingConfig,
|
|
15
|
+
EloConfig,
|
|
16
|
+
HeadToHeadConfig,
|
|
17
|
+
ScoreRankingRuleSet,
|
|
18
|
+
EloRankingRuleSet,
|
|
19
|
+
HeadToHeadRankingRuleSet,
|
|
20
|
+
RankingRuleSet,
|
|
21
|
+
CompiledScoreRankingRuleSet,
|
|
22
|
+
CompiledEloRankingRuleSet,
|
|
23
|
+
CompiledHeadToHeadRankingRuleSet,
|
|
24
|
+
CompiledRankingRuleSet,
|
|
25
|
+
PreviousRanking,
|
|
26
|
+
RankingOptions,
|
|
27
|
+
RankedEntity,
|
|
28
|
+
RankingResult
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
// Constants & utilities
|
|
32
|
+
export {
|
|
33
|
+
K_FACTOR_VALUES,
|
|
34
|
+
isKFactorPreset,
|
|
35
|
+
resolvePercentileLabel,
|
|
36
|
+
resolveMovement
|
|
37
|
+
} from './types';
|
|
38
|
+
|
|
39
|
+
// Compiler
|
|
40
|
+
export { compileRankingRuleSet } from './compiler';
|
|
41
|
+
|
|
42
|
+
// Strategy
|
|
43
|
+
export { RankingExecutor, rankingStrategy } from './strategy';
|
|
44
|
+
|
|
45
|
+
// Engine
|
|
46
|
+
export { RankingEngine } from './engine';
|