@lapxo/obligations 0.4.2 → 0.4.4

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.
Files changed (65) hide show
  1. package/README.md +4 -0
  2. package/TARGET.bound +68 -11
  3. package/dist/measures/cofailure-pairs.d.ts +113 -0
  4. package/dist/measures/cofailure-pairs.js +129 -0
  5. package/dist/measures/index.d.ts +10 -0
  6. package/dist/measures/index.js +5 -0
  7. package/dist/measures/marginal-information.d.ts +78 -0
  8. package/dist/measures/marginal-information.js +71 -0
  9. package/dist/measures/mix-loss.d.ts +187 -0
  10. package/dist/measures/mix-loss.js +101 -0
  11. package/dist/measures/phase-defect.d.ts +40 -0
  12. package/dist/measures/phase-defect.js +33 -0
  13. package/dist/measures/tolerant-subsets.d.ts +79 -0
  14. package/dist/measures/tolerant-subsets.js +60 -0
  15. package/docs/measures.md +24 -0
  16. package/docs/paper.md +2 -2
  17. package/docs/reference.md +6 -1
  18. package/package.json +1 -1
  19. package/proofs/cofailure-pairs.json +26 -0
  20. package/proofs/coordination.json +27 -0
  21. package/proofs/helly-comb.json +17 -0
  22. package/proofs/lean/Obligations/Cell.lean +159 -0
  23. package/proofs/lean/Obligations/HellyComb.lean +19 -0
  24. package/proofs/lineage-ceiling.json +41 -0
  25. package/proofs/marginal-information.json +30 -0
  26. package/proofs/resolution-encounters.json +23 -0
  27. package/receipts.bound +507 -118
  28. package/samples/laws/cofailure-pairs/no/sample.json +1 -0
  29. package/samples/laws/cofailure-pairs/yes/sample.json +20 -0
  30. package/samples/laws/linage-rest/no/sample.json +19 -0
  31. package/samples/laws/linage-rest/yes/sample.json +100 -0
  32. package/samples/laws/lineage-ceiling/no/sample.json +8 -0
  33. package/samples/laws/lineage-ceiling/yes/sample.json +30 -0
  34. package/samples/laws/marginal-information/no/sample.json +17 -0
  35. package/samples/laws/marginal-information/yes/sample.json +459 -0
  36. package/samples/laws/semilattice/no/sample.json +18 -0
  37. package/samples/laws/semilattice/yes/sample.json +29 -0
  38. package/samples/measures/l7-tolerant-k.json +90 -0
  39. package/samples/measures/l8-phase-defect.json +19 -0
  40. package/samples/measures/l9-mix-loss.json +36 -0
  41. package/samples/no/H1-uncertainty.json +24 -0
  42. package/samples/no/installed-artifact.json +103 -12
  43. package/samples/yes/H1-uncertainty.json +25 -0
  44. package/samples/yes/installed-artifact.json +36 -60
  45. package/vectors/laws/cofailure-pairs.json +2580 -0
  46. package/vectors/laws/cofailure-pairs.native.json +267 -0
  47. package/vectors/laws/coordination.json +386 -0
  48. package/vectors/laws/f1/finite-arithmetic.json +66 -0
  49. package/vectors/laws/f1/path-witness.json +242 -0
  50. package/vectors/laws/helly-comb.json +95 -0
  51. package/vectors/laws/helly-comb.lean.json +94 -0
  52. package/vectors/laws/implementation-043.json +98 -0
  53. package/vectors/laws/l7-tolerant-k.json +253 -0
  54. package/vectors/laws/l8-phase-defect.json +214 -0
  55. package/vectors/laws/l9-mix-loss.json +130 -0
  56. package/vectors/laws/linage-rest.native.json +335 -0
  57. package/vectors/laws/lineage-ceiling.json +790 -0
  58. package/vectors/laws/lineage-ceiling.native.json +291 -0
  59. package/vectors/laws/marginal-information.json +868 -0
  60. package/vectors/laws/marginal-information.native.json +259 -0
  61. package/vectors/laws/meet-semilattice.json +244 -0
  62. package/vectors/laws/meet-semilattice.native.json +265 -0
  63. package/vectors/laws/policy.json +22 -0
  64. package/vectors/laws/resolution-encounters.json +171 -0
  65. package/vectors/measures/installed-artifact.cli.json +8 -8
@@ -0,0 +1,113 @@
1
+ import { type ValueReading, type TruthReading, type CoincidenceReference } from './coincidence.ts';
2
+ import { type RationalInput } from './rational.ts';
3
+ export type PairReference = Readonly<{
4
+ model: 'independent-categorical@1';
5
+ marginals: readonly Readonly<{
6
+ origin: string;
7
+ probabilities: readonly Readonly<{
8
+ value: string;
9
+ p: RationalInput;
10
+ }>[];
11
+ }>[];
12
+ }>;
13
+ export type PairSampling = Readonly<{
14
+ model: 'iid-cells@1';
15
+ epsilon: RationalInput;
16
+ }>;
17
+ type Common = Readonly<{
18
+ origins: readonly string[];
19
+ values: readonly string[];
20
+ T?: number;
21
+ theta?: RationalInput;
22
+ budget: string;
23
+ sampling?: PairSampling;
24
+ }>;
25
+ export type CofailureModel = Common & (Readonly<{
26
+ variant: 'oracle-false-rate';
27
+ truth: readonly TruthReading[];
28
+ reference: CoincidenceReference;
29
+ }> | Readonly<{
30
+ variant: 'categorical-match-excess';
31
+ reference?: PairReference;
32
+ truth?: never;
33
+ }>);
34
+ export declare function cofailurePairs(rows: readonly ValueReading[], model: CofailureModel): {
35
+ status: "unmeasured";
36
+ cause: string;
37
+ required?: undefined;
38
+ events?: undefined;
39
+ T?: undefined;
40
+ variant?: undefined;
41
+ theta?: undefined;
42
+ origins?: undefined;
43
+ pairs?: undefined;
44
+ components?: undefined;
45
+ uncertainty?: undefined;
46
+ scoreMeaning?: undefined;
47
+ interpretation?: undefined;
48
+ } | {
49
+ status: "unmeasured";
50
+ cause: string;
51
+ required: string;
52
+ events?: undefined;
53
+ T?: undefined;
54
+ variant?: undefined;
55
+ theta?: undefined;
56
+ origins?: undefined;
57
+ pairs?: undefined;
58
+ components?: undefined;
59
+ uncertainty?: undefined;
60
+ scoreMeaning?: undefined;
61
+ interpretation?: undefined;
62
+ } | {
63
+ status: "unmeasured";
64
+ cause: string;
65
+ events: number;
66
+ T: number;
67
+ required?: undefined;
68
+ variant?: undefined;
69
+ theta?: undefined;
70
+ origins?: undefined;
71
+ pairs?: undefined;
72
+ components?: undefined;
73
+ uncertainty?: undefined;
74
+ scoreMeaning?: undefined;
75
+ interpretation?: undefined;
76
+ } | {
77
+ status: "measured";
78
+ variant: "oracle-false-rate" | "categorical-match-excess";
79
+ T: number;
80
+ theta: Readonly<{
81
+ numerator: string;
82
+ denominator: string;
83
+ }>;
84
+ origins: string[];
85
+ pairs: {
86
+ origins: readonly [string, string];
87
+ observed: Readonly<{
88
+ numerator: string;
89
+ denominator: string;
90
+ }>;
91
+ expected: Readonly<{
92
+ numerator: string;
93
+ denominator: string;
94
+ }>;
95
+ excess: Readonly<{
96
+ numerator: string;
97
+ denominator: string;
98
+ }>;
99
+ score: Readonly<{
100
+ numerator: string;
101
+ denominator: string;
102
+ }>;
103
+ edge: boolean;
104
+ }[];
105
+ components: string[][];
106
+ uncertainty: unknown;
107
+ scoreMeaning: string;
108
+ interpretation: "descriptive threshold components, not identified causes or probability of truth";
109
+ cause?: undefined;
110
+ required?: undefined;
111
+ events?: undefined;
112
+ };
113
+ export {};
@@ -0,0 +1,129 @@
1
+ import { coincidenceExcess } from "./coincidence.js";
2
+ import { unionBoundConfidence } from "./confidence.js";
3
+ import { add, sub, mul, div, q, zero, one, pack, rational, probability } from "./rational.js";
4
+ const refuse = (cause) => { throw Error('REFUSE·cofailure ' + cause); };
5
+ const named = (v) => typeof v === 'string' && v.trim().length > 0;
6
+ const compare = (a, b) => a[0] * b[1] < b[0] * a[1] ? -1 : a[0] * b[1] > b[0] * a[1] ? 1 : 0;
7
+ const max = (a, b) => compare(a, b) >= 0 ? a : b;
8
+ const min = (a, b) => compare(a, b) <= 0 ? a : b;
9
+ export function cofailurePairs(rows, model) {
10
+ if (!model || model.T === undefined || model.theta === undefined)
11
+ return { status: 'unmeasured', cause: 'cofailure/T-and-theta-required' };
12
+ if (!Number.isSafeInteger(model.T) || model.T < 1)
13
+ refuse('invalid-T');
14
+ if (!Array.isArray(rows) || !Array.isArray(model.origins) || model.origins.length < 2 || model.origins.some(id => !named(id)) || new Set(model.origins).size !== model.origins.length)
15
+ refuse('invalid-origins-or-rows');
16
+ if (!Array.isArray(model.values) || model.values.length < 2 || model.values.some(v => !named(v)) || new Set(model.values).size !== model.values.length)
17
+ refuse('invalid-alphabet');
18
+ if (model.variant !== 'oracle-false-rate' && model.variant !== 'categorical-match-excess')
19
+ refuse('declared-variant-required');
20
+ if (typeof model.budget !== 'string' || !/^\d+$/.test(model.budget))
21
+ refuse('budget-required');
22
+ const theta = rational(model.theta);
23
+ if (compare(theta, one) > 0 || compare(theta, model.variant === 'oracle-false-rate' ? zero : q(-1n)) < 0)
24
+ refuse('invalid-threshold');
25
+ const origins = [...model.origins].sort(), M = origins.length * (origins.length - 1) / 2;
26
+ const truth = model.variant === 'oracle-false-rate' ? model.truth : [];
27
+ if (!Array.isArray(truth))
28
+ refuse('truth-array-required');
29
+ if (model.variant === 'categorical-match-excess' && Object.hasOwn(model, 'truth'))
30
+ refuse('non-oracle-truth-input');
31
+ const required = BigInt(M) * (3n * BigInt(rows.length) + BigInt(truth.length) + BigInt(model.values.length) + 2n) + BigInt(rows.length) + BigInt(model.T) * BigInt(origins.length);
32
+ if (required > BigInt(model.budget))
33
+ return { status: 'unmeasured', cause: 'cofailure/budget', required: String(required) };
34
+ const alphabet = new Set(model.values), selected = new Set(origins), events = new Map();
35
+ for (const row of rows) {
36
+ if (!row || !named(row.event) || !named(row.origin) || !alphabet.has(row.value))
37
+ refuse('invalid-value-reading');
38
+ if (!selected.has(row.origin))
39
+ continue;
40
+ const at = events.get(row.event) ?? new Map();
41
+ if (at.has(row.origin) && at.get(row.origin) !== row.value)
42
+ refuse('conflicting-value ' + row.event + '/' + row.origin);
43
+ at.set(row.origin, row.value);
44
+ events.set(row.event, at);
45
+ }
46
+ if (events.size !== model.T || [...events.values()].some(at => at.size !== origins.length))
47
+ return { status: 'unmeasured', cause: 'cofailure/incomplete-declared-frame', events: events.size, T: model.T };
48
+ const marginals = new Map();
49
+ if (model.variant === 'oracle-false-rate' && model.reference?.marginals === 'handed') {
50
+ const errors = model.reference.errors;
51
+ if (!Array.isArray(errors) || errors.length !== origins.length || errors.some(e => !e) || new Set(errors.map(e => e.origin)).size !== origins.length || errors.some(e => !selected.has(e.origin)))
52
+ refuse('oracle-reference-origins');
53
+ }
54
+ if (model.variant === 'categorical-match-excess') {
55
+ if (!model.reference)
56
+ return { status: 'unmeasured', cause: 'cofailure/reference-required' };
57
+ if (model.reference.model !== 'independent-categorical@1' || !Array.isArray(model.reference.marginals) || model.reference.marginals.length !== origins.length)
58
+ refuse('categorical-reference-required');
59
+ for (const row of model.reference.marginals) {
60
+ if (!row || !selected.has(row.origin) || marginals.has(row.origin) || !Array.isArray(row.probabilities) || row.probabilities.length !== alphabet.size)
61
+ refuse('reference-origins-or-values');
62
+ const values = new Map();
63
+ for (const p of row.probabilities) {
64
+ if (!p || !alphabet.has(p.value) || values.has(p.value))
65
+ refuse('reference-origins-or-values');
66
+ values.set(p.value, probability(p.p));
67
+ }
68
+ const total = [...values.values()].reduce(add, zero);
69
+ if (compare(total, one) !== 0)
70
+ refuse('reference-not-normalized');
71
+ marginals.set(row.origin, values);
72
+ }
73
+ }
74
+ const pairs = [];
75
+ for (let i = 0; i < origins.length; i++)
76
+ for (let j = i + 1; j < origins.length; j++) {
77
+ const a = origins[i], b = origins[j];
78
+ let observed, expected;
79
+ if (model.variant === 'oracle-false-rate') {
80
+ if (!model.reference || !Array.isArray(model.reference.values) || model.reference.values.length !== alphabet.size || model.reference.values.some(v => !alphabet.has(v)))
81
+ refuse('oracle-reference-alphabet');
82
+ const reference = model.reference.marginals === 'handed' ? { ...model.reference, errors: model.reference.errors.filter(e => e.origin === a || e.origin === b) } : model.reference;
83
+ const reading = coincidenceExcess(rows, truth, [a, b], reference, { minEvents: model.T, minEventsPerTruth: 1, budget: model.budget });
84
+ if (reading.status !== 'measured')
85
+ return { status: 'unmeasured', cause: 'cofailure/oracle ' + reading.cause };
86
+ observed = rational(reading.observed);
87
+ expected = rational(reading.expected);
88
+ }
89
+ else {
90
+ let count = 0;
91
+ for (const row of events.values())
92
+ if (row.get(a) === row.get(b))
93
+ count++;
94
+ observed = q(BigInt(count), BigInt(model.T));
95
+ expected = model.values.map(v => mul(marginals.get(a).get(v), marginals.get(b).get(v))).reduce(add, zero);
96
+ }
97
+ const excess = sub(observed, expected), score = model.variant === 'oracle-false-rate' ? observed : excess;
98
+ pairs.push({ origins: [a, b], observed: pack(observed), expected: pack(expected), excess: pack(excess), score: pack(score), edge: compare(score, theta) > 0 });
99
+ }
100
+ const parent = origins.map((_, i) => i), find = (i) => parent[i] === i ? i : find(parent[i]);
101
+ for (const p of pairs)
102
+ if (p.edge)
103
+ parent[find(origins.indexOf(p.origins[0]))] = find(origins.indexOf(p.origins[1]));
104
+ const groups = new Map();
105
+ origins.forEach((id, i) => { const key = find(i); groups.set(key, [...(groups.get(key) ?? []), id]); });
106
+ const components = [...groups.values()].sort((a, b) => a[0].localeCompare(b[0]));
107
+ let uncertainty = { status: 'unmeasured', cause: 'cofailure/sampling-design-required' };
108
+ if (model.sampling) {
109
+ if (model.sampling.model !== 'iid-cells@1')
110
+ refuse('sampling-model-required');
111
+ const epsilon = probability(model.sampling.epsilon);
112
+ if (compare(epsilon, zero) <= 0)
113
+ refuse('positive-epsilon-required');
114
+ const failure = min(one, div(one, mul(q(4n * BigInt(model.T)), mul(epsilon, epsilon))));
115
+ const confidence = unionBoundConfidence(pairs.map((p, i) => ({ id: 'pair-' + i, p: pack(failure), justification: 'Chebyshev: declared iid Bernoulli indicators, variance <= 1/4; fixed handed reference' })));
116
+ const intervals = pairs.map(p => {
117
+ const score = rational(p.score), expected = rational(p.expected), low = model.variant === 'oracle-false-rate' ? zero : sub(zero, expected), high = model.variant === 'oracle-false-rate' ? one : sub(one, expected);
118
+ const lo = max(low, sub(score, epsilon)), hi = min(high, add(score, epsilon));
119
+ return { origins: p.origins, lo: pack(lo), hi: pack(hi), decision: compare(lo, theta) > 0 ? 'above' : compare(hi, theta) < 0 ? 'below' : 'unresolved' };
120
+ });
121
+ uncertainty = { status: 'conditional', model: model.sampling.model, epsilon: pack(epsilon), perPairFailureUpper: pack(failure), confidence,
122
+ intervals, thresholdGraphResolved: intervals.every(i => i.decision !== 'unresolved'),
123
+ conditionalGraphCertificate: !confidence.trivial && intervals.every(i => i.decision !== 'unresolved'),
124
+ meaning: 'fixed-frame threshold graph only, conditional on the handed iid design; neither causal grouping nor iid was inferred' };
125
+ }
126
+ return { status: 'measured', variant: model.variant, T: model.T, theta: pack(theta), origins, pairs, components, uncertainty,
127
+ scoreMeaning: model.variant === 'oracle-false-rate' ? 'same wrong answer / T' : 'same answer / T minus handed independent-categorical reference',
128
+ interpretation: 'descriptive threshold components, not identified causes or probability of truth' };
129
+ }
@@ -24,3 +24,13 @@ export { coincidenceExcess } from './coincidence.ts';
24
24
  export type { ValueReading, TruthReading, CoincidenceReference, CoincidencePolicy } from './coincidence.ts';
25
25
  export { unionBoundConfidence, tolerantCountConfidence } from './confidence.ts';
26
26
  export type { DeclaredFailure } from './confidence.ts';
27
+ export { tolerantSubsetConfidence, independentCountConfidence } from './tolerant-subsets.ts';
28
+ export type { JointIndependenceDeclaration } from './tolerant-subsets.ts';
29
+ export { phaseDefect, phaseDefectExtrema } from './phase-defect.ts';
30
+ export type { PhaseDefectInterpretation } from './phase-defect.ts';
31
+ export { mixProjectionLoss } from './mix-loss.ts';
32
+ export type { NamedConfiguration, ConfigurationPrior, MixLossModel, MixLossPolicy } from './mix-loss.ts';
33
+ export { cofailurePairs } from './cofailure-pairs.ts';
34
+ export type { PairReference, PairSampling, CofailureModel } from './cofailure-pairs.ts';
35
+ export { marginalInformation } from './marginal-information.ts';
36
+ export type { MarginalSource, JointAtom, MarginalModel } from './marginal-information.ts';
@@ -11,3 +11,8 @@ export { commonCauseCapacityBound, capacityBound, capacityP0, mutualInformation,
11
11
  export { fitUnanimityModel } from "./unanimity-fit.js";
12
12
  export { coincidenceExcess } from "./coincidence.js";
13
13
  export { unionBoundConfidence, tolerantCountConfidence } from "./confidence.js";
14
+ export { tolerantSubsetConfidence, independentCountConfidence } from "./tolerant-subsets.js";
15
+ export { phaseDefect, phaseDefectExtrema } from "./phase-defect.js";
16
+ export { mixProjectionLoss } from "./mix-loss.js";
17
+ export { cofailurePairs } from "./cofailure-pairs.js";
18
+ export { marginalInformation } from "./marginal-information.js";
@@ -0,0 +1,78 @@
1
+ import { type MixLossPolicy } from './mix-loss.ts';
2
+ import { type RationalInput } from './rational.ts';
3
+ export type MarginalSource = Readonly<{
4
+ id: string;
5
+ lineage: string;
6
+ values: readonly string[];
7
+ }>;
8
+ export type JointAtom = Readonly<{
9
+ id: string;
10
+ truth: string;
11
+ values: Readonly<Record<string, string>>;
12
+ p?: RationalInput;
13
+ }>;
14
+ export type MarginalModel = Readonly<{
15
+ model: 'finite-declared-joint@1';
16
+ truthValues: readonly string[];
17
+ sources: readonly MarginalSource[];
18
+ atoms: readonly JointAtom[];
19
+ }>;
20
+ export declare function marginalInformation(model: MarginalModel, orders: readonly (readonly string[])[], policy: MixLossPolicy): {
21
+ status: "unmeasured";
22
+ cause: string;
23
+ required?: undefined;
24
+ workUnit?: undefined;
25
+ model?: undefined;
26
+ unit?: undefined;
27
+ approximation?: undefined;
28
+ truthEntropy?: undefined;
29
+ declaredAtoms?: undefined;
30
+ computedSubsets?: undefined;
31
+ policy?: undefined;
32
+ readings?: undefined;
33
+ meaning?: undefined;
34
+ } | {
35
+ status: "unmeasured";
36
+ cause: string;
37
+ required: string;
38
+ workUnit: "declared-support-scans";
39
+ model?: undefined;
40
+ unit?: undefined;
41
+ approximation?: undefined;
42
+ truthEntropy?: undefined;
43
+ declaredAtoms?: undefined;
44
+ computedSubsets?: undefined;
45
+ policy?: undefined;
46
+ readings?: undefined;
47
+ meaning?: undefined;
48
+ } | {
49
+ status: "measured";
50
+ model: "finite-declared-joint@1";
51
+ unit: "bits";
52
+ approximation: "binary64-log2";
53
+ truthEntropy: number;
54
+ declaredAtoms: number;
55
+ computedSubsets: number;
56
+ policy: Readonly<{
57
+ logarithm: "binary64-log2";
58
+ budget: string;
59
+ }>;
60
+ workUnit: "declared-support-scans";
61
+ required: string;
62
+ readings: {
63
+ additionOrder: string[];
64
+ steps: {
65
+ source: string;
66
+ lineage: string;
67
+ priorSources: string[];
68
+ deltaInformation: number;
69
+ cumulativeInformation: number;
70
+ remainingConditionalEntropy: number;
71
+ }[];
72
+ totalInformation: number;
73
+ sumMarginals: number;
74
+ telescopingResidual: number;
75
+ }[];
76
+ meaning: "I(truth; new source | previous sources) under one unchanged declared joint prior; lineage labels supply no ordering or independence";
77
+ cause?: undefined;
78
+ };
@@ -0,0 +1,71 @@
1
+ import { mixProjectionLoss } from "./mix-loss.js";
2
+ import { add, probability, pack, zero } from "./rational.js";
3
+ const refuse = (why) => { throw Error('REFUSE·marginal ' + why); };
4
+ const names = (xs) => Array.isArray(xs) && xs.length > 0 && xs.every(x => typeof x === 'string' && x.trim().length > 0) && new Set(xs).size === xs.length;
5
+ export function marginalInformation(model, orders, policy) {
6
+ if (!model || model.model !== 'finite-declared-joint@1' || !Array.isArray(model.atoms))
7
+ return { status: 'unmeasured', cause: 'marginal/joint-model-required' };
8
+ if (!names(model.truthValues) || !Array.isArray(model.sources) || !model.sources.length || !names(model.sources.map(s => s?.id)))
9
+ refuse('declared-domain-required');
10
+ if (model.sources.some(s => !s || typeof s.lineage !== 'string' || !s.lineage.trim() || !names(s.values)))
11
+ refuse('declared-source-domain-required');
12
+ if (!policy || policy.logarithm !== 'binary64-log2' || typeof policy.budget !== 'string' || !/^\d+$/.test(policy.budget))
13
+ refuse('numeric-policy-required');
14
+ const ids = model.sources.map(s => s.id);
15
+ if (!Array.isArray(orders) || !orders.length || orders.some(o => !Array.isArray(o) || o.length !== ids.length || new Set(o).size !== ids.length || o.some(id => !ids.includes(id))))
16
+ refuse('order-must-be-permutation');
17
+ if (!model.atoms.length || !names(model.atoms.map(a => a?.id)))
18
+ refuse('unique-atom-ids-required');
19
+ if (model.atoms.some(a => !a || a.p === undefined || a.p === null))
20
+ return { status: 'unmeasured', cause: 'marginal/prior-required' };
21
+ const required = BigInt(model.atoms.length) * BigInt(ids.length + 1) * BigInt(1 + orders.length * (ids.length + 1));
22
+ if (required > BigInt(policy.budget))
23
+ return { status: 'unmeasured', cause: 'marginal/budget', required: String(required), workUnit: 'declared-support-scans' };
24
+ const probabilities = model.atoms.map(a => probability(a.p));
25
+ const total = probabilities.reduce(add, zero);
26
+ if (total[0] !== total[1])
27
+ refuse('prior-must-sum-to-one');
28
+ for (const a of model.atoms) {
29
+ if (!model.truthValues.includes(a.truth) || !a.values || Object.keys(a.values).length !== ids.length || model.sources.some(s => !Object.hasOwn(a.values, s.id) || !s.values.includes(a.values[s.id])))
30
+ refuse('atom-outside-domain');
31
+ }
32
+ const cached = new Map();
33
+ function conditional(selected) {
34
+ const coordinates = [...selected].sort(), key = JSON.stringify(coordinates), held = cached.get(key);
35
+ if (held !== undefined)
36
+ return held;
37
+ const states = new Map();
38
+ model.atoms.forEach((a, i) => {
39
+ const truth = JSON.stringify(['truth', a.truth]), tuple = JSON.stringify(['selected', coordinates.map(id => a.values[id])]), name = JSON.stringify([truth, tuple]);
40
+ const previous = states.get(name);
41
+ states.set(name, { truth, tuple, mass: add(previous?.mass ?? zero, probabilities[i]) });
42
+ });
43
+ const values = [...states.values()], alphabet = [...new Set(values.flatMap(v => [v.truth, v.tuple]))];
44
+ const result = mixProjectionLoss({ factors: [{ id: 'truth-coordinate', weight: '0' }, { id: 'selected-coordinate', weight: '1' }], alphabet,
45
+ configurations: values.map((v, i) => ({ id: String(i), values: [v.truth, v.tuple] })),
46
+ prior: { kind: 'explicit', probabilities: values.map((v, i) => ({ configuration: String(i), p: pack(v.mass) })) } }, policy);
47
+ if (result.status !== 'measured')
48
+ return refuse('projection-budget');
49
+ cached.set(key, result.lossBits);
50
+ return result.lossBits;
51
+ }
52
+ const truthEntropy = conditional([]);
53
+ const readings = orders.map(additionOrder => {
54
+ const selected = [];
55
+ let before = truthEntropy, sumMarginals = 0;
56
+ const steps = additionOrder.map(source => {
57
+ const priorSources = [...selected];
58
+ selected.push(source);
59
+ const after = conditional(selected), delta = before - after;
60
+ sumMarginals += delta;
61
+ before = after;
62
+ return { source, lineage: model.sources.find(s => s.id === source).lineage, priorSources,
63
+ deltaInformation: delta, cumulativeInformation: truthEntropy - after, remainingConditionalEntropy: after };
64
+ });
65
+ const totalInformation = truthEntropy - before;
66
+ return { additionOrder: [...additionOrder], steps, totalInformation, sumMarginals, telescopingResidual: sumMarginals - totalInformation };
67
+ });
68
+ return { status: 'measured', model: model.model, unit: 'bits', approximation: 'binary64-log2',
69
+ truthEntropy, declaredAtoms: model.atoms.length, computedSubsets: cached.size, policy, workUnit: 'declared-support-scans',
70
+ required: String(required), readings, meaning: 'I(truth; new source | previous sources) under one unchanged declared joint prior; lineage labels supply no ordering or independence' };
71
+ }
@@ -0,0 +1,187 @@
1
+ import { type RationalInput } from './rational.ts';
2
+ export type NamedConfiguration = Readonly<{
3
+ id: string;
4
+ values: readonly string[];
5
+ }>;
6
+ export type ConfigurationPrior = Readonly<{
7
+ kind: 'uniform';
8
+ }> | Readonly<{
9
+ kind: 'explicit';
10
+ probabilities: readonly Readonly<{
11
+ configuration: string;
12
+ p: RationalInput;
13
+ }>[];
14
+ }>;
15
+ export type MixLossModel = Readonly<{
16
+ factors: readonly Readonly<{
17
+ id: string;
18
+ weight: RationalInput;
19
+ }>[];
20
+ alphabet: readonly string[];
21
+ configurations: readonly NamedConfiguration[];
22
+ prior?: ConfigurationPrior;
23
+ }>;
24
+ export type MixLossPolicy = Readonly<{
25
+ logarithm: 'binary64-log2';
26
+ budget: string;
27
+ }>;
28
+ export declare function mixProjectionLoss(model: MixLossModel, policy: MixLossPolicy): {
29
+ status: "unmeasured";
30
+ cause: "mix-loss/prior-required";
31
+ parameters: {
32
+ m: number;
33
+ factors: {
34
+ id: string;
35
+ weight: Readonly<{
36
+ numerator: string;
37
+ denominator: string;
38
+ }>;
39
+ }[];
40
+ alphabet: string[];
41
+ configurations: {
42
+ id: string;
43
+ values: string[];
44
+ }[];
45
+ };
46
+ policy: Readonly<{
47
+ logarithm: "binary64-log2";
48
+ budget: string;
49
+ }>;
50
+ required?: undefined;
51
+ workUnit?: undefined;
52
+ model?: undefined;
53
+ prior?: undefined;
54
+ unit?: undefined;
55
+ approximation?: undefined;
56
+ configurationBits?: undefined;
57
+ mixBits?: undefined;
58
+ lossBits?: undefined;
59
+ entropyDifferenceBits?: undefined;
60
+ forkLossBits?: undefined;
61
+ mixInjectiveOnConfigurations?: undefined;
62
+ mixInjectiveOnPositiveSupport?: undefined;
63
+ configurations?: undefined;
64
+ outputs?: undefined;
65
+ meaning?: undefined;
66
+ } | {
67
+ status: "unmeasured";
68
+ cause: "mix-loss/work-budget";
69
+ parameters: {
70
+ m: number;
71
+ factors: {
72
+ id: string;
73
+ weight: Readonly<{
74
+ numerator: string;
75
+ denominator: string;
76
+ }>;
77
+ }[];
78
+ alphabet: string[];
79
+ configurations: {
80
+ id: string;
81
+ values: string[];
82
+ }[];
83
+ };
84
+ policy: Readonly<{
85
+ logarithm: "binary64-log2";
86
+ budget: string;
87
+ }>;
88
+ required: string;
89
+ workUnit: "declared-configurations";
90
+ model?: undefined;
91
+ prior?: undefined;
92
+ unit?: undefined;
93
+ approximation?: undefined;
94
+ configurationBits?: undefined;
95
+ mixBits?: undefined;
96
+ lossBits?: undefined;
97
+ entropyDifferenceBits?: undefined;
98
+ forkLossBits?: undefined;
99
+ mixInjectiveOnConfigurations?: undefined;
100
+ mixInjectiveOnPositiveSupport?: undefined;
101
+ configurations?: undefined;
102
+ outputs?: undefined;
103
+ meaning?: undefined;
104
+ } | {
105
+ status: "measured";
106
+ model: "declared-weighted-mix-projection@1";
107
+ parameters: {
108
+ m: number;
109
+ factors: {
110
+ id: string;
111
+ weight: Readonly<{
112
+ numerator: string;
113
+ denominator: string;
114
+ }>;
115
+ }[];
116
+ alphabet: string[];
117
+ configurations: {
118
+ id: string;
119
+ values: string[];
120
+ }[];
121
+ };
122
+ prior: {
123
+ kind: "uniform" | "explicit";
124
+ probabilities: {
125
+ configuration: string;
126
+ p: Readonly<{
127
+ numerator: string;
128
+ denominator: string;
129
+ }>;
130
+ }[];
131
+ };
132
+ policy: Readonly<{
133
+ logarithm: "binary64-log2";
134
+ budget: string;
135
+ }>;
136
+ unit: "bits";
137
+ approximation: "binary64-log2";
138
+ configurationBits: number;
139
+ mixBits: number;
140
+ lossBits: number;
141
+ entropyDifferenceBits: number;
142
+ forkLossBits: number;
143
+ mixInjectiveOnConfigurations: boolean;
144
+ mixInjectiveOnPositiveSupport: boolean;
145
+ configurations: {
146
+ id: string;
147
+ probability: Readonly<{
148
+ numerator: string;
149
+ denominator: string;
150
+ }>;
151
+ mix: Readonly<{
152
+ kind: "value";
153
+ value: string;
154
+ }> | {
155
+ kind: "tie";
156
+ };
157
+ fork: {
158
+ factor: string;
159
+ value: string;
160
+ }[];
161
+ }[];
162
+ outputs: {
163
+ output: Readonly<{
164
+ kind: "value";
165
+ value: string;
166
+ }> | {
167
+ kind: "tie";
168
+ };
169
+ probability: Readonly<{
170
+ numerator: string;
171
+ denominator: string;
172
+ }>;
173
+ configurations: string[];
174
+ posterior: {
175
+ configuration: string;
176
+ p: Readonly<{
177
+ numerator: string;
178
+ denominator: string;
179
+ }>;
180
+ }[];
181
+ conditionalBits: number | null;
182
+ }[];
183
+ meaning: "conditional entropy of the declared configuration given its deterministic mix output; named fork is injective";
184
+ cause?: undefined;
185
+ required?: undefined;
186
+ workUnit?: undefined;
187
+ };