@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/fitlib.jl +175 -18
- package/dist/index.cjs +141 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +130 -39
- package/dist/index.js.map +1 -1
- package/dist/julia-env/Manifest.toml +8 -6
- package/dist/julia-env/Project.toml +1 -0
- package/package.json +3 -3
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/
|
|
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 =
|
|
262
|
-
|
|
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)
|
|
378
|
+
if (ownTmp) rmSync3(tmp, { recursive: true, force: true });
|
|
290
379
|
}
|
|
291
380
|
const elapsedMs = Math.round(performance.now() - start);
|
|
292
|
-
if (code !== 0 || !
|
|
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
|
-
|
|
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:
|
|
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:
|
|
319
|
-
data_sha256: sha256(
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
341
|
-
import { canonicalJson as
|
|
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 ||
|
|
379
|
-
const root =
|
|
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(
|
|
383
|
-
return
|
|
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
|
|
476
|
+
return join6(dir, ".mcmcjs-packages.json");
|
|
387
477
|
}
|
|
388
478
|
var PROVISION_GENERATION = 1;
|
|
389
479
|
function expectedSentinel(pins) {
|
|
390
|
-
return
|
|
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
|
|
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
|
|
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 &&
|
|
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(
|
|
422
|
-
copyFileSync(
|
|
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
|
-
|
|
523
|
+
writeFileSync4(sentinelPath(dir), expectedSentinel(pins));
|
|
434
524
|
return dir;
|
|
435
525
|
}
|
|
436
526
|
|
|
437
527
|
// src/run-matrix.ts
|
|
438
|
-
import { join as
|
|
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:
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
659
|
-
|
|
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 ?
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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,
|