@bramas/simulant-ui 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/README.md +17 -0
- package/dist/SimulatorApp.jsx +0 -1
- package/dist/sim/describe.js +10 -8
- package/dist/sim/project.js +27 -17
- package/package.json +6 -6
- package/src/SimulatorApp.tsx +0 -1
- package/src/sim/describe.ts +10 -8
- package/src/sim/project.ts +27 -19
package/README.md
CHANGED
|
@@ -45,6 +45,23 @@ Projects can supply a custom drawing function, agent names and aliases for
|
|
|
45
45
|
older parameter names. Settings outside declared domains produce advisory
|
|
46
46
|
warnings; execution remains available.
|
|
47
47
|
|
|
48
|
+
Parameter declarations describe model domains, using `in [...]` for exact finite
|
|
49
|
+
sets and `where` for properties. Starting values come from a bounded search,
|
|
50
|
+
with editable typed fallbacks when no match is found. Override them separately
|
|
51
|
+
in the project registry:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
simProject(model, 'cautiousPair', {
|
|
55
|
+
defaults: { N: 8, site: 3 },
|
|
56
|
+
checkSettings: () => [{ N: 5, site: 1 }, { N: 8, site: 3 }],
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`checkSettings` supplies finite settings for `check-all` when model domains
|
|
61
|
+
cannot be enumerated. Each setting must include every parameter; settings
|
|
62
|
+
outside the model domains are skipped. Without it, `check-all` enumerates the
|
|
63
|
+
model's finite domains. Defaults never restrict the model or select check settings.
|
|
64
|
+
|
|
48
65
|
Renderers receive the page's colors in `options.theme` (light or dark): draw
|
|
49
66
|
labels and marks with `theme.foreground` and `theme.muted` so that drawings stay
|
|
50
67
|
readable in both themes. `simulator.css` is the whole stylesheet; the interface
|
package/dist/SimulatorApp.jsx
CHANGED
|
@@ -335,7 +335,6 @@ export const SimulatorApp = (props) => {
|
|
|
335
335
|
return { baseModel, execution: current.execute(baseModel) };
|
|
336
336
|
}
|
|
337
337
|
catch (e) {
|
|
338
|
-
console.error('Project setup failed:', e);
|
|
339
338
|
setSetupLog(e instanceof Error ? e.message : String(e));
|
|
340
339
|
return { baseModel: null, execution: null };
|
|
341
340
|
}
|
package/dist/sim/describe.js
CHANGED
|
@@ -111,10 +111,10 @@ const HOOKS = ['beforeLook', 'beforeMove', 'afterMove'];
|
|
|
111
111
|
/** Above this many definitions, the algorithm's facts give their number rather than their names. */
|
|
112
112
|
const MANY_DEFS = 6;
|
|
113
113
|
/** Counts the settings of `check-all`, up to `cap`. */
|
|
114
|
-
function countSettings(
|
|
114
|
+
function countSettings(settings, cap = 10_000) {
|
|
115
115
|
let n = 0;
|
|
116
116
|
try {
|
|
117
|
-
for (const _ of
|
|
117
|
+
for (const _ of settings())
|
|
118
118
|
if (++n >= cap)
|
|
119
119
|
return `more than ${cap}`;
|
|
120
120
|
}
|
|
@@ -130,7 +130,7 @@ function lineTable(exp) {
|
|
|
130
130
|
return {};
|
|
131
131
|
return Object.fromEntries(Object.entries(l.cases).flatMap(([pc, e]) => e.e === 'lit' && typeof e.value === 'number' ? [[pc, e.value]] : []));
|
|
132
132
|
}
|
|
133
|
-
export function describeExperiment(exp, sources, revision, lean) {
|
|
133
|
+
export function describeExperiment(exp, sources, revision, lean, checkSettings) {
|
|
134
134
|
const { model, algorithm, task } = exp;
|
|
135
135
|
const key = (kind, name) => `${kind} ${name}`;
|
|
136
136
|
// The declarations as written. Without recorded uses (IR built otherwise), the algorithm alone.
|
|
@@ -232,9 +232,9 @@ export function describeExperiment(exp, sources, revision, lean) {
|
|
|
232
232
|
const i = expItem(new RegExp(`^\\s+param\\s+${p.name}\\b`));
|
|
233
233
|
return {
|
|
234
234
|
label: `param ${p.name}`,
|
|
235
|
-
value: p.domain ? i?.text ?? 'a domain' :
|
|
236
|
-
code: !!p.domain,
|
|
237
|
-
note: `of type ${showType(p.type)}`,
|
|
235
|
+
value: p.domain || p.accepts ? i?.text ?? 'a domain' : `all values of ${showType(p.type)}`,
|
|
236
|
+
code: !!p.domain || !!p.accepts,
|
|
237
|
+
note: `of type ${showType(p.type)}${p.accepts ? '; where restricts the domain' : ''}`,
|
|
238
238
|
refs: expRef(i),
|
|
239
239
|
};
|
|
240
240
|
});
|
|
@@ -248,7 +248,7 @@ export function describeExperiment(exp, sources, revision, lean) {
|
|
|
248
248
|
{ label: 'Agents', value: agents?.text ?? 'see the experiment', code: true, refs: expRef(agents) },
|
|
249
249
|
{ label: 'Positions', value: position?.text ?? 'see the experiment', code: true, note: 'Initial position of agent i.', refs: expRef(position) },
|
|
250
250
|
...params,
|
|
251
|
-
{ label: 'Settings checked', value: countSettings(exp), note: 'Parameter values enumerated by check-all, from the domains above.' },
|
|
251
|
+
{ label: 'Settings checked', value: countSettings(checkSettings ?? (() => parameterValues(exp))), note: checkSettings ? 'Finite settings supplied by project.ts, filtered through the model domains.' : 'Parameter values enumerated by check-all, from the domains above.' },
|
|
252
252
|
{
|
|
253
253
|
label: 'Limits',
|
|
254
254
|
value: exp.limits ? Object.entries(exp.limits).map(([k, v]) => `${k} ${v}`).join(', ') : 'none',
|
|
@@ -379,7 +379,9 @@ export function describeExperiment(exp, sources, revision, lean) {
|
|
|
379
379
|
library: lean.library.map(l => ({ module: l.module, text: l.text, refs: refTo('component', l.component) })),
|
|
380
380
|
};
|
|
381
381
|
const caveats = [
|
|
382
|
-
|
|
382
|
+
checkSettings
|
|
383
|
+
? 'Checks are bounded: they cover the finite settings supplied by project.ts, not every admissible parameter value.'
|
|
384
|
+
: 'Checks are bounded: they cover the settings enumerated from the parameter domains, not every ring size.',
|
|
383
385
|
...(exp.limits ? ['Exploration limits can leave a check incomplete.'] : []),
|
|
384
386
|
...(algorithm.native ? [`The algorithm's step is native TypeScript (${algorithm.native}): it is not written in .sim and is not translated to Lean.`] : []),
|
|
385
387
|
...(group.rotations ? ['Symmetry reduction relies on equivariance declarations, which are claims tested on bounded instances.'] : []),
|
package/dist/sim/project.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parameterDomainViolations, parameterValues, System } from "@bramas/simulant-engine/system";
|
|
1
|
+
import { parameterDefaults, parameterDomainViolations, parameterValues, System } from "@bramas/simulant-engine/system";
|
|
2
2
|
import { checkFirstBranch, checkSetting, checkSettings, stateGraph } from "@bramas/simulant-engine/check";
|
|
3
3
|
import { formatDiagnostic } from "@bramas/simulant-vite-plugin/module";
|
|
4
4
|
import { isRenderer } from "../renderers/define.js";
|
|
@@ -57,24 +57,15 @@ function fits(value, t) {
|
|
|
57
57
|
default: return true;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
/** The first parameter values of an experiment's domains (the form's defaults). */
|
|
61
|
-
function firstValues(exp) {
|
|
62
|
-
try {
|
|
63
|
-
return parameterValues(exp).next().value;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
60
|
/**
|
|
70
61
|
* Settings for a new definition of the experiment (plan, Section 6.7): values
|
|
71
62
|
* of parameters that still exist with a compatible type are kept; the others
|
|
72
63
|
* are reset to their defaults, and each reset is noted.
|
|
73
64
|
*/
|
|
74
|
-
export function adjustSettings(exp, settings) {
|
|
65
|
+
export function adjustSettings(exp, settings, defaults = {}) {
|
|
75
66
|
if (!settings)
|
|
76
67
|
return { settings, notes: [] };
|
|
77
|
-
const first =
|
|
68
|
+
const first = parameterDefaults(exp, defaults);
|
|
78
69
|
const notes = [];
|
|
79
70
|
const out = {};
|
|
80
71
|
for (const p of exp.params) {
|
|
@@ -129,6 +120,25 @@ export function algorithmSource(exp, sources) {
|
|
|
129
120
|
}
|
|
130
121
|
export function simProject(module, experiment, opts = {}) {
|
|
131
122
|
const draw = opts.draw ?? drawRing;
|
|
123
|
+
const defaultsOf = (exp) => Object.fromEntries(exp.params
|
|
124
|
+
.filter(p => opts.defaults && p.name in opts.defaults)
|
|
125
|
+
.map(p => [p.name, coerce(opts.defaults[p.name], p.type)]));
|
|
126
|
+
const coerceSettings = (exp, settings) => Object.fromEntries(exp.params
|
|
127
|
+
.filter(p => p.name in settings).map(p => [p.name, coerce(settings[p.name], p.type)]));
|
|
128
|
+
function* settingsFor(exp) {
|
|
129
|
+
if (!opts.checkSettings) {
|
|
130
|
+
yield* parameterValues(exp);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
for (const given of opts.checkSettings(exp)) {
|
|
134
|
+
const params = coerceSettings(exp, renameSettings(given, opts.aliases));
|
|
135
|
+
for (const p of exp.params)
|
|
136
|
+
if (!(p.name in params))
|
|
137
|
+
throw new Error(`checkSettings is missing parameter ${p.name}`);
|
|
138
|
+
if (!parameterDomainViolations(exp, params).length)
|
|
139
|
+
yield params;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
132
142
|
const lazy = (f) => {
|
|
133
143
|
let done = false, value;
|
|
134
144
|
return () => { if (!done) {
|
|
@@ -140,7 +150,7 @@ export function simProject(module, experiment, opts = {}) {
|
|
|
140
150
|
const exp = m.experiments[experiment];
|
|
141
151
|
if (!exp)
|
|
142
152
|
return undefined;
|
|
143
|
-
const first = lazy(() =>
|
|
153
|
+
const first = lazy(() => parameterDefaults(exp, defaultsOf(exp)));
|
|
144
154
|
// Line information only affects the algorithm and model views: an edit that only moves lines redraws.
|
|
145
155
|
const { line: _line, source: _source, ...algorithm } = exp.algorithm;
|
|
146
156
|
const { source: _expSource, ...rest } = exp;
|
|
@@ -148,7 +158,7 @@ export function simProject(module, experiment, opts = {}) {
|
|
|
148
158
|
// Lines of expressions (`at`) are display only, like the line table.
|
|
149
159
|
exp, key: JSON.stringify({ ...rest, algorithm }, (k, v) => k === 'at' ? undefined : v), first,
|
|
150
160
|
source: algorithmSource(exp, m.sources ?? {}),
|
|
151
|
-
description: lazy(() => describeExperiment(exp, m.sources ?? {}, m.revision, m.lean?.[experiment])),
|
|
161
|
+
description: lazy(() => describeExperiment(exp, m.sources ?? {}, m.revision, m.lean?.[experiment], opts.checkSettings && (() => settingsFor(exp)))),
|
|
152
162
|
editorSchema: lazy(() => ({ fields: exp.params.map(p => field(p.name, p.type, first()?.[p.name])) })),
|
|
153
163
|
};
|
|
154
164
|
};
|
|
@@ -178,7 +188,7 @@ export function simProject(module, experiment, opts = {}) {
|
|
|
178
188
|
return;
|
|
179
189
|
}
|
|
180
190
|
else
|
|
181
|
-
emit({ kind: 'replaced', revision, adjust: settings => adjustSettings(loaded.exp, renameSettings(settings, opts.aliases)) });
|
|
191
|
+
emit({ kind: 'replaced', revision, adjust: settings => adjustSettings(loaded.exp, renameSettings(settings, opts.aliases), defaultsOf(loaded.exp)) });
|
|
182
192
|
});
|
|
183
193
|
if (isRenderer(draw))
|
|
184
194
|
draw.subscribe(() => emit({ kind: 'redraw' }));
|
|
@@ -257,7 +267,7 @@ export function simProject(module, experiment, opts = {}) {
|
|
|
257
267
|
checkAll: async (o = {}, onProgress) => {
|
|
258
268
|
const rev = revision;
|
|
259
269
|
const { exp } = need();
|
|
260
|
-
return report(await checkSettings(exp,
|
|
270
|
+
return report(await checkSettings(exp, settingsFor(exp), {
|
|
261
271
|
...limits(o), shared: !o.perSetting, maxConfigs: o.maxConfigs,
|
|
262
272
|
}, o.signal && { signal: o.signal }, onProgress), rev);
|
|
263
273
|
},
|
|
@@ -300,7 +310,7 @@ export function simProject(module, experiment, opts = {}) {
|
|
|
300
310
|
},
|
|
301
311
|
settingsGenerator: function* () {
|
|
302
312
|
if (current)
|
|
303
|
-
yield*
|
|
313
|
+
yield* settingsFor(current.exp);
|
|
304
314
|
},
|
|
305
315
|
get editorSchema() {
|
|
306
316
|
return current?.editorSchema() ?? { fields: [] };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bramas/simulant-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "CECILL-B",
|
|
6
6
|
"type": "module",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bramas/simulant-
|
|
35
|
-
"@bramas/simulant-
|
|
36
|
-
"@bramas/simulant-
|
|
37
|
-
"@bramas/simulant-
|
|
38
|
-
"@bramas/simulant-vite-plugin": "0.
|
|
34
|
+
"@bramas/simulant-lang": "0.3.0",
|
|
35
|
+
"@bramas/simulant-core": "0.3.0",
|
|
36
|
+
"@bramas/simulant-engine": "0.3.0",
|
|
37
|
+
"@bramas/simulant-lean-gen": "0.3.0",
|
|
38
|
+
"@bramas/simulant-vite-plugin": "0.3.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"solid-js": "^1.9.3"
|
package/src/SimulatorApp.tsx
CHANGED
|
@@ -371,7 +371,6 @@ export const SimulatorApp: Component<SimulatorAppProps> = (props) => {
|
|
|
371
371
|
setSetupLog('');
|
|
372
372
|
return { baseModel, execution: current.execute(baseModel) };
|
|
373
373
|
} catch (e) {
|
|
374
|
-
console.error('Project setup failed:', e);
|
|
375
374
|
setSetupLog(e instanceof Error ? e.message : String(e));
|
|
376
375
|
return { baseModel: null, execution: null };
|
|
377
376
|
}
|
package/src/sim/describe.ts
CHANGED
|
@@ -115,10 +115,10 @@ const HOOKS = ['beforeLook', 'beforeMove', 'afterMove'] as const;
|
|
|
115
115
|
const MANY_DEFS = 6;
|
|
116
116
|
|
|
117
117
|
/** Counts the settings of `check-all`, up to `cap`. */
|
|
118
|
-
function countSettings(
|
|
118
|
+
function countSettings(settings: () => Iterable<unknown>, cap = 10_000): string {
|
|
119
119
|
let n = 0;
|
|
120
120
|
try {
|
|
121
|
-
for (const _ of
|
|
121
|
+
for (const _ of settings()) if (++n >= cap) return `more than ${cap}`;
|
|
122
122
|
} catch {
|
|
123
123
|
return 'unknown (a domain cannot be enumerated)';
|
|
124
124
|
}
|
|
@@ -132,7 +132,7 @@ function lineTable(exp: Experiment): Record<string, number> {
|
|
|
132
132
|
return Object.fromEntries(Object.entries(l.cases).flatMap(([pc, e]) => e.e === 'lit' && typeof e.value === 'number' ? [[pc, e.value]] : []));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
export function describeExperiment(exp: Experiment, sources: Record<string, string>, revision?: string, lean?: SimLean): ModelDescription {
|
|
135
|
+
export function describeExperiment(exp: Experiment, sources: Record<string, string>, revision?: string, lean?: SimLean, checkSettings?: () => Iterable<unknown>): ModelDescription {
|
|
136
136
|
const { model, algorithm, task } = exp;
|
|
137
137
|
const key = (kind: string, name: string) => `${kind} ${name}`;
|
|
138
138
|
|
|
@@ -235,9 +235,9 @@ export function describeExperiment(exp: Experiment, sources: Record<string, stri
|
|
|
235
235
|
const i = expItem(new RegExp(`^\\s+param\\s+${p.name}\\b`));
|
|
236
236
|
return {
|
|
237
237
|
label: `param ${p.name}`,
|
|
238
|
-
value: p.domain ? i?.text ?? 'a domain' :
|
|
239
|
-
code: !!p.domain,
|
|
240
|
-
note: `of type ${showType(p.type)}`,
|
|
238
|
+
value: p.domain || p.accepts ? i?.text ?? 'a domain' : `all values of ${showType(p.type)}`,
|
|
239
|
+
code: !!p.domain || !!p.accepts,
|
|
240
|
+
note: `of type ${showType(p.type)}${p.accepts ? '; where restricts the domain' : ''}`,
|
|
241
241
|
refs: expRef(i),
|
|
242
242
|
};
|
|
243
243
|
});
|
|
@@ -252,7 +252,7 @@ export function describeExperiment(exp: Experiment, sources: Record<string, stri
|
|
|
252
252
|
{ label: 'Agents', value: agents?.text ?? 'see the experiment', code: true, refs: expRef(agents) },
|
|
253
253
|
{ label: 'Positions', value: position?.text ?? 'see the experiment', code: true, note: 'Initial position of agent i.', refs: expRef(position) },
|
|
254
254
|
...params,
|
|
255
|
-
{ label: 'Settings checked', value: countSettings(exp), note: 'Parameter values enumerated by check-all, from the domains above.' },
|
|
255
|
+
{ label: 'Settings checked', value: countSettings(checkSettings ?? (() => parameterValues(exp))), note: checkSettings ? 'Finite settings supplied by project.ts, filtered through the model domains.' : 'Parameter values enumerated by check-all, from the domains above.' },
|
|
256
256
|
{
|
|
257
257
|
label: 'Limits',
|
|
258
258
|
value: exp.limits ? Object.entries(exp.limits).map(([k, v]) => `${k} ${v}`).join(', ') : 'none',
|
|
@@ -386,7 +386,9 @@ export function describeExperiment(exp: Experiment, sources: Record<string, stri
|
|
|
386
386
|
};
|
|
387
387
|
|
|
388
388
|
const caveats = [
|
|
389
|
-
|
|
389
|
+
checkSettings
|
|
390
|
+
? 'Checks are bounded: they cover the finite settings supplied by project.ts, not every admissible parameter value.'
|
|
391
|
+
: 'Checks are bounded: they cover the settings enumerated from the parameter domains, not every ring size.',
|
|
390
392
|
...(exp.limits ? ['Exploration limits can leave a check incomplete.'] : []),
|
|
391
393
|
...(algorithm.native ? [`The algorithm's step is native TypeScript (${algorithm.native}): it is not written in .sim and is not translated to Lean.`] : []),
|
|
392
394
|
...(group.rotations ? ['Symmetry reduction relies on equivariance declarations, which are claims tested on bounded instances.'] : []),
|
package/src/sim/project.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `draw`, the algorithm view from the algorithm's source and line table.
|
|
7
7
|
*/
|
|
8
8
|
import type { Experiment, Type } from '@bramas/simulant-lang/ir';
|
|
9
|
-
import { parameterDomainViolations, parameterValues, System } from '@bramas/simulant-engine/system';
|
|
9
|
+
import { parameterDefaults, parameterDomainViolations, parameterValues, System } from '@bramas/simulant-engine/system';
|
|
10
10
|
import { checkFirstBranch, checkSetting, checkSettings, stateGraph, type CheckResult } from '@bramas/simulant-engine/check';
|
|
11
11
|
import type { EditorField, EditorSchema } from '@bramas/simulant-core/types/editor-schema';
|
|
12
12
|
import type { AlgorithmSource, CheckOptions, ModelDescription, ProjectType, ProjectUpdate } from '@bramas/simulant-core/types/project';
|
|
@@ -41,6 +41,10 @@ export type SimProjectOptions = {
|
|
|
41
41
|
* `{ BH_node: 'site' }` reads `BH_node` as `site` when `site` is not given.
|
|
42
42
|
*/
|
|
43
43
|
aliases?: Record<string, string>;
|
|
44
|
+
/** Simulator starting values, independent of the model's parameter domains. */
|
|
45
|
+
defaults?: Record<string, unknown>;
|
|
46
|
+
/** Finite settings for check-all when the model's domains cannot be enumerated. */
|
|
47
|
+
checkSettings?: (experiment: Experiment) => Iterable<Record<string, unknown>>;
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
/** Settings with older parameter names renamed (see `SimProjectOptions.aliases`). */
|
|
@@ -86,23 +90,14 @@ function fits(value: unknown, t: Type): boolean {
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
/** The first parameter values of an experiment's domains (the form's defaults). */
|
|
90
|
-
function firstValues(exp: Experiment): Record<string, unknown> | undefined {
|
|
91
|
-
try {
|
|
92
|
-
return parameterValues(exp).next().value as Record<string, unknown> | undefined;
|
|
93
|
-
} catch {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
93
|
/**
|
|
99
94
|
* Settings for a new definition of the experiment (plan, Section 6.7): values
|
|
100
95
|
* of parameters that still exist with a compatible type are kept; the others
|
|
101
96
|
* are reset to their defaults, and each reset is noted.
|
|
102
97
|
*/
|
|
103
|
-
export function adjustSettings(exp: Experiment, settings: Record<string, unknown> | null): { settings: Record<string, unknown> | null; notes: string[] } {
|
|
98
|
+
export function adjustSettings(exp: Experiment, settings: Record<string, unknown> | null, defaults: Record<string, unknown> = {}): { settings: Record<string, unknown> | null; notes: string[] } {
|
|
104
99
|
if (!settings) return { settings, notes: [] };
|
|
105
|
-
const first =
|
|
100
|
+
const first = parameterDefaults(exp, defaults);
|
|
106
101
|
const notes: string[] = [];
|
|
107
102
|
const out: Record<string, unknown> = {};
|
|
108
103
|
for (const p of exp.params) {
|
|
@@ -153,12 +148,25 @@ export function algorithmSource(exp: Experiment, sources: Record<string, string>
|
|
|
153
148
|
|
|
154
149
|
export function simProject(module: SimSources, experiment: string, opts: SimProjectOptions = {}): ProjectType<any, any> {
|
|
155
150
|
const draw = opts.draw ?? drawRing;
|
|
151
|
+
const defaultsOf = (exp: Experiment) => Object.fromEntries(exp.params
|
|
152
|
+
.filter(p => opts.defaults && p.name in opts.defaults)
|
|
153
|
+
.map(p => [p.name, coerce(opts.defaults![p.name], p.type)]));
|
|
154
|
+
const coerceSettings = (exp: Experiment, settings: Record<string, unknown>) => Object.fromEntries(exp.params
|
|
155
|
+
.filter(p => p.name in settings).map(p => [p.name, coerce(settings[p.name], p.type)]));
|
|
156
|
+
function* settingsFor(exp: Experiment) {
|
|
157
|
+
if (!opts.checkSettings) { yield* parameterValues(exp); return; }
|
|
158
|
+
for (const given of opts.checkSettings(exp)) {
|
|
159
|
+
const params = coerceSettings(exp, renameSettings(given, opts.aliases));
|
|
160
|
+
for (const p of exp.params) if (!(p.name in params)) throw new Error(`checkSettings is missing parameter ${p.name}`);
|
|
161
|
+
if (!parameterDomainViolations(exp, params).length) yield params;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
156
164
|
|
|
157
165
|
// What the project runs: the last definition that compiled, replaced on hot updates.
|
|
158
166
|
type Current = {
|
|
159
167
|
exp: Experiment; key: string;
|
|
160
|
-
/**
|
|
161
|
-
first: () => Record<string, unknown
|
|
168
|
+
/** Defaults are computed lazily; explicit domains may take time to evaluate. */
|
|
169
|
+
first: () => Record<string, unknown>; editorSchema: () => EditorSchema;
|
|
162
170
|
source: AlgorithmSource;
|
|
163
171
|
description: () => ModelDescription;
|
|
164
172
|
};
|
|
@@ -169,7 +177,7 @@ export function simProject(module: SimSources, experiment: string, opts: SimProj
|
|
|
169
177
|
const load = (m: SimSources): Current | undefined => {
|
|
170
178
|
const exp = m.experiments[experiment];
|
|
171
179
|
if (!exp) return undefined;
|
|
172
|
-
const first = lazy(() =>
|
|
180
|
+
const first = lazy(() => parameterDefaults(exp, defaultsOf(exp)));
|
|
173
181
|
// Line information only affects the algorithm and model views: an edit that only moves lines redraws.
|
|
174
182
|
const { line: _line, source: _source, ...algorithm } = exp.algorithm;
|
|
175
183
|
const { source: _expSource, ...rest } = exp;
|
|
@@ -177,7 +185,7 @@ export function simProject(module: SimSources, experiment: string, opts: SimProj
|
|
|
177
185
|
// Lines of expressions (`at`) are display only, like the line table.
|
|
178
186
|
exp, key: JSON.stringify({ ...rest, algorithm }, (k, v) => k === 'at' ? undefined : v), first,
|
|
179
187
|
source: algorithmSource(exp, m.sources ?? {}),
|
|
180
|
-
description: lazy(() => describeExperiment(exp, m.sources ?? {}, m.revision, m.lean?.[experiment])),
|
|
188
|
+
description: lazy(() => describeExperiment(exp, m.sources ?? {}, m.revision, m.lean?.[experiment], opts.checkSettings && (() => settingsFor(exp)))),
|
|
181
189
|
editorSchema: lazy(() => ({ fields: exp.params.map(p => field(p.name, p.type, first()?.[p.name])) })),
|
|
182
190
|
};
|
|
183
191
|
};
|
|
@@ -206,7 +214,7 @@ export function simProject(module: SimSources, experiment: string, opts: SimProj
|
|
|
206
214
|
emit({ kind: 'redraw' });
|
|
207
215
|
return;
|
|
208
216
|
}
|
|
209
|
-
else emit({ kind: 'replaced', revision, adjust: settings => adjustSettings(loaded.exp, renameSettings(settings, opts.aliases)) });
|
|
217
|
+
else emit({ kind: 'replaced', revision, adjust: settings => adjustSettings(loaded.exp, renameSettings(settings, opts.aliases), defaultsOf(loaded.exp)) });
|
|
210
218
|
});
|
|
211
219
|
if (isRenderer(draw)) draw.subscribe(() => emit({ kind: 'redraw' }));
|
|
212
220
|
|
|
@@ -287,7 +295,7 @@ export function simProject(module: SimSources, experiment: string, opts: SimProj
|
|
|
287
295
|
checkAll: async (o = {}, onProgress) => {
|
|
288
296
|
const rev = revision;
|
|
289
297
|
const { exp } = need();
|
|
290
|
-
return report(await checkSettings(exp,
|
|
298
|
+
return report(await checkSettings(exp, settingsFor(exp), {
|
|
291
299
|
...limits(o), shared: !o.perSetting, maxConfigs: o.maxConfigs,
|
|
292
300
|
}, o.signal && { signal: o.signal }, onProgress), rev);
|
|
293
301
|
},
|
|
@@ -328,7 +336,7 @@ export function simProject(module: SimSources, experiment: string, opts: SimProj
|
|
|
328
336
|
return v === 'success' && model.system.exp.task.goal !== 'eventually' ? 'inProgress' : v;
|
|
329
337
|
},
|
|
330
338
|
settingsGenerator: function* () {
|
|
331
|
-
if (current) yield*
|
|
339
|
+
if (current) yield* settingsFor(current.exp);
|
|
332
340
|
},
|
|
333
341
|
get editorSchema() {
|
|
334
342
|
return current?.editorSchema() ?? { fields: [] };
|