@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.
- package/README.md +4 -0
- package/TARGET.bound +68 -11
- package/dist/measures/cofailure-pairs.d.ts +113 -0
- package/dist/measures/cofailure-pairs.js +129 -0
- package/dist/measures/index.d.ts +10 -0
- package/dist/measures/index.js +5 -0
- package/dist/measures/marginal-information.d.ts +78 -0
- package/dist/measures/marginal-information.js +71 -0
- package/dist/measures/mix-loss.d.ts +187 -0
- package/dist/measures/mix-loss.js +101 -0
- package/dist/measures/phase-defect.d.ts +40 -0
- package/dist/measures/phase-defect.js +33 -0
- package/dist/measures/tolerant-subsets.d.ts +79 -0
- package/dist/measures/tolerant-subsets.js +60 -0
- package/docs/measures.md +24 -0
- package/docs/paper.md +2 -2
- package/docs/reference.md +6 -1
- package/package.json +1 -1
- package/proofs/cofailure-pairs.json +26 -0
- package/proofs/coordination.json +27 -0
- package/proofs/helly-comb.json +17 -0
- package/proofs/lean/Obligations/Cell.lean +159 -0
- package/proofs/lean/Obligations/HellyComb.lean +19 -0
- package/proofs/lineage-ceiling.json +41 -0
- package/proofs/marginal-information.json +30 -0
- package/proofs/resolution-encounters.json +23 -0
- package/receipts.bound +507 -118
- package/samples/laws/cofailure-pairs/no/sample.json +1 -0
- package/samples/laws/cofailure-pairs/yes/sample.json +20 -0
- package/samples/laws/linage-rest/no/sample.json +19 -0
- package/samples/laws/linage-rest/yes/sample.json +100 -0
- package/samples/laws/lineage-ceiling/no/sample.json +8 -0
- package/samples/laws/lineage-ceiling/yes/sample.json +30 -0
- package/samples/laws/marginal-information/no/sample.json +17 -0
- package/samples/laws/marginal-information/yes/sample.json +459 -0
- package/samples/laws/semilattice/no/sample.json +18 -0
- package/samples/laws/semilattice/yes/sample.json +29 -0
- package/samples/measures/l7-tolerant-k.json +90 -0
- package/samples/measures/l8-phase-defect.json +19 -0
- package/samples/measures/l9-mix-loss.json +36 -0
- package/samples/no/H1-uncertainty.json +24 -0
- package/samples/no/installed-artifact.json +103 -12
- package/samples/yes/H1-uncertainty.json +25 -0
- package/samples/yes/installed-artifact.json +36 -60
- package/vectors/laws/cofailure-pairs.json +2580 -0
- package/vectors/laws/cofailure-pairs.native.json +267 -0
- package/vectors/laws/coordination.json +386 -0
- package/vectors/laws/f1/finite-arithmetic.json +66 -0
- package/vectors/laws/f1/path-witness.json +242 -0
- package/vectors/laws/helly-comb.json +95 -0
- package/vectors/laws/helly-comb.lean.json +94 -0
- package/vectors/laws/implementation-043.json +98 -0
- package/vectors/laws/l7-tolerant-k.json +253 -0
- package/vectors/laws/l8-phase-defect.json +214 -0
- package/vectors/laws/l9-mix-loss.json +130 -0
- package/vectors/laws/linage-rest.native.json +335 -0
- package/vectors/laws/lineage-ceiling.json +790 -0
- package/vectors/laws/lineage-ceiling.native.json +291 -0
- package/vectors/laws/marginal-information.json +868 -0
- package/vectors/laws/marginal-information.native.json +259 -0
- package/vectors/laws/meet-semilattice.json +244 -0
- package/vectors/laws/meet-semilattice.native.json +265 -0
- package/vectors/laws/policy.json +22 -0
- package/vectors/laws/resolution-encounters.json +171 -0
- 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
|
+
}
|
package/dist/measures/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/measures/index.js
CHANGED
|
@@ -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
|
+
};
|