@lapxo/obligations 0.3.4 → 0.4.0
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 +12 -135
- package/TARGET.bound +574 -503
- package/dist/measures/error-events.d.ts +27 -0
- package/dist/measures/error-events.js +43 -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 +18 -0
- package/dist/measures/index.js +9 -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/docs/measures.md +149 -0
- package/package.json +1 -93
- package/receipts.bound +75 -0
- package/samples/measures/descent.json +92 -0
- package/samples/measures/m3-reader.mjs +732 -0
- package/samples/measures/m3.json +3126 -0
- package/samples/measures/observations.json +212 -0
- package/samples/measures/reader.mjs +424 -0
- package/samples/measures/spectrum.json +6530 -0
- package/samples/measures/unanimity.json +76 -0
- package/samples/no/installed-artifact.json +302 -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 +397 -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/descent.cli.json +148 -0
- package/vectors/measures/installed-artifact.cli.json +43 -0
- package/vectors/measures/m3.cli.json +148 -0
- package/vectors/measures/observations.cli.json +148 -0
- package/vectors/measures/spectrum.cli.json +148 -0
- package/vectors/measures/unanimity.cli.json +148 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ErrorEvent = Readonly<{
|
|
2
|
+
event: string;
|
|
3
|
+
origin: string;
|
|
4
|
+
error: 0 | 1;
|
|
5
|
+
}>;
|
|
6
|
+
export type Unmeasured = Readonly<{
|
|
7
|
+
status: 'unmeasured';
|
|
8
|
+
cause: string;
|
|
9
|
+
}>;
|
|
10
|
+
export type ErrorMatrix = Readonly<{
|
|
11
|
+
status: 'measured';
|
|
12
|
+
origins: readonly string[];
|
|
13
|
+
events: readonly string[];
|
|
14
|
+
matrix: readonly (readonly number[])[];
|
|
15
|
+
}>;
|
|
16
|
+
export type Covariance = ErrorMatrix;
|
|
17
|
+
export type Scalar = Readonly<{
|
|
18
|
+
status: 'measured';
|
|
19
|
+
value: number;
|
|
20
|
+
}> | Unmeasured;
|
|
21
|
+
export type ObservationPolicy = Readonly<{
|
|
22
|
+
minCells: number;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function errorMatrix(rows: readonly ErrorEvent[], policy: ObservationPolicy, requested?: readonly string[]): ErrorMatrix | Unmeasured;
|
|
25
|
+
export declare function covariance(reading: ErrorMatrix | Unmeasured): Covariance | Unmeasured;
|
|
26
|
+
export declare const errorCovariance: (rows: readonly ErrorEvent[], policy: ObservationPolicy) => Covariance | Unmeasured;
|
|
27
|
+
export declare function participationRatio(reading: Covariance | Unmeasured): Scalar;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function errorMatrix(rows, policy, requested) {
|
|
2
|
+
if (!Number.isSafeInteger(policy.minCells) || policy.minCells < 2)
|
|
3
|
+
throw new Error('REFUSE·measure invalid minimum cells');
|
|
4
|
+
const cells = new Map();
|
|
5
|
+
for (const row of rows) {
|
|
6
|
+
if (!row.event || !row.origin || (row.error !== 0 && row.error !== 1))
|
|
7
|
+
throw new Error('REFUSE·measure invalid error event');
|
|
8
|
+
const at = cells.get(row.event) ?? new Map();
|
|
9
|
+
if (at.has(row.origin) && at.get(row.origin) !== row.error)
|
|
10
|
+
throw new Error('REFUSE·measure conflicting error event');
|
|
11
|
+
at.set(row.origin, row.error);
|
|
12
|
+
cells.set(row.event, at);
|
|
13
|
+
}
|
|
14
|
+
const origins = [...new Set(requested ?? rows.map(row => row.origin))].sort();
|
|
15
|
+
if (origins.some(origin => !origin))
|
|
16
|
+
throw new Error('REFUSE·measure invalid origin');
|
|
17
|
+
const events = [...cells.keys()].filter(event => origins.some(origin => cells.get(event).has(origin))).sort();
|
|
18
|
+
if (events.length < policy.minCells || !origins.length)
|
|
19
|
+
return { status: 'unmeasured', cause: 'insufficient observed events' };
|
|
20
|
+
if (events.some(event => origins.some(origin => !cells.get(event).has(origin))))
|
|
21
|
+
return { status: 'unmeasured', cause: 'incomplete shared event observations' };
|
|
22
|
+
return { status: 'measured', origins, events, matrix: events.map(event => origins.map(origin => cells.get(event).get(origin))) };
|
|
23
|
+
}
|
|
24
|
+
export function covariance(reading) {
|
|
25
|
+
if (reading.status === 'unmeasured')
|
|
26
|
+
return reading;
|
|
27
|
+
const n = reading.events.length, m = reading.origins.length;
|
|
28
|
+
if (!n || !m || reading.matrix.length !== n || reading.matrix.some(row => row.length !== m || row.some(x => x !== 0 && x !== 1)) || new Set(reading.events).size !== n || new Set(reading.origins).size !== m)
|
|
29
|
+
throw new Error('REFUSE·measure invalid error matrix');
|
|
30
|
+
const mean = reading.origins.map((_, i) => reading.matrix.reduce((sum, row) => sum + row[i], 0) / n);
|
|
31
|
+
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));
|
|
32
|
+
return { ...reading, matrix };
|
|
33
|
+
}
|
|
34
|
+
export const errorCovariance = (rows, policy) => covariance(errorMatrix(rows, policy));
|
|
35
|
+
export function participationRatio(reading) {
|
|
36
|
+
if (reading.status === 'unmeasured')
|
|
37
|
+
return reading;
|
|
38
|
+
const trace = reading.matrix.reduce((sum, row, i) => sum + row[i], 0);
|
|
39
|
+
const square = reading.matrix.reduce((sum, row) => sum + row.reduce((n, value) => n + value * value, 0), 0);
|
|
40
|
+
if (square === 0)
|
|
41
|
+
return { status: 'unmeasured', cause: 'zero observed variation; dependence unidentified' };
|
|
42
|
+
return { status: 'measured', value: trace * trace / square };
|
|
43
|
+
}
|
|
@@ -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: "unmeasured" | "measured";
|
|
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
|
+
};
|