@seantalts/stanli 0.8.5 → 0.9.2

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 CHANGED
@@ -1,11 +1,12 @@
1
1
  # stanli
2
2
 
3
3
  Full [Stan](https://mc-stan.org) in the browser. stanc3 (the real Stan
4
- compiler, compiled to JavaScript) turns Stan source into its
5
- intermediate representation; a WebAssembly build of the stanli runtime
6
- lowers it to an op graph over precompiled stan-math kernels and samples
7
- with NUTS. No server, no C++ toolchain, everything in the tab. Live
8
- demo: <https://seantalts.github.io/stanli/>.
4
+ compiler, compiled to JavaScript) parses, typechecks, and optimizes the
5
+ model; the shared stanli OCaml pipeline encodes its typed MIR into portable
6
+ MIR. A WebAssembly build of the stanli runtime lowers that to an op graph over
7
+ precompiled stan-math kernels and samples with NUTS. No server, no C++
8
+ toolchain, everything in the tab. Live demo:
9
+ <https://seantalts.github.io/stanli/>.
9
10
 
10
11
  ```js
11
12
  import { sample } from "@seantalts/stanli";
@@ -30,13 +31,26 @@ parameters, and generated quantities (RNG draws stream from `seed`).
30
31
  The heavy work runs in a worker the package owns, so the page never
31
32
  blocks; calls queue and run one at a time.
32
33
 
33
- The payload is ~9 MB installed, ~1.9 MB over the wire with gzip: the
34
- WASM runtime plus the stanc3 compiler. The compiler loads lazily, only
35
- when a call passes Stan source. `preload()` starts both loads in the
36
- background; call it at page idle so the first `sample()` skips the
37
- fetch and parse. An app that ships a fixed model can precompile it at
38
- build time (`stanc --O1 --debug-optimized-mir model.stan`) and pass `mir`
39
- instead of `code`; the runtime alone is ~1.5 MB gzipped.
34
+ The preferred compiler, `stanli-compiler.js`, is 2,990,736 bytes raw and
35
+ 425,026 bytes gzipped in the current measured build. For one rollback cycle
36
+ the package also contains stock `stancjs.bc.js` (2,971,677 bytes raw, 418,847
37
+ bytes gzipped). The worker loads the stock compiler only if the portable
38
+ compiler is unavailable; its O1 legacy MIR remains accepted by the runtime.
39
+ Carrying both temporarily doubles the compiler portion of the installed and
40
+ downloaded package.
41
+
42
+ The compiler loads lazily, only when a call passes Stan source. `preload()`
43
+ starts the compiler and WASM loads in the background; call it at page idle so
44
+ the first `sample()` skips the fetch and parse. An app that ships a fixed model
45
+ can precompile it at build time (`stanc --O1 --debug-optimized-mir model.stan`)
46
+ and pass `mir` instead of `code`; neither browser compiler loads, and the WASM
47
+ runtime alone is ~1.5 MB gzipped.
48
+
49
+ On Eight Schools, portable MIR is 111,760 bytes (2,365 gzipped), compared with
50
+ 33,320 bytes (2,000 gzipped) for legacy MIR. On the same Apple arm64 release
51
+ build, median decoder parsing was 2.799 ms versus 0.290 ms, and complete
52
+ preparation was 3.31 ms versus 0.59 ms. These are one-time preparation
53
+ measurements; source compilation still dominates that path.
40
54
 
41
55
  118 of 119 posteriordb corpus models verify against CmdStan's log
42
56
  density, gradients, and write_array values from inside this WASM build;
package/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- // stanli: full Stan in the browser. stanc3 (compiled to JS) turns Stan
2
- // source into MIR, and a WASM build of the stanli runtime lowers it to an
3
- // op graph and runs NUTS. Everything happens client side, off the main
4
- // thread, in workers this module owns.
1
+ // stanli: full Stan in the browser. The custom stanc3 build (compiled to JS)
2
+ // turns Stan source into portable MIR, and a WASM build of the stanli runtime
3
+ // lowers it to an op graph and runs NUTS. Everything happens client side, off
4
+ // the main thread, in workers this module owns.
5
5
  //
6
6
  // import { compile, sample } from "@seantalts/stanli";
7
7
  // const { mir } = await compile({ code });
@@ -10,9 +10,10 @@
10
10
  //
11
11
  // Workers pool up to the hardware's concurrency, so independent calls --
12
12
  // one chain each -- run simultaneously. Compiling once and passing `mir`
13
- // keeps the 2.8 MB compiler in a single worker (or out of the page
13
+ // keeps the 3.0 MB preferred compiler in a single worker (or out of the page
14
14
  // entirely, if the model was precompiled at build time with
15
- // stanc --O1 --debug-optimized-mir).
15
+ // stanc --O1 --debug-optimized-mir). The package carries stock stancjs for one
16
+ // rollback cycle, but the worker loads it only if the portable compiler fails.
16
17
 
17
18
  const pool = [];
18
19
  const waiters = [];
@@ -88,7 +89,7 @@ export function preload(opts) {
88
89
  return Promise.all(jobs);
89
90
  }
90
91
 
91
- /** Compile Stan source to transformed MIR (one worker loads stanc3).
92
+ /** Compile Stan source to portable MIR (one worker loads stanc3).
92
93
  * @returns {Promise<{mir: string, ms: {stanc: number}}>} */
93
94
  export function compile(opts) {
94
95
  return request({ cmd: "compile", code: opts.code }, opts);
@@ -99,10 +100,9 @@ export function compile(opts) {
99
100
  * @param {Object} opts
100
101
  * @param {string} [opts.code] Stan source (compiled in the worker by
101
102
  * stanc3, which loads lazily on first use).
102
- * @param {string} [opts.mir] Precompiled MIR (from `compile()`
103
- * here, or `stanc --O1 --debug-optimized-mir` at build time).
104
- * When given, the 2.8 MB compiler never loads: the runtime alone is
105
- * ~1.3 MB gzipped.
103
+ * @param {string} [opts.mir] Precompiled MIR (from `compile()` here, or
104
+ * `stanc --O1 --debug-optimized-mir` at build time). When given, neither
105
+ * browser compiler loads: the runtime alone is ~1.5 MB gzipped.
106
106
  * @param {Object|string} [opts.data] Data as an object or JSON text.
107
107
  * @param {number} [opts.seed=1] Chain seed (sampler and GQ RNG).
108
108
  * @param {number} [opts.warmup=1000]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seantalts/stanli",
3
- "version": "0.8.5",
3
+ "version": "0.9.2",
4
4
  "description": "Full Stan in the browser: stanc3 compiles the model in JS, a WASM runtime lowers it to an op graph and runs NUTS. No server, no C++ toolchain.",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -12,6 +12,7 @@
12
12
  "worker.js",
13
13
  "stanli.js",
14
14
  "stanli.wasm",
15
+ "stanli-compiler.js",
15
16
  "stancjs.bc.js",
16
17
  "README.md",
17
18
  "LICENSE"