@mark1russell7/layout-contract 0.2.10 → 0.2.11
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 +5 -0
- package/build/index.d.ts +139 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +322 -5
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/corpus.ts +144 -4
- package/src/index.ts +1 -0
- package/src/verdict.ts +296 -0
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ The package has no runtime dependencies.
|
|
|
21
21
|
| The portfolio | `portfolio`, `Portfolio`, `PortfolioKey`, `PortfolioLevel`, `PortfolioOptions`, `PortfolioPick`, `PortfolioTrace`, `Member`, `MemberTrace`, `MemberStatus`, `pickMoves` |
|
|
22
22
|
| The pick rule | `selectBest`, `SelectCandidate`, `SelectLevel`, `Selection`, `PickSpec` |
|
|
23
23
|
| The corpus checks | `ClaimBreak`, `checkDeclared`, `declaredCounts`, `declaredClaimOf`, `DeclaredClaim`, `Declares`, `corpusHashOf`, `CorpusEntry` |
|
|
24
|
+
| The families | `familyOf`, `FamilyBy`, `FamilyCell`, `FAMILY_ANY_KIND` |
|
|
25
|
+
| The verdict | `verdict`, `VerdictOptions`, `VerdictResult`, `VerdictRecord`, `VerdictSide`, `VerdictInterval`, `FamilyVerdict`, `VerdictPermutation`, `VerdictSignTest`, `VerdictLabel`, `VERDICT_EXACT_MAX`, `VERDICT_EXACT_CAP`, `VERDICT_RUNS`, `VERDICT_DRAWS`, `VERDICT_SEED` |
|
|
24
26
|
| The split files | `checkSplit`, `splitFolds`, `leaveOneSeedOut`, `splitFileName`, `SPLIT_SCHEMA`, `CorpusSplit`, `SplitFold`, `SplitCorpus` |
|
|
25
27
|
| The strategy space | `StrategySpace`, `SpaceStage`, `StageOption`, `SpaceParam`, `ParamWhen`, `MemberRule`, `SpaceConstraint`, `OptionRef`, `StrategyConfig`, `StageChoice`, `ParamKind`, `ParamValue`, `STRATEGY_SPACE_SCHEMA` |
|
|
26
28
|
| The space functions | `checkSpace`, `checkConfig`, `canonicalConfig`, `configId`, `spaceSize`, `paramLevels`, `enumerateSpace`, `productSpace`, `joinConfigs`, `splitConfig`, `SpaceSize`, `SpaceSizeOptions`, `EnumerateOptions`, `GridOptions`, `SampleOptions` |
|
|
@@ -39,6 +41,7 @@ The package has no runtime dependencies.
|
|
|
39
41
|
- **The exact container.** Give `{ exactContainer: true }` to `packHard` or `countOutside`. Then a box of a shaped container with the size `fixed` must stay inside the shape. A cut corner, a notch and a hole count. A container with a free side keeps the bounding test. The default changes to the exact test at the next breaking version.
|
|
40
42
|
- **The output hash.** `outputHash` gives the first 16 hex characters of the SHA-256 of the canonical JSON of an output. Each number goes to the grid of `RECORD_EPS` first.
|
|
41
43
|
- **The corpus checks.** `checkDeclared` and `checkSplit` give a list of `ClaimBreak`. An empty list tells that each claim is true. `corpusHashOf` gives the corpus hash of `packing`.
|
|
44
|
+
- **The verdict.** `verdict` compares two strategies on one measure, with the family as the unit. A verdict counts only when it holds for the mean of the family means and for the mean of the inputs.
|
|
42
45
|
- **The tolerances.** `EPS` (`1e-6`) is for the float compares in an engine. `RECORD_EPS` (`1e-3`) is for the compares on a written file. A record writes the tolerance of each hard rule in its field `tolerance`. The defaults are `overlap` `0.001` and `through` `0.5`.
|
|
43
46
|
|
|
44
47
|
## An example
|
|
@@ -59,3 +62,5 @@ const hard = packHard(input, output);
|
|
|
59
62
|
```
|
|
60
63
|
|
|
61
64
|
The formats are in `docs/FORMAT.md` of `packing` and `docs/LAYOUT-RECORDS.md` of `highways`. The joint measures are in `docs/JOINT-MEASURES.md` of this repository, and the joint corpus is in `docs/JOINT-CORPUS.md`. The promotion guards are in `docs/PROMOTION.md`. The portfolio and its pick rule are in `docs/PORTFOLIO.md`. The corpus checks and the split files are in `docs/CORPUS-CHECKS.md`. The strategy space is in [`docs/STRATEGY-SPACE.md`](https://github.com/mark1russell7/layout-contract/blob/main/docs/STRATEGY-SPACE.md).
|
|
65
|
+
|
|
66
|
+
The verdict over families is in [`docs/VERDICT.md`](https://github.com/mark1russell7/layout-contract/blob/main/docs/VERDICT.md).
|
package/build/index.d.ts
CHANGED
|
@@ -714,7 +714,30 @@ export interface DeclaredClaim {
|
|
|
714
714
|
readonly thin: readonly string[];
|
|
715
715
|
/** The count of the inputs for each property id. */
|
|
716
716
|
readonly counts: Readonly<Record<string, number>>;
|
|
717
|
+
/** The key of the family of an input. `familyOf` gives the key of one cell. The family is the unit of a verdict. */
|
|
718
|
+
readonly familyBy?: FamilyBy;
|
|
719
|
+
/** The minimum count of the inputs of each family. Without it, `checkDeclared` does not count the families. */
|
|
720
|
+
readonly familyMin?: number;
|
|
717
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* The key of the family of a cell (`docs/CORPUS-CHECKS.md`).
|
|
724
|
+
*
|
|
725
|
+
* - The flat form is a list of dimensions. The key is the values of these dimensions of the cell, in order, with `/` between them.
|
|
726
|
+
* - The map form chooses an entry by the field `kind` of the cell, or the entry `"*"`. The key is the kind, then the values of the dimensions of the entry.
|
|
727
|
+
*/
|
|
728
|
+
export type FamilyBy = readonly string[] | {
|
|
729
|
+
readonly [kind: string]: readonly string[];
|
|
730
|
+
};
|
|
731
|
+
/** A cell of an input: one value for each dimension. The map form of `FamilyBy` also reads the field `kind`. */
|
|
732
|
+
export type FamilyCell = Readonly<Record<string, unknown>>;
|
|
733
|
+
/** The entry of the map form of `FamilyBy` for each kind without its own entry. */
|
|
734
|
+
export declare const FAMILY_ANY_KIND = "*";
|
|
735
|
+
/**
|
|
736
|
+
* This function gives the family key of a cell. It throws a `RangeError` when the cell does not have a dimension of the key.
|
|
737
|
+
* In the map form, it also throws for a cell without `kind`, and for a kind with no entry and no entry `"*"`.
|
|
738
|
+
* For example, `{ "rect-grid": ["obstacles"], "*": [] }` gives `rect-grid/wall` for a `rect-grid` cell and `gallery` for a `gallery` cell.
|
|
739
|
+
*/
|
|
740
|
+
export declare function familyOf(cell: FamilyCell, familyBy: FamilyBy): string;
|
|
718
741
|
/** The hook that gives the ids of the properties that one input declares. */
|
|
719
742
|
export type Declares<I> = (input: I) => readonly string[];
|
|
720
743
|
/**
|
|
@@ -734,8 +757,14 @@ export declare function declaredClaimOf(manifest: object): DeclaredClaim | undef
|
|
|
734
757
|
* - Each id of `ids` has a count in `claim.counts`, and `claim.counts` has no other id. Each count is the count of the inputs.
|
|
735
758
|
* - An id with a count below `minCount` is in `thin`. Each id of `thin` is an id of `ids`.
|
|
736
759
|
* - Each id that `declares` gives is an id of `ids`.
|
|
760
|
+
*
|
|
761
|
+
* With `claim.familyBy`, the hook `cellOf` gives the cell of each input. The default reads the field `cell` of the input.
|
|
762
|
+
*
|
|
763
|
+
* - Each cell has each dimension of its key. In the map form, each cell has a `kind`, and the kind has an entry or the entry `"*"` is there.
|
|
764
|
+
* - Two cells with different dimension values do not give the same key.
|
|
765
|
+
* - With `claim.familyMin`, each family has `familyMin` inputs or more.
|
|
737
766
|
*/
|
|
738
|
-
export declare function checkDeclared<I>(inputs: Iterable<I>, declares: Declares<I>, claim: DeclaredClaim, ids?: readonly string[]): ClaimBreak[];
|
|
767
|
+
export declare function checkDeclared<I>(inputs: Iterable<I>, declares: Declares<I>, claim: DeclaredClaim, ids?: readonly string[], cellOf?: (input: I) => FamilyCell | undefined): ClaimBreak[];
|
|
739
768
|
/** The id and the hash of one input of a corpus. */
|
|
740
769
|
export interface CorpusEntry {
|
|
741
770
|
readonly id: string;
|
|
@@ -966,4 +995,113 @@ export declare function splitConfig(config: StrategyConfig, engine: string): Str
|
|
|
966
995
|
* The constraints get the same prefixes. The named configs are the pairs `<name of a>+<name of b>`. It throws a `RangeError` when the two engines have the same name.
|
|
967
996
|
*/
|
|
968
997
|
export declare function productSpace(a: StrategySpace, b: StrategySpace, name: string): StrategySpace;
|
|
998
|
+
/**
|
|
999
|
+
* The largest count of inputs for the exact permutation test. Up to this count, the test enumerates all `2^n` sign sets.
|
|
1000
|
+
* At 16 inputs, that is 65 536 sign sets, approximately 3 times the default draws. Above it, the cost doubles with each input, so the test uses seeded draws.
|
|
1001
|
+
*/
|
|
1002
|
+
export declare const VERDICT_EXACT_MAX = 16;
|
|
1003
|
+
/** The largest value of the option `exactMax`. At 20 inputs, the exact test has approximately 1 million sign sets. */
|
|
1004
|
+
export declare const VERDICT_EXACT_CAP = 20;
|
|
1005
|
+
/** The default count of the samples of each bootstrap. It is the parameter `bootstrapRuns` of `docs/PROMOTION.md`. */
|
|
1006
|
+
export declare const VERDICT_RUNS = 1e4;
|
|
1007
|
+
/** The default count of the random sign sets of the permutation test above `VERDICT_EXACT_MAX` inputs. */
|
|
1008
|
+
export declare const VERDICT_DRAWS = 2e4;
|
|
1009
|
+
/** The default seed of the bootstraps and of the random sign sets. */
|
|
1010
|
+
export declare const VERDICT_SEED = 1;
|
|
1011
|
+
/** The fields of a record that the verdict reads. */
|
|
1012
|
+
export type VerdictRecord = Pick<LayoutRecord, "strategy" | "input" | "seed" | "measures"> & {
|
|
1013
|
+
readonly skipped?: string;
|
|
1014
|
+
};
|
|
1015
|
+
/** One side of a verdict: a strategy id, or a test that selects the records of the side. */
|
|
1016
|
+
export type VerdictSide<R extends VerdictRecord = VerdictRecord> = string | ((record: R) => boolean);
|
|
1017
|
+
/** The arguments of `verdict`. `tie` and `level` have no default. */
|
|
1018
|
+
export interface VerdictOptions<R extends VerdictRecord = VerdictRecord> {
|
|
1019
|
+
/** The unit of the verdict. The family is the only unit. */
|
|
1020
|
+
readonly by: "family";
|
|
1021
|
+
/** The hook that gives the family of a record, for example `familyOf` of the cell of its input. */
|
|
1022
|
+
readonly family: (record: R) => string;
|
|
1023
|
+
/** The measure. Its direction tells which side is better. */
|
|
1024
|
+
readonly measure: Pick<MeasureSpec, "id" | "direction">;
|
|
1025
|
+
/** The baseline side. */
|
|
1026
|
+
readonly a: VerdictSide<R>;
|
|
1027
|
+
/** The candidate side. A positive difference tells that `b` is better than `a`. */
|
|
1028
|
+
readonly b: VerdictSide<R>;
|
|
1029
|
+
/**
|
|
1030
|
+
* The minimum effect of the sign of a family, as `minEffect` of `MeasureSpec`. A smaller change of the family mean is a tie.
|
|
1031
|
+
* `compareMeasure` compares the two family means, so the smallest tie is one grid step of `RECORD_EPS`.
|
|
1032
|
+
*/
|
|
1033
|
+
readonly tie: number | {
|
|
1034
|
+
readonly rel: number;
|
|
1035
|
+
};
|
|
1036
|
+
/** The significance level, from 0 to 1. The permutation p must be below it. The intervals have the level `1 - level`. */
|
|
1037
|
+
readonly level: number;
|
|
1038
|
+
/** The count of the samples of each bootstrap. The default is `VERDICT_RUNS`. */
|
|
1039
|
+
readonly runs?: number;
|
|
1040
|
+
/** The count of the random sign sets above the exact limit. The default is `VERDICT_DRAWS`. */
|
|
1041
|
+
readonly draws?: number;
|
|
1042
|
+
/** The seed of the bootstraps and of the random sign sets. The default is `VERDICT_SEED`. */
|
|
1043
|
+
readonly seed?: number;
|
|
1044
|
+
/** The largest count of inputs for the exact test. The default is `VERDICT_EXACT_MAX`. The value 0 always uses draws. */
|
|
1045
|
+
readonly exactMax?: number;
|
|
1046
|
+
}
|
|
1047
|
+
/** A mean with its bootstrap interval. The interval is clear of 0 when `lo > 0` or `hi < 0`. */
|
|
1048
|
+
export interface VerdictInterval {
|
|
1049
|
+
readonly mean: number;
|
|
1050
|
+
readonly lo: number;
|
|
1051
|
+
readonly hi: number;
|
|
1052
|
+
}
|
|
1053
|
+
/** The difference of one family: the count of its inputs, the mean of their differences and its sign. */
|
|
1054
|
+
export interface FamilyVerdict {
|
|
1055
|
+
readonly family: string;
|
|
1056
|
+
readonly n: number;
|
|
1057
|
+
readonly mean: number;
|
|
1058
|
+
/** The value 1 tells that `b` is better, -1 that `b` is worse, and 0 a tie by `tie`. */
|
|
1059
|
+
readonly sign: -1 | 0 | 1;
|
|
1060
|
+
}
|
|
1061
|
+
/** The stratified paired permutation test. With `exact`, `draws` is the count of all the sign sets. */
|
|
1062
|
+
export interface VerdictPermutation {
|
|
1063
|
+
readonly p: number;
|
|
1064
|
+
readonly exact: boolean;
|
|
1065
|
+
readonly draws: number;
|
|
1066
|
+
readonly seed: number;
|
|
1067
|
+
}
|
|
1068
|
+
/** The two-sided exact sign test of the family signs. It is a report only. Ties do not count. */
|
|
1069
|
+
export interface VerdictSignTest {
|
|
1070
|
+
readonly wins: number;
|
|
1071
|
+
readonly losses: number;
|
|
1072
|
+
readonly ties: number;
|
|
1073
|
+
readonly p: number;
|
|
1074
|
+
}
|
|
1075
|
+
/** The label of a verdict. `better` and `worse` tell the side of `b`. */
|
|
1076
|
+
export type VerdictLabel = "better" | "worse" | "depends on the mix" | "no verdict";
|
|
1077
|
+
/** The result of `verdict`. */
|
|
1078
|
+
export interface VerdictResult {
|
|
1079
|
+
readonly measure: string;
|
|
1080
|
+
/** The count of the paired inputs. */
|
|
1081
|
+
readonly n: number;
|
|
1082
|
+
/** The mean of the family means, with a bootstrap of the seeds inside each family. */
|
|
1083
|
+
readonly stratified: VerdictInterval;
|
|
1084
|
+
/** The mean of the inputs, with a bootstrap of the inputs. */
|
|
1085
|
+
readonly pooled: VerdictInterval;
|
|
1086
|
+
readonly permutation: VerdictPermutation;
|
|
1087
|
+
/** The families, in sort order. */
|
|
1088
|
+
readonly families: readonly FamilyVerdict[];
|
|
1089
|
+
readonly signTest: VerdictSignTest;
|
|
1090
|
+
/** `true` only when the two means have the same sign, the two intervals are clear of 0 and the permutation p is below `level`. */
|
|
1091
|
+
readonly agree: boolean;
|
|
1092
|
+
readonly label: VerdictLabel;
|
|
1093
|
+
/** The inputs without a pair: one side has no record, a skipped record or no value of the measure. */
|
|
1094
|
+
readonly dropped: readonly string[];
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* This function gives the verdict of the candidate `b` against the baseline `a` on one measure, with the family as the unit (`docs/VERDICT.md`).
|
|
1098
|
+
*
|
|
1099
|
+
* - It pairs the records by input and seed. The difference of a pair is `b - a` for a measure where more is better, else `a - b`.
|
|
1100
|
+
* - `stratified` is the mean of the family means. Its bootstrap draws the seeds inside each family. `pooled` is the mean of the inputs.
|
|
1101
|
+
* - The permutation test changes the sign of each difference inside its family. Its statistic is the mean of the family means.
|
|
1102
|
+
* - The label is `better` or `worse` only when `agree` is `true`. Opposite signs of the two means give `depends on the mix`.
|
|
1103
|
+
*
|
|
1104
|
+
* It throws a `RangeError` for a missing `tie` or `level`, and for no pair. Two records of one side and input, or a value that is not finite, also cause a `RangeError`.
|
|
1105
|
+
*/
|
|
1106
|
+
export declare function verdict<R extends VerdictRecord>(records: Iterable<R>, options: VerdictOptions<R>): VerdictResult;
|
|
969
1107
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/geometry.ts","../src/tolerance.ts","../src/input.ts","../src/output.ts","../src/record.ts","../src/rules.ts","../src/turn.ts","../src/measures.ts","../src/strategy.ts","../src/random.ts","../src/edges.ts","../src/hash.ts","../src/portfolio.ts","../src/corpus.ts","../src/strategy-space.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/geometry.ts","../src/tolerance.ts","../src/input.ts","../src/output.ts","../src/record.ts","../src/rules.ts","../src/turn.ts","../src/measures.ts","../src/strategy.ts","../src/random.ts","../src/edges.ts","../src/hash.ts","../src/portfolio.ts","../src/corpus.ts","../src/strategy-space.ts","../src/verdict.ts"],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -1394,6 +1394,52 @@ function portfolio(id, members, key, options = {}) {
|
|
|
1394
1394
|
};
|
|
1395
1395
|
}
|
|
1396
1396
|
// src/corpus.ts
|
|
1397
|
+
/** The entry of the map form of `FamilyBy` for each kind without its own entry. */
|
|
1398
|
+
const FAMILY_ANY_KIND = "*";
|
|
1399
|
+
/** The separator of the parts of a family key. */
|
|
1400
|
+
const FAMILY_SEP = "/";
|
|
1401
|
+
/** This function gives the text of a dimension value. A missing value, or a value that is not a string, a number or a boolean, gives `undefined`. */
|
|
1402
|
+
function partOf$1(value) {
|
|
1403
|
+
if (typeof value === "string") return value;
|
|
1404
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1405
|
+
}
|
|
1406
|
+
/** This function gives the parts of the family key of a cell. A missing dimension, a missing kind or a kind without an entry gives a problem. */
|
|
1407
|
+
function familyParts(cell, familyBy) {
|
|
1408
|
+
let dims;
|
|
1409
|
+
let kind;
|
|
1410
|
+
const parts = [];
|
|
1411
|
+
if (Array.isArray(familyBy)) dims = familyBy;
|
|
1412
|
+
else {
|
|
1413
|
+
const map = familyBy;
|
|
1414
|
+
kind = partOf$1(cell["kind"]);
|
|
1415
|
+
if (kind === void 0) return { problem: "a cell without kind, which the map form of familyBy needs" };
|
|
1416
|
+
const entry = Object.hasOwn(map, kind) ? map[kind] : Object.hasOwn(map, "*") ? map["*"] : void 0;
|
|
1417
|
+
if (entry === void 0) return { problem: `the kind "${kind}", which has no entry in familyBy and no entry "*"` };
|
|
1418
|
+
dims = entry;
|
|
1419
|
+
parts.push(kind);
|
|
1420
|
+
}
|
|
1421
|
+
for (const dim of dims) {
|
|
1422
|
+
const part = partOf$1(cell[dim]);
|
|
1423
|
+
if (part === void 0) return { problem: `a cell${kind === void 0 ? "" : ` of the kind "${kind}"`} without the dimension "${dim}" of familyBy` };
|
|
1424
|
+
parts.push(part);
|
|
1425
|
+
}
|
|
1426
|
+
return { parts };
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* This function gives the family key of a cell. It throws a `RangeError` when the cell does not have a dimension of the key.
|
|
1430
|
+
* In the map form, it also throws for a cell without `kind`, and for a kind with no entry and no entry `"*"`.
|
|
1431
|
+
* For example, `{ "rect-grid": ["obstacles"], "*": [] }` gives `rect-grid/wall` for a `rect-grid` cell and `gallery` for a `gallery` cell.
|
|
1432
|
+
*/
|
|
1433
|
+
function familyOf(cell, familyBy) {
|
|
1434
|
+
const found = familyParts(cell, familyBy);
|
|
1435
|
+
if ("problem" in found) throw new RangeError(`familyOf: ${found.problem}.`);
|
|
1436
|
+
return found.parts.join(FAMILY_SEP);
|
|
1437
|
+
}
|
|
1438
|
+
/** This function tells if a value is a `FamilyBy`: a list of dimensions, or a map from a kind to a list of dimensions. */
|
|
1439
|
+
function isFamilyBy(value) {
|
|
1440
|
+
const isDims = (v) => Array.isArray(v) && v.every((d) => typeof d === "string");
|
|
1441
|
+
return isDims(value) || isObject$1(value) && Object.values(value).every(isDims);
|
|
1442
|
+
}
|
|
1397
1443
|
/**
|
|
1398
1444
|
* This function gives the count of the inputs for each property id. Each id of `ids` has a count, also when the count is 0.
|
|
1399
1445
|
* An id from `declares` that is not in `ids` comes after the ids of `ids`, in the order of the first input that declares it.
|
|
@@ -1420,10 +1466,15 @@ function declaredClaimOf(manifest) {
|
|
|
1420
1466
|
if (!Number.isInteger(minCount) || minCount < 0) throw new Error("The field claims.declared.minCount is a whole number of 0 or more.");
|
|
1421
1467
|
if (!Array.isArray(thin) || !thin.every((id) => typeof id === "string")) throw new Error("The field claims.declared.thin is a list of property ids.");
|
|
1422
1468
|
if (!isObject$1(counts) || !Object.values(counts).every((n) => Number.isInteger(n) && n >= 0)) throw new Error("The field claims.declared.counts gives a whole count for each property id.");
|
|
1469
|
+
const { familyBy, familyMin } = declared;
|
|
1470
|
+
if (familyBy !== void 0 && !isFamilyBy(familyBy)) throw new Error("The field claims.declared.familyBy is a list of dimensions, or a map from a kind to a list of dimensions.");
|
|
1471
|
+
if (familyMin !== void 0 && (!Number.isInteger(familyMin) || familyMin < 0)) throw new Error("The field claims.declared.familyMin is a whole number of 0 or more.");
|
|
1423
1472
|
return {
|
|
1424
1473
|
minCount,
|
|
1425
1474
|
thin: [...thin],
|
|
1426
|
-
counts: { ...counts }
|
|
1475
|
+
counts: { ...counts },
|
|
1476
|
+
...familyBy === void 0 ? {} : { familyBy: Array.isArray(familyBy) ? [...familyBy] : { ...familyBy } },
|
|
1477
|
+
...familyMin === void 0 ? {} : { familyMin }
|
|
1427
1478
|
};
|
|
1428
1479
|
}
|
|
1429
1480
|
/**
|
|
@@ -1433,9 +1484,16 @@ function declaredClaimOf(manifest) {
|
|
|
1433
1484
|
* - Each id of `ids` has a count in `claim.counts`, and `claim.counts` has no other id. Each count is the count of the inputs.
|
|
1434
1485
|
* - An id with a count below `minCount` is in `thin`. Each id of `thin` is an id of `ids`.
|
|
1435
1486
|
* - Each id that `declares` gives is an id of `ids`.
|
|
1487
|
+
*
|
|
1488
|
+
* With `claim.familyBy`, the hook `cellOf` gives the cell of each input. The default reads the field `cell` of the input.
|
|
1489
|
+
*
|
|
1490
|
+
* - Each cell has each dimension of its key. In the map form, each cell has a `kind`, and the kind has an entry or the entry `"*"` is there.
|
|
1491
|
+
* - Two cells with different dimension values do not give the same key.
|
|
1492
|
+
* - With `claim.familyMin`, each family has `familyMin` inputs or more.
|
|
1436
1493
|
*/
|
|
1437
|
-
function checkDeclared(inputs, declares, claim, ids = Object.keys(claim.counts)) {
|
|
1438
|
-
const
|
|
1494
|
+
function checkDeclared(inputs, declares, claim, ids = Object.keys(claim.counts), cellOf = cellField) {
|
|
1495
|
+
const list = [...inputs];
|
|
1496
|
+
const computed = declaredCounts(list, declares, ids);
|
|
1439
1497
|
const out = [];
|
|
1440
1498
|
for (const id of ids) if (!(id in claim.counts)) out.push(`the property "${id}" has no count`);
|
|
1441
1499
|
else if (claim.counts[id] !== computed[id]) out.push(`the property "${id}" has the count ${claim.counts[id]}, but the inputs give ${computed[id]}`);
|
|
@@ -1443,11 +1501,58 @@ function checkDeclared(inputs, declares, claim, ids = Object.keys(claim.counts))
|
|
|
1443
1501
|
for (const id of ids) if ((computed[id] ?? 0) < claim.minCount && !claim.thin.includes(id)) out.push(`the property "${id}" has ${computed[id]} inputs, below minCount ${claim.minCount}, and it is not in thin`);
|
|
1444
1502
|
for (const id of claim.thin) if (!ids.includes(id)) out.push(`"${id}" of thin is not a declared property`);
|
|
1445
1503
|
for (const id of Object.keys(computed)) if (!ids.includes(id)) out.push(`${computed[id]} inputs declare "${id}", which is not a declared property`);
|
|
1446
|
-
|
|
1504
|
+
const breaks = out.map((detail) => ({
|
|
1447
1505
|
claim: "declared",
|
|
1448
1506
|
detail,
|
|
1449
1507
|
inputs: []
|
|
1450
1508
|
}));
|
|
1509
|
+
if (claim.familyBy !== void 0) breaks.push(...checkFamilies(list, claim.familyBy, claim.familyMin, cellOf));
|
|
1510
|
+
return breaks;
|
|
1511
|
+
}
|
|
1512
|
+
/** This function gives the field `cell` of an input, when it is an object. */
|
|
1513
|
+
function cellField(input) {
|
|
1514
|
+
const cell = isObject$1(input) ? input["cell"] : void 0;
|
|
1515
|
+
return isObject$1(cell) ? cell : void 0;
|
|
1516
|
+
}
|
|
1517
|
+
/** This function gives the field `id` of an input, or its place in the list. */
|
|
1518
|
+
function inputId(input, at) {
|
|
1519
|
+
const id = isObject$1(input) ? input["id"] : void 0;
|
|
1520
|
+
return typeof id === "string" ? id : `#${at}`;
|
|
1521
|
+
}
|
|
1522
|
+
/** This function gives the breaks of the family keys of the inputs: the problems of each key, the collisions and the thin families. */
|
|
1523
|
+
function checkFamilies(inputs, familyBy, familyMin, cellOf) {
|
|
1524
|
+
const problems = /* @__PURE__ */ new Map();
|
|
1525
|
+
const members = /* @__PURE__ */ new Map();
|
|
1526
|
+
inputs.forEach((input, at) => {
|
|
1527
|
+
const id = inputId(input, at);
|
|
1528
|
+
const cell = cellOf(input);
|
|
1529
|
+
const found = cell === void 0 ? { problem: "no cell, which familyBy needs" } : familyParts(cell, familyBy);
|
|
1530
|
+
if ("problem" in found) {
|
|
1531
|
+
problems.set(found.problem, [...problems.get(found.problem) ?? [], id]);
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
const key = found.parts.join(FAMILY_SEP);
|
|
1535
|
+
const family = members.get(key) ?? {
|
|
1536
|
+
tuples: /* @__PURE__ */ new Map(),
|
|
1537
|
+
ids: []
|
|
1538
|
+
};
|
|
1539
|
+
const tuple = JSON.stringify(found.parts);
|
|
1540
|
+
family.tuples.set(tuple, [...family.tuples.get(tuple) ?? [], id]);
|
|
1541
|
+
family.ids.push(id);
|
|
1542
|
+
members.set(key, family);
|
|
1543
|
+
});
|
|
1544
|
+
const breaks = [];
|
|
1545
|
+
const add = (detail, ids) => void breaks.push({
|
|
1546
|
+
claim: "declared",
|
|
1547
|
+
detail,
|
|
1548
|
+
inputs: [...ids]
|
|
1549
|
+
});
|
|
1550
|
+
for (const [problem, ids] of problems) add(`${ids.length} inputs have ${problem}`, ids);
|
|
1551
|
+
for (const [key, family] of members) {
|
|
1552
|
+
if (family.tuples.size > 1) add(`the family "${key}" comes from different dimension values: ${[...family.tuples.keys()].join(" and ")}`, family.ids);
|
|
1553
|
+
if (familyMin !== void 0 && family.ids.length < familyMin) add(`the family "${key}" has ${family.ids.length} inputs, below familyMin ${familyMin}`, family.ids);
|
|
1554
|
+
}
|
|
1555
|
+
return breaks;
|
|
1451
1556
|
}
|
|
1452
1557
|
/**
|
|
1453
1558
|
* This function gives the hash of a corpus: the first 16 hex characters of a SHA-256. The text of the SHA-256 is `canonicalJson`
|
|
@@ -2258,6 +2363,218 @@ function productSpace(a, b, name) {
|
|
|
2258
2363
|
parts: [a, b]
|
|
2259
2364
|
};
|
|
2260
2365
|
}
|
|
2261
|
-
|
|
2366
|
+
// src/verdict.ts
|
|
2367
|
+
/**
|
|
2368
|
+
* The largest count of inputs for the exact permutation test. Up to this count, the test enumerates all `2^n` sign sets.
|
|
2369
|
+
* At 16 inputs, that is 65 536 sign sets, approximately 3 times the default draws. Above it, the cost doubles with each input, so the test uses seeded draws.
|
|
2370
|
+
*/
|
|
2371
|
+
const VERDICT_EXACT_MAX = 16;
|
|
2372
|
+
/** The largest value of the option `exactMax`. At 20 inputs, the exact test has approximately 1 million sign sets. */
|
|
2373
|
+
const VERDICT_EXACT_CAP = 20;
|
|
2374
|
+
/** The default count of the samples of each bootstrap. It is the parameter `bootstrapRuns` of `docs/PROMOTION.md`. */
|
|
2375
|
+
const VERDICT_RUNS = 1e4;
|
|
2376
|
+
/** The default count of the random sign sets of the permutation test above `VERDICT_EXACT_MAX` inputs. */
|
|
2377
|
+
const VERDICT_DRAWS = 2e4;
|
|
2378
|
+
/** The default seed of the bootstraps and of the random sign sets. */
|
|
2379
|
+
const VERDICT_SEED = 1;
|
|
2380
|
+
/** This function gives the mean of a list that is not empty. */
|
|
2381
|
+
const meanOf = (values) => values.reduce((s, v) => s + v, 0) / values.length;
|
|
2382
|
+
/** This function gives the `q` quantile of sorted values, with linear interpolation. */
|
|
2383
|
+
function quantileOf(sorted, q) {
|
|
2384
|
+
const at = (sorted.length - 1) * q;
|
|
2385
|
+
const lo = Math.floor(at);
|
|
2386
|
+
const hi = Math.ceil(at);
|
|
2387
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (at - lo);
|
|
2388
|
+
}
|
|
2389
|
+
/** This function gives the interval of the samples of a bootstrap at the level `1 - level`. */
|
|
2390
|
+
function intervalOf(mean, samples, level) {
|
|
2391
|
+
samples.sort((x, y) => x - y);
|
|
2392
|
+
return {
|
|
2393
|
+
mean,
|
|
2394
|
+
lo: quantileOf(samples, level / 2),
|
|
2395
|
+
hi: quantileOf(samples, 1 - level / 2)
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
/** This function tells if an interval is clear of 0 on the side of the sign `sign`. */
|
|
2399
|
+
const clearOn = (i, sign) => sign > 0 ? i.lo > 0 : sign < 0 ? i.hi < 0 : false;
|
|
2400
|
+
/** This function gives the two-sided p of the exact binomial sign test, with `p = 0.5`. No count gives 1. */
|
|
2401
|
+
function signP(wins, losses) {
|
|
2402
|
+
const n = wins + losses;
|
|
2403
|
+
if (n === 0) return 1;
|
|
2404
|
+
const k = Math.min(wins, losses);
|
|
2405
|
+
let tail = 0;
|
|
2406
|
+
let c = 1;
|
|
2407
|
+
for (let i = 0; i <= k; i++) {
|
|
2408
|
+
tail += c;
|
|
2409
|
+
c = c * (n - i) / (i + 1);
|
|
2410
|
+
}
|
|
2411
|
+
return Math.min(1, 2 * tail / 2 ** n);
|
|
2412
|
+
}
|
|
2413
|
+
/** This function tells if a value is a positive whole number. */
|
|
2414
|
+
const isCount = (v) => Number.isInteger(v) && v > 0;
|
|
2415
|
+
/** This function gives the test of one side. */
|
|
2416
|
+
function sideTest(side) {
|
|
2417
|
+
return typeof side === "string" ? (r) => r.strategy === side : side;
|
|
2418
|
+
}
|
|
2419
|
+
/** This function pairs the records of `a` and `b` by input and seed, and gives the pairs in the order of family and key. */
|
|
2420
|
+
function pairsOf(records, options) {
|
|
2421
|
+
const isA = sideTest(options.a);
|
|
2422
|
+
const isB = sideTest(options.b);
|
|
2423
|
+
const sides = /* @__PURE__ */ new Map();
|
|
2424
|
+
for (const r of records) {
|
|
2425
|
+
const inA = isA(r);
|
|
2426
|
+
const inB = isB(r);
|
|
2427
|
+
if (inA && inB) throw new RangeError(`verdict: the record of the input "${r.input}" is in the two sides`);
|
|
2428
|
+
if (!inA && !inB) continue;
|
|
2429
|
+
const key = `${r.input}#${r.seed}`;
|
|
2430
|
+
const entry = sides.get(key) ?? {};
|
|
2431
|
+
const side = inA ? "a" : "b";
|
|
2432
|
+
if (entry[side] !== void 0) throw new RangeError(`verdict: the side ${side} has two records of the input "${r.input}" and the seed ${r.seed}`);
|
|
2433
|
+
entry[side] = r;
|
|
2434
|
+
sides.set(key, entry);
|
|
2435
|
+
}
|
|
2436
|
+
const pairs = [];
|
|
2437
|
+
const dropped = [];
|
|
2438
|
+
const id = options.measure.id;
|
|
2439
|
+
for (const [key, { a, b }] of sides) {
|
|
2440
|
+
const va = a?.skipped === void 0 ? a?.measures[id] : void 0;
|
|
2441
|
+
const vb = b?.skipped === void 0 ? b?.measures[id] : void 0;
|
|
2442
|
+
if (a === void 0 || b === void 0 || va === void 0 || vb === void 0) {
|
|
2443
|
+
dropped.push(key);
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
if (!Number.isFinite(va) || !Number.isFinite(vb)) throw new RangeError(`verdict: the measure ${id} of the input "${a.input}" is not finite`);
|
|
2447
|
+
const family = options.family(a);
|
|
2448
|
+
if (options.family(b) !== family) throw new RangeError(`verdict: the two records of the input "${a.input}" have different families`);
|
|
2449
|
+
pairs.push({
|
|
2450
|
+
key,
|
|
2451
|
+
family,
|
|
2452
|
+
a: va,
|
|
2453
|
+
b: vb,
|
|
2454
|
+
d: options.measure.direction === "higher" ? vb - va : va - vb
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
pairs.sort((x, y) => x.family < y.family ? -1 : x.family > y.family ? 1 : x.key < y.key ? -1 : x.key > y.key ? 1 : 0);
|
|
2458
|
+
dropped.sort();
|
|
2459
|
+
return {
|
|
2460
|
+
pairs,
|
|
2461
|
+
dropped
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* This function gives the verdict of the candidate `b` against the baseline `a` on one measure, with the family as the unit (`docs/VERDICT.md`).
|
|
2466
|
+
*
|
|
2467
|
+
* - It pairs the records by input and seed. The difference of a pair is `b - a` for a measure where more is better, else `a - b`.
|
|
2468
|
+
* - `stratified` is the mean of the family means. Its bootstrap draws the seeds inside each family. `pooled` is the mean of the inputs.
|
|
2469
|
+
* - The permutation test changes the sign of each difference inside its family. Its statistic is the mean of the family means.
|
|
2470
|
+
* - The label is `better` or `worse` only when `agree` is `true`. Opposite signs of the two means give `depends on the mix`.
|
|
2471
|
+
*
|
|
2472
|
+
* It throws a `RangeError` for a missing `tie` or `level`, and for no pair. Two records of one side and input, or a value that is not finite, also cause a `RangeError`.
|
|
2473
|
+
*/
|
|
2474
|
+
function verdict(records, options) {
|
|
2475
|
+
const { tie, level, measure } = options;
|
|
2476
|
+
if (options.by !== "family") throw new RangeError(`verdict: the unit is "family", not "${String(options.by)}"`);
|
|
2477
|
+
if (tie === void 0 || (typeof tie === "number" ? !(tie >= 0) : !(tie.rel >= 0))) throw new RangeError("verdict: tie is a required number of 0 or more, or { rel } of 0 or more");
|
|
2478
|
+
if (level === void 0 || !(level > 0 && level < 1)) throw new RangeError("verdict: level is a required number between 0 and 1");
|
|
2479
|
+
const runs = options.runs ?? 1e4;
|
|
2480
|
+
const draws = options.draws ?? 2e4;
|
|
2481
|
+
const seed = options.seed ?? 1;
|
|
2482
|
+
const exactMax = options.exactMax ?? 16;
|
|
2483
|
+
if (!isCount(runs) || !isCount(draws)) throw new RangeError("verdict: runs and draws are whole numbers above 0");
|
|
2484
|
+
if (!Number.isInteger(exactMax) || exactMax < 0 || exactMax > 20) throw new RangeError(`verdict: exactMax is a whole number from 0 to 20`);
|
|
2485
|
+
const { pairs, dropped } = pairsOf(records, options);
|
|
2486
|
+
const n = pairs.length;
|
|
2487
|
+
if (n === 0) throw new RangeError(`verdict: the records give no pair of the measure ${measure.id}`);
|
|
2488
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2489
|
+
for (const p of pairs) groups.set(p.family, [...groups.get(p.family) ?? [], p]);
|
|
2490
|
+
const lists = [...groups.values()];
|
|
2491
|
+
const spec = {
|
|
2492
|
+
direction: measure.direction,
|
|
2493
|
+
minEffect: tie
|
|
2494
|
+
};
|
|
2495
|
+
const families = lists.map((list) => {
|
|
2496
|
+
const verdictOf = compareMeasure(meanOf(list.map((p) => p.b)), meanOf(list.map((p) => p.a)), spec);
|
|
2497
|
+
return {
|
|
2498
|
+
family: list[0].family,
|
|
2499
|
+
n: list.length,
|
|
2500
|
+
mean: meanOf(list.map((p) => p.d)),
|
|
2501
|
+
sign: verdictOf === "better" ? 1 : verdictOf === "worse" ? -1 : 0
|
|
2502
|
+
};
|
|
2503
|
+
});
|
|
2504
|
+
const stratMean = meanOf(families.map((f) => f.mean));
|
|
2505
|
+
const rs = stepRandom(seed, "verdict.stratified");
|
|
2506
|
+
const stratSamples = [];
|
|
2507
|
+
for (let run = 0; run < runs; run++) {
|
|
2508
|
+
let sum = 0;
|
|
2509
|
+
for (const list of lists) {
|
|
2510
|
+
let s = 0;
|
|
2511
|
+
for (let j = 0; j < list.length; j++) s += list[Math.floor(rs() * list.length)].d;
|
|
2512
|
+
sum += s / list.length;
|
|
2513
|
+
}
|
|
2514
|
+
stratSamples.push(sum / lists.length);
|
|
2515
|
+
}
|
|
2516
|
+
const stratified = intervalOf(stratMean, stratSamples, level);
|
|
2517
|
+
const pooledMean = meanOf(pairs.map((p) => p.d));
|
|
2518
|
+
const rp = stepRandom(seed, "verdict.pooled");
|
|
2519
|
+
const pooledSamples = [];
|
|
2520
|
+
for (let run = 0; run < runs; run++) {
|
|
2521
|
+
let s = 0;
|
|
2522
|
+
for (let j = 0; j < n; j++) s += pairs[Math.floor(rp() * n)].d;
|
|
2523
|
+
pooledSamples.push(s / n);
|
|
2524
|
+
}
|
|
2525
|
+
const pooled = intervalOf(pooledMean, pooledSamples, level);
|
|
2526
|
+
const terms = lists.flatMap((list) => list.map((p) => p.d / (lists.length * list.length)));
|
|
2527
|
+
const observed = Math.abs(terms.reduce((s, t) => s + t, 0));
|
|
2528
|
+
const slack = 1e-12 * terms.reduce((s, t) => s + Math.abs(t), 0);
|
|
2529
|
+
const exact = n <= exactMax;
|
|
2530
|
+
let hits = 0;
|
|
2531
|
+
let total;
|
|
2532
|
+
if (exact) {
|
|
2533
|
+
total = 2 ** n;
|
|
2534
|
+
for (let mask = 0; mask < total; mask++) {
|
|
2535
|
+
let t = 0;
|
|
2536
|
+
for (let i = 0; i < n; i++) t += mask & 1 << i ? -terms[i] : terms[i];
|
|
2537
|
+
if (Math.abs(t) >= observed - slack) hits++;
|
|
2538
|
+
}
|
|
2539
|
+
} else {
|
|
2540
|
+
total = draws;
|
|
2541
|
+
const rf = stepRandom(seed, "verdict.permutation");
|
|
2542
|
+
for (let k = 0; k < draws; k++) {
|
|
2543
|
+
let t = 0;
|
|
2544
|
+
for (let i = 0; i < n; i++) t += rf() < .5 ? -terms[i] : terms[i];
|
|
2545
|
+
if (Math.abs(t) >= observed - slack) hits++;
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
const permutation = {
|
|
2549
|
+
p: exact ? hits / total : (hits + 1) / (total + 1),
|
|
2550
|
+
exact,
|
|
2551
|
+
draws: total,
|
|
2552
|
+
seed
|
|
2553
|
+
};
|
|
2554
|
+
const wins = families.filter((f) => f.sign === 1).length;
|
|
2555
|
+
const losses = families.filter((f) => f.sign === -1).length;
|
|
2556
|
+
const signTest = {
|
|
2557
|
+
wins,
|
|
2558
|
+
losses,
|
|
2559
|
+
ties: families.length - wins - losses,
|
|
2560
|
+
p: signP(wins, losses)
|
|
2561
|
+
};
|
|
2562
|
+
const sign = Math.sign(stratified.mean);
|
|
2563
|
+
const agree = sign !== 0 && sign === Math.sign(pooled.mean) && clearOn(stratified, sign) && clearOn(pooled, sign) && permutation.p < level;
|
|
2564
|
+
const label = agree ? sign > 0 ? "better" : "worse" : sign * Math.sign(pooled.mean) < 0 ? "depends on the mix" : "no verdict";
|
|
2565
|
+
return {
|
|
2566
|
+
measure: measure.id,
|
|
2567
|
+
n,
|
|
2568
|
+
stratified,
|
|
2569
|
+
pooled,
|
|
2570
|
+
permutation,
|
|
2571
|
+
families,
|
|
2572
|
+
signTest,
|
|
2573
|
+
agree,
|
|
2574
|
+
label,
|
|
2575
|
+
dropped
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
export { DEFAULT_TOLERANCE, EDGE_DENSITY, EDGE_STEP_VERSION, EPS, FAMILY_ANY_KIND, JOINT_MEASURES, JOINT_MOVED_MIN, RECORD_EPS, RECORD_SCALE, SIDES, SPLIT_SCHEMA, STRATEGY_SPACE_SCHEMA, VERDICT_DRAWS, VERDICT_EXACT_CAP, VERDICT_EXACT_MAX, VERDICT_RUNS, VERDICT_SEED, applyPack, boundsOf, canonicalConfig, canonicalJson, checkConfig, checkDeclared, checkInput, checkSpace, checkSplit, compareMeasure, configId, corpusHashOf, countObstacleHits, countOutside, countOverlaps, countPortsOffBorder, declaredClaimOf, declaredCounts, documentHash, edgeStep, enumerateSpace, exercised, familyOf, fnv1a32, grow, joinConfigs, leaveOneSeedOut, marginSides, minEffectOf, mulberry32, outputGroups, outputHash, overlapOf, packHard, paramLevels, pickMoves, placedRect, portfolio, productSpace, randomInt, rectInRegion, regionHitsRect, selectBest, sha256Hex, spaceSize, splitConfig, splitFileName, splitFolds, stepRandom, toLayoutRecord, toPackInput, translate, turnBox, turnMargin, turnPoint, turnPort, turnRegion, turnSide, verdict };
|
|
2262
2579
|
|
|
2263
2580
|
//# sourceMappingURL=index.js.map
|