libpetri 2.13.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.
- package/README.md +89 -130
- package/dist/chunk-6NH64RCU.js +1016 -0
- package/dist/chunk-6NH64RCU.js.map +1 -0
- package/dist/{chunk-7VJ5CYUU.js → chunk-JZIEWVAV.js} +905 -62
- package/dist/chunk-JZIEWVAV.js.map +1 -0
- package/dist/{chunk-JVI5HFRX.js → chunk-VCDOKWVU.js} +2 -2
- package/dist/{chunk-E3ZWB645.js → chunk-YVIPJ6KM.js} +1 -1
- package/dist/chunk-YVIPJ6KM.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/debug/index.js +2 -2
- package/dist/doclet/index.d.ts +12 -3
- package/dist/doclet/index.js +8 -4
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +21 -0
- package/dist/doclet/resources/petrinet-diagrams.js +3575 -3573
- package/dist/dot-exporter-3STXYK74.js +9 -0
- package/dist/{render-ZGZEZ5RK.js → elk-place-YVNQFGXI.js} +3 -258
- package/dist/elk-place-YVNQFGXI.js.map +1 -0
- package/dist/{event-store-DKTenPbC.d.ts → event-store-BFX_yJ8I.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/export/index.js +2 -2
- package/dist/index.d.ts +91 -38
- package/dist/index.js +368 -294
- package/dist/index.js.map +1 -1
- package/dist/pan-zoom-Cp51IkDl.d.ts +33 -0
- package/dist/{petri-net-C3LSY-vm.d.ts → petri-net-WSScMyDL.d.ts} +43 -29
- package/dist/preprocess-FN3F75JR.js +193 -0
- package/dist/preprocess-FN3F75JR.js.map +1 -0
- package/dist/render-QOHGDWNE.js +78 -0
- package/dist/render-QOHGDWNE.js.map +1 -0
- package/dist/render-dom/index.d.ts +28 -29
- package/dist/render-dom/index.js +14 -21
- package/dist/render-dom/index.js.map +1 -1
- package/dist/verification/index.d.ts +273 -7
- package/dist/verification/index.js +7 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +24 -32
- package/dist/viewer/index.js +9 -1003
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer.css +21 -0
- package/dist/viewer/viewer.iife.js +3575 -3573
- package/package.json +2 -2
- package/dist/chunk-7VJ5CYUU.js.map +0 -1
- package/dist/chunk-E3ZWB645.js.map +0 -1
- package/dist/dot-exporter-SHBYMMJ3.js +0 -9
- package/dist/render-ZGZEZ5RK.js.map +0 -1
- /package/dist/{chunk-JVI5HFRX.js.map → chunk-VCDOKWVU.js.map} +0 -0
- /package/dist/{dot-exporter-SHBYMMJ3.js.map → dot-exporter-3STXYK74.js.map} +0 -0
|
@@ -3,6 +3,55 @@ import {
|
|
|
3
3
|
latest
|
|
4
4
|
} from "./chunk-ATT7U5H5.js";
|
|
5
5
|
|
|
6
|
+
// src/core/in.ts
|
|
7
|
+
function one(place) {
|
|
8
|
+
return { type: "one", place };
|
|
9
|
+
}
|
|
10
|
+
function exactly(count, place) {
|
|
11
|
+
if (count < 1) {
|
|
12
|
+
throw new Error(`count must be >= 1, got: ${count}`);
|
|
13
|
+
}
|
|
14
|
+
return { type: "exactly", place, count };
|
|
15
|
+
}
|
|
16
|
+
function all(place) {
|
|
17
|
+
return { type: "all", place };
|
|
18
|
+
}
|
|
19
|
+
function atLeast(minimum, place) {
|
|
20
|
+
if (minimum < 1) {
|
|
21
|
+
throw new Error(`minimum must be >= 1, got: ${minimum}`);
|
|
22
|
+
}
|
|
23
|
+
return { type: "at-least", place, minimum };
|
|
24
|
+
}
|
|
25
|
+
function requiredCount(spec) {
|
|
26
|
+
switch (spec.type) {
|
|
27
|
+
case "one":
|
|
28
|
+
return 1;
|
|
29
|
+
case "exactly":
|
|
30
|
+
return spec.count;
|
|
31
|
+
case "all":
|
|
32
|
+
return 1;
|
|
33
|
+
case "at-least":
|
|
34
|
+
return spec.minimum;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function consumptionCount(spec, available) {
|
|
38
|
+
if (available < requiredCount(spec)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Cannot consume from '${spec.place.name}': available=${available}, required=${requiredCount(spec)}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
switch (spec.type) {
|
|
44
|
+
case "one":
|
|
45
|
+
return 1;
|
|
46
|
+
case "exactly":
|
|
47
|
+
return spec.count;
|
|
48
|
+
case "all":
|
|
49
|
+
return available;
|
|
50
|
+
case "at-least":
|
|
51
|
+
return available;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
6
55
|
// src/core/out.ts
|
|
7
56
|
function and(...children) {
|
|
8
57
|
if (children.length === 0) {
|
|
@@ -612,6 +661,10 @@ function computePInvariants(matrix, flatNet, initialMarking) {
|
|
|
612
661
|
}
|
|
613
662
|
}
|
|
614
663
|
if (!isZero) continue;
|
|
664
|
+
if (!rowIsExact(augmented[row], T, P)) {
|
|
665
|
+
invariants.push(rawInvariant(augmented[row], T, P, flatNet, initialMarking));
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
615
668
|
const weights = new Array(P);
|
|
616
669
|
let allNonNegative = true;
|
|
617
670
|
let hasPositive = false;
|
|
@@ -662,6 +715,105 @@ function computePInvariants(matrix, flatNet, initialMarking) {
|
|
|
662
715
|
}
|
|
663
716
|
return invariants;
|
|
664
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
|
+
}
|
|
665
817
|
function computePSemiflows(matrix, flatNet, initialMarking) {
|
|
666
818
|
const np = matrix.numPlaces();
|
|
667
819
|
const nt = matrix.numTransitions();
|
|
@@ -953,11 +1105,13 @@ async function createSpacerRunner(timeoutMs) {
|
|
|
953
1105
|
const status = await fp.query(errorExpr);
|
|
954
1106
|
if (status === "unsat") {
|
|
955
1107
|
let invariantFormula = null;
|
|
1108
|
+
let provenAnswer = null;
|
|
956
1109
|
const levelInvariants = [];
|
|
957
1110
|
try {
|
|
958
1111
|
const answer = fp.getAnswer();
|
|
959
1112
|
if (answer != null) {
|
|
960
1113
|
invariantFormula = answer.toString();
|
|
1114
|
+
provenAnswer = answer;
|
|
961
1115
|
}
|
|
962
1116
|
} catch {
|
|
963
1117
|
}
|
|
@@ -973,7 +1127,7 @@ async function createSpacerRunner(timeoutMs) {
|
|
|
973
1127
|
} catch {
|
|
974
1128
|
}
|
|
975
1129
|
}
|
|
976
|
-
return { type: "proven", invariantFormula, levelInvariants };
|
|
1130
|
+
return { type: "proven", invariantFormula, levelInvariants, answer: provenAnswer };
|
|
977
1131
|
}
|
|
978
1132
|
if (status === "sat") {
|
|
979
1133
|
let answer = null;
|
|
@@ -1058,18 +1212,9 @@ function encodeTransitionRule(ctx, fp, reachable, ft, flatNet, invariants, P) {
|
|
|
1058
1212
|
const reachBody = reachable.call(...mVars);
|
|
1059
1213
|
const enabled = encodeEnabled(ctx, ft, flatNet, mVars, P);
|
|
1060
1214
|
const fireRelation = encodeFire(ctx, ft, flatNet, mVars, mPrimeVars, P);
|
|
1061
|
-
|
|
1062
|
-
for (let i = 0; i < P; i++) {
|
|
1063
|
-
nonNeg = ctx.And(nonNeg, mPrimeVars[i].ge(0));
|
|
1064
|
-
}
|
|
1215
|
+
const nonNeg = encodeNonNegativity(ctx, mPrimeVars, P);
|
|
1065
1216
|
const invConstraints = encodeInvariantConstraints(ctx, invariants, mPrimeVars, P);
|
|
1066
|
-
|
|
1067
|
-
for (const [name, bound] of flatNet.environmentBounds) {
|
|
1068
|
-
const idx = flatNet.placeIndex.get(name);
|
|
1069
|
-
if (idx != null) {
|
|
1070
|
-
envBounds = ctx.And(envBounds, mPrimeVars[idx].le(bound));
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1217
|
+
const envBounds = encodeEnvBounds(ctx, flatNet, mPrimeVars);
|
|
1073
1218
|
const body = ctx.And(reachBody, enabled, fireRelation, nonNeg, invConstraints, envBounds);
|
|
1074
1219
|
const head = reachable.call(...mPrimeVars);
|
|
1075
1220
|
const allVars = [...mVars, ...mPrimeVars];
|
|
@@ -1086,6 +1231,31 @@ function encodeInjectionRule(ctx, fp, reachable, idx, bound, P) {
|
|
|
1086
1231
|
mPrimeVars.push(Int.const(`mp${i}`));
|
|
1087
1232
|
}
|
|
1088
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) {
|
|
1089
1259
|
let fire = ctx.Bool.val(true);
|
|
1090
1260
|
for (let i = 0; i < P; i++) {
|
|
1091
1261
|
if (i === idx) {
|
|
@@ -1094,11 +1264,31 @@ function encodeInjectionRule(ctx, fp, reachable, idx, bound, P) {
|
|
|
1094
1264
|
fire = ctx.And(fire, mPrimeVars[i].eq(mVars[i]));
|
|
1095
1265
|
}
|
|
1096
1266
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
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);
|
|
1102
1292
|
}
|
|
1103
1293
|
function injectedEnvIndices(flatNet) {
|
|
1104
1294
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -1249,6 +1439,197 @@ function encodeInvariantConstraints(ctx, invariants, mVars, P) {
|
|
|
1249
1439
|
return result;
|
|
1250
1440
|
}
|
|
1251
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
|
+
|
|
1252
1633
|
// src/verification/analysis/dbm.ts
|
|
1253
1634
|
var EPSILON = 1e-9;
|
|
1254
1635
|
var DBM = class _DBM {
|
|
@@ -1734,18 +2115,8 @@ function inputRequiredCount(spec) {
|
|
|
1734
2115
|
return spec.minimum;
|
|
1735
2116
|
}
|
|
1736
2117
|
}
|
|
1737
|
-
function inputConsumeCount(spec) {
|
|
1738
|
-
|
|
1739
|
-
case "one":
|
|
1740
|
-
return 1;
|
|
1741
|
-
case "exactly":
|
|
1742
|
-
return spec.count;
|
|
1743
|
-
case "all":
|
|
1744
|
-
return 1;
|
|
1745
|
-
// Analysis: consume minimum (1 token)
|
|
1746
|
-
case "at-least":
|
|
1747
|
-
return spec.minimum;
|
|
1748
|
-
}
|
|
2118
|
+
function inputConsumeCount(spec, available) {
|
|
2119
|
+
return consumptionCount(spec, available);
|
|
1749
2120
|
}
|
|
1750
2121
|
function checkPlaceEnabled(place, required, marking, environmentPlaces, environmentMode) {
|
|
1751
2122
|
if (!environmentPlaces.has(place)) {
|
|
@@ -1763,7 +2134,11 @@ function checkPlaceEnabled(place, required, marking, environmentPlaces, environm
|
|
|
1763
2134
|
function fireTransition(marking, transition, outputPlaces, environmentPlaces, environmentMode) {
|
|
1764
2135
|
const builder = MarkingState.builder().copyFrom(marking);
|
|
1765
2136
|
for (const spec of transition.inputSpecs) {
|
|
1766
|
-
const
|
|
2137
|
+
const available = marking.tokens(spec.place);
|
|
2138
|
+
if (available < inputRequiredCount(spec)) {
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
const toConsume = inputConsumeCount(spec, available);
|
|
1767
2142
|
consumeFromPlace(builder, spec.place, toConsume, environmentPlaces, environmentMode);
|
|
1768
2143
|
}
|
|
1769
2144
|
for (const arc of transition.resets) {
|
|
@@ -1788,19 +2163,39 @@ function consumeFromPlace(builder, place, count, environmentPlaces, environmentM
|
|
|
1788
2163
|
}
|
|
1789
2164
|
|
|
1790
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
|
+
}
|
|
1791
2179
|
function decode(ctx, answer, flatNet) {
|
|
1792
2180
|
const trace = [];
|
|
1793
2181
|
const transitions = [];
|
|
2182
|
+
const stateByKey = /* @__PURE__ */ new Map();
|
|
1794
2183
|
if (answer == null) {
|
|
1795
|
-
return { trace, transitions };
|
|
2184
|
+
return { trace, transitions, states: /* @__PURE__ */ new Set(), failure: { kind: "no-answer" } };
|
|
1796
2185
|
}
|
|
2186
|
+
const counters = { skipped: 0 };
|
|
2187
|
+
let failure = null;
|
|
1797
2188
|
try {
|
|
1798
|
-
extractTrace(ctx, answer, flatNet, trace, transitions);
|
|
1799
|
-
} catch {
|
|
2189
|
+
extractTrace(ctx, answer, flatNet, trace, transitions, stateByKey, counters);
|
|
2190
|
+
} catch (e) {
|
|
2191
|
+
failure = { kind: "traversal-error", message: String(e?.message ?? e) };
|
|
1800
2192
|
}
|
|
1801
|
-
|
|
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 };
|
|
1802
2197
|
}
|
|
1803
|
-
function extractTrace(ctx, expr, flatNet, trace, transitions) {
|
|
2198
|
+
function extractTrace(ctx, expr, flatNet, trace, transitions, stateByKey, counters) {
|
|
1804
2199
|
if (expr == null) return;
|
|
1805
2200
|
if (!ctx.isApp(expr)) return;
|
|
1806
2201
|
let name;
|
|
@@ -1817,6 +2212,10 @@ function extractTrace(ctx, expr, flatNet, trace, transitions) {
|
|
|
1817
2212
|
const marking = extractMarking(ctx, expr, flatNet);
|
|
1818
2213
|
if (marking != null) {
|
|
1819
2214
|
trace.push(marking);
|
|
2215
|
+
const key = marking.toString();
|
|
2216
|
+
if (!stateByKey.has(key)) stateByKey.set(key, marking);
|
|
2217
|
+
} else {
|
|
2218
|
+
counters.skipped++;
|
|
1820
2219
|
}
|
|
1821
2220
|
}
|
|
1822
2221
|
}
|
|
@@ -1824,7 +2223,7 @@ function extractTrace(ctx, expr, flatNet, trace, transitions) {
|
|
|
1824
2223
|
const numArgs = expr.numArgs();
|
|
1825
2224
|
for (let i = 0; i < numArgs; i++) {
|
|
1826
2225
|
const child = expr.arg(i);
|
|
1827
|
-
extractTrace(ctx, child, flatNet, trace, transitions);
|
|
2226
|
+
extractTrace(ctx, child, flatNet, trace, transitions, stateByKey, counters);
|
|
1828
2227
|
}
|
|
1829
2228
|
} catch {
|
|
1830
2229
|
}
|
|
@@ -1850,6 +2249,238 @@ function extractMarking(ctx, reachableApp, flatNet) {
|
|
|
1850
2249
|
return builder.build();
|
|
1851
2250
|
}
|
|
1852
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
|
+
|
|
1853
2484
|
// src/verification/z3/name-coloured-encoder.ts
|
|
1854
2485
|
function colourSlotBound(coloured, semiflows) {
|
|
1855
2486
|
const w = (inv, pid) => inv.weights[pid] ?? 0;
|
|
@@ -2323,9 +2954,9 @@ function classify(net, mode, carrierPlaces) {
|
|
|
2323
2954
|
role: (tn) => roles.get(tn) ?? { type: "ordinary" }
|
|
2324
2955
|
};
|
|
2325
2956
|
}
|
|
2326
|
-
function fixedRequiredCount(t,
|
|
2957
|
+
function fixedRequiredCount(t, placeName2) {
|
|
2327
2958
|
for (const spec of t.inputSpecs) {
|
|
2328
|
-
if (spec.place.name ===
|
|
2959
|
+
if (spec.place.name === placeName2) {
|
|
2329
2960
|
switch (spec.type) {
|
|
2330
2961
|
case "one":
|
|
2331
2962
|
return 1;
|
|
@@ -2388,11 +3019,11 @@ var NameMarking = class _NameMarking {
|
|
|
2388
3019
|
return syms ? [...syms.keys()] : [];
|
|
2389
3020
|
}
|
|
2390
3021
|
liveSymbols() {
|
|
2391
|
-
const
|
|
3022
|
+
const all2 = /* @__PURE__ */ new Set();
|
|
2392
3023
|
for (const syms of this.perPlace.values()) {
|
|
2393
|
-
for (const s of syms.keys())
|
|
3024
|
+
for (const s of syms.keys()) all2.add(s);
|
|
2394
3025
|
}
|
|
2395
|
-
return [...
|
|
3026
|
+
return [...all2];
|
|
2396
3027
|
}
|
|
2397
3028
|
/**
|
|
2398
3029
|
* Symmetry-canonical key over `colouredOrder` (the finiteness mechanism). Two
|
|
@@ -2559,10 +3190,10 @@ function sharesConsumedInput(h, l, marking) {
|
|
|
2559
3190
|
}
|
|
2560
3191
|
return false;
|
|
2561
3192
|
}
|
|
2562
|
-
function consumedDemand(t,
|
|
3193
|
+
function consumedDemand(t, placeName2) {
|
|
2563
3194
|
let demand = 0;
|
|
2564
3195
|
for (const spec of t.inputSpecs) {
|
|
2565
|
-
if (spec.place.name ===
|
|
3196
|
+
if (spec.place.name === placeName2) demand += inputRequiredCount2(spec);
|
|
2566
3197
|
}
|
|
2567
3198
|
return demand;
|
|
2568
3199
|
}
|
|
@@ -2756,6 +3387,8 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
2756
3387
|
_budgetPlaces = /* @__PURE__ */ new Set();
|
|
2757
3388
|
_environmentMode = alwaysAvailable();
|
|
2758
3389
|
_timeoutMs = 6e4;
|
|
3390
|
+
_certificateCheck = true;
|
|
3391
|
+
_counterexampleReplay = true;
|
|
2759
3392
|
_nuMaxClasses = 1e5;
|
|
2760
3393
|
_fragmentMode = "base";
|
|
2761
3394
|
_carrierPlaces = /* @__PURE__ */ new Set();
|
|
@@ -2809,6 +3442,34 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
2809
3442
|
this._timeoutMs = ms;
|
|
2810
3443
|
return this;
|
|
2811
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
|
+
}
|
|
2812
3473
|
/**
|
|
2813
3474
|
* Sets the class-count cap for the ν-aware state-class-graph analysis (NU-050,
|
|
2814
3475
|
* Route B). When the symbolic name-aware graph would exceed this, the analysis
|
|
@@ -2955,6 +3616,7 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
2955
3616
|
report.push("=== RESULT ===\n");
|
|
2956
3617
|
report.push("PROVEN (structural): Deadlock-freedom verified by Commoner's theorem.");
|
|
2957
3618
|
report.push(" All siphons contain initially marked traps.");
|
|
3619
|
+
report.push(" Certificate check: not applicable (structural proof)");
|
|
2958
3620
|
return buildResult(
|
|
2959
3621
|
{ type: "proven", method: "structural", inductiveInvariant: null },
|
|
2960
3622
|
report.join("\n"),
|
|
@@ -2968,14 +3630,36 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
2968
3630
|
}
|
|
2969
3631
|
report.push("Phase 3: Computing P-invariants...");
|
|
2970
3632
|
const matrix = IncidenceMatrix.from(flatNet);
|
|
2971
|
-
const invariants =
|
|
2972
|
-
|
|
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
|
+
);
|
|
2973
3645
|
report.push(` Found: ${invariants.length} P-invariant(s)`);
|
|
2974
3646
|
const structurallyBounded = isCoveredByInvariants(invariants, flatNet.places.length);
|
|
2975
3647
|
report.push(` Structurally bounded: ${structurallyBounded ? "YES" : "NO"}`);
|
|
2976
3648
|
for (const inv of invariants) {
|
|
2977
3649
|
report.push(` ${formatInvariant(inv, flatNet)}`);
|
|
2978
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
|
+
}
|
|
2979
3663
|
report.push("");
|
|
2980
3664
|
report.push("Phase 4: IC3/PDR verification via Z3 Spacer...");
|
|
2981
3665
|
const colouredPlan = hasMatch && nuBounded ? buildColouredPlan(
|
|
@@ -3052,7 +3736,45 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3052
3736
|
{ places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
|
|
3053
3737
|
);
|
|
3054
3738
|
}
|
|
3055
|
-
report.push(" Status: UNSAT (property holds)
|
|
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("");
|
|
3056
3778
|
const discoveredInvariants = [];
|
|
3057
3779
|
if (queryResult.invariantFormula != null) {
|
|
3058
3780
|
discoveredInvariants.push(substituteNames(queryResult.invariantFormula, flatNet));
|
|
@@ -3075,7 +3797,7 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3075
3797
|
report.push("=== RESULT ===\n");
|
|
3076
3798
|
report.push(`PROVEN (IC3/PDR): ${propDesc}`);
|
|
3077
3799
|
report.push(" Z3 Spacer proved no reachable state violates the property.");
|
|
3078
|
-
report.push(" NOTE: Verification ignores timing constraints
|
|
3800
|
+
report.push(" NOTE: Verification ignores timing constraints.");
|
|
3079
3801
|
report.push(" An untimed proof is STRONGER than a timed one (timing only restricts behavior).");
|
|
3080
3802
|
return this.applyNuGuard(buildResult(
|
|
3081
3803
|
{
|
|
@@ -3095,29 +3817,92 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3095
3817
|
case "violated": {
|
|
3096
3818
|
report.push(" Status: SAT (counterexample found)\n");
|
|
3097
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
|
+
}
|
|
3098
3883
|
report.push("=== RESULT ===\n");
|
|
3099
3884
|
report.push(`VIOLATED: ${propDesc}`);
|
|
3100
|
-
if (
|
|
3101
|
-
report.push(` Counterexample trace (${
|
|
3102
|
-
for (let i = 0; i <
|
|
3103
|
-
report.push(` ${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]}`);
|
|
3104
3889
|
}
|
|
3105
3890
|
}
|
|
3106
|
-
if (
|
|
3107
|
-
report.push(` Firing sequence: ${
|
|
3891
|
+
if (transitions.length > 0) {
|
|
3892
|
+
report.push(` Firing sequence: ${transitions.join(" -> ")}`);
|
|
3108
3893
|
}
|
|
3109
3894
|
report.push("\n WARNING: This counterexample is in UNTIMED semantics.");
|
|
3110
3895
|
report.push(" It may be spurious if timing constraints prevent this sequence.");
|
|
3111
|
-
report.push(" JS guards are also ignored in this analysis.");
|
|
3112
3896
|
return this.applyNuGuard(buildResult(
|
|
3113
3897
|
{ type: "violated" },
|
|
3114
3898
|
report.join("\n"),
|
|
3115
3899
|
invariants,
|
|
3116
3900
|
[],
|
|
3117
|
-
|
|
3118
|
-
|
|
3901
|
+
trace,
|
|
3902
|
+
transitions,
|
|
3119
3903
|
performance.now() - start,
|
|
3120
|
-
|
|
3904
|
+
stats,
|
|
3905
|
+
confirmed
|
|
3121
3906
|
), hasMatch, nuBounded, colouredPlan != null);
|
|
3122
3907
|
}
|
|
3123
3908
|
case "unknown": {
|
|
@@ -3210,6 +3995,51 @@ function isReachabilitySafety(property) {
|
|
|
3210
3995
|
return false;
|
|
3211
3996
|
}
|
|
3212
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
|
+
}
|
|
3213
4043
|
function downgradeToUnknown(result, reason) {
|
|
3214
4044
|
return {
|
|
3215
4045
|
...result,
|
|
@@ -3219,9 +4049,13 @@ Downgraded to UNKNOWN: ${reason}
|
|
|
3219
4049
|
`,
|
|
3220
4050
|
discoveredInvariants: [],
|
|
3221
4051
|
counterexampleTrace: [],
|
|
3222
|
-
counterexampleTransitions: []
|
|
4052
|
+
counterexampleTransitions: [],
|
|
4053
|
+
counterexampleConfirmed: null
|
|
3223
4054
|
};
|
|
3224
4055
|
}
|
|
4056
|
+
function truncate(s, max) {
|
|
4057
|
+
return s.length <= max ? s : `${s.slice(0, max)}\u2026 (${s.length - max} chars truncated)`;
|
|
4058
|
+
}
|
|
3225
4059
|
function substituteNames(formula, flatNet) {
|
|
3226
4060
|
for (let i = flatNet.places.length - 1; i >= 0; i--) {
|
|
3227
4061
|
formula = formula.replace(new RegExp(`\\bm${i}\\b`, "g"), flatNet.places[i].name);
|
|
@@ -3237,10 +4071,10 @@ function formatInvariant(inv, flatNet) {
|
|
|
3237
4071
|
parts.push(flatNet.places[idx].name);
|
|
3238
4072
|
}
|
|
3239
4073
|
}
|
|
3240
|
-
return `${parts.join(" + ")} = ${inv.constant}`;
|
|
4074
|
+
return `${parts.length === 0 ? "0" : parts.join(" + ")} = ${inv.constant}`;
|
|
3241
4075
|
}
|
|
3242
|
-
function buildResult(verdict, report, invariants, discoveredInvariants, trace, transitions, elapsedMs, statistics) {
|
|
3243
|
-
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 };
|
|
3244
4078
|
}
|
|
3245
4079
|
|
|
3246
4080
|
// src/verification/smt-verification-result.ts
|
|
@@ -3252,6 +4086,12 @@ function isViolated(result) {
|
|
|
3252
4086
|
}
|
|
3253
4087
|
|
|
3254
4088
|
export {
|
|
4089
|
+
one,
|
|
4090
|
+
exactly,
|
|
4091
|
+
all,
|
|
4092
|
+
atLeast,
|
|
4093
|
+
requiredCount,
|
|
4094
|
+
consumptionCount,
|
|
3255
4095
|
and,
|
|
3256
4096
|
andPlaces,
|
|
3257
4097
|
xor,
|
|
@@ -3298,13 +4138,16 @@ export {
|
|
|
3298
4138
|
flatNetTransitionCount,
|
|
3299
4139
|
flatNetIndexOf,
|
|
3300
4140
|
encode,
|
|
4141
|
+
checkCertificate,
|
|
3301
4142
|
DBM,
|
|
3302
4143
|
StateClass,
|
|
3303
4144
|
requireOutputProducingActions,
|
|
3304
4145
|
StateClassGraph,
|
|
4146
|
+
describeDecodeFailure,
|
|
3305
4147
|
decode,
|
|
4148
|
+
replayCounterexample,
|
|
3306
4149
|
SmtVerifier,
|
|
3307
4150
|
isProven,
|
|
3308
4151
|
isViolated
|
|
3309
4152
|
};
|
|
3310
|
-
//# sourceMappingURL=chunk-
|
|
4153
|
+
//# sourceMappingURL=chunk-JZIEWVAV.js.map
|