@idlekit/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/scenario/compile.d.ts +25 -0
- package/dist/scenario/compile.js +60 -0
- package/dist/scenario/compile.js.map +1 -1
- package/dist/scenario/scenarioContract.d.ts +152 -0
- package/dist/scenario/scenarioContract.js +2815 -0
- package/dist/scenario/scenarioContract.js.map +1 -0
- package/dist/sim/analysis/analyzers.d.ts +245 -0
- package/dist/sim/analysis/analyzers.js +1076 -0
- package/dist/sim/analysis/analyzers.js.map +1 -0
- package/dist/sim/analysis/contracts.d.ts +147 -0
- package/dist/sim/analysis/contracts.js +1230 -0
- package/dist/sim/analysis/contracts.js.map +1 -0
- package/dist/sim/analysis/distribution.d.ts +199 -0
- package/dist/sim/analysis/distribution.js +1246 -0
- package/dist/sim/analysis/distribution.js.map +1 -0
- package/dist/sim/analysis/eta.d.ts +118 -0
- package/dist/sim/analysis/eta.js +968 -70
- package/dist/sim/analysis/eta.js.map +1 -1
- package/dist/sim/analysis/experiment.d.ts +300 -0
- package/dist/sim/analysis/experiment.js +2206 -0
- package/dist/sim/analysis/experiment.js.map +1 -0
- package/dist/sim/analysis/growth.d.ts +2 -0
- package/dist/sim/analysis/growth.js +5 -1
- package/dist/sim/analysis/growth.js.map +1 -1
- package/dist/sim/analysis/growthFit.d.ts +183 -0
- package/dist/sim/analysis/growthFit.js +1798 -0
- package/dist/sim/analysis/growthFit.js.map +1 -0
- package/dist/sim/analysis/inspect.d.ts +236 -0
- package/dist/sim/analysis/inspect.js +388 -0
- package/dist/sim/analysis/inspect.js.map +1 -0
- package/dist/sim/analysis/metrics.d.ts +91 -0
- package/dist/sim/analysis/metrics.js +266 -0
- package/dist/sim/analysis/metrics.js.map +1 -0
- package/dist/sim/analysis/prestigeCycle.d.ts +6 -0
- package/dist/sim/analysis/prestigeCycle.js +10 -1
- package/dist/sim/analysis/prestigeCycle.js.map +1 -1
- package/dist/sim/analysis/registry.d.ts +41 -0
- package/dist/sim/analysis/registry.js +54 -0
- package/dist/sim/analysis/registry.js.map +1 -0
- package/dist/sim/analysis/resetActions.d.ts +7 -0
- package/dist/sim/analysis/resetActions.js +45 -0
- package/dist/sim/analysis/resetActions.js.map +1 -0
- package/dist/sim/analysis/resetCycle.d.ts +175 -0
- package/dist/sim/analysis/resetCycle.js +1009 -0
- package/dist/sim/analysis/resetCycle.js.map +1 -0
- package/dist/sim/analysis/resetOptimization.d.ts +198 -0
- package/dist/sim/analysis/resetOptimization.js +2187 -0
- package/dist/sim/analysis/resetOptimization.js.map +1 -0
- package/dist/sim/analysis/sensitivity.d.ts +73 -0
- package/dist/sim/analysis/sensitivity.js +160 -0
- package/dist/sim/analysis/sensitivity.js.map +1 -0
- package/dist/sim/monteCarlo.d.ts +12 -0
- package/dist/sim/monteCarlo.js +33 -4
- package/dist/sim/monteCarlo.js.map +1 -1
- package/dist/sim/observation.d.ts +22 -0
- package/dist/sim/observation.js +33 -0
- package/dist/sim/observation.js.map +1 -1
- package/dist/sim/offline.js +9 -2
- package/dist/sim/offline.js.map +1 -1
- package/dist/sim/random.d.ts +15 -0
- package/dist/sim/random.js +28 -0
- package/dist/sim/random.js.map +1 -1
- package/dist/sim/session.d.ts +7 -0
- package/dist/sim/session.js +13 -0
- package/dist/sim/session.js.map +1 -1
- package/dist/sim/simulator.js +7 -1
- package/dist/sim/simulator.js.map +1 -1
- package/dist/sim/step.d.ts +67 -1
- package/dist/sim/step.js +100 -18
- package/dist/sim/step.js.map +1 -1
- package/dist/sim/strategy/opt/freshRun.d.ts +3 -0
- package/dist/sim/strategy/opt/freshRun.js +6 -0
- package/dist/sim/strategy/opt/freshRun.js.map +1 -0
- package/dist/sim/strategy/opt/runner.js +2 -2
- package/dist/sim/strategy/opt/runner.js.map +1 -1
- package/dist/sim/strategy/planner.d.ts +26 -0
- package/dist/sim/strategy/planner.js +30 -0
- package/dist/sim/strategy/planner.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,2206 @@
|
|
|
1
|
+
import * as _validateReport_1 from "typia/lib/internal/_validateReport";
|
|
2
|
+
import * as _createStandardSchema_1 from "typia/lib/internal/_createStandardSchema";
|
|
3
|
+
import typia, {} from "typia";
|
|
4
|
+
import { compileScenario } from "../../scenario/compile";
|
|
5
|
+
import { canonicalJson, sha256Text } from "../../scenario/scenarioContract";
|
|
6
|
+
import { createRunFactory } from "../runFactory";
|
|
7
|
+
import { runScenario } from "../simulator";
|
|
8
|
+
import { runElapsedSec } from "../types";
|
|
9
|
+
import { assignmentFitsParamSpace, elasticityFromChange, localElasticity, relativeParameterChange, paretoFrontBy, } from "./sensitivity";
|
|
10
|
+
/**
|
|
11
|
+
* Experiment spec v1.
|
|
12
|
+
* Separate from ScenarioV1 and from strategy-only TuneSpecV1.
|
|
13
|
+
* The first method is a fixed candidate sweep plus one-at-a-time local sensitivity.
|
|
14
|
+
*/
|
|
15
|
+
const EXPERIMENT_ALGORITHM_VERSION = "experiment-oat-v1";
|
|
16
|
+
const EXPERIMENT_ESTIMATOR_VERSION = "one-at-a-time-local-v1";
|
|
17
|
+
const EXPERIMENT_SCHEMA_ID = "https://idlekit.dev/schemas/experiment.v1.schema.json";
|
|
18
|
+
const EXPERIMENT_METHOD = "fixed-sweep-and-one-at-a-time";
|
|
19
|
+
/**
|
|
20
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-tune-signature TuneSpec stays strategy-only. The experiment spec is a separate object.
|
|
21
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-tune-signature #365035c Re-read the section: strategy tune still scores seeds 7 and 11 as 9, and the experiment spec is not a TuneSpec.
|
|
22
|
+
*/
|
|
23
|
+
export const experimentSeparateSpec = "experiment-spec-not-tunespec";
|
|
24
|
+
/**
|
|
25
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-registered-setter Assignment uses a registered identifier path and the model or strategy schema. JSONPath, prototype, and eval paths are refused before a run.
|
|
26
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-registered-setter #847f7d0 Re-read the section: a prototype path, an out-of-range integer, a non-finite choice value, a non-finite space bound, a schema-invalid rate, a stripped path, overlapping paths, a session dimension, a repeated seed, a non-finite limit, and a repeated constraint id throw before model create.
|
|
27
|
+
*/
|
|
28
|
+
export const experimentRegisteredSetter = "registered-key-not-jsonpath";
|
|
29
|
+
/**
|
|
30
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-local-elasticity Constant income reaches T/p. Local elasticity is signed and is not a Sobol or global index.
|
|
31
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-local-elasticity #bd1aa44 Re-read the section: rate 2 reaches in 5 with elasticity -0.5, rate 0.5 reaches in 20 with elasticity -2, maxPicks stays flat, and the conformance relation checks -p/p' for divisor rates.
|
|
32
|
+
*/
|
|
33
|
+
export const experimentLocalOat = "one-at-a-time-local-not-sobol";
|
|
34
|
+
/**
|
|
35
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-candidate-order Candidate order does not change the report, and the input scenario object is not written.
|
|
36
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-candidate-order #7cede33 Re-read the section: reversed candidate ids keep the same effective hash, the scenario JSON is unchanged, the one compile per experiment brings create to 6, and elasticity rows follow the sorted dimension ids.
|
|
37
|
+
*/
|
|
38
|
+
export const experimentCandidateOrder = "sorted-candidates-unmutated-scenario";
|
|
39
|
+
/**
|
|
40
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-censored-horizon A goal missed inside the horizon stays censored. That time is not the horizon and is not differentiated.
|
|
41
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-censored-horizon #befcebb Re-read the section: horizon 9 with target 10 leaves the reach time null, not 9, the reach time is read on the way while the run continues to the horizon, and no scenario stop condition or step budget ends it early.
|
|
42
|
+
*/
|
|
43
|
+
export const experimentCensoredHorizon = "censored-time-not-horizon";
|
|
44
|
+
/**
|
|
45
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-pareto-unknown Pareto keeps per-metric tradeoffs. An unknown objective is uncompared, not a win.
|
|
46
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-pareto-unknown #440698a Re-read the section: A, B, and C are non-dominated, D is dominated, E is uncompared, money is compared pairwise in the effective engine, and the elasticity stays in that engine until it is text.
|
|
47
|
+
*/
|
|
48
|
+
export const experimentParetoUnknown = "unknown-is-not-superior";
|
|
49
|
+
/**
|
|
50
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-task-descriptor Each task records its id, parameter JSON, logical seed, and engine and model versions. It does not store a function.
|
|
51
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-task-descriptor #10a3f42 Re-read the section: the income candidate changes incomeRate and the policy candidate changes maxPicks on the same seed, both from the raw scenario params applied in dimension id order, and a string probe id is JSON text.
|
|
52
|
+
*/
|
|
53
|
+
export const experimentTaskDescriptor = "task-id-params-seed-version";
|
|
54
|
+
const WARNINGS = [
|
|
55
|
+
"one-at-a-time local sensitivity is not global sensitivity and is not a Sobol index",
|
|
56
|
+
"a censored reach time is not the horizon and is not differentiated",
|
|
57
|
+
"an unknown or censored objective is not a superior Pareto result",
|
|
58
|
+
"constraint violations are listed per metric and are not a weighted score",
|
|
59
|
+
];
|
|
60
|
+
export function experimentSchemaId() {
|
|
61
|
+
return EXPERIMENT_SCHEMA_ID;
|
|
62
|
+
}
|
|
63
|
+
export function experimentAlgorithmVersion() {
|
|
64
|
+
return EXPERIMENT_ALGORITHM_VERSION;
|
|
65
|
+
}
|
|
66
|
+
export function experimentEstimatorVersion() {
|
|
67
|
+
return EXPERIMENT_ESTIMATOR_VERSION;
|
|
68
|
+
}
|
|
69
|
+
export class ExperimentRejected extends Error {
|
|
70
|
+
constructor(message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "ExperimentRejected";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/*
|
|
76
|
+
* typia runs on first call, not at module load. A plugin that the bundled CLI loads
|
|
77
|
+
* imports core source without the transform, and a module-load call would throw there.
|
|
78
|
+
*/
|
|
79
|
+
let validateExperimentArtifactValidator;
|
|
80
|
+
/**
|
|
81
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-task-descriptor The generated validator accepts this artifact.
|
|
82
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-task-descriptor #10a3f42 Re-read the section: the wire object is experiment v1 and the method is fixed-sweep-and-one-at-a-time.
|
|
83
|
+
*/
|
|
84
|
+
export const validateExperimentArtifact = (input) => (validateExperimentArtifactValidator ??= (() => {
|
|
85
|
+
const _ip0 = input => 1 === input["schemaVersion"];
|
|
86
|
+
const _ip1 = input => "idlekit.experiment.v1" === input["contract"];
|
|
87
|
+
const _ip2 = input => "one-at-a-time-local-v1" === input["estimatorVersion"];
|
|
88
|
+
const _ip3 = input => "experiment-oat-v1" === input["algorithmVersion"];
|
|
89
|
+
const _ip4 = input => "fixed-sweep-and-one-at-a-time" === input["method"];
|
|
90
|
+
const _ip5 = input => false === input["globalSensitivity"];
|
|
91
|
+
const _ip6 = input => false === input["sobol"];
|
|
92
|
+
const _ip7 = input => "object" === typeof input["scenario"] && null !== input["scenario"] && _io1(input["scenario"]);
|
|
93
|
+
const _ip8 = input => "string" === typeof input["originalSpecHash"];
|
|
94
|
+
const _ip9 = input => "string" === typeof input["effectiveSpecHash"];
|
|
95
|
+
const _ip10 = input => "string" === typeof input["effectiveRunHash"];
|
|
96
|
+
const _ip11 = input => "object" === typeof input["budget"] && null !== input["budget"] && _io2(input["budget"]);
|
|
97
|
+
const _ip12 = input => Array.isArray(input["tasks"]) && input["tasks"].every(elem => "object" === typeof elem && null !== elem && _io3(elem));
|
|
98
|
+
const _ip13 = input => Array.isArray(input["candidates"]) && input["candidates"].every(elem => "object" === typeof elem && null !== elem && _io4(elem));
|
|
99
|
+
const _ip14 = input => Array.isArray(input["elasticities"]) && input["elasticities"].every(elem => "object" === typeof elem && null !== elem && _io6(elem));
|
|
100
|
+
const _ip15 = input => "object" === typeof input["pareto"] && null !== input["pareto"] && _io7(input["pareto"]);
|
|
101
|
+
const _ip16 = input => Array.isArray(input["warnings"]) && input["warnings"].every(elem => "string" === typeof elem);
|
|
102
|
+
const _ip17 = input => "string" === typeof input["taskId"];
|
|
103
|
+
const _ip18 = input => "string" === typeof input["candidateId"];
|
|
104
|
+
const _ip19 = input => "number" === typeof input["logicalSeed"];
|
|
105
|
+
const _ip20 = input => "string" === typeof input["engineId"];
|
|
106
|
+
const _ip21 = input => "string" === typeof input["engineVersion"];
|
|
107
|
+
const _ip22 = input => "string" === typeof input["modelId"];
|
|
108
|
+
const _ip23 = input => "number" === typeof input["modelVersion"];
|
|
109
|
+
const _ip24 = input => null === input["strategyId"] || "string" === typeof input["strategyId"];
|
|
110
|
+
const _ip25 = input => "string" === typeof input["modelParams"];
|
|
111
|
+
const _ip26 = input => "string" === typeof input["strategyParams"];
|
|
112
|
+
const _ip27 = input => "string" === typeof input["sessionPattern"];
|
|
113
|
+
const _ip28 = input => "number" === typeof input["sessionDays"];
|
|
114
|
+
const _ip29 = input => "number" === typeof input["horizonSec"];
|
|
115
|
+
const _ip30 = input => "number" === typeof input["stepSec"];
|
|
116
|
+
const _ip31 = input => "endMoney" === input["metricId"] || "timeToMoney" === input["metricId"] || "varBenefit" === input["metricId"];
|
|
117
|
+
const _ip32 = input => "censored" === input["status"] || "failed" === input["status"] || "observed" === input["status"] || "rejected" === input["status"];
|
|
118
|
+
const _ip33 = input => null === input["value"] || "string" === typeof input["value"];
|
|
119
|
+
const _ve0 = "Readonly<{ schemaVersion: 1; contract: \"idlekit.experiment.v1\"; estimatorVersion: \"one-at-a-time-local-v1\"; algorithmVersion: \"experiment-oat-v1\"; method: \"fixed-sweep-and-one-at-a-time\"; ... 11 more ...; warnings: readonly string[]; }>";
|
|
120
|
+
const _vp0 = (input, _path, _exceptionable = true) => 1 === input["schemaVersion"] || _report(_exceptionable, {
|
|
121
|
+
path: _path + ".schemaVersion",
|
|
122
|
+
expected: "1",
|
|
123
|
+
value: input["schemaVersion"]
|
|
124
|
+
});
|
|
125
|
+
const _vp1 = (input, _path, _exceptionable = true) => "idlekit.experiment.v1" === input["contract"] || _report(_exceptionable, {
|
|
126
|
+
path: _path + ".contract",
|
|
127
|
+
expected: "\"idlekit.experiment.v1\"",
|
|
128
|
+
value: input["contract"]
|
|
129
|
+
});
|
|
130
|
+
const _vp2 = (input, _path, _exceptionable = true) => "one-at-a-time-local-v1" === input["estimatorVersion"] || _report(_exceptionable, {
|
|
131
|
+
path: _path + ".estimatorVersion",
|
|
132
|
+
expected: "\"one-at-a-time-local-v1\"",
|
|
133
|
+
value: input["estimatorVersion"]
|
|
134
|
+
});
|
|
135
|
+
const _vp3 = (input, _path, _exceptionable = true) => "experiment-oat-v1" === input["algorithmVersion"] || _report(_exceptionable, {
|
|
136
|
+
path: _path + ".algorithmVersion",
|
|
137
|
+
expected: "\"experiment-oat-v1\"",
|
|
138
|
+
value: input["algorithmVersion"]
|
|
139
|
+
});
|
|
140
|
+
const _vp4 = (input, _path, _exceptionable = true) => "fixed-sweep-and-one-at-a-time" === input["method"] || _report(_exceptionable, {
|
|
141
|
+
path: _path + ".method",
|
|
142
|
+
expected: "\"fixed-sweep-and-one-at-a-time\"",
|
|
143
|
+
value: input["method"]
|
|
144
|
+
});
|
|
145
|
+
const _vp5 = (input, _path, _exceptionable = true) => false === input["globalSensitivity"] || _report(_exceptionable, {
|
|
146
|
+
path: _path + ".globalSensitivity",
|
|
147
|
+
expected: "false",
|
|
148
|
+
value: input["globalSensitivity"]
|
|
149
|
+
});
|
|
150
|
+
const _vp6 = (input, _path, _exceptionable = true) => false === input["sobol"] || _report(_exceptionable, {
|
|
151
|
+
path: _path + ".sobol",
|
|
152
|
+
expected: "false",
|
|
153
|
+
value: input["sobol"]
|
|
154
|
+
});
|
|
155
|
+
const _vp7 = (input, _path, _exceptionable = true) => ("object" === typeof input["scenario"] && null !== input["scenario"] || _report(_exceptionable, {
|
|
156
|
+
path: _path + ".scenario",
|
|
157
|
+
expected: "Readonly<{ ref: string; hash: string; }>",
|
|
158
|
+
value: input["scenario"]
|
|
159
|
+
})) && _vo1(input["scenario"], _path + ".scenario", true && _exceptionable) || _report(_exceptionable, {
|
|
160
|
+
path: _path + ".scenario",
|
|
161
|
+
expected: "Readonly<{ ref: string; hash: string; }>",
|
|
162
|
+
value: input["scenario"]
|
|
163
|
+
});
|
|
164
|
+
const _vp8 = (input, _path, _exceptionable = true) => "string" === typeof input["originalSpecHash"] || _report(_exceptionable, {
|
|
165
|
+
path: _path + ".originalSpecHash",
|
|
166
|
+
expected: "string",
|
|
167
|
+
value: input["originalSpecHash"]
|
|
168
|
+
});
|
|
169
|
+
const _vp9 = (input, _path, _exceptionable = true) => "string" === typeof input["effectiveSpecHash"] || _report(_exceptionable, {
|
|
170
|
+
path: _path + ".effectiveSpecHash",
|
|
171
|
+
expected: "string",
|
|
172
|
+
value: input["effectiveSpecHash"]
|
|
173
|
+
});
|
|
174
|
+
const _vp10 = (input, _path, _exceptionable = true) => "string" === typeof input["effectiveRunHash"] || _report(_exceptionable, {
|
|
175
|
+
path: _path + ".effectiveRunHash",
|
|
176
|
+
expected: "string",
|
|
177
|
+
value: input["effectiveRunHash"]
|
|
178
|
+
});
|
|
179
|
+
const _vp11 = (input, _path, _exceptionable = true) => ("object" === typeof input["budget"] && null !== input["budget"] || _report(_exceptionable, {
|
|
180
|
+
path: _path + ".budget",
|
|
181
|
+
expected: "Readonly<{ candidates: number; seeds: number; usedCandidates: number; usedSeeds: number; }>",
|
|
182
|
+
value: input["budget"]
|
|
183
|
+
})) && _vo2(input["budget"], _path + ".budget", true && _exceptionable) || _report(_exceptionable, {
|
|
184
|
+
path: _path + ".budget",
|
|
185
|
+
expected: "Readonly<{ candidates: number; seeds: number; usedCandidates: number; usedSeeds: number; }>",
|
|
186
|
+
value: input["budget"]
|
|
187
|
+
});
|
|
188
|
+
const _vp12 = (input, _path, _exceptionable = true) => (Array.isArray(input["tasks"]) || _report(_exceptionable, {
|
|
189
|
+
path: _path + ".tasks",
|
|
190
|
+
expected: _ve2,
|
|
191
|
+
value: input["tasks"]
|
|
192
|
+
})) && input["tasks"].map((elem, _index9) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
193
|
+
path: _path + ".tasks[" + _index9 + "]",
|
|
194
|
+
expected: _ve1,
|
|
195
|
+
value: elem
|
|
196
|
+
})) && _vo3(elem, _path + ".tasks[" + _index9 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
197
|
+
path: _path + ".tasks[" + _index9 + "]",
|
|
198
|
+
expected: _ve1,
|
|
199
|
+
value: elem
|
|
200
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
201
|
+
path: _path + ".tasks",
|
|
202
|
+
expected: _ve2,
|
|
203
|
+
value: input["tasks"]
|
|
204
|
+
});
|
|
205
|
+
const _ve1 = "Readonly<{ taskId: string; candidateId: string; logicalSeed: number; engineId: string; engineVersion: string; modelId: string; modelVersion: number; strategyId: string | null; modelParams: string; ... 7 more ...; value: string | null; }>";
|
|
206
|
+
const _ve2 = "ReadonlyArray<Readonly<{ taskId: string; candidateId: string; logicalSeed: number; engineId: string; engineVersion: string; modelId: string; modelVersion: number; strategyId: string | null; modelParams: string; ... 7 more ...; value: string | null; }>>";
|
|
207
|
+
const _vp13 = (input, _path, _exceptionable = true) => (Array.isArray(input["candidates"]) || _report(_exceptionable, {
|
|
208
|
+
path: _path + ".candidates",
|
|
209
|
+
expected: _ve4,
|
|
210
|
+
value: input["candidates"]
|
|
211
|
+
})) && input["candidates"].map((elem, _index10) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
212
|
+
path: _path + ".candidates[" + _index10 + "]",
|
|
213
|
+
expected: _ve3,
|
|
214
|
+
value: elem
|
|
215
|
+
})) && _vo4(elem, _path + ".candidates[" + _index10 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
216
|
+
path: _path + ".candidates[" + _index10 + "]",
|
|
217
|
+
expected: _ve3,
|
|
218
|
+
value: elem
|
|
219
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
220
|
+
path: _path + ".candidates",
|
|
221
|
+
expected: _ve4,
|
|
222
|
+
value: input["candidates"]
|
|
223
|
+
});
|
|
224
|
+
const _ve3 = "Readonly<{ id: string; metrics: readonly Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>[]; constraintViolations: readonly string[]; }>";
|
|
225
|
+
const _ve4 = "ReadonlyArray<Readonly<{ id: string; metrics: readonly Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>[]; constraintViolations: readonly string[]; }>>";
|
|
226
|
+
const _vp14 = (input, _path, _exceptionable = true) => (Array.isArray(input["elasticities"]) || _report(_exceptionable, {
|
|
227
|
+
path: _path + ".elasticities",
|
|
228
|
+
expected: _ve6,
|
|
229
|
+
value: input["elasticities"]
|
|
230
|
+
})) && input["elasticities"].map((elem, _index11) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
231
|
+
path: _path + ".elasticities[" + _index11 + "]",
|
|
232
|
+
expected: _ve5,
|
|
233
|
+
value: elem
|
|
234
|
+
})) && _vo6(elem, _path + ".elasticities[" + _index11 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
235
|
+
path: _path + ".elasticities[" + _index11 + "]",
|
|
236
|
+
expected: _ve5,
|
|
237
|
+
value: elem
|
|
238
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
239
|
+
path: _path + ".elasticities",
|
|
240
|
+
expected: _ve6,
|
|
241
|
+
value: input["elasticities"]
|
|
242
|
+
});
|
|
243
|
+
const _ve5 = "Readonly<{ dimensionId: string; candidateId: string; metricId: ExperimentMetricId; method: \"one-at-a-time-local\"; methodVersion: 1; globalSensitivity: false; sobol: false; status: \"observed\" | \"unavailable\"; elasticity: string | null; absoluteChange: string | null; direction: \"flat\" | ... 2 more ... | \"unavailable\";...";
|
|
244
|
+
const _ve6 = "ReadonlyArray<Readonly<{ dimensionId: string; candidateId: string; metricId: ExperimentMetricId; method: \"one-at-a-time-local\"; methodVersion: 1; globalSensitivity: false; sobol: false; status: \"observed\" | \"unavailable\"; elasticity: string | null; absoluteChange: string | null; direction: \"flat\" | ... 2 more ... | \"unavailable\";...>";
|
|
245
|
+
const _vp15 = (input, _path, _exceptionable = true) => ("object" === typeof input["pareto"] && null !== input["pareto"] || _report(_exceptionable, {
|
|
246
|
+
path: _path + ".pareto",
|
|
247
|
+
expected: "Readonly<{ sense: \"per-metric\"; nonDominated: readonly string[]; uncompared: readonly string[]; }>",
|
|
248
|
+
value: input["pareto"]
|
|
249
|
+
})) && _vo7(input["pareto"], _path + ".pareto", true && _exceptionable) || _report(_exceptionable, {
|
|
250
|
+
path: _path + ".pareto",
|
|
251
|
+
expected: "Readonly<{ sense: \"per-metric\"; nonDominated: readonly string[]; uncompared: readonly string[]; }>",
|
|
252
|
+
value: input["pareto"]
|
|
253
|
+
});
|
|
254
|
+
const _vp16 = (input, _path, _exceptionable = true) => (Array.isArray(input["warnings"]) || _report(_exceptionable, {
|
|
255
|
+
path: _path + ".warnings",
|
|
256
|
+
expected: "ReadonlyArray<string>",
|
|
257
|
+
value: input["warnings"]
|
|
258
|
+
})) && input["warnings"].map((elem, _index12) => "string" === typeof elem || _report(_exceptionable, {
|
|
259
|
+
path: _path + ".warnings[" + _index12 + "]",
|
|
260
|
+
expected: "string",
|
|
261
|
+
value: elem
|
|
262
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
263
|
+
path: _path + ".warnings",
|
|
264
|
+
expected: "ReadonlyArray<string>",
|
|
265
|
+
value: input["warnings"]
|
|
266
|
+
});
|
|
267
|
+
const _vp17 = (input, _path, _exceptionable = true) => "string" === typeof input["taskId"] || _report(_exceptionable, {
|
|
268
|
+
path: _path + ".taskId",
|
|
269
|
+
expected: "string",
|
|
270
|
+
value: input["taskId"]
|
|
271
|
+
});
|
|
272
|
+
const _vp18 = (input, _path, _exceptionable = true) => "string" === typeof input["candidateId"] || _report(_exceptionable, {
|
|
273
|
+
path: _path + ".candidateId",
|
|
274
|
+
expected: "string",
|
|
275
|
+
value: input["candidateId"]
|
|
276
|
+
});
|
|
277
|
+
const _vp19 = (input, _path, _exceptionable = true) => "number" === typeof input["logicalSeed"] || _report(_exceptionable, {
|
|
278
|
+
path: _path + ".logicalSeed",
|
|
279
|
+
expected: "number",
|
|
280
|
+
value: input["logicalSeed"]
|
|
281
|
+
});
|
|
282
|
+
const _vp20 = (input, _path, _exceptionable = true) => "string" === typeof input["engineId"] || _report(_exceptionable, {
|
|
283
|
+
path: _path + ".engineId",
|
|
284
|
+
expected: "string",
|
|
285
|
+
value: input["engineId"]
|
|
286
|
+
});
|
|
287
|
+
const _vp21 = (input, _path, _exceptionable = true) => "string" === typeof input["engineVersion"] || _report(_exceptionable, {
|
|
288
|
+
path: _path + ".engineVersion",
|
|
289
|
+
expected: "string",
|
|
290
|
+
value: input["engineVersion"]
|
|
291
|
+
});
|
|
292
|
+
const _vp22 = (input, _path, _exceptionable = true) => "string" === typeof input["modelId"] || _report(_exceptionable, {
|
|
293
|
+
path: _path + ".modelId",
|
|
294
|
+
expected: "string",
|
|
295
|
+
value: input["modelId"]
|
|
296
|
+
});
|
|
297
|
+
const _vp23 = (input, _path, _exceptionable = true) => "number" === typeof input["modelVersion"] || _report(_exceptionable, {
|
|
298
|
+
path: _path + ".modelVersion",
|
|
299
|
+
expected: "number",
|
|
300
|
+
value: input["modelVersion"]
|
|
301
|
+
});
|
|
302
|
+
const _vp24 = (input, _path, _exceptionable = true) => null === input["strategyId"] || "string" === typeof input["strategyId"] || _report(_exceptionable, {
|
|
303
|
+
path: _path + ".strategyId",
|
|
304
|
+
expected: "(null | string)",
|
|
305
|
+
value: input["strategyId"]
|
|
306
|
+
});
|
|
307
|
+
const _vp25 = (input, _path, _exceptionable = true) => "string" === typeof input["modelParams"] || _report(_exceptionable, {
|
|
308
|
+
path: _path + ".modelParams",
|
|
309
|
+
expected: "string",
|
|
310
|
+
value: input["modelParams"]
|
|
311
|
+
});
|
|
312
|
+
const _vp26 = (input, _path, _exceptionable = true) => "string" === typeof input["strategyParams"] || _report(_exceptionable, {
|
|
313
|
+
path: _path + ".strategyParams",
|
|
314
|
+
expected: "string",
|
|
315
|
+
value: input["strategyParams"]
|
|
316
|
+
});
|
|
317
|
+
const _vp27 = (input, _path, _exceptionable = true) => "string" === typeof input["sessionPattern"] || _report(_exceptionable, {
|
|
318
|
+
path: _path + ".sessionPattern",
|
|
319
|
+
expected: "string",
|
|
320
|
+
value: input["sessionPattern"]
|
|
321
|
+
});
|
|
322
|
+
const _vp28 = (input, _path, _exceptionable = true) => "number" === typeof input["sessionDays"] || _report(_exceptionable, {
|
|
323
|
+
path: _path + ".sessionDays",
|
|
324
|
+
expected: "number",
|
|
325
|
+
value: input["sessionDays"]
|
|
326
|
+
});
|
|
327
|
+
const _vp29 = (input, _path, _exceptionable = true) => "number" === typeof input["horizonSec"] || _report(_exceptionable, {
|
|
328
|
+
path: _path + ".horizonSec",
|
|
329
|
+
expected: "number",
|
|
330
|
+
value: input["horizonSec"]
|
|
331
|
+
});
|
|
332
|
+
const _vp30 = (input, _path, _exceptionable = true) => "number" === typeof input["stepSec"] || _report(_exceptionable, {
|
|
333
|
+
path: _path + ".stepSec",
|
|
334
|
+
expected: "number",
|
|
335
|
+
value: input["stepSec"]
|
|
336
|
+
});
|
|
337
|
+
const _vp31 = (input, _path, _exceptionable = true) => "endMoney" === input["metricId"] || "timeToMoney" === input["metricId"] || "varBenefit" === input["metricId"] || _report(_exceptionable, {
|
|
338
|
+
path: _path + ".metricId",
|
|
339
|
+
expected: "(\"endMoney\" | \"timeToMoney\" | \"varBenefit\")",
|
|
340
|
+
value: input["metricId"]
|
|
341
|
+
});
|
|
342
|
+
const _vp32 = (input, _path, _exceptionable = true) => "censored" === input["status"] || "failed" === input["status"] || "observed" === input["status"] || "rejected" === input["status"] || _report(_exceptionable, {
|
|
343
|
+
path: _path + ".status",
|
|
344
|
+
expected: "(\"censored\" | \"failed\" | \"observed\" | \"rejected\")",
|
|
345
|
+
value: input["status"]
|
|
346
|
+
});
|
|
347
|
+
const _vp33 = (input, _path, _exceptionable = true) => null === input["value"] || "string" === typeof input["value"] || _report(_exceptionable, {
|
|
348
|
+
path: _path + ".value",
|
|
349
|
+
expected: "(null | string)",
|
|
350
|
+
value: input["value"]
|
|
351
|
+
});
|
|
352
|
+
const _io0 = input => _ip0(input) && _ip1(input) && _ip2(input) && _ip3(input) && _ip4(input) && _ip5(input) && _ip6(input) && _ip7(input) && _ip8(input) && _ip9(input) && _ip10(input) && _ip11(input) && _ip12(input) && _ip13(input) && _ip14(input) && _ip15(input) && _ip16(input);
|
|
353
|
+
const _io1 = input => "string" === typeof input.ref && "string" === typeof input.hash;
|
|
354
|
+
const _io2 = input => "number" === typeof input.candidates && "number" === typeof input.seeds && "number" === typeof input.usedCandidates && "number" === typeof input.usedSeeds;
|
|
355
|
+
const _io3 = input => _ip17(input) && _ip18(input) && _ip19(input) && _ip20(input) && _ip21(input) && _ip22(input) && _ip23(input) && _ip24(input) && _ip25(input) && _ip26(input) && _ip27(input) && _ip28(input) && _ip29(input) && _ip30(input) && _ip31(input) && _ip32(input) && _ip33(input);
|
|
356
|
+
const _io4 = input => "string" === typeof input.id && (Array.isArray(input.metrics) && input.metrics.every(elem => "object" === typeof elem && null !== elem && _io5(elem))) && (Array.isArray(input.constraintViolations) && input.constraintViolations.every(elem => "string" === typeof elem));
|
|
357
|
+
const _io5 = input => _ip31(input) && _ip32(input) && _ip33(input);
|
|
358
|
+
const _io6 = input => "string" === typeof input.dimensionId && _ip18(input) && _ip31(input) && "one-at-a-time-local" === input.method && 1 === input.methodVersion && _ip5(input) && _ip6(input) && ("observed" === input.status || "unavailable" === input.status) && (null === input.elasticity || "string" === typeof input.elasticity) && (null === input.absoluteChange || "string" === typeof input.absoluteChange) && ("flat" === input.direction || "higher" === input.direction || "lower" === input.direction || "unavailable" === input.direction);
|
|
359
|
+
const _io7 = input => "per-metric" === input.sense && (Array.isArray(input.nonDominated) && input.nonDominated.every(elem => "string" === typeof elem)) && (Array.isArray(input.uncompared) && input.uncompared.every(elem => "string" === typeof elem));
|
|
360
|
+
const _vo0 = (input, _path, _exceptionable = true) => [_vp0(input, _path, true && _exceptionable), _vp1(input, _path, true && _exceptionable), _vp2(input, _path, true && _exceptionable), _vp3(input, _path, true && _exceptionable), _vp4(input, _path, true && _exceptionable), _vp5(input, _path, true && _exceptionable), _vp6(input, _path, true && _exceptionable), _vp7(input, _path, true && _exceptionable), _vp8(input, _path, true && _exceptionable), _vp9(input, _path, true && _exceptionable), _vp10(input, _path, true && _exceptionable), _vp11(input, _path, true && _exceptionable), _vp12(input, _path, true && _exceptionable), _vp13(input, _path, true && _exceptionable), _vp14(input, _path, true && _exceptionable), _vp15(input, _path, true && _exceptionable), _vp16(input, _path, true && _exceptionable)].every(flag => flag);
|
|
361
|
+
const _vo1 = (input, _path, _exceptionable = true) => ["string" === typeof input.ref || _report(_exceptionable, {
|
|
362
|
+
path: _path + ".ref",
|
|
363
|
+
expected: "string",
|
|
364
|
+
value: input.ref
|
|
365
|
+
}), "string" === typeof input.hash || _report(_exceptionable, {
|
|
366
|
+
path: _path + ".hash",
|
|
367
|
+
expected: "string",
|
|
368
|
+
value: input.hash
|
|
369
|
+
})].every(flag => flag);
|
|
370
|
+
const _vo2 = (input, _path, _exceptionable = true) => ["number" === typeof input.candidates || _report(_exceptionable, {
|
|
371
|
+
path: _path + ".candidates",
|
|
372
|
+
expected: "number",
|
|
373
|
+
value: input.candidates
|
|
374
|
+
}), "number" === typeof input.seeds || _report(_exceptionable, {
|
|
375
|
+
path: _path + ".seeds",
|
|
376
|
+
expected: "number",
|
|
377
|
+
value: input.seeds
|
|
378
|
+
}), "number" === typeof input.usedCandidates || _report(_exceptionable, {
|
|
379
|
+
path: _path + ".usedCandidates",
|
|
380
|
+
expected: "number",
|
|
381
|
+
value: input.usedCandidates
|
|
382
|
+
}), "number" === typeof input.usedSeeds || _report(_exceptionable, {
|
|
383
|
+
path: _path + ".usedSeeds",
|
|
384
|
+
expected: "number",
|
|
385
|
+
value: input.usedSeeds
|
|
386
|
+
})].every(flag => flag);
|
|
387
|
+
const _vo3 = (input, _path, _exceptionable = true) => [_vp17(input, _path, true && _exceptionable), _vp18(input, _path, true && _exceptionable), _vp19(input, _path, true && _exceptionable), _vp20(input, _path, true && _exceptionable), _vp21(input, _path, true && _exceptionable), _vp22(input, _path, true && _exceptionable), _vp23(input, _path, true && _exceptionable), _vp24(input, _path, true && _exceptionable), _vp25(input, _path, true && _exceptionable), _vp26(input, _path, true && _exceptionable), _vp27(input, _path, true && _exceptionable), _vp28(input, _path, true && _exceptionable), _vp29(input, _path, true && _exceptionable), _vp30(input, _path, true && _exceptionable), _vp31(input, _path, true && _exceptionable), _vp32(input, _path, true && _exceptionable), _vp33(input, _path, true && _exceptionable)].every(flag => flag);
|
|
388
|
+
const _vo4 = (input, _path, _exceptionable = true) => ["string" === typeof input.id || _report(_exceptionable, {
|
|
389
|
+
path: _path + ".id",
|
|
390
|
+
expected: "string",
|
|
391
|
+
value: input.id
|
|
392
|
+
}), (Array.isArray(input.metrics) || _report(_exceptionable, {
|
|
393
|
+
path: _path + ".metrics",
|
|
394
|
+
expected: "ReadonlyArray<Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>>",
|
|
395
|
+
value: input.metrics
|
|
396
|
+
})) && input.metrics.map((elem, _index13) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
397
|
+
path: _path + ".metrics[" + _index13 + "]",
|
|
398
|
+
expected: "Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>",
|
|
399
|
+
value: elem
|
|
400
|
+
})) && _vo5(elem, _path + ".metrics[" + _index13 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
401
|
+
path: _path + ".metrics[" + _index13 + "]",
|
|
402
|
+
expected: "Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>",
|
|
403
|
+
value: elem
|
|
404
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
405
|
+
path: _path + ".metrics",
|
|
406
|
+
expected: "ReadonlyArray<Readonly<{ metricId: ExperimentMetricId; status: ExperimentMetricStatus; value: string | null; }>>",
|
|
407
|
+
value: input.metrics
|
|
408
|
+
}), (Array.isArray(input.constraintViolations) || _report(_exceptionable, {
|
|
409
|
+
path: _path + ".constraintViolations",
|
|
410
|
+
expected: "ReadonlyArray<string>",
|
|
411
|
+
value: input.constraintViolations
|
|
412
|
+
})) && input.constraintViolations.map((elem, _index14) => "string" === typeof elem || _report(_exceptionable, {
|
|
413
|
+
path: _path + ".constraintViolations[" + _index14 + "]",
|
|
414
|
+
expected: "string",
|
|
415
|
+
value: elem
|
|
416
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
417
|
+
path: _path + ".constraintViolations",
|
|
418
|
+
expected: "ReadonlyArray<string>",
|
|
419
|
+
value: input.constraintViolations
|
|
420
|
+
})].every(flag => flag);
|
|
421
|
+
const _vo5 = (input, _path, _exceptionable = true) => [_vp31(input, _path, true && _exceptionable), _vp32(input, _path, true && _exceptionable), _vp33(input, _path, true && _exceptionable)].every(flag => flag);
|
|
422
|
+
const _vo6 = (input, _path, _exceptionable = true) => ["string" === typeof input.dimensionId || _report(_exceptionable, {
|
|
423
|
+
path: _path + ".dimensionId",
|
|
424
|
+
expected: "string",
|
|
425
|
+
value: input.dimensionId
|
|
426
|
+
}), _vp18(input, _path, true && _exceptionable), _vp31(input, _path, true && _exceptionable), "one-at-a-time-local" === input.method || _report(_exceptionable, {
|
|
427
|
+
path: _path + ".method",
|
|
428
|
+
expected: "\"one-at-a-time-local\"",
|
|
429
|
+
value: input.method
|
|
430
|
+
}), 1 === input.methodVersion || _report(_exceptionable, {
|
|
431
|
+
path: _path + ".methodVersion",
|
|
432
|
+
expected: "1",
|
|
433
|
+
value: input.methodVersion
|
|
434
|
+
}), _vp5(input, _path, true && _exceptionable), _vp6(input, _path, true && _exceptionable), "observed" === input.status || "unavailable" === input.status || _report(_exceptionable, {
|
|
435
|
+
path: _path + ".status",
|
|
436
|
+
expected: "(\"observed\" | \"unavailable\")",
|
|
437
|
+
value: input.status
|
|
438
|
+
}), null === input.elasticity || "string" === typeof input.elasticity || _report(_exceptionable, {
|
|
439
|
+
path: _path + ".elasticity",
|
|
440
|
+
expected: "(null | string)",
|
|
441
|
+
value: input.elasticity
|
|
442
|
+
}), null === input.absoluteChange || "string" === typeof input.absoluteChange || _report(_exceptionable, {
|
|
443
|
+
path: _path + ".absoluteChange",
|
|
444
|
+
expected: "(null | string)",
|
|
445
|
+
value: input.absoluteChange
|
|
446
|
+
}), "flat" === input.direction || "higher" === input.direction || "lower" === input.direction || "unavailable" === input.direction || _report(_exceptionable, {
|
|
447
|
+
path: _path + ".direction",
|
|
448
|
+
expected: "(\"flat\" | \"higher\" | \"lower\" | \"unavailable\")",
|
|
449
|
+
value: input.direction
|
|
450
|
+
})].every(flag => flag);
|
|
451
|
+
const _vo7 = (input, _path, _exceptionable = true) => ["per-metric" === input.sense || _report(_exceptionable, {
|
|
452
|
+
path: _path + ".sense",
|
|
453
|
+
expected: "\"per-metric\"",
|
|
454
|
+
value: input.sense
|
|
455
|
+
}), (Array.isArray(input.nonDominated) || _report(_exceptionable, {
|
|
456
|
+
path: _path + ".nonDominated",
|
|
457
|
+
expected: "ReadonlyArray<string>",
|
|
458
|
+
value: input.nonDominated
|
|
459
|
+
})) && input.nonDominated.map((elem, _index15) => "string" === typeof elem || _report(_exceptionable, {
|
|
460
|
+
path: _path + ".nonDominated[" + _index15 + "]",
|
|
461
|
+
expected: "string",
|
|
462
|
+
value: elem
|
|
463
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
464
|
+
path: _path + ".nonDominated",
|
|
465
|
+
expected: "ReadonlyArray<string>",
|
|
466
|
+
value: input.nonDominated
|
|
467
|
+
}), (Array.isArray(input.uncompared) || _report(_exceptionable, {
|
|
468
|
+
path: _path + ".uncompared",
|
|
469
|
+
expected: "ReadonlyArray<string>",
|
|
470
|
+
value: input.uncompared
|
|
471
|
+
})) && input.uncompared.map((elem, _index16) => "string" === typeof elem || _report(_exceptionable, {
|
|
472
|
+
path: _path + ".uncompared[" + _index16 + "]",
|
|
473
|
+
expected: "string",
|
|
474
|
+
value: elem
|
|
475
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
476
|
+
path: _path + ".uncompared",
|
|
477
|
+
expected: "ReadonlyArray<string>",
|
|
478
|
+
value: input.uncompared
|
|
479
|
+
})].every(flag => flag);
|
|
480
|
+
const __is = input => "object" === typeof input && null !== input && _io0(input);
|
|
481
|
+
let errors;
|
|
482
|
+
let _report;
|
|
483
|
+
return _createStandardSchema_1._createStandardSchema(input => {
|
|
484
|
+
if (false === __is(input)) {
|
|
485
|
+
errors = [];
|
|
486
|
+
_report = _validateReport_1._validateReport(errors);
|
|
487
|
+
((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || _report(true, {
|
|
488
|
+
path: _path + "",
|
|
489
|
+
expected: _ve0,
|
|
490
|
+
value: input
|
|
491
|
+
})) && _vo0(input, _path + "", true) || _report(true, {
|
|
492
|
+
path: _path + "",
|
|
493
|
+
expected: _ve0,
|
|
494
|
+
value: input
|
|
495
|
+
}))(input, "$input", true);
|
|
496
|
+
const success = 0 === errors.length;
|
|
497
|
+
return success ? {
|
|
498
|
+
success,
|
|
499
|
+
data: input
|
|
500
|
+
} : {
|
|
501
|
+
success,
|
|
502
|
+
errors,
|
|
503
|
+
data: input
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
success: true,
|
|
508
|
+
data: input
|
|
509
|
+
};
|
|
510
|
+
});
|
|
511
|
+
})())(input);
|
|
512
|
+
function buildExperimentJsonSchema() {
|
|
513
|
+
return {
|
|
514
|
+
components: {
|
|
515
|
+
schemas: {
|
|
516
|
+
"ReadonlyschemaVersion1contractidlekit_x2E_experiment_x2E_v1estimatorVersionone-at-a-time-local-v1algorithmVersionexperiment-oat-v1methodfixed-sweep-and-one-at-a-time---11more---warningsreadonlystring-x86544DB8D4D46D5D": {
|
|
517
|
+
type: "object",
|
|
518
|
+
properties: {
|
|
519
|
+
schemaVersion: {
|
|
520
|
+
"const": 1
|
|
521
|
+
},
|
|
522
|
+
contract: {
|
|
523
|
+
"const": "idlekit.experiment.v1"
|
|
524
|
+
},
|
|
525
|
+
estimatorVersion: {
|
|
526
|
+
"const": "one-at-a-time-local-v1"
|
|
527
|
+
},
|
|
528
|
+
algorithmVersion: {
|
|
529
|
+
"const": "experiment-oat-v1"
|
|
530
|
+
},
|
|
531
|
+
method: {
|
|
532
|
+
"const": "fixed-sweep-and-one-at-a-time"
|
|
533
|
+
},
|
|
534
|
+
globalSensitivity: {
|
|
535
|
+
"const": false
|
|
536
|
+
},
|
|
537
|
+
sobol: {
|
|
538
|
+
"const": false
|
|
539
|
+
},
|
|
540
|
+
scenario: {
|
|
541
|
+
$ref: "#/components/schemas/Readonlyrefstringhashstring"
|
|
542
|
+
},
|
|
543
|
+
originalSpecHash: {
|
|
544
|
+
type: "string"
|
|
545
|
+
},
|
|
546
|
+
effectiveSpecHash: {
|
|
547
|
+
type: "string"
|
|
548
|
+
},
|
|
549
|
+
effectiveRunHash: {
|
|
550
|
+
type: "string"
|
|
551
|
+
},
|
|
552
|
+
budget: {
|
|
553
|
+
$ref: "#/components/schemas/ReadonlycandidatesnumberseedsnumberusedCandidatesnumberusedSeedsnumber"
|
|
554
|
+
},
|
|
555
|
+
tasks: {
|
|
556
|
+
type: "array",
|
|
557
|
+
items: {
|
|
558
|
+
$ref: "#/components/schemas/ReadonlytaskIdstringcandidateIdstringlogicalSeednumberengineIdstringengineVersionstringmodelIdstringmodelVersionnumberstrategyIdstringnullmodelParamsstring---7more---valuestringnull"
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
candidates: {
|
|
562
|
+
type: "array",
|
|
563
|
+
items: {
|
|
564
|
+
$ref: "#/components/schemas/ReadonlyidstringmetricsreadonlyReadonlymetricIdExperimentMetricIdstatusExperimentMetricStatusvaluestringnullconstraintViolationsreadonlystring"
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
elasticities: {
|
|
568
|
+
type: "array",
|
|
569
|
+
items: {
|
|
570
|
+
$ref: "#/components/schemas/ReadonlydimensionIdstringcandidateIdstringmetricIdExperimentMetricIdmethodone-at-a-time-localmethodVersion1globalSensitivityfalsesobolfalsestatusobservedunavailableelasticitystringnullabsoluteChangestringnulldirectionflat---2more---unavailable---"
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
pareto: {
|
|
574
|
+
$ref: "#/components/schemas/Readonlysenseper-metricnonDominatedreadonlystringuncomparedreadonlystring"
|
|
575
|
+
},
|
|
576
|
+
warnings: {
|
|
577
|
+
type: "array",
|
|
578
|
+
items: {
|
|
579
|
+
type: "string"
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
required: [
|
|
584
|
+
"schemaVersion",
|
|
585
|
+
"contract",
|
|
586
|
+
"estimatorVersion",
|
|
587
|
+
"algorithmVersion",
|
|
588
|
+
"method",
|
|
589
|
+
"globalSensitivity",
|
|
590
|
+
"sobol",
|
|
591
|
+
"scenario",
|
|
592
|
+
"originalSpecHash",
|
|
593
|
+
"effectiveSpecHash",
|
|
594
|
+
"effectiveRunHash",
|
|
595
|
+
"budget",
|
|
596
|
+
"tasks",
|
|
597
|
+
"candidates",
|
|
598
|
+
"elasticities",
|
|
599
|
+
"pareto",
|
|
600
|
+
"warnings"
|
|
601
|
+
],
|
|
602
|
+
additionalProperties: false
|
|
603
|
+
},
|
|
604
|
+
Readonlyrefstringhashstring: {
|
|
605
|
+
type: "object",
|
|
606
|
+
properties: {
|
|
607
|
+
ref: {
|
|
608
|
+
type: "string"
|
|
609
|
+
},
|
|
610
|
+
hash: {
|
|
611
|
+
type: "string"
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
required: [
|
|
615
|
+
"ref",
|
|
616
|
+
"hash"
|
|
617
|
+
],
|
|
618
|
+
additionalProperties: false
|
|
619
|
+
},
|
|
620
|
+
ReadonlycandidatesnumberseedsnumberusedCandidatesnumberusedSeedsnumber: {
|
|
621
|
+
type: "object",
|
|
622
|
+
properties: {
|
|
623
|
+
candidates: {
|
|
624
|
+
type: "number"
|
|
625
|
+
},
|
|
626
|
+
seeds: {
|
|
627
|
+
type: "number"
|
|
628
|
+
},
|
|
629
|
+
usedCandidates: {
|
|
630
|
+
type: "number"
|
|
631
|
+
},
|
|
632
|
+
usedSeeds: {
|
|
633
|
+
type: "number"
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
required: [
|
|
637
|
+
"candidates",
|
|
638
|
+
"seeds",
|
|
639
|
+
"usedCandidates",
|
|
640
|
+
"usedSeeds"
|
|
641
|
+
],
|
|
642
|
+
additionalProperties: false
|
|
643
|
+
},
|
|
644
|
+
"ReadonlytaskIdstringcandidateIdstringlogicalSeednumberengineIdstringengineVersionstringmodelIdstringmodelVersionnumberstrategyIdstringnullmodelParamsstring---7more---valuestringnull": {
|
|
645
|
+
type: "object",
|
|
646
|
+
properties: {
|
|
647
|
+
taskId: {
|
|
648
|
+
type: "string"
|
|
649
|
+
},
|
|
650
|
+
candidateId: {
|
|
651
|
+
type: "string"
|
|
652
|
+
},
|
|
653
|
+
logicalSeed: {
|
|
654
|
+
type: "number"
|
|
655
|
+
},
|
|
656
|
+
engineId: {
|
|
657
|
+
type: "string"
|
|
658
|
+
},
|
|
659
|
+
engineVersion: {
|
|
660
|
+
type: "string"
|
|
661
|
+
},
|
|
662
|
+
modelId: {
|
|
663
|
+
type: "string"
|
|
664
|
+
},
|
|
665
|
+
modelVersion: {
|
|
666
|
+
type: "number"
|
|
667
|
+
},
|
|
668
|
+
strategyId: {
|
|
669
|
+
oneOf: [
|
|
670
|
+
{
|
|
671
|
+
type: "null"
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
type: "string"
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
},
|
|
678
|
+
modelParams: {
|
|
679
|
+
type: "string"
|
|
680
|
+
},
|
|
681
|
+
strategyParams: {
|
|
682
|
+
type: "string"
|
|
683
|
+
},
|
|
684
|
+
sessionPattern: {
|
|
685
|
+
type: "string"
|
|
686
|
+
},
|
|
687
|
+
sessionDays: {
|
|
688
|
+
type: "number"
|
|
689
|
+
},
|
|
690
|
+
horizonSec: {
|
|
691
|
+
type: "number"
|
|
692
|
+
},
|
|
693
|
+
stepSec: {
|
|
694
|
+
type: "number"
|
|
695
|
+
},
|
|
696
|
+
metricId: {
|
|
697
|
+
$ref: "#/components/schemas/ExperimentMetricId"
|
|
698
|
+
},
|
|
699
|
+
status: {
|
|
700
|
+
$ref: "#/components/schemas/ExperimentMetricStatus"
|
|
701
|
+
},
|
|
702
|
+
value: {
|
|
703
|
+
oneOf: [
|
|
704
|
+
{
|
|
705
|
+
type: "null"
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
type: "string"
|
|
709
|
+
}
|
|
710
|
+
]
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
required: [
|
|
714
|
+
"taskId",
|
|
715
|
+
"candidateId",
|
|
716
|
+
"logicalSeed",
|
|
717
|
+
"engineId",
|
|
718
|
+
"engineVersion",
|
|
719
|
+
"modelId",
|
|
720
|
+
"modelVersion",
|
|
721
|
+
"strategyId",
|
|
722
|
+
"modelParams",
|
|
723
|
+
"strategyParams",
|
|
724
|
+
"sessionPattern",
|
|
725
|
+
"sessionDays",
|
|
726
|
+
"horizonSec",
|
|
727
|
+
"stepSec",
|
|
728
|
+
"metricId",
|
|
729
|
+
"status",
|
|
730
|
+
"value"
|
|
731
|
+
],
|
|
732
|
+
additionalProperties: false
|
|
733
|
+
},
|
|
734
|
+
ExperimentMetricId: {
|
|
735
|
+
oneOf: [
|
|
736
|
+
{
|
|
737
|
+
"const": "endMoney"
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
"const": "timeToMoney"
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
"const": "varBenefit"
|
|
744
|
+
}
|
|
745
|
+
]
|
|
746
|
+
},
|
|
747
|
+
ExperimentMetricStatus: {
|
|
748
|
+
oneOf: [
|
|
749
|
+
{
|
|
750
|
+
"const": "censored"
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
"const": "failed"
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"const": "observed"
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
"const": "rejected"
|
|
760
|
+
}
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
ReadonlyidstringmetricsreadonlyReadonlymetricIdExperimentMetricIdstatusExperimentMetricStatusvaluestringnullconstraintViolationsreadonlystring: {
|
|
764
|
+
type: "object",
|
|
765
|
+
properties: {
|
|
766
|
+
id: {
|
|
767
|
+
type: "string"
|
|
768
|
+
},
|
|
769
|
+
metrics: {
|
|
770
|
+
type: "array",
|
|
771
|
+
items: {
|
|
772
|
+
$ref: "#/components/schemas/ReadonlymetricIdExperimentMetricIdstatusExperimentMetricStatusvaluestringnull"
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
constraintViolations: {
|
|
776
|
+
type: "array",
|
|
777
|
+
items: {
|
|
778
|
+
type: "string"
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
required: [
|
|
783
|
+
"id",
|
|
784
|
+
"metrics",
|
|
785
|
+
"constraintViolations"
|
|
786
|
+
],
|
|
787
|
+
additionalProperties: false
|
|
788
|
+
},
|
|
789
|
+
ReadonlymetricIdExperimentMetricIdstatusExperimentMetricStatusvaluestringnull: {
|
|
790
|
+
type: "object",
|
|
791
|
+
properties: {
|
|
792
|
+
metricId: {
|
|
793
|
+
$ref: "#/components/schemas/ExperimentMetricId"
|
|
794
|
+
},
|
|
795
|
+
status: {
|
|
796
|
+
$ref: "#/components/schemas/ExperimentMetricStatus"
|
|
797
|
+
},
|
|
798
|
+
value: {
|
|
799
|
+
oneOf: [
|
|
800
|
+
{
|
|
801
|
+
type: "null"
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
type: "string"
|
|
805
|
+
}
|
|
806
|
+
]
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
required: [
|
|
810
|
+
"metricId",
|
|
811
|
+
"status",
|
|
812
|
+
"value"
|
|
813
|
+
],
|
|
814
|
+
additionalProperties: false
|
|
815
|
+
},
|
|
816
|
+
"ReadonlydimensionIdstringcandidateIdstringmetricIdExperimentMetricIdmethodone-at-a-time-localmethodVersion1globalSensitivityfalsesobolfalsestatusobservedunavailableelasticitystringnullabsoluteChangestringnulldirectionflat---2more---unavailable---": {
|
|
817
|
+
type: "object",
|
|
818
|
+
properties: {
|
|
819
|
+
dimensionId: {
|
|
820
|
+
type: "string"
|
|
821
|
+
},
|
|
822
|
+
candidateId: {
|
|
823
|
+
type: "string"
|
|
824
|
+
},
|
|
825
|
+
metricId: {
|
|
826
|
+
$ref: "#/components/schemas/ExperimentMetricId"
|
|
827
|
+
},
|
|
828
|
+
method: {
|
|
829
|
+
"const": "one-at-a-time-local"
|
|
830
|
+
},
|
|
831
|
+
methodVersion: {
|
|
832
|
+
"const": 1
|
|
833
|
+
},
|
|
834
|
+
globalSensitivity: {
|
|
835
|
+
"const": false
|
|
836
|
+
},
|
|
837
|
+
sobol: {
|
|
838
|
+
"const": false
|
|
839
|
+
},
|
|
840
|
+
status: {
|
|
841
|
+
oneOf: [
|
|
842
|
+
{
|
|
843
|
+
"const": "observed"
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
"const": "unavailable"
|
|
847
|
+
}
|
|
848
|
+
]
|
|
849
|
+
},
|
|
850
|
+
elasticity: {
|
|
851
|
+
oneOf: [
|
|
852
|
+
{
|
|
853
|
+
type: "null"
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
type: "string"
|
|
857
|
+
}
|
|
858
|
+
]
|
|
859
|
+
},
|
|
860
|
+
absoluteChange: {
|
|
861
|
+
oneOf: [
|
|
862
|
+
{
|
|
863
|
+
type: "null"
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
type: "string"
|
|
867
|
+
}
|
|
868
|
+
]
|
|
869
|
+
},
|
|
870
|
+
direction: {
|
|
871
|
+
oneOf: [
|
|
872
|
+
{
|
|
873
|
+
"const": "flat"
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
"const": "higher"
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
"const": "lower"
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
"const": "unavailable"
|
|
883
|
+
}
|
|
884
|
+
]
|
|
885
|
+
}
|
|
886
|
+
},
|
|
887
|
+
required: [
|
|
888
|
+
"dimensionId",
|
|
889
|
+
"candidateId",
|
|
890
|
+
"metricId",
|
|
891
|
+
"method",
|
|
892
|
+
"methodVersion",
|
|
893
|
+
"globalSensitivity",
|
|
894
|
+
"sobol",
|
|
895
|
+
"status",
|
|
896
|
+
"elasticity",
|
|
897
|
+
"absoluteChange",
|
|
898
|
+
"direction"
|
|
899
|
+
],
|
|
900
|
+
additionalProperties: false
|
|
901
|
+
},
|
|
902
|
+
"Readonlysenseper-metricnonDominatedreadonlystringuncomparedreadonlystring": {
|
|
903
|
+
type: "object",
|
|
904
|
+
properties: {
|
|
905
|
+
sense: {
|
|
906
|
+
"const": "per-metric"
|
|
907
|
+
},
|
|
908
|
+
nonDominated: {
|
|
909
|
+
type: "array",
|
|
910
|
+
items: {
|
|
911
|
+
type: "string"
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
uncompared: {
|
|
915
|
+
type: "array",
|
|
916
|
+
items: {
|
|
917
|
+
type: "string"
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
required: [
|
|
922
|
+
"sense",
|
|
923
|
+
"nonDominated",
|
|
924
|
+
"uncompared"
|
|
925
|
+
],
|
|
926
|
+
additionalProperties: false
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
schema: {
|
|
931
|
+
type: "object",
|
|
932
|
+
properties: {
|
|
933
|
+
schemaVersion: {
|
|
934
|
+
"const": 1
|
|
935
|
+
},
|
|
936
|
+
contract: {
|
|
937
|
+
"const": "idlekit.experiment.v1"
|
|
938
|
+
},
|
|
939
|
+
estimatorVersion: {
|
|
940
|
+
"const": "one-at-a-time-local-v1"
|
|
941
|
+
},
|
|
942
|
+
algorithmVersion: {
|
|
943
|
+
"const": "experiment-oat-v1"
|
|
944
|
+
},
|
|
945
|
+
method: {
|
|
946
|
+
"const": "fixed-sweep-and-one-at-a-time"
|
|
947
|
+
},
|
|
948
|
+
globalSensitivity: {
|
|
949
|
+
"const": false
|
|
950
|
+
},
|
|
951
|
+
sobol: {
|
|
952
|
+
"const": false
|
|
953
|
+
},
|
|
954
|
+
scenario: {
|
|
955
|
+
$ref: "#/components/schemas/Readonlyrefstringhashstring"
|
|
956
|
+
},
|
|
957
|
+
originalSpecHash: {
|
|
958
|
+
type: "string"
|
|
959
|
+
},
|
|
960
|
+
effectiveSpecHash: {
|
|
961
|
+
type: "string"
|
|
962
|
+
},
|
|
963
|
+
effectiveRunHash: {
|
|
964
|
+
type: "string"
|
|
965
|
+
},
|
|
966
|
+
budget: {
|
|
967
|
+
$ref: "#/components/schemas/ReadonlycandidatesnumberseedsnumberusedCandidatesnumberusedSeedsnumber"
|
|
968
|
+
},
|
|
969
|
+
tasks: {
|
|
970
|
+
type: "array",
|
|
971
|
+
items: {
|
|
972
|
+
$ref: "#/components/schemas/ReadonlytaskIdstringcandidateIdstringlogicalSeednumberengineIdstringengineVersionstringmodelIdstringmodelVersionnumberstrategyIdstringnullmodelParamsstring---7more---valuestringnull"
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
candidates: {
|
|
976
|
+
type: "array",
|
|
977
|
+
items: {
|
|
978
|
+
$ref: "#/components/schemas/ReadonlyidstringmetricsreadonlyReadonlymetricIdExperimentMetricIdstatusExperimentMetricStatusvaluestringnullconstraintViolationsreadonlystring"
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
elasticities: {
|
|
982
|
+
type: "array",
|
|
983
|
+
items: {
|
|
984
|
+
$ref: "#/components/schemas/ReadonlydimensionIdstringcandidateIdstringmetricIdExperimentMetricIdmethodone-at-a-time-localmethodVersion1globalSensitivityfalsesobolfalsestatusobservedunavailableelasticitystringnullabsoluteChangestringnulldirectionflat---2more---unavailable---"
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
pareto: {
|
|
988
|
+
$ref: "#/components/schemas/Readonlysenseper-metricnonDominatedreadonlystringuncomparedreadonlystring"
|
|
989
|
+
},
|
|
990
|
+
warnings: {
|
|
991
|
+
type: "array",
|
|
992
|
+
items: {
|
|
993
|
+
type: "string"
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
required: [
|
|
998
|
+
"schemaVersion",
|
|
999
|
+
"contract",
|
|
1000
|
+
"estimatorVersion",
|
|
1001
|
+
"algorithmVersion",
|
|
1002
|
+
"method",
|
|
1003
|
+
"globalSensitivity",
|
|
1004
|
+
"sobol",
|
|
1005
|
+
"scenario",
|
|
1006
|
+
"originalSpecHash",
|
|
1007
|
+
"effectiveSpecHash",
|
|
1008
|
+
"effectiveRunHash",
|
|
1009
|
+
"budget",
|
|
1010
|
+
"tasks",
|
|
1011
|
+
"candidates",
|
|
1012
|
+
"elasticities",
|
|
1013
|
+
"pareto",
|
|
1014
|
+
"warnings"
|
|
1015
|
+
],
|
|
1016
|
+
additionalProperties: false
|
|
1017
|
+
},
|
|
1018
|
+
version: "3.1"
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
let experimentJsonSchemaUnit;
|
|
1022
|
+
/**
|
|
1023
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-tune-signature The structural schema is OpenAPI 3.1 from typia.
|
|
1024
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-tune-signature #365035c Re-read the section: schema version is 3.1 and the contract id is idlekit.experiment.v1.
|
|
1025
|
+
*/
|
|
1026
|
+
export function experimentJsonSchema() {
|
|
1027
|
+
experimentJsonSchemaUnit ??= buildExperimentJsonSchema();
|
|
1028
|
+
return experimentJsonSchemaUnit;
|
|
1029
|
+
}
|
|
1030
|
+
let validateExperimentSpecValidator;
|
|
1031
|
+
export const validateExperimentSpec = (input) => (validateExperimentSpecValidator ??= (() => {
|
|
1032
|
+
const _ip0 = input => "endMoney" === input["metricId"] || "timeToMoney" === input["metricId"] || "varBenefit" === input["metricId"];
|
|
1033
|
+
const _ip1 = input => "string" === typeof input["id"];
|
|
1034
|
+
const _ip2 = input => "number" === typeof input["min"];
|
|
1035
|
+
const _ip3 = input => "number" === typeof input["max"];
|
|
1036
|
+
const _ve0 = "Readonly<{ schemaVersion: 1; scenario: Readonly<{ ref: string; hash: string; }>; model: Readonly<{ id: string; version: number; }>; strategy?: Readonly<{ id: string; }> | undefined; ... 10 more ...; session: Readonly<...>; }>";
|
|
1037
|
+
const _vp0 = (input, _path, _exceptionable = true) => "endMoney" === input["metricId"] || "timeToMoney" === input["metricId"] || "varBenefit" === input["metricId"] || _report(_exceptionable, {
|
|
1038
|
+
path: _path + ".metricId",
|
|
1039
|
+
expected: "(\"endMoney\" | \"timeToMoney\" | \"varBenefit\")",
|
|
1040
|
+
value: input["metricId"]
|
|
1041
|
+
});
|
|
1042
|
+
const _ve1 = "Readonly<{ id: string; target: \"model\" | \"session\" | \"strategy\"; path: string; space: ExperimentParamSpace; baseline: ExperimentScalar; probes?: readonly ExperimentScalar[] | undefined; }>";
|
|
1043
|
+
const _ve2 = "ReadonlyArray<Readonly<{ id: string; target: \"model\" | \"session\" | \"strategy\"; path: string; space: ExperimentParamSpace; baseline: ExperimentScalar; probes?: readonly ExperimentScalar[] | undefined; }>>";
|
|
1044
|
+
const _ve3 = "(ReadonlyArray<Readonly<{ id: string; assignments: readonly Readonly<{ dimensionId: string; value: ExperimentScalar; }>[]; }>> | undefined)";
|
|
1045
|
+
const _vp1 = (input, _path, _exceptionable = true) => "string" === typeof input["id"] || _report(_exceptionable, {
|
|
1046
|
+
path: _path + ".id",
|
|
1047
|
+
expected: "string",
|
|
1048
|
+
value: input["id"]
|
|
1049
|
+
});
|
|
1050
|
+
const _ve4 = "(Readonly<{ kind: \"bool\"; }> | Readonly<{ kind: \"choice\"; values: readonly ExperimentScalar[]; }> | Readonly<{ kind: \"int\"; min: number; max: number; }> | Readonly<{ kind: \"number\"; min: number; max: number; scale?: \"linear\" | \"log\" | undefined; }>)";
|
|
1051
|
+
const _vp2 = (input, _path, _exceptionable = true) => "number" === typeof input["min"] || _report(_exceptionable, {
|
|
1052
|
+
path: _path + ".min",
|
|
1053
|
+
expected: "number",
|
|
1054
|
+
value: input["min"]
|
|
1055
|
+
});
|
|
1056
|
+
const _vp3 = (input, _path, _exceptionable = true) => "number" === typeof input["max"] || _report(_exceptionable, {
|
|
1057
|
+
path: _path + ".max",
|
|
1058
|
+
expected: "number",
|
|
1059
|
+
value: input["max"]
|
|
1060
|
+
});
|
|
1061
|
+
const _ve5 = "(Readonly<{ kind: \"bool\"; }> | Readonly<{ kind: \"int\"; min: number; max: number; }> | Readonly<{ kind: \"number\"; min: number; max: number; scale?: \"linear\" | \"log\" | undefined; }> | Readonly<{ kind: \"choice\"; values: readonly ExperimentScalar[]; }>)";
|
|
1062
|
+
const _io0 = input => 1 === input.schemaVersion && ("object" === typeof input.scenario && null !== input.scenario && _io1(input.scenario)) && ("object" === typeof input.model && null !== input.model && _io2(input.model)) && (undefined === input.strategy || "object" === typeof input.strategy && null !== input.strategy && _io3(input.strategy)) && _ip0(input) && (undefined === input.goal || "object" === typeof input.goal && null !== input.goal && _io4(input.goal)) && (undefined === input.objectives || Array.isArray(input.objectives) && input.objectives.every(elem => "object" === typeof elem && null !== elem && _io5(elem))) && (undefined === input.constraints || Array.isArray(input.constraints) && input.constraints.every(elem => "object" === typeof elem && null !== elem && _io6(elem))) && (Array.isArray(input.dimensions) && input.dimensions.every(elem => "object" === typeof elem && null !== elem && _io7(elem))) && (undefined === input.candidates || Array.isArray(input.candidates) && input.candidates.every(elem => "object" === typeof elem && null !== elem && _io12(elem))) && ("object" === typeof input.budget && null !== input.budget && _io14(input.budget)) && (Array.isArray(input.seeds) && input.seeds.every(elem => "number" === typeof elem)) && "number" === typeof input.horizonSec && "number" === typeof input.stepSec && ("object" === typeof input.session && null !== input.session && _io15(input.session));
|
|
1063
|
+
const _io1 = input => "string" === typeof input.ref && "string" === typeof input.hash;
|
|
1064
|
+
const _io10 = input => "number" === input.kind && _ip2(input) && _ip3(input) && (undefined === input.scale || "linear" === input.scale || "log" === input.scale);
|
|
1065
|
+
const _io11 = input => "choice" === input.kind && (Array.isArray(input.values) && input.values.every(elem => null === elem || "string" === typeof elem || "number" === typeof elem || "boolean" === typeof elem));
|
|
1066
|
+
const _io12 = input => _ip1(input) && (Array.isArray(input.assignments) && input.assignments.every(elem => "object" === typeof elem && null !== elem && _io13(elem)));
|
|
1067
|
+
const _io13 = input => "string" === typeof input.dimensionId && (null === input.value || "string" === typeof input.value || "number" === typeof input.value || "boolean" === typeof input.value);
|
|
1068
|
+
const _io14 = input => "number" === typeof input.candidates && "number" === typeof input.seeds;
|
|
1069
|
+
const _io15 = input => "string" === typeof input.pattern && "number" === typeof input.days;
|
|
1070
|
+
const _io2 = input => _ip1(input) && "number" === typeof input.version;
|
|
1071
|
+
const _io3 = input => _ip1(input);
|
|
1072
|
+
const _io4 = input => "number" === typeof input.target;
|
|
1073
|
+
const _io5 = input => _ip1(input) && _ip0(input) && ("higher" === input.sense || "lower" === input.sense);
|
|
1074
|
+
const _io6 = input => _ip1(input) && _ip0(input) && ("gte" === input.op || "lte" === input.op) && "number" === typeof input.limit;
|
|
1075
|
+
const _io7 = input => _ip1(input) && ("model" === input.target || "session" === input.target || "strategy" === input.target) && "string" === typeof input.path && ("object" === typeof input.space && null !== input.space && _iu0(input.space)) && (null === input.baseline || "string" === typeof input.baseline || "number" === typeof input.baseline || "boolean" === typeof input.baseline) && (undefined === input.probes || Array.isArray(input.probes) && input.probes.every(elem => null === elem || "string" === typeof elem || "number" === typeof elem || "boolean" === typeof elem));
|
|
1076
|
+
const _io8 = input => "bool" === input.kind;
|
|
1077
|
+
const _io9 = input => "int" === input.kind && _ip2(input) && _ip3(input);
|
|
1078
|
+
const _iu0 = input => (() => {
|
|
1079
|
+
if ("bool" === input.kind)
|
|
1080
|
+
return _io8(input);
|
|
1081
|
+
else if ("int" === input.kind)
|
|
1082
|
+
return _io9(input);
|
|
1083
|
+
else if ("number" === input.kind)
|
|
1084
|
+
return _io10(input);
|
|
1085
|
+
else if ("choice" === input.kind)
|
|
1086
|
+
return _io11(input);
|
|
1087
|
+
else
|
|
1088
|
+
return false;
|
|
1089
|
+
})();
|
|
1090
|
+
const _vo0 = (input, _path, _exceptionable = true) => [1 === input.schemaVersion || _report(_exceptionable, {
|
|
1091
|
+
path: _path + ".schemaVersion",
|
|
1092
|
+
expected: "1",
|
|
1093
|
+
value: input.schemaVersion
|
|
1094
|
+
}), ("object" === typeof input.scenario && null !== input.scenario || _report(_exceptionable, {
|
|
1095
|
+
path: _path + ".scenario",
|
|
1096
|
+
expected: "Readonly<{ ref: string; hash: string; }>",
|
|
1097
|
+
value: input.scenario
|
|
1098
|
+
})) && _vo1(input.scenario, _path + ".scenario", true && _exceptionable) || _report(_exceptionable, {
|
|
1099
|
+
path: _path + ".scenario",
|
|
1100
|
+
expected: "Readonly<{ ref: string; hash: string; }>",
|
|
1101
|
+
value: input.scenario
|
|
1102
|
+
}), ("object" === typeof input.model && null !== input.model || _report(_exceptionable, {
|
|
1103
|
+
path: _path + ".model",
|
|
1104
|
+
expected: "Readonly<{ id: string; version: number; }>",
|
|
1105
|
+
value: input.model
|
|
1106
|
+
})) && _vo2(input.model, _path + ".model", true && _exceptionable) || _report(_exceptionable, {
|
|
1107
|
+
path: _path + ".model",
|
|
1108
|
+
expected: "Readonly<{ id: string; version: number; }>",
|
|
1109
|
+
value: input.model
|
|
1110
|
+
}), undefined === input.strategy || ("object" === typeof input.strategy && null !== input.strategy || _report(_exceptionable, {
|
|
1111
|
+
path: _path + ".strategy",
|
|
1112
|
+
expected: "(Readonly<{ id: string; }> | undefined)",
|
|
1113
|
+
value: input.strategy
|
|
1114
|
+
})) && _vo3(input.strategy, _path + ".strategy", true && _exceptionable) || _report(_exceptionable, {
|
|
1115
|
+
path: _path + ".strategy",
|
|
1116
|
+
expected: "(Readonly<{ id: string; }> | undefined)",
|
|
1117
|
+
value: input.strategy
|
|
1118
|
+
}), _vp0(input, _path, true && _exceptionable), undefined === input.goal || ("object" === typeof input.goal && null !== input.goal || _report(_exceptionable, {
|
|
1119
|
+
path: _path + ".goal",
|
|
1120
|
+
expected: "(Readonly<{ target: number; }> | undefined)",
|
|
1121
|
+
value: input.goal
|
|
1122
|
+
})) && _vo4(input.goal, _path + ".goal", true && _exceptionable) || _report(_exceptionable, {
|
|
1123
|
+
path: _path + ".goal",
|
|
1124
|
+
expected: "(Readonly<{ target: number; }> | undefined)",
|
|
1125
|
+
value: input.goal
|
|
1126
|
+
}), undefined === input.objectives || (Array.isArray(input.objectives) || _report(_exceptionable, {
|
|
1127
|
+
path: _path + ".objectives",
|
|
1128
|
+
expected: "(ReadonlyArray<Readonly<{ id: string; metricId: ExperimentMetricId; sense: \"higher\" | \"lower\"; }>> | undefined)",
|
|
1129
|
+
value: input.objectives
|
|
1130
|
+
})) && input.objectives.map((elem, _index9) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
1131
|
+
path: _path + ".objectives[" + _index9 + "]",
|
|
1132
|
+
expected: "Readonly<{ id: string; metricId: ExperimentMetricId; sense: \"higher\" | \"lower\"; }>",
|
|
1133
|
+
value: elem
|
|
1134
|
+
})) && _vo5(elem, _path + ".objectives[" + _index9 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
1135
|
+
path: _path + ".objectives[" + _index9 + "]",
|
|
1136
|
+
expected: "Readonly<{ id: string; metricId: ExperimentMetricId; sense: \"higher\" | \"lower\"; }>",
|
|
1137
|
+
value: elem
|
|
1138
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1139
|
+
path: _path + ".objectives",
|
|
1140
|
+
expected: "(ReadonlyArray<Readonly<{ id: string; metricId: ExperimentMetricId; sense: \"higher\" | \"lower\"; }>> | undefined)",
|
|
1141
|
+
value: input.objectives
|
|
1142
|
+
}), undefined === input.constraints || (Array.isArray(input.constraints) || _report(_exceptionable, {
|
|
1143
|
+
path: _path + ".constraints",
|
|
1144
|
+
expected: "(ReadonlyArray<Readonly<{ id: string; metricId: ExperimentMetricId; op: \"gte\" | \"lte\"; limit: number; }>> | undefined)",
|
|
1145
|
+
value: input.constraints
|
|
1146
|
+
})) && input.constraints.map((elem, _index10) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
1147
|
+
path: _path + ".constraints[" + _index10 + "]",
|
|
1148
|
+
expected: "Readonly<{ id: string; metricId: ExperimentMetricId; op: \"gte\" | \"lte\"; limit: number; }>",
|
|
1149
|
+
value: elem
|
|
1150
|
+
})) && _vo6(elem, _path + ".constraints[" + _index10 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
1151
|
+
path: _path + ".constraints[" + _index10 + "]",
|
|
1152
|
+
expected: "Readonly<{ id: string; metricId: ExperimentMetricId; op: \"gte\" | \"lte\"; limit: number; }>",
|
|
1153
|
+
value: elem
|
|
1154
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1155
|
+
path: _path + ".constraints",
|
|
1156
|
+
expected: "(ReadonlyArray<Readonly<{ id: string; metricId: ExperimentMetricId; op: \"gte\" | \"lte\"; limit: number; }>> | undefined)",
|
|
1157
|
+
value: input.constraints
|
|
1158
|
+
}), (Array.isArray(input.dimensions) || _report(_exceptionable, {
|
|
1159
|
+
path: _path + ".dimensions",
|
|
1160
|
+
expected: _ve2,
|
|
1161
|
+
value: input.dimensions
|
|
1162
|
+
})) && input.dimensions.map((elem, _index11) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
1163
|
+
path: _path + ".dimensions[" + _index11 + "]",
|
|
1164
|
+
expected: _ve1,
|
|
1165
|
+
value: elem
|
|
1166
|
+
})) && _vo7(elem, _path + ".dimensions[" + _index11 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
1167
|
+
path: _path + ".dimensions[" + _index11 + "]",
|
|
1168
|
+
expected: _ve1,
|
|
1169
|
+
value: elem
|
|
1170
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1171
|
+
path: _path + ".dimensions",
|
|
1172
|
+
expected: _ve2,
|
|
1173
|
+
value: input.dimensions
|
|
1174
|
+
}), undefined === input.candidates || (Array.isArray(input.candidates) || _report(_exceptionable, {
|
|
1175
|
+
path: _path + ".candidates",
|
|
1176
|
+
expected: _ve3,
|
|
1177
|
+
value: input.candidates
|
|
1178
|
+
})) && input.candidates.map((elem, _index12) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
1179
|
+
path: _path + ".candidates[" + _index12 + "]",
|
|
1180
|
+
expected: "Readonly<{ id: string; assignments: readonly Readonly<{ dimensionId: string; value: ExperimentScalar; }>[]; }>",
|
|
1181
|
+
value: elem
|
|
1182
|
+
})) && _vo12(elem, _path + ".candidates[" + _index12 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
1183
|
+
path: _path + ".candidates[" + _index12 + "]",
|
|
1184
|
+
expected: "Readonly<{ id: string; assignments: readonly Readonly<{ dimensionId: string; value: ExperimentScalar; }>[]; }>",
|
|
1185
|
+
value: elem
|
|
1186
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1187
|
+
path: _path + ".candidates",
|
|
1188
|
+
expected: _ve3,
|
|
1189
|
+
value: input.candidates
|
|
1190
|
+
}), ("object" === typeof input.budget && null !== input.budget || _report(_exceptionable, {
|
|
1191
|
+
path: _path + ".budget",
|
|
1192
|
+
expected: "Readonly<{ candidates: number; seeds: number; }>",
|
|
1193
|
+
value: input.budget
|
|
1194
|
+
})) && _vo14(input.budget, _path + ".budget", true && _exceptionable) || _report(_exceptionable, {
|
|
1195
|
+
path: _path + ".budget",
|
|
1196
|
+
expected: "Readonly<{ candidates: number; seeds: number; }>",
|
|
1197
|
+
value: input.budget
|
|
1198
|
+
}), (Array.isArray(input.seeds) || _report(_exceptionable, {
|
|
1199
|
+
path: _path + ".seeds",
|
|
1200
|
+
expected: "ReadonlyArray<number>",
|
|
1201
|
+
value: input.seeds
|
|
1202
|
+
})) && input.seeds.map((elem, _index13) => "number" === typeof elem || _report(_exceptionable, {
|
|
1203
|
+
path: _path + ".seeds[" + _index13 + "]",
|
|
1204
|
+
expected: "number",
|
|
1205
|
+
value: elem
|
|
1206
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1207
|
+
path: _path + ".seeds",
|
|
1208
|
+
expected: "ReadonlyArray<number>",
|
|
1209
|
+
value: input.seeds
|
|
1210
|
+
}), "number" === typeof input.horizonSec || _report(_exceptionable, {
|
|
1211
|
+
path: _path + ".horizonSec",
|
|
1212
|
+
expected: "number",
|
|
1213
|
+
value: input.horizonSec
|
|
1214
|
+
}), "number" === typeof input.stepSec || _report(_exceptionable, {
|
|
1215
|
+
path: _path + ".stepSec",
|
|
1216
|
+
expected: "number",
|
|
1217
|
+
value: input.stepSec
|
|
1218
|
+
}), ("object" === typeof input.session && null !== input.session || _report(_exceptionable, {
|
|
1219
|
+
path: _path + ".session",
|
|
1220
|
+
expected: "Readonly<{ pattern: string; days: number; }>",
|
|
1221
|
+
value: input.session
|
|
1222
|
+
})) && _vo15(input.session, _path + ".session", true && _exceptionable) || _report(_exceptionable, {
|
|
1223
|
+
path: _path + ".session",
|
|
1224
|
+
expected: "Readonly<{ pattern: string; days: number; }>",
|
|
1225
|
+
value: input.session
|
|
1226
|
+
})].every(flag => flag);
|
|
1227
|
+
const _vo1 = (input, _path, _exceptionable = true) => ["string" === typeof input.ref || _report(_exceptionable, {
|
|
1228
|
+
path: _path + ".ref",
|
|
1229
|
+
expected: "string",
|
|
1230
|
+
value: input.ref
|
|
1231
|
+
}), "string" === typeof input.hash || _report(_exceptionable, {
|
|
1232
|
+
path: _path + ".hash",
|
|
1233
|
+
expected: "string",
|
|
1234
|
+
value: input.hash
|
|
1235
|
+
})].every(flag => flag);
|
|
1236
|
+
const _vo10 = (input, _path, _exceptionable = true) => ["number" === input.kind || _report(_exceptionable, {
|
|
1237
|
+
path: _path + ".kind",
|
|
1238
|
+
expected: "\"number\"",
|
|
1239
|
+
value: input.kind
|
|
1240
|
+
}), _vp2(input, _path, true && _exceptionable), _vp3(input, _path, true && _exceptionable), undefined === input.scale || "linear" === input.scale || "log" === input.scale || _report(_exceptionable, {
|
|
1241
|
+
path: _path + ".scale",
|
|
1242
|
+
expected: "(\"linear\" | \"log\" | undefined)",
|
|
1243
|
+
value: input.scale
|
|
1244
|
+
})].every(flag => flag);
|
|
1245
|
+
const _vo11 = (input, _path, _exceptionable = true) => ["choice" === input.kind || _report(_exceptionable, {
|
|
1246
|
+
path: _path + ".kind",
|
|
1247
|
+
expected: "\"choice\"",
|
|
1248
|
+
value: input.kind
|
|
1249
|
+
}), (Array.isArray(input.values) || _report(_exceptionable, {
|
|
1250
|
+
path: _path + ".values",
|
|
1251
|
+
expected: "ReadonlyArray<ExperimentScalar>",
|
|
1252
|
+
value: input.values
|
|
1253
|
+
})) && input.values.map((elem, _index15) => null === elem || "string" === typeof elem || "number" === typeof elem || "boolean" === typeof elem || _report(_exceptionable, {
|
|
1254
|
+
path: _path + ".values[" + _index15 + "]",
|
|
1255
|
+
expected: "(boolean | null | number | string)",
|
|
1256
|
+
value: elem
|
|
1257
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1258
|
+
path: _path + ".values",
|
|
1259
|
+
expected: "ReadonlyArray<ExperimentScalar>",
|
|
1260
|
+
value: input.values
|
|
1261
|
+
})].every(flag => flag);
|
|
1262
|
+
const _vo12 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable), (Array.isArray(input.assignments) || _report(_exceptionable, {
|
|
1263
|
+
path: _path + ".assignments",
|
|
1264
|
+
expected: "ReadonlyArray<Readonly<{ dimensionId: string; value: ExperimentScalar; }>>",
|
|
1265
|
+
value: input.assignments
|
|
1266
|
+
})) && input.assignments.map((elem, _index16) => ("object" === typeof elem && null !== elem || _report(_exceptionable, {
|
|
1267
|
+
path: _path + ".assignments[" + _index16 + "]",
|
|
1268
|
+
expected: "Readonly<{ dimensionId: string; value: ExperimentScalar; }>",
|
|
1269
|
+
value: elem
|
|
1270
|
+
})) && _vo13(elem, _path + ".assignments[" + _index16 + "]", true && _exceptionable) || _report(_exceptionable, {
|
|
1271
|
+
path: _path + ".assignments[" + _index16 + "]",
|
|
1272
|
+
expected: "Readonly<{ dimensionId: string; value: ExperimentScalar; }>",
|
|
1273
|
+
value: elem
|
|
1274
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1275
|
+
path: _path + ".assignments",
|
|
1276
|
+
expected: "ReadonlyArray<Readonly<{ dimensionId: string; value: ExperimentScalar; }>>",
|
|
1277
|
+
value: input.assignments
|
|
1278
|
+
})].every(flag => flag);
|
|
1279
|
+
const _vo13 = (input, _path, _exceptionable = true) => ["string" === typeof input.dimensionId || _report(_exceptionable, {
|
|
1280
|
+
path: _path + ".dimensionId",
|
|
1281
|
+
expected: "string",
|
|
1282
|
+
value: input.dimensionId
|
|
1283
|
+
}), null === input.value || "string" === typeof input.value || "number" === typeof input.value || "boolean" === typeof input.value || _report(_exceptionable, {
|
|
1284
|
+
path: _path + ".value",
|
|
1285
|
+
expected: "(boolean | null | number | string)",
|
|
1286
|
+
value: input.value
|
|
1287
|
+
})].every(flag => flag);
|
|
1288
|
+
const _vo14 = (input, _path, _exceptionable = true) => ["number" === typeof input.candidates || _report(_exceptionable, {
|
|
1289
|
+
path: _path + ".candidates",
|
|
1290
|
+
expected: "number",
|
|
1291
|
+
value: input.candidates
|
|
1292
|
+
}), "number" === typeof input.seeds || _report(_exceptionable, {
|
|
1293
|
+
path: _path + ".seeds",
|
|
1294
|
+
expected: "number",
|
|
1295
|
+
value: input.seeds
|
|
1296
|
+
})].every(flag => flag);
|
|
1297
|
+
const _vo15 = (input, _path, _exceptionable = true) => ["string" === typeof input.pattern || _report(_exceptionable, {
|
|
1298
|
+
path: _path + ".pattern",
|
|
1299
|
+
expected: "string",
|
|
1300
|
+
value: input.pattern
|
|
1301
|
+
}), "number" === typeof input.days || _report(_exceptionable, {
|
|
1302
|
+
path: _path + ".days",
|
|
1303
|
+
expected: "number",
|
|
1304
|
+
value: input.days
|
|
1305
|
+
})].every(flag => flag);
|
|
1306
|
+
const _vo2 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable), "number" === typeof input.version || _report(_exceptionable, {
|
|
1307
|
+
path: _path + ".version",
|
|
1308
|
+
expected: "number",
|
|
1309
|
+
value: input.version
|
|
1310
|
+
})].every(flag => flag);
|
|
1311
|
+
const _vo3 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable)].every(flag => flag);
|
|
1312
|
+
const _vo4 = (input, _path, _exceptionable = true) => ["number" === typeof input.target || _report(_exceptionable, {
|
|
1313
|
+
path: _path + ".target",
|
|
1314
|
+
expected: "number",
|
|
1315
|
+
value: input.target
|
|
1316
|
+
})].every(flag => flag);
|
|
1317
|
+
const _vo5 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable), _vp0(input, _path, true && _exceptionable), "higher" === input.sense || "lower" === input.sense || _report(_exceptionable, {
|
|
1318
|
+
path: _path + ".sense",
|
|
1319
|
+
expected: "(\"higher\" | \"lower\")",
|
|
1320
|
+
value: input.sense
|
|
1321
|
+
})].every(flag => flag);
|
|
1322
|
+
const _vo6 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable), _vp0(input, _path, true && _exceptionable), "gte" === input.op || "lte" === input.op || _report(_exceptionable, {
|
|
1323
|
+
path: _path + ".op",
|
|
1324
|
+
expected: "(\"gte\" | \"lte\")",
|
|
1325
|
+
value: input.op
|
|
1326
|
+
}), "number" === typeof input.limit || _report(_exceptionable, {
|
|
1327
|
+
path: _path + ".limit",
|
|
1328
|
+
expected: "number",
|
|
1329
|
+
value: input.limit
|
|
1330
|
+
})].every(flag => flag);
|
|
1331
|
+
const _vo7 = (input, _path, _exceptionable = true) => [_vp1(input, _path, true && _exceptionable), "model" === input.target || "session" === input.target || "strategy" === input.target || _report(_exceptionable, {
|
|
1332
|
+
path: _path + ".target",
|
|
1333
|
+
expected: "(\"model\" | \"session\" | \"strategy\")",
|
|
1334
|
+
value: input.target
|
|
1335
|
+
}), "string" === typeof input.path || _report(_exceptionable, {
|
|
1336
|
+
path: _path + ".path",
|
|
1337
|
+
expected: "string",
|
|
1338
|
+
value: input.path
|
|
1339
|
+
}), ("object" === typeof input.space && null !== input.space || _report(_exceptionable, {
|
|
1340
|
+
path: _path + ".space",
|
|
1341
|
+
expected: _ve4,
|
|
1342
|
+
value: input.space
|
|
1343
|
+
})) && _vu0(input.space, _path + ".space", true && _exceptionable) || _report(_exceptionable, {
|
|
1344
|
+
path: _path + ".space",
|
|
1345
|
+
expected: _ve4,
|
|
1346
|
+
value: input.space
|
|
1347
|
+
}), null === input.baseline || "string" === typeof input.baseline || "number" === typeof input.baseline || "boolean" === typeof input.baseline || _report(_exceptionable, {
|
|
1348
|
+
path: _path + ".baseline",
|
|
1349
|
+
expected: "(boolean | null | number | string)",
|
|
1350
|
+
value: input.baseline
|
|
1351
|
+
}), undefined === input.probes || (Array.isArray(input.probes) || _report(_exceptionable, {
|
|
1352
|
+
path: _path + ".probes",
|
|
1353
|
+
expected: "(ReadonlyArray<ExperimentScalar> | undefined)",
|
|
1354
|
+
value: input.probes
|
|
1355
|
+
})) && input.probes.map((elem, _index14) => null === elem || "string" === typeof elem || "number" === typeof elem || "boolean" === typeof elem || _report(_exceptionable, {
|
|
1356
|
+
path: _path + ".probes[" + _index14 + "]",
|
|
1357
|
+
expected: "(boolean | null | number | string)",
|
|
1358
|
+
value: elem
|
|
1359
|
+
})).every(flag => flag) || _report(_exceptionable, {
|
|
1360
|
+
path: _path + ".probes",
|
|
1361
|
+
expected: "(ReadonlyArray<ExperimentScalar> | undefined)",
|
|
1362
|
+
value: input.probes
|
|
1363
|
+
})].every(flag => flag);
|
|
1364
|
+
const _vo8 = (input, _path, _exceptionable = true) => ["bool" === input.kind || _report(_exceptionable, {
|
|
1365
|
+
path: _path + ".kind",
|
|
1366
|
+
expected: "\"bool\"",
|
|
1367
|
+
value: input.kind
|
|
1368
|
+
})].every(flag => flag);
|
|
1369
|
+
const _vo9 = (input, _path, _exceptionable = true) => ["int" === input.kind || _report(_exceptionable, {
|
|
1370
|
+
path: _path + ".kind",
|
|
1371
|
+
expected: "\"int\"",
|
|
1372
|
+
value: input.kind
|
|
1373
|
+
}), _vp2(input, _path, true && _exceptionable), _vp3(input, _path, true && _exceptionable)].every(flag => flag);
|
|
1374
|
+
const _vu0 = (input, _path, _exceptionable = true) => (() => {
|
|
1375
|
+
if ("bool" === input.kind)
|
|
1376
|
+
return _vo8(input, _path, true && _exceptionable);
|
|
1377
|
+
else if ("int" === input.kind)
|
|
1378
|
+
return _vo9(input, _path, true && _exceptionable);
|
|
1379
|
+
else if ("number" === input.kind)
|
|
1380
|
+
return _vo10(input, _path, true && _exceptionable);
|
|
1381
|
+
else if ("choice" === input.kind)
|
|
1382
|
+
return _vo11(input, _path, true && _exceptionable);
|
|
1383
|
+
else
|
|
1384
|
+
return _report(_exceptionable, {
|
|
1385
|
+
path: _path,
|
|
1386
|
+
expected: _ve5,
|
|
1387
|
+
value: input
|
|
1388
|
+
});
|
|
1389
|
+
})();
|
|
1390
|
+
const __is = input => "object" === typeof input && null !== input && _io0(input);
|
|
1391
|
+
let errors;
|
|
1392
|
+
let _report;
|
|
1393
|
+
return _createStandardSchema_1._createStandardSchema(input => {
|
|
1394
|
+
if (false === __is(input)) {
|
|
1395
|
+
errors = [];
|
|
1396
|
+
_report = _validateReport_1._validateReport(errors);
|
|
1397
|
+
((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || _report(true, {
|
|
1398
|
+
path: _path + "",
|
|
1399
|
+
expected: _ve0,
|
|
1400
|
+
value: input
|
|
1401
|
+
})) && _vo0(input, _path + "", true) || _report(true, {
|
|
1402
|
+
path: _path + "",
|
|
1403
|
+
expected: _ve0,
|
|
1404
|
+
value: input
|
|
1405
|
+
}))(input, "$input", true);
|
|
1406
|
+
const success = 0 === errors.length;
|
|
1407
|
+
return success ? {
|
|
1408
|
+
success,
|
|
1409
|
+
data: input
|
|
1410
|
+
} : {
|
|
1411
|
+
success,
|
|
1412
|
+
errors,
|
|
1413
|
+
data: input
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
success: true,
|
|
1418
|
+
data: input
|
|
1419
|
+
};
|
|
1420
|
+
});
|
|
1421
|
+
})())(input);
|
|
1422
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1423
|
+
// Canonical JSON drops `__type`, so a parameter with that name would vanish from task descriptors and hashes.
|
|
1424
|
+
const BANNED_SEGMENT = new Set(["__proto__", "prototype", "constructor", "eval", "__type"]);
|
|
1425
|
+
function reject(message) {
|
|
1426
|
+
throw new ExperimentRejected(`Experiment ${message}`);
|
|
1427
|
+
}
|
|
1428
|
+
export function assertRegisteredPath(path, target) {
|
|
1429
|
+
if (typeof path !== "string" || path.length === 0 || path !== path.trim())
|
|
1430
|
+
reject("parameter path is empty");
|
|
1431
|
+
if (/[\$\[\]\(\)]/.test(path) || path.includes("..") || path.includes("/") || path.includes("\\") || path.includes(" ")) {
|
|
1432
|
+
reject("parameter path is not a registered identifier");
|
|
1433
|
+
}
|
|
1434
|
+
const parts = path.split(".");
|
|
1435
|
+
if (parts.length === 0 || parts.some((part) => !IDENTIFIER.test(part) || BANNED_SEGMENT.has(part))) {
|
|
1436
|
+
reject("parameter path is not a registered identifier");
|
|
1437
|
+
}
|
|
1438
|
+
if (target === "session" && (parts.length !== 1 || (parts[0] !== "pattern" && parts[0] !== "days"))) {
|
|
1439
|
+
reject("session path must be pattern or days");
|
|
1440
|
+
}
|
|
1441
|
+
return parts;
|
|
1442
|
+
}
|
|
1443
|
+
export function setRegisteredParam(root, path, value) {
|
|
1444
|
+
const parts = assertRegisteredPath(path, "model");
|
|
1445
|
+
// Copy only the objects on the assigned path, with their prototype and property descriptors.
|
|
1446
|
+
// Every other value keeps its identity and exact number. A copied property is configurable, so a frozen
|
|
1447
|
+
// or read-only source can still take the assigned value as a new own data property.
|
|
1448
|
+
const copy = (source) => {
|
|
1449
|
+
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
1450
|
+
for (const descriptor of Object.values(descriptors))
|
|
1451
|
+
descriptor.configurable = true;
|
|
1452
|
+
return Object.create(Object.getPrototypeOf(source), descriptors);
|
|
1453
|
+
};
|
|
1454
|
+
const put = (target, key, entry) => {
|
|
1455
|
+
Object.defineProperty(target, key, { value: entry, writable: true, enumerable: true, configurable: true });
|
|
1456
|
+
};
|
|
1457
|
+
const clone = copy(root);
|
|
1458
|
+
let cursor = clone;
|
|
1459
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
1460
|
+
const key = parts[index];
|
|
1461
|
+
// assertRegisteredPath already bans these. The inline guard keeps the write visibly safe at the assignment.
|
|
1462
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype")
|
|
1463
|
+
reject("parameter path is not a registered identifier");
|
|
1464
|
+
const next = Object.prototype.hasOwnProperty.call(cursor, key) ? cursor[key] : undefined;
|
|
1465
|
+
const copied = next === null || typeof next !== "object" || Array.isArray(next) ? {} : copy(next);
|
|
1466
|
+
put(cursor, key, copied);
|
|
1467
|
+
cursor = copied;
|
|
1468
|
+
}
|
|
1469
|
+
const leaf = parts[parts.length - 1];
|
|
1470
|
+
if (leaf === "__proto__" || leaf === "constructor" || leaf === "prototype")
|
|
1471
|
+
reject("parameter path is not a registered identifier");
|
|
1472
|
+
put(cursor, leaf, value);
|
|
1473
|
+
return clone;
|
|
1474
|
+
}
|
|
1475
|
+
function byText(left, right) {
|
|
1476
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1477
|
+
}
|
|
1478
|
+
function normalizeSpec(spec) {
|
|
1479
|
+
return {
|
|
1480
|
+
...spec,
|
|
1481
|
+
dimensions: [...spec.dimensions].sort((left, right) => byText(left.id, right.id)),
|
|
1482
|
+
...(spec.candidates
|
|
1483
|
+
? {
|
|
1484
|
+
candidates: [...spec.candidates]
|
|
1485
|
+
.map((candidate) => ({
|
|
1486
|
+
...candidate,
|
|
1487
|
+
assignments: [...candidate.assignments].sort((left, right) => byText(left.dimensionId, right.dimensionId)),
|
|
1488
|
+
}))
|
|
1489
|
+
.sort((left, right) => byText(left.id, right.id)),
|
|
1490
|
+
}
|
|
1491
|
+
: {}),
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
/** Two dimensions on one target overlap when one path equals the other or is a dotted prefix of it. */
|
|
1495
|
+
function pathsOverlap(left, right) {
|
|
1496
|
+
return left === right || left.startsWith(`${right}.`) || right.startsWith(`${left}.`);
|
|
1497
|
+
}
|
|
1498
|
+
function requireSpec(input) {
|
|
1499
|
+
const parsed = validateExperimentSpec(input);
|
|
1500
|
+
if (!parsed.success) {
|
|
1501
|
+
reject(`spec rejected: ${parsed.errors.map((error) => error.path).join(", ")}`);
|
|
1502
|
+
}
|
|
1503
|
+
const spec = parsed.data;
|
|
1504
|
+
if (!Number.isFinite(spec.horizonSec) || spec.horizonSec <= 0)
|
|
1505
|
+
reject("horizon must be a positive finite number");
|
|
1506
|
+
// A JSON literal such as 1e400 parses to Infinity, which the artifact and canonical JSON cannot carry.
|
|
1507
|
+
if (!Number.isFinite(spec.model.version) || Object.is(spec.model.version, -0))
|
|
1508
|
+
reject("model version must be finite and not -0");
|
|
1509
|
+
if (spec.strategy !== undefined && spec.strategy.id.length === 0)
|
|
1510
|
+
reject("strategy id must not be empty");
|
|
1511
|
+
if (!Number.isFinite(spec.stepSec) || spec.stepSec <= 0)
|
|
1512
|
+
reject("step must be a positive finite number");
|
|
1513
|
+
if (!Number.isInteger(spec.budget.candidates) || spec.budget.candidates < 1)
|
|
1514
|
+
reject("candidate budget must be a positive integer");
|
|
1515
|
+
if (!Number.isInteger(spec.budget.seeds) || spec.budget.seeds < 1)
|
|
1516
|
+
reject("seed budget must be a positive integer");
|
|
1517
|
+
if (spec.seeds.length === 0 || spec.seeds.some((seed) => !Number.isFinite(seed)))
|
|
1518
|
+
reject("seeds must be finite");
|
|
1519
|
+
if (new Set(spec.seeds).size !== spec.seeds.length)
|
|
1520
|
+
reject("seeds must be unique logical seeds");
|
|
1521
|
+
// Task ids, logical seeds, and canonical JSON write -0 as 0, while ctx.seed would still carry -0.
|
|
1522
|
+
if (spec.seeds.some((seed) => Object.is(seed, -0)))
|
|
1523
|
+
reject("seeds must not be -0");
|
|
1524
|
+
if (spec.seeds.length > spec.budget.seeds)
|
|
1525
|
+
reject("seed budget exceeded");
|
|
1526
|
+
if (!Number.isFinite(spec.session.days) || spec.session.days <= 0)
|
|
1527
|
+
reject("session days must be positive");
|
|
1528
|
+
const ids = new Set();
|
|
1529
|
+
for (const [index, dimension] of spec.dimensions.entries()) {
|
|
1530
|
+
if (ids.has(dimension.id))
|
|
1531
|
+
reject(`duplicate dimension ${dimension.id}`);
|
|
1532
|
+
ids.add(dimension.id);
|
|
1533
|
+
assertRegisteredPath(dimension.path, dimension.target);
|
|
1534
|
+
// A JSON literal such as 1e400 parses to Infinity, and canonical JSON would hash it as null.
|
|
1535
|
+
if (dimension.space.kind === "int" || dimension.space.kind === "number") {
|
|
1536
|
+
if (!Number.isFinite(dimension.space.min) || !Number.isFinite(dimension.space.max))
|
|
1537
|
+
reject(`space bounds for ${dimension.id} must be finite`);
|
|
1538
|
+
}
|
|
1539
|
+
else if (dimension.space.kind === "choice" && dimension.space.values.some((entry) => typeof entry === "number" && !Number.isFinite(entry))) {
|
|
1540
|
+
reject(`choice values for ${dimension.id} must be finite`);
|
|
1541
|
+
}
|
|
1542
|
+
// Each candidate runs one continuous horizon. No session schedule is executed, so a session value would not reach the run.
|
|
1543
|
+
if (dimension.target === "session")
|
|
1544
|
+
reject(`session dimension ${dimension.id} is not executed: each candidate runs one continuous horizon`);
|
|
1545
|
+
for (const other of spec.dimensions.slice(0, index)) {
|
|
1546
|
+
if (other.target === dimension.target && pathsOverlap(other.path, dimension.path)) {
|
|
1547
|
+
reject(`dimensions ${other.id} and ${dimension.id} target the same ${dimension.target} parameter`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (!assignmentFitsParamSpace(dimension.space, dimension.baseline))
|
|
1551
|
+
reject(`baseline for ${dimension.id} is outside its parameter space`);
|
|
1552
|
+
for (const probe of dimension.probes ?? []) {
|
|
1553
|
+
if (!assignmentFitsParamSpace(dimension.space, probe))
|
|
1554
|
+
reject(`probe for ${dimension.id} is outside its parameter space`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
// An objective id names one Pareto objective. A repeated id would be compared as two independent positions.
|
|
1558
|
+
const objectiveIds = new Set();
|
|
1559
|
+
for (const objective of spec.objectives ?? []) {
|
|
1560
|
+
if (objectiveIds.has(objective.id))
|
|
1561
|
+
reject(`duplicate objective ${objective.id}`);
|
|
1562
|
+
objectiveIds.add(objective.id);
|
|
1563
|
+
}
|
|
1564
|
+
// A violation lists only the constraint id, so the id must name one constraint. A JSON literal such as 1e400 parses to Infinity.
|
|
1565
|
+
const constraintIds = new Set();
|
|
1566
|
+
for (const constraint of spec.constraints ?? []) {
|
|
1567
|
+
if (constraintIds.has(constraint.id))
|
|
1568
|
+
reject(`duplicate constraint ${constraint.id}`);
|
|
1569
|
+
constraintIds.add(constraint.id);
|
|
1570
|
+
if (!Number.isFinite(constraint.limit))
|
|
1571
|
+
reject(`constraint ${constraint.id} limit must be finite`);
|
|
1572
|
+
}
|
|
1573
|
+
return spec;
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Generated probe id. A string probe is written as JSON text, so `1` and `"1"`, or `true` and `"true"`, stay distinct.
|
|
1577
|
+
* A number, boolean, or null probe is written as its plain text.
|
|
1578
|
+
*/
|
|
1579
|
+
function probeCandidateId(dimension, probe) {
|
|
1580
|
+
return `oat:${dimension.id}:${typeof probe === "string" ? JSON.stringify(probe) : String(probe)}`;
|
|
1581
|
+
}
|
|
1582
|
+
function plannedCandidates(spec) {
|
|
1583
|
+
const explicit = spec.candidates ?? [];
|
|
1584
|
+
const probes = spec.dimensions.some((dimension) => (dimension.probes?.length ?? 0) > 0);
|
|
1585
|
+
const generated = [];
|
|
1586
|
+
if (explicit.length === 0 || probes) {
|
|
1587
|
+
const baselineAssignments = spec.dimensions.map((dimension) => ({ dimensionId: dimension.id, value: dimension.baseline }));
|
|
1588
|
+
generated.push({ id: "baseline", assignments: baselineAssignments });
|
|
1589
|
+
for (const dimension of [...spec.dimensions].sort((left, right) => byText(left.id, right.id))) {
|
|
1590
|
+
for (const probe of dimension.probes ?? []) {
|
|
1591
|
+
generated.push({
|
|
1592
|
+
id: probeCandidateId(dimension, probe),
|
|
1593
|
+
assignments: spec.dimensions.map((item) => ({
|
|
1594
|
+
dimensionId: item.id,
|
|
1595
|
+
value: item.id === dimension.id ? probe : item.baseline,
|
|
1596
|
+
})),
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
const merged = [...explicit, ...generated].sort((left, right) => byText(left.id, right.id));
|
|
1602
|
+
if (merged.length === 0)
|
|
1603
|
+
reject("experiment requires at least one candidate");
|
|
1604
|
+
if (merged.length > spec.budget.candidates)
|
|
1605
|
+
reject("candidate budget exceeded");
|
|
1606
|
+
const seen = new Set();
|
|
1607
|
+
for (const candidate of merged) {
|
|
1608
|
+
if (candidate.id.length === 0 || seen.has(candidate.id))
|
|
1609
|
+
reject(`duplicate candidate ${candidate.id}`);
|
|
1610
|
+
seen.add(candidate.id);
|
|
1611
|
+
}
|
|
1612
|
+
return merged;
|
|
1613
|
+
}
|
|
1614
|
+
function hasOwnPath(root, path) {
|
|
1615
|
+
let cursor = root;
|
|
1616
|
+
for (const part of path.split(".")) {
|
|
1617
|
+
if (cursor === null || typeof cursor !== "object" || !Object.prototype.hasOwnProperty.call(cursor, part))
|
|
1618
|
+
return false;
|
|
1619
|
+
cursor = cursor[part];
|
|
1620
|
+
}
|
|
1621
|
+
return true;
|
|
1622
|
+
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Checks the candidate params like scenario compile does in legacy-raw mode. With a schema, its output only
|
|
1625
|
+
* confirms that every assigned path is registered: a schema that strips that path would leave the run at its
|
|
1626
|
+
* default while the task claims the assignment. Without a schema, the path must already be in the scenario or
|
|
1627
|
+
* default params, so a typo is not added as a key the factory ignores. The candidate keeps the raw params, so
|
|
1628
|
+
* unassigned fields stay as the scenario wrote them and `create` receives the same representation as a scenario run.
|
|
1629
|
+
*/
|
|
1630
|
+
function checkCandidateParams(schema, baseParams, params, assigned, label) {
|
|
1631
|
+
if (!schema) {
|
|
1632
|
+
for (const { dimension } of assigned) {
|
|
1633
|
+
if (!hasOwnPath(baseParams, dimension.path)) {
|
|
1634
|
+
reject(`${label} has no params schema and its scenario params do not register ${dimension.path} for ${dimension.id}`);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
// A detached copy: a schema that writes its input cannot reach the candidate, the base, or the caller.
|
|
1640
|
+
const result = schema["~standard"].validate(cloneJsonData(params));
|
|
1641
|
+
if (!result.success) {
|
|
1642
|
+
reject(`${label} params rejected: ${result.issues.map((issue) => issue.message).join("; ")}`);
|
|
1643
|
+
}
|
|
1644
|
+
for (const { dimension } of assigned) {
|
|
1645
|
+
if (!hasOwnPath(result.value, dimension.path))
|
|
1646
|
+
reject(`${label} params schema does not register ${dimension.path} for ${dimension.id}`);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
function asParamRecord(params, label) {
|
|
1650
|
+
if (params === undefined)
|
|
1651
|
+
return {};
|
|
1652
|
+
if (params === null || typeof params !== "object" || Array.isArray(params)) {
|
|
1653
|
+
reject(`${label} params of the scenario are not an object, so a path cannot be assigned`);
|
|
1654
|
+
}
|
|
1655
|
+
return params;
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Task descriptors and hashes write params as canonical JSON, so the params must be JSON data: null, booleans,
|
|
1659
|
+
* strings, finite numbers other than -0, arrays, and plain objects. A Date, a class instance, a function, or an
|
|
1660
|
+
* undefined field would run as one value and be recorded as another, and could not be copied faithfully.
|
|
1661
|
+
* An object reached twice (a shared reference or a cycle) and a `__type` key, which canonical JSON drops,
|
|
1662
|
+
* are refused too.
|
|
1663
|
+
*/
|
|
1664
|
+
/** An enumerable, writable, configurable data property: the only kind JSON.parse creates. */
|
|
1665
|
+
function isOrdinaryData(descriptor) {
|
|
1666
|
+
return (descriptor !== undefined &&
|
|
1667
|
+
"value" in descriptor &&
|
|
1668
|
+
descriptor.enumerable === true &&
|
|
1669
|
+
descriptor.writable === true &&
|
|
1670
|
+
descriptor.configurable === true);
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* `allowUndefined` lets an object field hold undefined. Canonical JSON drops that field and spec and scenario
|
|
1674
|
+
* readers treat it as absent. Params do not get this allowance, because a factory can see the key.
|
|
1675
|
+
*/
|
|
1676
|
+
function isJsonData(value, seen = new Set(), allowUndefined = false) {
|
|
1677
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
1678
|
+
return true;
|
|
1679
|
+
if (typeof value === "number")
|
|
1680
|
+
return Number.isFinite(value) && !Object.is(value, -0);
|
|
1681
|
+
if (typeof value !== "object" || seen.has(value))
|
|
1682
|
+
return false;
|
|
1683
|
+
// Visited objects stay recorded, so a shared reference fails like a cycle.
|
|
1684
|
+
seen.add(value);
|
|
1685
|
+
// A frozen, sealed, or non-extensible object is observable but writes the same JSON as an ordinary one.
|
|
1686
|
+
if (!Object.isExtensible(value))
|
|
1687
|
+
return false;
|
|
1688
|
+
if (Array.isArray(value)) {
|
|
1689
|
+
// Only a plain array of index slots: no subclass prototype, no named or symbol own property, no holes or accessors.
|
|
1690
|
+
if (Object.getPrototypeOf(value) !== Array.prototype)
|
|
1691
|
+
return false;
|
|
1692
|
+
// Every slot 0..length-1 is an own enumerable data property, and there is no other own key but length.
|
|
1693
|
+
if (Reflect.ownKeys(value).length !== value.length + 1)
|
|
1694
|
+
return false;
|
|
1695
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1696
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
1697
|
+
if (!isOrdinaryData(descriptor) || !isJsonData(descriptor.value, seen, allowUndefined))
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1700
|
+
return true;
|
|
1701
|
+
}
|
|
1702
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1703
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
1704
|
+
return false;
|
|
1705
|
+
return Reflect.ownKeys(value).every((key) => {
|
|
1706
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1707
|
+
return (typeof key === "string" &&
|
|
1708
|
+
key !== "__type" &&
|
|
1709
|
+
isOrdinaryData(descriptor) &&
|
|
1710
|
+
((allowUndefined && descriptor.value === undefined) || isJsonData(descriptor.value, seen, allowUndefined)));
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
/** One visited set spans the model and strategy params, so an object shared between them is refused too. */
|
|
1714
|
+
function requireJsonParams(params, label, seen) {
|
|
1715
|
+
// Task descriptors write absent params as null, so an explicit null root would share that record.
|
|
1716
|
+
if (params === null)
|
|
1717
|
+
reject(`${label} params must be an object or omitted, not null`);
|
|
1718
|
+
if (params !== undefined && !isJsonData(params, seen))
|
|
1719
|
+
reject(`${label} params must be JSON data, because task descriptors and hashes record them as JSON`);
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* The whole scenario is checked before it is hashed: every field the run reads must be one canonical JSON records.
|
|
1723
|
+
* Canonical JSON writes Infinity as null and -0 as 0 and skips non-enumerable fields, so a constraint such as
|
|
1724
|
+
* maxActionsPerStep or a hidden policy.mode could change the run but not the hash. Params and the initial state
|
|
1725
|
+
* then get the stricter params rule.
|
|
1726
|
+
*/
|
|
1727
|
+
function requireScenarioData(scenario) {
|
|
1728
|
+
const seen = new Set();
|
|
1729
|
+
requireJsonParams(scenario.model.params, "model", seen);
|
|
1730
|
+
requireJsonParams(scenario.strategy?.params, "strategy", seen);
|
|
1731
|
+
// The initial state, vars included, reaches every run and the scenario hash. It must be JSON data too.
|
|
1732
|
+
if (!isJsonData(scenario.initial, seen))
|
|
1733
|
+
reject("scenario initial state must be JSON data, because the scenario hash records it as JSON");
|
|
1734
|
+
if (!isJsonData(scenario, new Set(), true))
|
|
1735
|
+
reject("scenario must be JSON data, because the scenario hash records it as JSON");
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* A private copy of JSON data with object keys in canonical order. A factory that mutates its params cannot reach
|
|
1739
|
+
* another run or the caller, and two params with one canonical JSON reach a factory with one key order.
|
|
1740
|
+
*/
|
|
1741
|
+
function cloneJsonData(value) {
|
|
1742
|
+
return value === undefined ? value : JSON.parse(canonicalJson(value));
|
|
1743
|
+
}
|
|
1744
|
+
function candidateBase(spec, scenario, registries) {
|
|
1745
|
+
if (scenario.model.id !== spec.model.id || scenario.model.version !== spec.model.version) {
|
|
1746
|
+
reject(`model ${spec.model.id}@${spec.model.version} does not match the scenario model ${scenario.model.id}@${scenario.model.version}`);
|
|
1747
|
+
}
|
|
1748
|
+
const scenarioStrategy = scenario.strategy;
|
|
1749
|
+
if (spec.strategy && scenarioStrategy && scenarioStrategy.id !== spec.strategy.id) {
|
|
1750
|
+
reject(`strategy ${spec.strategy.id} does not match the scenario strategy ${scenarioStrategy.id}`);
|
|
1751
|
+
}
|
|
1752
|
+
const strategyId = spec.strategy?.id ?? scenarioStrategy?.id ?? null;
|
|
1753
|
+
// An empty id is falsy, so the run plan would build no strategy while the task names one.
|
|
1754
|
+
if (strategyId === "")
|
|
1755
|
+
reject("strategy id must not be empty");
|
|
1756
|
+
if (strategyId === null) {
|
|
1757
|
+
if (spec.dimensions.some((dimension) => dimension.target === "strategy"))
|
|
1758
|
+
reject("strategy id is required");
|
|
1759
|
+
return { modelParams: scenario.model.params, strategyId: null, strategyParams: {} };
|
|
1760
|
+
}
|
|
1761
|
+
const strategyFactory = registries.strategyRegistry.get(strategyId);
|
|
1762
|
+
if (!strategyFactory)
|
|
1763
|
+
reject(`strategy not found: ${strategyId}`);
|
|
1764
|
+
const strategyParams = scenarioStrategy?.params ?? strategyFactory.defaultParams ?? {};
|
|
1765
|
+
// Factory defaults are checked on their own: they are not part of the scenario object.
|
|
1766
|
+
if (scenarioStrategy?.params === undefined)
|
|
1767
|
+
requireJsonParams(strategyParams, "strategy", new Set());
|
|
1768
|
+
return { modelParams: scenario.model.params, strategyId, strategyParams };
|
|
1769
|
+
}
|
|
1770
|
+
function prepareCandidate(spec, base, candidate, registries) {
|
|
1771
|
+
const dimensions = new Map(spec.dimensions.map((dimension) => [dimension.id, dimension]));
|
|
1772
|
+
const modelAssigned = [];
|
|
1773
|
+
const strategyAssigned = [];
|
|
1774
|
+
const applied = new Set();
|
|
1775
|
+
for (const assignment of candidate.assignments) {
|
|
1776
|
+
const dimension = dimensions.get(assignment.dimensionId);
|
|
1777
|
+
if (!dimension)
|
|
1778
|
+
reject(`unknown dimension ${assignment.dimensionId}`);
|
|
1779
|
+
if (applied.has(dimension.id))
|
|
1780
|
+
reject(`duplicate assignment ${dimension.id}`);
|
|
1781
|
+
applied.add(dimension.id);
|
|
1782
|
+
assertRegisteredPath(dimension.path, dimension.target);
|
|
1783
|
+
if (!assignmentFitsParamSpace(dimension.space, assignment.value)) {
|
|
1784
|
+
reject(`value for ${dimension.id} is outside its parameter space`);
|
|
1785
|
+
}
|
|
1786
|
+
if (dimension.target === "model")
|
|
1787
|
+
modelAssigned.push({ dimension, value: assignment.value });
|
|
1788
|
+
else if (dimension.target === "strategy")
|
|
1789
|
+
strategyAssigned.push({ dimension, value: assignment.value });
|
|
1790
|
+
else
|
|
1791
|
+
reject(`session dimension ${dimension.id} is not executed`);
|
|
1792
|
+
}
|
|
1793
|
+
for (const dimension of spec.dimensions) {
|
|
1794
|
+
if (!applied.has(dimension.id))
|
|
1795
|
+
reject(`candidate ${candidate.id} omits ${dimension.id}`);
|
|
1796
|
+
}
|
|
1797
|
+
// Assignments apply in dimension id order, the order behind the effective spec hash, so a factory sees one key order.
|
|
1798
|
+
const assign = (params, assigned, label) => assigned.length === 0
|
|
1799
|
+
? params
|
|
1800
|
+
: [...assigned].sort((left, right) => byText(left.dimension.id, right.dimension.id)).reduce((acc, { dimension, value }) => setRegisteredParam(acc, dimension.path, value),
|
|
1801
|
+
// setRegisteredParam copies the root with its prototype, so the scenario object is not spread here.
|
|
1802
|
+
asParamRecord(params, label));
|
|
1803
|
+
const modelFactory = registries.modelRegistry.get(spec.model.id, spec.model.version);
|
|
1804
|
+
if (!modelFactory)
|
|
1805
|
+
reject(`model not found: ${spec.model.id}@${spec.model.version}`);
|
|
1806
|
+
const modelParams = assign(base.modelParams, modelAssigned, "model");
|
|
1807
|
+
checkCandidateParams(modelFactory.paramsSchema, base.modelParams, modelParams, modelAssigned, "model");
|
|
1808
|
+
let strategyParams = {};
|
|
1809
|
+
if (base.strategyId !== null) {
|
|
1810
|
+
const strategyFactory = registries.strategyRegistry.get(base.strategyId);
|
|
1811
|
+
if (!strategyFactory)
|
|
1812
|
+
reject(`strategy not found: ${base.strategyId}`);
|
|
1813
|
+
strategyParams = assign(base.strategyParams, strategyAssigned, "strategy");
|
|
1814
|
+
checkCandidateParams(strategyFactory.paramsSchema, base.strategyParams, strategyParams, strategyAssigned, "strategy");
|
|
1815
|
+
}
|
|
1816
|
+
// Private snapshots: the task records exactly these, and every run gets its own copy of them.
|
|
1817
|
+
return {
|
|
1818
|
+
id: candidate.id,
|
|
1819
|
+
modelParams: cloneJsonData(modelParams),
|
|
1820
|
+
strategyParams: cloneJsonData(strategyParams),
|
|
1821
|
+
sessionPattern: spec.session.pattern,
|
|
1822
|
+
sessionDays: spec.session.days,
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
function metricIds(spec) {
|
|
1826
|
+
const ids = new Set([spec.metricId]);
|
|
1827
|
+
for (const objective of spec.objectives ?? [])
|
|
1828
|
+
ids.add(objective.metricId);
|
|
1829
|
+
for (const constraint of spec.constraints ?? [])
|
|
1830
|
+
ids.add(constraint.metricId);
|
|
1831
|
+
return [...ids];
|
|
1832
|
+
}
|
|
1833
|
+
function readBenefit(vars) {
|
|
1834
|
+
if (vars === null || typeof vars !== "object")
|
|
1835
|
+
return null;
|
|
1836
|
+
const value = vars.benefit;
|
|
1837
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1838
|
+
}
|
|
1839
|
+
/** Metrics at the horizon. The reach time is the first committed state at or above the target, read on the way. */
|
|
1840
|
+
function observeMetrics(E, spec, end, startT, reachedAt,
|
|
1841
|
+
/** The runner elapsed clock. A censored reach time is later than it. */
|
|
1842
|
+
elapsedSec) {
|
|
1843
|
+
const amount = end.wallet.money.amount;
|
|
1844
|
+
const benefit = readBenefit(end.vars);
|
|
1845
|
+
return metricIds(spec).map((metricId) => {
|
|
1846
|
+
if (metricId === "timeToMoney") {
|
|
1847
|
+
if (reachedAt === undefined) {
|
|
1848
|
+
return { metricId, status: "censored", value: null, lowerBound: { sec: elapsedSec, strict: true } };
|
|
1849
|
+
}
|
|
1850
|
+
const elapsed = reachedAt - startT;
|
|
1851
|
+
return { metricId, status: "observed", value: String(elapsed), num: elapsed };
|
|
1852
|
+
}
|
|
1853
|
+
if (metricId === "endMoney") {
|
|
1854
|
+
if (!E.isFinite(amount))
|
|
1855
|
+
return { metricId, status: "censored", value: null };
|
|
1856
|
+
return { metricId, status: "observed", value: E.toString(amount), amount };
|
|
1857
|
+
}
|
|
1858
|
+
if (benefit === null)
|
|
1859
|
+
return { metricId, status: "censored", value: null };
|
|
1860
|
+
return { metricId, status: "observed", value: String(benefit), num: benefit };
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
function failedMetrics(spec) {
|
|
1864
|
+
return metricIds(spec).map((metricId) => ({ metricId, status: "failed", value: null }));
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Arithmetic mean over every seed. One failed seed fails the candidate metric and one censored seed censors it,
|
|
1868
|
+
* so a mean never mixes a missing draw with observed ones. Money is averaged in the engine.
|
|
1869
|
+
*/
|
|
1870
|
+
function aggregate(E, rows, metricId) {
|
|
1871
|
+
const picked = rows.map((row) => row.metrics.find((metric) => metric.metricId === metricId));
|
|
1872
|
+
if (picked.some((metric) => metric === undefined || metric.status === "failed")) {
|
|
1873
|
+
return { metricId, status: "failed", value: null };
|
|
1874
|
+
}
|
|
1875
|
+
if (picked.some((metric) => metric.status !== "observed" || metric.value === null)) {
|
|
1876
|
+
// Every seed is either observed or censored with a known lower bound, so the mean has a lower bound too.
|
|
1877
|
+
const bounded = picked.every((metric) => metric.num !== undefined || metric.lowerBound !== undefined);
|
|
1878
|
+
if (metricId !== "timeToMoney" || !bounded)
|
|
1879
|
+
return { metricId, status: "censored", value: null };
|
|
1880
|
+
const bounds = picked.map((metric) => metric.num ?? metric.lowerBound.sec);
|
|
1881
|
+
const total = bounds.reduce((sum, bound) => sum + bound, 0);
|
|
1882
|
+
const sec = Number.isFinite(total) ? total / bounds.length : scaledMean(bounds);
|
|
1883
|
+
const strict = picked.some((metric) => metric.lowerBound?.strict === true);
|
|
1884
|
+
return { metricId, status: "censored", value: null, lowerBound: { sec, strict } };
|
|
1885
|
+
}
|
|
1886
|
+
const observed = picked;
|
|
1887
|
+
const count = observed.length;
|
|
1888
|
+
if (metricId === "endMoney") {
|
|
1889
|
+
// Sum, then divide, so subnormal amounts keep their mean. Only when the sum leaves the engine range,
|
|
1890
|
+
// divide before adding, so large finite amounts keep a finite mean.
|
|
1891
|
+
const sum = observed.reduce((total, metric) => E.add(total, metric.amount), E.zero());
|
|
1892
|
+
const mean = E.isFinite(sum) ? E.div(sum, count) : scaledEngineMean(E, observed.map((metric) => metric.amount));
|
|
1893
|
+
if (!E.isFinite(mean))
|
|
1894
|
+
return { metricId, status: "censored", value: null };
|
|
1895
|
+
return { metricId, status: "observed", value: E.toString(mean), amount: mean };
|
|
1896
|
+
}
|
|
1897
|
+
// Sum, then divide, so subnormal values keep their mean. Only when the sum overflows, divide before adding,
|
|
1898
|
+
// so two finite values near the double limit keep a finite mean.
|
|
1899
|
+
const values = observed.map((metric) => metric.num);
|
|
1900
|
+
const sum = values.reduce((total, value) => total + value, 0);
|
|
1901
|
+
const mean = Number.isFinite(sum) ? sum / count : scaledMean(values);
|
|
1902
|
+
if (!Number.isFinite(mean))
|
|
1903
|
+
return { metricId, status: "censored", value: null };
|
|
1904
|
+
return { metricId, status: "observed", value: String(mean), num: mean };
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Mean of finite values whose sum overflows. Each value is divided by the largest magnitude, so every scaled
|
|
1908
|
+
* term is at most 1 and the scaled sum stays finite. The mean of the scaled terms is then scaled back.
|
|
1909
|
+
*/
|
|
1910
|
+
function scaledMean(values) {
|
|
1911
|
+
const scale = values.reduce((largest, value) => Math.max(largest, Math.abs(value)), 0);
|
|
1912
|
+
return scale * (values.reduce((total, value) => total + value / scale, 0) / values.length);
|
|
1913
|
+
}
|
|
1914
|
+
function scaledEngineMean(E, values) {
|
|
1915
|
+
const scale = values.reduce((largest, value) => (E.absLog10(value) > E.absLog10(largest) ? value : largest), values[0]);
|
|
1916
|
+
const magnitude = E.cmp(scale, E.zero()) < 0 ? E.sub(E.zero(), scale) : scale;
|
|
1917
|
+
const scaled = values.reduce((total, value) => E.add(total, E.divN(value, magnitude)), E.zero());
|
|
1918
|
+
return E.mulN(magnitude, E.div(scaled, values.length));
|
|
1919
|
+
}
|
|
1920
|
+
function isObserved(reading) {
|
|
1921
|
+
return reading !== undefined && reading.status === "observed" && (reading.amount !== undefined || reading.num !== undefined);
|
|
1922
|
+
}
|
|
1923
|
+
function compareReadings(E, left, right) {
|
|
1924
|
+
if (left.amount !== undefined && right.amount !== undefined)
|
|
1925
|
+
return E.cmp(left.amount, right.amount);
|
|
1926
|
+
const a = left.num;
|
|
1927
|
+
const b = right.num;
|
|
1928
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1929
|
+
}
|
|
1930
|
+
function violates(E, reading, constraint) {
|
|
1931
|
+
const order = reading.amount !== undefined
|
|
1932
|
+
? E.cmp(reading.amount, E.from(constraint.limit))
|
|
1933
|
+
: reading.num < constraint.limit ? -1 : reading.num > constraint.limit ? 1 : 0;
|
|
1934
|
+
return constraint.op === "lte" ? order > 0 : order < 0;
|
|
1935
|
+
}
|
|
1936
|
+
function readingElasticity(E, baselineP, probeP, baseline, probe) {
|
|
1937
|
+
if (!isObserved(baseline) || !isObserved(probe))
|
|
1938
|
+
return localElasticity({ baselineP, probeP, baselineY: null, probeY: null });
|
|
1939
|
+
if (baseline.amount !== undefined && probe.amount !== undefined) {
|
|
1940
|
+
const delta = E.sub(probe.amount, baseline.amount);
|
|
1941
|
+
// Two finite amounts of opposite sign can differ by more than the engine range. Their ratio stays finite.
|
|
1942
|
+
const finiteDelta = E.isFinite(delta);
|
|
1943
|
+
const order = E.cmp(probe.amount, baseline.amount);
|
|
1944
|
+
const unavailable = elasticityFromChange({
|
|
1945
|
+
baselineP,
|
|
1946
|
+
probeP,
|
|
1947
|
+
absoluteChange: finiteDelta ? E.toString(delta) : null,
|
|
1948
|
+
direction: order > 0 ? "higher" : order < 0 ? "lower" : "flat",
|
|
1949
|
+
relativeChange: null,
|
|
1950
|
+
});
|
|
1951
|
+
const pChanged = Number.isFinite(baselineP) && Number.isFinite(probeP) && probeP !== baselineP;
|
|
1952
|
+
if (!(E.cmp(baseline.amount, E.zero()) > 0) || !(baselineP > 0) || !pChanged)
|
|
1953
|
+
return unavailable;
|
|
1954
|
+
// The ratio of two engine amounts can leave the double range, so the elasticity stays in the engine until it is text.
|
|
1955
|
+
const relative = finiteDelta ? E.divN(delta, baseline.amount) : E.sub(E.divN(probe.amount, baseline.amount), E.from(1));
|
|
1956
|
+
// A tiny positive baseline can make the parameter change overflow. Dividing by it would report a false 0.
|
|
1957
|
+
const parameterChange = relativeParameterChange(baselineP, probeP);
|
|
1958
|
+
if (!Number.isFinite(parameterChange) || parameterChange === 0)
|
|
1959
|
+
return unavailable;
|
|
1960
|
+
const elasticity = E.div(relative, parameterChange);
|
|
1961
|
+
if (!E.isFinite(elasticity))
|
|
1962
|
+
return unavailable;
|
|
1963
|
+
return { ...unavailable, status: "observed", elasticity: E.toString(elasticity) };
|
|
1964
|
+
}
|
|
1965
|
+
return localElasticity({ baselineP, probeP, baselineY: baseline.num ?? null, probeY: probe.num ?? null });
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* Run options every candidate inherits from the compiled scenario.
|
|
1969
|
+
* A scenario stop condition or step budget would end a candidate before the spec horizon, and a report trace
|
|
1970
|
+
* would keep every state of every candidate and seed while the experiment reads only the end state.
|
|
1971
|
+
*
|
|
1972
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-censored-horizon Removes the scenario stop condition, step budget, and trace, so each candidate runs to the horizon without retaining states.
|
|
1973
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-censored-horizon #befcebb Re-read the section: until, maxSteps, and trace are removed and the rest of the run options stay.
|
|
1974
|
+
*/
|
|
1975
|
+
export function experimentRunBase(run) {
|
|
1976
|
+
const { until: _until, maxSteps: _maxSteps, trace: _trace, ...horizonRun } = run;
|
|
1977
|
+
return horizonRun;
|
|
1978
|
+
}
|
|
1979
|
+
/** Every rejection that does not need a model instance. No factory `create` runs here. */
|
|
1980
|
+
function prepareExperiment(request) {
|
|
1981
|
+
// Validate the spec shape and the execution state first, so an untyped caller gets ExperimentRejected
|
|
1982
|
+
// rather than a TypeError from hashing, for example on a bigint.
|
|
1983
|
+
const spec = requireSpec(request.spec);
|
|
1984
|
+
const metrics = metricIds(spec);
|
|
1985
|
+
// A JSON literal such as 1e400 parses to Infinity, and canonical JSON would hash it as null.
|
|
1986
|
+
if (spec.goal !== undefined && !Number.isFinite(spec.goal.target))
|
|
1987
|
+
reject("goal target must be finite");
|
|
1988
|
+
if (metrics.includes("timeToMoney") && spec.goal === undefined)
|
|
1989
|
+
reject("timeToMoney requires a finite goal target");
|
|
1990
|
+
// Every spec field the run reads must be one both spec hashes record.
|
|
1991
|
+
if (!isJsonData(request.spec, new Set(), true))
|
|
1992
|
+
reject("spec must be JSON data, because the spec hashes record it as JSON");
|
|
1993
|
+
requireScenarioData(request.scenario);
|
|
1994
|
+
let scenarioHash;
|
|
1995
|
+
try {
|
|
1996
|
+
scenarioHash = sha256Text(canonicalJson(request.scenario));
|
|
1997
|
+
}
|
|
1998
|
+
catch {
|
|
1999
|
+
reject("scenario cannot be written as canonical JSON");
|
|
2000
|
+
}
|
|
2001
|
+
if (scenarioHash !== spec.scenario.hash)
|
|
2002
|
+
reject("scenario hash does not match the experiment spec");
|
|
2003
|
+
// Both spec hashes are taken now, so a field canonical JSON cannot write fails before any model is created.
|
|
2004
|
+
let specHashes;
|
|
2005
|
+
try {
|
|
2006
|
+
specHashes = { original: sha256Text(canonicalJson(request.spec)), effective: sha256Text(canonicalJson(normalizeSpec(spec))) };
|
|
2007
|
+
}
|
|
2008
|
+
catch {
|
|
2009
|
+
reject("spec cannot be written as canonical JSON");
|
|
2010
|
+
}
|
|
2011
|
+
const base = candidateBase(spec, request.scenario, request);
|
|
2012
|
+
const prepared = plannedCandidates(spec).map((candidate) => prepareCandidate(spec, base, candidate, request));
|
|
2013
|
+
return { spec, metrics, base, prepared, specHashes };
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Runs every spec, scenario-hash, path, and parameter check of `runExperiment` without creating a model.
|
|
2017
|
+
* A caller that compiles or opens a run first calls this, so a rejected spec never reaches a factory.
|
|
2018
|
+
*
|
|
2019
|
+
* @evidence docs/requirements/active/parameter-experiments.md#req-pr14-registered-setter Throws the same rejections as runExperiment before any model create.
|
|
2020
|
+
* @evidenceReview docs/requirements/active/parameter-experiments.md#req-pr14-registered-setter #847f7d0 Re-read the section: the hash, spec, path, and schema checks run and no factory create is called.
|
|
2021
|
+
*/
|
|
2022
|
+
export function checkExperimentSpec(request) {
|
|
2023
|
+
prepareExperiment(request);
|
|
2024
|
+
}
|
|
2025
|
+
export function runExperiment(request) {
|
|
2026
|
+
const { spec, metrics, base, prepared, specHashes } = prepareExperiment(request);
|
|
2027
|
+
const measuresReach = metrics.includes("timeToMoney");
|
|
2028
|
+
const E = request.engine;
|
|
2029
|
+
// The plan builds the strategy, so compile does not build the scenario one.
|
|
2030
|
+
const { strategy: _scenarioStrategy, ...withoutStrategy } = request.scenario;
|
|
2031
|
+
// Compile reads copies, so its model create cannot write the caller's params or initial state.
|
|
2032
|
+
const compiled = compileScenario({
|
|
2033
|
+
E,
|
|
2034
|
+
scenario: {
|
|
2035
|
+
...withoutStrategy,
|
|
2036
|
+
model: { ...withoutStrategy.model, params: cloneJsonData(withoutStrategy.model.params) },
|
|
2037
|
+
initial: cloneJsonData(withoutStrategy.initial),
|
|
2038
|
+
},
|
|
2039
|
+
registry: request.modelRegistry,
|
|
2040
|
+
strategyRegistry: request.strategyRegistry,
|
|
2041
|
+
opts: { allowSuffixNotation: true },
|
|
2042
|
+
});
|
|
2043
|
+
const shared = { ...compiled, run: experimentRunBase(compiled.run) };
|
|
2044
|
+
const bindingFor = createRunFactory({
|
|
2045
|
+
models: request.modelRegistry,
|
|
2046
|
+
strategies: request.strategyRegistry,
|
|
2047
|
+
});
|
|
2048
|
+
const target = measuresReach && spec.goal !== undefined ? E.from(spec.goal.target) : undefined;
|
|
2049
|
+
// Stopping at the goal is safe only when no other metric reads the horizon state.
|
|
2050
|
+
const stopAtReach = metrics.every((metricId) => metricId === "timeToMoney");
|
|
2051
|
+
const tasks = [];
|
|
2052
|
+
const perCandidate = [];
|
|
2053
|
+
for (const candidate of prepared) {
|
|
2054
|
+
const rows = [];
|
|
2055
|
+
for (const seed of spec.seeds) {
|
|
2056
|
+
// Each seed binds its own copy of the params, so a factory that mutates them cannot change a later seed.
|
|
2057
|
+
const binding = bindingFor.bind(shared, {
|
|
2058
|
+
statefulModel: true,
|
|
2059
|
+
model: { id: spec.model.id, version: spec.model.version, params: cloneJsonData(candidate.modelParams) },
|
|
2060
|
+
});
|
|
2061
|
+
const taskId = `${candidate.id}#${seed}`;
|
|
2062
|
+
const plan = {
|
|
2063
|
+
contract: "idlekit.execution-plan",
|
|
2064
|
+
version: 1,
|
|
2065
|
+
stepSec: spec.stepSec,
|
|
2066
|
+
durationSec: spec.horizonSec,
|
|
2067
|
+
seed,
|
|
2068
|
+
...(base.strategyId !== null ? { strategyId: base.strategyId, strategyParams: cloneJsonData(candidate.strategyParams) } : {}),
|
|
2069
|
+
eventLog: { enabled: false, maxEvents: 0 },
|
|
2070
|
+
};
|
|
2071
|
+
let readings;
|
|
2072
|
+
try {
|
|
2073
|
+
const opened = binding.fresh({ trialId: taskId, seed, plan });
|
|
2074
|
+
const scenario = opened.scenario;
|
|
2075
|
+
let reachedAt;
|
|
2076
|
+
const result = runScenario({
|
|
2077
|
+
...scenario,
|
|
2078
|
+
run: {
|
|
2079
|
+
...scenario.run,
|
|
2080
|
+
...(target !== undefined
|
|
2081
|
+
? {
|
|
2082
|
+
until: (state) => {
|
|
2083
|
+
if (reachedAt === undefined && E.cmp(state.wallet.money.amount, target) >= 0)
|
|
2084
|
+
reachedAt = state.t;
|
|
2085
|
+
return stopAtReach && reachedAt !== undefined;
|
|
2086
|
+
},
|
|
2087
|
+
}
|
|
2088
|
+
: {}),
|
|
2089
|
+
},
|
|
2090
|
+
});
|
|
2091
|
+
readings = observeMetrics(E, spec, result.end, result.start.t, reachedAt, runElapsedSec(result));
|
|
2092
|
+
}
|
|
2093
|
+
catch {
|
|
2094
|
+
readings = failedMetrics(spec);
|
|
2095
|
+
}
|
|
2096
|
+
const primary = readings.find((metric) => metric.metricId === spec.metricId) ?? failedMetrics(spec)[0];
|
|
2097
|
+
tasks.push({
|
|
2098
|
+
taskId,
|
|
2099
|
+
candidateId: candidate.id,
|
|
2100
|
+
logicalSeed: seed,
|
|
2101
|
+
engineId: request.engineId,
|
|
2102
|
+
engineVersion: request.engineVersion,
|
|
2103
|
+
modelId: spec.model.id,
|
|
2104
|
+
modelVersion: spec.model.version,
|
|
2105
|
+
strategyId: base.strategyId,
|
|
2106
|
+
modelParams: canonicalJson(candidate.modelParams ?? null),
|
|
2107
|
+
strategyParams: canonicalJson(candidate.strategyParams ?? null),
|
|
2108
|
+
sessionPattern: candidate.sessionPattern,
|
|
2109
|
+
sessionDays: candidate.sessionDays,
|
|
2110
|
+
horizonSec: spec.horizonSec,
|
|
2111
|
+
stepSec: spec.stepSec,
|
|
2112
|
+
metricId: spec.metricId,
|
|
2113
|
+
status: primary.status,
|
|
2114
|
+
value: primary.value,
|
|
2115
|
+
});
|
|
2116
|
+
rows.push({ seed, metrics: readings });
|
|
2117
|
+
}
|
|
2118
|
+
perCandidate.push({ id: candidate.id, rows });
|
|
2119
|
+
}
|
|
2120
|
+
const measured = perCandidate.map((candidate) => {
|
|
2121
|
+
const readings = metrics.map((metricId) => aggregate(E, candidate.rows, metricId));
|
|
2122
|
+
const constraintViolations = (spec.constraints ?? [])
|
|
2123
|
+
.filter((constraint) => {
|
|
2124
|
+
const reading = readings.find((metric) => metric.metricId === constraint.metricId);
|
|
2125
|
+
// A censored reach time is later than its lower bound. That proves an lte violation once the bound passes the limit.
|
|
2126
|
+
const bound = reading !== undefined && reading.status === "censored" ? reading.lowerBound : undefined;
|
|
2127
|
+
if (isObserved(reading))
|
|
2128
|
+
return violates(E, reading, constraint);
|
|
2129
|
+
return constraint.op === "lte" && bound !== undefined && (bound.sec > constraint.limit || (bound.strict && bound.sec >= constraint.limit));
|
|
2130
|
+
})
|
|
2131
|
+
.map((constraint) => constraint.id);
|
|
2132
|
+
return { id: candidate.id, readings, constraintViolations };
|
|
2133
|
+
});
|
|
2134
|
+
const baseline = measured.find((candidate) => candidate.id === "baseline");
|
|
2135
|
+
const elasticities = baseline
|
|
2136
|
+
? // The normalized order, so reordered dimensions with one effective hash give one artifact.
|
|
2137
|
+
[...spec.dimensions].sort((left, right) => byText(left.id, right.id)).flatMap((dimension) => (dimension.probes ?? []).flatMap((probe) => {
|
|
2138
|
+
const candidateId = probeCandidateId(dimension, probe);
|
|
2139
|
+
const probeCandidate = measured.find((candidate) => candidate.id === candidateId);
|
|
2140
|
+
if (!probeCandidate)
|
|
2141
|
+
return [];
|
|
2142
|
+
return metrics.map((metricId) => {
|
|
2143
|
+
const local = readingElasticity(E, typeof dimension.baseline === "number" ? dimension.baseline : Number.NaN, typeof probe === "number" ? probe : Number.NaN, baseline.readings.find((metric) => metric.metricId === metricId), probeCandidate.readings.find((metric) => metric.metricId === metricId));
|
|
2144
|
+
return {
|
|
2145
|
+
dimensionId: dimension.id,
|
|
2146
|
+
candidateId,
|
|
2147
|
+
metricId,
|
|
2148
|
+
method: "one-at-a-time-local",
|
|
2149
|
+
methodVersion: 1,
|
|
2150
|
+
globalSensitivity: false,
|
|
2151
|
+
sobol: false,
|
|
2152
|
+
status: local.status,
|
|
2153
|
+
elasticity: local.elasticity,
|
|
2154
|
+
absoluteChange: local.absoluteChange,
|
|
2155
|
+
direction: local.direction,
|
|
2156
|
+
};
|
|
2157
|
+
});
|
|
2158
|
+
}))
|
|
2159
|
+
: [];
|
|
2160
|
+
const objectives = spec.objectives ?? [];
|
|
2161
|
+
// Each pair of candidates is compared in the engine. Ranks would chain the engine tolerance through a third value.
|
|
2162
|
+
const pareto = objectives.length === 0
|
|
2163
|
+
? { sense: "per-metric", nonDominated: [], uncompared: [] }
|
|
2164
|
+
: paretoFrontBy(measured.map((candidate) => ({
|
|
2165
|
+
id: candidate.id,
|
|
2166
|
+
objectives: objectives.map((objective) => {
|
|
2167
|
+
const reading = candidate.readings.find((metric) => metric.metricId === objective.metricId);
|
|
2168
|
+
return { sense: objective.sense, value: isObserved(reading) ? reading : null };
|
|
2169
|
+
}),
|
|
2170
|
+
})), (left, right) => compareReadings(E, left, right));
|
|
2171
|
+
const candidates = measured.map((candidate) => ({
|
|
2172
|
+
id: candidate.id,
|
|
2173
|
+
metrics: candidate.readings.map(({ metricId, status, value }) => ({ metricId, status, value })),
|
|
2174
|
+
constraintViolations: candidate.constraintViolations,
|
|
2175
|
+
}));
|
|
2176
|
+
tasks.sort((left, right) => byText(left.candidateId, right.candidateId) || left.logicalSeed - right.logicalSeed);
|
|
2177
|
+
const artifact = {
|
|
2178
|
+
schemaVersion: 1,
|
|
2179
|
+
contract: "idlekit.experiment.v1",
|
|
2180
|
+
estimatorVersion: EXPERIMENT_ESTIMATOR_VERSION,
|
|
2181
|
+
algorithmVersion: EXPERIMENT_ALGORITHM_VERSION,
|
|
2182
|
+
method: EXPERIMENT_METHOD,
|
|
2183
|
+
globalSensitivity: false,
|
|
2184
|
+
sobol: false,
|
|
2185
|
+
scenario: spec.scenario,
|
|
2186
|
+
originalSpecHash: specHashes.original,
|
|
2187
|
+
effectiveSpecHash: specHashes.effective,
|
|
2188
|
+
effectiveRunHash: request.effectiveRunHash,
|
|
2189
|
+
budget: {
|
|
2190
|
+
candidates: spec.budget.candidates,
|
|
2191
|
+
seeds: spec.budget.seeds,
|
|
2192
|
+
usedCandidates: prepared.length,
|
|
2193
|
+
usedSeeds: spec.seeds.length,
|
|
2194
|
+
},
|
|
2195
|
+
tasks,
|
|
2196
|
+
candidates: [...candidates].sort((left, right) => byText(left.id, right.id)),
|
|
2197
|
+
elasticities,
|
|
2198
|
+
pareto,
|
|
2199
|
+
warnings: WARNINGS,
|
|
2200
|
+
};
|
|
2201
|
+
const checked = validateExperimentArtifact(artifact);
|
|
2202
|
+
if (!checked.success)
|
|
2203
|
+
reject("artifact failed its own structural validator");
|
|
2204
|
+
return artifact;
|
|
2205
|
+
}
|
|
2206
|
+
//# sourceMappingURL=experiment.js.map
|