@mcmcjs/julia 0.11.3 → 0.13.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.js CHANGED
@@ -233,7 +233,7 @@ async function runFit(spec, resolved, io) {
233
233
  return finalizeOkFit(spec, resolved, io, lastJsonLine(stdout), startedAt, elapsedMs);
234
234
  }
235
235
 
236
- // src/predict.ts
236
+ // src/loglik.ts
237
237
  import { existsSync as existsSync3, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
238
238
  import { join as join4 } from "path";
239
239
  import {
@@ -241,6 +241,95 @@ import {
241
241
  parseSamples as parseSamples2,
242
242
  RUN_RECORD_SCHEMA_VERSION as RUN_RECORD_SCHEMA_VERSION2
243
243
  } from "@mcmcjs/core";
244
+ async function runLogLik(spec, resolved, io) {
245
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
246
+ const start = performance.now();
247
+ const runtimeRequested = spec.backend.version;
248
+ const ownTmp = io.tmpDir === void 0;
249
+ const tmp = io.tmpDir ?? makeRequestDir("loglik");
250
+ const requestPath = join4(tmp, "request.json");
251
+ writeFileSync2(
252
+ requestPath,
253
+ JSON.stringify({
254
+ mode: "loglik",
255
+ schema_version: spec.schema_version,
256
+ backend: { id: spec.backend.id },
257
+ model: { file: spec.modelPath, entry: spec.model.entry },
258
+ data: spec.data,
259
+ samples: io.samplesPath,
260
+ seed: spec.seed,
261
+ out: io.outPath
262
+ })
263
+ );
264
+ const args = [
265
+ ...resolved.args,
266
+ "--startup-file=no",
267
+ `--project=${io.projectDir}`,
268
+ driverPath(),
269
+ requestPath
270
+ ];
271
+ let stdout;
272
+ let stderr;
273
+ let code;
274
+ try {
275
+ ({ stdout, stderr, code } = await io.spawn(resolved.command, args));
276
+ } finally {
277
+ if (ownTmp) rmSync2(tmp, { recursive: true, force: true });
278
+ }
279
+ const elapsedMs = Math.round(performance.now() - start);
280
+ if (code !== 0 || !existsSync3(io.outPath)) {
281
+ const failure = lastJsonLine(stderr);
282
+ const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
283
+ return { status: "error", runtimeRequested, elapsedMs, stage: toStage(failure?.stage), error };
284
+ }
285
+ try {
286
+ parseSamples2(readFileSync2(io.outPath, "utf8"));
287
+ } catch (error) {
288
+ return {
289
+ status: "error",
290
+ runtimeRequested,
291
+ elapsedMs,
292
+ stage: "write",
293
+ error: `log-likelihood file did not parse: ${error.message}`
294
+ };
295
+ }
296
+ const provenance = lastJsonLine(stdout);
297
+ const julia = provenance?.julia_version;
298
+ const record = {
299
+ schema_version: RUN_RECORD_SCHEMA_VERSION2,
300
+ spec_hash: spec.specHash,
301
+ seed: spec.seed,
302
+ backend: { id: spec.backend.id, runtime: spec.backend.runtime },
303
+ runtime: { requested: runtimeRequested, actual: julia, path: resolved.command },
304
+ manifest_sha256: provenance?.manifest_sha256,
305
+ packages: provenance?.packages,
306
+ model_sha256: existsSync3(spec.modelPath) ? sha256(readFileSync2(spec.modelPath, "utf8")) : void 0,
307
+ data_sha256: sha256(canonicalJson2(spec.data)),
308
+ posterior_samples: io.samplesPath,
309
+ posterior_samples_sha256: existsSync3(io.samplesPath) ? sha256(readFileSync2(io.samplesPath, "utf8")) : void 0,
310
+ samples_file: io.outPath,
311
+ started_at: startedAt,
312
+ elapsed_ms: elapsedMs
313
+ };
314
+ writeFileSync2(`${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
315
+ `);
316
+ return {
317
+ status: "ok",
318
+ samplesFile: io.outPath,
319
+ runtimeRequested,
320
+ runtimeActual: julia,
321
+ elapsedMs
322
+ };
323
+ }
324
+
325
+ // src/predict.ts
326
+ import { existsSync as existsSync4, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
327
+ import { join as join5 } from "path";
328
+ import {
329
+ canonicalJson as canonicalJson3,
330
+ parseSamples as parseSamples3,
331
+ RUN_RECORD_SCHEMA_VERSION as RUN_RECORD_SCHEMA_VERSION3
332
+ } from "@mcmcjs/core";
244
333
  function predictData(spec) {
245
334
  const predict = spec.predict;
246
335
  if (!predict) throw new Error("spec has no [predict] block");
@@ -258,8 +347,8 @@ async function runPredict(spec, resolved, io) {
258
347
  const data = predictData(spec);
259
348
  const ownTmp = io.tmpDir === void 0;
260
349
  const tmp = io.tmpDir ?? makeRequestDir("predict");
261
- const requestPath = join4(tmp, "request.json");
262
- writeFileSync2(
350
+ const requestPath = join5(tmp, "request.json");
351
+ writeFileSync3(
263
352
  requestPath,
264
353
  JSON.stringify({
265
354
  mode: "predict",
@@ -286,16 +375,16 @@ async function runPredict(spec, resolved, io) {
286
375
  try {
287
376
  ({ stdout, stderr, code } = await io.spawn(resolved.command, args));
288
377
  } finally {
289
- if (ownTmp) rmSync2(tmp, { recursive: true, force: true });
378
+ if (ownTmp) rmSync3(tmp, { recursive: true, force: true });
290
379
  }
291
380
  const elapsedMs = Math.round(performance.now() - start);
292
- if (code !== 0 || !existsSync3(io.outPath)) {
381
+ if (code !== 0 || !existsSync4(io.outPath)) {
293
382
  const failure = lastJsonLine(stderr);
294
383
  const error = failure?.error ?? stderr.trim().split("\n").pop() ?? `julia exited with code ${code}`;
295
384
  return { status: "error", runtimeRequested, elapsedMs, stage: toStage(failure?.stage), error };
296
385
  }
297
386
  try {
298
- parseSamples2(readFileSync2(io.outPath, "utf8"));
387
+ parseSamples3(readFileSync3(io.outPath, "utf8"));
299
388
  } catch (error) {
300
389
  return {
301
390
  status: "error",
@@ -308,22 +397,22 @@ async function runPredict(spec, resolved, io) {
308
397
  const provenance = lastJsonLine(stdout);
309
398
  const julia = provenance?.julia_version;
310
399
  const record = {
311
- schema_version: RUN_RECORD_SCHEMA_VERSION2,
400
+ schema_version: RUN_RECORD_SCHEMA_VERSION3,
312
401
  spec_hash: spec.specHash,
313
402
  seed: spec.seed,
314
403
  backend: { id: spec.backend.id, runtime: spec.backend.runtime },
315
404
  runtime: { requested: runtimeRequested, actual: julia, path: resolved.command },
316
405
  manifest_sha256: provenance?.manifest_sha256,
317
406
  packages: provenance?.packages,
318
- model_sha256: existsSync3(spec.modelPath) ? sha256(readFileSync2(spec.modelPath, "utf8")) : void 0,
319
- data_sha256: sha256(canonicalJson2(data)),
407
+ model_sha256: existsSync4(spec.modelPath) ? sha256(readFileSync3(spec.modelPath, "utf8")) : void 0,
408
+ data_sha256: sha256(canonicalJson3(data)),
320
409
  posterior_samples: io.samplesPath,
321
- posterior_samples_sha256: existsSync3(io.samplesPath) ? sha256(readFileSync2(io.samplesPath, "utf8")) : void 0,
410
+ posterior_samples_sha256: existsSync4(io.samplesPath) ? sha256(readFileSync3(io.samplesPath, "utf8")) : void 0,
322
411
  samples_file: io.outPath,
323
412
  started_at: startedAt,
324
413
  elapsed_ms: elapsedMs
325
414
  };
326
- writeFileSync2(`${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
415
+ writeFileSync3(`${io.outPath}.run.json`, `${JSON.stringify(record, null, 2)}
327
416
  `);
328
417
  return {
329
418
  status: "ok",
@@ -335,10 +424,10 @@ async function runPredict(spec, resolved, io) {
335
424
  }
336
425
 
337
426
  // src/project.ts
338
- import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
427
+ import { copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
339
428
  import { homedir as homedir2 } from "os";
340
- import { join as join5 } from "path";
341
- import { canonicalJson as canonicalJson3, DEFAULT_JULIA_CHANNEL } from "@mcmcjs/core";
429
+ import { join as join6 } from "path";
430
+ import { canonicalJson as canonicalJson4, DEFAULT_JULIA_CHANNEL } from "@mcmcjs/core";
342
431
  import { createRunner as createRunner3 } from "@mcmcjs/engine";
343
432
  var PACKAGES = [
344
433
  "Turing",
@@ -349,6 +438,7 @@ var PACKAGES = [
349
438
  "AdvancedHMC",
350
439
  "ForwardDiff",
351
440
  "Mooncake",
441
+ "ReverseDiff",
352
442
  "JSON",
353
443
  "StableRNGs",
354
444
  "StatsFuns"
@@ -375,19 +465,19 @@ function hasPins(pins) {
375
465
  return pins !== void 0 && Object.keys(pins).length > 0;
376
466
  }
377
467
  function managedProjectDir(version, pins) {
378
- const base = process.env.XDG_DATA_HOME || join5(homedir2(), ".local", "share");
379
- const root = join5(base, "mcmcjs", "julia", "env");
468
+ const base = process.env.XDG_DATA_HOME || join6(homedir2(), ".local", "share");
469
+ const root = join6(base, "mcmcjs", "julia", "env");
380
470
  if (!version && !hasPins(pins)) return root;
381
471
  let leaf = (version ?? "default").replace(/[^A-Za-z0-9._-]/g, "_");
382
- if (hasPins(pins)) leaf += `-${sha256(canonicalJson3(pins)).slice(0, 8)}`;
383
- return join5(root, leaf);
472
+ if (hasPins(pins)) leaf += `-${sha256(canonicalJson4(pins)).slice(0, 8)}`;
473
+ return join6(root, leaf);
384
474
  }
385
475
  function sentinelPath(dir) {
386
- return join5(dir, ".mcmcjs-packages.json");
476
+ return join6(dir, ".mcmcjs-packages.json");
387
477
  }
388
478
  var PROVISION_GENERATION = 1;
389
479
  function expectedSentinel(pins) {
390
- return canonicalJson3({
480
+ return canonicalJson4({
391
481
  gen: PROVISION_GENERATION,
392
482
  packages: [...PACKAGES].sort(),
393
483
  pins: pins ?? {}
@@ -395,13 +485,13 @@ function expectedSentinel(pins) {
395
485
  }
396
486
  function provisioned(dir, pins) {
397
487
  try {
398
- return readFileSync3(sentinelPath(dir), "utf8").trim() === expectedSentinel(pins);
488
+ return readFileSync4(sentinelPath(dir), "utf8").trim() === expectedSentinel(pins);
399
489
  } catch {
400
490
  return false;
401
491
  }
402
492
  }
403
493
  function managedProjectReady(dir = managedProjectDir(), pins) {
404
- return existsSync4(join5(dir, "Project.toml")) && provisioned(dir, pins);
494
+ return existsSync5(join6(dir, "Project.toml")) && provisioned(dir, pins);
405
495
  }
406
496
  function packageSpecsCode(pins) {
407
497
  const specs = PACKAGES.map((name) => {
@@ -411,15 +501,15 @@ function packageSpecsCode(pins) {
411
501
  return `[${specs.join(", ")}]`;
412
502
  }
413
503
  function usesPinnedEnv(version, pins) {
414
- return !hasPins(pins) && version === DEFAULT_JULIA_CHANNEL && existsSync4(pinnedEnvDir());
504
+ return !hasPins(pins) && version === DEFAULT_JULIA_CHANNEL && existsSync5(pinnedEnvDir());
415
505
  }
416
506
  async function ensureProject(juliaBin, run = createRunner3(30 * 6e4), dir = managedProjectDir(), pins, opts) {
417
507
  validatePins(pins);
418
508
  if (managedProjectReady(dir, pins)) return dir;
419
509
  mkdirSync2(dir, { recursive: true });
420
510
  if (usesPinnedEnv(opts?.version, pins)) {
421
- copyFileSync(join5(pinnedEnvDir(), "Project.toml"), join5(dir, "Project.toml"));
422
- copyFileSync(join5(pinnedEnvDir(), "Manifest.toml"), join5(dir, "Manifest.toml"));
511
+ copyFileSync(join6(pinnedEnvDir(), "Project.toml"), join6(dir, "Project.toml"));
512
+ copyFileSync(join6(pinnedEnvDir(), "Manifest.toml"), join6(dir, "Manifest.toml"));
423
513
  await run(juliaBin, [
424
514
  "--startup-file=no",
425
515
  `--project=${dir}`,
@@ -430,12 +520,12 @@ async function ensureProject(juliaBin, run = createRunner3(30 * 6e4), dir = mana
430
520
  const code = `using Pkg; Pkg.add(${packageSpecsCode(pins)}); Pkg.precompile()`;
431
521
  await run(juliaBin, ["--startup-file=no", `--project=${dir}`, "-e", code]);
432
522
  }
433
- writeFileSync3(sentinelPath(dir), expectedSentinel(pins));
523
+ writeFileSync4(sentinelPath(dir), expectedSentinel(pins));
434
524
  return dir;
435
525
  }
436
526
 
437
527
  // src/run-matrix.ts
438
- import { join as join6 } from "path";
528
+ import { join as join7 } from "path";
439
529
  async function runMatrix(spec, versions, io) {
440
530
  const entries = [];
441
531
  for (const version of versions) {
@@ -447,7 +537,7 @@ async function runMatrix(spec, versions, io) {
447
537
  const result = await runFit(versioned, resolved, {
448
538
  spawn: io.spawn,
449
539
  projectDir,
450
- outPath: join6(io.outDir, `${version}.samples.json`),
540
+ outPath: join7(io.outDir, `${version}.samples.json`),
451
541
  dataFile: io.dataFile,
452
542
  dataSha256: io.dataSha256
453
543
  });
@@ -624,9 +714,9 @@ function assertVersionsInstalled(channels, installed) {
624
714
 
625
715
  // src/worker.ts
626
716
  import { spawn } from "child_process";
627
- import { mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4, rmdirSync, rmSync as rmSync3, statSync } from "fs";
717
+ import { mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, rmdirSync, rmSync as rmSync4, statSync } from "fs";
628
718
  import { createConnection } from "net";
629
- import { join as join7 } from "path";
719
+ import { join as join8 } from "path";
630
720
  import { parseDrawBatchLine, parseProgressLine } from "@mcmcjs/engine";
631
721
  var START_WAIT_MS = 18e4;
632
722
  var CONNECT_TIMEOUT_MS = 1e3;
@@ -645,7 +735,7 @@ function workerPidPath(socket) {
645
735
  function killWorker(socket) {
646
736
  const pidPath = workerPidPath(socket);
647
737
  try {
648
- const pid = Number(readFileSync4(pidPath, "utf8").trim());
738
+ const pid = Number(readFileSync5(pidPath, "utf8").trim());
649
739
  if (Number.isInteger(pid) && pid > 0) {
650
740
  try {
651
741
  process.kill(-pid, "SIGKILL");
@@ -655,17 +745,17 @@ function killWorker(socket) {
655
745
  }
656
746
  } catch {
657
747
  }
658
- rmSync3(socket, { force: true });
659
- rmSync3(pidPath, { force: true });
748
+ rmSync4(socket, { force: true });
749
+ rmSync4(pidPath, { force: true });
660
750
  }
661
751
  function workersDir() {
662
752
  const runtime = process.env.XDG_RUNTIME_DIR;
663
- const dir = runtime ? join7(runtime, "mcmcjs") : join7(sharedTmpParent(), "workers");
753
+ const dir = runtime ? join8(runtime, "mcmcjs") : join8(sharedTmpParent(), "workers");
664
754
  mkdirSync3(dir, { recursive: true, mode: 448 });
665
755
  return dir;
666
756
  }
667
757
  function workerSocketPath(command, projectDir) {
668
- return join7(workersDir(), `worker-${sha256(`${command}
758
+ return join8(workersDir(), `worker-${sha256(`${command}
669
759
  ${projectDir}`).slice(0, 12)}.sock`);
670
760
  }
671
761
  function connectOnce(path) {
@@ -720,7 +810,7 @@ async function ensureWorker(resolved, projectDir, notify, signal) {
720
810
  const startedHere = acquireStartLock(path);
721
811
  if (startedHere) {
722
812
  try {
723
- rmSync3(path, { force: true });
813
+ rmSync4(path, { force: true });
724
814
  notify?.("starting the Julia worker (first use loads Turing, can take a minute)...");
725
815
  const child = spawn(
726
816
  resolved.command,
@@ -915,7 +1005,7 @@ async function listWorkers() {
915
1005
  const sockets = readdirSync(dir).filter((f) => f.startsWith("worker-") && f.endsWith(".sock"));
916
1006
  const statuses = [];
917
1007
  for (const name of sockets) {
918
- const socket = join7(dir, name);
1008
+ const socket = join8(dir, name);
919
1009
  let alive = false;
920
1010
  try {
921
1011
  const sock = await connectOnce(socket);
@@ -932,7 +1022,7 @@ async function stopWorker(socket) {
932
1022
  try {
933
1023
  sock = await connectOnce(socket);
934
1024
  } catch {
935
- rmSync3(socket, { force: true });
1025
+ rmSync4(socket, { force: true });
936
1026
  return "stale";
937
1027
  }
938
1028
  const acked = await new Promise((resolve) => {
@@ -948,7 +1038,7 @@ async function stopWorker(socket) {
948
1038
  });
949
1039
  sock.destroy();
950
1040
  if (!acked) return "pending";
951
- rmSync3(socket, { force: true });
1041
+ rmSync4(socket, { force: true });
952
1042
  return "stopped";
953
1043
  }
954
1044
  export {
@@ -972,6 +1062,7 @@ export {
972
1062
  runDoctor,
973
1063
  runFit,
974
1064
  runFitAuto,
1065
+ runLogLik,
975
1066
  runMatrix,
976
1067
  runPredict,
977
1068
  runSetup,