@mark1russell7/rigor-bench 0.2.15 → 0.2.16
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 +33 -1
- package/build/index.d.ts +102 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +203 -12
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,9 @@ v.comparison; // the cases that gain and dro
|
|
|
42
42
|
| `pooledBand(pool, level)` | The pooled band: the larger of the floor and the A/A spread of the records of earlier runs. |
|
|
43
43
|
| `caseIntervals(records, level)` | The ratio of each case with a distribution-free interval of its median over the rounds. The level 0.95 needs 6 rounds or more. |
|
|
44
44
|
| `limitVerdict(candidate, aa, options)` | The verdict of the claim "the ratio of each case is at most `limit`": `within`, `beyond` or `cannot decide`. |
|
|
45
|
+
| `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. |
|
|
46
|
+
| `caseTails(records, rank)` | The value at one rank of the ratios of each case, from the largest. |
|
|
47
|
+
| `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. |
|
|
45
48
|
| `gateOf(cases)` | The gate of a list of case outcomes: `pass`, `block` or `no read`. |
|
|
46
49
|
| `notSlowerVerdict(jobs, options)` | The gate of the claim "not slower by more than the margin", with looks and a family level. |
|
|
47
50
|
| `budgetVerdict(rounds, aa, options)` | The verdict of the claim "the median of each case is at most `limit`", for one build: `within`, `beyond` or `cannot decide`. |
|
|
@@ -80,6 +83,35 @@ v.cases; // the interval, the bound and
|
|
|
80
83
|
|
|
81
84
|
The verdict has no correction for many cases. Each case has its own interval at the level `caseLevel`. Thus with many cases, a verdict `beyond` can come from noise only. Set `caseLevel` higher when a bench has many cases. A correction of Holm is an option for a later version.
|
|
82
85
|
|
|
86
|
+
## A claim with a limit on a tail
|
|
87
|
+
|
|
88
|
+
`limitVerdict` needs an interval of the median, and a few rounds give a wide interval. `tailLimitVerdict` reads each value as it is. An example is a gate with 7 rounds and one limit for each module:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const v = tailLimitVerdict(records, aaRecords, {
|
|
92
|
+
limit: (name) => limits[name],
|
|
93
|
+
aaCap: 1.3,
|
|
94
|
+
});
|
|
95
|
+
v.outcome; // "within", "beyond" or "cannot decide"
|
|
96
|
+
v.aaTail; // the largest tail of the A/A arm
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The rule of the verdict:
|
|
100
|
+
|
|
101
|
+
- The tail of a case is the value at `rank` of its ratios, from the largest. The default rank is 2, so one slow process cannot move it.
|
|
102
|
+
- A case is `beyond` when its median is over `limit.median`.
|
|
103
|
+
- A case is also `beyond` when its tail is over `limit.tail` and over the largest tail of the A/A arm of the run.
|
|
104
|
+
- Each case is `cannot decide` when the largest tail of the A/A arm is over `aaCap`. A loud run has no power, and this is not a pass. The caller takes another read.
|
|
105
|
+
- The option `aaCap` has no default, because a run with no cap passes each row when it is loud.
|
|
106
|
+
- The option `expected` names the cases that the run must have. A case of that list with no record reads `cannot decide`. Without this option, a run that loses a case reads the other cases only.
|
|
107
|
+
- `gateOf(v.cases)` gives the gate of the verdict: `pass`, `block` or `no read`.
|
|
108
|
+
|
|
109
|
+
With the default rank, a real cost in 1 round cannot fail the tail limit. This is by design: of 7 rounds, 1 round passes the tail, because 1 round cannot be told from noise. A cost in 2 rounds or more can fail it.
|
|
110
|
+
|
|
111
|
+
The verdict has no interval, so a limit needs a margin from the spread of earlier runs. `tailFalseFails` applies the rule to each case of an A/A arm. Each case reads the tail of the other cases. Print the count of `fails` on the gate, to state what the rule costs on noise.
|
|
112
|
+
|
|
113
|
+
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.
|
|
114
|
+
|
|
83
115
|
## A gate that can fail
|
|
84
116
|
|
|
85
117
|
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.
|
|
@@ -433,7 +465,7 @@ v.pool; // { cases, runs, spread, floo
|
|
|
433
465
|
- The rule `difference` uses the larger of `margin` and the noise of the pooled band as its margin.
|
|
434
466
|
- The run still needs its own A/A arm of 5 cases or more. The noise cap uses that arm.
|
|
435
467
|
|
|
436
|
-
For a fixed limit, for example 1.10 against a fixed base, use `limitVerdict`.
|
|
468
|
+
For a fixed limit, for example 1.10 against a fixed base, use `limitVerdict`. With few rounds and a limit on a high value of each case, use `tailLimitVerdict`.
|
|
437
469
|
|
|
438
470
|
## The outcomes
|
|
439
471
|
|
package/build/index.d.ts
CHANGED
|
@@ -790,6 +790,107 @@ 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 value at one rank of the ratios of one case, from the largest. */
|
|
794
|
+
interface CaseTail {
|
|
795
|
+
readonly case: string;
|
|
796
|
+
/** The value at the rank, or undefined when the rounds do not put the rank above the median. */
|
|
797
|
+
readonly tail: number | undefined;
|
|
798
|
+
readonly rounds: number;
|
|
799
|
+
}
|
|
800
|
+
/** The two limits of one case. */
|
|
801
|
+
interface TailLimit {
|
|
802
|
+
/** The largest median of the ratio `b / a` that the claim permits. */
|
|
803
|
+
readonly median: number;
|
|
804
|
+
/** The largest tail of the ratio `b / a` that the claim permits. */
|
|
805
|
+
readonly tail: number;
|
|
806
|
+
}
|
|
807
|
+
/** The options of `tailFalseFails`. */
|
|
808
|
+
interface TailRuleOptions {
|
|
809
|
+
/** The limits of all the cases, or a function that gives the limits of one case. */
|
|
810
|
+
readonly limit: TailLimit | ((caseName: string) => TailLimit);
|
|
811
|
+
/** The rank of the tail, from the largest. The default 2 reads the second largest ratio, so one slow process cannot move it. */
|
|
812
|
+
readonly rank?: number;
|
|
813
|
+
}
|
|
814
|
+
/** The options of a tail limit verdict. */
|
|
815
|
+
interface TailLimitOptions extends TailRuleOptions {
|
|
816
|
+
/**
|
|
817
|
+
* The largest tail of the A/A arm that lets a run decide. Each row under the tail of the A/A arm passes the tail limit, so
|
|
818
|
+
* a loud run has no power. This option has no default, because a run with no cap passes each row when it is loud.
|
|
819
|
+
*/
|
|
820
|
+
readonly aaCap: number;
|
|
821
|
+
/** The smallest number of A/A cases with a tail. The default is 5. */
|
|
822
|
+
readonly minimum?: number;
|
|
823
|
+
/**
|
|
824
|
+
* The names of the cases that the run must have. A case of this list with no record reads `cannot decide`. Without this
|
|
825
|
+
* option, the verdict reads only the cases of the candidate, so a run that loses a case can read `within`.
|
|
826
|
+
*/
|
|
827
|
+
readonly expected?: readonly string[];
|
|
828
|
+
}
|
|
829
|
+
/** The verdict of one case. */
|
|
830
|
+
interface CaseTailLimit extends CaseTail {
|
|
831
|
+
/** The median ratio of the case: the value `ratio` of `caseRatios`. The value is undefined when the case has no record. */
|
|
832
|
+
readonly ratio: number | undefined;
|
|
833
|
+
readonly limit: TailLimit;
|
|
834
|
+
/** The median is over its limit. */
|
|
835
|
+
readonly overMedian: boolean;
|
|
836
|
+
/** The tail is over its limit and over the tail of the A/A arm of the run. */
|
|
837
|
+
readonly overTail: boolean;
|
|
838
|
+
readonly outcome: LimitOutcome;
|
|
839
|
+
}
|
|
840
|
+
/** A case that an A/A arm cannot read, with the reason. */
|
|
841
|
+
interface TailGainCase {
|
|
842
|
+
readonly case: string;
|
|
843
|
+
/** The reason in one sentence. It names the limit under 1. */
|
|
844
|
+
readonly reason: string;
|
|
845
|
+
}
|
|
846
|
+
/** The result of `tailFalseFails`. */
|
|
847
|
+
interface TailFalseFails {
|
|
848
|
+
/** The cases that the rule fails on the A/A arm. Print the count of this list on the gate. */
|
|
849
|
+
readonly fails: readonly string[];
|
|
850
|
+
/** The cases with a limit under 1. These cases are not in `fails`, because an A/A pair shows no gain. */
|
|
851
|
+
readonly gains: readonly TailGainCase[];
|
|
852
|
+
}
|
|
853
|
+
/** A tail limit verdict, with the facts that it uses. */
|
|
854
|
+
interface TailLimitVerdict {
|
|
855
|
+
readonly outcome: LimitOutcome;
|
|
856
|
+
/** The reason of the outcome, in one sentence. */
|
|
857
|
+
readonly reason: string;
|
|
858
|
+
readonly cases: readonly CaseTailLimit[];
|
|
859
|
+
/** The largest tail over the cases of the A/A arm, or undefined when that arm has too few cases. */
|
|
860
|
+
readonly aaTail: number | undefined;
|
|
861
|
+
readonly aaCap: number;
|
|
862
|
+
readonly rank: number;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* This function gives the value at one rank of the ratios of each case, from the largest, in the order of `caseRatios`. It
|
|
866
|
+
* examines the records as `caseRatios` does. A case needs `2 * rank + 1` rounds, so that the value is above the median.
|
|
867
|
+
*/
|
|
868
|
+
export declare function caseTails(records: readonly PairedTime[], rank?: number): CaseTail[];
|
|
869
|
+
/**
|
|
870
|
+
* This function gives the verdict of the claim "the median and the tail of each case are at most their limits".
|
|
871
|
+
* - A case is `beyond` when its median is over its limit. It is also `beyond` when its tail is over its limit and over the
|
|
872
|
+
* largest tail of the A/A arm of the run.
|
|
873
|
+
* - A case with too few rounds for the rank is `cannot decide`, unless its median is over its limit.
|
|
874
|
+
* - A case of `expected` with no record is `cannot decide`.
|
|
875
|
+
* - The verdict is `beyond` when a case is `beyond`, and `within` when each case is `within`.
|
|
876
|
+
* - Each case and the verdict are `cannot decide` when the largest tail of the A/A arm is over `aaCap`. The same applies
|
|
877
|
+
* when the A/A arm has fewer than `minimum` cases with a tail. This is not a pass, so the caller takes another read.
|
|
878
|
+
*
|
|
879
|
+
* With the default rank, a real cost in 1 round cannot fail the tail limit, because 1 round cannot be told from noise. A
|
|
880
|
+
* cost in 2 rounds or more can fail it. The verdict has no interval, so a limit needs a margin from the spread of earlier
|
|
881
|
+
* runs.
|
|
882
|
+
*/
|
|
883
|
+
export declare function tailLimitVerdict(candidate: readonly PairedTime[], aa: readonly PairedTime[], options: TailLimitOptions): TailLimitVerdict;
|
|
884
|
+
/**
|
|
885
|
+
* This function applies the rule of `tailLimitVerdict` to each case of the A/A arm, and gives the cases that fail. Each
|
|
886
|
+
* case reads the tail of the other cases, because a case cannot be the noise of itself. An arm with one case gives no
|
|
887
|
+
* failed case.
|
|
888
|
+
*
|
|
889
|
+
* 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
|
|
890
|
+
* in `gains` with that reason. The tail of that case stays in the noise of the other cases. The false fails of a gain limit
|
|
891
|
+
* come from repeat reads of an unchanged tree against its fixed base.
|
|
892
|
+
*/
|
|
893
|
+
export declare function tailFalseFails(aa: readonly PairedTime[], options: TailRuleOptions): TailFalseFails;
|
|
793
894
|
/** One value of one measure, for one entry and one input. */
|
|
794
895
|
interface MeasureRow {
|
|
795
896
|
/** The name of the measure. */
|
|
@@ -1072,5 +1173,5 @@ interface Rounds {
|
|
|
1072
1173
|
* keeps the work.
|
|
1073
1174
|
*/
|
|
1074
1175
|
export declare function timeRounds(arms: Readonly<Record<Arm, () => number>>, options: RoundsOptions): Rounds;
|
|
1075
|
-
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 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 TimeOutcome, type TimeVerdict, type VerdictOptions, type VerdictRule, type WinClaim, type WorstBudget, caseRatios, checkPairedTime, firstArm };
|
|
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, caseRatios, checkPairedTime, firstArm };
|
|
1076
1177
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -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/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/tail.ts","../src/measures.ts","../src/power.ts","../src/rounds.ts"],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -28,7 +28,7 @@ function caseBudget(name, values, aaValues, options, level) {
|
|
|
28
28
|
const aaMedian = aaValues.length > 0 ? median(aaValues) : void 0;
|
|
29
29
|
const difference = aaMedian === void 0 ? void 0 : Math.abs(middle - aaMedian);
|
|
30
30
|
const aaCoverage = aaValues.length > 0 ? medianInterval(aaValues, level).coverage : 0;
|
|
31
|
-
const facts = `median ${round$
|
|
31
|
+
const facts = `median ${round$3(middle)} of ${values.length} rounds, interval ${round$3(interval.low)} to ${round$3(interval.high)}, limit ${limit}, A/A median ${aaMedian === void 0 ? "none" : round$3(aaMedian)} of ${aaValues.length} rounds, difference ${difference === void 0 ? "none" : round$3(difference)}`;
|
|
32
32
|
const decide = (outcome, why) => ({
|
|
33
33
|
case: name,
|
|
34
34
|
rounds: values.length,
|
|
@@ -180,7 +180,7 @@ function budgetLooksVerdict(rounds, aa, options) {
|
|
|
180
180
|
const looks = options.looks ?? BUDGET_LOOKS;
|
|
181
181
|
const level = options.level ?? .95;
|
|
182
182
|
const spent = budgetLookSpend(looks);
|
|
183
|
-
if (spent > 1 - level) throw new RangeError(`budgetLooksVerdict: the looks spend ${round$
|
|
183
|
+
if (spent > 1 - level) throw new RangeError(`budgetLooksVerdict: the looks spend ${round$3(spent)}, over the ${round$3(1 - level)} of the level ${level}.`);
|
|
184
184
|
if (!(limit >= 0 && Number.isFinite(limit))) throw new RangeError(`budgetLooksVerdict: the limit is finite and 0 or more, not ${limit}.`);
|
|
185
185
|
if (!(maxDifference >= 0 && Number.isFinite(maxDifference))) throw new RangeError(`budgetLooksVerdict: the largest difference is finite and 0 or more, not ${maxDifference}.`);
|
|
186
186
|
const byRound = (list) => [...list].sort((a, b) => a.round - b.round);
|
|
@@ -209,7 +209,7 @@ function budgetLooksVerdict(rounds, aa, options) {
|
|
|
209
209
|
fails: verdict.cases.some((c) => c.outcome === "beyond")
|
|
210
210
|
};
|
|
211
211
|
}
|
|
212
|
-
function round$
|
|
212
|
+
function round$3(x) {
|
|
213
213
|
return Math.round(x * 1e4) / 1e4;
|
|
214
214
|
}
|
|
215
215
|
// src/gate.ts
|
|
@@ -871,14 +871,14 @@ function timeVerdict(candidate, aa, options = {}) {
|
|
|
871
871
|
noise
|
|
872
872
|
};
|
|
873
873
|
if (noise.noisy && verdict.band !== void 0) {
|
|
874
|
-
const reason = `${spread === void 0 ? "The A/A arm of the run has no cases, so the noise cap has no spread." : `The A/A spread of the run is ${round$
|
|
874
|
+
const reason = `${spread === void 0 ? "The A/A arm of the run has no cases, so the noise cap has no spread." : `The A/A spread of the run is ${round$2(spread)}, above the noise cap ${cap}.`} Without the cap, the outcome is "${verdict.outcome}".`;
|
|
875
875
|
verdict = {
|
|
876
876
|
...verdict,
|
|
877
877
|
outcome: "cannot decide",
|
|
878
878
|
reason
|
|
879
879
|
};
|
|
880
880
|
} else if (noise.biased && verdict.band !== void 0) {
|
|
881
|
-
const reason = `The A/A median ratio of the run is ${round$
|
|
881
|
+
const reason = `The A/A median ratio of the run is ${round$2(aaMedian)}, out of [${round$2(Math.exp(-cap))}, ${round$2(Math.exp(cap))}] of the noise cap ${cap}. Without the cap, the outcome is "${verdict.outcome}".`;
|
|
882
882
|
verdict = {
|
|
883
883
|
...verdict,
|
|
884
884
|
outcome: "cannot decide",
|
|
@@ -946,7 +946,7 @@ function ruleVerdict(candidate, aa, options, pooled) {
|
|
|
946
946
|
};
|
|
947
947
|
if (rule === "difference") {
|
|
948
948
|
const margin = marginOf(options, pooled);
|
|
949
|
-
const shown = pooled === void 0 ? margin : round$
|
|
949
|
+
const shown = pooled === void 0 ? margin : round$2(margin);
|
|
950
950
|
const logs = cases.map((c) => Math.log(c.ratio));
|
|
951
951
|
const aaLogs = aaRatios.map((c) => Math.log(c.ratio));
|
|
952
952
|
const d = bootstrapTwo(logs, aaLogs, (x, y) => median(x) - median(y), options.bootstrap);
|
|
@@ -956,7 +956,7 @@ function ruleVerdict(candidate, aa, options, pooled) {
|
|
|
956
956
|
low: Math.exp(d.low),
|
|
957
957
|
high: Math.exp(d.high)
|
|
958
958
|
};
|
|
959
|
-
const span = `The interval of the candidate median over the A/A median is [${round$
|
|
959
|
+
const span = `The interval of the candidate median over the A/A median is [${round$2(interval.low)}, ${round$2(interval.high)}].`;
|
|
960
960
|
if (d.low >= -margin && d.high <= margin) return {
|
|
961
961
|
...facts,
|
|
962
962
|
interval,
|
|
@@ -983,7 +983,7 @@ function ruleVerdict(candidate, aa, options, pooled) {
|
|
|
983
983
|
};
|
|
984
984
|
}
|
|
985
985
|
const interval = bootstrapMedian(cases.map((c) => c.ratio), options.bootstrap);
|
|
986
|
-
const span = `The interval of the median ratio is [${round$
|
|
986
|
+
const span = `The interval of the median ratio is [${round$2(interval.low)}, ${round$2(interval.high)}], and the A/A band is [${round$2(band.low)}, ${round$2(band.high)}].`;
|
|
987
987
|
if (interval.high < band.low) return {
|
|
988
988
|
...facts,
|
|
989
989
|
interval,
|
|
@@ -1009,7 +1009,7 @@ function ruleVerdict(candidate, aa, options, pooled) {
|
|
|
1009
1009
|
reason: `${span} The interval crosses an end of the band.`
|
|
1010
1010
|
};
|
|
1011
1011
|
}
|
|
1012
|
-
function round$
|
|
1012
|
+
function round$2(x) {
|
|
1013
1013
|
return Math.round(x * 1e4) / 1e4;
|
|
1014
1014
|
}
|
|
1015
1015
|
// src/limit.ts
|
|
@@ -1114,17 +1114,208 @@ function limitVerdict(candidate, aa, options) {
|
|
|
1114
1114
|
const rest = others.length === 0 ? "" : ` ${others.length} of ${cases.length} cases are not within${permit}.`;
|
|
1115
1115
|
return {
|
|
1116
1116
|
outcome: "within",
|
|
1117
|
-
reason: `The largest bound of a case within is ${round(top.bound)} in the case ${JSON.stringify(top.case)}, and it is at most the limit ${limit}.${rest}`,
|
|
1117
|
+
reason: `The largest bound of a case within is ${round$1(top.bound)} in the case ${JSON.stringify(top.case)}, and it is at most the limit ${limit}.${rest}`,
|
|
1118
1118
|
...facts
|
|
1119
1119
|
};
|
|
1120
1120
|
}
|
|
1121
1121
|
const top = cases.reduce((a, b) => b.bound > a.bound ? b : a);
|
|
1122
1122
|
return {
|
|
1123
1123
|
outcome: "cannot decide",
|
|
1124
|
-
reason: `${others.length} of ${cases.length} cases are not within${permit}. The bound of the case ${JSON.stringify(top.case)} is ${round(top.bound)}, above the limit ${limit}, and too few floors are above it.`,
|
|
1124
|
+
reason: `${others.length} of ${cases.length} cases are not within${permit}. The bound of the case ${JSON.stringify(top.case)} is ${round$1(top.bound)}, above the limit ${limit}, and too few floors are above it.`,
|
|
1125
1125
|
...facts
|
|
1126
1126
|
};
|
|
1127
1127
|
}
|
|
1128
|
+
function round$1(x) {
|
|
1129
|
+
return Math.round(x * 1e4) / 1e4;
|
|
1130
|
+
}
|
|
1131
|
+
// src/tail.ts
|
|
1132
|
+
/**
|
|
1133
|
+
* 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
|
|
1134
|
+
* has another limit. The verdict `limitVerdict` needs an interval of the median, and a few rounds give a wide interval.
|
|
1135
|
+
* This verdict reads each value as it is, and the A/A arm of the same run tells which tail is noise.
|
|
1136
|
+
*/
|
|
1137
|
+
function ratiosOf(records) {
|
|
1138
|
+
const ratios = /* @__PURE__ */ new Map();
|
|
1139
|
+
for (const r of records) {
|
|
1140
|
+
const list = ratios.get(r.case) ?? [];
|
|
1141
|
+
list.push(r.b / r.a);
|
|
1142
|
+
ratios.set(r.case, list);
|
|
1143
|
+
}
|
|
1144
|
+
return ratios;
|
|
1145
|
+
}
|
|
1146
|
+
function checkRank(rank) {
|
|
1147
|
+
if (!Number.isSafeInteger(rank) || rank < 1) throw new RangeError(`tail: the rank is a whole number of 1 or more, not ${rank}.`);
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* This function gives the value at one rank of the ratios of each case, from the largest, in the order of `caseRatios`. It
|
|
1151
|
+
* examines the records as `caseRatios` does. A case needs `2 * rank + 1` rounds, so that the value is above the median.
|
|
1152
|
+
*/
|
|
1153
|
+
function caseTails(records, rank = 2) {
|
|
1154
|
+
checkRank(rank);
|
|
1155
|
+
const ratios = ratiosOf(records);
|
|
1156
|
+
return caseRatios(records).map((c) => {
|
|
1157
|
+
const sorted = (ratios.get(c.case) ?? []).toSorted((x, y) => y - x);
|
|
1158
|
+
return {
|
|
1159
|
+
case: c.case,
|
|
1160
|
+
rounds: c.rounds,
|
|
1161
|
+
tail: c.rounds >= 2 * rank + 1 ? sorted[rank - 1] : void 0
|
|
1162
|
+
};
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
function limitOf(options, name) {
|
|
1166
|
+
const limit = typeof options.limit === "function" ? options.limit(name) : options.limit;
|
|
1167
|
+
for (const value of [limit.median, limit.tail]) if (!(value > 0 && Number.isFinite(value))) throw new RangeError(`tail: each limit is a finite number above 0, not ${value} for the case ${JSON.stringify(name)}.`);
|
|
1168
|
+
return limit;
|
|
1169
|
+
}
|
|
1170
|
+
function largestTail(tails) {
|
|
1171
|
+
const values = tails.flatMap((c) => c.tail === void 0 ? [] : [c.tail]);
|
|
1172
|
+
return values.length === 0 ? void 0 : Math.max(...values);
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* This function gives the verdict of the claim "the median and the tail of each case are at most their limits".
|
|
1176
|
+
* - A case is `beyond` when its median is over its limit. It is also `beyond` when its tail is over its limit and over the
|
|
1177
|
+
* largest tail of the A/A arm of the run.
|
|
1178
|
+
* - A case with too few rounds for the rank is `cannot decide`, unless its median is over its limit.
|
|
1179
|
+
* - A case of `expected` with no record is `cannot decide`.
|
|
1180
|
+
* - The verdict is `beyond` when a case is `beyond`, and `within` when each case is `within`.
|
|
1181
|
+
* - Each case and the verdict are `cannot decide` when the largest tail of the A/A arm is over `aaCap`. The same applies
|
|
1182
|
+
* when the A/A arm has fewer than `minimum` cases with a tail. This is not a pass, so the caller takes another read.
|
|
1183
|
+
*
|
|
1184
|
+
* With the default rank, a real cost in 1 round cannot fail the tail limit, because 1 round cannot be told from noise. A
|
|
1185
|
+
* cost in 2 rounds or more can fail it. The verdict has no interval, so a limit needs a margin from the spread of earlier
|
|
1186
|
+
* runs.
|
|
1187
|
+
*/
|
|
1188
|
+
function tailLimitVerdict(candidate, aa, options) {
|
|
1189
|
+
const { aaCap } = options;
|
|
1190
|
+
const rank = options.rank ?? 2;
|
|
1191
|
+
const minimum = options.minimum ?? 5;
|
|
1192
|
+
checkRank(rank);
|
|
1193
|
+
if (!(aaCap > 0 && Number.isFinite(aaCap))) throw new RangeError(`tailLimitVerdict: the cap is a finite number above 0, not ${aaCap}.`);
|
|
1194
|
+
if (!Number.isSafeInteger(minimum) || minimum < 1) throw new RangeError(`tailLimitVerdict: the minimum is a whole number of 1 or more, not ${minimum}.`);
|
|
1195
|
+
const aaTails = caseTails(aa, rank).filter((c) => c.tail !== void 0);
|
|
1196
|
+
const aaTail = aaTails.length < minimum ? void 0 : largestTail(aaTails);
|
|
1197
|
+
const tails = new Map(caseTails(candidate, rank).map((c) => [c.case, c]));
|
|
1198
|
+
const facts = {
|
|
1199
|
+
aaTail,
|
|
1200
|
+
aaCap,
|
|
1201
|
+
rank
|
|
1202
|
+
};
|
|
1203
|
+
const present = caseRatios(candidate).map((c) => {
|
|
1204
|
+
const { tail } = tails.get(c.case) ?? { tail: void 0 };
|
|
1205
|
+
const limit = limitOf(options, c.case);
|
|
1206
|
+
if (aaTail === void 0 || aaTail > aaCap) return {
|
|
1207
|
+
case: c.case,
|
|
1208
|
+
tail,
|
|
1209
|
+
rounds: c.rounds,
|
|
1210
|
+
ratio: c.ratio,
|
|
1211
|
+
limit,
|
|
1212
|
+
overMedian: false,
|
|
1213
|
+
overTail: false,
|
|
1214
|
+
outcome: "cannot decide"
|
|
1215
|
+
};
|
|
1216
|
+
const overMedian = c.ratio > limit.median;
|
|
1217
|
+
const overTail = tail !== void 0 && tail > limit.tail && tail > aaTail;
|
|
1218
|
+
const outcome = overMedian || overTail ? "beyond" : tail === void 0 ? "cannot decide" : "within";
|
|
1219
|
+
return {
|
|
1220
|
+
case: c.case,
|
|
1221
|
+
tail,
|
|
1222
|
+
rounds: c.rounds,
|
|
1223
|
+
ratio: c.ratio,
|
|
1224
|
+
limit,
|
|
1225
|
+
overMedian,
|
|
1226
|
+
overTail,
|
|
1227
|
+
outcome
|
|
1228
|
+
};
|
|
1229
|
+
});
|
|
1230
|
+
const found = new Set(present.map((c) => c.case));
|
|
1231
|
+
const missing = [...new Set(options.expected ?? [])].filter((name) => !found.has(name)).map((name) => ({
|
|
1232
|
+
case: name,
|
|
1233
|
+
tail: void 0,
|
|
1234
|
+
rounds: 0,
|
|
1235
|
+
ratio: void 0,
|
|
1236
|
+
limit: limitOf(options, name),
|
|
1237
|
+
overMedian: false,
|
|
1238
|
+
overTail: false,
|
|
1239
|
+
outcome: "cannot decide"
|
|
1240
|
+
}));
|
|
1241
|
+
const cases = [...present, ...missing];
|
|
1242
|
+
if (aaTail === void 0) return {
|
|
1243
|
+
...facts,
|
|
1244
|
+
outcome: "cannot decide",
|
|
1245
|
+
cases,
|
|
1246
|
+
reason: `The A/A arm has ${aaTails.length} cases with a tail, and the verdict needs ${minimum} or more.`
|
|
1247
|
+
};
|
|
1248
|
+
if (aaTail > aaCap) return {
|
|
1249
|
+
...facts,
|
|
1250
|
+
outcome: "cannot decide",
|
|
1251
|
+
cases,
|
|
1252
|
+
reason: `The largest tail ${round(aaTail)} of the A/A arm is over the cap ${aaCap}, so this run has no power.`
|
|
1253
|
+
};
|
|
1254
|
+
if (cases.length === 0) return {
|
|
1255
|
+
...facts,
|
|
1256
|
+
outcome: "cannot decide",
|
|
1257
|
+
cases,
|
|
1258
|
+
reason: "The candidate has no cases."
|
|
1259
|
+
};
|
|
1260
|
+
const beyond = cases.filter((c) => c.outcome === "beyond");
|
|
1261
|
+
if (beyond.length > 0) return {
|
|
1262
|
+
...facts,
|
|
1263
|
+
outcome: "beyond",
|
|
1264
|
+
cases,
|
|
1265
|
+
reason: `${beyond.length} of ${cases.length} cases are over a limit. The first is ${JSON.stringify(beyond[0].case)}.`
|
|
1266
|
+
};
|
|
1267
|
+
if (missing.length > 0) return {
|
|
1268
|
+
...facts,
|
|
1269
|
+
outcome: "cannot decide",
|
|
1270
|
+
cases,
|
|
1271
|
+
reason: `${missing.length} of ${cases.length} cases have no record. The first is ${JSON.stringify(missing[0].case)}.`
|
|
1272
|
+
};
|
|
1273
|
+
const open = cases.filter((c) => c.outcome === "cannot decide");
|
|
1274
|
+
if (open.length > 0) return {
|
|
1275
|
+
...facts,
|
|
1276
|
+
outcome: "cannot decide",
|
|
1277
|
+
cases,
|
|
1278
|
+
reason: `${open.length} of ${cases.length} cases have too few rounds for the rank ${rank}. The first is ${JSON.stringify(open[0].case)}.`
|
|
1279
|
+
};
|
|
1280
|
+
return {
|
|
1281
|
+
...facts,
|
|
1282
|
+
outcome: "within",
|
|
1283
|
+
cases,
|
|
1284
|
+
reason: `Each of the ${cases.length} cases is at most its limits, or its tail is at most the tail ${round(aaTail)} of the A/A arm.`
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* This function applies the rule of `tailLimitVerdict` to each case of the A/A arm, and gives the cases that fail. Each
|
|
1289
|
+
* case reads the tail of the other cases, because a case cannot be the noise of itself. An arm with one case gives no
|
|
1290
|
+
* failed case.
|
|
1291
|
+
*
|
|
1292
|
+
* 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
|
|
1293
|
+
* in `gains` with that reason. The tail of that case stays in the noise of the other cases. The false fails of a gain limit
|
|
1294
|
+
* come from repeat reads of an unchanged tree against its fixed base.
|
|
1295
|
+
*/
|
|
1296
|
+
function tailFalseFails(aa, options) {
|
|
1297
|
+
const tails = caseTails(aa, options.rank ?? 2);
|
|
1298
|
+
const ratios = new Map(caseRatios(aa).map((c) => [c.case, c.ratio]));
|
|
1299
|
+
const gains = tails.flatMap((c) => {
|
|
1300
|
+
const limit = limitOf(options, c.case);
|
|
1301
|
+
const under = Math.min(limit.median, limit.tail);
|
|
1302
|
+
return under < 1 ? [{
|
|
1303
|
+
case: c.case,
|
|
1304
|
+
reason: `The limit ${under} is under 1, and an A/A pair shows no gain, so the A/A arm has no read of this case.`
|
|
1305
|
+
}] : [];
|
|
1306
|
+
});
|
|
1307
|
+
const noRead = new Set(gains.map((c) => c.case));
|
|
1308
|
+
return {
|
|
1309
|
+
fails: tails.filter((c) => {
|
|
1310
|
+
if (noRead.has(c.case)) return false;
|
|
1311
|
+
const others = largestTail(tails.filter((other) => other !== c));
|
|
1312
|
+
if (others === void 0) return false;
|
|
1313
|
+
const limit = limitOf(options, c.case);
|
|
1314
|
+
return (ratios.get(c.case) ?? 0) > limit.median || c.tail !== void 0 && c.tail > limit.tail && c.tail > others;
|
|
1315
|
+
}).map((c) => c.case),
|
|
1316
|
+
gains
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1128
1319
|
function round(x) {
|
|
1129
1320
|
return Math.round(x * 1e4) / 1e4;
|
|
1130
1321
|
}
|
|
@@ -1870,6 +2061,6 @@ function timeRounds(arms, options) {
|
|
|
1870
2061
|
sink
|
|
1871
2062
|
};
|
|
1872
2063
|
}
|
|
1873
|
-
export { BUDGET_LOOKS, GATE_LOOKS, LADDER_FORMAT, aaBand, aaSpread, armVerdicts, balanceOrders, budgetLookSpend, budgetLooksVerdict, budgetVerdict, caseIntervals, caseRatios, checkLadder, checkMeasures, checkPairedTime, controlFactor, firstArm, gateOf, ladderVerdict, limitVerdict, notSlowerVerdict, poolRecords, pooledBand, pooledLimitVerdict, pooledVerdict, rungVerdict, timeRounds, timeVerdict, verdictMargin, verdictPower, winClaim };
|
|
2064
|
+
export { BUDGET_LOOKS, GATE_LOOKS, LADDER_FORMAT, aaBand, aaSpread, 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 };
|
|
1874
2065
|
|
|
1875
2066
|
//# sourceMappingURL=index.js.map
|