@lapxo/obligations 0.3.5 → 0.4.1
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 +21 -126
- package/TARGET.bound +622 -507
- package/dist/measures/capacity.d.ts +302 -0
- package/dist/measures/capacity.js +168 -0
- package/dist/measures/coincidence.d.ts +111 -0
- package/dist/measures/coincidence.js +81 -0
- package/dist/measures/error-events.d.ts +27 -0
- package/dist/measures/error-events.js +62 -0
- package/dist/measures/experiments.d.ts +15 -0
- package/dist/measures/experiments.js +53 -0
- package/dist/measures/fitting.d.ts +243 -0
- package/dist/measures/fitting.js +115 -0
- package/dist/measures/identification.d.ts +156 -0
- package/dist/measures/identification.js +107 -0
- package/dist/measures/index.d.ts +24 -0
- package/dist/measures/index.js +12 -0
- package/dist/measures/m3-experiment.d.ts +69 -0
- package/dist/measures/m3-experiment.js +70 -0
- package/dist/measures/m3.d.ts +139 -0
- package/dist/measures/m3.js +181 -0
- package/dist/measures/model.d.ts +14 -0
- package/dist/measures/model.js +67 -0
- package/dist/measures/rational.d.ts +19 -0
- package/dist/measures/rational.js +53 -0
- package/dist/measures/spectrum.d.ts +22 -0
- package/dist/measures/spectrum.js +84 -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 +197 -0
- package/docs/paper.md +233 -0
- package/docs/reference.md +495 -0
- package/package.json +1 -93
- package/receipts.bound +118 -0
- package/samples/measures/capacity.json +71 -0
- package/samples/measures/coincidence.json +624 -0
- package/samples/measures/decisions.json +66 -0
- package/samples/measures/descent.json +92 -0
- package/samples/measures/m3.json +3126 -0
- package/samples/measures/observations.json +212 -0
- package/samples/measures/spectrum.json +6530 -0
- package/samples/measures/unanimity-fit.json +166 -0
- package/samples/measures/unanimity.json +76 -0
- package/samples/no/installed-artifact.json +314 -0
- package/samples/yes/algebra-is-closed.json +17 -0
- package/samples/yes/field-uses-price-and-debit.json +9 -0
- package/samples/yes/installed-artifact.json +424 -0
- package/samples/yes/three-theories-one-algebra.json +5 -0
- package/src/field/motion.ts +59 -0
- package/src/lattice/index.ts +79 -0
- package/vectors/measures/capacity.cli.json +97 -0
- package/vectors/measures/coincidence.cli.json +97 -0
- package/vectors/measures/decisions.cli.json +97 -0
- package/vectors/measures/descent.cli.json +97 -0
- package/vectors/measures/implementation.json +82 -0
- package/vectors/measures/installed-artifact.cli.json +63 -0
- package/vectors/measures/m3.cli.json +97 -0
- package/vectors/measures/observations.cli.json +97 -0
- package/vectors/measures/spectrum.cli.json +97 -0
- package/vectors/measures/unanimity-fit.cli.json +97 -0
- package/vectors/measures/unanimity.cli.json +97 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function errorMatrix(rows, policy, requested) {
|
|
2
|
+
if (!policy || !Number.isSafeInteger(policy.minCells) || policy.minCells < 2)
|
|
3
|
+
throw new Error('REFUSE·measure invalid minimum cells');
|
|
4
|
+
if (!Array.isArray(rows) || (requested !== undefined && !Array.isArray(requested)))
|
|
5
|
+
throw new Error('REFUSE·measure invalid event population');
|
|
6
|
+
const cells = new Map();
|
|
7
|
+
for (const row of rows) {
|
|
8
|
+
if (!row || typeof row.event !== 'string' || !row.event || typeof row.origin !== 'string' || !row.origin || (row.error !== 0 && row.error !== 1))
|
|
9
|
+
throw new Error('REFUSE·measure invalid error event');
|
|
10
|
+
const at = cells.get(row.event) ?? new Map();
|
|
11
|
+
if (at.has(row.origin) && at.get(row.origin) !== row.error)
|
|
12
|
+
throw new Error('REFUSE·measure conflicting error event');
|
|
13
|
+
at.set(row.origin, row.error);
|
|
14
|
+
cells.set(row.event, at);
|
|
15
|
+
}
|
|
16
|
+
const origins = [...new Set(requested ?? rows.map(row => row.origin))].sort();
|
|
17
|
+
if (origins.some(origin => typeof origin !== 'string' || !origin))
|
|
18
|
+
throw new Error('REFUSE·measure invalid origin');
|
|
19
|
+
const events = [...cells.keys()].filter(event => origins.some(origin => cells.get(event).has(origin))).sort();
|
|
20
|
+
if (events.length < policy.minCells || !origins.length)
|
|
21
|
+
return { status: 'unmeasured', cause: 'insufficient observed events' };
|
|
22
|
+
if (events.some(event => origins.some(origin => !cells.get(event).has(origin))))
|
|
23
|
+
return { status: 'unmeasured', cause: 'incomplete shared event observations' };
|
|
24
|
+
return { status: 'measured', origins, events, matrix: events.map(event => origins.map(origin => cells.get(event).get(origin))) };
|
|
25
|
+
}
|
|
26
|
+
export function covariance(reading) {
|
|
27
|
+
if (!reading || (reading.status !== 'measured' && reading.status !== 'unmeasured'))
|
|
28
|
+
throw new Error('REFUSE·measure invalid error matrix reading');
|
|
29
|
+
if (reading.status === 'unmeasured')
|
|
30
|
+
return reading;
|
|
31
|
+
if (!Array.isArray(reading.events) || !Array.isArray(reading.origins) || !Array.isArray(reading.matrix))
|
|
32
|
+
throw new Error('REFUSE·measure invalid error matrix');
|
|
33
|
+
const n = reading.events.length, m = reading.origins.length;
|
|
34
|
+
if (!n || !m || reading.events.some(event => typeof event !== 'string' || !event) || reading.origins.some(origin => typeof origin !== 'string' || !origin) ||
|
|
35
|
+
reading.matrix.length !== n || reading.matrix.some(row => !Array.isArray(row) || row.length !== m || row.some(x => x !== 0 && x !== 1)) || new Set(reading.events).size !== n || new Set(reading.origins).size !== m)
|
|
36
|
+
throw new Error('REFUSE·measure invalid error matrix');
|
|
37
|
+
const mean = reading.origins.map((_, i) => reading.matrix.reduce((sum, row) => sum + row[i], 0) / n);
|
|
38
|
+
const matrix = Array.from({ length: m }, (_, i) => Array.from({ length: m }, (_, j) => reading.matrix.reduce((sum, row) => sum + (row[i] - mean[i]) * (row[j] - mean[j]), 0) / n));
|
|
39
|
+
return { ...reading, matrix };
|
|
40
|
+
}
|
|
41
|
+
export const errorCovariance = (rows, policy) => covariance(errorMatrix(rows, policy));
|
|
42
|
+
export function participationRatio(reading) {
|
|
43
|
+
if (!reading || (reading.status !== 'measured' && reading.status !== 'unmeasured'))
|
|
44
|
+
throw new Error('REFUSE·measure invalid covariance reading');
|
|
45
|
+
if (reading.status === 'unmeasured')
|
|
46
|
+
return reading;
|
|
47
|
+
const n = reading.origins?.length;
|
|
48
|
+
if (!n || !Array.isArray(reading.matrix) || reading.matrix.length !== n || reading.matrix.some(row => !Array.isArray(row) || row.length !== n || row.some(value => !Number.isFinite(value))))
|
|
49
|
+
throw new Error('REFUSE·measure invalid covariance matrix');
|
|
50
|
+
for (let i = 0; i < n; i++) {
|
|
51
|
+
if (reading.matrix[i][i] < 0)
|
|
52
|
+
throw new Error('REFUSE·measure negative covariance diagonal');
|
|
53
|
+
for (let j = 0; j < n; j++)
|
|
54
|
+
if (reading.matrix[i][j] !== reading.matrix[j][i])
|
|
55
|
+
throw new Error('REFUSE·measure nonsymmetric covariance');
|
|
56
|
+
}
|
|
57
|
+
const trace = reading.matrix.reduce((sum, row, i) => sum + row[i], 0);
|
|
58
|
+
const square = reading.matrix.reduce((sum, row) => sum + row.reduce((n, value) => n + value * value, 0), 0);
|
|
59
|
+
if (square === 0)
|
|
60
|
+
return { status: 'unmeasured', cause: 'zero observed variation; dependence unidentified' };
|
|
61
|
+
return { status: 'measured', value: trace * trace / square };
|
|
62
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { UnanimityModel } from './model.ts';
|
|
2
|
+
export type ExperimentPolicy = Readonly<{
|
|
3
|
+
seed: number;
|
|
4
|
+
trials: number;
|
|
5
|
+
}>;
|
|
6
|
+
export type ConditionalRate = Readonly<{
|
|
7
|
+
events: number;
|
|
8
|
+
falseEvents: number;
|
|
9
|
+
rate: number | 'unmeasured';
|
|
10
|
+
}>;
|
|
11
|
+
export declare function dissentExperiment(model: UnanimityModel, k: number, policy: ExperimentPolicy): Readonly<{
|
|
12
|
+
unanimity: ConditionalRate;
|
|
13
|
+
majorityWithDissent: ConditionalRate;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function interestExperiment(model: UnanimityModel, bias: number, shared: boolean, policy: ExperimentPolicy): ConditionalRate;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { unanimityFloor } from "./model.js";
|
|
2
|
+
function stream(policy) {
|
|
3
|
+
if (!Number.isInteger(policy.seed) || policy.seed <= 0 || policy.seed > 0xffffffff || !Number.isSafeInteger(policy.trials) || policy.trials < 1)
|
|
4
|
+
throw new Error('REFUSE·experiment invalid seed or trial count');
|
|
5
|
+
let x = policy.seed >>> 0;
|
|
6
|
+
return () => { x ^= x << 13; x ^= x >>> 17; x ^= x << 5; return (x >>> 0) / 4294967296; };
|
|
7
|
+
}
|
|
8
|
+
const rate = (events, falseEvents) => ({ events, falseEvents, rate: events ? falseEvents / events : 'unmeasured' });
|
|
9
|
+
const wrong = (random, n, bias = 0) => random() < bias ? 1 : 1 + Math.floor(random() * (n - 1));
|
|
10
|
+
export function dissentExperiment(model, k, policy) {
|
|
11
|
+
unanimityFloor(model, k);
|
|
12
|
+
const random = stream(policy);
|
|
13
|
+
let unanimous = 0, unanimousFalse = 0, majority = 0, majorityFalse = 0;
|
|
14
|
+
for (let trial = 0; trial < policy.trials; trial++) {
|
|
15
|
+
const common = random() < model.c;
|
|
16
|
+
const counts = new Map();
|
|
17
|
+
for (let i = 0; i < k; i++) {
|
|
18
|
+
const value = common ? 1 : random() < model.p ? wrong(random, model.n) : 0;
|
|
19
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
20
|
+
}
|
|
21
|
+
for (const [value, count] of counts) {
|
|
22
|
+
if (count === k) {
|
|
23
|
+
unanimous++;
|
|
24
|
+
if (value !== 0)
|
|
25
|
+
unanimousFalse++;
|
|
26
|
+
}
|
|
27
|
+
else if (count > k / 2) {
|
|
28
|
+
majority++;
|
|
29
|
+
if (value !== 0)
|
|
30
|
+
majorityFalse++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { unanimity: rate(unanimous, unanimousFalse), majorityWithDissent: rate(majority, majorityFalse) };
|
|
35
|
+
}
|
|
36
|
+
export function interestExperiment(model, bias, shared, policy) {
|
|
37
|
+
unanimityFloor(model, 2);
|
|
38
|
+
if (!Number.isFinite(bias) || bias < 0 || bias > 1)
|
|
39
|
+
throw new Error('REFUSE·experiment invalid interest bias');
|
|
40
|
+
const random = stream(policy);
|
|
41
|
+
let agreed = 0, falseAgreed = 0;
|
|
42
|
+
for (let trial = 0; trial < policy.trials; trial++) {
|
|
43
|
+
const common = random() < model.c;
|
|
44
|
+
const a = common ? 1 : random() < model.p ? wrong(random, model.n, bias) : 0;
|
|
45
|
+
const b = common ? 1 : random() < model.p ? wrong(random, model.n, shared ? bias : 0) : 0;
|
|
46
|
+
if (a === b) {
|
|
47
|
+
agreed++;
|
|
48
|
+
if (a !== 0)
|
|
49
|
+
falseAgreed++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return rate(agreed, falseAgreed);
|
|
53
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { type ErrorEvent, type ObservationPolicy } from './error-events.ts';
|
|
2
|
+
export type FitStructure = Readonly<{
|
|
3
|
+
factors: readonly Readonly<{
|
|
4
|
+
id: string;
|
|
5
|
+
members: readonly string[];
|
|
6
|
+
}>[];
|
|
7
|
+
}>;
|
|
8
|
+
export type FitPolicy = Readonly<{
|
|
9
|
+
observation: ObservationPolicy;
|
|
10
|
+
activeSets: string;
|
|
11
|
+
solveTolerance: number;
|
|
12
|
+
residualTolerance: number;
|
|
13
|
+
bootstrap: Readonly<{
|
|
14
|
+
B: number;
|
|
15
|
+
seed: number;
|
|
16
|
+
prng: 'xorshift32';
|
|
17
|
+
randomWords: number;
|
|
18
|
+
}>;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function fitM3(rows: readonly ErrorEvent[], structure: FitStructure, policy: FitPolicy): {
|
|
21
|
+
status: "unmeasured";
|
|
22
|
+
cause: string;
|
|
23
|
+
model: "M/3";
|
|
24
|
+
parameters: {
|
|
25
|
+
rows: readonly Readonly<{
|
|
26
|
+
event: string;
|
|
27
|
+
origin: string;
|
|
28
|
+
error: 0 | 1;
|
|
29
|
+
}>[];
|
|
30
|
+
structure: Readonly<{
|
|
31
|
+
factors: readonly Readonly<{
|
|
32
|
+
id: string;
|
|
33
|
+
members: readonly string[];
|
|
34
|
+
}>[];
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
policy: Readonly<{
|
|
38
|
+
observation: ObservationPolicy;
|
|
39
|
+
activeSets: string;
|
|
40
|
+
solveTolerance: number;
|
|
41
|
+
residualTolerance: number;
|
|
42
|
+
bootstrap: Readonly<{
|
|
43
|
+
B: number;
|
|
44
|
+
seed: number;
|
|
45
|
+
prng: "xorshift32";
|
|
46
|
+
randomWords: number;
|
|
47
|
+
}>;
|
|
48
|
+
}>;
|
|
49
|
+
conditions: readonly ["one-declared-parameter-population", "declared-sharing-sets", "finite-data", "no-pseudocounts", "fitted-not-handed"];
|
|
50
|
+
procedure: "constrained-log-least-squares/active-set@1";
|
|
51
|
+
} | {
|
|
52
|
+
status: "unmeasured";
|
|
53
|
+
cause: string;
|
|
54
|
+
required: string;
|
|
55
|
+
model: "M/3";
|
|
56
|
+
parameters: {
|
|
57
|
+
rows: readonly Readonly<{
|
|
58
|
+
event: string;
|
|
59
|
+
origin: string;
|
|
60
|
+
error: 0 | 1;
|
|
61
|
+
}>[];
|
|
62
|
+
structure: Readonly<{
|
|
63
|
+
factors: readonly Readonly<{
|
|
64
|
+
id: string;
|
|
65
|
+
members: readonly string[];
|
|
66
|
+
}>[];
|
|
67
|
+
}>;
|
|
68
|
+
};
|
|
69
|
+
policy: Readonly<{
|
|
70
|
+
observation: ObservationPolicy;
|
|
71
|
+
activeSets: string;
|
|
72
|
+
solveTolerance: number;
|
|
73
|
+
residualTolerance: number;
|
|
74
|
+
bootstrap: Readonly<{
|
|
75
|
+
B: number;
|
|
76
|
+
seed: number;
|
|
77
|
+
prng: "xorshift32";
|
|
78
|
+
randomWords: number;
|
|
79
|
+
}>;
|
|
80
|
+
}>;
|
|
81
|
+
conditions: readonly ["one-declared-parameter-population", "declared-sharing-sets", "finite-data", "no-pseudocounts", "fitted-not-handed"];
|
|
82
|
+
procedure: "constrained-log-least-squares/active-set@1";
|
|
83
|
+
} | {
|
|
84
|
+
status: "unmeasured";
|
|
85
|
+
cause: string;
|
|
86
|
+
residuals: number[];
|
|
87
|
+
factors?: undefined;
|
|
88
|
+
residualSumSquares?: undefined;
|
|
89
|
+
residualSurvival?: undefined;
|
|
90
|
+
model: "M/3";
|
|
91
|
+
parameters: {
|
|
92
|
+
rows: readonly Readonly<{
|
|
93
|
+
event: string;
|
|
94
|
+
origin: string;
|
|
95
|
+
error: 0 | 1;
|
|
96
|
+
}>[];
|
|
97
|
+
structure: Readonly<{
|
|
98
|
+
factors: readonly Readonly<{
|
|
99
|
+
id: string;
|
|
100
|
+
members: readonly string[];
|
|
101
|
+
}>[];
|
|
102
|
+
}>;
|
|
103
|
+
};
|
|
104
|
+
policy: Readonly<{
|
|
105
|
+
observation: ObservationPolicy;
|
|
106
|
+
activeSets: string;
|
|
107
|
+
solveTolerance: number;
|
|
108
|
+
residualTolerance: number;
|
|
109
|
+
bootstrap: Readonly<{
|
|
110
|
+
B: number;
|
|
111
|
+
seed: number;
|
|
112
|
+
prng: "xorshift32";
|
|
113
|
+
randomWords: number;
|
|
114
|
+
}>;
|
|
115
|
+
}>;
|
|
116
|
+
conditions: readonly ["one-declared-parameter-population", "declared-sharing-sets", "finite-data", "no-pseudocounts", "fitted-not-handed"];
|
|
117
|
+
procedure: "constrained-log-least-squares/active-set@1";
|
|
118
|
+
} | {
|
|
119
|
+
bootstrap: {
|
|
120
|
+
status: "unmeasured";
|
|
121
|
+
cause: string;
|
|
122
|
+
words: number;
|
|
123
|
+
replicates?: undefined;
|
|
124
|
+
};
|
|
125
|
+
status: "fitted";
|
|
126
|
+
factors: {
|
|
127
|
+
id: string;
|
|
128
|
+
probability: number;
|
|
129
|
+
}[];
|
|
130
|
+
residuals: number[];
|
|
131
|
+
residualSumSquares: number;
|
|
132
|
+
residualSurvival: {
|
|
133
|
+
id: string;
|
|
134
|
+
r: number;
|
|
135
|
+
privateSingletonDecomposition: "unresolved";
|
|
136
|
+
}[];
|
|
137
|
+
cause?: undefined;
|
|
138
|
+
model: "M/3";
|
|
139
|
+
parameters: {
|
|
140
|
+
rows: readonly Readonly<{
|
|
141
|
+
event: string;
|
|
142
|
+
origin: string;
|
|
143
|
+
error: 0 | 1;
|
|
144
|
+
}>[];
|
|
145
|
+
structure: Readonly<{
|
|
146
|
+
factors: readonly Readonly<{
|
|
147
|
+
id: string;
|
|
148
|
+
members: readonly string[];
|
|
149
|
+
}>[];
|
|
150
|
+
}>;
|
|
151
|
+
};
|
|
152
|
+
policy: Readonly<{
|
|
153
|
+
observation: ObservationPolicy;
|
|
154
|
+
activeSets: string;
|
|
155
|
+
solveTolerance: number;
|
|
156
|
+
residualTolerance: number;
|
|
157
|
+
bootstrap: Readonly<{
|
|
158
|
+
B: number;
|
|
159
|
+
seed: number;
|
|
160
|
+
prng: "xorshift32";
|
|
161
|
+
randomWords: number;
|
|
162
|
+
}>;
|
|
163
|
+
}>;
|
|
164
|
+
conditions: readonly ["one-declared-parameter-population", "declared-sharing-sets", "finite-data", "no-pseudocounts", "fitted-not-handed"];
|
|
165
|
+
procedure: "constrained-log-least-squares/active-set@1";
|
|
166
|
+
} | {
|
|
167
|
+
bootstrap: {
|
|
168
|
+
status: "measured" | "unmeasured";
|
|
169
|
+
cause: string | undefined;
|
|
170
|
+
words: number;
|
|
171
|
+
replicates: ({
|
|
172
|
+
status: "unmeasured";
|
|
173
|
+
cause: string;
|
|
174
|
+
residuals?: undefined;
|
|
175
|
+
factors?: undefined;
|
|
176
|
+
residualSumSquares?: undefined;
|
|
177
|
+
residualSurvival?: undefined;
|
|
178
|
+
} | {
|
|
179
|
+
status: "unmeasured";
|
|
180
|
+
cause: string;
|
|
181
|
+
residuals: number[];
|
|
182
|
+
factors?: undefined;
|
|
183
|
+
residualSumSquares?: undefined;
|
|
184
|
+
residualSurvival?: undefined;
|
|
185
|
+
} | {
|
|
186
|
+
status: "fitted";
|
|
187
|
+
factors: {
|
|
188
|
+
id: string;
|
|
189
|
+
probability: number;
|
|
190
|
+
}[];
|
|
191
|
+
residuals: number[];
|
|
192
|
+
residualSumSquares: number;
|
|
193
|
+
residualSurvival: {
|
|
194
|
+
id: string;
|
|
195
|
+
r: number;
|
|
196
|
+
privateSingletonDecomposition: "unresolved";
|
|
197
|
+
}[];
|
|
198
|
+
cause?: undefined;
|
|
199
|
+
})[];
|
|
200
|
+
};
|
|
201
|
+
quantities: "Φ and D require handed parameters; never reported from this fit";
|
|
202
|
+
status: "fitted";
|
|
203
|
+
factors: {
|
|
204
|
+
id: string;
|
|
205
|
+
probability: number;
|
|
206
|
+
}[];
|
|
207
|
+
residuals: number[];
|
|
208
|
+
residualSumSquares: number;
|
|
209
|
+
residualSurvival: {
|
|
210
|
+
id: string;
|
|
211
|
+
r: number;
|
|
212
|
+
privateSingletonDecomposition: "unresolved";
|
|
213
|
+
}[];
|
|
214
|
+
cause?: undefined;
|
|
215
|
+
model: "M/3";
|
|
216
|
+
parameters: {
|
|
217
|
+
rows: readonly Readonly<{
|
|
218
|
+
event: string;
|
|
219
|
+
origin: string;
|
|
220
|
+
error: 0 | 1;
|
|
221
|
+
}>[];
|
|
222
|
+
structure: Readonly<{
|
|
223
|
+
factors: readonly Readonly<{
|
|
224
|
+
id: string;
|
|
225
|
+
members: readonly string[];
|
|
226
|
+
}>[];
|
|
227
|
+
}>;
|
|
228
|
+
};
|
|
229
|
+
policy: Readonly<{
|
|
230
|
+
observation: ObservationPolicy;
|
|
231
|
+
activeSets: string;
|
|
232
|
+
solveTolerance: number;
|
|
233
|
+
residualTolerance: number;
|
|
234
|
+
bootstrap: Readonly<{
|
|
235
|
+
B: number;
|
|
236
|
+
seed: number;
|
|
237
|
+
prng: "xorshift32";
|
|
238
|
+
randomWords: number;
|
|
239
|
+
}>;
|
|
240
|
+
}>;
|
|
241
|
+
conditions: readonly ["one-declared-parameter-population", "declared-sharing-sets", "finite-data", "no-pseudocounts", "fitted-not-handed"];
|
|
242
|
+
procedure: "constrained-log-least-squares/active-set@1";
|
|
243
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { errorMatrix } from "./error-events.js";
|
|
2
|
+
import { refuse } from "./rational.js";
|
|
3
|
+
function solve(matrix, values, tolerance) {
|
|
4
|
+
const a = matrix.map((row, i) => [...row, values[i]]), n = a.length;
|
|
5
|
+
for (let j = 0; j < n; j++) {
|
|
6
|
+
let pivot = j;
|
|
7
|
+
for (let i = j + 1; i < n; i++)
|
|
8
|
+
if (Math.abs(a[i][j]) > Math.abs(a[pivot][j]))
|
|
9
|
+
pivot = i;
|
|
10
|
+
if (Math.abs(a[pivot][j]) <= tolerance)
|
|
11
|
+
return undefined;
|
|
12
|
+
[a[j], a[pivot]] = [a[pivot], a[j]];
|
|
13
|
+
const d = a[j][j];
|
|
14
|
+
a[j] = a[j].map(v => v / d);
|
|
15
|
+
for (let i = 0; i < n; i++)
|
|
16
|
+
if (i !== j) {
|
|
17
|
+
const p = a[i][j];
|
|
18
|
+
a[i] = a[i].map((v, k) => v - p * a[j][k]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return a.map(row => row[n]);
|
|
22
|
+
}
|
|
23
|
+
function* subsets(total, max, start = 0, prefix = []) {
|
|
24
|
+
yield prefix;
|
|
25
|
+
if (prefix.length === max)
|
|
26
|
+
return;
|
|
27
|
+
for (let i = start; i < total; i++)
|
|
28
|
+
yield* subsets(total, max, i + 1, [...prefix, i]);
|
|
29
|
+
}
|
|
30
|
+
export function fitM3(rows, structure, policy) {
|
|
31
|
+
if (!policy || !/^\d+$/.test(policy.activeSets) || !Number.isFinite(policy.solveTolerance) || policy.solveTolerance <= 0 || !Number.isFinite(policy.residualTolerance) || policy.residualTolerance < 0 || !policy.bootstrap || policy.bootstrap.prng !== 'xorshift32' || !Number.isInteger(policy.bootstrap.seed) || policy.bootstrap.seed < 1 || policy.bootstrap.seed > 0xffffffff || !Number.isSafeInteger(policy.bootstrap.B) || policy.bootstrap.B < 0 || !Number.isSafeInteger(policy.bootstrap.randomWords) || policy.bootstrap.randomWords < 0)
|
|
32
|
+
refuse('invalid fitting policy');
|
|
33
|
+
const reading = errorMatrix(rows, policy.observation), metadata = { model: 'M/3', parameters: { rows, structure }, policy, conditions: ['one-declared-parameter-population', 'declared-sharing-sets', 'finite-data', 'no-pseudocounts', 'fitted-not-handed'], procedure: 'constrained-log-least-squares/active-set@1' };
|
|
34
|
+
if (reading.status === 'unmeasured')
|
|
35
|
+
return { ...metadata, ...reading };
|
|
36
|
+
if (!structure || !Array.isArray(structure.factors) || structure.factors.some(f => !f) || new Set(structure.factors.map(f => f.id)).size !== structure.factors.length || structure.factors.some(f => !f.id || !Array.isArray(f.members) || new Set(f.members).size !== f.members.length || f.members.some((id) => !reading.origins.includes(id))))
|
|
37
|
+
refuse('invalid fitting sharing structure');
|
|
38
|
+
const shared = structure.factors.filter(f => f.members.length > 1), n = shared.length, w = reading.origins.length;
|
|
39
|
+
const pairs = [...reading.origins.flatMap((_, i) => reading.origins.flatMap((__, j) => j > i ? [[i, j]] : []))];
|
|
40
|
+
const A = pairs.map(([i, j]) => shared.map(f => f.members.includes(reading.origins[i]) && f.members.includes(reading.origins[j]) ? 1 : 0));
|
|
41
|
+
const H = shared.map((_, i) => shared.map((__, j) => A.reduce((sum, row) => sum + row[i] * row[j], 0)));
|
|
42
|
+
if (n && !solve(H, Array(n).fill(0), policy.solveTolerance))
|
|
43
|
+
return { ...metadata, status: 'unmeasured', cause: 'rank-deficient declared sharing system' };
|
|
44
|
+
let combinations = 1n, term = 1n;
|
|
45
|
+
for (let k = 1; k <= n; k++) {
|
|
46
|
+
term = term * BigInt(n + w - k + 1) / BigInt(k);
|
|
47
|
+
combinations += term;
|
|
48
|
+
}
|
|
49
|
+
const required = combinations * BigInt(policy.bootstrap.B + 1);
|
|
50
|
+
if (required > BigInt(policy.activeSets))
|
|
51
|
+
return { ...metadata, status: 'unmeasured', cause: 'fitting/active-sets', required: String(required) };
|
|
52
|
+
const once = (data) => {
|
|
53
|
+
const s = reading.origins.map((_, i) => data.reduce((sum, row) => sum + (1 - row[i]), 0) / data.length);
|
|
54
|
+
if (s.some(v => v === 0))
|
|
55
|
+
return { status: 'unmeasured', cause: 'model/M misfit: zero correct count' };
|
|
56
|
+
const joints = pairs.map(([i, j]) => data.reduce((sum, row) => sum + (1 - row[i]) * (1 - row[j]), 0) / data.length);
|
|
57
|
+
if (joints.some(v => v === 0))
|
|
58
|
+
return { status: 'unmeasured', cause: 'model/M misfit: zero joint-correct count' };
|
|
59
|
+
const y = joints.map((v, k) => Math.log(v / (s[pairs[k][0]] * s[pairs[k][1]]))), g = shared.map((_, j) => A.reduce((sum, row, i) => sum + row[j] * y[i], 0));
|
|
60
|
+
const C = [...shared.map((_, i) => shared.map((__, j) => i === j ? -1 : 0)), ...reading.origins.map(id => shared.map(f => f.members.includes(id) ? 1 : 0))], d = [...shared.map(() => 0), ...s.map(v => -Math.log(v))];
|
|
61
|
+
let best, bestCost = Infinity;
|
|
62
|
+
for (const active of subsets(n + w, n)) {
|
|
63
|
+
const size = n + active.length, K = Array.from({ length: size }, () => Array(size).fill(0));
|
|
64
|
+
for (let i = 0; i < n; i++)
|
|
65
|
+
for (let j = 0; j < n; j++)
|
|
66
|
+
K[i][j] = H[i][j];
|
|
67
|
+
active.forEach((constraint, k) => C[constraint].forEach((v, j) => { K[n + k][j] = v; K[j][n + k] = v; }));
|
|
68
|
+
const candidate = solve(K, [...g, ...active.map(i => d[i])], policy.solveTolerance);
|
|
69
|
+
if (!candidate)
|
|
70
|
+
continue;
|
|
71
|
+
const alpha = candidate.slice(0, n), lambda = candidate.slice(n);
|
|
72
|
+
if (alpha.some(v => v < 0) || lambda.some(v => v < -policy.solveTolerance) || C.some((row, i) => row.reduce((sum, v, j) => sum + v * alpha[j], 0) > d[i] + policy.solveTolerance))
|
|
73
|
+
continue;
|
|
74
|
+
const cost = A.reduce((sum, row, i) => sum + (row.reduce((s, v, j) => s + v * alpha[j], 0) - y[i]) ** 2, 0);
|
|
75
|
+
if (cost < bestCost) {
|
|
76
|
+
best = alpha;
|
|
77
|
+
bestCost = cost;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!best)
|
|
81
|
+
return { status: 'unmeasured', cause: 'model/M misfit: no feasible numerical solution' };
|
|
82
|
+
const residuals = A.map((row, i) => row.reduce((sum, v, j) => sum + v * best[j], 0) - y[i]);
|
|
83
|
+
if (residuals.some(v => Math.abs(v) > policy.residualTolerance))
|
|
84
|
+
return { status: 'unmeasured', cause: 'model/M misfit: residual tolerance', residuals };
|
|
85
|
+
return { status: 'fitted', factors: shared.map((f, i) => ({ id: f.id, probability: 1 - Math.exp(-best[i]) })), residuals, residualSumSquares: bestCost, residualSurvival: reading.origins.map((id, i) => ({ id, r: s[i] * Math.exp(shared.reduce((sum, f, j) => sum + (f.members.includes(id) ? best[j] : 0), 0)), privateSingletonDecomposition: 'unresolved' })) };
|
|
86
|
+
};
|
|
87
|
+
const fit = once(reading.matrix);
|
|
88
|
+
if (fit.status !== 'fitted')
|
|
89
|
+
return { ...metadata, ...fit };
|
|
90
|
+
let x = policy.bootstrap.seed >>> 0, words = 0;
|
|
91
|
+
const exhausted = Symbol('bootstrap/random-words');
|
|
92
|
+
const index = () => { const N = reading.matrix.length, limit = Math.floor(4294967296 / N) * N; for (;;) {
|
|
93
|
+
if (words === policy.bootstrap.randomWords)
|
|
94
|
+
throw exhausted;
|
|
95
|
+
words++;
|
|
96
|
+
x ^= x << 13;
|
|
97
|
+
x ^= x >>> 17;
|
|
98
|
+
x ^= x << 5;
|
|
99
|
+
const r = x >>> 0;
|
|
100
|
+
if (r < limit)
|
|
101
|
+
return r % N;
|
|
102
|
+
} };
|
|
103
|
+
const replicates = [];
|
|
104
|
+
try {
|
|
105
|
+
for (let b = 0; b < policy.bootstrap.B; b++)
|
|
106
|
+
replicates.push(once(reading.matrix.map(() => reading.matrix[index()])));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error !== exhausted)
|
|
110
|
+
throw error;
|
|
111
|
+
return { ...metadata, ...fit, bootstrap: { status: 'unmeasured', cause: 'bootstrap/random-words', words } };
|
|
112
|
+
}
|
|
113
|
+
const complete = policy.bootstrap.B > 0 && replicates.every(r => r.status === 'fitted');
|
|
114
|
+
return { ...metadata, ...fit, bootstrap: { status: complete ? 'measured' : 'unmeasured', cause: complete ? undefined : policy.bootstrap.B ? 'model/M misfit in bootstrap replicates' : 'no bootstrap replicates requested', words, replicates }, quantities: 'Φ and D require handed parameters; never reported from this fit' };
|
|
115
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { type RationalInput } from './rational.ts';
|
|
2
|
+
export type IdentificationInput = Readonly<{
|
|
3
|
+
witnesses: readonly Readonly<{
|
|
4
|
+
id: string;
|
|
5
|
+
correct: RationalInput;
|
|
6
|
+
}>[];
|
|
7
|
+
factors: readonly Readonly<{
|
|
8
|
+
id: string;
|
|
9
|
+
members: readonly string[];
|
|
10
|
+
}>[];
|
|
11
|
+
pairs: readonly Readonly<{
|
|
12
|
+
a: string;
|
|
13
|
+
b: string;
|
|
14
|
+
correctTogether: RationalInput;
|
|
15
|
+
}>[];
|
|
16
|
+
}>;
|
|
17
|
+
export type IdentificationPolicy = Readonly<{
|
|
18
|
+
budget: string;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function identifyM3(input: IdentificationInput, policy: IdentificationPolicy): {
|
|
21
|
+
status: "unmeasured";
|
|
22
|
+
cause: string;
|
|
23
|
+
model: "M/3";
|
|
24
|
+
parameters: Readonly<{
|
|
25
|
+
witnesses: readonly Readonly<{
|
|
26
|
+
id: string;
|
|
27
|
+
correct: RationalInput;
|
|
28
|
+
}>[];
|
|
29
|
+
factors: readonly Readonly<{
|
|
30
|
+
id: string;
|
|
31
|
+
members: readonly string[];
|
|
32
|
+
}>[];
|
|
33
|
+
pairs: readonly Readonly<{
|
|
34
|
+
a: string;
|
|
35
|
+
b: string;
|
|
36
|
+
correctTogether: RationalInput;
|
|
37
|
+
}>[];
|
|
38
|
+
}>;
|
|
39
|
+
conditions: readonly ["population-moments", "declared-sharing-sets", "positive-correctness", "full-column-rank", "singleton-private-unresolved"];
|
|
40
|
+
policy: Readonly<{
|
|
41
|
+
budget: string;
|
|
42
|
+
}>;
|
|
43
|
+
} | {
|
|
44
|
+
status: "unmeasured";
|
|
45
|
+
cause: string;
|
|
46
|
+
required: string;
|
|
47
|
+
model: "M/3";
|
|
48
|
+
parameters: Readonly<{
|
|
49
|
+
witnesses: readonly Readonly<{
|
|
50
|
+
id: string;
|
|
51
|
+
correct: RationalInput;
|
|
52
|
+
}>[];
|
|
53
|
+
factors: readonly Readonly<{
|
|
54
|
+
id: string;
|
|
55
|
+
members: readonly string[];
|
|
56
|
+
}>[];
|
|
57
|
+
pairs: readonly Readonly<{
|
|
58
|
+
a: string;
|
|
59
|
+
b: string;
|
|
60
|
+
correctTogether: RationalInput;
|
|
61
|
+
}>[];
|
|
62
|
+
}>;
|
|
63
|
+
conditions: readonly ["population-moments", "declared-sharing-sets", "positive-correctness", "full-column-rank", "singleton-private-unresolved"];
|
|
64
|
+
policy: Readonly<{
|
|
65
|
+
budget: string;
|
|
66
|
+
}>;
|
|
67
|
+
} | {
|
|
68
|
+
status: "unmeasured";
|
|
69
|
+
cause: string;
|
|
70
|
+
rank: number;
|
|
71
|
+
columns: number;
|
|
72
|
+
model: "M/3";
|
|
73
|
+
parameters: Readonly<{
|
|
74
|
+
witnesses: readonly Readonly<{
|
|
75
|
+
id: string;
|
|
76
|
+
correct: RationalInput;
|
|
77
|
+
}>[];
|
|
78
|
+
factors: readonly Readonly<{
|
|
79
|
+
id: string;
|
|
80
|
+
members: readonly string[];
|
|
81
|
+
}>[];
|
|
82
|
+
pairs: readonly Readonly<{
|
|
83
|
+
a: string;
|
|
84
|
+
b: string;
|
|
85
|
+
correctTogether: RationalInput;
|
|
86
|
+
}>[];
|
|
87
|
+
}>;
|
|
88
|
+
conditions: readonly ["population-moments", "declared-sharing-sets", "positive-correctness", "full-column-rank", "singleton-private-unresolved"];
|
|
89
|
+
policy: Readonly<{
|
|
90
|
+
budget: string;
|
|
91
|
+
}>;
|
|
92
|
+
} | {
|
|
93
|
+
status: "identified";
|
|
94
|
+
rank: number;
|
|
95
|
+
columns: number;
|
|
96
|
+
factors: ({
|
|
97
|
+
id: string;
|
|
98
|
+
misfit: true;
|
|
99
|
+
survival?: undefined;
|
|
100
|
+
probability?: undefined;
|
|
101
|
+
} | {
|
|
102
|
+
id: string;
|
|
103
|
+
survival: {
|
|
104
|
+
radicand: Readonly<{
|
|
105
|
+
numerator: string;
|
|
106
|
+
denominator: string;
|
|
107
|
+
}>;
|
|
108
|
+
degree: string;
|
|
109
|
+
rational: Readonly<{
|
|
110
|
+
numerator: string;
|
|
111
|
+
denominator: string;
|
|
112
|
+
}> | undefined;
|
|
113
|
+
};
|
|
114
|
+
probability: Readonly<{
|
|
115
|
+
numerator: string;
|
|
116
|
+
denominator: string;
|
|
117
|
+
}> | undefined;
|
|
118
|
+
misfit?: undefined;
|
|
119
|
+
})[];
|
|
120
|
+
residualSurvival: {
|
|
121
|
+
witness: string;
|
|
122
|
+
r: {
|
|
123
|
+
radicand: Readonly<{
|
|
124
|
+
numerator: string;
|
|
125
|
+
denominator: string;
|
|
126
|
+
}>;
|
|
127
|
+
degree: string;
|
|
128
|
+
rational: Readonly<{
|
|
129
|
+
numerator: string;
|
|
130
|
+
denominator: string;
|
|
131
|
+
}> | undefined;
|
|
132
|
+
};
|
|
133
|
+
privateSingletonDecomposition: "unresolved";
|
|
134
|
+
}[];
|
|
135
|
+
representation: "positive exact rational roots";
|
|
136
|
+
model: "M/3";
|
|
137
|
+
parameters: Readonly<{
|
|
138
|
+
witnesses: readonly Readonly<{
|
|
139
|
+
id: string;
|
|
140
|
+
correct: RationalInput;
|
|
141
|
+
}>[];
|
|
142
|
+
factors: readonly Readonly<{
|
|
143
|
+
id: string;
|
|
144
|
+
members: readonly string[];
|
|
145
|
+
}>[];
|
|
146
|
+
pairs: readonly Readonly<{
|
|
147
|
+
a: string;
|
|
148
|
+
b: string;
|
|
149
|
+
correctTogether: RationalInput;
|
|
150
|
+
}>[];
|
|
151
|
+
}>;
|
|
152
|
+
conditions: readonly ["population-moments", "declared-sharing-sets", "positive-correctness", "full-column-rank", "singleton-private-unresolved"];
|
|
153
|
+
policy: Readonly<{
|
|
154
|
+
budget: string;
|
|
155
|
+
}>;
|
|
156
|
+
};
|