@mark1russell7/rigor-bench 0.2.17 → 0.2.19

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 CHANGED
@@ -46,6 +46,11 @@ v.comparison; // the cases that gain and dro
46
46
  | `limitVerdict(candidate, aa, options)` | The verdict of the claim "the ratio of each case is at most `limit`": `within`, `beyond` or `cannot decide`. |
47
47
  | `tailLimitVerdict(candidate, aa, options)` | The verdict of the claim "the median and a high value of each case are at most their limits", for a bench with few rounds. |
48
48
  | `caseTails(records, rank)` | The value at one rank of the ratios of each case, from the largest. |
49
+ | `modelTest(reads, options)` | The read of each CPU model against the rest, for each case: `differs`, `no read`, `too-few` or `not asked`. |
50
+ | `visibleShift(values, rest, level, direction, options)` | The smallest shift in one direction that a test can see at this count and level, and the smallest p of this count. |
51
+ | `modelRowText(row, family)` | The words of one row. No word says that a model is the same as the rest. |
52
+ | `limitOnModel(limits, model)` | The limit of a gain on one CPU model: the gain limit, or the ceiling with its words. |
53
+ | `armModels(pairs)` | The CPU models of each arm of a set of pairs, and if all the reads come from one known model. |
49
54
  | `tailFalseFails(aa, options)` | The cases of an A/A arm that the rule of `tailLimitVerdict` fails, and apart from these the cases with a limit under 1. |
50
55
  | `gateOf(cases)` | The gate of a list of case outcomes: `pass`, `block` or `no read`. |
51
56
  | `notSlowerVerdict(jobs, options)` | The gate of the claim "not slower by more than the margin", with looks and a family level. |
@@ -114,6 +119,21 @@ The verdict has no interval, so a limit needs a margin from the spread of earlie
114
119
 
115
120
  A limit under 1 states a gain, and an A/A pair shows no gain. So the A/A arm has no read of that case, and the case goes in `gains` with that reason. The false fails of a gain limit come from repeat reads of an unchanged tree against its fixed base.
116
121
 
122
+ ## A read of each CPU model
123
+
124
+ Runners of one label have different CPU models. A check of the pool passes when most reads come from the other models, so it can hide a model that reads apart. `modelTest` reads each model against the rest of its case. It uses a two-sample permutation test of the median, with one Holm family over all the rows.
125
+
126
+ - `differs`: the adjusted p is under `alpha`, and the shift is at `resolution` or more, when the caller gives one.
127
+ - `no read`: the test finds no difference. Few reads have little power, so this does not say that the model is the same. The row gives `floor`, the smallest p of that count, and `level`, the first level of Holm in the family. When `floor` is over `level`, the words of `modelRowText` say that this size cannot reach the level, with no flip. A row of that size reads `differs` only if other rows read `differs` before it, because each of those rows raises the level. When the level is in reach, `visible` gives the smallest shift that this test can see at that count and family. The option `searchVisible` set to false stops only that search.
128
+ - `too-few`: the model has under `minReads` reads, or the case has no other model. The row keeps its facts, and it stays out of the family.
129
+ - `not asked`: the option `asked` leaves the row out of the question. State the question before the reads, because a family that is chosen after a read has no valid p.
130
+
131
+ A read of `differs` tells where to look. It does not name a cause, because the reads of one model can share a runner image and a date.
132
+
133
+ `limitOnModel` gives the limit of a gain that reads on some models only. A model with no earlier reads gets the ceiling, and its read carries the words of `CEILING_ONLY`. A pass at the ceiling stops a loss, and it does not hold the gain.
134
+
135
+ `armModels` reads the models of the two arms of a set of pairs. When the arms come from different computers, a ratio compares the computers. A record with no model reads as `unknown`, and `unknown` is not one known model.
136
+
117
137
  ## A gate that can fail
118
138
 
119
139
  A check that blocks a merge needs a result that can fail. `gateOf` reads the case outcomes of a verdict, and `notSlowerVerdict` gives the claim of a speed gate.
@@ -323,6 +343,22 @@ Each finding fails a gate, and each one carries its tests with `n`, `ties`, `p`
323
343
 
324
344
  `winClaim(check, measure, entry, stratum)` gives `alone`, `show`, `likeControl`, `censored` and `open`. The fields are about this entry. The field `likeControl` is true when this entry does not beat a control on the measure and beats it on no partner. A win by another entry clears nothing. The field `alone` is true only when this entry itself beats each control on the measure, so a measure with no control is not `alone`.
325
345
 
346
+ `jointClaim(check, measures, entry, stratum)` reads a set of measures together, because one measure cannot call a win when each control wins a measure and loses a partner:
347
+
348
+ ```ts
349
+ const joint = jointClaim(check, ["fill", "order"], "my-entry");
350
+ joint.verdict; // "survives", "dominated", "not-ahead" or "no-read"
351
+ ```
352
+
353
+ | Verdict | Meaning |
354
+ | --- | --- |
355
+ | `survives` | No control dominates the entry, and the entry beats at least one control on each measure. |
356
+ | `dominated` | A control beats the entry on at least one measure, and the entry beats that control on none. The field `dominatedBy` names it. |
357
+ | `not-ahead` | The entry beats no control on a measure with each read made. The field `notAhead` names it. |
358
+ | `no-read` | A test with too few keys, or a measure with no control, decides the claim. This verdict is not a pass. |
359
+
360
+ A tie does not clear a control, and the win of another entry gives this entry no credit. The function adds no test: it reads the tests of `checkMeasures`, with their Holm families.
361
+
326
362
  ## The fresh runner
327
363
 
328
364
  The entry `@mark1russell7/rigor-bench/fresh` starts child processes, so it is for Node only. The root entry stays free of `node:child_process`.
package/build/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as caseRatios, i as armOrder, n as CaseRatio, o as checkPairedTime, r as PairedTime, s as firstArm, t as Arm } from "./paired.js";
2
- import { BootstrapOptions, Comparison as Comparison$1, Interval } from "@mark1russell7/rigor-statistics";
2
+ import { BootstrapOptions, Comparison as Comparison$1, Interval, TwoSamplePermutationOptions } from "@mark1russell7/rigor-statistics";
3
3
  /** One value of one build on one case. The value is finite and 0 or more. */
4
4
  interface BudgetRound {
5
5
  /** The name of the case: one input of the bench. */
@@ -790,6 +790,137 @@ export declare function rungVerdict(rung: Rung, record: RungRecord | undefined,
790
790
  * a `RangeError` for a ladder with problems, because a verdict on a bad ladder has no meaning. See `checkLadder`.
791
791
  */
792
792
  export declare function ladderVerdict(ladder: Ladder, records: readonly RungRecord[], options: LadderOptions): LadderVerdict;
793
+ /** The name of a record with no CPU model. */
794
+ export declare const UNKNOWN_MODEL = "unknown";
795
+ /** One value of one case, from one read on one CPU model. */
796
+ interface ModelRead {
797
+ readonly case: string;
798
+ readonly model: string;
799
+ /** The value of the read: a ratio or a time. All the reads of a case have one unit. */
800
+ readonly value: number;
801
+ }
802
+ /** The options of `modelTest`. */
803
+ interface ModelTestOptions {
804
+ /** The smallest count of reads of a model that the test reads. The default is 5. */
805
+ readonly minReads?: number;
806
+ /** The level of the Holm family over all the rows that the test reads. The default is 0.05. */
807
+ readonly alpha?: number;
808
+ /** The smallest shift of a median that counts. Without it, only the test decides. It has no default. */
809
+ readonly resolution?: number;
810
+ /**
811
+ * The largest number of splits of each test. The default gives 100 counts at the level of the strictest step of Holm, and
812
+ * 10 000 or more. A test reads each split when their count is this number or less, so its p is exact.
813
+ */
814
+ readonly runs?: number;
815
+ /** The seed of the splits. The default is 1. */
816
+ readonly seed?: number | string;
817
+ /**
818
+ * The rows of the question, when a question has fewer rows than the data. Each other row reads `not asked`, and it stays
819
+ * out of the family. State the question before the reads: a family that is chosen after a read has no valid p.
820
+ */
821
+ readonly asked?: (row: {
822
+ readonly case: string;
823
+ readonly model: string;
824
+ }) => boolean;
825
+ /**
826
+ * With false, the test does not search for the smallest shift of a row of `no read`, and the words of the row say so.
827
+ * This search is most of the cost of the test. The default is true.
828
+ */
829
+ readonly searchVisible?: boolean;
830
+ }
831
+ /** The result of one model on one case. */
832
+ type ModelStatus = "differs" | "no read" | "too-few" | "not asked";
833
+ /** One model on one case, against all the other reads of that case. */
834
+ interface ModelRow {
835
+ readonly case: string;
836
+ readonly model: string;
837
+ readonly reads: number;
838
+ readonly median: number;
839
+ readonly low: number;
840
+ readonly high: number;
841
+ /** The count of the reads of the other models. */
842
+ readonly restReads: number;
843
+ /** The median of the reads of the other models, or undefined when no other model has a read. */
844
+ readonly restMedian: number | undefined;
845
+ /** The median of this model minus the median of the rest, or undefined when no other model has a read. */
846
+ readonly shift: number | undefined;
847
+ /** The p after the Holm correction, or undefined for a row outside the family. */
848
+ readonly p: number | undefined;
849
+ readonly status: ModelStatus;
850
+ /** The size of the smallest shift that this test can see at this count and family, in the direction of the shift of this row. It applies to a row of `no read`. It is undefined on each other row, and also when no shift can reach the family. */
851
+ readonly visible: number | undefined;
852
+ /** The p of this row with its reads far from the rest, in the direction of its shift, for a row of `no read`. No shift gives a smaller p. */
853
+ readonly floor: number | undefined;
854
+ /**
855
+ * The first level of Holm in this family, alpha over the size of the family, for a row of `no read`. A row with a floor over this level reads `differs` only if other rows read `differs` before it.
856
+ * Each of those rows raises the level.
857
+ */
858
+ readonly level: number | undefined;
859
+ }
860
+ /** The result of `modelTest`. */
861
+ interface ModelTest {
862
+ /** Each model on each case, sorted by case and by model. */
863
+ readonly rows: readonly ModelRow[];
864
+ /** The count of the rows in the Holm family. */
865
+ readonly family: number;
866
+ /** Each row that reads `differs`. */
867
+ readonly differs: readonly ModelRow[];
868
+ }
869
+ /** The result of `visibleShift`. */
870
+ interface VisibleShift {
871
+ /** The size of the smallest shift that reaches the level, or undefined when no shift reaches it. */
872
+ readonly shift: number | undefined;
873
+ /** The p with the values far from the rest, in this direction. No shift in this direction gives a smaller p. */
874
+ readonly floor: number;
875
+ }
876
+ /**
877
+ * This function finds the smallest shift of the median of `values` from the median of `rest` that gives a p of `level` or less.
878
+ * It keeps the spread of the values, and it reads one direction: 1 for a shift up, and -1 for a shift down. The two
879
+ * directions can differ, because the spread of the values and of the rest need not be symmetric.
880
+ *
881
+ * The level of a family is its alpha over its size, the strictest step of Holm. So the result errs on the side of a shift
882
+ * that is too large. The cost is 9 permutation tests when the level is in reach, and 1 when it is not.
883
+ */
884
+ export declare function visibleShift(values: readonly number[], rest: readonly number[], level: number, direction: 1 | -1, options?: TwoSamplePermutationOptions): VisibleShift;
885
+ /** This function reads each CPU model against the rest, for each case. */
886
+ export declare function modelTest(reads: readonly ModelRead[], options?: ModelTestOptions): ModelTest;
887
+ /** This function gives the words of one row. No word says that a model is the same as the rest. */
888
+ export declare function modelRowText(row: ModelRow, family: number): string;
889
+ /** The limits of a gain that reads on some CPU models only. */
890
+ interface ModelLimits {
891
+ /** The limit that holds the gain, on a model of `models`. */
892
+ readonly gain: number;
893
+ /** The limit on each other model. It stops a loss, and it does not hold the gain. */
894
+ readonly ceiling: number;
895
+ /** The models with a read of the gain. */
896
+ readonly models: readonly string[];
897
+ }
898
+ /** The limit of a gain on one CPU model. */
899
+ interface LimitOnModel {
900
+ readonly limit: number;
901
+ /** This field is true when a pass at this limit holds the gain. */
902
+ readonly holdsGain: boolean;
903
+ /** The words for a read that does not hold the gain, or undefined when it holds the gain. */
904
+ readonly note: string | undefined;
905
+ }
906
+ /** The words of a read at the ceiling. A pass at the ceiling is not a gain held. */
907
+ export declare const CEILING_ONLY = "gain not read on this model, ceiling only";
908
+ /** This function gives the limit of a gain on one CPU model. A model with no earlier reads gets the ceiling. */
909
+ export declare function limitOnModel(limits: ModelLimits, model: string): LimitOnModel;
910
+ /** One pair of reads, with the CPU model of each arm. A record with no model has `undefined`. */
911
+ interface ArmPair {
912
+ readonly a: string | undefined;
913
+ readonly b: string | undefined;
914
+ }
915
+ /** The CPU models of the two arms of a set of pairs. */
916
+ interface ArmModels {
917
+ readonly a: readonly string[];
918
+ readonly b: readonly string[];
919
+ /** This field is true when all the reads of the two arms have one known model. Otherwise, a ratio can compare two computers. */
920
+ readonly one: boolean;
921
+ }
922
+ /** This function gives the sorted CPU models of each arm, and tells if all the reads come from one known model. */
923
+ export declare function armModels(pairs: readonly ArmPair[]): ArmModels;
793
924
  /** The value at one rank of the ratios of one case, from the largest. */
794
925
  interface CaseTail {
795
926
  readonly case: string;
@@ -1087,6 +1218,49 @@ interface WinClaim {
1087
1218
  /** The reason and the date, for a measure with no control. */
1088
1219
  readonly open: NoControl | undefined;
1089
1220
  }
1221
+ /** The read of one control over the set of measures of a joint claim. */
1222
+ interface JointControl {
1223
+ readonly control: string;
1224
+ /** The measures where the entry beats this control. */
1225
+ readonly beaten: readonly string[];
1226
+ /** The measures where this control beats the entry. */
1227
+ readonly lostTo: readonly string[];
1228
+ /** The measures with no read of the entry and this control: no test, or too few keys. A tie on enough keys is a read and does not count here. */
1229
+ readonly unread: readonly string[];
1230
+ /** This field is true when no measure is unread and the entry beats this control on none. This control also beats the entry on at least one. */
1231
+ readonly dominates: boolean;
1232
+ }
1233
+ /** The read of one measure of a joint claim. */
1234
+ interface JointMeasure {
1235
+ readonly measure: string;
1236
+ /** The controls that the entry beats on this measure, each by its own test. */
1237
+ readonly beats: readonly string[];
1238
+ /** The controls in `beats` that also pass the level divided by the count of controls on this measure. Only these count for `survives`. */
1239
+ readonly clears: readonly string[];
1240
+ /** The controls that tie the entry on this measure: enough shared keys, and too few keys that differ. */
1241
+ readonly tied: readonly string[];
1242
+ /** The controls of the set with no read on this measure: no test, or too few keys. A measure with no test lists each control of the set. */
1243
+ readonly unread: readonly string[];
1244
+ }
1245
+ /**
1246
+ * The verdict of a joint claim. `dominated`: a control dominates the entry. `not-ahead`: on a measure with each read made, the entry beats no control.
1247
+ * `no-read`: a read that is not there decides the claim, and that is not a pass. `survives`: no control dominates the entry, and the entry clears a control on each measure.
1248
+ */
1249
+ type JointVerdict = "survives" | "dominated" | "not-ahead" | "no-read";
1250
+ /** What one real entry earned on a set of measures taken together. */
1251
+ interface JointClaim {
1252
+ readonly entry: string;
1253
+ readonly stratum: string | undefined;
1254
+ readonly verdict: JointVerdict;
1255
+ /** The controls that dominate the entry. */
1256
+ readonly dominatedBy: readonly string[];
1257
+ /** The controls with each test read, where neither side beats the other on any measure. The entry earned nothing over them, so a page shows this list. */
1258
+ readonly equalledBy: readonly string[];
1259
+ /** The measures where the entry clears no control. */
1260
+ readonly notAhead: readonly string[];
1261
+ readonly controls: readonly JointControl[];
1262
+ readonly measures: readonly JointMeasure[];
1263
+ }
1090
1264
  /**
1091
1265
  * This function reads the controls and the spread of each measure of a bench.
1092
1266
  *
@@ -1111,6 +1285,16 @@ export declare function checkMeasures(rows: readonly MeasureRow[], options: Meas
1111
1285
  * The function throws a `RangeError` for a measure with no read and for an entry that the rows do not hold.
1112
1286
  */
1113
1287
  export declare function winClaim(check: MeasureChecks, measure: string, entry: string, stratum?: string): WinClaim;
1288
+ /**
1289
+ * This function tells what one real entry earned on a set of measures taken together. Each win of a control loses a partner, so one measure cannot call a win.
1290
+ * A control dominates the entry when the entry beats it on no measure of the set, and it beats the entry on at least one. A tie does not clear a control.
1291
+ * The entry survives when no control dominates it and it clears at least one control on each measure. The function adds no test: it reads the tests of `check`.
1292
+ * One win over any control is enough on a measure, so a beat clears only at the level divided by the count of controls there.
1293
+ * All measures must hold, so the measures add no division. Dominance reads the plain tests, because that is the cautious direction.
1294
+ * With a stratum, the function reads that stratum. Without one, it reads the rows with no stratum.
1295
+ * The function throws a `RangeError` for an empty set, a repeat, a measure with no rule and an entry that the rows do not hold.
1296
+ */
1297
+ export declare function jointClaim(check: MeasureChecks, measures: readonly string[], entry: string, stratum?: string): JointClaim;
1114
1298
  /** The options of a power test. */
1115
1299
  interface PowerOptions {
1116
1300
  /** The count of the cases of the candidate and of the A/A arm. */
@@ -1173,5 +1357,5 @@ interface Rounds {
1173
1357
  * keeps the work.
1174
1358
  */
1175
1359
  export declare function timeRounds(arms: Readonly<Record<Arm, () => number>>, options: RoundsOptions): Rounds;
1176
- export { type Arm, type Balanced, type Band, type BandKind, type BandOptions, type BudgetLook, type BudgetLooksOptions, type BudgetLooksVerdict, type BudgetMore, type BudgetOptions, type BudgetOutcome, type BudgetResult, type BudgetRound, type BudgetVerdict, type CaseBudget, type CaseGate, type CaseInterval, type CaseLimit, type CaseRatio, type CaseTail, type CaseTailLimit, type Comparison, type ControlFactors, type ControlOptions, type ControlRead, type ControlRow, type EntryCount, type Gate, type GateCaseOutcome, type GateJobRead, type GateOutcome, type GroupCounts, type GroupFactor, type GuardRead, type JobLimitVerdict, type JobRecords, type JobVerdict, type Ladder, type LadderDimension, type LadderOptions, type LadderVerdict, type LimitOptions, type LimitOutcome, type LimitVerdict, type MeasureCheck, type MeasureCheckOptions, type MeasureChecks, type MeasureFinding, type MeasureFindingKind, type MeasureRow, type MeasureRule, type MeasuredFactor, type MedianBudget, type NoControl, type NoFactor, type NoiseCheck, type NotSlowerOptions, type NotSlowerRead, type NotSlowerVerdict, type PairedTime, type PartnerRead, type PoolFacts, type PoolOptions, type PooledJob, type PooledLimitVerdict, type PooledRecords, type PooledVerdict, type Power, type PowerOptions, type QuantileBudget, type RateBudget, type Rounds, type RoundsOptions, type RunPairs, type Rung, type RungBudget, type RungEvidence, type RungRecord, type RungVerdict, type SpreadRead, type TailFalseFails, type TailGainCase, type TailLimit, type TailLimitOptions, type TailLimitVerdict, type TailRuleOptions, type TimeOutcome, type TimeVerdict, type VerdictOptions, type VerdictRule, type WinClaim, type WorstBudget, armOrder, caseRatios, checkPairedTime, firstArm };
1360
+ export { type Arm, type ArmModels, type ArmPair, type Balanced, type Band, type BandKind, type BandOptions, type BudgetLook, type BudgetLooksOptions, type BudgetLooksVerdict, type BudgetMore, type BudgetOptions, type BudgetOutcome, type BudgetResult, type BudgetRound, type BudgetVerdict, type CaseBudget, type CaseGate, type CaseInterval, type CaseLimit, type CaseRatio, type CaseTail, type CaseTailLimit, type Comparison, type ControlFactors, type ControlOptions, type ControlRead, type ControlRow, type EntryCount, type Gate, type GateCaseOutcome, type GateJobRead, type GateOutcome, type GroupCounts, type GroupFactor, type GuardRead, type JobLimitVerdict, type JobRecords, type JobVerdict, type JointClaim, type JointControl, type JointMeasure, type JointVerdict, type Ladder, type LadderDimension, type LadderOptions, type LadderVerdict, type LimitOnModel, type LimitOptions, type LimitOutcome, type LimitVerdict, type MeasureCheck, type MeasureCheckOptions, type MeasureChecks, type MeasureFinding, type MeasureFindingKind, type MeasureRow, type MeasureRule, type MeasuredFactor, type MedianBudget, type ModelLimits, type ModelRead, type ModelRow, type ModelStatus, type ModelTest, type ModelTestOptions, type NoControl, type NoFactor, type NoiseCheck, type NotSlowerOptions, type NotSlowerRead, type NotSlowerVerdict, type PairedTime, type PartnerRead, type PoolFacts, type PoolOptions, type PooledJob, type PooledLimitVerdict, type PooledRecords, type PooledVerdict, type Power, type PowerOptions, type QuantileBudget, type RateBudget, type Rounds, type RoundsOptions, type RunPairs, type Rung, type RungBudget, type RungEvidence, type RungRecord, type RungVerdict, type SpreadRead, type TailFalseFails, type TailGainCase, type TailLimit, type TailLimitOptions, type TailLimitVerdict, type TailRuleOptions, type TimeOutcome, type TimeVerdict, type VerdictOptions, type VerdictRule, type VisibleShift, type WinClaim, type WorstBudget, armOrder, caseRatios, checkPairedTime, firstArm };
1177
1361
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["Comparison"],"sources":["../src/budget.ts","../src/verdict.ts","../src/limit.ts","../src/merge.ts","../src/gate.ts","../src/control.ts","../src/ladder.ts","../src/tail.ts","../src/measures.ts","../src/power.ts","../src/rounds.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":["Comparison"],"sources":["../src/budget.ts","../src/verdict.ts","../src/limit.ts","../src/merge.ts","../src/gate.ts","../src/control.ts","../src/ladder.ts","../src/model.ts","../src/tail.ts","../src/measures.ts","../src/power.ts","../src/rounds.ts"],"mappings":"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"}
package/build/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { i as firstArm, n as caseRatios, r as checkPairedTime, t as armOrder } from "./paired.js";
2
- import { bootstrapMedian, bootstrapTwo, compareRatios, holm, median, medianInterval, partialConjunction, quantile, quantileInterval, quantileSamples, signTest, signTestPower, wilson } from "@mark1russell7/rigor-statistics";
2
+ import { bootstrapMedian, bootstrapTwo, compareRatios, holm, median, medianInterval, partialConjunction, quantile, quantileInterval, quantileSamples, signTest, signTestPower, twoSamplePermutation, wilson } from "@mark1russell7/rigor-statistics";
3
3
  import { random } from "@mark1russell7/rigor-generators";
4
4
  // src/budget.ts
5
5
  /**
@@ -1128,6 +1128,212 @@ function limitVerdict(candidate, aa, options) {
1128
1128
  function round$1(x) {
1129
1129
  return Math.round(x * 1e4) / 1e4;
1130
1130
  }
1131
+ // src/model.ts
1132
+ /**
1133
+ * The CPU model test. A limit from reads of many runners can hide a model that reads apart. A check of the pool
1134
+ * passes when most reads come from the other models. This test reads each model against the rest, for each case.
1135
+ *
1136
+ * A read of `differs` is about these reads. A read of `no read` does not say that a model is the same, because few reads
1137
+ * have little power. Its row gives the smallest p that this count can reach, and the level. So a row at a size that cannot
1138
+ * reach the level says so with no flip. Its row also gives the smallest shift that this test can see at that count and
1139
+ * family. A model with too few reads gives `too-few`, and it stays out of the Holm family.
1140
+ *
1141
+ * Reads of one model are not independent of their runner image and their date. The p holds only if the reads of a case
1142
+ * can change places under the null. So a read of `differs` tells where to look, and it does not name a cause.
1143
+ */
1144
+ /** The name of a record with no CPU model. */
1145
+ const UNKNOWN_MODEL = "unknown";
1146
+ /** This function groups a list of reads by a key, and sorts the groups by their key. */
1147
+ function groupsOf(reads, key) {
1148
+ const groups = /* @__PURE__ */ new Map();
1149
+ for (const read of reads) groups.set(key(read), [...groups.get(key(read)) ?? [], read]);
1150
+ return [...groups].sort(([a], [b]) => a < b ? -1 : 1);
1151
+ }
1152
+ /** The count of the halvings of the search for a visible shift. */
1153
+ const VISIBLE_STEPS = 8;
1154
+ /** The count of the splits at the level of a test, for the default of its runs. The noise of a p at the level is then 10 %. */
1155
+ const COUNTS_AT_LEVEL = 100;
1156
+ /** The smallest default of the runs of a test. */
1157
+ const MIN_RUNS = 1e4;
1158
+ /** This function gives a shift that puts the moved values on one side of the rest: twice the span of the pool. */
1159
+ function farShift(values, rest) {
1160
+ const all = [...values, ...rest];
1161
+ const span = Math.max(...all) - Math.min(...all);
1162
+ return span > 0 ? 2 * span : 1;
1163
+ }
1164
+ /** This function gives the p of the values with their median moved to `shift` from the median of the rest. */
1165
+ function pAtShift(values, rest, shift, options) {
1166
+ const move = median(rest) + shift - median(values);
1167
+ return twoSamplePermutation(values.map((value) => value + move), rest, options).p;
1168
+ }
1169
+ /**
1170
+ * This function finds the smallest shift of the median of `values` from the median of `rest` that gives a p of `level` or less.
1171
+ * It keeps the spread of the values, and it reads one direction: 1 for a shift up, and -1 for a shift down. The two
1172
+ * directions can differ, because the spread of the values and of the rest need not be symmetric.
1173
+ *
1174
+ * The level of a family is its alpha over its size, the strictest step of Holm. So the result errs on the side of a shift
1175
+ * that is too large. The cost is 9 permutation tests when the level is in reach, and 1 when it is not.
1176
+ */
1177
+ function visibleShift(values, rest, level, direction, options = {}) {
1178
+ const far = farShift(values, rest);
1179
+ const floor = pAtShift(values, rest, direction * far, options);
1180
+ if (floor > level) return {
1181
+ shift: void 0,
1182
+ floor
1183
+ };
1184
+ let low = 0;
1185
+ let high = far;
1186
+ for (let step = 0; step < VISIBLE_STEPS; step++) {
1187
+ const middle = (low + high) / 2;
1188
+ if (pAtShift(values, rest, direction * middle, options) <= level) high = middle;
1189
+ else low = middle;
1190
+ }
1191
+ return {
1192
+ shift: high,
1193
+ floor
1194
+ };
1195
+ }
1196
+ /** This function reads each CPU model against the rest, for each case. */
1197
+ function modelTest(reads, options = {}) {
1198
+ const minReads = options.minReads ?? 5;
1199
+ if (!Number.isSafeInteger(minReads) || minReads < 2) throw new RangeError(`modelTest: minReads is a whole number of 2 or more, not ${minReads}.`);
1200
+ for (const read of reads) if (!Number.isFinite(read.value)) throw new RangeError(`modelTest: the read of the case "${read.case}" on "${read.model}" is ${read.value}, not a finite value.`);
1201
+ const seed = String(options.seed ?? 1);
1202
+ const alpha = options.alpha ?? .05;
1203
+ const draft = groupsOf(reads, (r) => r.case).flatMap(([caseName, ofCase]) => groupsOf(ofCase, (r) => r.model).map(([model, ofModel]) => {
1204
+ const values = ofModel.map((r) => r.value);
1205
+ const rest = ofCase.filter((r) => r.model !== model).map((r) => r.value);
1206
+ const restMedian = rest.length === 0 ? void 0 : median(rest);
1207
+ const middle = median(values);
1208
+ const row = {
1209
+ case: caseName,
1210
+ model,
1211
+ reads: values.length,
1212
+ median: middle,
1213
+ low: Math.min(...values),
1214
+ high: Math.max(...values),
1215
+ restReads: rest.length,
1216
+ restMedian,
1217
+ shift: restMedian === void 0 ? void 0 : middle - restMedian
1218
+ };
1219
+ const asked = options.asked?.({
1220
+ case: caseName,
1221
+ model
1222
+ }) ?? true;
1223
+ return {
1224
+ row,
1225
+ asked,
1226
+ inFamily: asked && values.length >= minReads && rest.length > 0,
1227
+ values,
1228
+ rest
1229
+ };
1230
+ }));
1231
+ const family = draft.filter((d) => d.inFamily).length;
1232
+ const level = alpha / Math.max(family, 1);
1233
+ const runs = options.runs ?? Math.max(MIN_RUNS, Math.ceil(COUNTS_AT_LEVEL / level));
1234
+ const read = draft.map((d) => {
1235
+ const test = {
1236
+ seed: `${seed} ${d.row.case} ${d.row.model}`,
1237
+ runs
1238
+ };
1239
+ return {
1240
+ ...d,
1241
+ test,
1242
+ raw: d.inFamily ? twoSamplePermutation(d.values, d.rest, test).p : void 0
1243
+ };
1244
+ });
1245
+ const tested = read.flatMap((d) => d.raw === void 0 ? [] : [d.raw]);
1246
+ const corrected = holm(tested, alpha);
1247
+ let next = 0;
1248
+ const rows = read.map(({ row, asked, raw, values, rest, test }) => {
1249
+ if (raw === void 0) return {
1250
+ ...row,
1251
+ p: void 0,
1252
+ status: asked ? "too-few" : "not asked",
1253
+ visible: void 0,
1254
+ floor: void 0,
1255
+ level: void 0
1256
+ };
1257
+ const held = corrected[next++];
1258
+ if (held === void 0) throw new Error("modelTest: the Holm family lost a row.");
1259
+ const large = options.resolution === void 0 || Math.abs(row.shift ?? 0) >= options.resolution;
1260
+ if (held.reject && large) return {
1261
+ ...row,
1262
+ p: held.adjusted,
1263
+ status: "differs",
1264
+ visible: void 0,
1265
+ floor: void 0,
1266
+ level: void 0
1267
+ };
1268
+ const direction = (row.shift ?? 0) < 0 ? -1 : 1;
1269
+ if (options.searchVisible === false) return {
1270
+ ...row,
1271
+ p: held.adjusted,
1272
+ status: "no read",
1273
+ visible: void 0,
1274
+ floor: pAtShift(values, rest, direction * farShift(values, rest), test),
1275
+ level
1276
+ };
1277
+ const seen = visibleShift(values, rest, level, direction, test);
1278
+ const visible = seen.shift === void 0 ? void 0 : Math.max(seen.shift, options.resolution ?? 0);
1279
+ return {
1280
+ ...row,
1281
+ p: held.adjusted,
1282
+ status: "no read",
1283
+ visible,
1284
+ floor: seen.floor,
1285
+ level
1286
+ };
1287
+ });
1288
+ return {
1289
+ rows,
1290
+ family,
1291
+ differs: rows.filter((r) => r.status === "differs")
1292
+ };
1293
+ }
1294
+ /** This function gives the words of one row. No word says that a model is the same as the rest. */
1295
+ function modelRowText(row, family) {
1296
+ const counts = `${row.reads} reads against ${row.restReads}, family ${family}`;
1297
+ const name = `${row.case} on ${row.model}`;
1298
+ if (row.status === "not asked") return `not asked: ${name} has ${row.reads} reads, and it is outside the family`;
1299
+ if (row.status === "too-few") return `too few: ${name} has ${row.reads} reads against ${row.restReads}, and the test does not read it`;
1300
+ const shift = (row.shift ?? 0).toFixed(3);
1301
+ if (row.status === "differs") return `differs: ${name} has a shift of ${shift} at ${counts}, Holm p ${(row.p ?? 1).toFixed(4)}`;
1302
+ const floor = row.floor ?? 1;
1303
+ const level = row.level ?? 0;
1304
+ const facts = `the smallest p is ${floor.toFixed(5)}, and the first level of Holm is ${level.toFixed(5)}`;
1305
+ const head = `no read: ${name} has a shift of ${shift} at ${counts}`;
1306
+ if (floor > level) return `${head}. This size cannot reach the level: ${facts}`;
1307
+ if (row.visible === void 0) return `${head}. The test did not search for the shift that it can see: ${facts}`;
1308
+ return `${head}. This test sees a shift of ${row.visible.toFixed(3)} or more: ${facts}`;
1309
+ }
1310
+ /** The words of a read at the ceiling. A pass at the ceiling is not a gain held. */
1311
+ const CEILING_ONLY = "gain not read on this model, ceiling only";
1312
+ /** This function gives the limit of a gain on one CPU model. A model with no earlier reads gets the ceiling. */
1313
+ function limitOnModel(limits, model) {
1314
+ if (limits.ceiling < limits.gain) throw new RangeError(`limitOnModel: the ceiling ${limits.ceiling} is under the gain limit ${limits.gain}.`);
1315
+ return limits.models.includes(model) ? {
1316
+ limit: limits.gain,
1317
+ holdsGain: true,
1318
+ note: void 0
1319
+ } : {
1320
+ limit: limits.ceiling,
1321
+ holdsGain: false,
1322
+ note: CEILING_ONLY
1323
+ };
1324
+ }
1325
+ /** This function gives the sorted CPU models of each arm, and tells if all the reads come from one known model. */
1326
+ function armModels(pairs) {
1327
+ const distinct = (values) => [...new Set(values)].sort();
1328
+ const a = distinct(pairs.map((p) => p.a ?? "unknown"));
1329
+ const b = distinct(pairs.map((p) => p.b ?? "unknown"));
1330
+ const all = distinct([...a, ...b]);
1331
+ return {
1332
+ a,
1333
+ b,
1334
+ one: all.length === 1 && all[0] !== "unknown"
1335
+ };
1336
+ }
1131
1337
  // src/tail.ts
1132
1338
  /**
1133
1339
  * The tail limit verdict of a bench with few rounds. The median of each case has one limit, and a high value of its rounds
@@ -1947,6 +2153,76 @@ function winClaim(check, measure, entry, stratum) {
1947
2153
  open: read.open
1948
2154
  };
1949
2155
  }
2156
+ /**
2157
+ * This function tells what one real entry earned on a set of measures taken together. Each win of a control loses a partner, so one measure cannot call a win.
2158
+ * A control dominates the entry when the entry beats it on no measure of the set, and it beats the entry on at least one. A tie does not clear a control.
2159
+ * The entry survives when no control dominates it and it clears at least one control on each measure. The function adds no test: it reads the tests of `check`.
2160
+ * One win over any control is enough on a measure, so a beat clears only at the level divided by the count of controls there.
2161
+ * All measures must hold, so the measures add no division. Dominance reads the plain tests, because that is the cautious direction.
2162
+ * With a stratum, the function reads that stratum. Without one, it reads the rows with no stratum.
2163
+ * The function throws a `RangeError` for an empty set, a repeat, a measure with no rule and an entry that the rows do not hold.
2164
+ */
2165
+ function jointClaim(check, measures, entry, stratum) {
2166
+ if (measures.length === 0) throw new RangeError("jointClaim: the set of measures is empty.");
2167
+ if (new Set(measures).size !== measures.length) throw new RangeError("jointClaim: the set of measures has a repeat.");
2168
+ for (const measure of measures) {
2169
+ const read = check.measures.find((m) => m.measure === measure);
2170
+ if (read === void 0) throw new RangeError(`jointClaim: the measure ${JSON.stringify(measure)} has no rule.`);
2171
+ if (!read.mixed && !read.entries.some((c) => c.group === entry && c.stratum === stratum)) throw new RangeError(`jointClaim: the entry ${JSON.stringify(entry)} has no rows on ${JSON.stringify(measure)}${stratumText(stratum)}.`);
2172
+ }
2173
+ const reads = check.reads.filter((r) => r.stratum === stratum && measures.includes(r.measure));
2174
+ const names = [...new Set(reads.map((r) => r.control))];
2175
+ const testOf = (measure, control) => {
2176
+ const read = reads.find((r) => r.measure === measure && r.control === control);
2177
+ return {
2178
+ mine: read?.comparisons.find((c) => c.better === entry),
2179
+ theirs: read?.against.find((c) => c.other === entry)
2180
+ };
2181
+ };
2182
+ const noRead = (test) => test === void 0 || unread(test);
2183
+ const testsOn = (measure) => names.flatMap((c) => {
2184
+ const mine = testOf(measure, c).mine;
2185
+ return mine === void 0 ? [] : [[c, mine]];
2186
+ });
2187
+ const hasPower = (test, tests) => test.shared >= pairsNeeded(check.alpha / (test.family * tests), 1);
2188
+ const controls = names.map((control) => {
2189
+ const beaten = measures.filter((m) => testOf(m, control).mine?.beats === true);
2190
+ const lostTo = measures.filter((m) => testOf(m, control).theirs?.beats === true);
2191
+ const missing = measures.filter((m) => noRead(testOf(m, control).mine));
2192
+ return {
2193
+ control,
2194
+ beaten,
2195
+ lostTo,
2196
+ unread: missing,
2197
+ dominates: beaten.length === 0 && lostTo.length > 0 && missing.length === 0
2198
+ };
2199
+ });
2200
+ const measureReads = measures.map((measure) => ({
2201
+ measure,
2202
+ beats: names.filter((c) => testOf(measure, c).mine?.beats === true),
2203
+ clears: testsOn(measure).filter(([, test]) => clearsPartner(test, testsOn(measure).length, check.alpha)).map(([c]) => c),
2204
+ tied: names.filter((c) => testOf(measure, c).mine?.tied === true),
2205
+ unread: names.filter((c) => noRead(testOf(measure, c).mine))
2206
+ }));
2207
+ const dominatedBy = controls.filter((c) => c.dominates).map((c) => c.control);
2208
+ const equalledBy = controls.filter((c) => c.beaten.length === 0 && c.lostTo.length === 0 && c.unread.length === 0).map((c) => c.control);
2209
+ const notAhead = measureReads.filter((m) => m.clears.length === 0).map((m) => m.measure);
2210
+ const closed = measureReads.some((m) => {
2211
+ const tests = testsOn(m.measure);
2212
+ return m.clears.length === 0 && m.unread.length === 0 && tests.length > 0 && tests.every(([, test]) => hasPower(test, tests.length));
2213
+ });
2214
+ const undecided = controls.some((c) => c.beaten.length === 0 && c.lostTo.length > 0 && c.unread.length > 0);
2215
+ return {
2216
+ entry,
2217
+ stratum,
2218
+ verdict: dominatedBy.length > 0 ? "dominated" : closed ? "not-ahead" : notAhead.length > 0 || undecided ? "no-read" : "survives",
2219
+ dominatedBy,
2220
+ equalledBy,
2221
+ notAhead,
2222
+ controls,
2223
+ measures: measureReads
2224
+ };
2225
+ }
1950
2226
  // src/power.ts
1951
2227
  /**
1952
2228
  * The power test of a time verdict. It draws seeded benches with a known speedup and counts the verdicts that find it.
@@ -2061,6 +2337,6 @@ function timeRounds(arms, options) {
2061
2337
  sink
2062
2338
  };
2063
2339
  }
2064
- export { BUDGET_LOOKS, GATE_LOOKS, LADDER_FORMAT, aaBand, aaSpread, armOrder, armVerdicts, balanceOrders, budgetLookSpend, budgetLooksVerdict, budgetVerdict, caseIntervals, caseRatios, caseTails, checkLadder, checkMeasures, checkPairedTime, controlFactor, firstArm, gateOf, ladderVerdict, limitVerdict, notSlowerVerdict, poolRecords, pooledBand, pooledLimitVerdict, pooledVerdict, rungVerdict, tailFalseFails, tailLimitVerdict, timeRounds, timeVerdict, verdictMargin, verdictPower, winClaim };
2340
+ export { BUDGET_LOOKS, CEILING_ONLY, GATE_LOOKS, LADDER_FORMAT, UNKNOWN_MODEL, aaBand, aaSpread, armModels, armOrder, armVerdicts, balanceOrders, budgetLookSpend, budgetLooksVerdict, budgetVerdict, caseIntervals, caseRatios, caseTails, checkLadder, checkMeasures, checkPairedTime, controlFactor, firstArm, gateOf, jointClaim, ladderVerdict, limitOnModel, limitVerdict, modelRowText, modelTest, notSlowerVerdict, poolRecords, pooledBand, pooledLimitVerdict, pooledVerdict, rungVerdict, tailFalseFails, tailLimitVerdict, timeRounds, timeVerdict, verdictMargin, verdictPower, visibleShift, winClaim };
2065
2341
 
2066
2342
  //# sourceMappingURL=index.js.map