@lapxo/obligations 0.4.0 → 0.4.2
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 +32 -2
- package/TARGET.bound +151 -30
- package/dist/lattice/obligatory.js +29 -1
- package/dist/measures/capacity.d.ts +324 -0
- package/dist/measures/capacity.js +184 -0
- package/dist/measures/coincidence.d.ts +111 -0
- package/dist/measures/coincidence.js +81 -0
- package/dist/measures/confidence.d.ts +73 -0
- package/dist/measures/confidence.js +52 -0
- package/dist/measures/error-events.js +23 -4
- package/dist/measures/fitting.d.ts +1 -1
- package/dist/measures/index.d.ts +8 -0
- package/dist/measures/index.js +4 -0
- package/dist/measures/unanimity-fit.d.ts +156 -0
- package/dist/measures/unanimity-fit.js +133 -0
- package/docs/img/cell.svg +28 -0
- package/docs/img/logo-fork.svg +6 -0
- package/docs/img/logo.svg +6 -0
- package/docs/img/price.svg +28 -0
- package/docs/img/thermometer.svg +28 -0
- package/docs/img/withdrawal.svg +28 -0
- package/docs/learn/in-phase.md +46 -0
- package/docs/learn/price.md +52 -0
- package/docs/learn/signature.md +50 -0
- package/docs/learn/thermometer.md +58 -0
- package/docs/learn/withdrawal.md +51 -0
- package/docs/measures.md +64 -0
- package/docs/paper.md +233 -0
- package/docs/reference.md +495 -0
- package/package.json +1 -1
- package/receipts.bound +138 -70
- package/samples/measures/capacity.json +71 -0
- package/samples/measures/coincidence.json +624 -0
- package/samples/measures/common-cause-capacity.json +48 -0
- package/samples/measures/confidence/union-bound.json +54 -0
- package/samples/measures/confidence.json +70 -0
- package/samples/measures/decisions.json +66 -0
- package/samples/measures/t12/common-cause.json +32 -0
- package/samples/measures/tolerant-count.json +144 -0
- package/samples/measures/unanimity-fit.json +166 -0
- package/samples/no/installed-artifact.json +19 -3
- package/samples/yes/installed-artifact.json +42 -6
- package/vectors/measures/capacity.cli.json +97 -0
- package/vectors/measures/coincidence.cli.json +97 -0
- package/vectors/measures/common-cause-capacity.cli.json +97 -0
- package/vectors/measures/confidence/expected.json +26 -0
- package/vectors/measures/confidence.cli.json +97 -0
- package/vectors/measures/decisions.cli.json +97 -0
- package/vectors/measures/descent.cli.json +19 -70
- package/vectors/measures/implementation-042.json +86 -0
- package/vectors/measures/implementation.json +82 -0
- package/vectors/measures/installed-artifact.cli.json +8 -8
- package/vectors/measures/m3.cli.json +19 -70
- package/vectors/measures/observations.cli.json +19 -70
- package/vectors/measures/spectrum.cli.json +18 -69
- package/vectors/measures/t12/expected.json +35 -0
- package/vectors/measures/tolerant-count.cli.json +97 -0
- package/vectors/measures/unanimity-fit.cli.json +97 -0
- package/vectors/measures/unanimity.cli.json +19 -70
- package/samples/measures/m3-reader.mjs +0 -732
- package/samples/measures/reader.mjs +0 -424
|
@@ -18,6 +18,7 @@ export function restOf(c, world = new Map()) {
|
|
|
18
18
|
const out = [];
|
|
19
19
|
const held = new Set();
|
|
20
20
|
const edge = [...c.restsOn];
|
|
21
|
+
const above = new Map();
|
|
21
22
|
for (let i = 0; i < edge.length; i += 1) {
|
|
22
23
|
const name = edge[i];
|
|
23
24
|
if (name === own)
|
|
@@ -26,7 +27,34 @@ export function restOf(c, world = new Map()) {
|
|
|
26
27
|
continue;
|
|
27
28
|
held.add(name);
|
|
28
29
|
out.push(name);
|
|
29
|
-
|
|
30
|
+
const rests = world.get(name)?.restsOn ?? [];
|
|
31
|
+
above.set(name, rests);
|
|
32
|
+
for (const ancestor of rests)
|
|
33
|
+
edge.push(ancestor);
|
|
34
|
+
}
|
|
35
|
+
const status = new Map();
|
|
36
|
+
for (const name of out) {
|
|
37
|
+
if (status.has(name))
|
|
38
|
+
continue;
|
|
39
|
+
status.set(name, 'visiting');
|
|
40
|
+
const path = [{ name, next: 0 }];
|
|
41
|
+
while (path.length) {
|
|
42
|
+
const head = path[path.length - 1];
|
|
43
|
+
const rests = above.get(head.name);
|
|
44
|
+
if (head.next === rests.length) {
|
|
45
|
+
status.set(head.name, 'done');
|
|
46
|
+
path.pop();
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const ancestor = rests[head.next++];
|
|
50
|
+
const seen = status.get(ancestor);
|
|
51
|
+
if (seen === 'visiting')
|
|
52
|
+
throw new UnitError('rest-cycle', `obligations: REFUSE·rest ${own} reaches a cycle at ${ancestor}: an order of rest with a cycle has no fold to reach`, { at: own });
|
|
53
|
+
if (seen === undefined) {
|
|
54
|
+
status.set(ancestor, 'visiting');
|
|
55
|
+
path.push({ name: ancestor, next: 0 });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
30
58
|
}
|
|
31
59
|
return out;
|
|
32
60
|
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { type RationalInput } from './rational.ts';
|
|
2
|
+
export type CapacityModel = Readonly<{
|
|
3
|
+
n: number;
|
|
4
|
+
c: RationalInput;
|
|
5
|
+
p: RationalInput;
|
|
6
|
+
}>;
|
|
7
|
+
export type LogarithmPolicy = Readonly<{
|
|
8
|
+
logarithm: 'binary64-log2';
|
|
9
|
+
}>;
|
|
10
|
+
export type CapacityPolicy = LogarithmPolicy & Readonly<{
|
|
11
|
+
budget: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type DecisionRule = 'plurality-uniform-ties@1' | 'posterior-map-uniform-ties@1';
|
|
14
|
+
export declare function capacityBound(n: number, c: RationalInput, policy: LogarithmPolicy): {
|
|
15
|
+
model: "single-dominant-factor@1";
|
|
16
|
+
parameters: {
|
|
17
|
+
n: number;
|
|
18
|
+
c: Readonly<{
|
|
19
|
+
numerator: string;
|
|
20
|
+
denominator: string;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
policy: Readonly<{
|
|
24
|
+
logarithm: "binary64-log2";
|
|
25
|
+
}>;
|
|
26
|
+
status: "measured";
|
|
27
|
+
unit: "bits";
|
|
28
|
+
value: number;
|
|
29
|
+
meaning: "upper bound for every k; achievability depends on the private channel";
|
|
30
|
+
};
|
|
31
|
+
export declare function commonCauseCapacityBound(n: number, c: RationalInput, accuracy: RationalInput, policy: LogarithmPolicy): {
|
|
32
|
+
model: "single-covering-symmetric-cause@1";
|
|
33
|
+
parameters: {
|
|
34
|
+
n: number;
|
|
35
|
+
c: Readonly<{
|
|
36
|
+
numerator: string;
|
|
37
|
+
denominator: string;
|
|
38
|
+
}>;
|
|
39
|
+
q: Readonly<{
|
|
40
|
+
numerator: string;
|
|
41
|
+
denominator: string;
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
policy: Readonly<{
|
|
45
|
+
logarithm: "binary64-log2";
|
|
46
|
+
}>;
|
|
47
|
+
status: "measured";
|
|
48
|
+
unit: "bits";
|
|
49
|
+
conditions: readonly ["uniform-truth", "cause-indicator-independent-of-truth", "active-cause-copies-one-symmetric-channel", "inactive-private-channel-unrestricted"];
|
|
50
|
+
value: number;
|
|
51
|
+
meaning: "upper bound after revealing cause indicator; copies do not add; no achievability claim";
|
|
52
|
+
};
|
|
53
|
+
export declare function capacityP0(n: number, c: RationalInput, policy: LogarithmPolicy): {
|
|
54
|
+
value: number;
|
|
55
|
+
meaning: "mutual information at p=0 for every positive k";
|
|
56
|
+
model: "single-dominant-factor@1";
|
|
57
|
+
parameters: {
|
|
58
|
+
n: number;
|
|
59
|
+
c: Readonly<{
|
|
60
|
+
numerator: string;
|
|
61
|
+
denominator: string;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
64
|
+
policy: Readonly<{
|
|
65
|
+
logarithm: "binary64-log2";
|
|
66
|
+
}>;
|
|
67
|
+
status: "measured";
|
|
68
|
+
unit: "bits";
|
|
69
|
+
};
|
|
70
|
+
export declare function unanimityNoFactor(k: number, n: number, p: RationalInput, policy: Readonly<{
|
|
71
|
+
budget: string;
|
|
72
|
+
}>): {
|
|
73
|
+
status: "unmeasured";
|
|
74
|
+
cause: string;
|
|
75
|
+
required: string;
|
|
76
|
+
workUnit: "rational-bit-bound";
|
|
77
|
+
model?: undefined;
|
|
78
|
+
parameters?: undefined;
|
|
79
|
+
policy?: undefined;
|
|
80
|
+
value?: undefined;
|
|
81
|
+
} | {
|
|
82
|
+
status: "measured";
|
|
83
|
+
model: "single-dominant-factor@1";
|
|
84
|
+
parameters: {
|
|
85
|
+
k: number;
|
|
86
|
+
n: number;
|
|
87
|
+
p: Readonly<{
|
|
88
|
+
numerator: string;
|
|
89
|
+
denominator: string;
|
|
90
|
+
}>;
|
|
91
|
+
};
|
|
92
|
+
policy: Readonly<{
|
|
93
|
+
budget: string;
|
|
94
|
+
}>;
|
|
95
|
+
workUnit: "rational-bit-bound";
|
|
96
|
+
value: Readonly<{
|
|
97
|
+
numerator: string;
|
|
98
|
+
denominator: string;
|
|
99
|
+
}>;
|
|
100
|
+
cause?: undefined;
|
|
101
|
+
required?: undefined;
|
|
102
|
+
};
|
|
103
|
+
export declare function mutualInformation(k: number, model: CapacityModel, policy: CapacityPolicy): {
|
|
104
|
+
status: "measured";
|
|
105
|
+
unit: "bits";
|
|
106
|
+
value: number;
|
|
107
|
+
probabilitySum: Readonly<{
|
|
108
|
+
numerator: string;
|
|
109
|
+
denominator: string;
|
|
110
|
+
}>;
|
|
111
|
+
work: string;
|
|
112
|
+
loss: {
|
|
113
|
+
exclusion: number;
|
|
114
|
+
privateUncertainty: number;
|
|
115
|
+
factorAmbiguity: number;
|
|
116
|
+
};
|
|
117
|
+
exactConditionalEntropy: {
|
|
118
|
+
mass: Readonly<{
|
|
119
|
+
numerator: string;
|
|
120
|
+
denominator: string;
|
|
121
|
+
}>;
|
|
122
|
+
posterior: Readonly<{
|
|
123
|
+
numerator: string;
|
|
124
|
+
denominator: string;
|
|
125
|
+
}>;
|
|
126
|
+
}[];
|
|
127
|
+
model: "single-dominant-factor@1";
|
|
128
|
+
parameters: {
|
|
129
|
+
k: number;
|
|
130
|
+
n: number;
|
|
131
|
+
c: Readonly<{
|
|
132
|
+
numerator: string;
|
|
133
|
+
denominator: string;
|
|
134
|
+
}>;
|
|
135
|
+
p: Readonly<{
|
|
136
|
+
numerator: string;
|
|
137
|
+
denominator: string;
|
|
138
|
+
}>;
|
|
139
|
+
};
|
|
140
|
+
policy: CapacityPolicy;
|
|
141
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
142
|
+
} | {
|
|
143
|
+
status: "unmeasured";
|
|
144
|
+
cause: string;
|
|
145
|
+
requiredAtLeast: string;
|
|
146
|
+
model: "single-dominant-factor@1";
|
|
147
|
+
parameters: {
|
|
148
|
+
k: number;
|
|
149
|
+
n: number;
|
|
150
|
+
c: Readonly<{
|
|
151
|
+
numerator: string;
|
|
152
|
+
denominator: string;
|
|
153
|
+
}>;
|
|
154
|
+
p: Readonly<{
|
|
155
|
+
numerator: string;
|
|
156
|
+
denominator: string;
|
|
157
|
+
}>;
|
|
158
|
+
};
|
|
159
|
+
policy: CapacityPolicy;
|
|
160
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
161
|
+
} | {
|
|
162
|
+
decision: {
|
|
163
|
+
rule: DecisionRule | undefined;
|
|
164
|
+
accuracy: Readonly<{
|
|
165
|
+
numerator: string;
|
|
166
|
+
denominator: string;
|
|
167
|
+
}>;
|
|
168
|
+
information: number;
|
|
169
|
+
joint: Readonly<{
|
|
170
|
+
numerator: string;
|
|
171
|
+
denominator: string;
|
|
172
|
+
}>[][];
|
|
173
|
+
tiePolicy: "uniform-over-maximizers";
|
|
174
|
+
};
|
|
175
|
+
status: "measured";
|
|
176
|
+
unit: "bits";
|
|
177
|
+
value: number;
|
|
178
|
+
probabilitySum: Readonly<{
|
|
179
|
+
numerator: string;
|
|
180
|
+
denominator: string;
|
|
181
|
+
}>;
|
|
182
|
+
work: string;
|
|
183
|
+
loss: {
|
|
184
|
+
exclusion: number;
|
|
185
|
+
privateUncertainty: number;
|
|
186
|
+
factorAmbiguity: number;
|
|
187
|
+
};
|
|
188
|
+
exactConditionalEntropy: {
|
|
189
|
+
mass: Readonly<{
|
|
190
|
+
numerator: string;
|
|
191
|
+
denominator: string;
|
|
192
|
+
}>;
|
|
193
|
+
posterior: Readonly<{
|
|
194
|
+
numerator: string;
|
|
195
|
+
denominator: string;
|
|
196
|
+
}>;
|
|
197
|
+
}[];
|
|
198
|
+
model: "single-dominant-factor@1";
|
|
199
|
+
parameters: {
|
|
200
|
+
k: number;
|
|
201
|
+
n: number;
|
|
202
|
+
c: Readonly<{
|
|
203
|
+
numerator: string;
|
|
204
|
+
denominator: string;
|
|
205
|
+
}>;
|
|
206
|
+
p: Readonly<{
|
|
207
|
+
numerator: string;
|
|
208
|
+
denominator: string;
|
|
209
|
+
}>;
|
|
210
|
+
};
|
|
211
|
+
policy: CapacityPolicy;
|
|
212
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
213
|
+
};
|
|
214
|
+
export declare function decisionInformation(k: number, model: CapacityModel, rule: DecisionRule, policy: CapacityPolicy): {
|
|
215
|
+
status: "measured";
|
|
216
|
+
unit: "bits";
|
|
217
|
+
value: number;
|
|
218
|
+
probabilitySum: Readonly<{
|
|
219
|
+
numerator: string;
|
|
220
|
+
denominator: string;
|
|
221
|
+
}>;
|
|
222
|
+
work: string;
|
|
223
|
+
loss: {
|
|
224
|
+
exclusion: number;
|
|
225
|
+
privateUncertainty: number;
|
|
226
|
+
factorAmbiguity: number;
|
|
227
|
+
};
|
|
228
|
+
exactConditionalEntropy: {
|
|
229
|
+
mass: Readonly<{
|
|
230
|
+
numerator: string;
|
|
231
|
+
denominator: string;
|
|
232
|
+
}>;
|
|
233
|
+
posterior: Readonly<{
|
|
234
|
+
numerator: string;
|
|
235
|
+
denominator: string;
|
|
236
|
+
}>;
|
|
237
|
+
}[];
|
|
238
|
+
model: "single-dominant-factor@1";
|
|
239
|
+
parameters: {
|
|
240
|
+
k: number;
|
|
241
|
+
n: number;
|
|
242
|
+
c: Readonly<{
|
|
243
|
+
numerator: string;
|
|
244
|
+
denominator: string;
|
|
245
|
+
}>;
|
|
246
|
+
p: Readonly<{
|
|
247
|
+
numerator: string;
|
|
248
|
+
denominator: string;
|
|
249
|
+
}>;
|
|
250
|
+
};
|
|
251
|
+
policy: CapacityPolicy;
|
|
252
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
253
|
+
} | {
|
|
254
|
+
status: "unmeasured";
|
|
255
|
+
cause: string;
|
|
256
|
+
requiredAtLeast: string;
|
|
257
|
+
model: "single-dominant-factor@1";
|
|
258
|
+
parameters: {
|
|
259
|
+
k: number;
|
|
260
|
+
n: number;
|
|
261
|
+
c: Readonly<{
|
|
262
|
+
numerator: string;
|
|
263
|
+
denominator: string;
|
|
264
|
+
}>;
|
|
265
|
+
p: Readonly<{
|
|
266
|
+
numerator: string;
|
|
267
|
+
denominator: string;
|
|
268
|
+
}>;
|
|
269
|
+
};
|
|
270
|
+
policy: CapacityPolicy;
|
|
271
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
272
|
+
} | {
|
|
273
|
+
decision: {
|
|
274
|
+
rule: DecisionRule | undefined;
|
|
275
|
+
accuracy: Readonly<{
|
|
276
|
+
numerator: string;
|
|
277
|
+
denominator: string;
|
|
278
|
+
}>;
|
|
279
|
+
information: number;
|
|
280
|
+
joint: Readonly<{
|
|
281
|
+
numerator: string;
|
|
282
|
+
denominator: string;
|
|
283
|
+
}>[][];
|
|
284
|
+
tiePolicy: "uniform-over-maximizers";
|
|
285
|
+
};
|
|
286
|
+
status: "measured";
|
|
287
|
+
unit: "bits";
|
|
288
|
+
value: number;
|
|
289
|
+
probabilitySum: Readonly<{
|
|
290
|
+
numerator: string;
|
|
291
|
+
denominator: string;
|
|
292
|
+
}>;
|
|
293
|
+
work: string;
|
|
294
|
+
loss: {
|
|
295
|
+
exclusion: number;
|
|
296
|
+
privateUncertainty: number;
|
|
297
|
+
factorAmbiguity: number;
|
|
298
|
+
};
|
|
299
|
+
exactConditionalEntropy: {
|
|
300
|
+
mass: Readonly<{
|
|
301
|
+
numerator: string;
|
|
302
|
+
denominator: string;
|
|
303
|
+
}>;
|
|
304
|
+
posterior: Readonly<{
|
|
305
|
+
numerator: string;
|
|
306
|
+
denominator: string;
|
|
307
|
+
}>;
|
|
308
|
+
}[];
|
|
309
|
+
model: "single-dominant-factor@1";
|
|
310
|
+
parameters: {
|
|
311
|
+
k: number;
|
|
312
|
+
n: number;
|
|
313
|
+
c: Readonly<{
|
|
314
|
+
numerator: string;
|
|
315
|
+
denominator: string;
|
|
316
|
+
}>;
|
|
317
|
+
p: Readonly<{
|
|
318
|
+
numerator: string;
|
|
319
|
+
denominator: string;
|
|
320
|
+
}>;
|
|
321
|
+
};
|
|
322
|
+
policy: CapacityPolicy;
|
|
323
|
+
conditions: readonly ["uniform-truth", "factor-independent-of-truth", "uniform-shared-wrong-value", "independent-homogeneous-private-channel"];
|
|
324
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { add, sub, mul, div, q, pow, zero, one, pack, probability } from "./rational.js";
|
|
2
|
+
const refuse = (cause) => { throw new Error('REFUSE·capacity ' + cause); };
|
|
3
|
+
function cardinality(n) { if (!Number.isSafeInteger(n) || n < 2)
|
|
4
|
+
refuse('invalid cardinality'); }
|
|
5
|
+
function logPolicy(policy) { if (!policy || policy.logarithm !== 'binary64-log2')
|
|
6
|
+
refuse('logarithm policy required'); }
|
|
7
|
+
function logInteger(value) {
|
|
8
|
+
const bits = value.toString(2).length, shift = Math.max(0, bits - 53);
|
|
9
|
+
return Math.log2(Number(value >> BigInt(shift))) + shift;
|
|
10
|
+
}
|
|
11
|
+
function logQ(value) { return logInteger(value[0]) - logInteger(value[1]); }
|
|
12
|
+
function numeric(value) { return value[0] === 0n ? 0 : 2 ** logQ(value); }
|
|
13
|
+
function entropy(probabilities) {
|
|
14
|
+
let value = 0, correction = 0;
|
|
15
|
+
for (const p of probabilities) {
|
|
16
|
+
if (p[0] === 0n)
|
|
17
|
+
continue;
|
|
18
|
+
const term = -numeric(p) * logQ(p) - correction, next = value + term;
|
|
19
|
+
correction = (next - value) - term;
|
|
20
|
+
value = next;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
export function capacityBound(n, c, policy) {
|
|
25
|
+
cardinality(n);
|
|
26
|
+
logPolicy(policy);
|
|
27
|
+
const factor = probability(c);
|
|
28
|
+
return { model: 'single-dominant-factor@1', parameters: { n, c: pack(factor) }, policy,
|
|
29
|
+
status: 'measured', unit: 'bits', value: Math.log2(n) - numeric(factor) * Math.log2(n - 1),
|
|
30
|
+
meaning: 'upper bound for every k; achievability depends on the private channel' };
|
|
31
|
+
}
|
|
32
|
+
export function commonCauseCapacityBound(n, c, accuracy, policy) {
|
|
33
|
+
cardinality(n);
|
|
34
|
+
logPolicy(policy);
|
|
35
|
+
if (accuracy === undefined || accuracy === null)
|
|
36
|
+
refuse('common-cause accuracy q required');
|
|
37
|
+
const factor = probability(c), correct = probability(accuracy), wrong = sub(one, correct);
|
|
38
|
+
const uniform = correct[0] * BigInt(n) === correct[1];
|
|
39
|
+
const channel = uniform ? 0 : correct[0] === 0n ? Math.log1p(1 / (n - 1)) / Math.LN2 :
|
|
40
|
+
Math.max(0, Math.log2(n) - entropy([correct, wrong]) - numeric(wrong) * Math.log2(n - 1));
|
|
41
|
+
return { model: 'single-covering-symmetric-cause@1',
|
|
42
|
+
parameters: { n, c: pack(factor), q: pack(correct) }, policy, status: 'measured', unit: 'bits',
|
|
43
|
+
conditions: ['uniform-truth', 'cause-indicator-independent-of-truth',
|
|
44
|
+
'active-cause-copies-one-symmetric-channel', 'inactive-private-channel-unrestricted'],
|
|
45
|
+
value: numeric(sub(one, factor)) * Math.log2(n) + numeric(factor) * channel,
|
|
46
|
+
meaning: 'upper bound after revealing cause indicator; copies do not add; no achievability claim' };
|
|
47
|
+
}
|
|
48
|
+
export function capacityP0(n, c, policy) {
|
|
49
|
+
const bound = capacityBound(n, c, policy), factor = probability(c);
|
|
50
|
+
return { ...bound, value: bound.value - entropy([factor, sub(one, factor)]), meaning: 'mutual information at p=0 for every positive k' };
|
|
51
|
+
}
|
|
52
|
+
export function unanimityNoFactor(k, n, p, policy) {
|
|
53
|
+
cardinality(n);
|
|
54
|
+
if (!Number.isSafeInteger(k) || k < 1)
|
|
55
|
+
refuse('invalid witness count');
|
|
56
|
+
if (!policy || typeof policy.budget !== 'string' || !/^\d+$/.test(policy.budget))
|
|
57
|
+
refuse('power budget required');
|
|
58
|
+
const error = probability(p);
|
|
59
|
+
const required = BigInt(k) * (BigInt(error[1].toString(2).length) + BigInt(BigInt(n - 1).toString(2).length) + 2n);
|
|
60
|
+
if (required > BigInt(policy.budget))
|
|
61
|
+
return { status: 'unmeasured', cause: 'capacity/power-budget', required: String(required), workUnit: 'rational-bit-bound' };
|
|
62
|
+
return { status: 'measured', model: 'single-dominant-factor@1', parameters: { k, n, p: pack(error) },
|
|
63
|
+
policy, workUnit: 'rational-bit-bound', value: pack(add(pow(sub(one, error), k), div(pow(error, k), pow(q(BigInt(n - 1)), k - 1)))) };
|
|
64
|
+
}
|
|
65
|
+
function term(terms, mass, posterior) {
|
|
66
|
+
if (mass[0] === 0n)
|
|
67
|
+
return;
|
|
68
|
+
const key = posterior.join('/'), previous = terms.get(key);
|
|
69
|
+
terms.set(key, { mass: previous ? add(previous.mass, mass) : mass, posterior });
|
|
70
|
+
}
|
|
71
|
+
function evaluateEntropy(terms) {
|
|
72
|
+
let sum = 0, correction = 0;
|
|
73
|
+
for (const item of terms.values()) {
|
|
74
|
+
const contribution = -numeric(item.mass) * logQ(item.posterior) - correction, next = sum + contribution;
|
|
75
|
+
correction = (next - sum) - contribution;
|
|
76
|
+
sum = next;
|
|
77
|
+
}
|
|
78
|
+
return sum;
|
|
79
|
+
}
|
|
80
|
+
function prepared(k, model, policy) {
|
|
81
|
+
if (!model)
|
|
82
|
+
refuse('model required');
|
|
83
|
+
cardinality(model.n);
|
|
84
|
+
logPolicy(policy);
|
|
85
|
+
if (!Number.isSafeInteger(k) || k < 1)
|
|
86
|
+
refuse('invalid witness count');
|
|
87
|
+
if (typeof policy.budget !== 'string' || !/^\d+$/.test(policy.budget))
|
|
88
|
+
refuse('invalid enumeration budget');
|
|
89
|
+
const c = probability(model.c), p = probability(model.p), cap = BigInt(policy.budget);
|
|
90
|
+
let work = 1n;
|
|
91
|
+
for (let i = 0; i <= k; i++) {
|
|
92
|
+
work *= BigInt(model.n);
|
|
93
|
+
if (work > cap)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
return { c, p, cap, work };
|
|
97
|
+
}
|
|
98
|
+
export function mutualInformation(k, model, policy) {
|
|
99
|
+
return evaluateCapacity(k, model, policy);
|
|
100
|
+
}
|
|
101
|
+
export function decisionInformation(k, model, rule, policy) {
|
|
102
|
+
if (rule !== 'plurality-uniform-ties@1' && rule !== 'posterior-map-uniform-ties@1')
|
|
103
|
+
refuse('unknown decision rule');
|
|
104
|
+
return evaluateCapacity(k, model, policy, rule);
|
|
105
|
+
}
|
|
106
|
+
function evaluateCapacity(k, model, policy, rule) {
|
|
107
|
+
const { c, p, cap, work } = prepared(k, model, policy), n = model.n;
|
|
108
|
+
const metadata = { model: 'single-dominant-factor@1', parameters: { k, n, c: pack(c), p: pack(p) }, policy,
|
|
109
|
+
conditions: ['uniform-truth', 'factor-independent-of-truth', 'uniform-shared-wrong-value',
|
|
110
|
+
'independent-homogeneous-private-channel'] };
|
|
111
|
+
if (work > cap)
|
|
112
|
+
return { ...metadata, status: 'unmeasured', cause: 'capacity/budget', requiredAtLeast: String(work) };
|
|
113
|
+
const terms = new Map(), privateTerms = new Map();
|
|
114
|
+
const decisions = rule ? Array.from({ length: n }, () => Array.from({ length: n }, () => zero)) : undefined;
|
|
115
|
+
let total = zero, accuracy = zero;
|
|
116
|
+
const counts = Array(n).fill(0);
|
|
117
|
+
function visit(depth) {
|
|
118
|
+
if (depth < k) {
|
|
119
|
+
for (let value = 0; value < n; value++) {
|
|
120
|
+
counts[value]++;
|
|
121
|
+
visit(depth + 1);
|
|
122
|
+
counts[value]--;
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const unanimous = counts.findIndex(count => count === k), privateMass = [], joint = [];
|
|
127
|
+
for (let t = 0; t < n; t++) {
|
|
128
|
+
const channel = mul(pow(sub(one, p), counts[t]), pow(div(p, q(BigInt(n - 1))), k - counts[t]));
|
|
129
|
+
const independent = div(channel, q(BigInt(n)));
|
|
130
|
+
privateMass.push(independent);
|
|
131
|
+
const forced = unanimous >= 0 && unanimous !== t ? div(c, q(BigInt(n) * BigInt(n - 1))) : zero;
|
|
132
|
+
joint.push(add(mul(sub(one, c), independent), forced));
|
|
133
|
+
}
|
|
134
|
+
const mass = joint.reduce(add, zero), privateTotal = privateMass.reduce(add, zero);
|
|
135
|
+
total = add(total, mass);
|
|
136
|
+
joint.forEach(value => { if (value[0] !== 0n)
|
|
137
|
+
term(terms, value, div(value, mass)); });
|
|
138
|
+
privateMass.forEach(value => { if (value[0] !== 0n)
|
|
139
|
+
term(privateTerms, value, div(value, privateTotal)); });
|
|
140
|
+
if (!decisions || mass[0] === 0n)
|
|
141
|
+
return;
|
|
142
|
+
const winners = [];
|
|
143
|
+
if (rule === 'plurality-uniform-ties@1') {
|
|
144
|
+
const largest = Math.max(...counts);
|
|
145
|
+
counts.forEach((count, i) => { if (count === largest)
|
|
146
|
+
winners.push(i); });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
let largest = zero;
|
|
150
|
+
for (const value of joint)
|
|
151
|
+
if (value[0] * largest[1] > largest[0] * value[1])
|
|
152
|
+
largest = value;
|
|
153
|
+
joint.forEach((value, i) => { if (value[0] * largest[1] === largest[0] * value[1])
|
|
154
|
+
winners.push(i); });
|
|
155
|
+
}
|
|
156
|
+
for (let t = 0; t < n; t++)
|
|
157
|
+
for (const answer of winners) {
|
|
158
|
+
const mass = div(joint[t], q(BigInt(winners.length)));
|
|
159
|
+
decisions[t][answer] = add(decisions[t][answer], mass);
|
|
160
|
+
if (answer === t)
|
|
161
|
+
accuracy = add(accuracy, mass);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
visit(0);
|
|
165
|
+
const conditional = evaluateEntropy(terms), privateConditional = evaluateEntropy(privateTerms);
|
|
166
|
+
const result = { ...metadata, status: 'measured', unit: 'bits',
|
|
167
|
+
value: Math.log2(n) - conditional, probabilitySum: pack(total), work: String(work),
|
|
168
|
+
loss: { exclusion: numeric(c) * Math.log2(n - 1), privateUncertainty: numeric(sub(one, c)) * privateConditional,
|
|
169
|
+
factorAmbiguity: conditional - numeric(c) * Math.log2(n - 1) - numeric(sub(one, c)) * privateConditional },
|
|
170
|
+
exactConditionalEntropy: [...terms.values()].map(({ mass, posterior }) => ({ mass: pack(mass), posterior: pack(posterior) })) };
|
|
171
|
+
if (!decisions)
|
|
172
|
+
return result;
|
|
173
|
+
const decisionTerms = new Map();
|
|
174
|
+
for (let answer = 0; answer < n; answer++) {
|
|
175
|
+
const mass = decisions.reduce((sum, row) => add(sum, row[answer]), zero);
|
|
176
|
+
if (mass[0] === 0n)
|
|
177
|
+
continue;
|
|
178
|
+
for (const row of decisions)
|
|
179
|
+
if (row[answer][0] !== 0n)
|
|
180
|
+
term(decisionTerms, row[answer], div(row[answer], mass));
|
|
181
|
+
}
|
|
182
|
+
return { ...result, decision: { rule, accuracy: pack(accuracy), information: Math.log2(n) - evaluateEntropy(decisionTerms),
|
|
183
|
+
joint: decisions.map(row => row.map(pack)), tiePolicy: 'uniform-over-maximizers' } };
|
|
184
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type RationalInput } from './rational.ts';
|
|
2
|
+
export type ValueReading = Readonly<{
|
|
3
|
+
event: string;
|
|
4
|
+
origin: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}>;
|
|
7
|
+
export type TruthReading = Readonly<{
|
|
8
|
+
event: string;
|
|
9
|
+
value: string;
|
|
10
|
+
kind: 'truth';
|
|
11
|
+
}>;
|
|
12
|
+
export type CoincidenceReference = Readonly<{
|
|
13
|
+
model: 'independent-uniform-wrong@1';
|
|
14
|
+
values: readonly string[];
|
|
15
|
+
}> & (Readonly<{
|
|
16
|
+
marginals: 'handed';
|
|
17
|
+
errors: readonly Readonly<{
|
|
18
|
+
origin: string;
|
|
19
|
+
probability: RationalInput;
|
|
20
|
+
}>[];
|
|
21
|
+
}> | Readonly<{
|
|
22
|
+
marginals: 'estimated-by-truth';
|
|
23
|
+
}>);
|
|
24
|
+
export type CoincidencePolicy = Readonly<{
|
|
25
|
+
minEvents: number;
|
|
26
|
+
minEventsPerTruth: number;
|
|
27
|
+
budget: string;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function coincidenceExcess(rows: readonly ValueReading[], truth: readonly TruthReading[], selection: readonly string[], reference: CoincidenceReference, policy: CoincidencePolicy): {
|
|
30
|
+
status: "unmeasured";
|
|
31
|
+
cause: string;
|
|
32
|
+
required: string;
|
|
33
|
+
reference: CoincidenceReference;
|
|
34
|
+
policy: Readonly<{
|
|
35
|
+
minEvents: number;
|
|
36
|
+
minEventsPerTruth: number;
|
|
37
|
+
budget: string;
|
|
38
|
+
}>;
|
|
39
|
+
selection: string[];
|
|
40
|
+
order: number;
|
|
41
|
+
interpretation: "coincidence relative to the declared reference; not causal identification";
|
|
42
|
+
} | {
|
|
43
|
+
status: "unmeasured";
|
|
44
|
+
cause: string;
|
|
45
|
+
events: number;
|
|
46
|
+
reference: CoincidenceReference;
|
|
47
|
+
policy: Readonly<{
|
|
48
|
+
minEvents: number;
|
|
49
|
+
minEventsPerTruth: number;
|
|
50
|
+
budget: string;
|
|
51
|
+
}>;
|
|
52
|
+
selection: string[];
|
|
53
|
+
order: number;
|
|
54
|
+
interpretation: "coincidence relative to the declared reference; not causal identification";
|
|
55
|
+
} | {
|
|
56
|
+
status: "measured";
|
|
57
|
+
events: number;
|
|
58
|
+
falseCoincidences: number;
|
|
59
|
+
observed: Readonly<{
|
|
60
|
+
numerator: string;
|
|
61
|
+
denominator: string;
|
|
62
|
+
}>;
|
|
63
|
+
expected: Readonly<{
|
|
64
|
+
numerator: string;
|
|
65
|
+
denominator: string;
|
|
66
|
+
}>;
|
|
67
|
+
delta: Readonly<{
|
|
68
|
+
numerator: string;
|
|
69
|
+
denominator: string;
|
|
70
|
+
}>;
|
|
71
|
+
ratio: {
|
|
72
|
+
status: "unmeasured";
|
|
73
|
+
cause: string;
|
|
74
|
+
value?: undefined;
|
|
75
|
+
} | {
|
|
76
|
+
status: "measured";
|
|
77
|
+
value: Readonly<{
|
|
78
|
+
numerator: string;
|
|
79
|
+
denominator: string;
|
|
80
|
+
}>;
|
|
81
|
+
cause?: undefined;
|
|
82
|
+
};
|
|
83
|
+
strata: {
|
|
84
|
+
truth: string;
|
|
85
|
+
events: number;
|
|
86
|
+
marginals: {
|
|
87
|
+
origin: string;
|
|
88
|
+
error: Readonly<{
|
|
89
|
+
numerator: string;
|
|
90
|
+
denominator: string;
|
|
91
|
+
}>;
|
|
92
|
+
}[];
|
|
93
|
+
reference: Readonly<{
|
|
94
|
+
numerator: string;
|
|
95
|
+
denominator: string;
|
|
96
|
+
}>;
|
|
97
|
+
}[];
|
|
98
|
+
uncertainty: {
|
|
99
|
+
status: "unmeasured";
|
|
100
|
+
cause: string;
|
|
101
|
+
};
|
|
102
|
+
reference: CoincidenceReference;
|
|
103
|
+
policy: Readonly<{
|
|
104
|
+
minEvents: number;
|
|
105
|
+
minEventsPerTruth: number;
|
|
106
|
+
budget: string;
|
|
107
|
+
}>;
|
|
108
|
+
selection: string[];
|
|
109
|
+
order: number;
|
|
110
|
+
interpretation: "coincidence relative to the declared reference; not causal identification";
|
|
111
|
+
};
|