libpetri 3.0.0 → 3.0.1

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.
Files changed (37) hide show
  1. package/README.md +49 -1
  2. package/dist/chunk-6NH64RCU.js +1016 -0
  3. package/dist/chunk-6NH64RCU.js.map +1 -0
  4. package/dist/{chunk-5W6SVYPD.js → chunk-JZIEWVAV.js} +839 -44
  5. package/dist/chunk-JZIEWVAV.js.map +1 -0
  6. package/dist/debug/index.d.ts +2 -2
  7. package/dist/doclet/index.d.ts +12 -3
  8. package/dist/doclet/index.js +5 -1
  9. package/dist/doclet/index.js.map +1 -1
  10. package/dist/doclet/resources/petrinet-diagrams.css +21 -0
  11. package/dist/doclet/resources/petrinet-diagrams.js +3575 -3573
  12. package/dist/{render-ZGZEZ5RK.js → elk-place-YVNQFGXI.js} +3 -258
  13. package/dist/elk-place-YVNQFGXI.js.map +1 -0
  14. package/dist/{event-store-Df_sAVQ_.d.ts → event-store-BFX_yJ8I.d.ts} +1 -1
  15. package/dist/export/index.d.ts +1 -1
  16. package/dist/index.d.ts +4 -4
  17. package/dist/index.js +1 -1
  18. package/dist/pan-zoom-Cp51IkDl.d.ts +33 -0
  19. package/dist/{petri-net-UQBBkvLl.d.ts → petri-net-WSScMyDL.d.ts} +20 -1
  20. package/dist/preprocess-FN3F75JR.js +193 -0
  21. package/dist/preprocess-FN3F75JR.js.map +1 -0
  22. package/dist/render-QOHGDWNE.js +78 -0
  23. package/dist/render-QOHGDWNE.js.map +1 -0
  24. package/dist/render-dom/index.d.ts +28 -29
  25. package/dist/render-dom/index.js +14 -21
  26. package/dist/render-dom/index.js.map +1 -1
  27. package/dist/verification/index.d.ts +269 -4
  28. package/dist/verification/index.js +7 -1
  29. package/dist/verification/index.js.map +1 -1
  30. package/dist/viewer/index.d.ts +24 -32
  31. package/dist/viewer/index.js +9 -1003
  32. package/dist/viewer/index.js.map +1 -1
  33. package/dist/viewer/viewer.css +21 -0
  34. package/dist/viewer/viewer.iife.js +3575 -3573
  35. package/package.json +2 -2
  36. package/dist/chunk-5W6SVYPD.js.map +0 -1
  37. package/dist/render-ZGZEZ5RK.js.map +0 -1
@@ -661,6 +661,10 @@ function computePInvariants(matrix, flatNet, initialMarking) {
661
661
  }
662
662
  }
663
663
  if (!isZero) continue;
664
+ if (!rowIsExact(augmented[row], T, P)) {
665
+ invariants.push(rawInvariant(augmented[row], T, P, flatNet, initialMarking));
666
+ continue;
667
+ }
664
668
  const weights = new Array(P);
665
669
  let allNonNegative = true;
666
670
  let hasPositive = false;
@@ -711,6 +715,105 @@ function computePInvariants(matrix, flatNet, initialMarking) {
711
715
  }
712
716
  return invariants;
713
717
  }
718
+ function rowIsExact(row, T, P) {
719
+ for (let i = 0; i < P; i++) {
720
+ if (!Number.isSafeInteger(row[T + i])) return false;
721
+ }
722
+ return true;
723
+ }
724
+ function rawInvariant(row, T, P, flatNet, initialMarking) {
725
+ const weights = new Array(P);
726
+ const support = /* @__PURE__ */ new Set();
727
+ let constant = 0;
728
+ for (let i = 0; i < P; i++) {
729
+ weights[i] = row[T + i];
730
+ if (weights[i] !== 0) {
731
+ support.add(i);
732
+ constant += weights[i] * initialMarking.tokens(flatNet.places[i]);
733
+ }
734
+ }
735
+ return pInvariant(weights, constant, support);
736
+ }
737
+ function validateInvariantsExact(matrix, invariants, flatNet, initialMarking) {
738
+ const nonlinear = nonlinearPlaces(flatNet);
739
+ const valid = [];
740
+ const dropped = [];
741
+ for (const inv of invariants) {
742
+ const reason = exactCheckFailure(matrix, inv, nonlinear, flatNet, initialMarking);
743
+ if (reason === null) {
744
+ valid.push(inv);
745
+ } else {
746
+ dropped.push({ invariant: inv, reason });
747
+ }
748
+ }
749
+ return { valid, dropped };
750
+ }
751
+ function nonlinearPlaces(flatNet) {
752
+ const nonlinear = /* @__PURE__ */ new Set();
753
+ for (const ft of flatNet.transitions) {
754
+ for (let p = 0; p < ft.consumeAll.length; p++) {
755
+ if (ft.consumeAll[p]) nonlinear.add(p);
756
+ }
757
+ for (const p of ft.resetPlaces) nonlinear.add(p);
758
+ }
759
+ return nonlinear;
760
+ }
761
+ function exactCheckFailure(matrix, inv, nonlinear, flatNet, initialMarking) {
762
+ const P = matrix.numPlaces();
763
+ const T = matrix.numTransitions();
764
+ if (inv.weights.length !== P) {
765
+ return `weight vector has ${inv.weights.length} entries, expected ${P}`;
766
+ }
767
+ for (let p = 0; p < P; p++) {
768
+ if (!Number.isSafeInteger(inv.weights[p])) {
769
+ return `weight overflow at place '${placeName(flatNet, p)}' (exact value outside this implementation's integer extraction range)`;
770
+ }
771
+ }
772
+ if (!Number.isSafeInteger(inv.constant)) {
773
+ return `constant ${inv.constant} is outside the safe-integer range`;
774
+ }
775
+ for (let p = 0; p < inv.weights.length; p++) {
776
+ if (inv.weights[p] !== 0 && nonlinear.has(p)) {
777
+ return `support intersects consume-all/reset place '${placeName(flatNet, p)}' (non-linear consumption; see Strengthening.lean H1)`;
778
+ }
779
+ }
780
+ const y = inv.weights.map((w) => BigInt(w));
781
+ const incidence = matrix.incidence();
782
+ for (let t = 0; t < T; t++) {
783
+ const row = incidence[t];
784
+ let dot = 0n;
785
+ for (let p = 0; p < P; p++) {
786
+ if (y[p] === 0n) continue;
787
+ if (!Number.isSafeInteger(row[p])) {
788
+ return `incidence entry ${row[p]} at [t=${t}][p=${p}] is outside the safe-integer range`;
789
+ }
790
+ dot += y[p] * BigInt(row[p]);
791
+ }
792
+ if (dot !== 0n) {
793
+ return `y*C is ${dot} (not 0) at ${columnName(flatNet, t)}`;
794
+ }
795
+ }
796
+ let exact = 0n;
797
+ for (let p = 0; p < P; p++) {
798
+ if (y[p] === 0n) continue;
799
+ const tokens = initialMarking.tokens(flatNet.places[p]);
800
+ if (!Number.isSafeInteger(tokens)) {
801
+ return `initial marking of place ${p} (${tokens}) is outside the safe-integer range`;
802
+ }
803
+ exact += y[p] * BigInt(tokens);
804
+ }
805
+ if (exact !== BigInt(inv.constant)) {
806
+ return `constant ${inv.constant} does not match exact y*M0 = ${exact}`;
807
+ }
808
+ return null;
809
+ }
810
+ function placeName(flatNet, p) {
811
+ return flatNet.places[p]?.name ?? `#${p}`;
812
+ }
813
+ function columnName(flatNet, t) {
814
+ const ft = flatNet.transitions[t];
815
+ return ft != null ? `transition '${ft.name}'` : `env-injector column ${t - flatNet.transitions.length}`;
816
+ }
714
817
  function computePSemiflows(matrix, flatNet, initialMarking) {
715
818
  const np = matrix.numPlaces();
716
819
  const nt = matrix.numTransitions();
@@ -1002,11 +1105,13 @@ async function createSpacerRunner(timeoutMs) {
1002
1105
  const status = await fp.query(errorExpr);
1003
1106
  if (status === "unsat") {
1004
1107
  let invariantFormula = null;
1108
+ let provenAnswer = null;
1005
1109
  const levelInvariants = [];
1006
1110
  try {
1007
1111
  const answer = fp.getAnswer();
1008
1112
  if (answer != null) {
1009
1113
  invariantFormula = answer.toString();
1114
+ provenAnswer = answer;
1010
1115
  }
1011
1116
  } catch {
1012
1117
  }
@@ -1022,7 +1127,7 @@ async function createSpacerRunner(timeoutMs) {
1022
1127
  } catch {
1023
1128
  }
1024
1129
  }
1025
- return { type: "proven", invariantFormula, levelInvariants };
1130
+ return { type: "proven", invariantFormula, levelInvariants, answer: provenAnswer };
1026
1131
  }
1027
1132
  if (status === "sat") {
1028
1133
  let answer = null;
@@ -1107,18 +1212,9 @@ function encodeTransitionRule(ctx, fp, reachable, ft, flatNet, invariants, P) {
1107
1212
  const reachBody = reachable.call(...mVars);
1108
1213
  const enabled = encodeEnabled(ctx, ft, flatNet, mVars, P);
1109
1214
  const fireRelation = encodeFire(ctx, ft, flatNet, mVars, mPrimeVars, P);
1110
- let nonNeg = ctx.Bool.val(true);
1111
- for (let i = 0; i < P; i++) {
1112
- nonNeg = ctx.And(nonNeg, mPrimeVars[i].ge(0));
1113
- }
1215
+ const nonNeg = encodeNonNegativity(ctx, mPrimeVars, P);
1114
1216
  const invConstraints = encodeInvariantConstraints(ctx, invariants, mPrimeVars, P);
1115
- let envBounds = ctx.Bool.val(true);
1116
- for (const [name, bound] of flatNet.environmentBounds) {
1117
- const idx = flatNet.placeIndex.get(name);
1118
- if (idx != null) {
1119
- envBounds = ctx.And(envBounds, mPrimeVars[idx].le(bound));
1120
- }
1121
- }
1217
+ const envBounds = encodeEnvBounds(ctx, flatNet, mPrimeVars);
1122
1218
  const body = ctx.And(reachBody, enabled, fireRelation, nonNeg, invConstraints, envBounds);
1123
1219
  const head = reachable.call(...mPrimeVars);
1124
1220
  const allVars = [...mVars, ...mPrimeVars];
@@ -1135,6 +1231,31 @@ function encodeInjectionRule(ctx, fp, reachable, idx, bound, P) {
1135
1231
  mPrimeVars.push(Int.const(`mp${i}`));
1136
1232
  }
1137
1233
  const reachBody = reachable.call(...mVars);
1234
+ const fire = encodeInjectionFire(ctx, idx, mVars, mPrimeVars, P);
1235
+ const guard = encodeInjectionGuard(ctx, idx, bound, mVars);
1236
+ const body = ctx.And(reachBody, guard, fire);
1237
+ const head = reachable.call(...mPrimeVars);
1238
+ const qRule = ctx.ForAll([...mVars, ...mPrimeVars], ctx.Implies(body, head));
1239
+ fp.addRule(qRule, `env_inject_${idx}`);
1240
+ }
1241
+ function encodeNonNegativity(ctx, vars, P) {
1242
+ let result = ctx.Bool.val(true);
1243
+ for (let i = 0; i < P; i++) {
1244
+ result = ctx.And(result, vars[i].ge(0));
1245
+ }
1246
+ return result;
1247
+ }
1248
+ function encodeEnvBounds(ctx, flatNet, vars) {
1249
+ let result = ctx.Bool.val(true);
1250
+ for (const [name, bound] of flatNet.environmentBounds) {
1251
+ const idx = flatNet.placeIndex.get(name);
1252
+ if (idx != null) {
1253
+ result = ctx.And(result, vars[idx].le(bound));
1254
+ }
1255
+ }
1256
+ return result;
1257
+ }
1258
+ function encodeInjectionFire(ctx, idx, mVars, mPrimeVars, P) {
1138
1259
  let fire = ctx.Bool.val(true);
1139
1260
  for (let i = 0; i < P; i++) {
1140
1261
  if (i === idx) {
@@ -1143,11 +1264,31 @@ function encodeInjectionRule(ctx, fp, reachable, idx, bound, P) {
1143
1264
  fire = ctx.And(fire, mPrimeVars[i].eq(mVars[i]));
1144
1265
  }
1145
1266
  }
1146
- const guard = bound === null ? ctx.Bool.val(true) : mVars[idx].lt(bound);
1147
- const body = ctx.And(reachBody, guard, fire);
1148
- const head = reachable.call(...mPrimeVars);
1149
- const qRule = ctx.ForAll([...mVars, ...mPrimeVars], ctx.Implies(body, head));
1150
- fp.addRule(qRule, `env_inject_${idx}`);
1267
+ return fire;
1268
+ }
1269
+ function encodeInjectionGuard(ctx, idx, bound, mVars) {
1270
+ return bound === null ? ctx.Bool.val(true) : mVars[idx].lt(bound);
1271
+ }
1272
+ function encodeStepRelation(ctx, flatNet, mVars, mPrimeVars) {
1273
+ const P = flatNet.places.length;
1274
+ const disjuncts = [];
1275
+ for (const ft of flatNet.transitions) {
1276
+ const enabled = encodeEnabled(ctx, ft, flatNet, mVars, P);
1277
+ const fire = encodeFire(ctx, ft, flatNet, mVars, mPrimeVars, P);
1278
+ const nonNeg = encodeNonNegativity(ctx, mPrimeVars, P);
1279
+ const envBounds = encodeEnvBounds(ctx, flatNet, mPrimeVars);
1280
+ disjuncts.push(ctx.And(enabled, fire, nonNeg, envBounds));
1281
+ }
1282
+ for (const [name, bound] of flatNet.environmentInjection) {
1283
+ const idx = flatNet.placeIndex.get(name);
1284
+ if (idx == null) continue;
1285
+ disjuncts.push(ctx.And(
1286
+ encodeInjectionGuard(ctx, idx, bound, mVars),
1287
+ encodeInjectionFire(ctx, idx, mVars, mPrimeVars, P)
1288
+ ));
1289
+ }
1290
+ if (disjuncts.length === 0) return ctx.Bool.val(false);
1291
+ return ctx.Or(...disjuncts);
1151
1292
  }
1152
1293
  function injectedEnvIndices(flatNet) {
1153
1294
  const out = /* @__PURE__ */ new Map();
@@ -1298,6 +1439,197 @@ function encodeInvariantConstraints(ctx, invariants, mVars, P) {
1298
1439
  return result;
1299
1440
  }
1300
1441
 
1442
+ // src/verification/z3/certificate-checker.ts
1443
+ async function checkCertificate(ctx, answer, flatNet, initialMarking, property, invariants, sinkPlaces, timeoutMs) {
1444
+ try {
1445
+ if (answer == null) {
1446
+ return {
1447
+ type: "unavailable",
1448
+ reason: "invariant missing (Z3 produced no inductive-invariant answer)",
1449
+ invariant: null
1450
+ };
1451
+ }
1452
+ const P = flatNet.places.length;
1453
+ const Int = ctx.Int;
1454
+ const mVars = [];
1455
+ const mPrimeVars = [];
1456
+ for (let i = 0; i < P; i++) {
1457
+ mVars.push(Int.const(`m${i}`));
1458
+ mPrimeVars.push(Int.const(`mp${i}`));
1459
+ }
1460
+ const invariant = extractInvariant(ctx, answer, P, mVars);
1461
+ if (invariant == null) {
1462
+ return {
1463
+ type: "unavailable",
1464
+ reason: "invariant unparseable (no Reachable definition recognized in the Z3 answer)",
1465
+ invariant: String(answer)
1466
+ };
1467
+ }
1468
+ const candidate = invariants.length > 0 ? ctx.And(invariant, encodeInvariantConstraints(ctx, invariants, mVars, P)) : invariant;
1469
+ const failure = await validateCandidate(
1470
+ ctx,
1471
+ candidate,
1472
+ flatNet,
1473
+ initialMarking,
1474
+ property,
1475
+ sinkPlaces,
1476
+ timeoutMs,
1477
+ mVars,
1478
+ mPrimeVars
1479
+ );
1480
+ if (failure == null) {
1481
+ return { type: "passed", invariant: String(candidate) };
1482
+ }
1483
+ return { type: "failed", vc: failure.vc, detail: failure.detail, invariant: String(candidate) };
1484
+ } catch (e) {
1485
+ return {
1486
+ type: "unavailable",
1487
+ reason: `certificate check error: ${e?.message ?? e}`,
1488
+ invariant: null
1489
+ };
1490
+ }
1491
+ }
1492
+ async function validateCandidate(ctx, candidate, flatNet, initialMarking, property, sinkPlaces, timeoutMs, mVars, mPrimeVars) {
1493
+ const P = flatNet.places.length;
1494
+ const Int = ctx.Int;
1495
+ let nonNegM = ctx.Bool.val(true);
1496
+ for (let i = 0; i < P; i++) {
1497
+ nonNegM = ctx.And(nonNegM, mVars[i].ge(0));
1498
+ }
1499
+ const initPairs = mVars.map((v, i) => [
1500
+ v,
1501
+ Int.val(initialMarking.tokens(flatNet.places[i]))
1502
+ ]);
1503
+ const candidateAtInit = ctx.substitute(candidate, ...initPairs);
1504
+ const vc1 = await checkUnsat(ctx, flatNet, mVars, timeoutMs, "initiation (VC1)", [ctx.Not(candidateAtInit)]);
1505
+ if (vc1 != null) return vc1;
1506
+ const primePairs = mVars.map((v, i) => [v, mPrimeVars[i]]);
1507
+ const candidatePrime = ctx.substitute(candidate, ...primePairs);
1508
+ const step = encodeStepRelation(ctx, flatNet, mVars, mPrimeVars);
1509
+ const vc2 = await checkUnsat(
1510
+ ctx,
1511
+ flatNet,
1512
+ mVars,
1513
+ timeoutMs,
1514
+ "consecution (VC2)",
1515
+ [candidate, nonNegM, step, ctx.Not(candidatePrime)]
1516
+ );
1517
+ if (vc2 != null) return vc2;
1518
+ const bad = encodePropertyViolation(ctx, flatNet, property, sinkPlaces, mVars, P);
1519
+ const vc3 = await checkUnsat(ctx, flatNet, mVars, timeoutMs, "safety (VC3)", [candidate, nonNegM, bad]);
1520
+ if (vc3 != null) return vc3;
1521
+ return null;
1522
+ }
1523
+ function extractInvariant(ctx, answer, P, mVars) {
1524
+ for (const conjunct of topLevelConjuncts(ctx, answer)) {
1525
+ const invariant = tryExtractDefinition(ctx, conjunct, P, mVars);
1526
+ if (invariant != null) return invariant;
1527
+ }
1528
+ return null;
1529
+ }
1530
+ function topLevelConjuncts(ctx, expr) {
1531
+ if (!ctx.isQuantifier(expr) && ctx.isApp(expr) && String(expr.decl().name()) === "and") {
1532
+ const out = [];
1533
+ const n = expr.numArgs();
1534
+ for (let i = 0; i < n; i++) out.push(expr.arg(i));
1535
+ return out;
1536
+ }
1537
+ return [expr];
1538
+ }
1539
+ function tryExtractDefinition(ctx, expr, P, mVars) {
1540
+ if (ctx.isQuantifier(expr) && expr.is_forall()) {
1541
+ const q = expr;
1542
+ const parts2 = splitDefinition(ctx, q.body(), P);
1543
+ if (parts2 == null) return null;
1544
+ const numVars = q.num_vars();
1545
+ const to = new Array(numVars);
1546
+ for (let j = 0; j < P; j++) {
1547
+ const arg = parts2.app.arg(j);
1548
+ if (!ctx.isVar(arg)) return null;
1549
+ const idx = ctx.getVarIndex(arg);
1550
+ if (idx >= numVars || to[idx] != null) return null;
1551
+ to[idx] = mVars[j];
1552
+ }
1553
+ for (let i = 0; i < numVars; i++) {
1554
+ if (to[i] == null) to[i] = ctx.Int.const(`certFree${i}`);
1555
+ }
1556
+ return ctx.substituteVars(parts2.phi, ...to);
1557
+ }
1558
+ const parts = splitDefinition(ctx, expr, P);
1559
+ if (parts == null) return null;
1560
+ const pairs = [];
1561
+ for (let j = 0; j < P; j++) {
1562
+ const arg = parts.app.arg(j);
1563
+ if (ctx.isVar(arg)) return null;
1564
+ pairs.push([arg, mVars[j]]);
1565
+ }
1566
+ if (pairs.length === 0) return parts.phi;
1567
+ return ctx.substitute(parts.phi, ...pairs);
1568
+ }
1569
+ function splitDefinition(ctx, body, P) {
1570
+ if (ctx.isQuantifier(body) || !ctx.isApp(body)) return null;
1571
+ const b = body;
1572
+ const decl = String(b.decl().name());
1573
+ if ((decl === "=" || decl === "iff") && b.numArgs() === 2) {
1574
+ if (isReachableApp(ctx, b.arg(0), P)) return { app: b.arg(0), phi: b.arg(1) };
1575
+ if (isReachableApp(ctx, b.arg(1), P)) return { app: b.arg(1), phi: b.arg(0) };
1576
+ return null;
1577
+ }
1578
+ if (decl === "=>" && b.numArgs() === 2 && isReachableApp(ctx, b.arg(0), P)) {
1579
+ return { app: b.arg(0), phi: b.arg(1) };
1580
+ }
1581
+ return null;
1582
+ }
1583
+ function isReachableApp(ctx, expr, P) {
1584
+ return ctx.isApp(expr) && String(expr.decl().name()) === "Reachable" && expr.numArgs() === P;
1585
+ }
1586
+ async function checkUnsat(ctx, flatNet, mVars, timeoutMs, vc, assertions) {
1587
+ const solver = new ctx.Solver();
1588
+ if (timeoutMs > 0) {
1589
+ solver.set("timeout", Math.min(timeoutMs, 2147483647));
1590
+ }
1591
+ for (const assertion of assertions) {
1592
+ solver.add(assertion);
1593
+ }
1594
+ const status = await solver.check();
1595
+ if (status === "unsat") return null;
1596
+ if (status === "sat") {
1597
+ const witness = describeWitness(solver, flatNet, mVars);
1598
+ return {
1599
+ vc,
1600
+ detail: `solver returned SATISFIABLE${witness == null ? "" : ` (witness: ${witness})`}`
1601
+ };
1602
+ }
1603
+ let why = null;
1604
+ try {
1605
+ why = String(solver.reasonUnknown());
1606
+ } catch {
1607
+ }
1608
+ return { vc, detail: `solver returned UNKNOWN${why == null || why === "" ? "" : ` (${why})`}` };
1609
+ }
1610
+ function describeWitness(solver, flatNet, mVars) {
1611
+ try {
1612
+ const model = solver.model();
1613
+ const parts = [];
1614
+ let length = 0;
1615
+ for (let i = 0; i < mVars.length; i++) {
1616
+ const value = model.eval(mVars[i], false);
1617
+ const text = value == null ? "" : String(value);
1618
+ if (!/^(-?\d+|\(- \d+\))$/.test(text)) continue;
1619
+ const part = `${flatNet.places[i].name}=${text}`;
1620
+ parts.push(part);
1621
+ length += part.length + 2;
1622
+ if (length > 160) {
1623
+ parts.push("...");
1624
+ break;
1625
+ }
1626
+ }
1627
+ return parts.length === 0 ? null : parts.join(", ");
1628
+ } catch {
1629
+ return null;
1630
+ }
1631
+ }
1632
+
1301
1633
  // src/verification/analysis/dbm.ts
1302
1634
  var EPSILON = 1e-9;
1303
1635
  var DBM = class _DBM {
@@ -1831,19 +2163,39 @@ function consumeFromPlace(builder, place, count, environmentPlaces, environmentM
1831
2163
  }
1832
2164
 
1833
2165
  // src/verification/z3/counterexample-decoder.ts
2166
+ function describeDecodeFailure(failure) {
2167
+ if (failure == null) {
2168
+ return "no Reachable applications with concrete arguments found in the derivation";
2169
+ }
2170
+ switch (failure.kind) {
2171
+ case "no-answer":
2172
+ return "Z3 produced no derivation answer";
2173
+ case "traversal-error":
2174
+ return `derivation walk failed: ${failure.message}`;
2175
+ case "non-concrete":
2176
+ return `${failure.skipped} Reachable application(s) had non-concrete arguments`;
2177
+ }
2178
+ }
1834
2179
  function decode(ctx, answer, flatNet) {
1835
2180
  const trace = [];
1836
2181
  const transitions = [];
2182
+ const stateByKey = /* @__PURE__ */ new Map();
1837
2183
  if (answer == null) {
1838
- return { trace, transitions };
2184
+ return { trace, transitions, states: /* @__PURE__ */ new Set(), failure: { kind: "no-answer" } };
1839
2185
  }
2186
+ const counters = { skipped: 0 };
2187
+ let failure = null;
1840
2188
  try {
1841
- extractTrace(ctx, answer, flatNet, trace, transitions);
1842
- } catch {
2189
+ extractTrace(ctx, answer, flatNet, trace, transitions, stateByKey, counters);
2190
+ } catch (e) {
2191
+ failure = { kind: "traversal-error", message: String(e?.message ?? e) };
1843
2192
  }
1844
- return { trace, transitions };
2193
+ if (failure == null && counters.skipped > 0) {
2194
+ failure = { kind: "non-concrete", skipped: counters.skipped };
2195
+ }
2196
+ return { trace, transitions, states: new Set(stateByKey.values()), failure };
1845
2197
  }
1846
- function extractTrace(ctx, expr, flatNet, trace, transitions) {
2198
+ function extractTrace(ctx, expr, flatNet, trace, transitions, stateByKey, counters) {
1847
2199
  if (expr == null) return;
1848
2200
  if (!ctx.isApp(expr)) return;
1849
2201
  let name;
@@ -1860,6 +2212,10 @@ function extractTrace(ctx, expr, flatNet, trace, transitions) {
1860
2212
  const marking = extractMarking(ctx, expr, flatNet);
1861
2213
  if (marking != null) {
1862
2214
  trace.push(marking);
2215
+ const key = marking.toString();
2216
+ if (!stateByKey.has(key)) stateByKey.set(key, marking);
2217
+ } else {
2218
+ counters.skipped++;
1863
2219
  }
1864
2220
  }
1865
2221
  }
@@ -1867,7 +2223,7 @@ function extractTrace(ctx, expr, flatNet, trace, transitions) {
1867
2223
  const numArgs = expr.numArgs();
1868
2224
  for (let i = 0; i < numArgs; i++) {
1869
2225
  const child = expr.arg(i);
1870
- extractTrace(ctx, child, flatNet, trace, transitions);
2226
+ extractTrace(ctx, child, flatNet, trace, transitions, stateByKey, counters);
1871
2227
  }
1872
2228
  } catch {
1873
2229
  }
@@ -1893,6 +2249,238 @@ function extractMarking(ctx, reachableApp, flatNet) {
1893
2249
  return builder.build();
1894
2250
  }
1895
2251
 
2252
+ // src/verification/z3/abstract-replayer.ts
2253
+ function stepName(step) {
2254
+ return step.kind === "fire" ? step.transition : `inject(${step.place})`;
2255
+ }
2256
+ function stateKey(state) {
2257
+ return state.join(",");
2258
+ }
2259
+ function vectorize(marking, flatNet) {
2260
+ return flatNet.places.map((p) => marking.tokens(p));
2261
+ }
2262
+ function toMarkingState(state, flatNet) {
2263
+ const builder = MarkingState.builder();
2264
+ for (let i = 0; i < flatNet.places.length; i++) {
2265
+ if (state[i] > 0) builder.tokens(flatNet.places[i], state[i]);
2266
+ }
2267
+ return builder.build();
2268
+ }
2269
+ function enabledA(state, ft) {
2270
+ const P = state.length;
2271
+ for (let p = 0; p < P; p++) {
2272
+ if (ft.preVector[p] > 0 && state[p] < ft.preVector[p]) return false;
2273
+ }
2274
+ for (const p of ft.readPlaces) {
2275
+ if (state[p] < 1) return false;
2276
+ }
2277
+ for (const p of ft.inhibitorPlaces) {
2278
+ if (state[p] !== 0) return false;
2279
+ }
2280
+ return true;
2281
+ }
2282
+ function fireIndexed(state, ft, resets) {
2283
+ const P = state.length;
2284
+ const next = new Array(P);
2285
+ for (let p = 0; p < P; p++) {
2286
+ if (resets.has(p) || ft.consumeAll[p]) {
2287
+ next[p] = ft.postVector[p];
2288
+ } else {
2289
+ next[p] = state[p] - ft.preVector[p] + ft.postVector[p];
2290
+ }
2291
+ }
2292
+ return next;
2293
+ }
2294
+ function injectA(state, idx) {
2295
+ const next = [...state];
2296
+ next[idx] = next[idx] + 1;
2297
+ return next;
2298
+ }
2299
+ function buildIndex(flatNet) {
2300
+ const resetSets = flatNet.transitions.map((ft) => new Set(ft.resetPlaces));
2301
+ const envInj = /* @__PURE__ */ new Map();
2302
+ for (const [name, bound] of flatNet.environmentInjection) {
2303
+ const idx = flatNet.placeIndex.get(name);
2304
+ if (idx != null) envInj.set(idx, bound);
2305
+ }
2306
+ const envCaps = [];
2307
+ for (const [name, cap] of flatNet.environmentBounds) {
2308
+ const idx = flatNet.placeIndex.get(name);
2309
+ if (idx != null) envCaps.push([idx, cap]);
2310
+ }
2311
+ return { flatNet, resetSets, envInj, envCaps };
2312
+ }
2313
+ function withinEnvBounds(index, state) {
2314
+ for (const [idx, cap] of index.envCaps) {
2315
+ if (state[idx] > cap) return false;
2316
+ }
2317
+ return true;
2318
+ }
2319
+ function successorsIndexed(index, state) {
2320
+ const out = [];
2321
+ const transitions = index.flatNet.transitions;
2322
+ for (let t = 0; t < transitions.length; t++) {
2323
+ const ft = transitions[t];
2324
+ if (!enabledA(state, ft)) continue;
2325
+ const next = fireIndexed(state, ft, index.resetSets[t]);
2326
+ if (!withinEnvBounds(index, next)) continue;
2327
+ out.push({ state: next, step: { kind: "fire", transition: ft.name } });
2328
+ }
2329
+ for (const [name, bound] of index.flatNet.environmentInjection) {
2330
+ const idx = index.flatNet.placeIndex.get(name);
2331
+ if (idx == null) continue;
2332
+ if (bound === null || state[idx] < bound) {
2333
+ out.push({ state: injectA(state, idx), step: { kind: "inject", place: name } });
2334
+ }
2335
+ }
2336
+ return out;
2337
+ }
2338
+ function enabledRelaxEnv(state, ft, envInj) {
2339
+ const P = state.length;
2340
+ for (let p = 0; p < P; p++) {
2341
+ const pre = ft.preVector[p];
2342
+ if (pre <= 0) continue;
2343
+ if (envInj.has(p)) {
2344
+ const bound = envInj.get(p);
2345
+ if (bound !== null && pre > bound) return false;
2346
+ continue;
2347
+ }
2348
+ if (state[p] < pre) return false;
2349
+ }
2350
+ for (const p of ft.readPlaces) {
2351
+ if (envInj.has(p)) {
2352
+ const bound = envInj.get(p);
2353
+ if (bound !== null && bound < 1) return false;
2354
+ continue;
2355
+ }
2356
+ if (state[p] < 1) return false;
2357
+ }
2358
+ for (const p of ft.inhibitorPlaces) {
2359
+ if (state[p] !== 0) return false;
2360
+ }
2361
+ return true;
2362
+ }
2363
+ function isDeadlockA(index, state) {
2364
+ for (const ft of index.flatNet.transitions) {
2365
+ if (enabledRelaxEnv(state, ft, index.envInj)) return false;
2366
+ }
2367
+ return true;
2368
+ }
2369
+ function satisfiesBadIndexed(index, state, property, sinkPlaces) {
2370
+ const flatNet = index.flatNet;
2371
+ switch (property.type) {
2372
+ case "deadlock-free": {
2373
+ if (!isDeadlockA(index, state)) return false;
2374
+ for (const sink of sinkPlaces) {
2375
+ const idx = flatNetIndexOf(flatNet, sink);
2376
+ if (idx >= 0 && state[idx] > 0) return false;
2377
+ }
2378
+ return true;
2379
+ }
2380
+ case "mutual-exclusion": {
2381
+ const idx1 = flatNetIndexOf(flatNet, property.p1);
2382
+ const idx2 = flatNetIndexOf(flatNet, property.p2);
2383
+ if (idx1 < 0 || idx2 < 0) return false;
2384
+ return state[idx1] >= 1 && state[idx2] >= 1;
2385
+ }
2386
+ case "place-bound":
2387
+ case "branch-place-bound": {
2388
+ const idx = flatNetIndexOf(flatNet, property.place);
2389
+ if (idx < 0) return false;
2390
+ return state[idx] > property.bound;
2391
+ }
2392
+ case "joined-or-dead-lettered": {
2393
+ const idx = flatNetIndexOf(flatNet, property.pending);
2394
+ if (idx < 0) return false;
2395
+ return isDeadlockA(index, state) && state[idx] >= 1;
2396
+ }
2397
+ case "unreachable": {
2398
+ let resolved = 0;
2399
+ for (const p of property.places) {
2400
+ const idx = flatNetIndexOf(flatNet, p);
2401
+ if (idx < 0) continue;
2402
+ resolved++;
2403
+ if (state[idx] < 1) return false;
2404
+ }
2405
+ return resolved > 0;
2406
+ }
2407
+ }
2408
+ }
2409
+ function replayCounterexample(flatNet, initial, decodedStates, property, sinkPlaces, options = {}) {
2410
+ const segmentBudget = options.segmentBudget ?? 3;
2411
+ const nodeBudget = options.nodeBudget ?? 1e4;
2412
+ const anchors = /* @__PURE__ */ new Set();
2413
+ for (const s of decodedStates) anchors.add(stateKey(s));
2414
+ if (anchors.size === 0) {
2415
+ return { kind: "exhausted", reason: "no decoded states to replay", nodesExplored: 0 };
2416
+ }
2417
+ const initKey = stateKey(initial);
2418
+ if (!anchors.has(initKey)) {
2419
+ return {
2420
+ kind: "exhausted",
2421
+ reason: "the initial marking is not among the decoded states",
2422
+ nodesExplored: 0
2423
+ };
2424
+ }
2425
+ const index = buildIndex(flatNet);
2426
+ if (satisfiesBadIndexed(index, initial, property, sinkPlaces)) {
2427
+ return { kind: "confirmed", states: [initial], steps: [], nodesExplored: 1 };
2428
+ }
2429
+ const nodes = [{ state: initial, step: null, parent: -1, segment: 0 }];
2430
+ const bestSegment = /* @__PURE__ */ new Map([[initKey, 0]]);
2431
+ const queue = [0];
2432
+ let truncated = false;
2433
+ for (let head = 0; head < queue.length; head++) {
2434
+ const idx = queue[head];
2435
+ const node = nodes[idx];
2436
+ if (node.segment >= segmentBudget) {
2437
+ truncated = true;
2438
+ continue;
2439
+ }
2440
+ for (const succ of successorsIndexed(index, node.state)) {
2441
+ const key = stateKey(succ.state);
2442
+ const segment = anchors.has(key) ? 0 : node.segment + 1;
2443
+ const prior = bestSegment.get(key);
2444
+ if (prior !== void 0 && prior <= segment) continue;
2445
+ bestSegment.set(key, segment);
2446
+ if (nodes.length >= nodeBudget) {
2447
+ return {
2448
+ kind: "exhausted",
2449
+ reason: `search budget exhausted (${nodeBudget} nodes) before reaching a violating state`,
2450
+ nodesExplored: nodes.length
2451
+ };
2452
+ }
2453
+ nodes.push({ state: succ.state, step: succ.step, parent: idx, segment });
2454
+ const childIdx = nodes.length - 1;
2455
+ if (satisfiesBadIndexed(index, succ.state, property, sinkPlaces)) {
2456
+ const chain = reconstruct(nodes, childIdx);
2457
+ return { kind: "confirmed", ...chain, nodesExplored: nodes.length };
2458
+ }
2459
+ queue.push(childIdx);
2460
+ }
2461
+ }
2462
+ if (truncated) {
2463
+ return {
2464
+ kind: "exhausted",
2465
+ reason: `no violating state within ${segmentBudget} abstract step(s) of a decoded state (${bestSegment.size} state(s) explored)`,
2466
+ nodesExplored: nodes.length
2467
+ };
2468
+ }
2469
+ return { kind: "no-chain", nodesExplored: nodes.length };
2470
+ }
2471
+ function reconstruct(nodes, last) {
2472
+ const states = [];
2473
+ const steps = [];
2474
+ for (let i = last; i >= 0; i = nodes[i].parent) {
2475
+ const node = nodes[i];
2476
+ states.push(node.state);
2477
+ if (node.step != null) steps.push(node.step);
2478
+ }
2479
+ states.reverse();
2480
+ steps.reverse();
2481
+ return { states, steps };
2482
+ }
2483
+
1896
2484
  // src/verification/z3/name-coloured-encoder.ts
1897
2485
  function colourSlotBound(coloured, semiflows) {
1898
2486
  const w = (inv, pid) => inv.weights[pid] ?? 0;
@@ -2366,9 +2954,9 @@ function classify(net, mode, carrierPlaces) {
2366
2954
  role: (tn) => roles.get(tn) ?? { type: "ordinary" }
2367
2955
  };
2368
2956
  }
2369
- function fixedRequiredCount(t, placeName) {
2957
+ function fixedRequiredCount(t, placeName2) {
2370
2958
  for (const spec of t.inputSpecs) {
2371
- if (spec.place.name === placeName) {
2959
+ if (spec.place.name === placeName2) {
2372
2960
  switch (spec.type) {
2373
2961
  case "one":
2374
2962
  return 1;
@@ -2602,10 +3190,10 @@ function sharesConsumedInput(h, l, marking) {
2602
3190
  }
2603
3191
  return false;
2604
3192
  }
2605
- function consumedDemand(t, placeName) {
3193
+ function consumedDemand(t, placeName2) {
2606
3194
  let demand = 0;
2607
3195
  for (const spec of t.inputSpecs) {
2608
- if (spec.place.name === placeName) demand += inputRequiredCount2(spec);
3196
+ if (spec.place.name === placeName2) demand += inputRequiredCount2(spec);
2609
3197
  }
2610
3198
  return demand;
2611
3199
  }
@@ -2799,6 +3387,8 @@ var SmtVerifier = class _SmtVerifier {
2799
3387
  _budgetPlaces = /* @__PURE__ */ new Set();
2800
3388
  _environmentMode = alwaysAvailable();
2801
3389
  _timeoutMs = 6e4;
3390
+ _certificateCheck = true;
3391
+ _counterexampleReplay = true;
2802
3392
  _nuMaxClasses = 1e5;
2803
3393
  _fragmentMode = "base";
2804
3394
  _carrierPlaces = /* @__PURE__ */ new Set();
@@ -2852,6 +3442,34 @@ var SmtVerifier = class _SmtVerifier {
2852
3442
  this._timeoutMs = ms;
2853
3443
  return this;
2854
3444
  }
3445
+ /**
3446
+ * Enables/disables the independent IC3 certificate check (default: enabled).
3447
+ *
3448
+ * When a proven verdict comes from the IC3/Spacer path on the flat count
3449
+ * encoding, the synthesized inductive invariant is re-validated with a plain
3450
+ * solver against the UNSTRENGTHENED step relation — VC1 (init), VC2
3451
+ * (consecution), VC3 (safety) — so a Spacer or encoder defect cannot certify
3452
+ * a false PROVEN. A certificate that fails validation downgrades the verdict
3453
+ * to unknown. Structural proofs and the coloured ν-encoding are unaffected.
3454
+ */
3455
+ certificateCheck(enabled) {
3456
+ this._certificateCheck = enabled;
3457
+ return this;
3458
+ }
3459
+ /**
3460
+ * Enables/disables abstract counterexample replay (default: enabled).
3461
+ *
3462
+ * When a violated verdict comes from the flat count encoding, the decoded
3463
+ * counterexample states (an order-free set — the derivation tree is walked in
3464
+ * traversal order, not firing order) are re-executed TS-side against the
3465
+ * abstract semantics the encoder emits (Lean's `fireA`, Basic.lean), searching
3466
+ * for a firing order from M₀ to a property-violating marking. See
3467
+ * `SmtVerificationResult.counterexampleConfirmed` for how each outcome lands.
3468
+ */
3469
+ counterexampleReplay(enabled) {
3470
+ this._counterexampleReplay = enabled;
3471
+ return this;
3472
+ }
2855
3473
  /**
2856
3474
  * Sets the class-count cap for the ν-aware state-class-graph analysis (NU-050,
2857
3475
  * Route B). When the symbolic name-aware graph would exceed this, the analysis
@@ -2998,6 +3616,7 @@ var SmtVerifier = class _SmtVerifier {
2998
3616
  report.push("=== RESULT ===\n");
2999
3617
  report.push("PROVEN (structural): Deadlock-freedom verified by Commoner's theorem.");
3000
3618
  report.push(" All siphons contain initially marked traps.");
3619
+ report.push(" Certificate check: not applicable (structural proof)");
3001
3620
  return buildResult(
3002
3621
  { type: "proven", method: "structural", inductiveInvariant: null },
3003
3622
  report.join("\n"),
@@ -3011,14 +3630,36 @@ var SmtVerifier = class _SmtVerifier {
3011
3630
  }
3012
3631
  report.push("Phase 3: Computing P-invariants...");
3013
3632
  const matrix = IncidenceMatrix.from(flatNet);
3014
- const invariants = computePInvariants(matrix, flatNet, this._initialMarking);
3015
- const semiflows = computePSemiflows(matrix, flatNet, this._initialMarking);
3633
+ const { valid: invariants, dropped: droppedInvariants } = validateInvariantsExact(
3634
+ matrix,
3635
+ computePInvariants(matrix, flatNet, this._initialMarking),
3636
+ flatNet,
3637
+ this._initialMarking
3638
+ );
3639
+ const { valid: semiflows, dropped: droppedSemiflows } = validateInvariantsExact(
3640
+ matrix,
3641
+ computePSemiflows(matrix, flatNet, this._initialMarking),
3642
+ flatNet,
3643
+ this._initialMarking
3644
+ );
3016
3645
  report.push(` Found: ${invariants.length} P-invariant(s)`);
3017
3646
  const structurallyBounded = isCoveredByInvariants(invariants, flatNet.places.length);
3018
3647
  report.push(` Structurally bounded: ${structurallyBounded ? "YES" : "NO"}`);
3019
3648
  for (const inv of invariants) {
3020
3649
  report.push(` ${formatInvariant(inv, flatNet)}`);
3021
3650
  }
3651
+ for (const { invariant, reason } of droppedInvariants) {
3652
+ report.push(` Dropped invariant: ${formatInvariant(invariant, flatNet)} - ${reason}`);
3653
+ }
3654
+ if (droppedInvariants.length > 0) {
3655
+ report.push(` Dropped: ${droppedInvariants.length} invariant(s) failed the exact re-check`);
3656
+ }
3657
+ for (const { invariant, reason } of droppedSemiflows) {
3658
+ report.push(` Dropped semiflow: ${formatInvariant(invariant, flatNet)} - ${reason}`);
3659
+ }
3660
+ if (droppedSemiflows.length > 0) {
3661
+ report.push(` Dropped: ${droppedSemiflows.length} semiflow(s) failed the exact re-check`);
3662
+ }
3022
3663
  report.push("");
3023
3664
  report.push("Phase 4: IC3/PDR verification via Z3 Spacer...");
3024
3665
  const colouredPlan = hasMatch && nuBounded ? buildColouredPlan(
@@ -3095,7 +3736,45 @@ var SmtVerifier = class _SmtVerifier {
3095
3736
  { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
3096
3737
  );
3097
3738
  }
3098
- report.push(" Status: UNSAT (property holds)\n");
3739
+ report.push(" Status: UNSAT (property holds)");
3740
+ if (colouredPlan != null) {
3741
+ report.push(" Certificate check: not applicable (name-coloured encoding)");
3742
+ } else if (!this._certificateCheck) {
3743
+ report.push(" Certificate check: not applicable (disabled)");
3744
+ } else {
3745
+ const certificate = await checkCertificate(
3746
+ runner.ctx,
3747
+ queryResult.answer,
3748
+ flatNet,
3749
+ this._initialMarking,
3750
+ this._property,
3751
+ invariants,
3752
+ this._sinkPlaces,
3753
+ this._timeoutMs
3754
+ );
3755
+ const reason = certificateDowngradeReason(certificate);
3756
+ if (reason != null) {
3757
+ report.push(" Certificate check: FAILED");
3758
+ if (certificate.type !== "passed" && certificate.invariant != null) {
3759
+ report.push(` Uncertified invariant: ${substituteNames(certificate.invariant, flatNet)}`);
3760
+ }
3761
+ report.push("");
3762
+ report.push("=== RESULT ===\n");
3763
+ report.push(`UNKNOWN: ${reason}`);
3764
+ return buildResult(
3765
+ { type: "unknown", reason },
3766
+ report.join("\n"),
3767
+ invariants,
3768
+ [],
3769
+ [],
3770
+ [],
3771
+ performance.now() - start,
3772
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
3773
+ );
3774
+ }
3775
+ report.push(" Certificate check: PASSED (init, consecution, safety)");
3776
+ }
3777
+ report.push("");
3099
3778
  const discoveredInvariants = [];
3100
3779
  if (queryResult.invariantFormula != null) {
3101
3780
  discoveredInvariants.push(substituteNames(queryResult.invariantFormula, flatNet));
@@ -3138,16 +3817,79 @@ var SmtVerifier = class _SmtVerifier {
3138
3817
  case "violated": {
3139
3818
  report.push(" Status: SAT (counterexample found)\n");
3140
3819
  const decoded = decode(runner.ctx, queryResult.answer, flatNet);
3820
+ const stats = {
3821
+ places: flatNet.places.length,
3822
+ transitions: flatNet.transitions.length,
3823
+ invariantsFound: invariants.length,
3824
+ structuralResult: structResultStr
3825
+ };
3826
+ let confirmed = null;
3827
+ let trace = decoded.trace;
3828
+ let transitions = decoded.transitions;
3829
+ let replayed = false;
3830
+ if (colouredPlan == null && this._counterexampleReplay) {
3831
+ const assessment = assessCounterexample(
3832
+ flatNet,
3833
+ this._initialMarking,
3834
+ decoded.states,
3835
+ this._property,
3836
+ this._sinkPlaces
3837
+ );
3838
+ if (assessment.kind === "confirmed") {
3839
+ confirmed = true;
3840
+ replayed = true;
3841
+ trace = assessment.trace;
3842
+ transitions = assessment.firings;
3843
+ report.push(" Counterexample replay: CONFIRMED (abstract chain M0 -> bad re-executed)");
3844
+ } else if (assessment.kind === "unconfirmed") {
3845
+ confirmed = false;
3846
+ report.push(` Counterexample replay: UNCONFIRMED (${assessment.note})`);
3847
+ if (decoded.failure != null) {
3848
+ report.push(` Decoder degradation: ${describeDecodeFailure(decoded.failure)}`);
3849
+ }
3850
+ report.push(" The verdict rests on Spacer's SAT answer.");
3851
+ } else {
3852
+ report.push(" Counterexample replay: FAILED");
3853
+ report.push(` Decoded states (order-free set, ${decoded.states.size}):`);
3854
+ for (const m of decoded.states) {
3855
+ report.push(` ${m}`);
3856
+ }
3857
+ if (decoded.trace.length > 0) {
3858
+ report.push(` Raw traversal-order trace (${decoded.trace.length} states):`);
3859
+ for (let i = 0; i < decoded.trace.length; i++) {
3860
+ report.push(` ${i}: ${decoded.trace[i]}`);
3861
+ }
3862
+ }
3863
+ if (decoded.failure != null) {
3864
+ report.push(` Decoder degradation: ${describeDecodeFailure(decoded.failure)}`);
3865
+ }
3866
+ report.push(` Raw Z3 answer: ${truncate(String(queryResult.answer), 2e3)}`);
3867
+ report.push("");
3868
+ report.push("=== RESULT ===\n");
3869
+ report.push(`UNKNOWN: ${assessment.reason}`);
3870
+ return buildResult(
3871
+ { type: "unknown", reason: assessment.reason },
3872
+ report.join("\n"),
3873
+ invariants,
3874
+ [],
3875
+ [],
3876
+ [],
3877
+ performance.now() - start,
3878
+ stats,
3879
+ false
3880
+ );
3881
+ }
3882
+ }
3141
3883
  report.push("=== RESULT ===\n");
3142
3884
  report.push(`VIOLATED: ${propDesc}`);
3143
- if (decoded.trace.length > 0) {
3144
- report.push(` Counterexample trace (${decoded.trace.length} states):`);
3145
- for (let i = 0; i < decoded.trace.length; i++) {
3146
- report.push(` ${i}: ${decoded.trace[i]}`);
3885
+ if (trace.length > 0) {
3886
+ report.push(` Counterexample trace (${replayed ? "replay order, " : ""}${trace.length} states):`);
3887
+ for (let i = 0; i < trace.length; i++) {
3888
+ report.push(` ${i}: ${trace[i]}`);
3147
3889
  }
3148
3890
  }
3149
- if (decoded.transitions.length > 0) {
3150
- report.push(` Firing sequence: ${decoded.transitions.join(" -> ")}`);
3891
+ if (transitions.length > 0) {
3892
+ report.push(` Firing sequence: ${transitions.join(" -> ")}`);
3151
3893
  }
3152
3894
  report.push("\n WARNING: This counterexample is in UNTIMED semantics.");
3153
3895
  report.push(" It may be spurious if timing constraints prevent this sequence.");
@@ -3156,10 +3898,11 @@ var SmtVerifier = class _SmtVerifier {
3156
3898
  report.join("\n"),
3157
3899
  invariants,
3158
3900
  [],
3159
- decoded.trace,
3160
- decoded.transitions,
3901
+ trace,
3902
+ transitions,
3161
3903
  performance.now() - start,
3162
- { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
3904
+ stats,
3905
+ confirmed
3163
3906
  ), hasMatch, nuBounded, colouredPlan != null);
3164
3907
  }
3165
3908
  case "unknown": {
@@ -3252,6 +3995,51 @@ function isReachabilitySafety(property) {
3252
3995
  return false;
3253
3996
  }
3254
3997
  }
3998
+ function assessCounterexample(flatNet, initialMarking, decodedStates, property, sinkPlaces) {
3999
+ if (decodedStates.size === 0) {
4000
+ return {
4001
+ kind: "unconfirmed",
4002
+ note: "no counterexample states could be decoded from the Spacer answer, so the abstract replay could not run"
4003
+ };
4004
+ }
4005
+ let outcome;
4006
+ try {
4007
+ outcome = replayCounterexample(
4008
+ flatNet,
4009
+ vectorize(initialMarking, flatNet),
4010
+ [...decodedStates].map((m) => vectorize(m, flatNet)),
4011
+ property,
4012
+ sinkPlaces
4013
+ );
4014
+ } catch (e) {
4015
+ outcome = { kind: "exhausted", reason: `replay threw: ${e?.message ?? e}`, nodesExplored: 0 };
4016
+ }
4017
+ switch (outcome.kind) {
4018
+ case "confirmed":
4019
+ return {
4020
+ kind: "confirmed",
4021
+ trace: outcome.states.map((s) => toMarkingState(s, flatNet)),
4022
+ firings: outcome.steps.map(stepName)
4023
+ };
4024
+ case "exhausted":
4025
+ return { kind: "unconfirmed", note: `abstract replay did not complete: ${outcome.reason}` };
4026
+ case "no-chain":
4027
+ return {
4028
+ kind: "downgraded",
4029
+ reason: "counterexample replay found no firing chain to the violation under the abstract semantics, so VIOLATED is withheld"
4030
+ };
4031
+ }
4032
+ }
4033
+ function certificateDowngradeReason(outcome) {
4034
+ switch (outcome.type) {
4035
+ case "passed":
4036
+ return null;
4037
+ case "failed":
4038
+ return `certificate check failed: ${outcome.vc} was not UNSAT - ${outcome.detail}; the IC3 certificate could not be independently re-validated against the unstrengthened step relation, so PROVEN is withheld`;
4039
+ case "unavailable":
4040
+ return `certificate check could not run: ${outcome.reason}; PROVEN is withheld without an independently validated certificate`;
4041
+ }
4042
+ }
3255
4043
  function downgradeToUnknown(result, reason) {
3256
4044
  return {
3257
4045
  ...result,
@@ -3261,9 +4049,13 @@ Downgraded to UNKNOWN: ${reason}
3261
4049
  `,
3262
4050
  discoveredInvariants: [],
3263
4051
  counterexampleTrace: [],
3264
- counterexampleTransitions: []
4052
+ counterexampleTransitions: [],
4053
+ counterexampleConfirmed: null
3265
4054
  };
3266
4055
  }
4056
+ function truncate(s, max) {
4057
+ return s.length <= max ? s : `${s.slice(0, max)}\u2026 (${s.length - max} chars truncated)`;
4058
+ }
3267
4059
  function substituteNames(formula, flatNet) {
3268
4060
  for (let i = flatNet.places.length - 1; i >= 0; i--) {
3269
4061
  formula = formula.replace(new RegExp(`\\bm${i}\\b`, "g"), flatNet.places[i].name);
@@ -3279,10 +4071,10 @@ function formatInvariant(inv, flatNet) {
3279
4071
  parts.push(flatNet.places[idx].name);
3280
4072
  }
3281
4073
  }
3282
- return `${parts.join(" + ")} = ${inv.constant}`;
4074
+ return `${parts.length === 0 ? "0" : parts.join(" + ")} = ${inv.constant}`;
3283
4075
  }
3284
- function buildResult(verdict, report, invariants, discoveredInvariants, trace, transitions, elapsedMs, statistics) {
3285
- return { verdict, report, invariants, discoveredInvariants, counterexampleTrace: trace, counterexampleTransitions: transitions, elapsedMs, statistics };
4076
+ function buildResult(verdict, report, invariants, discoveredInvariants, trace, transitions, elapsedMs, statistics, counterexampleConfirmed = null) {
4077
+ return { verdict, report, invariants, discoveredInvariants, counterexampleTrace: trace, counterexampleTransitions: transitions, counterexampleConfirmed, elapsedMs, statistics };
3286
4078
  }
3287
4079
 
3288
4080
  // src/verification/smt-verification-result.ts
@@ -3346,13 +4138,16 @@ export {
3346
4138
  flatNetTransitionCount,
3347
4139
  flatNetIndexOf,
3348
4140
  encode,
4141
+ checkCertificate,
3349
4142
  DBM,
3350
4143
  StateClass,
3351
4144
  requireOutputProducingActions,
3352
4145
  StateClassGraph,
4146
+ describeDecodeFailure,
3353
4147
  decode,
4148
+ replayCounterexample,
3354
4149
  SmtVerifier,
3355
4150
  isProven,
3356
4151
  isViolated
3357
4152
  };
3358
- //# sourceMappingURL=chunk-5W6SVYPD.js.map
4153
+ //# sourceMappingURL=chunk-JZIEWVAV.js.map