@mcmcjs/julia 0.11.3 → 0.12.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",
@@ -375,19 +464,19 @@ function hasPins(pins) {
375
464
  return pins !== void 0 && Object.keys(pins).length > 0;
376
465
  }
377
466
  function managedProjectDir(version, pins) {
378
- const base = process.env.XDG_DATA_HOME || join5(homedir2(), ".local", "share");
379
- const root = join5(base, "mcmcjs", "julia", "env");
467
+ const base = process.env.XDG_DATA_HOME || join6(homedir2(), ".local", "share");
468
+ const root = join6(base, "mcmcjs", "julia", "env");
380
469
  if (!version && !hasPins(pins)) return root;
381
470
  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);
471
+ if (hasPins(pins)) leaf += `-${sha256(canonicalJson4(pins)).slice(0, 8)}`;
472
+ return join6(root, leaf);
384
473
  }
385
474
  function sentinelPath(dir) {
386
- return join5(dir, ".mcmcjs-packages.json");
475
+ return join6(dir, ".mcmcjs-packages.json");
387
476
  }
388
477
  var PROVISION_GENERATION = 1;
389
478
  function expectedSentinel(pins) {
390
- return canonicalJson3({
479
+ return canonicalJson4({
391
480
  gen: PROVISION_GENERATION,
392
481
  packages: [...PACKAGES].sort(),
393
482
  pins: pins ?? {}
@@ -395,13 +484,13 @@ function expectedSentinel(pins) {
395
484
  }
396
485
  function provisioned(dir, pins) {
397
486
  try {
398
- return readFileSync3(sentinelPath(dir), "utf8").trim() === expectedSentinel(pins);
487
+ return readFileSync4(sentinelPath(dir), "utf8").trim() === expectedSentinel(pins);
399
488
  } catch {
400
489
  return false;
401
490
  }
402
491
  }
403
492
  function managedProjectReady(dir = managedProjectDir(), pins) {
404
- return existsSync4(join5(dir, "Project.toml")) && provisioned(dir, pins);
493
+ return existsSync5(join6(dir, "Project.toml")) && provisioned(dir, pins);
405
494
  }
406
495
  function packageSpecsCode(pins) {
407
496
  const specs = PACKAGES.map((name) => {
@@ -411,15 +500,15 @@ function packageSpecsCode(pins) {
411
500
  return `[${specs.join(", ")}]`;
412
501
  }
413
502
  function usesPinnedEnv(version, pins) {
414
- return !hasPins(pins) && version === DEFAULT_JULIA_CHANNEL && existsSync4(pinnedEnvDir());
503
+ return !hasPins(pins) && version === DEFAULT_JULIA_CHANNEL && existsSync5(pinnedEnvDir());
415
504
  }
416
505
  async function ensureProject(juliaBin, run = createRunner3(30 * 6e4), dir = managedProjectDir(), pins, opts) {
417
506
  validatePins(pins);
418
507
  if (managedProjectReady(dir, pins)) return dir;
419
508
  mkdirSync2(dir, { recursive: true });
420
509
  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"));
510
+ copyFileSync(join6(pinnedEnvDir(), "Project.toml"), join6(dir, "Project.toml"));
511
+ copyFileSync(join6(pinnedEnvDir(), "Manifest.toml"), join6(dir, "Manifest.toml"));
423
512
  await run(juliaBin, [
424
513
  "--startup-file=no",
425
514
  `--project=${dir}`,
@@ -430,12 +519,12 @@ async function ensureProject(juliaBin, run = createRunner3(30 * 6e4), dir = mana
430
519
  const code = `using Pkg; Pkg.add(${packageSpecsCode(pins)}); Pkg.precompile()`;
431
520
  await run(juliaBin, ["--startup-file=no", `--project=${dir}`, "-e", code]);
432
521
  }
433
- writeFileSync3(sentinelPath(dir), expectedSentinel(pins));
522
+ writeFileSync4(sentinelPath(dir), expectedSentinel(pins));
434
523
  return dir;
435
524
  }
436
525
 
437
526
  // src/run-matrix.ts
438
- import { join as join6 } from "path";
527
+ import { join as join7 } from "path";
439
528
  async function runMatrix(spec, versions, io) {
440
529
  const entries = [];
441
530
  for (const version of versions) {
@@ -447,7 +536,7 @@ async function runMatrix(spec, versions, io) {
447
536
  const result = await runFit(versioned, resolved, {
448
537
  spawn: io.spawn,
449
538
  projectDir,
450
- outPath: join6(io.outDir, `${version}.samples.json`),
539
+ outPath: join7(io.outDir, `${version}.samples.json`),
451
540
  dataFile: io.dataFile,
452
541
  dataSha256: io.dataSha256
453
542
  });
@@ -624,9 +713,9 @@ function assertVersionsInstalled(channels, installed) {
624
713
 
625
714
  // src/worker.ts
626
715
  import { spawn } from "child_process";
627
- import { mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4, rmdirSync, rmSync as rmSync3, statSync } from "fs";
716
+ import { mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, rmdirSync, rmSync as rmSync4, statSync } from "fs";
628
717
  import { createConnection } from "net";
629
- import { join as join7 } from "path";
718
+ import { join as join8 } from "path";
630
719
  import { parseDrawBatchLine, parseProgressLine } from "@mcmcjs/engine";
631
720
  var START_WAIT_MS = 18e4;
632
721
  var CONNECT_TIMEOUT_MS = 1e3;
@@ -645,7 +734,7 @@ function workerPidPath(socket) {
645
734
  function killWorker(socket) {
646
735
  const pidPath = workerPidPath(socket);
647
736
  try {
648
- const pid = Number(readFileSync4(pidPath, "utf8").trim());
737
+ const pid = Number(readFileSync5(pidPath, "utf8").trim());
649
738
  if (Number.isInteger(pid) && pid > 0) {
650
739
  try {
651
740
  process.kill(-pid, "SIGKILL");
@@ -655,17 +744,17 @@ function killWorker(socket) {
655
744
  }
656
745
  } catch {
657
746
  }
658
- rmSync3(socket, { force: true });
659
- rmSync3(pidPath, { force: true });
747
+ rmSync4(socket, { force: true });
748
+ rmSync4(pidPath, { force: true });
660
749
  }
661
750
  function workersDir() {
662
751
  const runtime = process.env.XDG_RUNTIME_DIR;
663
- const dir = runtime ? join7(runtime, "mcmcjs") : join7(sharedTmpParent(), "workers");
752
+ const dir = runtime ? join8(runtime, "mcmcjs") : join8(sharedTmpParent(), "workers");
664
753
  mkdirSync3(dir, { recursive: true, mode: 448 });
665
754
  return dir;
666
755
  }
667
756
  function workerSocketPath(command, projectDir) {
668
- return join7(workersDir(), `worker-${sha256(`${command}
757
+ return join8(workersDir(), `worker-${sha256(`${command}
669
758
  ${projectDir}`).slice(0, 12)}.sock`);
670
759
  }
671
760
  function connectOnce(path) {
@@ -720,7 +809,7 @@ async function ensureWorker(resolved, projectDir, notify, signal) {
720
809
  const startedHere = acquireStartLock(path);
721
810
  if (startedHere) {
722
811
  try {
723
- rmSync3(path, { force: true });
812
+ rmSync4(path, { force: true });
724
813
  notify?.("starting the Julia worker (first use loads Turing, can take a minute)...");
725
814
  const child = spawn(
726
815
  resolved.command,
@@ -915,7 +1004,7 @@ async function listWorkers() {
915
1004
  const sockets = readdirSync(dir).filter((f) => f.startsWith("worker-") && f.endsWith(".sock"));
916
1005
  const statuses = [];
917
1006
  for (const name of sockets) {
918
- const socket = join7(dir, name);
1007
+ const socket = join8(dir, name);
919
1008
  let alive = false;
920
1009
  try {
921
1010
  const sock = await connectOnce(socket);
@@ -932,7 +1021,7 @@ async function stopWorker(socket) {
932
1021
  try {
933
1022
  sock = await connectOnce(socket);
934
1023
  } catch {
935
- rmSync3(socket, { force: true });
1024
+ rmSync4(socket, { force: true });
936
1025
  return "stale";
937
1026
  }
938
1027
  const acked = await new Promise((resolve) => {
@@ -948,7 +1037,7 @@ async function stopWorker(socket) {
948
1037
  });
949
1038
  sock.destroy();
950
1039
  if (!acked) return "pending";
951
- rmSync3(socket, { force: true });
1040
+ rmSync4(socket, { force: true });
952
1041
  return "stopped";
953
1042
  }
954
1043
  export {
@@ -972,6 +1061,7 @@ export {
972
1061
  runDoctor,
973
1062
  runFit,
974
1063
  runFitAuto,
1064
+ runLogLik,
975
1065
  runMatrix,
976
1066
  runPredict,
977
1067
  runSetup,