jssm 5.153.0 → 5.154.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 +7 -7
- package/custom-elements.json +290 -4
- package/dist/cdn/instance.js +148 -1
- package/dist/cdn/viz.js +146 -1
- package/dist/cli/fsl-export-system-prompt.cjs +1 -1
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/cli/lib.cjs +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/deno/README.md +7 -7
- package/dist/deno/jssm.d.ts +64 -1
- package/dist/deno/jssm.js +1 -1
- package/dist/deno/jssm_types.d.ts +29 -1
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -1
- package/dist/jssm_viz.iife.cjs +1 -1
- package/dist/jssm_viz.mjs +1 -1
- package/dist/wc/instance.js +2 -0
- package/dist/wc/widgets.define.js +3 -2
- package/dist/wc/widgets.js +331 -34
- package/jssm.es5.d.cts +92 -1
- package/jssm.es6.d.ts +92 -1
- package/jssm_viz.es5.d.cts +87 -0
- package/jssm_viz.es6.d.ts +87 -0
- package/package.json +1 -1
package/dist/wc/instance.js
CHANGED
|
@@ -1090,6 +1090,7 @@ function split_ratio(coord, start, size) {
|
|
|
1090
1090
|
* @slot hook-log - Slot for the hook-firing log (`<fsl-hook-log>`, #664).
|
|
1091
1091
|
* @slot effective-properties - Slot for the resolved-properties panel (`<fsl-effective-properties>`, #665).
|
|
1092
1092
|
* @slot simulation - Slot for the random-walk simulation (`<fsl-simulation>`, #668).
|
|
1093
|
+
* @slot stochastic - Slot for the stochastic analysis panel (`<fsl-stochastic>`, fsl#1384).
|
|
1093
1094
|
* @slot export - Slot for the export menu (`<fsl-export>`, #667).
|
|
1094
1095
|
* @slot footer - Footer slot.
|
|
1095
1096
|
*/
|
|
@@ -1967,6 +1968,7 @@ class FslInstance extends LitElement {
|
|
|
1967
1968
|
${docked ? '' : html `<section class="hook-log" ?hidden=${h('hook-log')}><slot name="hook-log"></slot></section>`}
|
|
1968
1969
|
<section class="effective-properties" ?hidden=${h('effective-properties')}><slot name="effective-properties"></slot></section>
|
|
1969
1970
|
<section class="simulation" ?hidden=${h('simulation')}><slot name="simulation"></slot></section>
|
|
1971
|
+
<section class="stochastic" ?hidden=${h('stochastic')}><slot name="stochastic"></slot></section>
|
|
1970
1972
|
<section class="export" ?hidden=${h('export')}><slot name="export"></slot></section>
|
|
1971
1973
|
`;
|
|
1972
1974
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FslToolbar, FslActions, FslFooter, FslHelp, FslHistory, FslDataInspector, FslHookLog, FslSimulation, FslExport } from './widgets.js';
|
|
1
|
+
import { FslToolbar, FslActions, FslFooter, FslHelp, FslHistory, FslDataInspector, FslHookLog, FslSimulation, FslExport, FslStochastic } from './widgets.js';
|
|
2
2
|
export * from './widgets.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -50,7 +50,7 @@ function define_canonical(canonical_tag, CanonicalClass) {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Bundle entry: registers the entire light fsl-* widget suite (toolbar, actions,
|
|
53
|
-
* footer, help, history, data-inspector, hook-log, simulation, export) in one import.
|
|
53
|
+
* footer, help, history, data-inspector, hook-log, simulation, export, stochastic) in one import.
|
|
54
54
|
* Canonical `fsl-*` tags only — no deprecated `jssm-*` synonyms. Registration
|
|
55
55
|
* is idempotent, so this composes safely with the per-widget `*.define` modules.
|
|
56
56
|
*/
|
|
@@ -63,3 +63,4 @@ define_canonical('fsl-data-inspector', FslDataInspector);
|
|
|
63
63
|
define_canonical('fsl-hook-log', FslHookLog);
|
|
64
64
|
define_canonical('fsl-simulation', FslSimulation);
|
|
65
65
|
define_canonical('fsl-export', FslExport);
|
|
66
|
+
define_canonical('fsl-stochastic', FslStochastic);
|
package/dist/wc/widgets.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { css, html, LitElement } from 'lit';
|
|
2
2
|
import { state, property } from 'lit/decorators.js';
|
|
3
3
|
import { machine_to_dot, machine_to_svg_string } from 'jssm/viz';
|
|
4
|
+
import { STOCHASTIC_DEFAULT_RUNS, STOCHASTIC_DEFAULT_MAX_STEPS, sm } from 'jssm';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Shared FSL appearance contract — the `--fsl-*` design-token vocabulary.
|
|
@@ -193,7 +194,7 @@ async function permalink_for(fsl, key = DEFAULT_PERMALINK_KEY, href = location.h
|
|
|
193
194
|
return `${href.split('#')[0]}#${fragment}`;
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
var __decorate$
|
|
197
|
+
var __decorate$8 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
197
198
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
198
199
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
199
200
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -496,23 +497,23 @@ FslToolbar.styles = css `
|
|
|
496
497
|
.spacer { flex: 1; }
|
|
497
498
|
${fslTokens}
|
|
498
499
|
`;
|
|
499
|
-
__decorate$
|
|
500
|
+
__decorate$8([
|
|
500
501
|
state()
|
|
501
502
|
], FslToolbar.prototype, "_openMenu", void 0);
|
|
502
|
-
__decorate$
|
|
503
|
+
__decorate$8([
|
|
503
504
|
state()
|
|
504
505
|
], FslToolbar.prototype, "_dest", void 0);
|
|
505
|
-
__decorate$
|
|
506
|
+
__decorate$8([
|
|
506
507
|
property({ attribute: false })
|
|
507
508
|
], FslToolbar.prototype, "lastDirectory", void 0);
|
|
508
|
-
__decorate$
|
|
509
|
+
__decorate$8([
|
|
509
510
|
property({ type: Boolean, attribute: 'no-validate' })
|
|
510
511
|
], FslToolbar.prototype, "noValidate", void 0);
|
|
511
|
-
__decorate$
|
|
512
|
+
__decorate$8([
|
|
512
513
|
property({ type: Boolean, attribute: 'no-lint' })
|
|
513
514
|
], FslToolbar.prototype, "noLint", void 0);
|
|
514
515
|
|
|
515
|
-
var __decorate$
|
|
516
|
+
var __decorate$7 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
516
517
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
517
518
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
518
519
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -662,20 +663,20 @@ FslActions.styles = css `
|
|
|
662
663
|
.empty { color: var(--_fsl-muted); font-style: italic; }
|
|
663
664
|
${fslTokens}
|
|
664
665
|
`;
|
|
665
|
-
__decorate$
|
|
666
|
+
__decorate$7([
|
|
666
667
|
state()
|
|
667
668
|
], FslActions.prototype, "_actions", void 0);
|
|
668
|
-
__decorate$
|
|
669
|
+
__decorate$7([
|
|
669
670
|
state()
|
|
670
671
|
], FslActions.prototype, "_main", void 0);
|
|
671
|
-
__decorate$
|
|
672
|
+
__decorate$7([
|
|
672
673
|
state()
|
|
673
674
|
], FslActions.prototype, "_regular", void 0);
|
|
674
|
-
__decorate$
|
|
675
|
+
__decorate$7([
|
|
675
676
|
state()
|
|
676
677
|
], FslActions.prototype, "_forced", void 0);
|
|
677
678
|
|
|
678
|
-
var __decorate$
|
|
679
|
+
var __decorate$6 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
679
680
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
680
681
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
681
682
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -791,32 +792,32 @@ FslFooter.styles = css `
|
|
|
791
792
|
.spacer { flex: 1; }
|
|
792
793
|
${fslTokens}
|
|
793
794
|
`;
|
|
794
|
-
__decorate$
|
|
795
|
+
__decorate$6([
|
|
795
796
|
state()
|
|
796
797
|
], FslFooter.prototype, "_state", void 0);
|
|
797
|
-
__decorate$
|
|
798
|
+
__decorate$6([
|
|
798
799
|
state()
|
|
799
800
|
], FslFooter.prototype, "_actions", void 0);
|
|
800
|
-
__decorate$
|
|
801
|
+
__decorate$6([
|
|
801
802
|
state()
|
|
802
803
|
], FslFooter.prototype, "_transitions", void 0);
|
|
803
|
-
__decorate$
|
|
804
|
+
__decorate$6([
|
|
804
805
|
state()
|
|
805
806
|
], FslFooter.prototype, "_terminal", void 0);
|
|
806
|
-
__decorate$
|
|
807
|
+
__decorate$6([
|
|
807
808
|
state()
|
|
808
809
|
], FslFooter.prototype, "_complete", void 0);
|
|
809
|
-
__decorate$
|
|
810
|
+
__decorate$6([
|
|
810
811
|
state()
|
|
811
812
|
], FslFooter.prototype, "_gActions", void 0);
|
|
812
|
-
__decorate$
|
|
813
|
+
__decorate$6([
|
|
813
814
|
state()
|
|
814
815
|
], FslFooter.prototype, "_gStarts", void 0);
|
|
815
|
-
__decorate$
|
|
816
|
+
__decorate$6([
|
|
816
817
|
state()
|
|
817
818
|
], FslFooter.prototype, "_gTransitions", void 0);
|
|
818
819
|
|
|
819
|
-
var __decorate$
|
|
820
|
+
var __decorate$5 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
820
821
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
821
822
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
822
823
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -885,14 +886,14 @@ FslHelp.styles = css `
|
|
|
885
886
|
.body { overflow-y: auto; padding: 0.5rem 0.9rem; font-size: 0.86rem; line-height: 1.5; }
|
|
886
887
|
${fslTokens}
|
|
887
888
|
`;
|
|
888
|
-
__decorate$
|
|
889
|
+
__decorate$5([
|
|
889
890
|
property({ type: Boolean, reflect: true })
|
|
890
891
|
], FslHelp.prototype, "open", void 0);
|
|
891
|
-
__decorate$
|
|
892
|
+
__decorate$5([
|
|
892
893
|
property({ type: String })
|
|
893
894
|
], FslHelp.prototype, "heading", void 0);
|
|
894
895
|
|
|
895
|
-
var __decorate$
|
|
896
|
+
var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
896
897
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
897
898
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
898
899
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -965,11 +966,11 @@ FslHistory.styles = css `
|
|
|
965
966
|
.empty { color: var(--_fsl-muted); font-style: italic; padding: 0.5rem 0.7rem; }
|
|
966
967
|
${fslTokens}
|
|
967
968
|
`;
|
|
968
|
-
__decorate$
|
|
969
|
+
__decorate$4([
|
|
969
970
|
state()
|
|
970
971
|
], FslHistory.prototype, "_history", void 0);
|
|
971
972
|
|
|
972
|
-
var __decorate$
|
|
973
|
+
var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
973
974
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
974
975
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
975
976
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -1089,11 +1090,11 @@ FslDataInspector.styles = css `
|
|
|
1089
1090
|
.tok-bool, .tok-null { color: var(--fsl-color-json-atom, #c2185b); }
|
|
1090
1091
|
${fslTokens}
|
|
1091
1092
|
`;
|
|
1092
|
-
__decorate$
|
|
1093
|
+
__decorate$3([
|
|
1093
1094
|
state()
|
|
1094
1095
|
], FslDataInspector.prototype, "_data", void 0);
|
|
1095
1096
|
|
|
1096
|
-
var __decorate$
|
|
1097
|
+
var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1097
1098
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1098
1099
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1099
1100
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -1163,11 +1164,11 @@ FslHookLog.styles = css `
|
|
|
1163
1164
|
.empty { color: var(--_fsl-muted); font-style: italic; }
|
|
1164
1165
|
${fslTokens}
|
|
1165
1166
|
`;
|
|
1166
|
-
__decorate$
|
|
1167
|
+
__decorate$2([
|
|
1167
1168
|
state()
|
|
1168
1169
|
], FslHookLog.prototype, "_log", void 0);
|
|
1169
1170
|
|
|
1170
|
-
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1171
|
+
var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1171
1172
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1172
1173
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1173
1174
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -1261,13 +1262,13 @@ FslSimulation.styles = css `
|
|
|
1261
1262
|
.count.idle { font-style: italic; }
|
|
1262
1263
|
${fslTokens}
|
|
1263
1264
|
`;
|
|
1264
|
-
__decorate([
|
|
1265
|
+
__decorate$1([
|
|
1265
1266
|
property({ type: Number })
|
|
1266
1267
|
], FslSimulation.prototype, "interval", void 0);
|
|
1267
|
-
__decorate([
|
|
1268
|
+
__decorate$1([
|
|
1268
1269
|
state()
|
|
1269
1270
|
], FslSimulation.prototype, "_running", void 0);
|
|
1270
|
-
__decorate([
|
|
1271
|
+
__decorate$1([
|
|
1271
1272
|
state()
|
|
1272
1273
|
], FslSimulation.prototype, "_steps", void 0);
|
|
1273
1274
|
|
|
@@ -1334,4 +1335,300 @@ FslExport.styles = css `
|
|
|
1334
1335
|
${fslTokens}
|
|
1335
1336
|
`;
|
|
1336
1337
|
|
|
1337
|
-
|
|
1338
|
+
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1339
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1340
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1341
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1342
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1343
|
+
};
|
|
1344
|
+
/**
|
|
1345
|
+
* `<fsl-stochastic>` — a statistical/Monte-Carlo explorer for a parent
|
|
1346
|
+
* `<fsl-instance>` (fsl#1384). Builds its own throwaway machine from the
|
|
1347
|
+
* host's `.fsl` source (never touching the live machine) and renders
|
|
1348
|
+
* aggregate run statistics in-panel. Standalone (no host) the controls are
|
|
1349
|
+
* disabled.
|
|
1350
|
+
*
|
|
1351
|
+
* @element fsl-stochastic
|
|
1352
|
+
* @csspart controls - The control row.
|
|
1353
|
+
* @fires fsl-stochastic-complete - CustomEvent<JssmStochasticSummary> after a run.
|
|
1354
|
+
*/
|
|
1355
|
+
class FslStochastic extends LitElement {
|
|
1356
|
+
constructor() {
|
|
1357
|
+
super(...arguments);
|
|
1358
|
+
/** Run-count (montecarlo). Defaults from editor_config().stochastic_run_count. */
|
|
1359
|
+
this.runs = STOCHASTIC_DEFAULT_RUNS;
|
|
1360
|
+
/** Per-run step cap (montecarlo) / walk length (steady_state). */
|
|
1361
|
+
this.maxSteps = STOCHASTIC_DEFAULT_MAX_STEPS;
|
|
1362
|
+
/** Seed string ('' = time-based). Kept as string so the field can be blank. */
|
|
1363
|
+
this.seed = '';
|
|
1364
|
+
/** Run mode. */
|
|
1365
|
+
this.mode = 'montecarlo';
|
|
1366
|
+
this._summary = null;
|
|
1367
|
+
this._error = null;
|
|
1368
|
+
this._host = null;
|
|
1369
|
+
this._playing = false;
|
|
1370
|
+
/** Execute a batch synchronously and render the aggregates. */
|
|
1371
|
+
this.run = () => {
|
|
1372
|
+
if (this._host === null) {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
let machine;
|
|
1376
|
+
try {
|
|
1377
|
+
machine = sm `${this._host.fsl}`;
|
|
1378
|
+
}
|
|
1379
|
+
catch (_a) {
|
|
1380
|
+
this._error = 'Cannot run on invalid FSL source.';
|
|
1381
|
+
this._summary = null;
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
this._error = null;
|
|
1385
|
+
const seedNum = this.seed.trim() === '' ? undefined : Number(this.seed);
|
|
1386
|
+
this._summary = machine.stochastic_summary({
|
|
1387
|
+
mode: this.mode, runs: this.runs, max_steps: this.maxSteps, seed: seedNum,
|
|
1388
|
+
});
|
|
1389
|
+
this.dispatchEvent(new CustomEvent('fsl-stochastic-complete', {
|
|
1390
|
+
detail: this._summary, bubbles: true, composed: true,
|
|
1391
|
+
}));
|
|
1392
|
+
};
|
|
1393
|
+
/**
|
|
1394
|
+
* Animate the batch: accumulate runs incrementally via `requestAnimationFrame`,
|
|
1395
|
+
* redrawing as they land. Resolves when the batch completes or is paused-to-stop.
|
|
1396
|
+
*
|
|
1397
|
+
* Falls back to immediate (synchronous chunk) scheduling under jsdom where
|
|
1398
|
+
* `requestAnimationFrame` is undefined.
|
|
1399
|
+
*
|
|
1400
|
+
* @example
|
|
1401
|
+
* panel.runs = 100;
|
|
1402
|
+
* await panel.play(); // resolves when all 100 runs are done
|
|
1403
|
+
*/
|
|
1404
|
+
this.play = () => new Promise((resolve) => {
|
|
1405
|
+
if (this._host === null) {
|
|
1406
|
+
resolve();
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
let machine;
|
|
1410
|
+
try {
|
|
1411
|
+
machine = sm `${this._host.fsl}`;
|
|
1412
|
+
}
|
|
1413
|
+
catch (_a) {
|
|
1414
|
+
this._error = 'Cannot run on invalid FSL source.';
|
|
1415
|
+
this._summary = null;
|
|
1416
|
+
resolve();
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
this._error = null;
|
|
1420
|
+
if (this.seed.trim() !== '') {
|
|
1421
|
+
machine.rng_seed = Number(this.seed);
|
|
1422
|
+
}
|
|
1423
|
+
const effective_seed = machine.rng_seed;
|
|
1424
|
+
const state_visits = new Map();
|
|
1425
|
+
const edges = new Map();
|
|
1426
|
+
const path_lengths = [];
|
|
1427
|
+
let reached = 0;
|
|
1428
|
+
let capped = 0;
|
|
1429
|
+
let runs = 0;
|
|
1430
|
+
const iter = machine.stochastic_runs({ mode: this.mode, runs: this.runs, max_steps: this.maxSteps });
|
|
1431
|
+
this._playing = true;
|
|
1432
|
+
const CHUNK = 50;
|
|
1433
|
+
const schedule = (fn) => {
|
|
1434
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
1435
|
+
requestAnimationFrame(fn);
|
|
1436
|
+
}
|
|
1437
|
+
else {
|
|
1438
|
+
fn();
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
const tick = () => {
|
|
1442
|
+
var _a, _b;
|
|
1443
|
+
if (!this._playing) {
|
|
1444
|
+
resolve();
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
for (let i = 0; i < CHUNK; i++) {
|
|
1448
|
+
const next = iter.next();
|
|
1449
|
+
if (next.done) {
|
|
1450
|
+
this._commit(state_visits, edges, path_lengths, reached, capped, runs, effective_seed);
|
|
1451
|
+
this._playing = false;
|
|
1452
|
+
resolve();
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const r = next.value;
|
|
1456
|
+
runs += 1;
|
|
1457
|
+
for (const s of r.states) {
|
|
1458
|
+
state_visits.set(s, ((_a = state_visits.get(s)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
1459
|
+
}
|
|
1460
|
+
for (const e of r.edges) {
|
|
1461
|
+
edges.set(e, ((_b = edges.get(e)) !== null && _b !== void 0 ? _b : 0) + 1);
|
|
1462
|
+
}
|
|
1463
|
+
if (this.mode === 'montecarlo') {
|
|
1464
|
+
if (r.terminated) {
|
|
1465
|
+
reached += 1;
|
|
1466
|
+
path_lengths.push(r.length);
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
capped += 1;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
this._commit(state_visits, edges, path_lengths, reached, capped, runs, effective_seed);
|
|
1474
|
+
schedule(tick);
|
|
1475
|
+
};
|
|
1476
|
+
schedule(tick);
|
|
1477
|
+
});
|
|
1478
|
+
/** Toggle between playing and paused. Starts a new {@link play} batch when idle. */
|
|
1479
|
+
this._togglePlay = () => { if (this._playing) {
|
|
1480
|
+
this._playing = false;
|
|
1481
|
+
}
|
|
1482
|
+
else {
|
|
1483
|
+
void this.play();
|
|
1484
|
+
} };
|
|
1485
|
+
this._onMode = (e) => { this.mode = e.target.value; };
|
|
1486
|
+
this._onRuns = (e) => { this.runs = Number(e.target.value); };
|
|
1487
|
+
this._onMax = (e) => { this.maxSteps = Number(e.target.value); };
|
|
1488
|
+
this._onSeed = (e) => { this.seed = e.target.value; };
|
|
1489
|
+
}
|
|
1490
|
+
connectedCallback() {
|
|
1491
|
+
super.connectedCallback();
|
|
1492
|
+
this._host = closest_wc(this, 'instance');
|
|
1493
|
+
}
|
|
1494
|
+
disconnectedCallback() {
|
|
1495
|
+
super.disconnectedCallback();
|
|
1496
|
+
this._host = null;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Read `editor_config().stochastic_run_count` after the first render so the
|
|
1500
|
+
* host's machine is guaranteed to be built (machine construction happens in
|
|
1501
|
+
* the host's connectedCallback, which may fire after the panel's own
|
|
1502
|
+
* connectedCallback in some environments).
|
|
1503
|
+
*/
|
|
1504
|
+
firstUpdated(_changedProperties) {
|
|
1505
|
+
var _a, _b, _c, _d;
|
|
1506
|
+
super.firstUpdated(_changedProperties);
|
|
1507
|
+
try {
|
|
1508
|
+
const declared = (_d = (_c = (_b = (_a = this._host) === null || _a === void 0 ? void 0 : _a.machine) === null || _b === void 0 ? void 0 : _b.editor_config) === null || _c === void 0 ? void 0 : _c.call(_b)) === null || _d === void 0 ? void 0 : _d.stochastic_run_count;
|
|
1509
|
+
if (typeof declared === 'number') {
|
|
1510
|
+
this.runs = declared;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
catch ( /* machine not ready — leave default */_e) { /* machine not ready — leave default */ }
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Fold accumulated counters into a rendered summary. Shared by {@link play}
|
|
1517
|
+
* for incremental rendering during animation.
|
|
1518
|
+
*
|
|
1519
|
+
* @param state_visits - Accumulated visit counts per state name.
|
|
1520
|
+
* @param edge_traversals - Accumulated traversal counts per edge key.
|
|
1521
|
+
* @param path_lengths - Lengths of completed (terminated) paths.
|
|
1522
|
+
* @param terminal_reached - Count of runs that reached a terminal state.
|
|
1523
|
+
* @param capped - Count of runs that hit the step cap.
|
|
1524
|
+
* @param runs - Total runs processed so far.
|
|
1525
|
+
* @param seed - The effective RNG seed used for this batch.
|
|
1526
|
+
*/
|
|
1527
|
+
_commit(state_visits, edge_traversals, path_lengths, terminal_reached, capped, runs, seed) {
|
|
1528
|
+
const total = [...state_visits.values()].reduce((a, b) => a + b, 0);
|
|
1529
|
+
const frac = new Map();
|
|
1530
|
+
for (const [s, c] of state_visits) {
|
|
1531
|
+
frac.set(s, c / total);
|
|
1532
|
+
}
|
|
1533
|
+
const summary = {
|
|
1534
|
+
mode: this.mode, runs, seed,
|
|
1535
|
+
state_visits, state_visit_fraction: frac, edge_traversals,
|
|
1536
|
+
};
|
|
1537
|
+
if (this.mode === 'montecarlo') {
|
|
1538
|
+
summary.path_lengths = path_lengths;
|
|
1539
|
+
summary.terminal_reached = terminal_reached;
|
|
1540
|
+
summary.capped = capped;
|
|
1541
|
+
}
|
|
1542
|
+
this._summary = summary;
|
|
1543
|
+
}
|
|
1544
|
+
_bars() {
|
|
1545
|
+
const frac = this._summary.state_visit_fraction;
|
|
1546
|
+
const rows = [...frac.entries()].sort((a, b) => b[1] - a[1]);
|
|
1547
|
+
return html `${rows.map(([name, f]) => html `
|
|
1548
|
+
<div class="bar-row">
|
|
1549
|
+
<span>${name}</span>
|
|
1550
|
+
<span class="track"><span class="bar" style="width:${(f * 100).toFixed(1)}%"></span></span>
|
|
1551
|
+
<span>${(f * 100).toFixed(1)}%</span>
|
|
1552
|
+
</div>`)}`;
|
|
1553
|
+
}
|
|
1554
|
+
_panes() {
|
|
1555
|
+
const s = this._summary;
|
|
1556
|
+
const mc = s.mode === 'montecarlo';
|
|
1557
|
+
const reached = mc && s.runs > 0 ? Math.round((s.terminal_reached / s.runs) * 100) : 0;
|
|
1558
|
+
const capped = mc && s.runs > 0 ? Math.round((s.capped / s.runs) * 100) : 0;
|
|
1559
|
+
return html `
|
|
1560
|
+
<div class="panes">
|
|
1561
|
+
<div><strong>State visits</strong></div>
|
|
1562
|
+
${this._bars()}
|
|
1563
|
+
${mc ? html `<div>Reached terminal: ${reached}% · Hit cap: ${capped}%</div>` : html `<div class="muted">steady-state distribution</div>`}
|
|
1564
|
+
</div>`;
|
|
1565
|
+
}
|
|
1566
|
+
render() {
|
|
1567
|
+
const disabled = this._host === null;
|
|
1568
|
+
return html `
|
|
1569
|
+
<div class="controls" part="controls">
|
|
1570
|
+
<select @change=${this._onMode} .value=${this.mode}>
|
|
1571
|
+
<option value="montecarlo">Monte Carlo</option>
|
|
1572
|
+
<option value="steady_state">Steady-state</option>
|
|
1573
|
+
</select>
|
|
1574
|
+
${this.mode === 'montecarlo'
|
|
1575
|
+
? html `<label>runs <input type="number" .value=${String(this.runs)} @change=${this._onRuns}></label>`
|
|
1576
|
+
: ''}
|
|
1577
|
+
<label>max steps <input type="number" .value=${String(this.maxSteps)} @change=${this._onMax}></label>
|
|
1578
|
+
<label>seed <input type="text" .value=${this.seed} @change=${this._onSeed}></label>
|
|
1579
|
+
<button class="run" ?disabled=${disabled} @click=${this.run}>Run</button>
|
|
1580
|
+
<button class="btn" ?disabled=${disabled} @click=${this._togglePlay}>${this._playing ? 'Pause' : 'Play'}</button>
|
|
1581
|
+
</div>
|
|
1582
|
+
${this._error ? html `<div class="error">${this._error}</div>` : ''}
|
|
1583
|
+
${this._summary && !this._error ? this._panes() : html `<div class="panes muted">No run yet.</div>`}`;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
FslStochastic.styles = css `
|
|
1587
|
+
:host { display: block; font: 0.8rem var(--_fsl-font); color: var(--_fsl-text); }
|
|
1588
|
+
.controls {
|
|
1589
|
+
display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
|
|
1590
|
+
padding: 0.4rem 0.6rem; background: var(--_fsl-surface);
|
|
1591
|
+
border-top: 1px solid var(--_fsl-border);
|
|
1592
|
+
}
|
|
1593
|
+
.btn, .run {
|
|
1594
|
+
height: 1.8rem; padding: 0 0.7rem; cursor: pointer; border-radius: 4px;
|
|
1595
|
+
border: 1px solid var(--_fsl-border); background: var(--_fsl-surface);
|
|
1596
|
+
color: var(--_fsl-text); font: 600 0.8rem var(--_fsl-font);
|
|
1597
|
+
}
|
|
1598
|
+
.btn:disabled, .run:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1599
|
+
input { width: 4.5rem; height: 1.6rem; background: var(--_fsl-surface);
|
|
1600
|
+
color: var(--_fsl-text); border: 1px solid var(--_fsl-border); border-radius: 4px; }
|
|
1601
|
+
.panes { padding: 0.4rem 0.6rem; display: grid; gap: 0.5rem; }
|
|
1602
|
+
.bar-row { display: grid; grid-template-columns: 4rem 1fr 3rem; align-items: center; gap: 0.4rem; }
|
|
1603
|
+
.track { background: color-mix(in srgb, var(--_fsl-text) 8%, var(--_fsl-surface)); border-radius: 3px; height: 0.9rem; }
|
|
1604
|
+
.bar { background: var(--_fsl-accent); height: 0.9rem; border-radius: 3px; }
|
|
1605
|
+
.muted { color: var(--_fsl-muted); }
|
|
1606
|
+
.error { color: var(--_fsl-danger, #c0392b); padding: 0.4rem 0.6rem; }
|
|
1607
|
+
${fslTokens}
|
|
1608
|
+
`;
|
|
1609
|
+
__decorate([
|
|
1610
|
+
property({ type: Number })
|
|
1611
|
+
], FslStochastic.prototype, "runs", void 0);
|
|
1612
|
+
__decorate([
|
|
1613
|
+
property({ type: Number })
|
|
1614
|
+
], FslStochastic.prototype, "maxSteps", void 0);
|
|
1615
|
+
__decorate([
|
|
1616
|
+
property({ type: String })
|
|
1617
|
+
], FslStochastic.prototype, "seed", void 0);
|
|
1618
|
+
__decorate([
|
|
1619
|
+
property({ type: String })
|
|
1620
|
+
], FslStochastic.prototype, "mode", void 0);
|
|
1621
|
+
__decorate([
|
|
1622
|
+
state()
|
|
1623
|
+
], FslStochastic.prototype, "_summary", void 0);
|
|
1624
|
+
__decorate([
|
|
1625
|
+
state()
|
|
1626
|
+
], FslStochastic.prototype, "_error", void 0);
|
|
1627
|
+
__decorate([
|
|
1628
|
+
state()
|
|
1629
|
+
], FslStochastic.prototype, "_host", void 0);
|
|
1630
|
+
__decorate([
|
|
1631
|
+
state()
|
|
1632
|
+
], FslStochastic.prototype, "_playing", void 0);
|
|
1633
|
+
|
|
1634
|
+
export { FslActions, FslDataInspector, FslExport, FslFooter, FslHelp, FslHistory, FslHookLog, FslSimulation, FslStochastic, FslToolbar };
|
package/jssm.es5.d.cts
CHANGED
|
@@ -524,6 +524,34 @@ declare type JssmEditorConfig = {
|
|
|
524
524
|
stochastic_run_count?: number;
|
|
525
525
|
panels?: Array<string>;
|
|
526
526
|
};
|
|
527
|
+
/** Which stochastic view a run batch produces. */
|
|
528
|
+
declare type JssmStochasticMode = 'montecarlo' | 'steady_state';
|
|
529
|
+
/** Options for {@link Machine.stochastic_summary} / {@link Machine.stochastic_runs}. */
|
|
530
|
+
declare type JssmStochasticOptions = {
|
|
531
|
+
mode?: JssmStochasticMode;
|
|
532
|
+
runs?: number;
|
|
533
|
+
max_steps?: number;
|
|
534
|
+
seed?: number;
|
|
535
|
+
};
|
|
536
|
+
/** One walk's result, yielded by {@link Machine.stochastic_runs}. */
|
|
537
|
+
declare type JssmStochasticRun = {
|
|
538
|
+
states: Array<string>;
|
|
539
|
+
edges: Array<string>;
|
|
540
|
+
length: number;
|
|
541
|
+
terminated: boolean;
|
|
542
|
+
};
|
|
543
|
+
/** Aggregate statistics over a stochastic run batch. */
|
|
544
|
+
declare type JssmStochasticSummary = {
|
|
545
|
+
mode: JssmStochasticMode;
|
|
546
|
+
runs: number;
|
|
547
|
+
seed: number;
|
|
548
|
+
state_visits: Map<string, number>;
|
|
549
|
+
state_visit_fraction: Map<string, number>;
|
|
550
|
+
edge_traversals: Map<string, number>;
|
|
551
|
+
path_lengths?: Array<number>;
|
|
552
|
+
terminal_reached?: number;
|
|
553
|
+
capped?: number;
|
|
554
|
+
};
|
|
527
555
|
declare type JssmGenericConfig<StateType, DataType> = {
|
|
528
556
|
graph_layout?: JssmLayout;
|
|
529
557
|
complete?: Array<StateType>;
|
|
@@ -1991,6 +2019,10 @@ declare function transfer_state_properties(state_decl: JssmStateDeclaration): Js
|
|
|
1991
2019
|
*
|
|
1992
2020
|
*/
|
|
1993
2021
|
declare function state_style_condense(jssk: JssmStateStyleKeyList, machine?: any): JssmStateConfig;
|
|
2022
|
+
/** Default number of independent Monte-Carlo runs when none is declared. */
|
|
2023
|
+
declare const STOCHASTIC_DEFAULT_RUNS = 1000;
|
|
2024
|
+
/** Default per-run step cap (montecarlo) / walk length (steady_state). */
|
|
2025
|
+
declare const STOCHASTIC_DEFAULT_MAX_STEPS = 1000;
|
|
1994
2026
|
declare class Machine<mDT> {
|
|
1995
2027
|
_state: StateType;
|
|
1996
2028
|
_states: Map<StateType, JssmGenericState>;
|
|
@@ -2919,6 +2951,65 @@ declare class Machine<mDT> {
|
|
|
2919
2951
|
* @returns A `Map` from state name to visit count.
|
|
2920
2952
|
*/
|
|
2921
2953
|
probabilistic_histo_walk(n: number): Map<StateType, number>;
|
|
2954
|
+
/** One non-destructive weighted-random walk over the graph from `start`.
|
|
2955
|
+
*
|
|
2956
|
+
* Reads the graph and advances the PRNG only — it never calls
|
|
2957
|
+
* {@link Machine.transition}, so it fires no hooks, mutates no machine
|
|
2958
|
+
* state, and touches no `data`. A state with no probabilistic exits
|
|
2959
|
+
* (a terminal, or a forced-only `~>` state) ends the walk.
|
|
2960
|
+
*
|
|
2961
|
+
* @param start - State to begin the walk from.
|
|
2962
|
+
* @param max_steps - Maximum transitions before the walk is step-capped.
|
|
2963
|
+
* @returns The {@link JssmStochasticRun} for this walk.
|
|
2964
|
+
*/
|
|
2965
|
+
private _stochastic_one_walk;
|
|
2966
|
+
/** Lazily yield one {@link JssmStochasticRun} at a time.
|
|
2967
|
+
*
|
|
2968
|
+
* In `montecarlo` mode (default) yields `runs` independent walks from the
|
|
2969
|
+
* current state, each ending at a terminal or after `max_steps`. In
|
|
2970
|
+
* `steady_state` mode yields exactly one walk of `max_steps` steps. This
|
|
2971
|
+
* is the lazy engine behind {@link Machine.stochastic_summary}; the
|
|
2972
|
+
* fsl-stochastic panel drives it across animation frames.
|
|
2973
|
+
*
|
|
2974
|
+
* Passing `seed` reseeds the machine for reproducible runs. Unlike
|
|
2975
|
+
* {@link Machine.stochastic_summary}, the generator does NOT restore the
|
|
2976
|
+
* prior seed afterward — a direct caller's machine is left reseeded.
|
|
2977
|
+
*
|
|
2978
|
+
* @param opts - {@link JssmStochasticOptions}.
|
|
2979
|
+
* @returns A generator of per-run results.
|
|
2980
|
+
*
|
|
2981
|
+
* @example
|
|
2982
|
+
* const m = sm`a 'go' -> b 'go' -> c;`;
|
|
2983
|
+
* [...m.stochastic_runs({ runs: 2, seed: 1 })].length; // => 2
|
|
2984
|
+
*/
|
|
2985
|
+
stochastic_runs(opts?: JssmStochasticOptions): Generator<JssmStochasticRun>;
|
|
2986
|
+
/** Run many weighted-random walks and return aggregate statistics.
|
|
2987
|
+
*
|
|
2988
|
+
* Honors `%` transition probabilities (via the existing probabilistic
|
|
2989
|
+
* machinery). Non-destructive: the machine's current state and
|
|
2990
|
+
* {@link Machine.rng_seed} are restored before returning, so calling this
|
|
2991
|
+
* never perturbs the live machine. `montecarlo` mode (default) reports
|
|
2992
|
+
* per-run `path_lengths`, `terminal_reached`, and `capped`; `steady_state`
|
|
2993
|
+
* mode runs one long walk and omits those fields.
|
|
2994
|
+
*
|
|
2995
|
+
* Timing (`after`) decorations and data-guard conditions are not modeled
|
|
2996
|
+
* by this sampler; it walks the probabilistic graph topology.
|
|
2997
|
+
*
|
|
2998
|
+
* @param opts - {@link JssmStochasticOptions}. `runs` defaults to the
|
|
2999
|
+
* machine's declared `editor: { stochastic_run_count }` (fsl#1334) when
|
|
3000
|
+
* present, otherwise {@link STOCHASTIC_DEFAULT_RUNS}.
|
|
3001
|
+
* @returns A {@link JssmStochasticSummary}.
|
|
3002
|
+
*
|
|
3003
|
+
* @see Machine.stochastic_runs
|
|
3004
|
+
* @see Machine.probabilistic_walk
|
|
3005
|
+
* @see Machine.editor_config
|
|
3006
|
+
*
|
|
3007
|
+
* @example
|
|
3008
|
+
* const m = sm`a 'go' -> b 'go' -> c;`;
|
|
3009
|
+
* const s = m.stochastic_summary({ runs: 100, seed: 1 });
|
|
3010
|
+
* s.terminal_reached; // => 100
|
|
3011
|
+
*/
|
|
3012
|
+
stochastic_summary(opts?: JssmStochasticOptions): JssmStochasticSummary;
|
|
2922
3013
|
/********
|
|
2923
3014
|
*
|
|
2924
3015
|
* List all actions available from this state. Please note that the order of
|
|
@@ -4691,4 +4782,4 @@ declare function compareVersions(v1: string, v2: string): number;
|
|
|
4691
4782
|
*/
|
|
4692
4783
|
declare function deserialize<mDT>(machine_string: string, ser: JssmSerialization<mDT>): Machine<mDT>;
|
|
4693
4784
|
|
|
4694
|
-
export { FslDirections, Machine, abstract_everything_hook_step, abstract_hook_step, action_label_chars, arrow_direction, arrow_left_kind, arrow_right_kind, build_time, compareVersions, compile, jssm_constants_d as constants, deserialize, find_repeated, from, fslCompletions, fslDiagnostics, fslSemanticSpans, gen_splitmix32, gviz_shapes, histograph, is_hook_complex_result, is_hook_rejection, make, named_colors, wrap_parse as parse, seq, shapes, sleep, sm, state_name_chars, state_name_first_chars, state_style_condense, transfer_state_properties, unique, version, weighted_histo_key, weighted_rand_select, weighted_sample_select };
|
|
4785
|
+
export { FslDirections, Machine, STOCHASTIC_DEFAULT_MAX_STEPS, STOCHASTIC_DEFAULT_RUNS, abstract_everything_hook_step, abstract_hook_step, action_label_chars, arrow_direction, arrow_left_kind, arrow_right_kind, build_time, compareVersions, compile, jssm_constants_d as constants, deserialize, find_repeated, from, fslCompletions, fslDiagnostics, fslSemanticSpans, gen_splitmix32, gviz_shapes, histograph, is_hook_complex_result, is_hook_rejection, make, named_colors, wrap_parse as parse, seq, shapes, sleep, sm, state_name_chars, state_name_first_chars, state_style_condense, transfer_state_properties, unique, version, weighted_histo_key, weighted_rand_select, weighted_sample_select };
|